diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
commit | 44cf8ec67278bd1ab6c7f83a9993f7a5686a9541 (patch) | |
tree | 5eec4b0d1a3f163d279c3c27c03324ba49fa235a | |
parent | Initial commit. (diff) | |
download | zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.tar.xz zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.zip |
Adding upstream version 0.23.93.upstream/0.23.93upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
425 files changed, 85904 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..77bae36 --- /dev/null +++ b/.clang-format @@ -0,0 +1,141 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveBitFields: true +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true +AlignEscapedNewlines: true +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortBlocksOnASingleLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true + +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true + +BreakBeforeBinaryOperators: None +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '"config\.h"' + Priority: 1 + SortPriority: 1 + - Regex: '<^(glib).*>' + Priority: 2 + SortPriority: 2 + - Regex: '<glib\.h>' + Priority: 3 + SortPriority: 3 + - Regex: '<glib.*>' + Priority: 4 + SortPriority: 4 + - Regex: '[\<\"]zbar\.h[\>\"]' + Priority: 5 + SortPriority: 6 + - Regex: '[\<\"]zbar.*' + Priority: 6 + SortPriority: 7 + - Regex: '<.*>' + Priority: 7 + SortPriority: 5 + - Regex: '.*' + Priority: 8 + SortPriority: 8 +SortIncludes: true + +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 50 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..12033db --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,238 @@ +name: CI + +# Should run only on branches and PR, as "on_tag.yml" will handle tags +on: + push: + branches: master test + pull_request: + branches: master + +jobs: + +# +# Linux +# + Ubuntu: + name: Ubuntu debuild + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev pkg-config xmlto \ + devscripts build-essential lintian \ + debhelper-compat \ + dh-exec libdbus-1-dev \ + libmagick++-dev libv4l-dev python3-dev \ + libgtk-3-dev lftp \ + dh-sequence-python3 libgtk2.0-dev \ + libqt5x11extras5-dev qtbase5-dev + - name: build + run: | + .github/workflows/debuilder.sh + + Ubuntu_Gtk2: + name: Ubuntu with Gtk2 + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev libgtk2.0-dev libmagick++-dev \ + libqt5x11extras5-dev libv4l-dev libx11-dev openjdk-8-jdk-headless \ + perl pkg-config python-all-dev python-all-dbg python-gtk2-dev \ + qt5-default xmlto + + - name: configure + run: | + autoreconf -vfi + export QT_SELECT=5 + ./configure + - name: build + run: + make + - name: check + run: + make check-local + - name: install + run: + sudo make install + Ubuntu_Gtk3: + name: Ubuntu with Gtk3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev gir1.2-gtk-3.0 libgtk-3-dev \ + libgirepository1.0-dev libmagick++-dev libqt5x11extras5-dev \ + libv4l-dev libx11-dev openjdk-8-jdk-headless perl \ + pkg-config python3-minimal python3-dev python3 python3-gi \ + qt5-default xmlto + - name: configure + run: | + autoreconf -vfi + + # Let configure get this + unset PYTHON_CFLAGS PYTHON + + export QT_SELECT=5 + ./configure --with-gtk=gtk3 --with-python=python3 + - name: build + run: + make + - name: check + run: + make check-local + - name: install + run: + sudo make install + Mingw_w64_VfW: + name: Mingw-w64 VfW + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev pkg-config win-iconv-mingw-w64-dev \ + binutils-mingw-w64-i686 gcc-mingw-w64 mingw-w64-i686-dev \ + mingw-w64-common xmlto + - name: configure + run: | + export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig + + autoreconf -vfi + + CC=i686-w64-mingw32-gcc ./configure \ + --host=i686-w64-mingw32 --prefix=/usr/local/win32 \ + --without-gtk --without-python --without-qt --without-java \ + --without-imagemagick --enable-pthread + - name: build + run: + make + - name: install + run: + sudo make install + Mingw_w64_DShow: + name: Mingw-w64 DShow + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev pkg-config win-iconv-mingw-w64-dev \ + binutils-mingw-w64-i686 gcc-mingw-w64 mingw-w64-i686-dev \ + mingw-w64-common xmlto + - name: configure + run: | + export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig + + autoreconf -vfi + + CC=i686-w64-mingw32-gcc ./configure \ + --host=i686-w64-mingw32 --prefix=/usr/local/win32 \ + --without-gtk --without-python --without-qt --without-java \ + --without-imagemagick --enable-pthread \ + --with-directshow + - name: build + run: + make + - name: install + run: + sudo make install + +# +# Mac OS +# + Mac_OS: + name: Mac OS + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + brew install gettext autoconf automake libiconv libtool \ + gs graphicsmagick python xmlto pkg-config ccache + + brew unlink libtool && brew link libtool + brew unlink gettext && brew link gettext + - name: configure + run: + autoreconf -vfi + + ./configure --disable-video --disable-nls --with-python=python3 + + - name: build + run: | + make + sudo make install + +# +# Windows +# + Windows: + name: Windows + runs-on: windows-latest + strategy: + matrix: + arch: [x86_64, i686] + video: [VfW, DShow] + include: + - arch: x86_64 + msystem: MINGW64 + grep: x86-64 + - arch: i686 + msystem: MINGW32 + grep: 386 + - video: DShow + extra: --with-directshow + - video: VfW + extra: + defaults: + run: + shell: msys2 {0} + + env: + CPPFLAGS: -D__USE_MINGW_ANSI_STDIO=1 + + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + update: false + install: >- + mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-iconv + mingw-w64-${{ matrix.arch }}-imagemagick + mingw-w64-${{ matrix.arch }}-python + base-devel git xmlto + autoconf libtool automake gettext make autoconf-archive pkg-config + + - uses: actions/checkout@v2 + + - name: configure + shell: msys2 {0} + run: | + autoreconf -vfi + ./configure LDFLAGS="-static" \ + --enable-pthread --disable-dependency-tracking \ + ${{ matrix.extra }} + + - name: build + shell: msys2 {0} + run: + make + - name: install + shell: msys2 {0} + run: + make install diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 0000000..e99308d --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,51 @@ +name: Run coverity scan +on: + workflow_dispatch: + + schedule: + - cron: '3 8 * * *' # Daily at 08:03 UTC + +jobs: + Coverity: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev gir1.2-gtk-3.0 libgtk-3-dev \ + libgirepository1.0-dev libmagick++-dev libqt5x11extras5-dev \ + libv4l-dev libx11-dev openjdk-8-jdk-headless perl \ + pkg-config python3-minimal python3-dev python3 python3-gi \ + qt5-default xmlto + - name: Download Coverity Build Tool + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + run: | + wget -q https://scan.coverity.com/download/cxx/linux64 \ + --post-data "token=$TOKEN&project=ZBar" \ + -O cov-analysis-linux64.tar.gz + mkdir cov-analysis-linux64 + tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 + - name: Fixed world writable dirs + run: | + chmod go-w $HOME + sudo chmod -R go-w /usr/share + - name: Prepare Coverity + run: | + autoreconf -vfi + ./configure + export PATH=${PWD}/cov-analysis-linux64/bin:$PATH + cov-build --dir cov-int make + - name: Submit the result to Coverity Scan + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + run: | + tar czvf zbar.tgz cov-int + curl --form token=$TOKEN \ + --form email=mchehab@kernel.org \ + --form file=@zbar.tgz \ + --form version=trunk \ + --form description="Zbar-git-$(git log -1 --pretty='%h')" \ + https://scan.coverity.com/builds?project=ZBar diff --git a/.github/workflows/debuilder.sh b/.github/workflows/debuilder.sh new file mode 100755 index 0000000..9c3958a --- /dev/null +++ b/.github/workflows/debuilder.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -e + +# A debian ruleset file which runs on Github's distro +DEB_FNAME="zbar_0.23.90-*.debian.tar.xz" +DEB_URL="http://deb.debian.org/debian/pool/main/z/zbar/" + +# Should be the same version as provided by the host OS +COMPAT=12 + +# Set directories used during the build + +ZBARDIR=${PWD} +BUILDDIR=${ZBARDIR}/../build + +echo "Generating an origin tarball" + +cd "${ZBARDIR}" + +VER=$(cat "${ZBARDIR}/configure.ac" | grep AC_INIT | perl -ne 'print $1 if /(\d+[.\d]+)/') +TAR=${ZBARDIR}/../zbar_${VER}.orig.tar.gz + +git archive --format tgz -o "${TAR}" HEAD + +echo "Retrieving Debian ruleset" +lftp -e "mget -c ${DEB_FNAME}; exit" "${DEB_URL}" + +# Ensure to use just one version, in case multiple ones were downloaded +DEB_FNAME=$(ls -1 ${DEB_FNAME} | tail -1) + +echo "Preparing build environment" +rm -rf "${BUILDDIR}/" || true +mkdir -p "${BUILDDIR}" +cd "${BUILDDIR}" + +tar xf "${TAR}" +tar xf "${ZBARDIR}/${DEB_FNAME}" + +# Ensure that debhelper-compat will use the one expected by the build distro +sed -E "s#debhelper-compat.*,#debhelper-compat (= $COMPAT),#" -i debian/control + +# Ignore missing SONAME for libs, if any, as it is not a build robot's task +# to update ${DEB_FNAME} ruleset +echo -e "\noverride_dh_shlibdeps:" >> debian/rules +echo -e "\tdh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info" >> debian/rules + +# We want it to build cleanly - so drop all patches from it +rm -rf debian/patches + +# Override the changelog to ensure that it will contain the current version +cat << EOF > debian/changelog +zbar (${VER}) unstable; urgency=medium + + * Upstream version + + -- LinuxTV bot <linuxtv-commits@linuxtv.org> $(date -R) +EOF + +OS_VERSION=$(. /etc/os-release && echo "$ID-$VERSION_ID") + +echo "Building ZBar packages for ${OS_VERSION}" +debuild -us -uc diff --git a/.github/workflows/gen_release.pl b/.github/workflows/gen_release.pl new file mode 100755 index 0000000..9a108ce --- /dev/null +++ b/.github/workflows/gen_release.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl + +my $body_path = shift or die "Need a file name to store the release body"; + +my $ver; + +open IN, "configure.ac" or die; +while (<IN>) { + if (m/^[^\#]*AC_INIT\s*\(\s*\[\s*zbar\s*\]\s*,\s*\[(\d+[\.\d]+)/) { + $ver=$1; + last; + } +} +close IN or die; + +die if (!$ver); + +sub gen_version() { + print "Generating release for version $ver\n"; + + open IN, "ChangeLog" or return "error opening ChangeLog"; + open OUT, ">$body_path" or return "error creating $body_path"; + my $start=1; + while (<IN>) { + if ($start) { + print OUT $_; + $start = 0; + next; + } + last if (m/^\S/); + print OUT $_ or return "error writing to $body_path"; + } + close OUT or return "error closing $body_path"; + + return ""; +} + +my $ret = gen_version(); + +die($ret) if ($ret ne ""); diff --git a/.github/workflows/on_release.yml b/.github/workflows/on_release.yml new file mode 100644 index 0000000..218119c --- /dev/null +++ b/.github/workflows/on_release.yml @@ -0,0 +1,149 @@ +name: Upload binaries to release + +on: + release: + types: + - created + - published + - edited + + workflow_dispatch: + +jobs: + Ubuntu: + name: Build for Ubuntu 20.04 + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake autopoint autotools-dev \ + gettext libdbus-1-dev pkg-config xmlto \ + devscripts build-essential lintian \ + debhelper-compat \ + dh-exec libdbus-1-dev \ + libmagick++-dev libv4l-dev python3-dev \ + libgtk-3-dev lftp \ + dh-sequence-python3 libgtk2.0-dev \ + libqt5x11extras5-dev qtbase5-dev + - name: build + run: | + .github/workflows/debuilder.sh + - name: generating tarball + run: | + (cd ..; tar cvfz zbar-ubuntu-20.04.tar.gz *deb) + + - name: upload + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: mchehab/upload-release-asset@v1.0.3 + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ../zbar-ubuntu-20.04.tar.gz + asset_name: zbar-ubuntu-20.04.tar.gz + asset_content_type: application/gzip + + Mac_OS: + name: Build for Mac OS + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: prepare + run: | + brew install gettext autoconf automake libiconv libtool \ + gs graphicsmagick python xmlto pkg-config ccache + + brew unlink libtool && brew link libtool + brew unlink gettext && brew link gettext + - name: configure + run: + autoreconf -vfi + + ./configure --disable-video --disable-nls --with-python=python3 + + - name: build + run: | + make + DESTDIR=${PWD}/macos make install + - name: generating tarball + run: | + DIR="$PWD" + tar c -C ${PWD}/macos -f ${DIR}/zbar-macos.tar.gz -z . + - name: upload + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: mchehab/upload-release-asset@v1.0.3 + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./zbar-macos.tar.gz + asset_name: zbar-macos.tar.gz + asset_content_type: application/gzip + + Windows: + name: Build for Windows + runs-on: windows-latest + strategy: + matrix: + arch: [x86_64, i686] + video: [VfW, DShow] + include: + - arch: x86_64 + msystem: MINGW64 + grep: x86-64 + - arch: i686 + msystem: MINGW32 + grep: 386 + - video: DShow + extra: --with-directshow + - video: VfW + extra: + defaults: + run: + shell: msys2 {0} + + env: + CPPFLAGS: -D__USE_MINGW_ANSI_STDIO=1 + + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + update: false + install: >- + mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-iconv + mingw-w64-${{ matrix.arch }}-imagemagick + mingw-w64-${{ matrix.arch }}-python + base-devel git xmlto zip + autoconf libtool automake gettext make autoconf-archive pkg-config + + - uses: actions/checkout@v2 + + - name: configure + shell: msys2 {0} + run: | + autoreconf -vfi + ./configure LDFLAGS="-static" \ + --enable-pthread --disable-dependency-tracking \ + ${{ matrix.extra }} + + - name: build + shell: msys2 {0} + run: + make + - name: generating zip + shell: msys2 {0} + run: | + DESTDIR=${PWD}/win_${{ matrix.arch }}-${{ matrix.video }} make install + (cd ${PWD}/win_${{ matrix.arch }}-${{ matrix.video }}; zip -r ../zbar-win_${{ matrix.arch }}-${{ matrix.video }}.zip .) + + - name: upload + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: mchehab/upload-release-asset@v1.0.3 + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./zbar-win_${{ matrix.arch }}-${{ matrix.video }}.zip + asset_name: zbar-win_${{ matrix.arch }}-${{ matrix.video }}.zip + asset_content_type: application/gzip diff --git a/.github/workflows/on_tag.yml b/.github/workflows/on_tag.yml new file mode 100644 index 0000000..b1d48fe --- /dev/null +++ b/.github/workflows/on_tag.yml @@ -0,0 +1,31 @@ +name: Create release on tag + +on: + workflow_dispatch: + push: + # Sequence of patterns matched against refs/tags + tags: + - '[0-9]+*' + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - uses: actions/checkout@v2 + - name: Release changelog + run: .github/workflows/gen_release.pl body_file.tmp + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + body_path: body_file.tmp + draft: false + prerelease: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51276f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +*.la +*.lo +*.o +*~ + +.deps/ +.dirstamp +.libs/ +build/ + +ABOUT-NLS +aclocal.m4 +autom4te.cache/ +config/ +config.log +config.status +configure +doc/doxygen.conf +doc/man/ +doc/reldate.xml +doc/version.xml +gtk/zbarmarshal.c +gtk/zbarmarshal.h +gtk/ZBar-1.0.gir +gtk/ZBar-1.0.typelib +include/config.h +include/config.h.in +include/stamp-h1 +iphone/build +iphone/zbar.xcodeproj/project.xcworkspace +iphone/zbar.xcodeproj/xcuserdata +java/net/sourceforge/zbar/*.class +java/zbar.jar +java/zbarjni.h +libtool +Makefile +Makefile.in +po/*.gmo +po/*.mo +po/Makefile.in.in +po/Makevars.template +po/POTFILES +po/Rules-quot +po/boldquot.sed +po/en@boldquot.header +po/en@quot.header +po/insert-header.sin +po/quot.sed +po/remove-potcdate.sed +po/remove-potcdate.sin +po/stamp-po +po/zbar.pot +pygtk/zbarpygtk.c +pygtk/zbarpygtk.defs +qt/moc_*.cpp +test/check_dbus.sh +test/test_examples.sh +test/test_convert +test/test_cpp +test/test_cpp_img +test/test_dbus +test/test_decode +test/test_jpeg +test/test_proc +test/test_video +zbarcam/moc_*.h +zbarcam/zbarcam +zbarcam/zbarcam-gtk +zbarcam/zbarcam-qt +zbarimg/zbarimg +zbar*.pc +zbar-*.tar.bz2 +zbar-*.tar.gz diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..f8b9026 --- /dev/null +++ b/.hgignore @@ -0,0 +1,36 @@ +syntax: glob + +*~ +Makefile +Makefile.in +aclocal.m4 +configure +config +config.guess +config/config.h +config.h.in +config.log +config.status +config.sub +depcomp +compile +install-sh +libtool +ltmain.sh +autom4te.cache +missing +python/MANIFEST +build +*.xcodeproj/*.mode1v3 +*.xcodeproj/*.pbxuser +xcuserdata +contents.xcworkspacedata +.DS_Store +android/obj +*.class +*.dex +android/libs +android/examples/*/libs +android/*.zip +android/local.properties +android/examples/*/local.properties @@ -0,0 +1,29 @@ +20d75a548cf6daee2e590683ee2166d6af24e94f 0.5 +590b1d022f92b67f1bcd52b3c2810682285a5f6b 0.7 +6cdb3f2ec455b6abb747adfe236c619c6c0ee1ef 0.2 +c6178a9dde5527c0ff59e8e7e3e04f9057e0c3a2 0.3 +c7ff4e3a6f6d8e9ccdebece0ff276db5cecf34fd 0.6 +f68c5b088507f424840f8efa4077aac37c5390cd 0.4 +fe6f4843de209522918690dc741fd7fff442ef6d 0.1 +f0aa6bf0cab7fda8725ce0da35d3e69473269c71 0.8 +38265c7b4ad4dfafe7b5e9eae4bb0d43d68b4143 0.9 +2dc2d73c468a032edc68a127e9ddbbfe5f520777 0.9 +f3670ba47f2e070f4c43e97f4de3200903720e73 0.10 +f3670ba47f2e070f4c43e97f4de3200903720e73 0.10 +563557a923d0e23e36c81878d660aefc1c0fe3e7 0.10 +b21098d27ff88c4f1a529fe0762f1cb307e04399 iPhoneSDK-0.1 +b0bda6247b2fe4ffb52db89a776f91244f25d897 iPhoneSDK-0.1.1 +d1655a680c3df68b3466d3b2c714f30af7223ec6 iPhoneSDK-0.1.2 +4556c996acaaa6fcd5e8e9879f410f8120746e2b iPhoneSDK-1.0 +12de07e51dce0d1cfc3e9699bbf9d5c8774a5371 iPhoneSDK-1.0.1 +12de07e51dce0d1cfc3e9699bbf9d5c8774a5371 iPhoneSDK-1.0.1 +2541a66ae7d64fbd923168ae677abb156021e88d iPhoneSDK-1.0.1 +5e5060b563a1878ea297af5a50261d14cf1cb449 iPhoneSDK-1.1 +f9e46865dbcebd04683ca7592ab32aeda615e772 iPhoneSDK-1.1.1 +801318a6195060c31259da1f076ecc9a609616cc iPhoneSDK-1.1.2 +4bbb4e2bdba8c41a282df7e65a02797250f44daa iPhoneSDK-1.1.3 +1ad84c3c03252c366a82552fa952674ec593c73f iPhoneSDK-1.2 +9e7bad13ae6a4b65e0f0964512f96284d37f0dc8 iPhoneSDK-1.2.1 +146b857ff41ae462cc52f8dddf46d867763433a1 iPhoneSDK-1.2.2 +9b946a7ead73ac99e121de2e7f495213cda88e6f AndroidSDK-0.1 +5eb3c8786845fe3ad2c74f6a660e70176b88347f iPhoneSDK-1.3.1 @@ -0,0 +1,27 @@ +The ZBar Bar Code Reader is Copyright (C) 2007-2011 Jeff Brown + <spadix@users.sourceforge.net> +The QR Code reader is Copyright (C) 1999-2009 Timothy B. Terriberry + <tterribe@xiph.org> + +You can redistribute this library and/or modify it under the terms of the GNU + Lesser General Public License as published by the Free Software Foundation; + either version 2.1 of the License, or (at your option) any later version. + +This library 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 General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along + with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +ISAAC is based on the public domain implementation by Robert J. Jenkins Jr., + and is itself public domain. + +Portions of the bit stream reader are copyright (C) The Xiph.Org Foundation + 1994-2008, and are licensed under a BSD-style license. + +The Reed-Solomon decoder is derived from an implementation (C) 1991-1995 Henry + Minsky (hqm@ua.com, hqm@ai.mit.edu), and is licensed under the LGPL with + permission. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..1ecbdee --- /dev/null +++ b/ChangeLog @@ -0,0 +1,787 @@ +0.23.93: + * Set a better dpi resolution when parsing PDF files + * Fix memory recycle bug of empty symbols + * Fix compilation with python 3.11 and 3.12 + * CVE-2023-40889: Fix array out-of-bounds access + * Stop ignoring non-binary entries that follow binary ones + * Increase allocated buffer memory for symbols + * barcodetest.py: fix error code print logic + * convert: Crash fixing while using camera + * Add some pod information for additional functions + * perl skip more tests if DISPLAY not set and set prereqs in Makefile.PL + * Fixes rt.cpan.org 122061 - test fails when DISPLAY not set + * Update Barcode::ZBar + * isaac: ensure proper order of parsing expression + * Enforce x11 backend even on wayland + * zbarimg: add the --polygon option + * xml output: Add polygon containing code bar. + * configure.ac: drop support for Qt4 and prepare for Qt6 support + * win: fix compiling error in Visual studio + * Enforce a coding style + * configure.ac: fix some issues with gtk parameter + * zbargtk: fix version check macros + * zbar: Address some header issues + * zbar, test: fix compilation issues with FreeBSD + * zbar: Function stdcall declaration issue. + * symbol: make it compatible with MSC + * zbar: change the code to make it c90 standard compatible + * test: fix decode test + +0.23.92: + + * Added a --enable-static option to make easier to distribute + Windows binaries + +0.23.90: + * Started using github actions for CI and binary releases + * Fixed several issues with configure.ac, making it auto-detect + most things, when possible + * README.md now shows the absolute minimum requirement for + building ZBar on Ubuntu + * Fixed some build issues + * Make it compatible with Python 3.9 + * Fixed some Python 3.9 and Qt5 warnings + * Typo fixes + * Several fixes at zbarcam + * zbarimg: fix stderr output when symbols are found + +0.23.1: + + * Add i18n translation support and add a pt_BR translation + * Change default to autodetect python and gtk versions + * Improve tests and builds on travisCI + * Some documentation updates + * Add support for binary data extraction + * Add support for raw decoding without charset conversions + * Add one shot scanning mode + * Improved OSX and iphone support + * Fix help messages + * Fix some makefile issues + * Fix error detection in video4linux read + * Fix pkgconfig for zbar-qt5 + * Fix a double-free condition + +0.23: + + * Windows: added support for DirectShow + * Text files at main dir converted to Markdown notation + * HACKING.md text now reflects the procedures we use since 0.20 + * ZBar's URL locations updated on several places + * Added support for using Gtk3 with zbarcam-gtk + * Added support for using GObject Integration (GIR) with pygobject-3.0 + * Added support for Python3 bindings + * Python scripts now runs with either python2 or python3 + * added Travis checks for Gtk3 with Python3 and GIR + * added Travis builds for cross-compilation with winGW + * added Travis builds for Windows native build + * added a test script for checking python bindings + * Added a test script for Python with Gtk support via GIR + * Suppressed gcc warnings when building with Gtk3 + * Got rid of gdk_threads for good at zbarcam-gtk, using an idle hook to + handle async messages + * The debian/ and travis/ directories were removed from distribution files + * Java sources added to the distribution tarball + +0.22.2: + + * Improve some pkg check logic, in order to solve some + ./configure issues + * Fix logic that allows disabling Qt support + * Add support for Java 11 detection + * Fix Java detection logic + * Fix Travis CI breakages due to the usage of Java 11 on Debian Sid + * Fix some issues with MinGW Windows build + * Search for new ImageMagick 7 header location + +0.22.1: + + * Ensure that version.xml and reldate.xml will be placed at the source dir + * Make use of glib thread names + * Windows: Make zbargtk build + * Windows: Use -no-undefined for libzbargtk + * Windows: Disable zbarcam-gtk + * Windows: Check for clock_gettime on pthread library as well + * Windows: Specify correct path to barcode.png + * Windows: Include windows.h for vfw + * Makefile.am: Add missing extra-dist-file + * configure.ac: allow building libzbar-qt as a static library + +0.22: + + * zbarcam-qt: allow changing resolutions in real time + * zbarcam-qt: better support ZBar options + * zbarcam-qt: do several visual improvements + * zbarcam-qt: make it remember the geometry + * zbarcam-qt: allow show/hide control and options bars + * zbarcam-qt: remember lastly used settings and camera controls + * zbarcam-qt: allow changing ZBar decode options via GUI + * Add API to allow get/set resolutions + * img_scanner: add handler for color-inverted codes + * img_scanner: fix get_config parameter validation + * scan_video: improve logic to remove duplicated/invalid devnodes + * symbol.c: fix symbol hash logic (prevents crash with QR options) + * configure.ac: fix an error at libv4l2 package check + * fix some typos + * exit gracefully when decoding split QR codes + +0.21: + + * zbarcam-qt: allow selecting codes via GUI interface + * When both enabled, ISBN-13 has precedence over ISBN-10 + * ZBar is now using Travis CI for continuous integration tests + * Convert INSTALL and README to markdown and update them + * Improve ZBar testing code and make easier to run the tests + * Fix build with Clang + * Add simple dbus IPC API to zbarcam. + * zbarimg: display only the compiled symbologies + * v4l2: make ZBar build and run on Kernels < 3.16 + * configure.ac: The pdf417 code is incomplete. Warn about that + * Add Debian packaging and Travis CI configuration + * Add barcode examples for different supported symbologies + * Several improvements at the building system + * Add support for SQ code symbology + * v4l2: add fallback for systems without v4l2_ext_controls which field + * v4l2: use device_caps instead of capabilities + * v4l2: make v4l2_request_buffers() more generic + * release video buffers after probing and request them again when needed + * Ignore ENOTTY errors when calling VIDIOC_S_CROP + * doc/Makefile.am.inc: clean html generated files + * Add --disable-doc configure option to disable building docs + * Fix function prototype to be compatible with recent libjpeg + * Wrap logical not operations into parentheses + * INSTALL: warn that autoconf should be called before configure + * code128: fix error logic + * convert: ensure that it will not use a freed value + * zbar: use g_thread_new() instead of g_tread_create() + * zbargtk: add a missing break + * gtk/zbargtk: add a missing check if zbar->window is not null + +0.20.1: + * Be sure to use python2, as /usr/bin/python is being removed + (or made non-functional) on some distributions + * Prefer using pygobject-codegen-2.0 instead of pygtk-codegen-2.0 + * Make it work with modern versions of python 2 + +0.20: + * As upstream became abandoned, created a ZBar fork at linuxtv.org + * Use libv4l2 for V4L2 support, using emulated formats as last resort + * Add support for Qt5 + * Add zbarcam-qt and zbarcam-gtk (from the example codes) + * Add support for v4l2 controls + * Add the needed GUI bits for zbarcam-qt to work with controls + * Fix compilation issues with newer automake versions + +0.11: + * Codabar reliability enhancements + - fix missing check + - require minimum quality + - bump default uncertainty + * tweak Codabar bar/space ratio validation + * finish Codabar support for python, perl, java and iPhone interfaces + - reduce Codabar uncertainty to minimum + * add core support for Codabar symbology + - TBD: python, perl, java and iPhone interfaces + * fix v4l config and build variations (bug #3348888) + - thanks to jarekczek for reporting this! + - NB v4l1 removed from kernel as of 2.6.38 + * fix missing python thread initialization (bug #3349199) + - thanks to jarekczek for reporting this problem! + * fix missing configure check for Python.h (bug #3092663) + - thanks to Zoltan Kovacs for reporting this problem! + * fix C++ wrapper missing Symbol.quality (bug #3076524) + - thanks to Rudy Melli for pointing this out! + * fix C++ wrapper bugs (bug #3396068) + - thanks to anotheruser1 for reporting this! + - add new C++ wrapper test + * fix avoid compositing ISBN10 data + * add support for GraphicsMagick as ImageMagick alternative + * mention xmlto in HACKING (patch #3297039) + - thanks to jarekczek for the patch! + * disable addons by default until synchronization with main symbol is working + * fix image scanner min quality filtering + * fix i25 buffer overrun corner case + * fix EAN addon enables + * fix zbarimg to accept autodetected stdin (lone dash) + * fix Qt 4.6.3 compilation error (patch #3178244) + - thanks to hrhristov for the patch! + * add Python Processor support for request_size interface + * fix Python Processor support for GIL, allowing asynchronous scanning + * fix jpeg decoder skip handling + - thanks to jarekczek for the patch! + * rename dprintf macro to avoid conflicts (patch #3128538) + - thanks to maurochehab for the patch! + * add support for EAN/UPC 2 and 5 digit add-on symbols + - deprecate original, unfinished add-on APIs + - add self-checking to test_decode + * fix support for GS1 AIs + - thanks to jockusch for a patch! + - add decoder/symbol "modifier" flags and config read access + - set flags or emit GS appropriately for Code 128 FNC1 + - add iphone, java, perl, python bindings for modifiers and configs + * add support for Code 93 symbology + * add video size request to (Py)GTK widget (req #3034522) + - thanks to Jerome Charaoui for the patch! + * add support for GS1 DataBar Expanded (RSS Expanded) symbology + * add language bindings for DataBar + * add preliminary support for GS1 DataBar (RSS-14) symbology + * enhance decoder reliability (EAN, Code 39, Code 128) + - enhance decoder test + * fix documentation of command exit codes (bug #3017322) + * fix C++ video callback bug + - add C and C++ processor examples + * add per-symbology cache consistency + - reliable symbologies decode immediately by default + - no more need to disable cache with video + - also fix crop bugs w/video scanning + * add support for scanning partial images (cropping) + - update c++, python, perl, java bindings and tests + * fix couple of leaks + * remove doc hints about GFDL (bug #3004411) + - apply LGPL to API docs + * relax Code 39 width ratio checks + * core library portability improvements + - remove some unnecessary C99 syntax + - add configure checks for errno + - enhance C++ Symbol interface + * adjust Python module README (add examples and note DLL in path) + * fix QR Code missing from man pages (bug #2969857) + * cleanup decoder assertions and locking (bug #2966916) + * add Java interface to library via JNI + - add Java tools and JNI build tests to configure + - fix compiler warnings from binary output changes + * fix output of binary data in zbarimg and zbarcam + - thanks to fukuchi for a patch! + - add base64 format for binary xml output + * add coarse symbol orientation information (patch #2913094) + - thanks to Anssi for a patch! + - add decode direction feedback to decoder + - update C++, Python, Perl and ObjC wrappers + - add orientation to test suites + * fix inconsistent fourcc endian handling bugs (bug #2918994) + - thanks to jdccdevel for a patch! + - add fourcc conversion and parse interfaces to zbar.h + * report QR immediately for video (no inter-frame consistency check) + * add python distutils build infrastructure + +version 0.10: + * hotfix add MinGW import lib to Windows installer + * attempt fix for Qt4 < 4.4 + * release updates + - fix perl Processor init without video + * fix window closed notification during events + - add read_one example, fix xs compiler warnings, update perl docs + * add result query interfaces to image scanner and processor + - move result filtering into image scanner (from user) + - abort output blockers when window closed + * Windows updates + - update installer and README for distribution of dependency libraries + - fix applications to use binary output for xml and raw modes + * add regression tests to makefile + * cleanup warnings from newer gcc versions + * fix excessive i25 QZ checks + * add regression script + - add zbarimg xml output for every source (even empty) + - add edge detection to svg debug overlay + * image scanner cleanup and minor performance enhancements + * bug hunt and stability improvements + - fix broken processor locks + - fix X connection polling, revert previous separate thread workaround + - refuse to resize windows larger than screen size + - fix window output scaling - preserve image aspect ratio, adjust overlay + - fix window redraw + - fix crash on Xv image creation failure (still need XImage fallback) + - clean up zbarimg exit cases (last image window close, missing decodes) + * always use separate video thread when threads enabled (even v4l2) + * add configure check for features.h + * overlay enhancements + - add fps to overlay + - add overlay control to processor + - add windows overlay drawing + * tweak linear code position info + * trim deep qrcode hierarchy + * fix zero length symbol data + * fix QR structured append result handling + * cleanup SVG debug dump (partial) + - some QR integration API cleanup + * extract explicit result container, separate from image + - remove (broken/tmp) converted image result sharing + - add explicit symbol recycle API, update processor/widgets to use + - cleanup and update C++ API + - update Python/Perl APIs + - fix broken and add new Python/Perl tests + * cleanup QR result integration + - add hierarchy to symbol results + - extract symbols during text decode, preserving position and structure + - outline QR symbols in window overlay + - tmp fix converted image result propagation + * factor image scanner cache and apply to QR + - fix image scanner handler called once per-image (vs every decode) + * QZ and clustering fixes to QR integration + - remove qr_finder QZ checks + - decrease center clustering threshold from 1/3 to 1/5 of length + - add img_scanner svg debug output + - manually add config.rpath to workaround broken autofoo + * finish initial integration of QR Code reader from maemo-barcode project + * zbar-side updates for QR Code integration + - add linear scanner position interface + - add QR finder position feedback + - integrate QR Code reader with img_scanner + - refactor some symbol/image interaction + - change default scanner density to 1 + - add iconv to build infrastructure + * initial hooks for QR Code, first pass at finder + * fix broken builds with --disable-pthread + +version 0.9: + * hotfix configure check for Wand as well as MagickWand (bug #2848437) + * hotfix trim extraneous MagickWand calls (bug #2848445) + * release updates + * fix uninitialized member in Qt widget (bug #2844846) + * move image conversion out of image scanner up to higher levels + (preparation for library split) + * add symbol quality metric and image scanner position config + - update python, perl and docs + * compatibility fixes + - work around ImageMagick API breakages + - fix some OS X compile issues + * Qt widget cleanup + - handle video device set before window attached + - prevent exceptions from escaping event handlers + * more Qt window creation hook fixes + - NB may still cause problems if video is opened before window is visible + * finish fix for Qt init ordering (bug #2844846) + * potential fix for display init ordering (bug #2844846) + - new workaround for filtering bad EAN results in the image scanner + * more testing, fixes and cleanup + - fix v4l1 + - fix/add null processor interface + * change default image scanner density to 1x1 + - random cleanup, vfw hang, quit key + - fix scan_image example MSVC project + - windows installer tweaks + * add zbarcam to windows installer + * major restructuring to improve platform abstraction + - add lock, thread and timer abstractions + - migrate specific notification locks to platform independent layer + - fixes to vfw capture interface + - fix window format conversion issues + - fix some broken configure checks + - zbarcam working in windows! + * fix symbol leaks (bug #2820658) + - add symbol reference counting + * add support for binary symbol data + * initial VFW video support + - mostly working with uvc driver, broken for others + - factor out common video buffer handling + - fix processor to destroy window *before* video (ref TODO) + - use /dev/video* VFW pseudo-devices + - windows configure skip non-windows checks + - prep for platform refactoring + * fix zbarimg b&w format handling + * fix scan (image) boundary QZ handling (bug #2807538) + - add linear scanner manual flush API + - linear scanner always starts/ends w/a space of width 0 + - remove artificial image scanner border + - decoders special case 0 width space for QZ checks + - add missing Code 128 leading QZ check + * fix Code39 max ICS checks (bug #2807447) + - add decoder lock owner tracking (debug) + - update dbg_scan to match img_scanner + * first pass installer + - add version and icon resources for libzbar, zbarimg + * zbarimg working in windows + - switch to StretchDIBits over DrawDib + - refactor some window drawing code to remove redundancies + - make refcounts thread safe + - clean up alloc/free bugs + * convert zbarimg to C (cross compiled C++ cannot run w/native libraries) + - fix DrawDib image width granularity + - fix window resize dimensions to include decorations + - images still inverted, otherwise zbarimg now "working" in windows + * refactor processor implementation to support cross-platform + - first pass windows processor (before debugging) + - make processor locks reentrant (call APIs from callback) + * initial Windows support for window interface + - currently supports VFW DrawDib interface for image display + (DirectDraw and others TBD) + - also basic processor window creation/manipulation + - Windows configure tests + +version 0.8: + * release updates + * add "raw" output option (without symbology prefix) to apps (req #2671863) + * fix Code 39 min length and document min/max configs (bug #2787925) + * fix zbar_image_free_data windows recursion loop (bug #2796277) + * fix uninitialized decoder configs (bug #2800163) + * switchover from subversion to mercurial + +version 0.7: + * fix perl MANIFEST + * release updates (version, NEWS, packaging) + * adjust [py]gtk distributed files + * draw new logo (rough, no Xrender yet) + * fix Makefile.am syntax error + * fixup some perl distribution details + * project name change: everything "zebra" becomes "zbar" + * remove old logo + * add first pass python bindings! + * fix perl mortality bug + * add new cfg constants to perl + * fix perl doc handler ref + * fix processor set_active error propagation + * add wiki examples (scan_image.*, processor.*) + * add missing trailing quiet zone checks for ean and code39 + * add min/max code length cfg/check for i25,code128,code39,pdf417 + * add image scan density API/option + * tweak option parser to be more strict/correct about abbreviations + * add API to force specific video io modes (req #2293955) + * apply patches for more broken driver workarounds (req #2293955) + * fix(?) C++ string to fourcc conversion + * add missing C++ wrappers + * add additional examples to man pages (symbology enable/disable) + * add missing options to man page synopsis + * add missing --xml option to man pages + +version 0.6: + * hotfix broken perl install (name change) + * add missing files to distribution + * release updates (version, NEWS, pacakging) + * rename perl module to Barcode::Zebra (fit with existing cpan namespace) + * add perl documentation and example + * add v4l version debug/test override + * add docs for new zebracam prescale option + * add video pre-scaling API/option to video/processor/zebracam (req #2277340) + * add few missing APIs to perl xs wrapper + * fix missing libjpeg #ifdef in convert + * initial support for decoding jpeg images using libjpeg! + * workaround broken v4l2 drivers in USERPTR mode + * have configure double check Magick++ install (bug #2582232) + * update README dependency list + * fix C++ warnings in Processor + * fixes for building DLLs with libtool under MinGW + * automatically remove "processor" layer if poll.h is unavailable + * test_decode portability workarounds + * add config/compile time symbology enables + * add low-level PDF417 decode stage - converts widths to codewords + * add XML output option to zebracam and zebraimg + * add sequence number image attribute, set to frame number by video + * change v4l2 interlaced only drivers to warning instead of hard fail + * workaround broken drivers that return error for V4L2_FIELD_NONE request + * add some initial PDF417 hooks + * first pass perl bindings for Processor, Scanner and Decoder + * fix error propagation double free bug + * add missing APIs: processor userdata and image data length + * fix configure check for v4l2 - thanks to Simon Matter for the patch! + * finish support for UPC-E + * fix zebraimg to scan all pages/frames of a multi-frame input + * fix debian packaging dependencies (bug #2070164) + * *remove* debian directory from distribution (bug #2070164) + * fix inappropriately installed headers and man pages (bug #2055133) + * fix pygtk multiple definition link errors on darwin (bug #2052681) + * fixes to configure for detecting python settings (bug #2052663) + * remove zebrapygtk module link against libpython (bug #2052663) + * add drag and drop support for *images* to Qt widget...unfortunately not + very useful; every application i tried drops uri-refs rather than images + * minor reference documentation updates + +version 0.5: + * release updates (version, NEWS, packaging) + * add pkg-config files + * update to latest libtool and new autoconf macros + * cleanup library symbol exports + * remove test programs using internal hooks + * improve portability of some format specifiers + * fix missing stub for --without-x - thanks to Simon Schmeisser for a patch! + * fix --disable-pthread compile errors and warnings + * fix XImage size mismatch background artifacts + * fix new generated file distribution errors + * switch Qt headers to old-style names (investigate possible Qt3 support?) + * add independent ABI versioning for GTK and Qt widget libraries + * reimplement widget internals to support image scanning and + improve locking efficiency + * add image scanning to widgets. including builtin conversions from + toolkit image types: GtkPixbuf and QImage + * add video opened hooks to widgets (improved use model) + * add logo, used when there is nothing better to draw + * add userdata to image object + * fix image reuse cleanup bug + * fix format specifiers in some error messages + * enhance widget tests to support enable/disable and scan from image + * fix broken deallocation assumptions in test_qt + * widget API documentation (still need to hookup gtkdoc, and PyGtk docs) + * API documentation toplevel overview + * update configure summary for new features + * replace all decoder assertions w/non-fatal debug spew (bug #1986478) + * fix glib-genmarshal check + * add first pass of Qt widget! + - test/example in test/test_qt.cpp + - factor video device scan to share among tests + * more C++ integration fixes + - additional Image ref tweaks + - add Video.close() and Window.clear() APIs + * fix missing image scanner handler call + * add dereference operator to C++ Symbol + * add count attribute to C++ Symbol + * fix broken C++ header definitions + * fix broken C++ Image references + * expose internal reference counting interface + * fix window locking bug + * cleanup some minor memory leaks + * convert Code 128 assertions to non-fatal warning spew + * fix single buffer video hang (bug #1984543) + * replace inferred video size assertion with warning message (bug #1984543) + * add first pass of GTK widget! + * add PyGTK widget wrapper + * API change: modify window draw/redraw interface to improve interoperability + with toolkits + - add window locking for thread safety + - zebra_window_draw() no longer actually "draws" anything + => use window.draw to update the image from video thread + then schedule window.redraw from toolkit GUI thread + * fix missing C++ std lib dependencies + * fix uninitialized handler/userdata bug in decoder + * fix broken Code 128 checksum assertion + * fix video destructor segfault + * fix window destructor Xvideo errors (XID is unsigned...) + * switch configure to use pkg-config to find most dependencies + * API documentation updates + +version 0.4: + * release updates (version, NEWS, packaging, examples) + * couple of portability tweaks + * finish format conversion resize cases + * add missing conversions + * fix some broken conversions + * fix some broken redraw and Xv checks + * add decoder configuration API + - only boolean configs currently implemented + - integrate config option parsing w/zebracam and zebraimg + - add config to enable/disable each symbology type + - add optional conversions from EAN-13 to UPC-A, ISBN-10 and ISBN-13 + (disabled by default) + - add config to emit/suppress check digit + NB behavior change! check digit is now emitted by default + * related documentation updates + - split common options to a separate entity + * fallback to gettimeofday when POSIX timers are not available + * image format conversion fixes + - fix format size roundoff (NB now rounds *down*) + - add convert and resize API to pad/crop image to specific size + (eg, to handle Xv min/max image size) + NB this is still not implemented for many conversions + * fix window deletion visibility inconsistency + * add couple processor commands + - 'q' to delete window + - 'd' to dump displayed image for debug + * remove problematic includes used for v4l2 configure test + * address compiler portability concerns w/debug print vararg macro + * workaround v4l1_set_format() failed consistency check for broken drivers + - change from error to warning w/more useful message + - calc expected image size using expected value instead of driver value + * add missing example scripts to distribution + * add missing files for Interleaved 2 of 5 + * add support for Interleaved 2 of 5 symbology! + - again no check digit option yet + * increase decode window from 8 to 16 bars + - remove Code 39 "extra bar" hack + - add Code 39 quiet zone check + - facilitate Interleaved 2 of 5 + * optimize character width calculations for all symbologies + * fix image scanner bug w/lost symbols at end of scan passes + * fix EAN-8 buffer overrun case + * add API doc footer + * add API documentation generated by Doxygen + - markup, cleanup and finish writing header comments + - setup Doxygen config file + * add/fix window GC + * add base support for Code 39 (no check digit or full ASCII options yet) + * cleanup decoder locking + * add support for EAN-8! + +version 0.3: + * add interface stub files + * fix wait timeouts + * fix XImage format filtering + * fix several error handling corner cases + * fix c++ error handling + * add missing Window.h + * add better hierarchy to library sources + * build configuration fixes + * portability fixes + * packaging updates + * fix zebracam beeps + * fix some RGB component ordering and XImage format problems + * fix window resize and redraw state problems + * fix EAN testcase in test_decode - thanks to Steffen Kube for the patch! + * add APIs and (hidden) zebracam options to force specific formats for debug + * add example scripts + * documentation updates + * remove implementation of deprecated img_walker + * add XImage formats + - basic support for 16-bit depths + * add some missing rgb format conversions + * add basic overlay + - currently only markers at detected scan locations (needs improved) + * fix memory leak for converted images w/new cleanup semantics + * migrate inter-frame consistency check from old zebracam into image_scanner + - add API to enable/disable/flush result cache + - add API to retrieve cache status from returned symbol + * cleanup user_wait blocking semantics for various state combinations + * fix bug w/v4l1 not unlinking dequeued image + * major restructuring for improved modularity + NB not all changes are are backward compatible! + - modular support for v4l2 falling back to v4l1 + - flexible support for more varied image formats (incomplete) + - added reusable abstractions for: an "image" object and associated + metadata, a "video" input object and a "window" output object + - added new "processor" API to wrap everything together, simplifying + trivial application usage (including zebracam and zebraimg) + - removed deprecated "img_walker" interface, completely replaced by + more integrated "image_scanner" (moving toward more image processing) + - updated/added c++ APIs, including improved iterator interfaces + * removed SDL dependency due to various usage incompatibilities + (most notably the inability to embed video window into a widget) + * cleaned up zebracam and zebraimg command line processing (bug #1838535) + * many useful enhancements thanks to proposal by mhirsch45 (req #1759923) + including: + - v4l2 support + - support for UYVY image format + - zebracam cursor hiding + - zebracam audio mute + - command line video device specification, + * significant error handling and debug improvements + * some associated documentation updates + * various new test programs (slowly working toward more formal test suite) + * add missing xlink namespace to dbg_scan generated output (bug #1764188) + * qualify char types as [un]signed to avoid non-portable C ambiguity + - thanks to mhirsch45@users.sf.net and Colin Leroy for the patches! + (bug #1759914) + * add integrated 2D image scanning API + - automatically ties to internal scanner and decoder + - results reported via new symbol API + - deprecated previous, cumbersome img_walker interface + - uses new simpler/faster and denser grid pattern (FIXME add 45 and 135) + - first step toward more sophisticated image processing + * updated zebraimg to use new ImageScanner API + - add initial decode location tracking + * updated zebracam to use new img_scanner API + - extended cache to track multiple symbols per-image + - add initial decode location tracking + - removed scan grid overlay + * add configure check for ImageMagick version >= 6.3.0 + * configure dumps ImageMagick, SDL and firefox versions for debug + * add NPAPI plugin stub and build infrastructure + * flush zebracam output after each scanned symbol + * integrate RPM packaging spec file - thanks to V�t Hrachov� for the draft! + (patch #1723536) + * finally add HACKING to distribution/install (bug #1698202) + * add extra documentation files to install (README NEWS INSTALL, etc) + * Debian package patch for 0.2 - thanks to V�t Hrachov�: + - add libsdl1.2-dev as a build dependency + - update automake (>= 1:1.10) as a build dependency + - new package + +version 0.2: + * update distribution to include debian packaging + * add consistency checking to zebracam + * add redundant output suppression to zebraimg + * fix couple of Code 128 decode table bugs + * fix reversed Code 128 decode + * add outstanding scanner edge flush to new_scan() + - API change: scanner reset/new_scan() now return scan/decode status + - update zebracam and zebraimg to call this between each walker pass + (interface still needs improvement...) + => improves in scan results for many cases + * fix dbg_scan filename generation so results go in local directory + * continue Code 128 refinement + - finish character set C decode/expansion + - add per-character validation + - resolve scan direction in separate postprocessing pass before handling + ASCII conversion + - add several img_walker passes parallel to major axis (for long symbols) + - add simple character set C example to test_decode + * promote zebraimg images to TrueColor so colored scan pattern always shows + * more dbg_scan tweaks + * significant scanner improvements + - changed "classic" [-1 0 1] first differential kernel to [-1 1] + to improve minimum edge resolution to single pixel elements + => still need to do some more research and validate assumptions + - adaptive thresholding fixes + - adjusted filtering for better edge detection + - separate constants out to defines (FIXME add config API?) + * fix EAN-13 secondary determinant decoding + * dbg_scan tweaks to make annotations smaller/more usable + * add get_color() interface to decoder + * annotated zebraimg scan pattern for marginally useful high-level debug + * random include cleanup + * cleanup 64-bit compile warnings in zebraimg (bug #1712504) + * add first-pass partial support for Code 128 + - separate out more EAN state from shared state + - internal interface changes + - finish dynamic buffer management + - add shared resource locking + - add Code 128 to test_decode program + => still needs additional functionality and plenty of debug + => reading both Code 128 *and* EAN still destabilized + * add diagnostic program test_video to dump more verbose video debug + * incorporate documentation into Debian package + - thanks to V�t Hrachov� for the patch! + * fix VPATH doc builds (requires automake-1.10 and autoconf-2.61) + * build and dist fixes + - suppress documentation rebuilds in distributed sources + * add Debian packaging sources - thanks to V�t Hrachov� for the patch! + * add DocBook template and build infrastructure + * add manpages for zebracam and zebraimg + * add GNU standard options to zebracam and zebraimg + * internal decoder restructuring to support additional symbologies + - separated out 1-D decoder infrastructure into generic internal API + - moved EAN/UPC specific decoding into it's own module + * fix confusing configure behavior which quietly avoided building + targets with missing dependencies(?!) + configure will now fail with a descriptive error message if you + do not have Magick++ and fail to specify --without-imagemagick or + do not have SDL and fail to specify --without-sdl + * add configure summary describing what will be built (req #1698196) + * fix parity encoding in test_decode and add decoded symbol output + * introduce Code 128 symbol type + * increase width of zebra_symbol_type_t to 16 bits + * add HACKING (bug #1698202) + +version 0.1: + * add NEWS and ChangeLog + * fix some config/build issues found on other boxes + * add missing ImageWalker install + * fix scanner runaway threshold calculation bug + * fix zebracam/zebraimg bugs overwriting currently scanning image w/scan + pattern + * add c++ interface to img_walker + * apply ImageWalker to zebraimg + * add decoder soft reset on partial mismatch + * finish basic decoder symbol assembly/reporting + * add decoder symbol checksum verification + * add callback API option to decoder for "async" symbol processing + * add "image walker" library API to trace scan pattern over 2D images + * apply image walker to zebracam (C++/zebraimg scan pattern still TBD) + * add audio feedback to zebracam (still has long latency) + * add zebracam key cmd to dump frame to file (for debugging) + * fixes for decoder/scanner reset/new_scan + * fixes to scanner initialization and algorithm tweaks + * made decoder less sensitive to violated quiet-zone + * apply zebraimg workaround for imagemagick image display bug + * add string names for symbol types to library and API to access them + * add dbg_scan test program for visually debugging image scanner (and + decoder) + * add test_walk for basic image walker sanity/debug + * removed recursive makes in favor of monolithic build + * renamed some makefiles accordingly + * finished some final symbol data construction + * added result callbacks to decoder APIs for data reporting + * zebraimg hooks into callback + * zebracam still seems to "hang" in undecodeable state? + * populate svn with current sources. + * most of the basic functionality is included and working. + * still need to combine final decode data, finish addons, etc (see TODO). + * c++ wrappers are included and tested, but API may need tweaked. + * zebraimg and zebracam basically working but need cleanup/polish. + * need to create some basic documentation... + * initial repository layout diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..67d7f9f --- /dev/null +++ b/HACKING.md @@ -0,0 +1,96 @@ +Downloading ZBar source code +============================ + +When hacking at ZBar, PLEASE send patches against the latest Git tree! + +There are currently 3 mirrors with ZBar: + +LinuxTV: + Cgit GUI interface: + https://git.linuxtv.org/zbar.git/ + Git tree: + git://linuxtv.org/zbar.git + https://git.linuxtv.org/cgit.cgi/zbar.git + http://git.linuxtv.org/cgit.cgi/zbar.git + +Github: + https://github.com/mchehab/zbar.git + +Gitlab: + https://github.com/mchehab/zbar + +You can use git clone to get the latest version from any of the +above repositories. + +If you haven't already, grab the ZBar git repository. For example, to +get it from Github, use: + + git clone https://github.com/mchehab/zbar.git + cd zbar + autoreconf -vfi + +This will generate ./configure and all that other foo you usually get with +a release. You will need to have recent versions of some basic "developer +tools" installed in order for this to work, particularly GNU autotools. +These versions of autotools are known to work (newer versions should also +be fine): + + GNU autoconf 2.61 + GNU automake 1.10.1 + GNU libtool 2.2.6 + GNU gettext 0.18.1.1 + GNU pkg-config 0.25 + xmlto 0.0.20-5 (for docs building) + +All above mentioned tools (except xmlto) must be installed in the same +prefix. Mixing prefixes (e.g. /usr/bin and /usr/local/bin) may lead to +errors in configuration stages. + +Writing descriptions for your patches +===================================== + +Please add a good description to your patch adding why it is needed, +what the patch does and how. This helps us when reviewing your work +when you submit upstream. + +We use a process similar to the Linux Kernel for patch submissions. +In particular, submitted patches should have a developer's certificate +of origin, as described at: + https://linuxtv.org/wiki/index.php/Development:_Submitting_Patches#Developer.27s_Certificate_of_Origin_1.1 + +In practice, please add: + + Signed-off-by: your name <your@email> + +on your patches. + +Submitting patches +================== + +When you're done hacking, please submit your work back upstream. + +If you use Github or Gitlab, you can fork ZBar from it, develop your +patches and then push again to your clone, asking the patch merge using +the GUI. + +Although we prefer if you submit patches via either Github or +Gitlab, you can also submit them via e-mail to: + + linux-media@vger.kernel.org + +If you opt to do so, please place [PATCH ZBar] at the subject of +your e-mails for us to not mix them with patches for the Kernel +or for other media tools. + +To make your patch, run: + + git diff > hacked.patch + +Other things for you to read, in order to know more about how +to submit your work for upstreaming processes in general, that +could be useful for you to prepare yourself on submitting patches +to ZBar: + +- https://linuxtv.org/wiki/index.php/Development:_Submitting_Patches +- http://www.faqs.org/docs/artu/ch19s02.html +- http://www.catb.org/~esr/faqs/smart-questions.html diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..84eee00 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,199 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure` shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile` in each directory of the package. +It may also create one or more `.h` files containing system-dependent +definitions. Finally, it creates a shell script `config.status` that +you can run in the future to recreate the current configuration, a file +`config.cache` that saves the results of its tests to speed up +reconfiguring, and a file `config.log` containing compiler output +(useful mainly for debugging `configure`). + + If you need to do unusual things to compile the package, please try +to figure out how `configure` could check whether to do them, and mail +diffs or instructions to the address given in the `README` so they can +be considered for the next release. If at some point `config.cache` +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in` is used to create `configure` by a program +called `autoconf`. You only need `configure.in` if you want to change +it or regenerate `configure` using a newer version of `autoconf`. + +The simplest way to compile this package is: + + 1. `cd` to the directory containing the package's source code and type + `autoreconf -vfi && ./configure` to configure the package for + your system. If you're using `csh` on an old version of System V, + you might need to type `sh ./configure` instead to prevent `csh` + from trying to execute `configure` itself. + + Running `configure` takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make` to compile the package. + + 3. Optionally, type `make check` to run any self-tests that come with + the package. + + 4. Type `make install` to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean`. To also remove the + files that `configure` created (so you can compile the package for + a different kind of computer), type `make distclean`. There is + also a `make maintainer-clean` target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure` script does not know about. You can give `configure` +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env` program, you can do it like this: + + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make` that +supports the `VPATH` variable, such as GNU `make`. `cd` to the +directory where you want the object files and executables to go and run +the `configure` script. `configure` automatically checks for the +source code in the directory that `configure` is in and in `../`. + + If you have to use a `make` that does not supports the `VPATH` +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean` before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install` will install the package's files in +`/usr/local/bin`, `/usr/local/man`, etc. You can specify an +installation prefix other than `/usr/local` by giving `configure` the +option `--prefix=PATH`. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure` the option `--exec-prefix=PATH`, the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH` to specify different values for particular +kinds of files. Run `configure --help` for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure` the +option `--program-prefix=PREFIX` or `--program-suffix=SUFFIX`. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE` options to +`configure`, where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE` options, where PACKAGE +is something like `gnu-as` or `x` (for the X Window System). The +`README` should mention any `--enable-` and `--with-` options that the +package recognizes. + + For packages that use the X Window System, `configure` can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure` options `--x-includes=DIR` and +`--x-libraries=DIR` to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure` can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure` can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE` option. TYPE can either be a short name for the system +type, such as `sun4`, or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub` for the possible values of each field. If +`config.sub` isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE` option to select the type of system they will +produce code for and the `--build=TYPE` option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure` scripts to share, +you can create a site shell script called `config.site` that gives +default values for variables like `CC`, `cache_file`, and `prefix`. +`configure` looks for `PREFIX/share/config.site` if it exists, then +`PREFIX/etc/config.site` if it exists. Or, you can set the +`CONFIG_SITE` environment variable to the location of the site script. +A warning: not all `configure` scripts look for a site script. + +Operation Controls +================== + + `configure` recognizes the following options to control how it +operates. + +`--cache-file=FILE` + Use and save the results of the tests in FILE instead of + `./config.cache`. Set FILE to `/dev/null` to disable caching, for + debugging `configure`. + +`--help` + Print a summary of the options to `configure`, and exit. + +`--quiet` +`--silent` +`-q` + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null` (any error + messages will still be shown). + +`--srcdir=DIR` + Look for the package's source code in directory DIR. Usually + `configure` can determine that directory automatically. + +`--version` + Print the version of Autoconf used to generate the `configure` + script, and exit. + +`configure` also accepts some other, not widely useful, options. + +Debian/Ubuntu package build +=========================== + +For Debian/Ubuntu, one alternative way to build ZBar is by using +pbuilder. In order to install pbuilder, see, for example: + + https://wiki.ubuntu.com/PbuilderHowto + +Once you have pbuilder installed and configured, you +can build a ZBar package, running the following commands as +root: + + # pbuilder create --basetgz /var/cache/pbuilder/base-test.tgz + # pbuilder build --basetgz /var/cache/pbuilder/base-test.tgz ../zbar_0.20.2.dsc diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..ec032ef --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,503 @@ +### GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public Licenses +are intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there +is no warranty for the free library. Also, if the library is modified +by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's +reputation will not be affected by problems that might be introduced +by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using a +shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). Each +licensee is addressed as "you". + +A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does and +what the program that uses the Library does. + +**1.** You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Library or any +portion of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +- **a)** The modified work must itself be a software library. +- **b)** You must cause the files modified to carry prominent + notices stating that you changed the files and the date of + any change. +- **c)** You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. +- **d)** If a facility in the modified Library refers to a function + or a table of data to be supplied by an application program that + uses the facility, other than as an argument passed when the + facility is invoked, then you must make a good faith effort to + ensure that, in the event an application does not supply such + function or table, the facility still operates, and performs + whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of + the application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may opt to apply the terms of the ordinary GNU General +Public License instead of this License to a given copy of the Library. +To do this, you must alter all the notices that refer to this License, +so that they refer to the ordinary GNU General Public License, version +2, instead of to this License. (If a newer version than version 2 of +the ordinary GNU General Public License has appeared, then you can +specify that version instead if you wish.) Do not make any other +change in these notices. + +Once this change is made in a given copy, it is irreversible for that +copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the +Library into a program that is not a library. + +**4.** You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from +a designated place, then offering equivalent access to copy the source +code from the same place satisfies the requirement to distribute the +source code, even though third parties are not compelled to copy the +source along with the object code. + +**5.** A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a work, +in isolation, is not a derivative work of the Library, and therefore +falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section +6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure +layouts and accessors, and small macros and small inline functions +(ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. +(Executables containing this object code plus portions of the Library +will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +**6.** As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a work +containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of +the work for the customer's own use and reverse engineering for +debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +- **a)** Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood that + the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) +- **b)** Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (1) uses at run time + a copy of the library already present on the user's computer + system, rather than copying library functions into the executable, + and (2) will operate properly with a modified version of the + library, if the user installs one, as long as the modified version + is interface-compatible with the version that the work was + made with. +- **c)** Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. +- **d)** If distribution of the work is made by offering access to + copy from a designated place, offer equivalent access to copy the + above specified materials from the same place. +- **e)** Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +**7.** You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +- **a)** Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other + library facilities. This must be distributed under the terms of + the Sections above. +- **b)** Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + +**8.** You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +**9.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +**10.** Each time you redistribute the Library (or any work based on +the Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +**11.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this +License, they do not excuse you from the conditions of this License. +If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Library at all. For +example, if a patent license would not permit royalty-free +redistribution of the Library by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**12.** If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**13.** The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. Such +new versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +**14.** If you wish to incorporate parts of the Library into other +free programs whose distribution conditions are incompatible with +these, write to the author to ask for permission. For software which +is copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +**NO WARRANTY** + +**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +### END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It +is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; 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. + + This library 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper +mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in + the library `Frob' (a library for tweaking knobs) written + by James Random Hacker. + + signature of Ty Coon, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it!
\ No newline at end of file diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..cb3c0f9 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,149 @@ +ACLOCAL_AMFLAGS = -I config +AM_CFLAGS += -DLOCALEDIR=\"$(localedir)\" +AM_CXXFLAGS += -DLOCALEDIR=\"$(localedir)\" +bin_PROGRAMS = +check_PROGRAMS = +EXTRA_PROGRAMS = +lib_LTLIBRARIES = +pyexec_LTLIBRARIES = +CLEANFILES = +DISTCLEANFILES = \ + ABOUT-NLS \ + po/*.gmo \ + po/*.mo \ + po/Makefile.in.in \ + po/Makevars.template \ + po/POTFILES \ + po/Rules-quot \ + po/boldquot.sed \ + po/en@boldquot.header \ + po/en@quot.header \ + po/insert-header.sin \ + po/quot.sed \ + po/remove-potcdate.sed \ + po/remove-potcdate.sin \ + po/stamp-po \ + po/zbar.pot +MAINTAINERCLEANFILES = +BUILT_SOURCES = +EXTRA_DIST = +PHONY = $(SUBDIRS) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = zbar.pc + +dist_doc_DATA = COPYING HACKING.md INSTALL.md LICENSE.md NEWS.md README.md TODO.md + +include $(srcdir)/include/Makefile.am.inc + +SUBDIRS = zbar + +if USE_NLS + SUBDIRS += po + dist_doc_DATA += ABOUT-NLS +endif + +zbar/libzbar.la: + $(MAKE) -C @builddir@/zbar libzbar.la + +if HAVE_MAGICK +include $(srcdir)/zbarimg/Makefile.am.inc +endif +if HAVE_VIDEO +include $(srcdir)/zbarcam/Makefile.am.inc +endif +if HAVE_PYTHON +include $(srcdir)/python/Makefile.am.inc +endif +if HAVE_GTK +SUBDIRS += gtk +pkgconfig_DATA += zbar-gtk.pc + +gtk/libzbargtk.la: + $(MAKE) -C @builddir@/gtk libzbargtk.la + +gtk/zbarmarshal.h: + $(MAKE) -C @builddir@/gtk zbarmarshal.h + +gtk/ZBar-1.0.typelib: + $(MAKE) -C $(srcdir)/gtk ZBar-1.0.typelib + +if HAVE_PYGTK2 +include $(srcdir)/pygtk/Makefile.am.inc +endif +endif +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 +include $(srcdir)/test/Makefile.am.inc +if HAVE_DOC +include $(srcdir)/doc/Makefile.am.inc +endif + +if HAVE_DBUS +dbusconfdir = @DBUS_CONFDIR@ +dbusconf_DATA = $(srcdir)/dbus/org.linuxtv.Zbar.conf +endif + +EXTRA_DIST += zbar.ico zbar.nsi zbar-qt5.pc.in \ + dbus/org.linuxtv.Zbar.conf + +EXTRA_DIST += examples/*.png examples/sha1sum \ + 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 \ + perl/ZBar.pm perl/inc/Devel/CheckLib.pm perl/ZBar/Image.pod \ + perl/ZBar/ImageScanner.pod perl/ZBar/Processor.pod perl/ZBar/Symbol.pod \ + perl/examples/paginate.pl perl/examples/processor.pl \ + perl/examples/read_one.pl perl/examples/scan_image.pl \ + perl/t/barcode.png perl/t/ZBar.t perl/t/Decoder.t perl/t/Image.t \ + perl/t/Processor.t perl/t/Scanner.t perl/t/pod.t perl/t/pod-coverage.t + +if WIN32 +dist_doc_DATA += README-windows.md +pkgdata_DATA = $(srcdir)/python/test/barcode.png \ + $(srcdir)/examples/scan_image.cpp $(srcdir)/examples/scan_image.vcproj + +%-rc.o: %.rc + $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< +%-rc.lo: %.rc + $(LIBTOOL) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< + +# install to tmp dest and run NSIS to generate installer +dist-nsis: html-local + test ! -e _nsis || test -d _nsis && rm -rf _nsis + mkdir _nsis + tmpinst=`cd _nsis && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR=$$tmpinst prefix=/ install + cp zbar/.libs/libzbar-0.dll.def _nsis/lib/libzbar-0.def + cp -r doc/html _nsis/share/doc/zbar/ + $(WINEXEC) lib.exe /machine:x86 /def:_nsis/lib/libzbar-0.def /out:_nsis/lib/libzbar-0.lib + cd _nsis && \ + makensis -NOCD -V2 -DVERSION=$(VERSION) $(builddir)/zbar.nsi + @ls -l _nsis/zbar-$(VERSION)-setup.exe + +PHONY += dist-nsis +endif + +SUBDIRS += . + +archive: + git archive --format=tar --prefix=zbar-$(VERSION)/ -o zbar-$(VERSION).tar $(VERSION) + bzip2 zbar-$(VERSION).tar + +.PHONY : $(PHONY) archive + +dist-hook: + rm -f $(distdir)/debian $(distdir)/travis @@ -0,0 +1,157 @@ +ZBar Barcode Reader News +======================== + +Version 0.23 +============ + + Update ZBar for it to work with updated library versions, making it + compatible with either Gtk2 or Gtk3 and either Python2 or Python3. + As part of the new port, it is now possible to use ZBar Gtk bindings + not only with python2/python3, but also on other languages, as it now + uses GObject Introspection- GIR, with is a method to allow using libraries + developed on one language on others. Several languages support it. + On Windows side, support for DirectShow was added. + +Version 0.22.2 (2019-04-29) +=========================== + + Some fixes to another set of Windows issues and to support Java 11. + +Version 0.22.1 (2019-04-25) +=========================== + + Some fixes to allow building ZBar on Windows with MinGw. + +Version 0.22 (2019-02-20) +========================= + + Lots of improvements at zbarcam-qt, allowing it to fully configure the + decoders that will be used, and the options that will be used at the + decoders. Some improvements at the image scanner logic and plugin + selection. + +Version 0.21 (2019-02-12) +========================= + + Added support for SQ code, and the ability of compiling ZBar with the + LLVM/Clang compiler. Several bugs fixes and enhancements are also found + in this release; existing users are encouraged to upgrade. + +Version 0.20.1 (2018-08-08) +=========================== + + Minor changes at ZBar, in order to adapt to modern distributions + with are removing /usr/bin/python in favor of just using python2. + Also, updated some python2 scripts to work with modern distros, + where the Image module is now inside PIL. + +Version 0.20 (2017-04-11) +========================= + + As upstream didn't have any version since 2009, created a ZBar fork at + linuxtv.org. This release improves a lot V4L2 support, by using libv4l2 + to handle formats that are alien to ZBar, making it compatible with a lot + more webcam models. Qt support was also updated, making it compatible + with Qt5. ZBar now have two other GUI applications (zbarcam-qt and + zbarcam-gtk). With zbarcam-qt, it is now possible to adjust the camera + controls, making easier to read barcodes using a camera. + +Version 0.10 (2009-10-23) +========================= + + ZBar goes 2D! This release introduces support for QR Code, developed + by our new project member, Timothy Terriberry. Timothy is an image + processing master, providing this sophisticated functionality using + only integer arithmetic and maintaining an extremely small image + size. Feel free to drop in on #zbar over at freenode to welcome + Timothy (aka derf) to the project and congratulate him on his awesome + work. + + Several bugs fixes and enhancements are also found in this release; + existing users are encouraged to upgrade. + +Version 0.9 (2009-08-31) +======================== + + Introducing ZBar for Windows! So far we only have straight ports of + the command line applications, but work on a cross-platform GUI has + already begun. This release also has a few scanner/decoder + enhancements and fixes several bugs. Many internal changes are + represented, so please open a support request if you experience any + problems. + +Version 0.8 (2009-06-05) +======================== + + This is a bugfix release just to clean up a few issues found in the + last release. Existing users are encouraged to upgrade to pick up + these fixes. + +Version 0.7 (2009-04-21) +======================== + + Welcome to ZBar! In addition to finalizing the project name change, + this release adds new bindings for Python and fixes a few bugs with + the Perl interface. The decoder also has several new features and + addresses missing checks that will improve reliability with + excessively noisy images. + +Version 0.6 (2009-02-28) +======================== + + This release fixes many bugs and adds several improvements suggested + by users: support for decoding UPC-E is finished. zebraimg is + now able to scan all pages of a document (such as PDF or TIFF) and + the new XML output includes the page where each barcode is found. + Camera support has been significantly improved, including the + addition of JPEG image formats. Perl bindings make it even easier + to write a document or video scanning application. Finally, we are + now able to offer initial support for building the base library for + Windows! + +Version 0.5 (2008-07-25) +======================== + + Introducing zebra widgets! Prioritized by popular demand, this + release includes fully functional barcode scanning widgets for GTK, + PyGTK, and Qt. Application developers may now seamlessly integrate + barcode reader support with their user interface. + + This release also fixes many bugs; existing users are encouraged to + upgrade. + +Version 0.4 (2008-05-31) +======================== + + new support for EAN-8, Code 39 and Interleaved 2 of 5! + this release also introduces the long awaited decoder configuration + options and fixes several bugs + +Version 0.3 (2008-02-25) +======================== + + this is a beta release of the enhanced internal architecture. + support has been added for version 2 of the video4linux API and many + more image formats. several other feature enhancements and bug + fixes are also included. image scanning is slightly improved for + some images, but the base scan/decode function is relatively + untouched. significant new code is represented in this release + - all bug reports are welcome and will be addressed promptly! + +Version 0.2 (2007-05-16) +======================== + + this release introduces significant enhancements, bug fixes and new + features! basic EAN-13/UPC-A reading has been improved and should + now be much more reliable. by popular request, new support has been + added for decoding Code 128. additionally, the build process was + improved and there is even basic documentation for the application + commands + +Version 0.1 (2007-03-24) +======================== + + first official Zebra release! + supports basic scanning and decoding of EAN-13 and UPC-A symbols + using a webcam (zebracam) or from stored image files (zebraimg). + still need to add support for addons and EAN-8/UPC-E diff --git a/README-windows.md b/README-windows.md new file mode 100644 index 0000000..04e2987 --- /dev/null +++ b/README-windows.md @@ -0,0 +1,225 @@ +ZBAR BAR CODE READER
+====================
+
+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,
+Codabar, Interleaved 2 of 5 and QR Code. Included with the library are basic
+applications for decoding captured bar code images and using a video device
+(e.g. webcam) as a bar code scanner. For application developers, language
+bindings are included for C, C++, Python and Perl as well as GUI widgets for
+Qt, GTK and PyGTK.
+
+Check the ZBar home page for the latest release, mailing lists, etc.
+
+ * <https://github.com/mchehab/zbar>
+
+License information can be found in 'COPYING'.
+
+Once built, the Windows binaries will use binaries of several supporting
+libraries, each one with its own copyright, license and source code locations.
+
+It follows a non-exhaustive list of those components:
+
+ * The GNU libiconv character set conversion library
+
+ Copyright (C) since 1999 Free Software Foundation, Inc.
+
+ Licensed under LGPL. The source code is available from
+
+ * <http://www.gnu.org/software/libiconv>
+
+ * The ImageMagick software imaging library
+
+ Copyright since 1999 ImageMagick Studio LLC
+
+ Licensed under a derived Apache 2.0 license:
+
+ * https://imagemagick.org/script/license.php
+
+ The source code is available from
+
+ * <http://imagemagick.org>
+
+ * The libxml2 XML C parser and toolkit
+
+ Copyright (C) since 1998 Daniel Veillard.
+
+ Licensed under the MIT license.
+
+ The source code is available from:
+
+ * <http://xmlsoft.org>
+
+ * JPEG library
+
+ The Independent JPEG Group's software's version is:
+
+ Copyright (C) since 1991 Thomas G. Lane, Guido Vollbeding.
+
+ Libjpeg-turbo has additional copyrights:
+
+ Copyright (C) since 2009 D. R. Commander.
+ Copyright (C) since 2015 Google, Inc.
+
+ Licensed under BSD-style licenses with their own terms:
+
+ * https://www.ijg.org/files/README
+ * https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/LICENSE.md
+
+ The source code is available from:
+
+ * <http://www.ijg.org>
+
+ * libtiff, a library for reading and writing TIFF
+
+ Copyright (c) since 1988 Sam Leffler
+
+ Copyright (c) since 1991 Silicon Graphics, Inc.
+
+ Licensed under a BSD-style license.
+
+ The source code is available from
+
+ * <http://www.libtiff.org>
+
+ * libpng, the official PNG reference library
+
+ Copyright (c) since 1998 Glenn Randers-Pehrson
+
+ Licensed under a BSD-style license.
+
+ The source code is available from
+
+ * <http://www.libpng.org/pub/png/libpng.html>
+
+ * The zlib general purpose compression library
+
+ Copyright (C) since 1995 Jean-loup Gailly and Mark Adler.
+
+ Licensed under a BSD-style license.
+
+ The source code is available from
+
+ * <http://zlib.net>
+
+ * The bzip2 compression library
+
+ Copyright (C) since 1996 Julian Seward.
+
+ Licensed under a BSD-style license.
+
+ The source code is available from
+
+ * <http://bzip.org>
+
+ * Depending on how this is packaged, other licenses may apply
+
+
+BUILDING
+========
+
+NOTE: this is a simplified version of what it was done in order to do the
+Travis CI builds. You may use this as a guide, but the instructions here
+may be incomplete. If you find inconsistencies, feel free to submit patches
+improving the building steps.
+
+Also, please notice that the instructions here is for a minimal version,
+without any bindings nor ImageMagick.
+
+Building on Ubuntu Bionic
+-------------------------
+
+You need to install the following packages:
+
+ sudo apt-get install -y \
+ autoconf automake autotools-dev libdbus-1-dev \ + pkg-config binutils-mingw-w64-i686 gcc-mingw-w64 \ + mingw-w64-i686-dev mingw-w64-common win-iconv-mingw-w64-dev \ + xmlto autopoint + +Then, build Zbar with: + + export PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/lib/pkgconfig
+
+ autoreconf -vfi
+
+ CC=i686-w64-mingw32-gcc ./configure --host=i686-w64-mingw32 \
+ --prefix=/usr/local/win32 --with-directshow \
+ --without-gtk --without-python --without-qt --without-java \
+ --without-imagemagick --enable-pthread
+
+ make
+
+
+Building natively on Windows
+----------------------------
+
+It is possible to build it natively on Windows too.
+
+You need first to setup a building environment with minGw. One way would
+be to use Chocolatey to download what's needed:
+
+ * https://chocolatey.org/
+
+With Cocolatey installed, ensure that you have minGw and needed deps with:
+
+ choco install -r --no-progress -y msys2 make
+
+Then use pacman to install the needed packages: + + pacman -Syu --noconfirm autoconf libtool automake make \ + autoconf-archive pkg-config autopoint gettext-devel + +Once you have everything needed and set the PATH to the places where the +building environment is, you can build ZBar with: +
+ autoreconf -vfi
+
+ ./configure \
+ --host=i686-w64-mingw32 --prefix=/usr/local/win32 \
+ --without-gtk --without-python --without-qt --without-java \
+ --without-imagemagick --enable-pthread \
+ --with-directshow --disable-dependency-tracking
+
+ make
+
+
+RUNNING
+=======
+
+This version of the package includes *only command line programs*.
+(The graphical interface is scheduled for a future release)
+
+Invoke Start -> Programs -> ZBar Bar Code Reader -> Start ZBar Command Prompt
+to open a shell that has the zbarimg and zbarcam commands available
+(in the PATH).
+
+To start the webcam reader using the default camera, type:
+
+ zbarcam
+
+To decode an image file, type e.g.:
+
+ zbarimg -d examples\barcode.png
+
+For basic command instructions, type:
+
+ zbarimg --help
+ zbarcam --help
+
+Check the manual for more details.
+
+
+REPORTING BUGS
+==============
+
+Bugs can be reported at the GitHub project page
+
+ * <https://github.com/mchehab/zbar>
+
+Please include the ZBar version number and a detailed description of
+the problem. You'll probably have better luck if you're also familiar
+with the concepts from:
+
+ * <http://www.catb.org/~esr/faqs/smart-questions.html>
diff --git a/README.md b/README.md new file mode 100644 index 0000000..0214cfd --- /dev/null +++ b/README.md @@ -0,0 +1,244 @@ +ZBAR BAR CODE READER +==================== + +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, Codabar, Interleaved 2 of 5, QR Code and SQ Code. + +Included with the library are basic applications for decoding captured bar +code images and using a video device (e.g. webcam) as a bar code scanner. +For application developers, language bindings are included for C, C++, +Python 2 and Perl as well as GUI widgets for Qt, GTK and PyGTK 2.0. + +Zbar also supports sending the scanned codes via dbus, allowing its +integration with other applications. + +Check the ZBar home page for the latest release, mailing lists, etc.: + +- <https://github.com/mchehab/zbar> + +Tarballs with ZBar can be obtained from: + +- <https://linuxtv.org/downloads/zbar/> + +Since ZBar version 0.23.90, binaries auto-generated from Github's +Actions workflows are auto-generated for each release: + +- <https://linuxtv.org/downloads/zbar/binaries/> + +They contain binaries for: + +- Ubuntu SID, generated via pbuilder; +- Mac OS; +- Windows, for 4 different configurations: + - 32 bits/64 bits; + - Video for Windows (VfW) or DirectShow (DShow). + +License information can be found in `COPYING`. + +You may find some outdated documentation at the original ZBar's +site at Sourceforge, but please notice that the content there is not +updated for ages: + http://zbar.sourceforge.net/ + + +BUILDING +======== + +See `INSTALL.md` for generic configuration and build instructions. + +Please notice that at least autotools related packages and a +C compiler are needed, in order to generate the configure script. + +So, on Debian, at least those packages are needed: + autoconf autopoint pkg-config libtool gcc make + +If you have installed all needed dependencies, all you need to do is to run: + +``` +autoreconf -vfi +./configure +make +``` + + +* NOTES + + + 1) Currently, we maintain a Continuous Integration build test at + TravisCI: + + <https://travis-ci.org/github/mchehab/zbar/> + + Due to that, there are scripts meant to test ZBar build on + Linux, Windows and MacOS, that could be helpful. Please see + the `.travis.yml` file, and the corresponding scripts under `travis/`. + + 2) On version 0.23, since the support for gtk3 and python3 are new, + the default is to use gtk2 and python2. + + If you want to use gtk3 and python3, you should have the development + packages for them, and run: +``` +autoreconf -vfi +./configure --with-gtk=auto --with-python=auto +make +``` + + This will make the building system to seek for the latest versions + for gtk and python. + +The scanner/decoder library itself only requires a few standard +library functions which should be available almost anywhere. + +The zbarcam program uses the video4linux API (v4l1 or v4l2) to access +the video device. This interface is part of the linux kernel, a 3.16 +kernel or upper is recommended for full support. More information is +available at: + +- <http://www.linuxtv.org/wiki/> + +`pkg-config` is used to locate installed libraries. You should have +installed `pkg-config` if you need any of the remaining components. +pkg-config may be obtained from: + +- <http://pkg-config.freedesktop.org/> + +The `zbarimg` program uses `ImageMagick` to read image files in many +different formats. You will need at least `ImageMagick` version 6.2.6 +if you want to scan image files. You may also use `GraphicsMagick` +package instead. + +`ImageMagick` may be obtained from: + +- <http://www.imagemagick.org/> + +Qt Widget +--------- + +The Qt widget requires Qt4 or Qt5. You will need Qt if you would like to +use or develop a Qt GUI application with an integrated bar code +scanning widget. Qt4 may be obtained from: + +- <https://www.qt.io/> + +Gtk Widget +---------- + +The GTK+ widget requires GTK+-2.x or GTK+3.x. You will need GTK+ if you +would like to use or develop a GTK+ GUI application with an integrated bar +code scanning widget. GTK+ may be obtained from: + +- <http://www.gtk.org/> + +Python widgets +-------------- + +**Python 2 legacy Gtk widget** + +The PyGTK 2.0/pygobject 2.0 wrapper for the GTK+ 2.x widget requires Python 2, +PyGTK. You will need to enable both pygtk2 and gtk2 if you would like to use +or develop a Python 2 GUI application with an integrated bar code scanning +widget. PyGTK may be obtained from: + +- <http://www.pygtk.org/> + +**Python 2 or 3 GIR Gtk widget** + +The GObject Introspection (GIR) wrapper for GTK+ widget is compatible with +PyGObject, with works with either Python version 2 or 3. You will need to +enable both Gtk and Python in order to use or develop a Python application +with an integrated bar code scanning and webcam support. In order to build +it, you need the required dependencies for GIR development. The actual +package depends on the distribution. On Fedora, it is `pygobject3-devel`. +On Debian/Ubuntu, it is `libgirepository1.0-dev` and `gir1.2-gtk-3.0`. +While GIR builds with Gtk2, It is strongly recommended to use GTK+ +version 3.x, as there are known issues with version 2.x and GIR, with +will likely make it to fail. A test script can be built and run with: +`make check-gi`. Instructions about how to use are GIR on Python are +available at: + +- <https://pygobject.readthedocs.io/en/latest/> + +**Python bindings** + +The Python bindings require Python 2 or 3 and provide only non-GUI functions. +You will need Python and PIL or Pillow if you would like to scan images or +video directly using Python. Python is available from: + +- <http://python.org/> + +Perl Widget +----------- + +The Perl bindings require Perl (version 5). You will need Perl if you +would like to scan images or video directly using Perl. Perl is +available from: + +- <http://www.perl.org/> + +If required libraries are not available you may disable building for +the corresponding component using configure (see configure --help). + +The Perl bindings must be built separately after installing the +library. see: + +- `perl/README` + +Java Widget +----------- + +The Java ZBar widget uses Java Native Interface (JNI), meaning that the +widget will contain machine-dependent code. It works with Java version +7 and above. Java open JDK is available from: + +- <https://openjdk.java.net/> + +RUNNING +======= + +`make install` will install the library and application programs. Run +`zbarcam-qt` or `zbarcam` to start the video scanner. Use `zbarimg <file>` +to decode a saved image file. + +Check the manual to find specific options for each program. + +DBUS TESTING +============ + +In order to test if dbus is working, you could use: + + $ dbus-monitor --system interface=org.linuxtv.Zbar1.Code + +or build the test programs with: + + $ make test_progs + +And run: + $ ./test/test_dbus + +With that, running this command on a separate shell: + + $ ./zbarimg/zbarimg examples/code-128.png + CODE-128:https://github.com/mchehab/zbar + scanned 1 barcode symbols from 1 images in 0.01 seconds + +Will produce this output at test_dbus shell window: + + Waiting for Zbar events + Type = CODE-128 + Value = https://github.com/mchehab/zbar + +REPORTING BUGS +============== + +Bugs can be reported on the project page: + +- <https://github.com/mchehab/zbar> + +Please include the ZBar version number and a detailed description of +the problem. You'll probably have better luck if you're also familiar +with the concepts from: + +- <http://www.catb.org/~esr/faqs/smart-questions.html> @@ -0,0 +1,84 @@ +general +======= + + * finish error handling + * handle video destroyed w/images outstanding + * dbg_scan background image stretched (still...) + * profile and weed out obvious oversights + * example using SANE to scan symbol(s) + +windows port +============ + + * libzbar-0.dll should be zbar-0.dll + +wrappers +======== + + * build API docs for zbargtk, zbarpygtk + * is zbargtk/QZBar BGR4 alpha swapped? + * widget config APIs + * drag-and-drop for widgets (configurable...) + * Perl build support integration? + * GTK and Qt perl bindings + * C++ global wrappers + +symbologies +=========== + + * PDF417 + * extract/resolve symbol matrix parameters (NB multiple symbols) + * error detection/correction + * high-level decode + * Code 39, i25 optional features (check digit and ASCII escapes) + * handle Code 128 function characters (FNC1-4) + * Code 128 trailing quiet zone checks + +decoder +======= + + * start/stop/abort and location detail APIs (PDF417, OMR) + * more configuration options + * disable for at least UPC-E (maybe UPC-A?) + * Code-39/i25 check digit (after implementation) + * Code-39 full ASCII (after implementation) + * standard symbology identifiers (which standard?) + * set consistency requirements + * set scanner filter params + * fix max length check during decode + * revisit noise and resolution independence + +image scanner +============= + * extract and track symbol polygons + * dynamic scan density (PDF417, OMR) + * add multi-sample array interface to linear scanner + +image formats +============= + + * fix image data inheritance + * de-interlacing + * add color support to conversions (also jpeg) + * add support for scanline pad throughout + * factor conversion redundancy + +window +====== + + * add XShm support + * X protocol error handling + * Direct2D + * API to query used interface (video, window?) (/format?) + * simple image manipulations scale(xv?)/mirror + * maintain aspect ratio + * more overlay details + * decoded result(?) + * stats + +zbarcam/zbarimg +=============== + + * zbarimg multi-frame duplicate suppression + * stats/fps at zbarcam exit + * decode hook (program/script)? (also zbarimg?) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000..f497968 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="net.sourceforge.zbar.android" + android:versionCode="1" + android:versionName="1.0"> + <application android:label="@string/app_name" > + <activity android:name="ACTIVITY_ENTRY_NAME" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/android/ChangeLog b/android/ChangeLog new file mode 100644 index 0000000..25ef5c5 --- /dev/null +++ b/android/ChangeLog @@ -0,0 +1,2 @@ +version 0.1: + * Add initial support for Android platform diff --git a/android/README b/android/README new file mode 100644 index 0000000..c253110 --- /dev/null +++ b/android/README @@ -0,0 +1,110 @@ +ZBar Android 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, Code 128, +Code 93, Code 39, Codabar, Interleaved 2 of 5, QR Code and +DataBar. These are the JNI wrappers for developing the library on +Android platform. + +Check the ZBar home page for the latest release, mailing lists, etc. + https://github.com/mchehab/zbar + +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-2012 © Jeff Brown <spadix@users.sourceforge.net> et al + +The Android distribution also includes pre-compiled binaries of +supporting libraries, for which copyright, license and source code +locations are as follows: + * The GNU libiconv character set conversion library + Copyright (C) 1999-2011 Free Software Foundation, Inc. + This distribution includes GNU libiconv version 1.14, licensed under + the LGPL version 2. The source code is available from + http://www.gnu.org/software/libiconv + +See included files COPYING and LICENSE.md for details. + + +Installation +------------ + +After downloading the ZBar-Android-Lib-<version>.zip file, you need to +unzip the file and add it to your Android project. Unzip the file +using your favorite method (ie: command-line, finder, windows +explorer...) + +Follow one of the two options. +Option 1 - via command line + cd <android project> + cp -r ZBar-Android-SDK-<version>/libs . + +Option 2 - via Eclipse + Right click on Android Project + Select "Import" -> "File System" + Select "Browse" (next to "From directory File" and select the + ZBar-Android-SDK-<version>/libs directory and click "Open". + Click the check box next to "libs" and the "Options" "Create top-level folder" + check box (below). + Then click "Finish". + + You should then see a "libs" folder under your project. + +Building +-------- + +Via Eclipse +You have to add the zbar.jar file to your build path + 1) select zbar.jar under libs + 2) right-click, select "Build Path" -> "Add to Build Path" + +Via command-line +You are all set; ant will automatically find jar files under the "libs" +subdirectory. + +Documentation +------------- + TDB + +Examples +-------- + +You should be able to open and build the examples directly from the +unzipped directory. You will need to run the android tools to setup +the local.properties file which sets sdk.dir. + 1) cd <unzip dir>/examples/CameraTest + 2) android update project --path . + 3) ant debug install + +If you have problems with this, please create a new Android project +and copy the necessary files from the examples. + +examples/CameraTest is a simple demonstration of how to integrate the +ZBar image scanner with the camera. + +Manually building ZBar JNI library +---------------------------------- +First download and unzip the iconv library source from + http://www.gnu.org/software/libiconv/ + +Then kick off the build from the ZBar android directory. You will +need to run the android tools to setup the local.properties file which +setups sdk.dir. + + 1) cd <zbar project>/android + 2) android update project --path . + 3) ant -Dndk.dir=<NDK path> -Diconv.src=<iconv library src> zbar-all + +This will rebuild all source files, create zbar.jar and +ZBarAndroidSDK.zip file (which bundles the jar and shared +libraries). From here, you can follow the steps for "Integrating ZBar +JNI library in Android project". + +To clean run: + ant -Dndk.dir=<NDK path> zbar-clean + +See build-ndk.xml for additional target options. diff --git a/android/ant.properties b/android/ant.properties new file mode 100644 index 0000000..ee52d86 --- /dev/null +++ b/android/ant.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/android/build-ndk.xml b/android/build-ndk.xml new file mode 100644 index 0000000..938afa4 --- /dev/null +++ b/android/build-ndk.xml @@ -0,0 +1,65 @@ +<!-- +Ant build file to compile the ZBar JNI files using Android NDK tool +targets: + zbar-clean - removes build generated files, build dir, jar and zip files + zbar-ndk-build - builds the zbarjni and iconv shared libraries + zbar-compile - builds the zbar java files + zbar-jar - builds and jars the zbar java files + zbar-zip - Creates ZBarAndroidSDK-x.y.zip of jar, .so, etc + zbar-all - performs all the above :) +--> +<project name="zbar"> + <property name="project.name" value="zbar" /> + <property name="project.sdk.name" value="ZBarAndroidSDK" /> + + <target name="zbar-clean"> + <delete dir="../java/build"/> + <delete file="libs/${project.name}.jar"/> + <delete file="${ant.project.name}.zip"/> + <exec executable="${ndk.dir}/ndk-build" failonerror="true"> + <arg value="clean"/> + </exec> + </target> + + <target name="zbar-ndk-build"> + <exec executable="${ndk.dir}/ndk-build" failonerror="true"> + <arg value="ICONV_SRC=${iconv.src}" /> + </exec> + </target> + + <target name="zbar-compile" depends="zbar-ndk-build"> + <mkdir dir="../java/build" /> + <javac srcdir="../java/net" destdir="../java/build" /> + </target> + + <target name="zbar-jar" depends="zbar-compile"> + <jar destfile="libs/${project.name}.jar" basedir="../java/build"> + </jar> + </target> + + <target name="zbar-zip"> + <if><condition><not><isset property="version"/></not></condition><then> + <property name="version" value="0.2" /> + </then></if> + <zip destfile="${project.sdk.name}-${version}.zip" > + <zipfileset dir="../" prefix="${project.sdk.name}-${version}" includes="COPYING, LICENSE.md"/> + <zipfileset dir="." prefix="${project.sdk.name}-${version}" includes="README"/> + <zipfileset dir="libs" prefix="${project.sdk.name}-${version}/libs"/> + <zipfileset dir="examples" prefix="${project.sdk.name}-${version}/examples"/> + <zipfileset dir="libs" prefix="${project.sdk.name}-${version}/examples/CameraTest/libs"/> + </zip> + </target> + + <target name="zbar-all" depends="zbar-jar"> + <if><condition><not><isset property="version"/></not></condition><then> + <property name="version" value="0.2" /> + </then></if> + <zip destfile="${project.sdk.name}-${version}.zip" > + <zipfileset dir="../" prefix="${project.sdk.name}-${version}" includes="COPYING, LICENSE.md"/> + <zipfileset dir="." prefix="${project.sdk.name}-${version}" includes="README"/> + <zipfileset dir="libs" prefix="${project.sdk.name}-${version}/libs"/> + <zipfileset dir="examples" prefix="${project.sdk.name}-${version}/examples"/> + <zipfileset dir="libs" prefix="${project.sdk.name}-${version}/examples/CameraTest/libs"/> + </zip> + </target> +</project> diff --git a/android/build.xml b/android/build.xml new file mode 100644 index 0000000..e7b93ac --- /dev/null +++ b/android/build.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="ZBarAndroidSDK" default="help"> + + <!-- The local.properties file is created and updated by the 'android' tool. + It contains the path to the SDK. It should *NOT* be checked into + Version Control Systems. --> + <property file="local.properties" /> + + <!-- The ant.properties file can be created by you. It is only edited by the + 'android' tool to add properties to it. + This is the place to change some Ant specific build properties. + Here are some properties you may want to change/update: + + source.dir + The name of the source directory. Default is 'src'. + out.dir + The name of the output directory. Default is 'bin'. + + For other overridable properties, look at the beginning of the rules + files in the SDK, at tools/ant/build.xml + + Properties related to the SDK location or the project target should + be updated using the 'android' tool with the 'update' action. + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. + + --> + <property file="ant.properties" /> + + <!-- The project.properties file is created and updated by the 'android' + tool, as well as ADT. + + This contains project specific properties such as project target, and library + dependencies. Lower level build properties are stored in ant.properties + (or in .classpath for Eclipse projects). + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. --> + <loadproperties srcFile="project.properties" /> + + <!-- quick check on sdk.dir --> + <fail + message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var" + unless="sdk.dir" + /> + + +<!-- extension targets. Uncomment the ones where you want to do custom work + in between standard targets --> +<!-- + <target name="-pre-build"> + </target> + <target name="-pre-compile"> + </target> + + /* This is typically used for code obfuscation. + Compiled code location: ${out.classes.absolute.dir} + If this is not done in place, override ${out.dex.input.absolute.dir} */ + <target name="-post-compile"> + <copy file="${out.absolute.dir}/classes.jar" tofile="${jar.libs.dir}/zbar_android.jar" /> + </target> +--> + + <!-- Import the actual build file. + + To customize existing targets, there are two options: + - Customize only one target: + - copy/paste the target into this file, *before* the + <import> task. + - customize it to your needs. + - Customize the whole content of build.xml + - copy/paste the content of the rules files (minus the top node) + into this file, replacing the <import> task. + - customize to your needs. + + *********************** + ****** IMPORTANT ****** + *********************** + In all cases you must update the value of version-tag below to read 'custom' instead of an integer, + in order to avoid having your file be overridden by tools such as "android update project" + --> + <!-- version-tag: 1 --> + <import file="build-ndk.xml" /> + <import file="${sdk.dir}/tools/ant/build.xml" /> +</project> diff --git a/android/examples/CameraTest/AndroidManifest.xml b/android/examples/CameraTest/AndroidManifest.xml new file mode 100644 index 0000000..2bc2ae7 --- /dev/null +++ b/android/examples/CameraTest/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="net.sourceforge.zbar.android.CameraTest" + android:versionCode="1" + android:versionName="1.0"> + <uses-sdk android:minSdkVersion="8" /> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-feature android:name="android.hardware.camera" /> + <uses-feature android:name="android.hardware.camera.autofocus" /> + <application android:label="@string/app_name" > + <activity android:name="CameraTestActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/android/examples/CameraTest/ant.properties b/android/examples/CameraTest/ant.properties new file mode 100644 index 0000000..ee52d86 --- /dev/null +++ b/android/examples/CameraTest/ant.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/android/examples/CameraTest/build.xml b/android/examples/CameraTest/build.xml new file mode 100644 index 0000000..df1f293 --- /dev/null +++ b/android/examples/CameraTest/build.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="CameraTest" default="help"> + + <!-- The local.properties file is created and updated by the 'android' tool. + It contains the path to the SDK. It should *NOT* be checked into + Version Control Systems. --> + <property file="local.properties" /> + + <!-- The ant.properties file can be created by you. It is only edited by the + 'android' tool to add properties to it. + This is the place to change some Ant specific build properties. + Here are some properties you may want to change/update: + + source.dir + The name of the source directory. Default is 'src'. + out.dir + The name of the output directory. Default is 'bin'. + + For other overridable properties, look at the beginning of the rules + files in the SDK, at tools/ant/build.xml + + Properties related to the SDK location or the project target should + be updated using the 'android' tool with the 'update' action. + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. + + --> + <property file="ant.properties" /> + + <!-- The project.properties file is created and updated by the 'android' + tool, as well as ADT. + + This contains project specific properties such as project target, and library + dependencies. Lower level build properties are stored in ant.properties + (or in .classpath for Eclipse projects). + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. --> + <loadproperties srcFile="project.properties" /> + + <!-- quick check on sdk.dir --> + <fail + message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var" + unless="sdk.dir" + /> + + +<!-- extension targets. Uncomment the ones where you want to do custom work + in between standard targets --> +<!-- + <target name="-pre-build"> + </target> + <target name="-pre-compile"> + </target> + + /* This is typically used for code obfuscation. + Compiled code location: ${out.classes.absolute.dir} + If this is not done in place, override ${out.dex.input.absolute.dir} */ + <target name="-post-compile"> + </target> +--> + + <!-- Import the actual build file. + + To customize existing targets, there are two options: + - Customize only one target: + - copy/paste the target into this file, *before* the + <import> task. + - customize it to your needs. + - Customize the whole content of build.xml + - copy/paste the content of the rules files (minus the top node) + into this file, replacing the <import> task. + - customize to your needs. + + *********************** + ****** IMPORTANT ****** + *********************** + In all cases you must update the value of version-tag below to read 'custom' instead of an integer, + in order to avoid having your file be overridden by tools such as "android update project" + --> + <!-- version-tag: 1 --> + <import file="${sdk.dir}/tools/ant/build.xml" /> + +</project> diff --git a/android/examples/CameraTest/proguard.cfg b/android/examples/CameraTest/proguard.cfg new file mode 100644 index 0000000..b1cdf17 --- /dev/null +++ b/android/examples/CameraTest/proguard.cfg @@ -0,0 +1,40 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native <methods>; +} + +-keepclasseswithmembers class * { + public <init>(android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembers class * { + public <init>(android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/android/examples/CameraTest/project.properties b/android/examples/CameraTest/project.properties new file mode 100644 index 0000000..8da376a --- /dev/null +++ b/android/examples/CameraTest/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-15 diff --git a/android/examples/CameraTest/res/layout/main.xml b/android/examples/CameraTest/res/layout/main.xml new file mode 100644 index 0000000..36279a1 --- /dev/null +++ b/android/examples/CameraTest/res/layout/main.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:id="@+id/cameraPreview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + /> + + <TextView + android:id="@+id/scanText" + android:text="Scanning..." + android:layout_height="wrap_content" + android:layout_width="match_parent"> + </TextView> + <Button + android:id="@+id/ScanButton" + android:text="Scan" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + /> +</LinearLayout> diff --git a/android/examples/CameraTest/res/values/strings.xml b/android/examples/CameraTest/res/values/strings.xml new file mode 100644 index 0000000..c1b70c2 --- /dev/null +++ b/android/examples/CameraTest/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">ZBar CameraTest</string> +</resources> diff --git a/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraPreview.java b/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraPreview.java new file mode 100644 index 0000000..41efe68 --- /dev/null +++ b/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraPreview.java @@ -0,0 +1,108 @@ +/* + * Barebones implementation of displaying camera preview. + * + * Created by lisah0 on 2012-02-24 + */ +package net.sourceforge.zbar.android.CameraTest; + +import java.io.IOException; + +import android.app.Activity; +import android.os.Bundle; + +import android.util.Log; + +import android.view.View; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.SurfaceHolder; + +import android.content.Context; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.AutoFocusCallback; +import android.hardware.Camera.Parameters; + +/** A basic Camera preview class */ +public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { + private SurfaceHolder mHolder; + private Camera mCamera; + private PreviewCallback previewCallback; + private AutoFocusCallback autoFocusCallback; + + public CameraPreview(Context context, Camera camera, + PreviewCallback previewCb, + AutoFocusCallback autoFocusCb) { + super(context); + mCamera = camera; + previewCallback = previewCb; + autoFocusCallback = autoFocusCb; + + /* + * Set camera to continuous focus if supported, otherwise use + * software auto-focus. Only works for API level >=9. + */ + /* + Camera.Parameters parameters = camera.getParameters(); + for (String f : parameters.getSupportedFocusModes()) { + if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) { + mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + autoFocusCallback = null; + break; + } + } + */ + + // Install a SurfaceHolder.Callback so we get notified when the + // underlying surface is created and destroyed. + mHolder = getHolder(); + mHolder.addCallback(this); + + // deprecated setting, but required on Android versions prior to 3.0 + mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + } + + public void surfaceCreated(SurfaceHolder holder) { + // The Surface has been created, now tell the camera where to draw the preview. + try { + mCamera.setPreviewDisplay(holder); + } catch (IOException e) { + Log.d("DBG", "Error setting camera preview: " + e.getMessage()); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) { + // Camera preview released in activity + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + /* + * If your preview can change or rotate, take care of those events here. + * Make sure to stop the preview before resizing or reformatting it. + */ + if (mHolder.getSurface() == null){ + // preview surface does not exist + return; + } + + // stop preview before making changes + try { + mCamera.stopPreview(); + } catch (Exception e){ + // ignore: tried to stop a non-existent preview + } + + try { + // Hard code camera surface rotation 90 degs to match Activity view in portrait + mCamera.setDisplayOrientation(90); + + mCamera.setPreviewDisplay(mHolder); + mCamera.setPreviewCallback(previewCallback); + mCamera.startPreview(); + mCamera.autoFocus(autoFocusCallback); + } catch (Exception e){ + Log.d("DBG", "Error starting camera preview: " + e.getMessage()); + } + } +} diff --git a/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraTestActivity.java b/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraTestActivity.java new file mode 100644 index 0000000..e20bcad --- /dev/null +++ b/android/examples/CameraTest/src/net/sourceforge/zbar/android/CameraTest/CameraTestActivity.java @@ -0,0 +1,155 @@ +/* + * Basic no frills app which integrates the ZBar barcode scanner with + * the camera. + * + * Created by lisah0 on 2012-02-24 + */ +package net.sourceforge.zbar.android.CameraTest; + +import net.sourceforge.zbar.android.CameraTest.CameraPreview; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; + +import android.view.View; +import android.view.View.OnClickListener; +import android.view.Window; +import android.widget.FrameLayout; +import android.widget.Button; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.AutoFocusCallback; +import android.hardware.Camera.Parameters; +import android.hardware.Camera.Size; + +import android.widget.TextView; +import android.graphics.ImageFormat; + +/* Import ZBar Class files */ +import net.sourceforge.zbar.ImageScanner; +import net.sourceforge.zbar.Image; +import net.sourceforge.zbar.Symbol; +import net.sourceforge.zbar.SymbolSet; +import net.sourceforge.zbar.Config; + +public class CameraTestActivity extends Activity +{ + private Camera mCamera; + private CameraPreview mPreview; + private Handler autoFocusHandler; + + TextView scanText; + Button scanButton; + + ImageScanner scanner; + + private boolean barcodeScanned = false; + private boolean previewing = true; + + static { + System.loadLibrary("iconv"); + } + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + autoFocusHandler = new Handler(); + mCamera = getCameraInstance(); + + /* Instance barcode scanner */ + scanner = new ImageScanner(); + scanner.setConfig(0, Config.X_DENSITY, 3); + scanner.setConfig(0, Config.Y_DENSITY, 3); + + mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB); + FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview); + preview.addView(mPreview); + + scanText = (TextView)findViewById(R.id.scanText); + + scanButton = (Button)findViewById(R.id.ScanButton); + + scanButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (barcodeScanned) { + barcodeScanned = false; + scanText.setText("Scanning..."); + mCamera.setPreviewCallback(previewCb); + mCamera.startPreview(); + previewing = true; + mCamera.autoFocus(autoFocusCB); + } + } + }); + } + + public void onPause() { + super.onPause(); + releaseCamera(); + } + + /** A safe way to get an instance of the Camera object. */ + public static Camera getCameraInstance(){ + Camera c = null; + try { + c = Camera.open(); + } catch (Exception e){ + } + return c; + } + + private void releaseCamera() { + if (mCamera != null) { + previewing = false; + mCamera.setPreviewCallback(null); + mCamera.release(); + mCamera = null; + } + } + + private Runnable doAutoFocus = new Runnable() { + public void run() { + if (previewing) + mCamera.autoFocus(autoFocusCB); + } + }; + + PreviewCallback previewCb = new PreviewCallback() { + public void onPreviewFrame(byte[] data, Camera camera) { + Camera.Parameters parameters = camera.getParameters(); + Size size = parameters.getPreviewSize(); + + Image barcode = new Image(size.width, size.height, "Y800"); + barcode.setData(data); + + int result = scanner.scanImage(barcode); + + if (result != 0) { + previewing = false; + mCamera.setPreviewCallback(null); + mCamera.stopPreview(); + + SymbolSet syms = scanner.getResults(); + for (Symbol sym : syms) { + scanText.setText("barcode result " + sym.getData()); + barcodeScanned = true; + } + } + } + }; + + // Mimic continuous auto-focusing + AutoFocusCallback autoFocusCB = new AutoFocusCallback() { + public void onAutoFocus(boolean success, Camera camera) { + autoFocusHandler.postDelayed(doAutoFocus, 1000); + } + }; +} diff --git a/android/jni/Android.mk b/android/jni/Android.mk new file mode 100644 index 0000000..14f91b7 --- /dev/null +++ b/android/jni/Android.mk @@ -0,0 +1,76 @@ +# +# Android NDK makefile +# +# build - <ndk path>/ndk-build ICONV_SRC=<iconv library src> +# clean - <ndk path>/ndk-build clean +# +MY_LOCAL_PATH := $(call my-dir) + +# libiconv +include $(CLEAR_VARS) +LOCAL_PATH := $(ICONV_SRC) + +LOCAL_MODULE := libiconv + +LOCAL_CFLAGS := \ + -Wno-multichar \ + -D_ANDROID \ + -DLIBDIR="c" \ + -DBUILDING_LIBICONV \ + -DBUILDING_LIBCHARSET \ + -DIN_LIBRARY + +LOCAL_SRC_FILES := \ + lib/iconv.c \ + libcharset/lib/localcharset.c \ + lib/relocatable.c + +LOCAL_C_INCLUDES := \ + $(ICONV_SRC)/include \ + $(ICONV_SRC)/libcharset \ + $(ICONV_SRC)/libcharset/include + +include $(BUILD_SHARED_LIBRARY) + +LOCAL_LDLIBS := -llog -lcharset + +# libzbarjni +include $(CLEAR_VARS) + +LOCAL_PATH := $(MY_LOCAL_PATH) +LOCAL_MODULE := zbarjni +LOCAL_SRC_FILES := ../../java/zbarjni.c \ + ../../zbar/img_scanner.c \ + ../../zbar/decoder.c \ + ../../zbar/image.c \ + ../../zbar/symbol.c \ + ../../zbar/convert.c \ + ../../zbar/config.c \ + ../../zbar/scanner.c \ + ../../zbar/error.c \ + ../../zbar/refcnt.c \ + ../../zbar/video.c \ + ../../zbar/video/null.c \ + ../../zbar/decoder/code128.c \ + ../../zbar/decoder/code39.c \ + ../../zbar/decoder/code93.c \ + ../../zbar/decoder/codabar.c \ + ../../zbar/decoder/databar.c \ + ../../zbar/decoder/ean.c \ + ../../zbar/decoder/i25.c \ + ../../zbar/decoder/qr_finder.c \ + ../../zbar/qrcode/bch15_5.c \ + ../../zbar/qrcode/binarize.c \ + ../../zbar/qrcode/isaac.c \ + ../../zbar/qrcode/qrdec.c \ + ../../zbar/qrcode/qrdectxt.c \ + ../../zbar/qrcode/rs.c \ + ../../zbar/qrcode/util.c + +LOCAL_C_INCLUDES := ../include \ + ../zbar \ + $(ICONV_SRC)/include + +LOCAL_SHARED_LIBRARIES := libiconv + +include $(BUILD_SHARED_LIBRARY) diff --git a/android/jni/Application.mk b/android/jni/Application.mk new file mode 100644 index 0000000..7866257 --- /dev/null +++ b/android/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := armeabi armeabi-v7a x86 diff --git a/android/jni/config.h b/android/jni/config.h new file mode 100644 index 0000000..adfe324 --- /dev/null +++ b/android/jni/config.h @@ -0,0 +1,238 @@ +/* manually customized for iPhone platform */ + +#define HAVE_LANGINFO_CODESET 0 + +/* 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 Codabar symbology */ +#define ENABLE_CODABAR 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 <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the <features.h> 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 <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <jpeglib.h> 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 <linux/videodev2.h> header file. */ +#undef HAVE_LINUX_VIDEODEV2_H + +/* Define to 1 if you have the <linux/videodev.h> header file. */ +#undef HAVE_LINUX_VIDEODEV_H + +/* Define to 1 if you have the <memory.h> 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 <poll.h> header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the <pthread.h> 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 <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/ipc.h> header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the <sys/shm.h> header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/times.h> header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> 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 <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <vfw.h> header file. */ +#undef HAVE_VFW_H + +/* Define to 1 if you have the <X11/extensions/XShm.h> header file. */ +#undef HAVE_X11_EXTENSIONS_XSHM_H + +/* Define to 1 if you have the <X11/extensions/Xvlib.h> 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 + +/* Program minor version (after the second '.') as a number */ +#define ZBAR_VERSION_PATCH 0 + +/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> 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 <sys/synch.h>, + <pthread.h>, or <semaphore.h> 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/android/proguard.cfg b/android/proguard.cfg new file mode 100644 index 0000000..b1cdf17 --- /dev/null +++ b/android/proguard.cfg @@ -0,0 +1,40 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native <methods>; +} + +-keepclasseswithmembers class * { + public <init>(android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembers class * { + public <init>(android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/android/project.properties b/android/project.properties new file mode 100644 index 0000000..7662246 --- /dev/null +++ b/android/project.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +android.library=true +# Project target. +target=android-8 diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml new file mode 100644 index 0000000..7d62fbb --- /dev/null +++ b/android/res/layout/main.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Hello World, ACTIVITY_ENTRY_NAME" + /> +</LinearLayout> + diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml new file mode 100644 index 0000000..ee5af40 --- /dev/null +++ b/android/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">ACTIVITY_ENTRY_NAME</string> +</resources> diff --git a/config/.keep b/config/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/config/.keep diff --git a/config/config.rpath b/config/config.rpath new file mode 100755 index 0000000..24be79c --- /dev/null +++ b/config/config.rpath @@ -0,0 +1,684 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable. +# +# Copyright 1996-2020 Free Software Foundation, Inc. +# Taken from GNU libtool, 2001 +# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld +# should be set by the caller. +# +# The set of defined variables is at the end of this script. + +# Known limitations: +# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer +# than 256 bytes, otherwise the compiler driver will dump core. The only +# known workaround is to choose shorter directory names for the build +# directory and/or the installation directory. + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a +shrext=.so + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +# Code taken from libtool.m4's _LT_CC_BASENAME. + +for cc_temp in $CC""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` + +# Code taken from libtool.m4's _LT_COMPILER_PIC. + +wl= +if test "$GCC" = yes; then + wl='-Wl,' +else + case "$host_os" in + aix*) + wl='-Wl,' + ;; + mingw* | cygwin* | pw32* | os2* | cegcc*) + ;; + hpux9* | hpux10* | hpux11*) + wl='-Wl,' + ;; + irix5* | irix6* | nonstopux*) + wl='-Wl,' + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + ecc*) + wl='-Wl,' + ;; + icc* | ifort*) + wl='-Wl,' + ;; + lf95*) + wl='-Wl,' + ;; + nagfor*) + wl='-Wl,-Wl,,' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + wl='-Wl,' + ;; + ccc*) + wl='-Wl,' + ;; + xl* | bgxl* | bgf* | mpixl*) + wl='-Wl,' + ;; + como) + wl='-lopt=' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ F* | *Sun*Fortran*) + wl= + ;; + *Sun\ C*) + wl='-Wl,' + ;; + esac + ;; + esac + ;; + newsos6) + ;; + *nto* | *qnx*) + ;; + osf3* | osf4* | osf5*) + wl='-Wl,' + ;; + rdos*) + ;; + solaris*) + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + wl='-Qoption ld ' + ;; + *) + wl='-Wl,' + ;; + esac + ;; + sunos4*) + wl='-Qoption ld ' + ;; + sysv4 | sysv4.2uw2* | sysv4.3*) + wl='-Wl,' + ;; + sysv4*MP*) + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + wl='-Wl,' + ;; + unicos*) + wl='-Wl,' + ;; + uts4*) + ;; + esac +fi + +# Code taken from libtool.m4's _LT_LINKER_SHLIBS. + +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no + +case "$host_os" in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + # Unlike libtool, we use -rpath here, not --rpath, since the documented + # option of GNU ld is called -rpath, not --rpath. + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + case "$host_os" in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + fi + ;; + amigaos*) + case "$host_cpu" in + powerpc) + ;; + m68k) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + cygwin* | mingw* | pw32* | cegcc*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + haiku*) + ;; + interix[3-9]*) + hardcode_direct=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + netbsd*) + ;; + solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' + else + ld_shlibs=no + fi + ;; + esac + ;; + sunos4*) + hardcode_direct=yes + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + esac + if test "$ld_shlibs" = no; then + hardcode_libdir_flag_spec= + fi +else + case "$host_os" in + aix3*) + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + else + aix_use_runtimelinking=no + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + fi + hardcode_direct=yes + hardcode_libdir_separator=':' + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + fi + # Begin _LT_AC_SYS_LIBPATH_AIX. + echo 'int main () { return 0; }' > conftest.c + ${CC} ${LDFLAGS} conftest.c -o conftest + aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + fi + if test -z "$aix_libpath"; then + aix_libpath="/usr/lib:/lib" + fi + rm -f conftest.c conftest + # End _LT_AC_SYS_LIBPATH_AIX. + if test "$aix_use_runtimelinking" = yes; then + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + else + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + fi + fi + ;; + amigaos*) + case "$host_cpu" in + powerpc) + ;; + m68k) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + libext=lib + ;; + darwin* | rhapsody*) + hardcode_direct=no + if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then + : + else + ld_shlibs=no + fi + ;; + dgux*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + freebsd2.[01]*) + hardcode_direct=yes + hardcode_minus_L=yes + ;; + freebsd* | dragonfly*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + hpux9*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + hpux10*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + hpux11*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + ;; + *) + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + irix5* | irix6* | nonstopux*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + netbsd*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + newsos6) + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + *nto* | *qnx*) + ;; + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + osf3*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + osf4* | osf5*) + if test "$GCC" = yes; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + # Both cc and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + solaris*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + sunos4*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + sysv4) + case $host_vendor in + sni) + hardcode_direct=yes # is this really true??? + ;; + siemens) + hardcode_direct=no + ;; + motorola) + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + ;; + sysv4.3*) + ;; + sysv4*MP*) + if test -d /usr/nec; then + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + ;; + sysv5* | sco3.2v5* | sco5v6*) + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' + hardcode_libdir_separator=':' + ;; + uts4*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + *) + ld_shlibs=no + ;; + esac +fi + +# Check dynamic linker characteristics +# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. +# Unlike libtool.m4, here we don't care about _all_ names of the library, but +# only about the one the linker finds when passed -lNAME. This is the last +# element of library_names_spec in libtool.m4, or possibly two of them if the +# linker has special search rules. +library_names_spec= # the last element of library_names_spec in libtool.m4 +libname_spec='lib$name' +case "$host_os" in + aix3*) + library_names_spec='$libname.a' + ;; + aix[4-9]*) + library_names_spec='$libname$shrext' + ;; + amigaos*) + case "$host_cpu" in + powerpc*) + library_names_spec='$libname$shrext' ;; + m68k) + library_names_spec='$libname.a' ;; + esac + ;; + beos*) + library_names_spec='$libname$shrext' + ;; + bsdi[45]*) + library_names_spec='$libname$shrext' + ;; + cygwin* | mingw* | pw32* | cegcc*) + shrext=.dll + library_names_spec='$libname.dll.a $libname.lib' + ;; + darwin* | rhapsody*) + shrext=.dylib + library_names_spec='$libname$shrext' + ;; + dgux*) + library_names_spec='$libname$shrext' + ;; + freebsd[23].*) + library_names_spec='$libname$shrext$versuffix' + ;; + freebsd* | dragonfly*) + library_names_spec='$libname$shrext' + ;; + gnu*) + library_names_spec='$libname$shrext' + ;; + haiku*) + library_names_spec='$libname$shrext' + ;; + hpux9* | hpux10* | hpux11*) + case $host_cpu in + ia64*) + shrext=.so + ;; + hppa*64*) + shrext=.sl + ;; + *) + shrext=.sl + ;; + esac + library_names_spec='$libname$shrext' + ;; + interix[3-9]*) + library_names_spec='$libname$shrext' + ;; + irix5* | irix6* | nonstopux*) + library_names_spec='$libname$shrext' + case "$host_os" in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; + *) libsuff= shlibsuff= ;; + esac + ;; + esac + ;; + linux*oldld* | linux*aout* | linux*coff*) + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + library_names_spec='$libname$shrext' + ;; + knetbsd*-gnu) + library_names_spec='$libname$shrext' + ;; + netbsd*) + library_names_spec='$libname$shrext' + ;; + newsos6) + library_names_spec='$libname$shrext' + ;; + *nto* | *qnx*) + library_names_spec='$libname$shrext' + ;; + openbsd*) + library_names_spec='$libname$shrext$versuffix' + ;; + os2*) + libname_spec='$name' + shrext=.dll + library_names_spec='$libname.a' + ;; + osf3* | osf4* | osf5*) + library_names_spec='$libname$shrext' + ;; + rdos*) + ;; + solaris*) + library_names_spec='$libname$shrext' + ;; + sunos4*) + library_names_spec='$libname$shrext$versuffix' + ;; + sysv4 | sysv4.3*) + library_names_spec='$libname$shrext' + ;; + sysv4*MP*) + library_names_spec='$libname$shrext' + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + library_names_spec='$libname$shrext' + ;; + tpf*) + library_names_spec='$libname$shrext' + ;; + uts4*) + library_names_spec='$libname$shrext' + ;; +esac + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` +shlibext=`echo "$shrext" | sed -e 's,^\.,,'` +escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF + +# How to pass a linker flag through the compiler. +wl="$escaped_wl" + +# Static library suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally "so"). +shlibext="$shlibext" + +# Format of library name prefix. +libname_spec="$escaped_libname_spec" + +# Library names that the linker finds when passed -lNAME. +library_names_spec="$escaped_library_names_spec" + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="$hardcode_libdir_separator" + +# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the +# resulting binary. +hardcode_direct="$hardcode_direct" + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L="$hardcode_minus_L" + +EOF diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f0f9723 --- /dev/null +++ b/configure.ac @@ -0,0 +1,927 @@ +changecom()dnl +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ([2.68]) +AC_INIT([zbar],[0.23.93],[mchehab+huawei@kernel.org]) +m4_ifndef([AC_LANG_DEFINES_PROVIDED], + [m4_define([AC_LANG_DEFINES_PROVIDED])]) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_MACRO_DIR(config) +AM_INIT_AUTOMAKE([1.13 -Werror foreign subdir-objects std-options dist-bzip2]) +m4_pattern_allow([AM_PROG_AR]) +AC_CONFIG_HEADERS([include/config.h]) +AC_CONFIG_SRCDIR(zbar/scanner.c) +LT_PREREQ([2.2]) +LT_INIT([dlopen win32-dll]) +LT_LANG([Windows Resource]) +AM_SILENT_RULES([yes]) + +dnl update these just before each release (along w/package version above) +dnl LIB_VERSION update instructions copied from libtool docs: +dnl library version follows the form current:revision:age +dnl - If the library source code has changed at all since the last update, +dnl then increment revision (c:r:a becomes c:r+1:a). +dnl - If any interfaces have been added, removed, or changed, +dnl increment current, and set revision to 0. +dnl - If any interfaces have been added since the last public release, +dnl then increment age. +dnl - If any interfaces have been removed since the last public release, +dnl then set age to 0. +AC_SUBST([LIB_VERSION], [3:0:3]) +AC_SUBST([RELDATE], [2017-04-11]) + +dnl widget libraries use their own versioning. +dnl NB pygtk wrapper is *unversioned* +AC_SUBST([ZGTK_LIB_VERSION], [0:2:0]) +AC_SUBST([ZQT_LIB_VERSION], [1:2:1]) + +AC_DEFINE_UNQUOTED([ZBAR_VERSION_MAJOR], + [[`echo "$PACKAGE_VERSION" | sed -e 's/\..*$//'`]], + [Program major version (before the '.') as a number]) +AC_DEFINE_UNQUOTED([ZBAR_VERSION_MINOR], + [[`echo "$PACKAGE_VERSION" | sed -e 's/^[^\.]*\.\([^\.]*\).*/\1/'`]], + [Program minor version (after '.') as a number]) +AC_DEFINE_UNQUOTED([ZBAR_VERSION_PATCH], + [[`echo "$PACKAGE_VERSION" | sed -e 's/^[^\.]*\.[^\.]*\.*//' | sed s,^$,0,`]], + [Program patch version (after the second '.') as a number]) + +cur=`echo "$LIB_VERSION" | sed -e 's/:.*$//'` +age=`echo "$LIB_VERSION" | sed -e 's/^.*://'` +AC_DEFINE_UNQUOTED([LIB_VERSION_MAJOR], [[$(( $cur - $age ))]], + [Library major version]) +AC_DEFINE_UNQUOTED([LIB_VERSION_MINOR], [[$age]], + [Library minor version]) +AC_DEFINE_UNQUOTED([LIB_VERSION_REVISION], + [[`echo "$LIB_VERSION" | sed -e 's/^[^:]*:\([^:]*\):.*$/\1/'`]], + [Library revision]) + +AM_CPPFLAGS="-I\$(top_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 +case $host_os in + *cygwin* | *mingw* | *uwin* | *djgpp* | *ems* ) + win32="yes" + with_dbus="no" + AC_DEFINE([_WIN32_WINNT], [0x0500], [Minimum Windows API version]) + AC_FUNC_ALLOCA + AC_FUNC_ERROR_AT_LINE + AC_FUNC_FSEEKO + AC_CHECK_HEADERS([arpa/inet.h malloc.h mntent.h netdb.h netinet/in.h shadow.h sys/file.h sys/mount.h sys/param.h sys/socket.h sys/statfs.h sys/statvfs.h sys/vfs.h unistd.h values.h]) + ;; + * ) + win32="no" + ;; +esac +AM_CONDITIONAL([WIN32], [test "x$win32" = "xyes"]) + +dnl programs + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX + +PKG_PROG_PKG_CONFIG + +dnl symbologies + +AC_ARG_ENABLE([codes], + [AS_HELP_STRING([--enable-codes=SYMS], + [select symbologies to compile [default=ean,databar,code128,code93,code39,codabar,i25,qrcode,sqcode]])], + [], + [enable_codes="ean,databar,code128,code93,code39,codabar,i25,qrcode,sqcode"]) + +AC_DEFUN([AC_DEFINE_SUBST], + [AC_DEFINE($1,$2,$3) + AC_SUBST($1,$2)]) + +AC_DEFUN([ZBAR_CHK_CODE], [ + AC_MSG_CHECKING([whether to build $2]) + enable_$1="no" + AH_TEMPLATE([ENABLE_]translit($1, a-z, A-Z), + [whether to build support for $2]) + AS_CASE([$enable_codes], + [*$1* | *all*], + [enable_$1="yes" + enabled_codes="$enabled_codes $1" + AC_DEFINE_SUBST([ENABLE_]translit($1, a-z, A-Z), [1]) + ], [ + disabled_codes="$disabled_codes $1" + AC_DEFINE_SUBST([ENABLE_]translit($1, a-z, A-Z), [0]) + ]) + AM_CONDITIONAL([ENABLE_]translit($1, a-z, A-Z), + [test "x$enable_$1" = "xyes"]) + AC_MSG_RESULT([$enable_$1]) +])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([codabar], [Codabar symbology]) +ZBAR_CHK_CODE([i25], [Interleaved 2 of 5 symbology]) +ZBAR_CHK_CODE([qrcode], [QR Code]) +ZBAR_CHK_CODE([sqcode], [SQ Code]) +ZBAR_CHK_CODE([pdf417], [PDF417 symbology (incomplete)]) + +dnl libraries + +AC_SEARCH_LIBS([clock_gettime], [rt pthread]) +AM_ICONV() +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION(0.20) +AM_GNU_GETTEXT_REQUIRE_VERSION(0.18) +AM_CONDITIONAL([USE_NLS], [test "x${USE_NLS}" = "xyes"]) + +dnl libraries linkage +AC_ARG_ENABLE([static_qt], + [AS_HELP_STRING([--enable-static-qt], + [Produce a static library for libzbarcam-qt])]) + +AS_IF([test x$enable_static_qt = xyes], [AC_SUBST([LIBQT_EXTRA_LDFLAGS], ["-static"])]) + + +dnl poll support + +AC_CHECK_HEADERS([poll.h], [have_poll="yes"], [have_poll="no"]) +AM_CONDITIONAL([HAVE_POLL], [test "x$have_poll" = "xyes"]) + +dnl pthreads +dnl FIXME this doesn't port well, integrate something like this: +dnl http://autoconf-archive.cryp.to/acx_pthread.html + +AC_ARG_ENABLE([pthread], + [AS_HELP_STRING([--disable-pthread], + [omit support for threaded applications])], + [], + [AS_IF([test "x$win32" = "xno"], + [enable_pthread="yes"], + [enable_pthread="no" +])]) + +AS_IF([test "x$enable_pthread" != "xno"], + [AC_CHECK_HEADERS([pthread.h], [], + [AC_MSG_FAILURE([test for pthread support failed! +configure --disable-pthread to skip threaded support.])]) + AC_CHECK_LIB([pthread], [pthread_create], [], + [AC_MSG_FAILURE([unable to link against -lpthread, although you +appear to have pthread.h? set LDFLAGS and/or LIBS to help the linker, +or configure --disable-pthread to skip threaded support.])]) + AC_DEFINE([__USE_UNIX98], [1], [used only for pthread debug attributes]) +]) + +dnl doc +AC_ARG_ENABLE([doc], + [AS_HELP_STRING([--disable-doc], + [disable building docs])], + [], + [enable_doc="yes"]) + +AS_IF([test "x$enable_doc" != "xno"], + [AC_ARG_VAR([XMLTO], [location of xmlto, used for optional documentation generation]) + AC_ARG_VAR([XMLTOFLAGS], [additional arguments for xmlto]) + AC_CHECK_PROGS([XMLTO], [xmlto])]) + +AS_IF([test "x$XMLTO" = "x"], enable_doc="no") + +AM_CONDITIONAL([HAVE_DOC], [test "x$enable_doc" != "xno"]) + +dnl video +AC_ARG_ENABLE([video], + [AS_HELP_STRING([--disable-video], + [exclude video scanner features])], + [], + [enable_video="yes"]) + +dnl video, directshow +AC_ARG_WITH([directshow], + [AS_HELP_STRING([--with-directshow], + [compile directshow driver on windows instead of vfw, +available only when video support is enabled])], + [], + [with_directshow="no"]) + +have_v4l1="no" +have_v4l2="no" +have_libv4l="no" +AS_IF([test "x$enable_video" = "xno"], + [], + [test "x$win32" = "xno"], + [AC_CHECK_HEADERS([linux/videodev.h], [have_v4l1="yes"]) + AC_CHECK_HEADERS([linux/videodev2.h], [have_v4l2="yes"]) + AC_CHECK_HEADERS([libv4l2.h], [have_libv4l="yes"]) + AS_IF([test "x$have_v4l2" = "xno" && test "x$have_v4l1" = "xno"], + [AC_MSG_FAILURE([test for video support failed! +rebuild your kernel to include video4linux support or +configure --disable-video to skip building video support.])], + [AS_IF([test "x$have_v4l2" = "xno"], + [AC_MSG_WARN([v4l2 API not detected, upgrade your kernel!])])] + )], + [AS_IF([test "x$with_directshow" != "xno"], + [with_video="directshow"], + [AC_CHECK_HEADERS([vfw.h], [with_video="vfw"], + [AC_MSG_FAILURE([test for VfW video support failed! +configure --disable-video to skip building video support.])],[#include <windows.h>])])]) + +AS_IF([test "x$have_libv4l" = "xyes"], + [PKG_CHECK_MODULES([V4L2], [libv4l2], [], + [AC_MSG_FAILURE([unable to find libv4l2.so])])], + [AC_MSG_WARN([libv4l not detected. Install it to support more cameras!])]) + +AM_CONDITIONAL([HAVE_VIDEO], [test "x$enable_video" != "xno"]) +AM_CONDITIONAL([HAVE_V4L1], [test "x$have_v4l1" != "xno"]) +AM_CONDITIONAL([HAVE_V4L2], [test "x$have_v4l2" != "xno"]) +AM_CONDITIONAL([HAVE_LIBV4L], [test "x$have_libv4l" != "xno"]) +AM_CONDITIONAL([WITH_DIRECTSHOW], [test "x$with_directshow" != "xno"]) + +dnl X +AC_ARG_VAR([XSHM_LIBS], [linker flags for X shared memory extension]) + +AS_IF([test "x$win32" != "xno"], + [have_x="no"], + [AC_PATH_XTRA + AH_BOTTOM([#ifndef X_DISPLAY_MISSING +# define HAVE_X +#endif +])]) +AM_CONDITIONAL([HAVE_X], [test "x$have_x" = "xyes"]) + +AS_IF([test "x$XSHM_LIBS" = "x"], [XSHM_LIBS="-lXext"]) +AC_ARG_WITH([xshm], + [AS_HELP_STRING([--without-xshm], + [disable support for X shared memory extension])], + [], + [with_xshm="check"]) + +AS_IF([test "x$with_xshm" != "xno"], + [AC_CHECK_HEADERS([X11/extensions/XShm.h], + [with_xshm="yes"], + [AS_IF([test "x$with_xshm" = "xcheck"], + [with_xshm="no"], + [AC_MSG_FAILURE([test for X shared memory extension failed! +install the X shared memory extension, specify --x-includes or +configure --without-xshm to disable the extension])])], + [[#include <X11/Xlib.h> +#include <sys/ipc.h> +#include <sys/shm.h> +]]) + AS_IF([test "x$with_xshm" != "xno"], + [AC_CHECK_LIB([Xext], [XShmQueryVersion], + [with_xshm="yes"], + [AC_MSG_FAILURE([unable to find XShmQueryVersion in $XSHM_LIBS! +specify XSHM_LIBS or configure --without-xshm to disable the extension])], + ["$X_LIBS" "$X_PRE_LIBS" -lX11 "$X_EXTRA_LIBS" "$XSHM_LIBS"]) + ]) +]) +AM_CONDITIONAL([HAVE_XSHM], [test "x$with_xshm" = "xyes"]) + +AC_ARG_VAR([XV_LIBS], [linker flags for XVideo extension]) +AS_IF([test "x$XV_LIBS" = "x"], [XV_LIBS="-lXv"]) +AC_ARG_WITH([xv], + [AS_HELP_STRING([--without-xv], + [disable support for XVideo extension])], + [], + [with_xv="check"]) + +AS_IF([test "x$with_xv" != "xno"], + [AC_CHECK_HEADERS([X11/extensions/Xvlib.h], + [with_xv="yes"], + [AS_IF([test "x$with_xv" = "xcheck"], + [with_xv="no"], + [AC_MSG_FAILURE([test for XVideo extension failed! +install the XVideo extension, specify --x-includes or +configure --without-xv to disable the extension])])], + [[#include <X11/Xlib.h> +]]) + AS_IF([test "x$with_xv" != "xno"], + [AC_CHECK_LIB([Xv], [XvQueryExtension], + [with_xv="yes"], + [AC_MSG_FAILURE([unable to find XvQueryExtension in $XV_LIBS! +specify XV_LIBS or configure --without-xv to disable the extension])], + ["$X_LIBS" "$X_PRE_LIBS" -lX11 "$X_EXTRA_LIBS" "$XV_LIBS"]) + ]) +]) +AM_CONDITIONAL([HAVE_XV], [test "x$with_xv" = "xyes"]) + +dnl dbus +AC_ARG_WITH([dbus], + [AS_HELP_STRING([--without-dbus], + [disable support for dbus])], + [], + [with_dbus="check"]) + +AS_IF([test "x$with_dbus" != "xno"], + [PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, have_dbus="yes", have_dbus="no") + AS_IF([test "x$have_dbus$with_dbus" = "xnoyes"], + [AC_MSG_FAILURE([DBus development libraries not found])], + [with_dbus="$have_dbus"]) +]) +AM_CONDITIONAL([HAVE_DBUS], [test "x$with_dbus" = "xyes"]) + +AS_IF([test "x$with_dbus" = "xyes"], + [CPPFLAGS="$CPPFLAGS $DBUS_CFLAGS" + AC_ARG_VAR([DBUS_LIBS], [linker flags for building dbus]) + AC_DEFINE([HAVE_DBUS], [1], [Define to 1 to use dbus]) + AC_ARG_WITH(dbusconfdir, AS_HELP_STRING([--with-dbusconfdir=PATH],[path to D-Bus config directory]), + [path_dbusconf=$withval], + [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"]) + AS_IF([test -z "$path_dbusconf"], + DBUS_CONFDIR="$sysconfdir/dbus-1/system.d", + DBUS_CONFDIR="$path_dbusconf/dbus-1/system.d") + AC_SUBST(DBUS_CONFDIR) +]) + +dnl libjpeg +AC_ARG_WITH([jpeg], + [AS_HELP_STRING([--without-jpeg], + [disable support for JPEG image conversions])], + [], + [with_jpeg="check"]) + +have_jpeg="maybe" +AS_IF([test "x$with_jpeg" != "xno"], + [AC_CHECK_HEADERS([jpeglib.h jerror.h], [], [have_jpeg="no"]) + AC_CHECK_LIB([jpeg], [jpeg_read_header], [], [have_jpeg="no"]) + AS_IF([test "x$have_jpeg" != "xno"], + [with_jpeg="yes"], + [test "x$with_jpeg" = "xyes"], + [AC_MSG_FAILURE([unable to find libjpeg! ensure CFLAGS/LDFLAGS are +set appropriately or configure --without-jpeg])], + [with_jpeg="no"]) +]) +AM_CONDITIONAL([HAVE_JPEG], [test "x$with_jpeg" = "xyes"]) + +dnl ImageMagick or GraphicsMagick +dnl disable both if IM is explicitly disabled to preserve old behavior + +AC_ARG_WITH([imagemagick], + [AS_HELP_STRING([--without-imagemagick], + [disable support for scanning images with ImageMagick])], + [], + [with_imagemagick="check"]) + +AC_ARG_WITH([graphicsmagick], + [AS_HELP_STRING([--with-graphicsmagick], + [use GraphicsMagick alternative to ImageMagick])], + [], + [with_graphicsmagick="check"]) + +magick="UnknownMagick" +have_IM="maybe" +AS_IF([test "x$with_imagemagick" = "xno"], [], + [test "x$with_imagemagick" = "xyes" || \ + test "x$with_graphicsmagick" != "xyes"], + [looked_for="ImageMagick >= 6.2.6" + PKG_CHECK_MODULES([MAGICK], [MagickWand >= 6.2.6], + [MAGICK_VERSION=`$PKG_CONFIG MagickWand --modversion`], + [dnl +dnl Wand is deprecated in favor of MagickWand, +dnl but the latter doesn't exist in older versions (bug #2848437) + saved_error=$MAGICK_PKG_ERRORS + PKG_CHECK_MODULES([MAGICK], [Wand >= 6.2.6], + [MAGICK_VERSION=`$PKG_CONFIG Wand --modversion`], + [have_IM="no"])]) + AS_IF([test "x$have_IM" != "xno"], + [magick="ImageMagick" + AC_MSG_NOTICE([trying ImageMagick version $MAGICK_VERSION]) +dnl double check ImageMagick install (bug #2582232) + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $MAGICK_CFLAGS" + AC_CHECK_HEADERS([wand/MagickWand.h], + [ + have_IM="yes" + with_imagemagick="yes" + ], + [have_IM="broken"]) +dnl check for ImageMagick 7, see https://imagemagick.org/script/porting.php#headers + AS_IF([test "x$have_IM" = "xbroken"], [ + AC_CHECK_HEADERS([MagickWand/MagickWand.h], + [ + have_IM="yes" + have_IM7="yes" + with_imagemagick="yes" + ], + [have_IM="broken"]) + ]) + CPPFLAGS="$CPPFLAGS_save"])]) + +have_GM="maybe" +AS_IF([test "x$have_IM" = "xyes"], [], + [test "x$with_graphicsmagick" = "xno"], [], + [test "x$with_graphicsmagick" = "xyes" || \ + test "x$with_imagemagick" = "xcheck"], + [AS_IF([test "x$looked_for" = "x"], + [looked_for="GraphicsMagick"], + [looked_for="$looked_for or GraphicsMagick"]) + PKG_CHECK_MODULES([GM], [GraphicsMagickWand], + [have_GM="yes" + with_imagemagick="no" + magick="GraphicsMagick" + MAGICK_CFLAGS="$MAGICK_CFLAGS $GM_CFLAGS" + MAGICK_LIBS="$MAGICK_LIBS $GM_LIBS" + MAGICK_VERSION=`$PKG_CONFIG GraphicsMagickWand --modversion`], + [have_GM="no" + AS_IF([test "x$saved_error" = "x"], + [saved_error=$MAGICK_PKG_ERRORS])])]) + +dnl now that we have collected all the info abt what Magick is available +dnl let the user know what we will or can't do +AS_IF([test "x$have_IM" = "xbroken" && test "x$have_GM" = "xyes"], + [ + with_imagemagick="no" + AC_MSG_WARN([Your ImageMagick install is broken, using GraphicsMagick instead]) + ]) +AS_IF([test "x$have_IM" = "xyes" || test "x$have_GM" = "xyes"], + [AC_MSG_NOTICE([using $magick version $MAGICK_VERSION])], + [test "x$with_imagemagick" = "xno" && \ + test "x$with_graphicsmagick" != "xyes"], + [AC_MSG_NOTICE([image scanning disabled -- zbarimg will *not* be built])], + [test "x$have_IM" = "xbroken"], + [AC_MSG_FAILURE([$magick package found but wand/MagickWand.h not installed?! +this is a problem with your $magick install, please try again after +resolving the inconsistency or installing GraphicsMagick alternative...])], + [test "x$with_graphicsmagick" = "xcheck"], + [AC_MSG_NOTICE([ImageMagick/GraphicsMagick not detected. Several features will be disabled]) + with_imagemagick="no"], + [AC_MSG_FAILURE([dnl +Unable to find $looked_for: + +$saved_error + +* Ensure that you installed any "development" packages for ImageMagick or + GraphicsMagick. +* Consider adjusting the PKG_CONFIG_PATH environment variable if you + installed software in a non-standard prefix. +* You may set the environment variables MAGICK_CFLAGS and MAGICK_LIBS + to avoid the need to call pkg-config. + See the pkg-config man page for more details. +* To avoid using ImageMagick or GraphicsMagick altogether you may add the + --without-imagemagick flag to the configure command; the zbarimg program + will *not* be built. +])]) + +AS_IF([test "x$have_IM" = "xyes"], + [AC_DEFINE([HAVE_IMAGEMAGICK], [1], [Define to 1 to use ImageMagick])], + [test "x$have_GM" = "xyes"], + [AC_DEFINE([HAVE_GRAPHICSMAGICK], [1], [Define to 1 to use GraphicsMagick])]) +AS_IF([test "x$have_IM7" = "xyes"], + [AC_DEFINE([HAVE_IMAGEMAGICK7], [1], [Define to 1 to use ImageMagick 7])]) +AM_CONDITIONAL([HAVE_MAGICK], + [test "x$have_IM" = "xyes" || test "x$have_GM" = "xyes"]) + +dnl Mozilla NPAPI +AC_ARG_WITH([npapi], + [AS_HELP_STRING([--with-npapi], + [enable support for Firefox/Mozilla/OpenOffice plugin])], + [], + [with_npapi="no"]) + +AS_IF([test "x$with_npapi" != "xno"], + [PKG_CHECK_MODULES([NPAPI], [firefox-plugin]) + NPAPI_VERSION=`$PKG_CONFIG firefox-plugin --modversion` + AC_MSG_NOTICE([using firefox-plugin version $NPAPI_VERSION])]) + +AM_CONDITIONAL([HAVE_NPAPI], [test "x$with_npapi" = "xyes"]) + +dnl GTK +dnl For now, defaults to GTK version 3 + +AC_ARG_WITH([gtk], + [AS_HELP_STRING([--with-gtk], + [Specify support for GTK. Valid values are: no, auto, gtk2, gtk3 (default is auto)])], + [AS_IF([test "x$with_gtk" != "xno" && test "x$with_gtk" != "xauto" && + test "x$with_gtk" != "xgtk2" && test "x$with_gtk" != "xgtk3"], + [echo "Invalid value for --with-gtk. Falling back to 'no'" + with_gtk="no"])], + [with_gtk="auto"]) + +AC_ARG_VAR([GLIB_GENMARSHAL], [full path to glib-genmarshal]) +AC_ARG_VAR([GTK_VERSION_MAJOR]) + +AS_IF([test "x$with_gtk" = "xgtk3" || test "x$with_gtk" = "xauto"], + [PKG_CHECK_MODULES([GTK3], [gtk+-3.0], + [GLIB_GENMARSHAL=`$PKG_CONFIG glib-2.0 --variable=glib_genmarshal` + GTK_VERSION=`$PKG_CONFIG gtk+-3.0 --modversion` + AC_MSG_NOTICE([using GTK+ version $GTK_VERSION]) + GTK_VERSION_MAJOR=3.0 + with_gtk="gtk3" + GTK_CFLAGS=$GTK3_CFLAGS + GTK_LIBS=$GTK3_LIBS + ],[libgtk3=false]) + ]) + +AS_IF([test "x$with_gtk" = "xgtk2" || test "x$with_gtk" = "xauto"], + [PKG_CHECK_MODULES([GTK2], [gtk+-2.0], + [GLIB_GENMARSHAL=`$PKG_CONFIG glib-2.0 --variable=glib_genmarshal` + GTK_VERSION=`$PKG_CONFIG gtk+-2.0 --modversion` + AC_MSG_NOTICE([using GTK+ version $GTK_VERSION]) + GTK_VERSION_MAJOR=2.0 + with_gtk="gtk2" + GTK_CFLAGS=$GTK2_CFLAGS + GTK_LIBS=$GTK2_LIBS + ],[libgtk2=false]) + ]) + +dnl GTK not found +AS_IF([test "x$with_gtk" = "xauto"], [with_gtk="no"]) + +AC_SUBST(GTK_LIBS) +AC_SUBST(GTK_CFLAGS) + +AM_CONDITIONAL([HAVE_GTK], [test "x$with_gtk" != "xno"]) + +AC_ARG_WITH([gir], + [AS_HELP_STRING([--with-gir], [enable support for GObject Introspection])], + [], + [with_gir="yes"]) + +dnl Python +dnl For now, keep python2 as default +dnl If both PYTHON and --with-python=foo are defined, PYTHON takes precedence + +AC_ARG_WITH([python], + [AS_HELP_STRING([--with-python], + [Specify support for Python. Valid values are: no, auto, python2, python3 +(default is auto). +Please notice that PYTHON var, if especified, takes precedence.])], + [AS_IF([test "x$with_python" != "xno" && test "x$with_python" != "xauto" && + test "x$with_python" != "xpython2" && test "x$with_python" != "xpython3"], + [echo "Invalid value for --with-python. Falling back to 'no'" + with_python="xno"])], + [with_python="auto"]) + +AC_ARG_VAR([PYTHON_CONFIG], [full path to python-config program]) +AC_ARG_VAR([PYTHON_CFLAGS], [compiler flags for building python extensions]) +AC_ARG_VAR([PYTHON_LDFLAGS], [linker flags for building python extensions]) + +AC_ARG_VAR([PYGTK_H2DEF], [full path to PyGTK h2def.py module (python2 only)]) +AC_ARG_VAR([PYGTK_CODEGEN], [full path to pygtk-codegen program (python2 only)]) +AC_ARG_VAR([PYGTK_DEFS], [directory where PyGTK definitions may be found (python2 only)]) + +AS_IF([test -z "$PYTHON"], + [AS_IF([test "x$with_python" = "xauto"], + [AC_PATH_PROGS([PYTHON], [python3 python2 python], [:], [$PATH])], + [AS_IF([test "x$with_python" = "xpython3"], + [AC_PATH_PROGS([PYTHON], [python3 python], [:], [$PATH])], + [AS_IF([test "x$with_python" = "xpython2"], + [AC_PATH_PROGS([PYTHON], [python2 python], [:], [$PATH])], + [with_python="no"]) + ]) + ] + )], + [with_python="auto"] +) + +AS_IF([test "x$with_python" != "xno"], + [AM_PATH_PYTHON([2.7.0])]) + +AS_IF([test "x$PYTHON_VERSION" != "x" && test "x$with_python" != "xno"], + [PYTHON_VERSION_MAJOR="`echo $PYTHON_VERSION | cut -d'.' -f 1`" + + AS_IF([test "x$PYTHON_CFLAGS" != "x"], + [], + [test "x$PYTHON_CONFIG" != "x" && test -x "$PYTHON_CONFIG"], + [PYTHON_CFLAGS=`$PYTHON_CONFIG --cflags`], + [test -x "$PYTHON-config"], + [PYTHON_CFLAGS=`$PYTHON-config --cflags`], + [PYTHON_CFLAGS=`$PYTHON -c 'import distutils.sysconfig as s, sys; sys.stdout.write(" ".join(s.get_config_vars("CFLAGS")) + " -I"+s.get_python_inc() + " -I"+s.get_python_inc(plat_specific=True))'`]) + AS_IF([test "x$PYTHON_LDFLAGS" != "x"], + [], + [test "x$PYTHON_CONFIG" != "x" && test -x "$PYTHON_CONFIG"], + [PYTHON_LDFLAGS=`$PYTHON_CONFIG --ldflags`], + [test -x "$PYTHON-config"], + [PYTHON_LDFLAGS=`$PYTHON-config --ldflags`], + [PYTHON_LDFLAGS=`$PYTHON -c 'import distutils.sysconfig as s, sys; sys.stdout.write(" ".join(s.get_config_vars("LDFLAGS")) + " -I"+s.get_python_inc() + " -I"+s.get_python_inc(plat_specific=True))'`]) + + dnl check that #include <Python.h> compiles (bug #3092663) + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $PYTHON_CFLAGS" + AS_IF([test "x$win32" = "xyes"], PYTHON_LDFLAGS="$PYTHON_LDFLAGS -no-undefined") + AC_CHECK_HEADERS([Python.h], [], + [AS_IF([test "x$with_python" = "xauto"],with_python="no", + [AC_MSG_ERROR([dnl +Python module enabled, but unable to compile Python.h. +Install the development package for python-$am_cv_python_version, or configure +--without-python to disable the python bindings.dnl +])])]) + CPPFLAGS="$CPPFLAGS_save" + + dnl PyGTK + dnl disable pygtk when we're on Python 3 + AS_IF([test "x$with_gtk" != "xno"], + [AS_IF([test "x$PYTHON_VERSION_MAJOR" = "x2"], + [PKG_CHECK_MODULES([PYGTK], [pygtk-2.0], [with_pygtk2="yes"], [with_pygtk2="no"]) + AC_CHECK_PROGS([PYGTK_CODEGEN], [pygobject-codegen-2.0 pygtk-codegen-2.0 pygtk-codegen], [:]) + + AS_IF([test "x$PYGTK_H2DEF" = "x"], + [PYGTK_H2DEF=`$PKG_CONFIG pygtk-2.0 --variable=codegendir`/h2def.py + AS_IF([test -f "$PYGTK_H2DEF"], [], [PYGTK_H2DEF=":"])]) + AS_IF([test "x$PYGTK_DEFS" = "x"], + [PYGTK_DEFS=`$PKG_CONFIG pygtk-2.0 --variable=defsdir`]) + ]) + ]) +], [with_python="no"]) + +AS_IF([test "x$PYTHON_VERSION_MAJOR" != "x2"], [with_pygtk2="no"]) +AM_CONDITIONAL([HAVE_PYTHON], [test "x$with_python" != "xno"]) +AM_CONDITIONAL([HAVE_PYGTK2], [test "x$with_pygtk2" != "xno"]) + +dnl GObject Introspection (GIR) + +AS_IF([test "x$with_gir" = "xyes" && test "x$with_gtk" != "xno"], + [m4_ifdef([GOBJECT_INTROSPECTION_CHECK], + [GOBJECT_INTROSPECTION_CHECK([0.6.7]) + AS_IF([test "x$found_introspection" = "xyes"], + [INTROSPECTION_TYPELIBDIR=`$PKG_CONFIG --variable=typelibdir --define-variable="libdir=${libdir}" gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir --define-variable="datadir=${datadir}" gobject-introspection-1.0` + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_GIRDIR)])]) + ]) + +AS_IF([test "x$found_introspection" != "xyes"], [with_gir="no"]) +AM_CONDITIONAL([HAVE_INTROSPECTION], [test "x$with_gir" = "xyes"]) + +dnl Qt +AC_ARG_WITH([qt], + [AS_HELP_STRING([--without-qt], + [disable support for Qt widget])], + [], + [with_qt="yes"]) + +dnl Qt6 +AC_ARG_WITH([qt6], + [AS_HELP_STRING([--with-qt6], + [If --with-qt, enable experimental support for Qt6 widget (currently broken)])], + [], + [with_qt6="no"]) + +AS_IF([test "x$with_qt" != "xno"], + [AS_IF([test "x$with_qt6" != "xno"], + PKG_CHECK_MODULES([QT], + [Qt6], + [MOC=`pkg-config Qt6 --variable=moc` + QT_VERSION=`$PKG_CONFIG Qt6 --modversion` + QT6_HEADERS=`pkg-config Qt6 --variable=headerdir` + CPPFLAGS="$CPPFLAGS -I$QT6_HEADERS -I$QT6_HEADERS/QtWidgets -I$QT6_HEADERS/QtCore -I$QT6_HEADERS/QtGui" + qt_pkgconfig_file="zbar-qt5.pc" + ], + [with_qt6="no"])) + AS_IF([test "x$with_qt6" = "xno"], + PKG_CHECK_MODULES([QT], + [Qt5Core >= 5 Qt5Gui >= 5 Qt5Widgets >= 5.0 Qt5X11Extras >= 5.0], + [MOC=`pkg-config Qt5 --variable=moc` + QT_VERSION=`$PKG_CONFIG Qt5 --modversion` + qt_pkgconfig_file="zbar-qt5.pc" + ], + [with_qt="no"])) + ]) + +AC_ARG_VAR([MOC], [full path to Qt moc program]) + +AM_CONDITIONAL([HAVE_QT], [test "x$with_qt" = "xyes"]) + +AM_COND_IF([HAVE_QT], + [AC_MSG_NOTICE([using Qt version $QT_VERSION]) + AC_MSG_NOTICE([using moc from $MOC]) + CPPFLAGS="$CPPFLAGS $QT_CPPFLAGS" + LIBS="$LIBS $QT_LIBS" + AC_CONFIG_FILES([zbar-qt.pc:"${qt_pkgconfig_file}.in"]) + #dnl -fPIC has no effect on Windows and breaks windres + AS_IF([test "x$win32" = "xno"], [CPPFLAGS="$CPPFLAGS -fPIC"])]) + +dnl Java +have_java="maybe" + +AC_ARG_VAR([JAVA_HOME], [root location of JDK]) + +AC_ARG_VAR([JAVAC], [location of Java language compiler]) +AC_ARG_VAR([JAVAH], [location of Java header generator]) + +dnl If $JAVA_HOME not defined, try to autodetect it +AS_IF([test -z "$JAVA_HOME"], + [AC_PATH_PROGS([JAVAC], [javac jikes ecj gcj], [:], [$PATH]) + AS_IF([test ! -z "$JAVAC"], + [JAVA_HOME=$( readlink -f ${JAVAC} | rev | cut -d/ -f3- | rev )])]) + +dnl If $JAVA_HOME is defined, set JAVA_PATH and JAVAC +AS_IF([test ! -z "$JAVA_HOME"], + [JAVA_PATH="$JAVA_HOME/bin$PATH_SEPARATOR$PATH" + AS_IF([test -z "$JAVAC"], + [AC_PATH_PROGS([JAVAC], [javac jikes ecj gcj], [:], [$JAVA_PATH])])]) + +AC_ARG_WITH([java], + [AS_HELP_STRING([--without-java], + [disable support for Java interface])], + [], + [with_java="check"]) + +JAVAC=$(echo $JAVAC |sed 's/ecj/ecj -1.5/') + +dnl Javah was obsoleted on Java 8 and removed on Java 11. So, we need to +dnl look strictly at the $JAVA_HOME in order to avoid mixing different versions +AS_IF([test -z "$JAVAH"], + [AC_PATH_PROGS([JAVAH], [javah], [], [$JAVA_HOME/bin])]) + +AM_CONDITIONAL([HAVE_JAVAH], [test "x$JAVAH" != "x"]) + +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 Search for Java unit test library +AS_IF([test -z "$JUNIT_HOME"], + [JUNIT_HOME="/usr/share/java"]) + +AS_IF([test -f "$JUNIT_HOME/junit4.jar"], + [JUNIT="$JUNIT_HOME/junit4.jar"], + [AS_IF([test -f "$JUNIT_HOME/junit.jar"], + [JUNIT="$JUNIT_HOME/junit.jar"])]) + +AS_IF([test "x$JUNIT" != "x"], + [AS_IF([test -f "/usr/share/java/hamcrest/all.jar"], + [CLASSPATH="$JUNIT:/usr/share/java/hamcrest/all.jar:$CLASSPATH" + AC_SUBST(CLASSPATH) + with_java_unit="yes"])], + [AS_IF([test -f "/usr/share/java/hamcrest-all.jar"], + [CLASSPATH="$JUNIT:/usr/share/java/hamcrest-all.jar:$CLASSPATH" + AC_SUBST(CLASSPATH) + with_java_unit="yes"])]) + +AM_CONDITIONAL([HAVE_JAVA_UNIT], [test "x$with_java_unit" = "xyes"]) + +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 -d "$JAVA_HOME/include/linux"], + [JAVA_CFLAGS="$JAVA_CFLAGS -I$JAVA_HOME/include/linux"]) + +AS_IF([test "x$with_java" != "xno"], + [CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $JAVA_CFLAGS" + AC_CHECK_HEADERS([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([errno.h fcntl.h features.h inttypes.h float.h limits.h \ + locale.h stddef.h stdlib.h string.h unistd.h sys/types.h sys/stat.h \ + sys/ioctl.h sys/time.h sys/times.h sys/ipc.h sys/shm.h sys/mman.h]) +AC_HEADER_MAJOR +AC_CHECK_HEADER_STDBOOL + +dnl types + +AC_TYPE_INT32_T +AC_TYPE_UINT32_T +AC_TYPE_UINT8_T +AC_TYPE_UINTPTR_T +AC_TYPE_UID_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T +AC_CHECK_MEMBERS([struct stat.st_rdev]) + +dnl compile characteristics + +AC_C_CONST +AC_C_INLINE + +dnl functions + +AC_FUNC_MMAP +AC_CHECK_FUNCS([alarm clock_gettime floor getcwd gettimeofday localeconv memchr memmove memset modf munmap pow select setenv sqrt strcasecmp strchr strdup strerror strrchr strstr strtol strtoul malloc realloc]) + + +dnl output generation + +dnl avoid doc rebuilds unless revision info changes +AC_CONFIG_COMMANDS([doc/version.xml], + [AS_IF([test -f doc/version.xml && \ + ! echo $VERSION | diff doc/version.xml - >/dev/null 2>&1 || \ + ! echo $VERSION | diff $srcdir/doc/version.xml - >/dev/null 2>&1 ], + [echo "writing new doc/version.xml" ; echo $VERSION > $srcdir/doc/version.xml ])], + [VERSION="$VERSION"] +) +AC_CONFIG_COMMANDS([doc/reldate.xml], + [AS_IF([test -f doc/reldate.xml && \ + ! echo $RELDATE | diff doc/reldate.xml - >/dev/null 2>&1 || \ + ! echo $RELDATE | diff $srcdir/doc/reldate.xml - >/dev/null 2>&1 ], + [echo "writing new doc/reldate.xml" ; echo $RELDATE > $srcdir/doc/reldate.xml ])], + [RELDATE="$RELDATE"] +) + +echo "Generating config files" + +AC_CONFIG_FILES([ +Makefile +gtk/Makefile +java/Makefile +po/Makefile.in +zbar/Makefile +zbar.pc +zbar-gtk.pc +doc/doxygen.conf]) + +AC_CONFIG_FILES([test/test_examples.sh],[chmod 755 test/test_examples.sh]) +AC_CONFIG_FILES([test/check_dbus.sh],[chmod 755 test/check_dbus.sh]) + +AC_OUTPUT + +dnl summary log + +echo "" +echo "please verify that the detected configuration matches your expectations:" +echo "------------------------------------------------------------------------" +AS_IF([test "x$USE_NLS" = "xyes"], + [echo "gettext $USE_NLS"] +) +AS_IF([test "x$win32" != "xno"], + [AS_IF([test "x$with_directshow" != "xno"], + [echo "DirectShow driver --with-directshow=$with_directshow"], + [echo "VfW driver --with-directshow=$with_directshow"] + )], + [echo "X --with-x=$have_x"] +) + +AS_IF([test "x$with_python" != "xno"], pyver="\tpython${PYTHON_VERSION}") +AS_IF([test "x$with_gtk" != "xno"], gtkver="\tGtk${GTK_VERSION}") +AS_IF([test "x$with_qt" != "xno"], qtver="\tQt${QT_VERSION}") + +echo "pthreads --enable-pthread=$enable_pthread" +echo "doc --enable-doc=$enable_doc" +echo "v4l --enable-video=$enable_video" +echo "jpeg --with-jpeg=$with_jpeg" +echo -e "Python --with-python=$with_python $pyver" +echo -e "GTK --with-gtk=$with_gtk $gtkver" +echo "GObject introspection --with-gir=$with_gir" +echo -e "Qt --with-qt=$with_qt $qtver" +echo "Java --with-java=$with_java" + +AS_IF([test "x$win32" = "xno"], + [echo "Dbus --with-dbus=$with_dbus"]) +AS_IF([test "x$have_GM" = "xyes"], + [echo "GraphicsMagick --with-graphicsmagick=yes"], + [echo "ImageMagick --with-imagemagick=$with_imagemagick"]) +echo "Enabled codes: $enabled_codes" +echo "Disabled codes: $disabled_codes" +AS_IF([test "x$with_java" = "xyes"], + [echo "JAVA_HOME $JAVA_HOME"]) + +dnl Display "warnings" about disabled and experimental features + +echo "" +AS_IF([test "x$enable_video" != "xyes"], + [echo " => zbarcam video scanner will *NOT* be built"]) +AS_IF([test "x$have_libv4l" != "xyes"], + [echo " => libv4l will *NOT* be used"]) +AS_IF([test "x$with_jpeg" != "xyes"], + [echo " => JPEG image conversions will *NOT* be supported"]) +AS_IF([test "x$have_IM" != "xyes" && test "x$have_GM" != "xyes"], + [echo " => the zbarimg file scanner will *NOT* be built"]) +AS_IF([test "x$have_GM" = "xyes"], + [echo " => ImageMagick is preferred, as GraphicsMagick doesn't support https"]) +AS_IF([test "x$with_gtk" = "xno"], + [echo " => GTK support will *NOT* be built"]) +AS_IF([test "x$with_pygtk2" != "xyes" && test "xPYTHON_VERSION_MAJOR" = "x2"], + [echo " => the Python 2 GTK widget wrapper will *NOT* be built"]) +AS_IF([test "x$with_qt" != "xyes"], + [echo " => the Qt widget will *NOT* be built"]) +AS_IF([test "x$with_qt" = "xyes" && test "x$enable_static_qt" = "xyes" ], + [echo " => Building a static Qt library"]) +AS_IF([test "x$with_java" != "xyes"], + [echo " => the Java interface will *NOT* be built"]) +AS_IF([test "x$with_java_unit" != "xyes"], + [echo " => the Java unit test will *NOT* be enabled"]) +dnl echo "NPAPI Plugin --with-npapi=$with_npapi" +dnl AS_IF([test "x$with_mozilla" != "xyes"], +dnl [echo " => the Mozilla/Firefox/OpenOffice plugin will *NOT* be built"]) +AS_IF([test "x$enable_pdf417" = "xyes"], + [echo " => the pdf417 code support is incomplete!"]) diff --git a/dbus/org.linuxtv.Zbar.conf b/dbus/org.linuxtv.Zbar.conf new file mode 100644 index 0000000..602922d --- /dev/null +++ b/dbus/org.linuxtv.Zbar.conf @@ -0,0 +1,15 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy context="default"> + <allow own="org.linuxtv.Zbar"/> + <allow send_destination="org.linuxtv.Zbar"/> + <allow send_destination="org.linuxtv.Zbar" + send_interface="org.linuxtv.Zbar1.Code"/> + <allow send_destination="org.linuxtv.Zbar" + send_interface="org.freedesktop.DBus.Properties"/> + <allow send_destination="org.linuxtv.Zbar" + send_interface="org.freedesktop.DBus.Introspectable"/> + </policy> +</busconfig> diff --git a/doc/Makefile.am.inc b/doc/Makefile.am.inc new file mode 100644 index 0000000..2ce509e --- /dev/null +++ b/doc/Makefile.am.inc @@ -0,0 +1,55 @@ +# documentation sources +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 = +if HAVE_MAGICK +dist_man_MANS += doc/man/zbarimg.1 +endif +if HAVE_VIDEO +dist_man_MANS += doc/man/zbarcam.1 +endif + +# witness to man page build (many-to-many workaround) +man_stamp = doc/man/man.stamp + +# TBD add manual content +#dist_doc_DATA = doc/zbar.pdf doc/zbar.html + +# distribute all documentation related files to avoid end-user rebuilds +EXTRA_DIST += $(DOCSOURCES) $(man_stamp) +EXTRA_DIST += doc/api/footer.html doc/style.xsl + +docs: $(dist_man_MANS) #dist_doc_DATA + +PHONY += docs + +doc_path = --searchpath $(abs_builddir)/doc -m $(abs_srcdir)/doc/style.xsl +# xmlto --searchpath broken again... +doc_path += --skip-validation + +#pdf: doc/zbar-manual.pdf +#doc/zbar-manual.pdf: $(DOCSOURCES) +# $(XMLTO) $(XMLTOFLAGS) -o doc pdf $< + +html-local: doc/html/index.html +doc/html/index.html: $(DOCSOURCES) + $(XMLTO) $(doc_path) $(XMLTOFLAGS) -o doc/html xhtml $< + +CLEANFILES += doc/html/*.html + +$(dist_man_MANS): $(man_stamp) + @if test ! -f $@ ; then \ + rm -f $(man_stamp) ; \ + $(MAKE) $(AM_MAKEFLAGS) $(man_stamp) ; \ + fi + +$(man_stamp): $(DOCSOURCES) + @$(mkdir_p) doc/man 2>/dev/null + @rm -f $(man_stamp).tmp + @touch $(man_stamp).tmp + $(XMLTO) $(doc_path) $(XMLTOFLAGS) -o doc/man man $< + @mv $(man_stamp).tmp $(man_stamp) diff --git a/doc/api/footer.html b/doc/api/footer.html new file mode 100644 index 0000000..2eefbb2 --- /dev/null +++ b/doc/api/footer.html @@ -0,0 +1,20 @@ + +<hr></hr> +<div style="float: right; clear: right"> + <a href="http://sourceforge.net"> + <img style="border: none" src="http://sflogo.sourceforge.net/sflogo.php?group_id=189236&type=3" + width="125" height="37" alt="SourceForge.net Logo"/> + </a> +</div> + +<address><a href="mailto:spadix@users.sourceforge.net">spadix@users.sourceforge.net</a></address> + +<p>Copyright 2008-2010 (c) Jeff Brown</p> +<p>This documentation is part of the ZBar Barcode Reader; you can +redistribute it and/or modify it under the terms of the +<a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">GNU +Lesser General Public License</a> as published by the Free Software +Foundation; either version 2.1 of the License, or (at your option) any +later version.</p> + +</body></html> diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in new file mode 100644 index 0000000..bd6a9bc --- /dev/null +++ b/doc/doxygen.conf.in @@ -0,0 +1,39 @@ +PROJECT_NAME = "ZBar Bar Code Reader Library" +PROJECT_NUMBER = "version @VERSION@" + +INPUT = @top_srcdir@/include +RECURSIVE = YES +EXCLUDE = @top_srcdir@/include/zbar/zbargtk.h +EXCLUDE_PATTERNS = */.svn/* */.hg/* +STRIP_FROM_PATH = @top_srcdir@ + +OUTPUT_DIRECTORY = doc/api +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English + +SOURCE_BROWSER = NO +VERBATIM_HEADERS = NO +TAB_SIZE = 4 + +JAVADOC_AUTOBRIEF = YES + +SUBGROUPING = NO +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +TYPEDEF_HIDES_STRUCT = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = YES +INTERNAL_DOCS = NO +MAX_INITIALIZER_LINES = 0 +SHOW_INCLUDE_FILES = NO +EXTRACT_STATIC = YES + +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES + +GENERATE_XML = YES +HTML_HEADER = +HTML_FOOTER = @top_srcdir@/doc/api/footer.html +HTML_STYLESHEET = diff --git a/doc/manual.xml b/doc/manual.xml new file mode 100644 index 0000000..549d42c --- /dev/null +++ b/doc/manual.xml @@ -0,0 +1,50 @@ +<?xml version='1.0'?> + +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" + "file:///usr/share/sgml/docbook/xml-dtd-4.4/docbookx.dtd" [ + + <!ENTITY version SYSTEM "version.xml"> + <!ENTITY date SYSTEM "reldate.xml"> + + <!ENTITY refcommonoptions SYSTEM "ref/commonoptions.xml"> + <!ENTITY refzbarimg SYSTEM "ref/zbarimg.xml"> + <!ENTITY refzbarcam SYSTEM "ref/zbarcam.xml"> +]> + +<book> + <title>ZBar Barcode Reader</title> + <bookinfo> + <productname>zbar-&version;</productname> + <date>&date;</date> + + <author> + <firstname>Jeff</firstname><surname>Brown</surname> + <affiliation> + <address><email>spadix@users.sourceforge.net</email></address> + </affiliation> + <contrib>Lead developer</contrib> + </author> + + <copyright> + <year>2007</year> + <year>2008</year> + <year>2009</year> + <year>2010</year> + <holder>Jeff Brown</holder> + </copyright> + <legalnotice><para>All Rights Reserved</para></legalnotice> + </bookinfo> + + <chapter id="intro"> + <title>Introduction</title> + <para></para> + </chapter> + + <reference id="ref"> + <title>ZBar Barcode Reader - Command Reference</title> + + &refzbarcam; + &refzbarimg; + + </reference> +</book> diff --git a/doc/ref/commonoptions.xml b/doc/ref/commonoptions.xml new file mode 100644 index 0000000..3552025 --- /dev/null +++ b/doc/ref/commonoptions.xml @@ -0,0 +1,165 @@ +<varlistentry> + <term><option>-h</option></term> + <term><option>--help</option></term> + <listitem> + <simpara>Print a short help message describing command line + options to standard output and exit</simpara> + </listitem> +</varlistentry> + +<varlistentry> + <term><option>--version</option></term> + <listitem> + <simpara>Print program version information to standard output and + exit</simpara> + </listitem> +</varlistentry> + +<varlistentry> + <term><option>-v</option></term> + <term><option>--verbose<optional>=<replaceable + class="parameter">n</replaceable></optional></option></term> + <listitem> + <simpara>Increase debug output level. Multiple + <option>-v</option> options create more spew. Alternatively + specify <replaceable class="parameter">n</replaceable> to + set the debug level directly + </simpara> + </listitem> +</varlistentry> + +<varlistentry> + <term><option>-S<optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></term> + <term><option>--set <optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></term> + <listitem> + <simpara>Set decoder configuration option <replaceable + class="parameter">config</replaceable> for <replaceable + class="parameter">symbology</replaceable> to <replaceable + class="parameter">value</replaceable>. <replaceable + class="parameter">value</replaceable> defaults to 1 if omitted. + <replaceable class="parameter">symbology</replaceable> is one of + <option>ean13</option>, <option>ean8</option>, + <option>upca</option>, <option>upce</option>, + <option>isbn13</option>, <option>isbn10</option>, + <option>i25</option>, <option>codabar</option>, <option>code39</option>, + <option>code93</option>, <option>code128</option>, + <option>qrcode</option> or the special value <option>*</option>. + If <replaceable class="parameter">symbology</replaceable> is + omitted or <option>*</option>, the <replaceable + class="parameter">config</replaceable> will be set for all + applicable symbologies. These are the currently recognized + <replaceable class="parameter">config</replaceable>s. Prefix a + config with "no-" to negate it. Not all configs are appropriate + for every symbology.</simpara> + + <variablelist> + <varlistentry> + <term><option>enable</option></term> + <listitem> + <simpara>Control decoding/reporting of a symbology. For + symbologies which are just subsets of <option>ean13</option> + (<option>upca</option>, <option>upce</option>, + <option>isbn13</option>, <option>isbn10</option>), this + config controls whether the subsets are detected and + reported as such. These special cases are disabled by + default, all other symbologies default to enabled</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>disable</option></term> + <listitem> + <simpara>Antonym for <option>enable</option></simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>emit-check</option></term> + <listitem> + <simpara>Control whether check digits are included in the + decoded output. Enabled by default. This config does not + apply for <option>code128</option>, which never returns the + check digit. It also not apply for cases where the check + digit is disabled (see <option>add-check</option>). Check + digits are currently not implemented for + <option>i25</option> or <option>code39</option></simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>add-check</option></term> + <listitem> + <simpara>Enable decode and verification of a check digit for + symbologies where it is optional: this will include + <option>code39</option> and <option>i25</option>, neither of + which implements the check digit yet</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>ascii</option></term> + <listitem> + <simpara>Enable escape sequences that encode the full ASCII + character set. This would apply to <option>code39</option>, + except that it's not implemented either...</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>position</option></term> + <listitem> + <simpara>Enable collection of symbol position information. + Enabled by default. Currently, the position information is + unusable, so you can save a few cycles by disabling + this.</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>test-inverted</option></term> + <listitem> + <simpara>Specially for QR code images, sometimes the image + is inverted, e. g. lines are written in white instead of black. + This option makes ZBar to invert the image and parse again, in + case it fails using the normal order. Enabling it affects all + decoders.</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>min-length=<replaceable class="parameter">n</replaceable></option></term> + <term><option>max-length=<replaceable class="parameter">n</replaceable></option></term> + <listitem> + <simpara>Bound the number of decoded characters in a valid symbol. + If a decode result is outside the configured min/max range + (inclusive), it will not be reported. Set to 0 to disable the + corresponding check. This setting applies to variable-length + symbologies: <option>i25</option>, <option>codabar</option>, + <option>code39</option>, <option>code128</option> and + <option>pdf417</option>. <option>min-length</option> defaults to 6 + for <option>i25</option> and 1 for <option>code39</option> (per Code + 39 autodiscrimination recommendation); all others default to + 0</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>x-density=<replaceable class="parameter">n</replaceable></option></term> + <term><option>y-density=<replaceable class="parameter">n</replaceable></option></term> + <listitem> + <simpara>Adjust the density of the scanner passes. Lower values + scan more of the image at the cost of decreased performance. + Setting to 0 disables scanning along that axis. Defaults are both + 1.</simpara> + </listitem> + </varlistentry> + </variablelist> + + </listitem> +</varlistentry> diff --git a/doc/ref/zbarcam.xml b/doc/ref/zbarcam.xml new file mode 100644 index 0000000..69ad593 --- /dev/null +++ b/doc/ref/zbarcam.xml @@ -0,0 +1,207 @@ +<refentry xml:id="zbarcam" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <refmeta> + <refentrytitle>zbarcam</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>zbarcam</refname> + + <refpurpose>scan and decode bar codes from a video device + </refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>zbarcam</command> + <arg><option>-qv</option></arg> + <arg><option>--quiet</option></arg> + <arg><option>--nodisplay</option></arg> + <arg><option>--xml</option></arg> + <arg><option>--verbose<arg>=<replaceable + class="parameter">n</replaceable></arg></option></arg> + <arg><option>--prescale=<replaceable + class="parameter">W</replaceable>x<replaceable + class="parameter">H</replaceable></option></arg> + <arg><option>-S<optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></arg> + <arg><option>--set <optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></arg> + <arg><replaceable class="parameter">device</replaceable></arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>zbarcam</command> + <group choice="req"> + <arg choice="plain"><option>-h</option></arg> + <arg choice="plain"><option>--help</option></arg> + <arg choice="plain"><option>--version</option></arg> + </group> + </cmdsynopsis> + </refsynopsisdiv> + + <refsection> + <title>Description</title> + + <para><command>zbarcam</command> scans a video4linux video source + (eg, a webcam) for bar codes and prints any decoded data to the + standard output. The video stream is also displayed to the + screen. </para> + + <para><replaceable class="parameter">device</replaceable> is the + path to the video4linux (version 1 or 2) character device special + file (major number 81 and minor number 0 thru 63). It defaults to + <filename>/dev/video0</filename></para> + + <para>The underlying library currently supports EAN-13 (including + UPC and ISBN subsets), EAN-8, DataBar, DataBar Expanded, Code 128, + Code 93, Code 39, Codabar, Interleaved 2 of 5 and QR Code symbologies. + The specific type of each detected symbol is printed with the decoded + data.</para> + + </refsection> + + <refsection> + <title>Options</title> + + <para>This program follows the usual GNU command line syntax. + Single letter options may be bundled, long options start with two + dashes (`-').</para> + + <variablelist> + &refcommonoptions; + + <varlistentry> + <term><option>-q</option></term> + <term><option>--quiet</option></term> + <listitem> + <simpara>Quiet operation; disable the audible beep otherwise + emitted when a symbol is decoded</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--nodisplay</option></term> + <listitem> + <simpara>Disable output video window. Video input will be + scanned until the program is interrupted or otherwise + signaled</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--xml</option></term> + <listitem> + <simpara>Stream results using an XML output format. This + format wraps the raw data from the symbol with information + about the scan in an easy to parse format. The latest + schema is available from <link xlink:href="http://zbar.sourceforge.net/2008/barcode.xsd"/></simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--raw</option></term> + <listitem> + <simpara>Use raw symbol data output format. This format + prints symbol data separated by newlines without the + additional symbology type information that is printed by + default</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--prescale=<replaceable + class="parameter">W</replaceable>x<replaceable + class="parameter">H</replaceable></option></term> + <listitem> + <simpara>Request video input scaling from the camera driver. + Possibly useful for reducing huge frames to achieve a higher + frame rate. Note that the driver may adjust or completely + ignore the scaling request</simpara> + </listitem> + </varlistentry> + + </variablelist> + </refsection> + + <refsection> + <title>Examples</title> + + <para>Scan for barcodes using the second video device and pipe the + resulting data through a script that searches for each code in a + database and does something useful with them: + + <screen><command>zbarcam</command> <filename>/dev/video1</filename> | <command>upcrpc.py</command></screen> + + The <command>upcrpc.py</command> example script included in the + <filename>examples/</filename> subdirectory of the distribution + will make an XMLRPC call to a popular internet UPC database and + print the product description if found.</para> + + <para>Scan for barcodes using the default video device and stream + results to stdout in XML format, also disable recognition of + Interleaved 2 of 5 codes to prevent confusion with other + symbologies or background noise: + + <screen><command>zbarcam</command> <option>--xml</option> <option>-Si25.disable</option></screen> + </para> + + <para>Scan only for Code 39, without using the preview window - + maybe for a fixed installation. To enable only Code 39, first all + symbologies are disabled, then Code 39 is re-enabled: + + <screen><command>zbarcam</command> <option>--nodisplay</option> <option>-Sdisable</option> <option>-Scode39.enable</option></screen> + </para> + </refsection> + + <refsection> + <title>Exit Status</title> + + <para><command>zbarcam</command> returns an exit code to indicate the + status of the program execution. Current exit codes are:</para> + + <variablelist> + <varlistentry> + <term>0</term> + <listitem> + <para>Successful program completion.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>1</term> + <listitem> + <para>An error occurred. This includes bad arguments and I/O + errors.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>2</term> + <listitem> + <para>A fatal error occurred.</para> + </listitem> + </varlistentry> + </variablelist> + </refsection> + + <refsection> + <title>See Also</title> + <para><xref linkend="zbarimg"/></para> + <para><link xlink:href="http://zbar.sf.net/"/></para> + </refsection> + + <refsection> + <title>Bugs</title> + + <para>See <link xlink:href="http://sf.net/tracker/?group_id=189236&atid=928515"/></para> + + </refsection> + +</refentry> diff --git a/doc/ref/zbarimg.xml b/doc/ref/zbarimg.xml new file mode 100644 index 0000000..572bea9 --- /dev/null +++ b/doc/ref/zbarimg.xml @@ -0,0 +1,242 @@ +<refentry xml:id="zbarimg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <refmeta> + <refentrytitle>zbarimg</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>zbarimg</refname> + + <refpurpose>scan and decode bar codes from image file(s) + </refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>zbarimg</command> + <arg><option>-qv</option></arg> + <arg><option>--polygon</option></arg> + <arg><option>--quiet</option></arg> + <arg><option>--verbose<arg>=<replaceable + class="parameter">n</replaceable></arg></option></arg> + <sbr/> + <group choice="req" rep="repeat"> + <arg choice="plain"><option>-dD</option></arg> + <arg choice="plain"><option>--display</option></arg> + <arg choice="plain"><option>--nodisplay</option></arg> + <arg choice="plain"><option>--xml</option></arg> + <arg choice="plain"><option>--noxml</option></arg> + <arg choice="plain"><option>-S<optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></arg> + <arg choice="plain"><option>--set <optional><replaceable + class="parameter">symbology</replaceable>.</optional><replaceable + class="parameter">config</replaceable><optional>=<replaceable + class="parameter">value</replaceable></optional></option></arg> + <arg choice="plain"><replaceable>image</replaceable></arg> + </group> + </cmdsynopsis> + + <cmdsynopsis> + <command>zbarimg</command> + <group choice="req"> + <arg choice="plain"><option>-h</option></arg> + <arg choice="plain"><option>--help</option></arg> + <arg choice="plain"><option>--version</option></arg> + </group> + </cmdsynopsis> + </refsynopsisdiv> + + <refsection> + <title>Description</title> + + <para>For each specified + <filename><replaceable>image</replaceable></filename> file + <command>zbarimg</command> scans the image for bar codes and + prints any decoded data to stdout. Images may optionally be + displayed to the screen.</para> + + <para>The underlying library currently supports EAN-13 (including + UPC and ISBN subsets), EAN-8, DataBar, DataBar Expanded, Code 128, + Code 93, Code 39, Codabar, Interleaved 2 of 5 and QR Code symbologies. + The specific type of each detected symbol is printed with the decoded + data.</para> + + <para>Note that "<filename><replaceable>image</replaceable></filename>" + + in this context refers to any format supported by ImageMagick, + including many vector formats such as PDF and PostScript. Keep in + mind that vector formats are rasterized before scanning; manually + rasterize vector images before scanning to avoid unintentionally + corrupting embedded barcode bitmaps.</para> + + </refsection> + + <refsection> + <title>Options</title> + + <para>This program follows the usual GNU command line syntax. + Single letter options may be bundled, long options start with two + dashes (`-').</para> + + <variablelist> + &refcommonoptions; + + <varlistentry> + <term><option>--polygon</option></term> + <listitem> + <simpara>Ouput points of the polygon containing the code bar. + Using a format compatible with The <polygon> element + of the Scalable Vector Graphics (SVG) markup language</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-q</option></term> + <term><option>--quiet</option></term> + <listitem> + <simpara>Quiet operation; only output decoded symbol data. + specifically this disables the statistics line printed (to + stderr) before exiting, as well as the warning message + printed (also to stderr) when no barcodes are found in an + image</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-d</option></term> + <term><option>--display</option></term> + <term><option>-D</option></term> + <term><option>--nodisplay</option></term> + <listitem> + <simpara>Enable/disable display of subsequent + <filename><replaceable>image</replaceable></filename> files, + until next <option>--display</option> or + <option>--nodisplay</option> is encountered. This option + may appear multiple times to enable display of specific + images. Image display is disabled by default</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--xml</option></term> + <term><option>--noxml</option></term> + <listitem> + <simpara>Enable/disable result output using an XML format. + This format wraps the raw data from the symbol with + information about the scan (such as page indices) in an + easy to parse format. The latest schema is available from + <link xlink:href="http://zbar.sourceforge.net/2008/barcode.xsd"/>.</simpara> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--raw</option></term> + <listitem> + <simpara>Enable raw symbol data output. This format prints symbol + data separated by newlines without the additional symbology type + information that is printed by default</simpara> + </listitem> + </varlistentry> + + </variablelist> + </refsection> + + <refsection> + <title>Examples</title> + + <para>Scan a PNG image of a UPC bar code symbol and pass + resulting data to a script that searches for the code in a + database and does something useful with it: + + <screen><command>zbarimg</command> <filename>product.png</filename> | <command>upcrpc.py</command></screen> + + The <command>upcrpc.py</command> example script included in the + <filename>examples/</filename> subdirectory of the distribution + will make an XMLRPC call to a popular internet UPC database and + print the product description if found.</para> + + <para>Scan a JPEG image containing several barcodes and display + the image in a window, also disabling recognition of Interleaved 2 + of 5 codes to prevent confusion with other symbologies or + background noise: + + <screen><command>zbarimg</command> <option>--display</option> <option>-Si25.disable</option> <filename>label.jpg</filename></screen> + </para> + + <para>Look in a scanned document only for Code 39, using XML + output format so the page numbers are available. To enable only + Code 39, first all symbologies are disabled, then Code 39 is + re-enabled: + + <screen><command>zbarimg</command> <option>--xml</option> <option>-Sdisable</option> <option>-Scode39.enable</option> <filename>scan.tiff</filename></screen> + </para> + </refsection> + + <refsection> + <title>Exit Status</title> + + <para><command>zbarimg</command> returns an exit code to indicate the + status of the program execution. Current exit codes are:</para> + + <variablelist> + <varlistentry> + <term>0</term> + <listitem> + <para>Barcodes successfully detected in all images. Warnings may + have been generated, but no errors.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>1</term> + <listitem> + <para>An error occurred while processing some image(s). This + includes bad arguments, I/O errors and image handling errors from + ImageMagick.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>2</term> + <listitem> + <para>ImageMagick fatal error.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>3</term> + <listitem> + <para>The user quit the program before all images were scanned. + Only applies when running in interactive mode + (with <option>--display</option>)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>4</term> + <listitem> + <para>No barcode was detected in one or more of the images. No + other errors occurred.</para> + </listitem> + </varlistentry> + </variablelist> + </refsection> + + <refsection> + <title>See Also</title> + <para><xref linkend="zbarcam"/></para> + <para><link xlink:href="http://zbar.sf.net/"/></para> + </refsection> + + <refsection> + <title>Bugs</title> + + <para>See <link xlink:href="http://sf.net/tracker/?group_id=189236&atid=928515"/></para> + + </refsection> + +</refentry> diff --git a/doc/style.xsl b/doc/style.xsl new file mode 100644 index 0000000..40d4f2f --- /dev/null +++ b/doc/style.xsl @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:param name="make.year.ranges" select="1"/> + <xsl:param name="make.single.year.ranges" select="1"/> + <xsl:param name="use.id.as.filename" select="1"/> + +</xsl:stylesheet> diff --git a/examples/codabar.png b/examples/codabar.png Binary files differnew file mode 100644 index 0000000..3482a31 --- /dev/null +++ b/examples/codabar.png diff --git a/examples/code-128.png b/examples/code-128.png Binary files differnew file mode 100644 index 0000000..d8b8845 --- /dev/null +++ b/examples/code-128.png diff --git a/examples/code-39.png b/examples/code-39.png Binary files differnew file mode 100644 index 0000000..2b0fbc8 --- /dev/null +++ b/examples/code-39.png diff --git a/examples/code-93.png b/examples/code-93.png Binary files differnew file mode 100644 index 0000000..83a65b1 --- /dev/null +++ b/examples/code-93.png diff --git a/examples/code-upc-a.png b/examples/code-upc-a.png Binary files differnew file mode 100644 index 0000000..992a49c --- /dev/null +++ b/examples/code-upc-a.png diff --git a/examples/databar-exp.png b/examples/databar-exp.png Binary files differnew file mode 100644 index 0000000..7091be4 --- /dev/null +++ b/examples/databar-exp.png diff --git a/examples/databar.png b/examples/databar.png Binary files differnew file mode 100644 index 0000000..23dc6c6 --- /dev/null +++ b/examples/databar.png diff --git a/examples/ean-13.png b/examples/ean-13.png Binary files differnew file mode 100644 index 0000000..ac6dd0b --- /dev/null +++ b/examples/ean-13.png diff --git a/examples/ean-2.png b/examples/ean-2.png Binary files differnew file mode 100644 index 0000000..5ec2983 --- /dev/null +++ b/examples/ean-2.png diff --git a/examples/ean-5.png b/examples/ean-5.png Binary files differnew file mode 100644 index 0000000..2c3d039 --- /dev/null +++ b/examples/ean-5.png diff --git a/examples/ean-8.png b/examples/ean-8.png Binary files differnew file mode 100644 index 0000000..d1cc3a8 --- /dev/null +++ b/examples/ean-8.png diff --git a/examples/i2-5.png b/examples/i2-5.png Binary files differnew file mode 100644 index 0000000..7d46fdc --- /dev/null +++ b/examples/i2-5.png diff --git a/examples/processor.c b/examples/processor.c new file mode 100644 index 0000000..773440e --- /dev/null +++ b/examples/processor.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <zbar.h> + +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 index 0000000..a032a5b --- /dev/null +++ b/examples/processor.cpp @@ -0,0 +1,44 @@ +#include <iostream> +#include <zbar.h> + +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/qr-code-binary.png b/examples/qr-code-binary.png Binary files differnew file mode 100644 index 0000000..6337d26 --- /dev/null +++ b/examples/qr-code-binary.png diff --git a/examples/qr-code-inverted.png b/examples/qr-code-inverted.png Binary files differnew file mode 100644 index 0000000..79c46d3 --- /dev/null +++ b/examples/qr-code-inverted.png diff --git a/examples/qr-code.png b/examples/qr-code.png Binary files differnew file mode 100644 index 0000000..bd04229 --- /dev/null +++ b/examples/qr-code.png diff --git a/examples/scan_image.c b/examples/scan_image.c new file mode 100644 index 0000000..a44b58c --- /dev/null +++ b/examples/scan_image.c @@ -0,0 +1,117 @@ +#include <png.h> +#include <stdio.h> +#include <stdlib.h> +#include <zbar.h> + +#if !defined(PNG_LIBPNG_VER) || PNG_LIBPNG_VER < 10018 || \ + (PNG_LIBPNG_VER > 10200 && PNG_LIBPNG_VER < 10209) +/* Changes to Libpng from version 1.2.42 to 1.4.0 (January 4, 2010) + * ... + * 2. m. The function png_set_gray_1_2_4_to_8() was removed. It has been + * deprecated since libpng-1.0.18 and 1.2.9, when it was replaced with + * png_set_expand_gray_1_2_4_to_8() because the former function also + * expanded palette images. + */ +#define png_set_expand_gray_1_2_4_to_8 png_set_gray_1_2_4_to_8 +#endif + +zbar_image_scanner_t *scanner = NULL; + +/* to complete a runnable example, this abbreviated implementation of + * get_data() will use libpng to read an image file. refer to libpng + * documentation for details + */ +static void get_data(const char *name, int *width, int *height, void **raw) +{ + png_structp png; + png_infop info; + int color, bits; + png_bytep *rows; + int i; + FILE *file = fopen(name, "rb"); + + if (!file) + exit(2); + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + exit(3); + if (setjmp(png_jmpbuf(png))) + exit(4); + info = png_create_info_struct(png); + if (!info) + exit(5); + png_init_io(png, file); + png_read_info(png, info); + /* configure for 8bpp grayscale input */ + color = png_get_color_type(png, info); + bits = png_get_bit_depth(png, info); + if (color & PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + if (color == PNG_COLOR_TYPE_GRAY && bits < 8) + png_set_expand_gray_1_2_4_to_8(png); + if (bits == 16) + png_set_strip_16(png); + if (color & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png); + if (color & PNG_COLOR_MASK_COLOR) + png_set_rgb_to_gray_fixed(png, 1, -1, -1); + + /* allocate image */ + *width = png_get_image_width(png, info); + *height = png_get_image_height(png, info); + *raw = (png_bytep)calloc(*width * *height, sizeof(png_byte)); + rows = (png_bytep *)calloc(*height, sizeof(*rows)); + + for (i = 0; i < *height; i++) + rows[i] = ((png_bytep)(*raw)) + (*width * i); + + png_read_image(png, rows); + free(rows); +} + +int main(int argc, char **argv) +{ + int width, height, n; + void *raw; + zbar_image_t *image; + const zbar_symbol_t *symbol; + + if (argc < 2) + return (1); + + /* create a reader */ + scanner = zbar_image_scanner_create(); + + /* configure the reader */ + zbar_image_scanner_set_config(scanner, 0, ZBAR_CFG_ENABLE, 1); + + /* obtain image data */ + width = 0, height = 0; + raw = NULL; + get_data(argv[1], &width, &height, &raw); + + /* wrap image data */ + image = zbar_image_create(); + 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); + + /* scan the image for barcodes */ + n = zbar_scan_image(scanner, image); + + /* extract results */ + 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); + } + + /* clean up */ + zbar_image_destroy(image); + zbar_image_scanner_destroy(scanner); + + return (0); +} diff --git a/examples/scan_image.cpp b/examples/scan_image.cpp new file mode 100644 index 0000000..55f5c32 --- /dev/null +++ b/examples/scan_image.cpp @@ -0,0 +1,56 @@ +#include <Magick++.h> +#include <iostream> +#include <zbar.h> +#define STR(s) #s + +using namespace std; +using namespace zbar; + +int main(int argc, char **argv) +{ + if (argc < 2) + return (1); + +#ifdef MAGICK_HOME + // http://www.imagemagick.org/Magick++/ + // under Windows it is necessary to initialize the ImageMagick + // library prior to using the Magick++ library + Magick::InitializeMagick(MAGICK_HOME); +#endif + + // create a reader + ImageScanner scanner; + + // configure the reader + scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); + + // obtain image data + Magick::Image magick(argv[1]); // read an image file + + int width = magick.columns(); // extract dimensions + int height = magick.rows(); + + Magick::Blob blob; // extract the raw data + magick.modifyImage(); + magick.write(&blob, "GRAY", 8); + const void *raw = blob.data(); + + // wrap image data + Image image(width, height, "Y800", raw, width * height); + + // scan the image for barcodes + int n = scanner.scan(image); + + // extract results + for (Image::SymbolIterator symbol = image.symbol_begin(); + symbol != image.symbol_end(); ++symbol) { + // do something useful with results + cout << "decoded " << symbol->get_type_name() << " symbol \"" + << symbol->get_data() << '"' << endl; + } + + // clean up + image.set_data(NULL, 0); + + return (0); +} diff --git a/examples/scan_image.vcproj b/examples/scan_image.vcproj new file mode 100644 index 0000000..37937de --- /dev/null +++ b/examples/scan_image.vcproj @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="scan_image" + RootNamespace="scan_image" + Keyword="Win32Proj"> + + <Platforms> + <Platform Name="Win32"/> + </Platforms> + + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\include;"C:\Program Files\ImageMagick-6.5.4-Q16\include"" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"MAGICK_HOME=STR(C:\\Program Files\\ImageMagick-6.5.4-Q16)"" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="4" + WarnAsError="true" + DisableSpecificWarnings="4100;4189;4251" + DebugInformationFormat="4"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="..\lib\libzbar-0.lib kernel32.lib "C:\Program Files\ImageMagick-6.5.4-Q16\lib\CORE_RL_Magick++_.lib" $(NoInherit)" + ShowProgress="0" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1"/> + </Configuration> + </Configurations> + + <Files> + <Filter + Name="Source Files" + Filter="cpp"> + <File RelativePath=".\scan_image.cpp"/> + </Filter> + </Files> + +</VisualStudioProject> diff --git a/examples/sha1sum b/examples/sha1sum new file mode 100644 index 0000000..72cab2e --- /dev/null +++ b/examples/sha1sum @@ -0,0 +1,19 @@ +a56811d078ea5cfac9be5deb4b6796177763e152 zbarimg codabar.png +cc53bf34878f769fc3611020c11e572f2853bd2a zbarimg code-128.png +7537d593ea42393a43bc0eda0a896c0e31017dd8 zbarimg code-39.png +f8f55b828eb7d0400f300be021d29293bd4a3191 zbarimg code-93.png +aebbdbed0b32d7fd72f1245e3fb384822d492062 zbarimg databar.png +9e245874d3229a575eabfdba1c668369c55960e3 zbarimg databar-exp.png +53429fc04dfcf674349e2db6cfbaf73e301fc3dc zbarimg ean-13.png +4095418b74efbb026dd730543558fefdda46f5b9 zbarimg ean-8.png +5501245dbba21c153f690787fc97ab50c973b846 zbarimg i2-5.png +b350ca7efad7a50c5ac082d5c683a8e8d8d380a7 zbarimg qr-code.png +84c0ce7072e2227073dc8bd1e5f4518d8f42ae3d zbarimg sqcode1-generated.png +84c0ce7072e2227073dc8bd1e5f4518d8f42ae3d zbarimg sqcode1-scanned.png +5ab2b518e2c9d827cedc5825d2e3c9646d43713a zbarimg -Sean2.enable ean-2.png +668fef8cb9caac34df8cb8564c2cde62e4af5e65 zbarimg -Sean5.enable ean-5.png +b567e550216fe24f7652f683146365a9fe7ee867 zbarimg -Sisbn10.enable ean-13.png +d0f37aa076d42c270f7231c5490beea5605e2ba0 zbarimg -Sisbn13.enable ean-13.png +3f041225df3b8364b5fd0daf9cf402e8a4731f9b zbarimg -Supca.enable code-upc-a.png +b350ca7efad7a50c5ac082d5c683a8e8d8d380a7 zbarimg -Stest-inverted qr-code-inverted.png +df896e459e47a7d392031a7d4962722a143e276b zbarimg --raw --oneshot -Sbinary qr-code-binary.png diff --git a/examples/sqcode1-generated.png b/examples/sqcode1-generated.png Binary files differnew file mode 100644 index 0000000..323c45b --- /dev/null +++ b/examples/sqcode1-generated.png diff --git a/examples/sqcode1-scanned.png b/examples/sqcode1-scanned.png Binary files differnew file mode 100644 index 0000000..af831b5 --- /dev/null +++ b/examples/sqcode1-scanned.png diff --git a/examples/upcrpc.pl b/examples/upcrpc.pl new file mode 100755 index 0000000..5b0e10e --- /dev/null +++ b/examples/upcrpc.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl +use warnings; +use strict; +use Frontier::Client; +use Data::Dumper; +my $s = Frontier::Client->new('url' => 'http://www.upcdatabase.com/rpc'); + +$| = 1; # autoflush + +foreach (@ARGV) { + lookup($_); +} +if(!-t) { + while(1) { + my $decode = <STDIN>; + last unless(defined($decode)); + chomp($decode); + lookup($decode); + } +} + +sub lookup { + my $decode = shift; + if($decode =~ m[^(EAN-13:|UPC-A:)?(\d{11,13})$] && + ($1 && $1 eq "UPC-A:") || ($2 && length($2) > 11)) { + my $ean = $2; + $ean = "0" . $ean + if($1 && $1 eq "UPC-A:"); + $ean = $s->call('calculateCheckDigit', $ean . "C") + if(length($ean) == 12); + print("[$decode] "); + my $result = $s->call('lookupEAN', $s->string($ean)); + if(ref($result)) { + print((!$result->{found} || + (ref($result->{found}) && !$result->{found}->value())) + ? "not found\n" + : "$result->{description}\n") + } + else { + print("$result\n"); + } + } + else { + print("$decode\n"); + } +} diff --git a/examples/upcrpc.py b/examples/upcrpc.py new file mode 100755 index 0000000..5b0cd13 --- /dev/null +++ b/examples/upcrpc.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +from __future__ import print_function + +try: + from xmlrpc.client import ServerProxy +except: + from xmlrpclib import ServerProxy + +import sys, re + +server = ServerProxy("http://www.upcdatabase.com/rpc") +ean_re = re.compile(r'^(UPC-A:|EAN-13:)?(\d{11,13})$', re.M) + +def lookup(decode): + match = ean_re.search(decode) + if match is None: + print(decode, end=" ") + return + ean = match.group(2) + if match.group(1) == "UPC-A:": + ean = "0" + ean; + elif len(ean) < 12: + print(decode, end=' ') + return + if len(ean) == 12: + ean = server.calculateCheckDigit(ean + "C") + print("[" + match.group(1) + ean + "]", end=' ') + result = server.lookupEAN(ean) + if isinstance(result, dict): + if "found" not in result or not result["found"] or \ + "description" not in result: + print("not found") + else: + print(result["description"]) + else: + print(str(result)) + sys.stdout.flush() + +if __name__ == "__main__": + del sys.argv[0] + if len(sys.argv): + for decode in sys.argv: + lookup(decode) + if not sys.stdin.isatty(): + while 1: + decode = sys.stdin.readline() + if not decode: + break + lookup(decode) diff --git a/gtk/Makefile.am b/gtk/Makefile.am new file mode 100644 index 0000000..8744682 --- /dev/null +++ b/gtk/Makefile.am @@ -0,0 +1,88 @@ +lib_LTLIBRARIES = libzbargtk.la +libzbargtk_la_CPPFLAGS = $(GTK_CFLAGS) $(AM_CPPFLAGS) +libzbargtk_la_LDFLAGS = -version-info $(ZGTK_LIB_VERSION) \ + -export-symbols-regex "^zbar_.*" $(AM_LDFLAGS) -no-undefined +libzbargtk_la_LIBADD = $(GTK_LIBS) ../zbar/libzbar.la $(AM_LIBADD) +libzbargtk_la_DEPENDENCIES = ../zbar/libzbar.la + + +if HAVE_X +libzbargtk_la_CPPFLAGS += -DHAVE_X +endif + +dist_libzbargtk_la_SOURCES = zbargtk.c zbargtkprivate.h +nodist_libzbargtk_la_SOURCES = zbarmarshal.c zbarmarshal.h +BUILT_SOURCES = zbarmarshal.c zbarmarshal.h +CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = zbarmarshal.list + +%.h: %.list + $(GLIB_GENMARSHAL) --g-fatal-warnings --prefix=zbar_marshal \ + --header $^ > $@ + +%.c: %.list + $(GLIB_GENMARSHAL) --g-fatal-warnings --prefix=zbar_marshal \ + --body $^ > $@ + +../zbar/libzbar.la: + $(MAKE) -C $(abs_top_srcdir) zbar/libzbar.la + +../zbarcam/zbarcam-gtk: libzbargtk.la + $(MAKE) -C $(abs_top_srcdir) zbarcam/zbarcam-gtk + +# GObject Introspection + +include $(INTROSPECTION_MAKEFILE) + + + +# NOTE: +# +# At least with Fedora 30 builds using mock (e. g. inside a chroot and +# having the build dir different than the source dir, using +# gobject-introspection-1.60, there is a bug with GIR file generation: +# sometimes, g-ir-scanner is not capable of producing a C file that would +# be loading libzbargtk.so.0. So, it fails with: +# +# error while loading shared libraries: libzbargtk.so.0: +# cannot open shared object file: No such file or directory +# +# I suspect that it has something to do with libtool-2.4.6. + +# The fix is hackish, but it should be safe: it should manually include +# the paths where libtool generate those at INTROSPECTION_SCANNER_ARGS. +# +# It should be noticed that, this shouldn't affect a non-buggy environment, +# so, better to be safe than sorry. +INTROSPECTION_SCANNER_ARGS = --warn-all \ + --symbol-prefix=zbar \ + --identifier-prefix=zbar_ \ + --identifier-prefix=ZBar \ + --library-path=$(abs_builddir)/.libs \ + --library-path=$(abs_top_builddir)/zbar/.libs + +# Just in case +INTROSPECTION_SCANNER_ENV = GI_SCANNER_DISABLE_CACHE=yes + +INTROSPECTION_GIRS = ZBar-1.0.gir + +ZBar_1_0_gir_NAMESPACE = ZBar +ZBar_1_0_gir_VERSION = 1.0 +ZBar_1_0_gir_LIBS = $(lib_LTLIBRARIES) $(top_builddir)/zbar/libzbar.la +ZBar_1_0_gir_FILES = $(top_builddir)/include/zbar/zbargtk.h zbargtk.c +ZBar_1_0_gir_INCLUDES = Gtk-@GTK_VERSION_MAJOR@ Gdk-@GTK_VERSION_MAJOR@ +ZBar_1_0_gir_CFLAGS = $(libzbargtk_la_CPPFLAGS) + +# This may generate some warnings, but it is needed for "make dist" +ZBar-1.0.gir: $(lib_LTLIBRARIES) + +if HAVE_INTROSPECTION + +girdir = $(INTROSPECTION_GIRDIR) +dist_gir_DATA = $(INTROSPECTION_GIRS) +typelibdir = $(INTROSPECTION_TYPELIBDIR) +typelib_DATA = ZBar-1.0.typelib + +CLEANFILES += $(dist_gir_DATA) $(typelib_DATA) + +endif diff --git a/gtk/zbargtk.c b/gtk/zbargtk.c new file mode 100644 index 0000000..8c86016 --- /dev/null +++ b/gtk/zbargtk.c @@ -0,0 +1,908 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <gtk/gtk.h> +#ifdef HAVE_X +#include <gdk/gdkx.h> +#elif defined(_WIN32) +#include <gdk/gdkwin32.h> +#endif + +#include "zbargtkprivate.h" +#include "zbarmarshal.h" +#include <zbar/zbargtk.h> + +#ifndef G_PARAM_STATIC_STRINGS +#define G_PARAM_STATIC_STRINGS \ + (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) +#endif + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +enum +{ + DECODED, + DECODED_TEXT, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_VIDEO_DEVICE, + PROP_VIDEO_ENABLED, + PROP_VIDEO_OPENED, +}; + +static guint zbar_gtk_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(ZBarGtk, zbar_gtk, GTK_TYPE_WIDGET); + +/* FIXME what todo w/errors? OOM? */ +/* FIXME signal failure notifications to main gui idle handler */ + +void zbar_gtk_release_pixbuf(zbar_image_t *img) +{ + GdkPixbuf *pixbuf = zbar_image_get_userdata(img); + + g_assert(GDK_IS_PIXBUF(pixbuf)); + + /* remove reference */ + zbar_image_set_userdata(img, NULL); + + /* release reference to associated pixbuf and it's data */ + g_object_unref(pixbuf); +} + +gboolean zbar_gtk_image_from_pixbuf(zbar_image_t *zimg, GdkPixbuf *pixbuf) +{ + unsigned pitch, width, height; + GdkColorspace colorspace; + unsigned long datalen; + int nchannels, bps; + long type; + + /* apparently should always be packed RGB? */ + colorspace = gdk_pixbuf_get_colorspace(pixbuf); + + if (colorspace != GDK_COLORSPACE_RGB) { + g_warning("non-RGB color space not supported: %d\n", colorspace); + return (FALSE); + } + + nchannels = gdk_pixbuf_get_n_channels(pixbuf); + bps = gdk_pixbuf_get_bits_per_sample(pixbuf); + type = 0; + + /* these are all guesses... */ + if (nchannels == 3 && bps == 8) + type = zbar_fourcc('R', 'G', 'B', '3'); + else if (nchannels == 4 && bps == 8) + type = zbar_fourcc('B', 'G', 'R', '4'); /* FIXME alpha flipped?! */ + else if (nchannels == 1 && bps == 8) + type = zbar_fourcc('Y', '8', '0', '0'); + else if (nchannels == 3 && bps == 5) + type = zbar_fourcc('R', 'G', 'B', 'R'); + else if (nchannels == 3 && bps == 4) + type = zbar_fourcc('R', '4', '4', '4'); /* FIXME maybe? */ + else { + g_warning("unsupported combination: nchannels=%d bps=%d\n", nchannels, + bps); + return (FALSE); + } + zbar_image_set_format(zimg, type); + + /* FIXME we don't deal w/bpl... + * this will cause problems w/unpadded pixbufs :| + */ + pitch = gdk_pixbuf_get_rowstride(pixbuf); + width = pitch / ((nchannels * bps) / 8); + + if ((width * nchannels * 8 / bps) != pitch) { + g_warning("unsupported: width=%d nchannels=%d bps=%d rowstride=%d\n", + width, nchannels, bps, pitch); + return (FALSE); + } + height = gdk_pixbuf_get_height(pixbuf); + /* FIXME this isn't correct either */ + datalen = width * height * nchannels; + + zbar_image_set_size(zimg, width, height); + + /* when the zbar image is released, the pixbuf will be + * automatically be released + */ + zbar_image_set_userdata(zimg, pixbuf); + zbar_image_set_data(zimg, gdk_pixbuf_get_pixels(pixbuf), datalen, + zbar_gtk_release_pixbuf); +#ifdef DEBUG_ZBARGTK + g_message("colorspace=%d nchannels=%d bps=%d type=%.4s(%08lx)\n" + "\tpitch=%d width=%d height=%d datalen=0x%lx\n", + colorspace, nchannels, bps, (char *)&type, type, pitch, width, + height, datalen); +#endif + return (TRUE); +} + +static inline gboolean zbar_gtk_video_open(ZBarGtk *self, + const char *video_device) +{ + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + gboolean video_opened = FALSE; + + zbar->video_opened = FALSE; + if (zbar->idle_id) + g_object_notify(G_OBJECT(self), "video-opened"); + + if (zbar->window) { + /* ensure old video doesn't have image ref + * (FIXME handle video destroyed w/images outstanding) + */ + zbar_window_draw(zbar->window, NULL); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } + + if (zbar->video) { + zbar_video_destroy(zbar->video); + zbar->video = NULL; + } + + if (video_device && video_device[0] && zbar->idle_id) { + /* create video + * FIXME video should support re-open + */ + zbar->video = zbar_video_create(); + g_assert(zbar->video); + + if (zbar_video_open(zbar->video, video_device)) { + zbar_video_error_spew(zbar->video, 0); + zbar_video_destroy(zbar->video); + zbar->video = NULL; + /* FIXME error propagation */ + return (FALSE); + } + + /* negotiation accesses the window format list, + * so we hold the lock for this part + */ + 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) { + zbar->req_width = zbar_video_get_width(zbar->video); + zbar->req_height = zbar_video_get_height(zbar->video); + } + gtk_widget_queue_resize(GTK_WIDGET(self)); + + zbar->video_opened = video_opened; + if (zbar->idle_id) + g_object_notify(G_OBJECT(self), "video-opened"); + } + + return (video_opened); +} + +static inline int zbar_gtk_process_image(ZBarGtk *self, zbar_image_t *image) +{ + ZBarGtkPrivate *zbar; + int rc; + + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (!image) + return (-1); + + zbar_image_t *tmp = + zbar_image_convert(image, zbar_fourcc('Y', '8', '0', '0')); + if (!tmp) + return (-1); + + zbar_image_scanner_recycle_image(zbar->scanner, image); + rc = zbar_scan_image(zbar->scanner, tmp); + zbar_image_set_symbols(image, zbar_image_get_symbols(tmp)); + zbar_image_destroy(tmp); + if (rc < 0) + return (rc); + + if (rc && zbar->idle_id) { + /* update decode results */ + const zbar_symbol_t *sym; + for (sym = zbar_image_first_symbol(image); sym; + sym = zbar_symbol_next(sym)) + if (!zbar_symbol_get_count(sym)) { + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + const char *data = zbar_symbol_get_data(sym); + g_signal_emit(self, zbar_gtk_signals[DECODED], 0, type, data); + + /* FIXME skip this when unconnected? */ + gchar *text = + g_strconcat(zbar_get_symbol_name(type), ":", data, NULL); + g_signal_emit(self, zbar_gtk_signals[DECODED_TEXT], 0, text); + g_free(text); + } + } + + if (zbar->window) { + rc = zbar_window_draw(zbar->window, image); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } else + rc = -1; + + return (rc); +} + +static gboolean zbar_processing_idle_callback(gpointer data) +{ + ZBarGtk *self = data; + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + GValue *msg = g_async_queue_try_pop(zbar->queue); + GType type; + + if (!msg) { + if (zbar->video_enabled_state) { + zbar_image_t *image = zbar_video_next_image(zbar->video); + if (zbar_gtk_process_image(self, image) < 0) + zbar->video_enabled_state = FALSE; + if (image) + zbar_image_destroy(image); + + if (zbar->video_enabled_state) + return TRUE; + + if (zbar_video_enable(zbar->video, 0)) { + zbar_video_error_spew(zbar->video, 0); + zbar->video_enabled_state = FALSE; + } + + zbar_image_scanner_enable_cache(zbar->scanner, 0); + /* release video image and revert to logo */ + if (zbar->window) { + zbar_window_draw(zbar->window, NULL); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } + + /* must have been an error while streaming */ + zbar_gtk_video_open(self, NULL); + } + + return TRUE; + } + + g_assert(G_IS_VALUE(msg)); + + type = G_VALUE_TYPE(msg); + if (type == G_TYPE_INT) { + /* video state change */ + int state = g_value_get_int(msg); + if (state < 0) { + /* error identifying state */ + g_value_unset(msg); + g_free(msg); + return TRUE; + } + g_assert(state >= 0 && state <= 1); + zbar->video_enabled_state = (state != 0); + } else if (type == G_TYPE_STRING) { + /* open new video device */ + const char *video_device = g_value_get_string(msg); + + zbar->video_enabled_state = zbar_gtk_video_open(self, video_device); + } else if (type == GDK_TYPE_PIXBUF) { + /* scan provided image and broadcast results */ + zbar_image_t *image = zbar_image_create(); + GdkPixbuf *pixbuf = GDK_PIXBUF(g_value_dup_object(msg)); + + if (zbar_gtk_image_from_pixbuf(image, pixbuf)) + zbar_gtk_process_image(self, image); + else + g_object_unref(pixbuf); + zbar_image_destroy(image); + } else { + gchar *dbg = g_strdup_value_contents(msg); + + g_warning("unknown message type (%x) received: %s\n", (unsigned)type, + dbg); + g_free(dbg); + } + g_value_unset(msg); + g_free(msg); + msg = NULL; + + if (zbar->video_enabled_state) { + /* release reference to any previous pixbuf */ + if (zbar->window) + zbar_window_draw(zbar->window, NULL); + + if (zbar_video_enable(zbar->video, 1)) { + zbar_video_error_spew(zbar->video, 0); + zbar->video_enabled_state = FALSE; + return TRUE; + } + zbar_image_scanner_enable_cache(zbar->scanner, 1); + + return TRUE; + } + + return TRUE; +} + +static void zbar_gtk_realize(GtkWidget *widget) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + GdkWindowAttr attributes; + GtkAllocation allocation; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + gtk_widget_set_realized(widget, TRUE); + +#if GTK_CHECK_VERSION(3, 14, 0) + // FIXME: this is deprecated after 3.14 - no idea what replaces it +#else + gtk_widget_set_double_buffered(widget, FALSE); +#endif + + gtk_widget_get_allocation(widget, &allocation); + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.event_mask = (gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK); + + GdkWindow *window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, GDK_WA_X | GDK_WA_Y); + gtk_widget_set_window(widget, window); + gdk_window_set_user_data(window, widget); +#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 18 + gdk_window_set_background_pattern(window, NULL); +#elif GTK_MAJOR_VERSION < 3 + gdk_window_set_back_pixmap(window, NULL, TRUE); +#endif + + /* attach zbar_window to underlying X window */ +#ifdef HAVE_X + if (zbar_window_attach(zbar->window, + GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), + GDK_WINDOW_XID(window))) +#elif defined(_WIN32) + if (zbar_window_attach(zbar->window, GDK_WINDOW_HWND(window), 0)) +#endif + zbar_window_error_spew(zbar->window, 0); +} + +static inline GValue *zbar_gtk_new_value(GType type) +{ + return (g_value_init(g_malloc0(sizeof(GValue)), type)); +} + +static void zbar_gtk_unrealize(GtkWidget *widget) +{ + ZBarGtkPrivate *zbar; + GdkWindow *window; + ZBarGtk *self; + + if (gtk_widget_get_mapped(widget)) + gtk_widget_unmap(widget); + + gtk_widget_set_mapped(widget, FALSE); + self = ZBAR_GTK(widget); + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (zbar->video_enabled) { + zbar->video_enabled = FALSE; + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, 0); + g_async_queue_push(zbar->queue, msg); + } + + zbar_window_attach(zbar->window, NULL, 0); + + gtk_widget_set_realized(widget, FALSE); + + window = gtk_widget_get_window(widget); + gdk_window_set_user_data(window, NULL); + gdk_window_destroy(window); + gtk_widget_set_window(widget, NULL); +} + +#if GTK_MAJOR_VERSION >= 3 + +static void zbar_get_preferred_width(GtkWidget *widget, gint *minimum_width, + gint *natural_width) +{ + unsigned int screen_width; + ZBarGtkPrivate *zbar; + ZBarGtk *self; +#if GTK_CHECK_VERSION(3, 22, 0) + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle geo; +#endif + self = ZBAR_GTK(widget); + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ +#if GTK_CHECK_VERSION(3, 22, 0) + display = gdk_display_get_default(); + monitor = gdk_display_get_monitor(display, 0); + gdk_monitor_get_geometry(monitor, &geo); + + screen_width = geo.width; +#else + screen_width = gdk_screen_width(); +#endif + + if (zbar->req_width > screen_width) { + float scale = screen_width * .8 / zbar->req_width; + + zbar->req_width *= scale; + zbar->req_height *= scale; + } + + *minimum_width = zbar->req_width; + *natural_width = zbar->req_width; +} + +static void zbar_get_preferred_height(GtkWidget *widget, gint *minimum_height, + gint *natural_height) +{ + ZBarGtk *self = ZBAR_GTK(widget); + unsigned int screen_height; +#if GTK_CHECK_VERSION(3, 22, 0) + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle geo; +#endif + + if (!self->_private) + return; + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ +#if GTK_CHECK_VERSION(3, 22, 0) + display = gdk_display_get_default(); + monitor = gdk_display_get_monitor(display, 0); + gdk_monitor_get_geometry(monitor, &geo); + + screen_height = geo.height; +#else + screen_height = gdk_screen_height(); +#endif + + if (zbar->req_height > screen_height) { + float scale = screen_height * .8 / zbar->req_height; + + zbar->req_width *= scale; + zbar->req_height *= scale; + } + + *minimum_height = zbar->req_height; + *natural_height = zbar->req_height; +} + +static gboolean zbar_gtk_scale_draw(GtkWidget *widget, cairo_t *cr) +{ + // NOTE: should we change something here? + + ZBarGtk *self = ZBAR_GTK(widget); + + if (!self->_private) + return (FALSE); + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (gtk_widget_get_visible(widget) && gtk_widget_get_mapped(widget) && + zbar_window_redraw(zbar->window)) + return (TRUE); + return (FALSE); +} + +#else +static void zbar_gtk_size_request(GtkWidget *widget, + GtkRequisition *requisition) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ + requisition->width = zbar->req_width; + requisition->height = zbar->req_height; +} + +static gboolean zbar_gtk_expose(GtkWidget *widget, GdkEventExpose *event) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (gtk_widget_get_visible(widget) && gtk_widget_get_mapped(widget) && + zbar_window_redraw(zbar->window)) + return (TRUE); + return (FALSE); +} +#endif + +static void zbar_gtk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + (*GTK_WIDGET_CLASS(zbar_gtk_parent_class)->size_allocate)(widget, + allocation); + if (zbar->window) + zbar_window_resize(zbar->window, allocation->width, allocation->height); +} + +void zbar_gtk_scan_image(ZBarGtk *self, GdkPixbuf *img) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + g_object_ref(G_OBJECT(img)); + + /* queue for scanning by the processor idle handler */ + GValue *msg = zbar_gtk_new_value(GDK_TYPE_PIXBUF); + + /* this grabs a new reference to the image, + * eventually released by the processor idle handler + */ + g_value_set_object(msg, img); + g_async_queue_push(zbar->queue, msg); +} + +const char *zbar_gtk_get_video_device(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (NULL); + zbar = ZBAR_GTK_PRIVATE(self->_private); + if (zbar->video_device) + return (zbar->video_device); + else + return (""); +} + +void zbar_gtk_set_video_device(ZBarGtk *self, const char *video_device) +{ + ZBarGtkPrivate *zbar; + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + g_free((void *)zbar->video_device); + zbar->video_device = g_strdup(video_device); + zbar->video_enabled = video_device && video_device[0]; + + /* push another copy to processor idle handler */ + GValue *msg = zbar_gtk_new_value(G_TYPE_STRING); + if (video_device) + g_value_set_string(msg, video_device); + else + g_value_set_static_string(msg, ""); + g_async_queue_push(zbar->queue, msg); + + g_object_freeze_notify(G_OBJECT(self)); + g_object_notify(G_OBJECT(self), "video-device"); + g_object_notify(G_OBJECT(self), "video-enabled"); + g_object_thaw_notify(G_OBJECT(self)); +} + +gboolean zbar_gtk_get_video_enabled(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + return (zbar->video_enabled); +} + +void zbar_gtk_set_video_enabled(ZBarGtk *self, gboolean video_enabled) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + video_enabled = (video_enabled != FALSE); + if (zbar->video_enabled != video_enabled) { + zbar->video_enabled = video_enabled; + + /* push state change to processor idle handler */ + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, zbar->video_enabled); + g_async_queue_push(zbar->queue, msg); + + g_object_notify(G_OBJECT(self), "video-enabled"); + } +} + +gboolean zbar_gtk_get_video_opened(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + + return (zbar->video_opened); +} + +void zbar_gtk_request_video_size(ZBarGtk *self, int width, int height) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private || width < 0 || height < 0) + return; + 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, GParamSpec *pspec) +{ + ZBarGtk *self = ZBAR_GTK(object); + + switch (prop_id) { + case PROP_VIDEO_DEVICE: + zbar_gtk_set_video_device(self, g_value_get_string(value)); + break; + case PROP_VIDEO_ENABLED: + zbar_gtk_set_video_enabled(self, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void zbar_gtk_get_property(GObject *object, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + ZBarGtk *self = ZBAR_GTK(object); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + switch (prop_id) { + case PROP_VIDEO_DEVICE: + if (zbar->video_device) + g_value_set_string(value, zbar->video_device); + else + g_value_set_static_string(value, ""); + break; + case PROP_VIDEO_ENABLED: + g_value_set_boolean(value, zbar->video_enabled); + break; + case PROP_VIDEO_OPENED: + g_value_set_boolean(value, zbar->video_opened); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void zbar_gtk_init(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar = g_object_new(ZBAR_TYPE_GTK_PRIVATE, NULL); + self->_private = (void *)zbar; + + zbar->window = zbar_window_create(); + g_assert(zbar->window); + + zbar->req_width = zbar->video_width = DEFAULT_WIDTH; + zbar->req_height = zbar->video_height = DEFAULT_HEIGHT; + + /* use a queue to signalize about the need to handle decoding and video */ + zbar->queue = g_async_queue_new(); + zbar->idle_id = g_idle_add(zbar_processing_idle_callback, self); + zbar->video_enabled_state = FALSE; + + /* Prepare for idle handler */ + g_object_ref(zbar); + g_assert(zbar->queue); + g_async_queue_ref(zbar->queue); + + zbar->scanner = zbar_image_scanner_create(); + g_assert(zbar->scanner); +} + +static void zbar_gtk_dispose(GObject *object) +{ + ZBarGtk *self = ZBAR_GTK(object); + + if (!self->_private) + return; + + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + self->_private = NULL; + + g_free((void *)zbar->video_device); + zbar->video_device = NULL; + + /* signal processor idle handler to exit */ + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, -1); + g_async_queue_push(zbar->queue, msg); + zbar->idle_id = 0; + + /* there are no external references which might call other APIs */ + g_async_queue_unref(zbar->queue); + + g_object_unref(G_OBJECT(zbar)); +} + +static void zbar_gtk_private_finalize(GObject *object) +{ + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(object); + + if (zbar->idle_id) { + if (zbar->window) + zbar_window_draw(zbar->window, NULL); + g_object_unref(zbar); + g_source_remove(zbar->idle_id); + zbar->idle_id = 0; + } + + if (zbar->window) { + zbar_window_destroy(zbar->window); + zbar->window = NULL; + } + if (zbar->scanner) { + zbar_image_scanner_destroy(zbar->scanner); + zbar->scanner = NULL; + } + if (zbar->video) { + zbar_video_destroy(zbar->video); + zbar->video = NULL; + } + g_async_queue_unref(zbar->queue); + zbar->queue = NULL; +} + +static void zbar_gtk_class_init(ZBarGtkClass *klass) +{ + GParamSpec *p; + + zbar_gtk_parent_class = g_type_class_peek_parent(klass); + + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->dispose = zbar_gtk_dispose; + object_class->set_property = zbar_gtk_set_property; + object_class->get_property = zbar_gtk_get_property; + + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + widget_class->realize = zbar_gtk_realize; + widget_class->unrealize = zbar_gtk_unrealize; +#if GTK_MAJOR_VERSION >= 3 + widget_class->get_preferred_width = zbar_get_preferred_width; + widget_class->get_preferred_height = zbar_get_preferred_height; + widget_class->draw = zbar_gtk_scale_draw; +#else + widget_class->size_request = zbar_gtk_size_request; + widget_class->expose_event = zbar_gtk_expose; +#endif + widget_class->size_allocate = zbar_gtk_size_allocate; + + widget_class->unmap = NULL; + + zbar_gtk_signals[DECODED] = + g_signal_new("decoded", G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_CLEANUP, + G_STRUCT_OFFSET(ZBarGtkClass, decoded), NULL, NULL, + zbar_marshal_VOID__INT_STRING, G_TYPE_NONE, 2, G_TYPE_INT, + G_TYPE_STRING); + + zbar_gtk_signals[DECODED_TEXT] = g_signal_new( + "decoded-text", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_CLEANUP, + G_STRUCT_OFFSET(ZBarGtkClass, decoded_text), NULL, NULL, + g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + p = g_param_spec_string("video-device", "Video device", + "the platform specific name of the video device", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_DEVICE, p); + + p = g_param_spec_boolean("video-enabled", "Video enabled", + "controls streaming from the video device", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_ENABLED, p); + + p = g_param_spec_boolean("video-opened", "Video opened", + "current opened state of the video device", FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_OPENED, p); +} + +static void zbar_gtk_private_class_init(ZBarGtkPrivateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = zbar_gtk_private_finalize; +} + +static GType zbar_gtk_private_get_type(void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(ZBarGtkPrivateClass), + NULL, + NULL, + (GClassInitFunc)zbar_gtk_private_class_init, + NULL, + NULL, + sizeof(ZBarGtkPrivate), + }; + type = + g_type_register_static(G_TYPE_OBJECT, "ZBarGtkPrivate", &info, 0); + } + return (type); +} + +GtkWidget *zbar_gtk_new(void) +{ + return (GTK_WIDGET(g_object_new(ZBAR_TYPE_GTK, NULL))); +} diff --git a/gtk/zbargtkprivate.h b/gtk/zbargtkprivate.h new file mode 100644 index 0000000..f570393 --- /dev/null +++ b/gtk/zbargtkprivate.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef __ZBAR_GTK_PRIVATE_H__ +#define __ZBAR_GTK_PRIVATE_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <zbar.h> + +G_BEGIN_DECLS + +#define ZBAR_TYPE_GTK_PRIVATE (zbar_gtk_private_get_type()) +#define ZBAR_GTK_PRIVATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), ZBAR_TYPE_GTK_PRIVATE, ZBarGtkPrivate)) +#define ZBAR_GTK_PRIVATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), ZBAR_TYPE_GTK_PRIVATE, \ + ZBarGtkPrivateClass)) +#define ZBAR_IS_GTK_PRIVATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), ZBAR_TYPE_GTK_PRIVATE)) +#define ZBAR_IS_GTK_PRIVATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), ZBAR_TYPE_GTK_PRIVATE)) +#define ZBAR_GTK_PRIVATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), ZBAR_TYPE_GTK_PRIVATE, \ + ZBarGtkPrivateClass)) + +/* zbar widget processor thread shared/private data */ +typedef struct _ZBarGtkPrivate { + GObject object; + + /* these are all owned by the main gui thread */ + gint idle_id; + const char *video_device; + gboolean video_enabled, video_enabled_state; + + /* messages are queued from the gui thread to the processor thread. + * each message is a GValue containing one of: + * - G_TYPE_INT: state change + * 1 = video enable + * 0 = video disable + * -1 = terminate processor thread + * - G_TYPE_STRING: a named video device to open ("" to close) + * - GDK_TYPE_PIXBUF: an image to scan + */ + GAsyncQueue *queue; + + /* current processor state is shared: + * written by processor thread just after opening video or + * scanning an image, read by main gui thread during size_request. + * 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. + * processor thread only calls draw() and negotiate_format(). + * protected by main gui lock (and internal lock) + */ + zbar_window_t *window; + + /* video and scanner are owned by the processor thread */ + zbar_video_t *video; + zbar_image_scanner_t *scanner; + +} ZBarGtkPrivate; + +typedef struct _ZBarGtkPrivateClass { + GObjectClass parent_class; + +} ZBarGtkPrivateClass; + +static GType zbar_gtk_private_get_type(void) G_GNUC_CONST; + +G_END_DECLS + +#endif diff --git a/gtk/zbarmarshal.list b/gtk/zbarmarshal.list new file mode 100644 index 0000000..158cf7a --- /dev/null +++ b/gtk/zbarmarshal.list @@ -0,0 +1 @@ +VOID:INT,STRING diff --git a/include/Makefile.am.inc b/include/Makefile.am.inc new file mode 100644 index 0000000..a782b29 --- /dev/null +++ b/include/Makefile.am.inc @@ -0,0 +1,13 @@ +zincludedir = $(includedir)/zbar +include_HEADERS = include/zbar.h +zinclude_HEADERS = include/zbar/Scanner.h include/zbar/Decoder.h \ + include/zbar/Exception.h include/zbar/Symbol.h include/zbar/Image.h \ + include/zbar/ImageScanner.h include/zbar/Video.h include/zbar/Window.h \ + include/zbar/Processor.h + +if HAVE_GTK +zinclude_HEADERS += include/zbar/zbargtk.h +endif +if HAVE_QT +zinclude_HEADERS += include/zbar/QZBar.h include/zbar/QZBarImage.h +endif diff --git a/include/zbar.h b/include/zbar.h new file mode 100644 index 0000000..66281c1 --- /dev/null +++ b/include/zbar.h @@ -0,0 +1,1587 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_H_ +#define _ZBAR_H_ + +#include <stdint.h> + +/** @file + * ZBar Barcode Reader C API definition + */ + +/** @mainpage + * + * interface to the barcode reader is available at several levels. + * most applications will want to use the high-level interfaces: + * + * @section high-level High-Level Interfaces + * + * these interfaces wrap all library functionality into an easy-to-use + * package for a specific toolkit: + * - the "GTK+ 2.x widget" may be used with GTK GUI applications. a + * Python wrapper is included for PyGtk + * - the @ref zbar::QZBar "Qt4 widget" may be used with Qt GUI + * applications + * - the Processor interface (in @ref c-processor "C" or @ref + * zbar::Processor "C++") adds a scanning window to an application + * with no GUI. + * + * @section mid-level Intermediate Interfaces + * + * building blocks used to construct high-level interfaces: + * - the ImageScanner (in @ref c-imagescanner "C" or @ref + * zbar::ImageScanner "C++") looks for barcodes in a library defined + * image object + * - the Window abstraction (in @ref c-window "C" or @ref + * zbar::Window "C++") sinks library images, displaying them on the + * platform display + * - the Video abstraction (in @ref c-video "C" or @ref zbar::Video + * "C++") sources library images from a video device + * + * @section low-level Low-Level Interfaces + * + * direct interaction with barcode scanning and decoding: + * - the Scanner (in @ref c-scanner "C" or @ref zbar::Scanner "C++") + * looks for barcodes in a linear intensity sample stream + * - the Decoder (in @ref c-decoder "C" or @ref zbar::Decoder "C++") + * extracts barcodes from a stream of bar and space widths + */ + +#ifdef __cplusplus + +/** C++ namespace for library interfaces */ +namespace zbar +{ +extern "C" { +#endif + +/** @name Global library interfaces */ +/*@{*/ + +/** "color" of element: bar or space. */ +typedef enum zbar_color_e +{ + ZBAR_SPACE = 0, /**< light area or space between bars */ + ZBAR_BAR = 1, /**< dark area or colored bar segment */ +} zbar_color_t; + +/** decoded symbol type. */ +typedef enum zbar_symbol_type_e +{ + ZBAR_NONE = 0, /**< no symbol decoded */ + ZBAR_PARTIAL = 1, /**< intermediate status */ + ZBAR_EAN2 = 2, /**< GS1 2-digit add-on */ + ZBAR_EAN5 = 5, /**< GS1 5-digit add-on */ + ZBAR_EAN8 = 8, /**< EAN-8 */ + ZBAR_UPCE = 9, /**< UPC-E */ + ZBAR_ISBN10 = 10, /**< ISBN-10 (from EAN-13). @since 0.4 */ + ZBAR_UPCA = 12, /**< UPC-A */ + ZBAR_EAN13 = 13, /**< EAN-13 */ + ZBAR_ISBN13 = 14, /**< ISBN-13 (from EAN-13). @since 0.4 */ + ZBAR_COMPOSITE = 15, /**< EAN/UPC composite */ + 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_CODABAR = 38, /**< Codabar. @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_SQCODE = 80, /**< SQ Code. @since 0.20.1 */ + ZBAR_CODE93 = 93, /**< Code 93. @since 0.11 */ + ZBAR_CODE128 = 128, /**< Code 128 */ + + /* + * Please see _zbar_get_symbol_hash() if adding + * anything after 128 + */ + + /** mask for base symbol type. + * @deprecated in 0.11, remove this from existing code + */ + ZBAR_SYMBOL = 0x00ff, + /** 2-digit add-on flag. + * @deprecated in 0.11, a ::ZBAR_EAN2 component is used for + * 2-digit GS1 add-ons + */ + ZBAR_ADDON2 = 0x0200, + /** 5-digit add-on flag. + * @deprecated in 0.11, a ::ZBAR_EAN5 component is used for + * 5-digit GS1 add-ons + */ + ZBAR_ADDON5 = 0x0500, + /** add-on flag mask. + * @deprecated in 0.11, GS1 add-ons are represented using composite + * symbols of type ::ZBAR_COMPOSITE; add-on components use ::ZBAR_EAN2 + * or ::ZBAR_EAN5 + */ + ZBAR_ADDON = 0x0700, +} 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 */ + ZBAR_ERR_NOMEM, /**< out of memory */ + ZBAR_ERR_INTERNAL, /**< internal library error */ + ZBAR_ERR_UNSUPPORTED, /**< unsupported request */ + ZBAR_ERR_INVALID, /**< invalid request */ + ZBAR_ERR_SYSTEM, /**< system error */ + ZBAR_ERR_LOCKING, /**< locking error */ + ZBAR_ERR_BUSY, /**< all resources busy */ + ZBAR_ERR_XDISPLAY, /**< X11 display error */ + ZBAR_ERR_XPROTO, /**< X11 protocol error */ + ZBAR_ERR_CLOSED, /**< output window is closed */ + ZBAR_ERR_WINAPI, /**< windows system error */ + ZBAR_ERR_NUM /**< number of error codes */ +} zbar_error_t; + +/** decoder configuration options. + * @since 0.4 + */ +typedef enum zbar_config_e +{ + ZBAR_CFG_ENABLE = 0, /**< enable symbology/feature */ + ZBAR_CFG_ADD_CHECK, /**< enable check digit when optional */ + ZBAR_CFG_EMIT_CHECK, /**< return check digit when present */ + ZBAR_CFG_ASCII, /**< enable full ASCII character set */ + ZBAR_CFG_BINARY, /**< don't convert binary data to text */ + ZBAR_CFG_NUM, /**< number of boolean decoder configs */ + + 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_TEST_INVERTED, /**< if fails to decode, test inverted */ + + 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; + +typedef enum video_control_type_e +{ + VIDEO_CNTL_INTEGER = 1, + VIDEO_CNTL_MENU, + VIDEO_CNTL_BUTTON, + VIDEO_CNTL_INTEGER64, + VIDEO_CNTL_STRING, + VIDEO_CNTL_BOOLEAN, +} video_control_type_t; + +/** store video control menu + * @param name name of the menu item + * @param val integer value associated with the item + */ +typedef struct video_control_menu_s { + char *name; + int64_t value; +} video_control_menu_t; + +/** store video controls + * @param name name of the control + * @param group name of the control group/class + * @param type type of the control + * @param min minimum value of control (if control is integer) + * @param max maximum value of control (if control is integer) + * @param def default value of control (if control is integer) + * @param step increment steps (if control is integer) + * @param menu menu array + * @param menu_size menu size + * @since 0.20 + */ +typedef struct video_controls_s { + char *name; + char *group; + video_control_type_t type; + + int64_t min, max, def; + uint64_t step; + + unsigned int menu_size; + video_control_menu_t *menu; + + void *next; + + // video drivers may add extra private data in the end of this struct +} video_controls_t; + +/** store a video resolution + * @param width width of the video window + * @param height length of the video window + * @param max_fps maximum streaming speed, in frames per second + * @since 0.22 + */ +struct video_resolution_s { + unsigned int width, height; + float max_fps; +}; + +/** 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) + * @returns 0 + */ +extern int zbar_version(unsigned *major, unsigned *minor, unsigned *patch); + +/** set global library debug level. + * @param verbosity desired debug level. higher values create more spew + */ +extern void zbar_set_verbosity(int verbosity); + +/** increase global library debug level. + * eg, for -vvvv + */ +extern void zbar_increase_verbosity(void); + +/** retrieve string name for symbol encoding. + * @param sym symbol type encoding + * @returns the static string name for the specified symbol type, + * or "UNKNOWN" if the encoding is not recognized + */ +extern const char *zbar_get_symbol_name(zbar_symbol_type_t sym); + +/** retrieve string name for addon encoding. + * @param sym symbol type encoding + * @returns static string name for any addon, or the empty string + * if no addons were decoded + * @deprecated in 0.11 + */ +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. + * if symbology is unspecified, it will be set to 0. + * if value is unspecified it will be set to 1. + * @returns 0 if the config is parsed successfully, 1 otherwise + * @since 0.4 + */ +extern int zbar_parse_config(const char *config_string, + zbar_symbol_type_t *symbology, + 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); +extern const char *_zbar_error_string(const void *object, int verbosity); +extern zbar_error_t _zbar_get_error_code(const void *object); + +/*@}*/ + +struct zbar_symbol_s; +typedef struct zbar_symbol_s zbar_symbol_t; + +struct zbar_symbol_set_s; +typedef struct zbar_symbol_set_s zbar_symbol_set_t; + +/*------------------------------------------------------------*/ +/** @name Symbol interface + * decoded barcode symbol result object. stores type, data, and image + * location of decoded symbol. all memory is owned by the library + */ +/*@{*/ + +/** @typedef zbar_symbol_t + * opaque decoded symbol object. + */ + +/** symbol reference count manipulation. + * increment the reference count when you store a new reference to the + * symbol. decrement when the reference is no longer used. do not + * refer to the symbol once the count is decremented and the + * containing image has been recycled or destroyed. + * @note the containing image holds a reference to the symbol, so you + * only need to use this if you keep a symbol after the image has been + * destroyed or reused. + * @since 0.9 + */ +extern void zbar_symbol_ref(const zbar_symbol_t *symbol, int refs); + +/** retrieve type of decoded symbol. + * @returns the symbol type + */ +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 + */ +extern const char *zbar_symbol_get_data(const zbar_symbol_t *symbol); + +/** retrieve length of binary data. + * @returns the length of the decoded data + */ +extern unsigned int zbar_symbol_get_data_length(const zbar_symbol_t *symbol); + +/** retrieve a symbol confidence metric. + * @returns an unscaled, relative quantity: larger values are better + * than smaller values, where "large" and "small" are application + * dependent. + * @note expect the exact definition of this quantity to change as the + * metric is refined. currently, only the ordered relationship + * between two values is defined and will remain stable in the future + * @since 0.9 + */ +extern int zbar_symbol_get_quality(const zbar_symbol_t *symbol); + +/** 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 + */ +extern int zbar_symbol_get_count(const zbar_symbol_t *symbol); + +/** retrieve the number of points in the location polygon. the + * location polygon defines the image area that the symbol was + * extracted from. + * @returns the number of points in the location polygon + * @note this is currently not a polygon, but the scan locations + * where the symbol was decoded + */ +extern unsigned zbar_symbol_get_loc_size(const zbar_symbol_t *symbol); + +/** retrieve location polygon x-coordinates. + * points are specified by 0-based index. + * @returns the x-coordinate for a point in the location polygon. + * @returns -1 if index is out of range + */ +extern int zbar_symbol_get_loc_x(const zbar_symbol_t *symbol, unsigned index); + +/** retrieve location polygon y-coordinates. + * points are specified by 0-based index. + * @returns the y-coordinate for a point in the location polygon. + * @returns -1 if index is out of range + */ +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 + */ +extern const zbar_symbol_t *zbar_symbol_next(const zbar_symbol_t *symbol); + +/** retrieve components of a composite result. + * @returns the symbol set containing the components + * @returns NULL if the symbol is already a physical symbol + * @since 0.10 + */ +extern const zbar_symbol_set_t * +zbar_symbol_get_components(const zbar_symbol_t *symbol); + +/** iterate components of a composite result. + * @returns the first physical component symbol of a composite result + * @returns NULL if the symbol is already a physical symbol + * @since 0.10 + */ +extern const zbar_symbol_t * +zbar_symbol_first_component(const zbar_symbol_t *symbol); + +/** print XML symbol element representation to user result buffer. + * @see http://zbar.sourceforge.net/2008/barcode.xsd for the schema. + * @param symbol is the symbol to print + * @param buffer is the inout result pointer, it will be reallocated + * with a larger size if necessary. + * @param buflen is inout length of the result buffer. + * @returns the buffer pointer + * @since 0.6 + */ +extern char *zbar_symbol_xml(const zbar_symbol_t *symbol, char **buffer, + unsigned *buflen); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Symbol Set interface + * container for decoded result symbols associated with an image + * or a composite symbol. + * @since 0.10 + */ +/*@{*/ + +/** @typedef zbar_symbol_set_t + * opaque symbol iterator object. + * @since 0.10 + */ + +/** reference count manipulation. + * increment the reference count when you store a new reference. + * decrement when the reference is no longer used. do not refer to + * the object any longer once references have been released. + * @since 0.10 + */ +extern void zbar_symbol_set_ref(const zbar_symbol_set_t *symbols, int refs); + +/** retrieve set size. + * @returns the number of symbols in the set. + * @since 0.10 + */ +extern int zbar_symbol_set_get_size(const zbar_symbol_set_t *symbols); + +/** set iterator. + * @returns the first decoded symbol result in a set + * @returns NULL if the set is empty + * @since 0.10 + */ +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); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Image interface + * stores image data samples along with associated format and size + * metadata + */ +/*@{*/ + +struct zbar_image_s; +/** + * zbar_image_t: opaque image object. + */ +typedef struct zbar_image_s zbar_image_t; + +/** cleanup handler callback function. + * called to free sample data when an image is destroyed. + */ +typedef void(zbar_image_cleanup_handler_t)(zbar_image_t *image); + +/** data handler callback function. + * called when decoded symbol results are available for an image + */ +typedef void(zbar_image_data_handler_t)(zbar_image_t *image, + const void *userdata); + +/** new image constructor. + * @returns a new image object with uninitialized data and format. + * this image should be destroyed (using zbar_image_destroy()) as + * soon as the application is finished with it + */ +extern zbar_image_t *zbar_image_create(void); + +/** image destructor. all images created by or returned to the + * application should be destroyed using this function. when an image + * is destroyed, the associated data cleanup handler will be invoked + * if available + * @note make no assumptions about the image or the data buffer. + * they may not be destroyed/cleaned immediately if the library + * is still using them. if necessary, use the cleanup handler hook + * to keep track of image data buffers + */ +extern void zbar_image_destroy(zbar_image_t *image); + +/** image reference count manipulation. + * increment the reference count when you store a new reference to the + * image. decrement when the reference is no longer used. do not + * refer to the image any longer once the count is decremented. + * zbar_image_ref(image, -1) is the same as zbar_image_destroy(image) + * @since 0.5 + */ +extern void zbar_image_ref(zbar_image_t *image, int refs); + +/** image format conversion. refer to the documentation for supported + * image formats + * @returns a @em new image with the sample data from the original image + * converted to the requested format. the original image is + * unaffected. + * @note the converted image size may be rounded (up) due to format + * constraints + */ +extern zbar_image_t *zbar_image_convert(const zbar_image_t *image, + unsigned long format); + +/** image format conversion with crop/pad. + * if the requested size is larger than the image, the last row/column + * are duplicated to cover the difference. if the requested size is + * smaller than the image, the extra rows/columns are dropped from the + * right/bottom. + * @returns a @em new image with the sample data from the original + * image converted to the requested format and size. + * @note the image is @em not scaled + * @see zbar_image_convert() + * @since 0.4 + */ +extern zbar_image_t *zbar_image_convert_resize(const zbar_image_t *image, + unsigned long format, + unsigned width, unsigned height); + +/** retrieve the image format. + * @returns the fourcc describing the format of the image sample data + */ +extern unsigned long zbar_image_get_format(const zbar_image_t *image); + +/** retrieve a "sequence" (page/frame) number associated with this image. + * @since 0.6 + */ +extern unsigned zbar_image_get_sequence(const zbar_image_t *image); + +/** retrieve the width of the image. + * @returns the width in sample columns + */ +extern unsigned zbar_image_get_width(const zbar_image_t *image); + +/** retrieve the height of the image. + * @returns the height in sample rows + */ +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 + */ +extern const void *zbar_image_get_data(const zbar_image_t *image); + +/** return the size of image data. + * @since 0.6 + */ +extern unsigned long zbar_image_get_data_length(const zbar_image_t *img); + +/** retrieve the decoded results. + * @returns the (possibly empty) set of decoded symbols + * @returns NULL if the image has not been scanned + * @since 0.10 + */ +extern const zbar_symbol_set_t * +zbar_image_get_symbols(const zbar_image_t *image); + +/** associate the specified symbol set with the image, replacing any + * existing results. use NULL to release the current results from the + * image. + * @see zbar_image_scanner_recycle_image() + * @since 0.10 + */ +extern void zbar_image_set_symbols(zbar_image_t *image, + const zbar_symbol_set_t *symbols); + +/** image_scanner decode result iterator. + * @returns the first decoded symbol result for an image + * or NULL if no results are available + */ +extern const zbar_symbol_t *zbar_image_first_symbol(const zbar_image_t *image); + +/** specify the fourcc image format code for image sample data. + * refer to the documentation for supported formats. + * @note this does not convert the data! + * (see zbar_image_convert() for that) + */ +extern void zbar_image_set_format(zbar_image_t *image, unsigned long format); + +/** associate a "sequence" (page/frame) number with this image. + * @since 0.6 + */ +extern void zbar_image_set_sequence(zbar_image_t *image, 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) + * @note application image data will not be modified by the library + */ +extern void zbar_image_set_data(zbar_image_t *image, const void *data, + unsigned long data_byte_length, + zbar_image_cleanup_handler_t *cleanup_hndlr); + +/** built-in cleanup handler. + * passes the image data buffer to free() + */ +extern void zbar_image_free_data(zbar_image_t *image); + +/** associate user specified data value with an image. + * @since 0.5 + */ +extern void zbar_image_set_userdata(zbar_image_t *image, void *userdata); + +/** return user specified data value associated with the image. + * @since 0.5 + */ +extern void *zbar_image_get_userdata(const zbar_image_t *image); + +/** dump raw image data to a file for debug. + * the data will be prefixed with a 16 byte header consisting of: + * - 4 bytes uint = 0x676d697a ("zimg") + * - 4 bytes format fourcc + * - 2 bytes width + * - 2 bytes height + * - 4 bytes size of following image data in bytes + * this header can be dumped w/eg: + * @verbatim + od -Ax -tx1z -N16 -w4 [file] +@endverbatim + * for some formats the image can be displayed/converted using + * ImageMagick, eg: + * @verbatim + display -size 640x480+16 [-depth ?] [-sampling-factor ?x?] \ + {GRAY,RGB,UYVY,YUV}:[file] +@endverbatim + * + * @param image the image object to dump + * @param filebase base filename, appended with ".XXXX.zimg" where + * XXXX is the format fourcc + * @returns 0 on success or a system error code on failure + */ +extern int zbar_image_write(const zbar_image_t *image, const char *filebase); + +/** read back an image in the format written by zbar_image_write() + * @note TBD + */ +extern zbar_image_t *zbar_image_read(char *filename); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Processor interface + * @anchor c-processor + * high-level self-contained image processor. + * processes video and images for barcodes, optionally displaying + * images to a library owned output window + */ +/*@{*/ + +struct zbar_processor_s; +/** opaque standalone processor object. */ +typedef struct zbar_processor_s zbar_processor_t; + +/** constructor. + * if threaded is set and threading is available the processor + * will spawn threads where appropriate to avoid blocking and + * improve responsiveness + */ +extern zbar_processor_t *zbar_processor_create(int threaded); + +/** destructor. cleans up all resources associated with the processor + */ +extern void zbar_processor_destroy(zbar_processor_t *processor); + +/** (re)initialization. + * opens a video input device and/or prepares to display output + */ +extern int zbar_processor_init(zbar_processor_t *processor, + const char *video_device, int enable_display); + +/** request a preferred size for the video image from the device. + * the request may be adjusted or completely ignored by the driver. + * @note must be called before zbar_processor_init() + * @since 0.6 + */ +extern int zbar_processor_request_size(zbar_processor_t *processor, + unsigned width, unsigned height); + +/** request a preferred video driver interface version for + * debug/testing. + * @note must be called before zbar_processor_init() + * @since 0.6 + */ +extern int zbar_processor_request_interface(zbar_processor_t *processor, + int version); + +/** request a preferred video I/O mode for debug/testing. You will + * get errors if the driver does not support the specified mode. + * @verbatim + 0 = auto-detect + 1 = force I/O using read() + 2 = force memory mapped I/O using mmap() + 3 = force USERPTR I/O (v4l2 only) +@endverbatim + * @note must be called before zbar_processor_init() + * @since 0.7 + */ +extern int zbar_processor_request_iomode(zbar_processor_t *video, int iomode); + +/** force specific input and output formats for debug/testing. + * @note must be called before zbar_processor_init() + */ +extern int zbar_processor_force_format(zbar_processor_t *processor, + unsigned long input_format, + unsigned long output_format); + +/** setup result handler callback. + * the specified function will be called by the processor whenever + * new results are available from the video stream or a static image. + * pass a NULL value to disable callbacks. + * @param processor the object on which to set the handler. + * @param handler the function to call when new results are available. + * @param userdata is set as with zbar_processor_set_userdata(). + * @returns the previously registered handler + */ +extern zbar_image_data_handler_t * +zbar_processor_set_data_handler(zbar_processor_t *processor, + zbar_image_data_handler_t *handler, + const void *userdata); + +/** associate user specified data value with the processor. + * @since 0.6 + */ +extern void zbar_processor_set_userdata(zbar_processor_t *processor, + void *userdata); + +/** return user specified data value associated with the processor. + * @since 0.6 + */ +extern void *zbar_processor_get_userdata(const zbar_processor_t *processor); + +/** set config for indicated symbology (0 for all) to specified value. + * @returns 0 for success, non-0 for failure (config does not apply to + * specified symbology, or value out of range) + * @see zbar_decoder_set_config() + * @since 0.4 + */ +extern int zbar_processor_set_config(zbar_processor_t *processor, + zbar_symbol_type_t symbology, + zbar_config_t config, int value); + +/** set video control value + * @returns 0 for success, non-0 for failure + * @since 0.20 + * @see zbar_video_set_control() + */ +extern int zbar_processor_set_control(zbar_processor_t *processor, + const char *control_name, int value); + +/** get video control value + * @returns 0 for success, non-0 for failure + * @since 0.20 + * @see zbar_video_get_control() + */ +extern int zbar_processor_get_control(zbar_processor_t *processor, + const char *control_name, int *value); + +/** parse configuration string using zbar_parse_config() + * and apply to processor using zbar_processor_set_config(). + * @returns 0 for success, non-0 for failure + * @see zbar_parse_config() + * @see zbar_processor_set_config() + * @since 0.4 + */ +static inline int zbar_processor_parse_config(zbar_processor_t *processor, + const char *config_string) +{ + zbar_symbol_type_t sym; + zbar_config_t cfg; + int val; + return (zbar_parse_config(config_string, &sym, &cfg, &val) || + zbar_processor_set_config(processor, sym, cfg, val)); +} + +/** retrieve the current state of the output window. + * @returns 1 if the output window is currently displayed, 0 if not. + * @returns -1 if an error occurs + */ +extern int zbar_processor_is_visible(zbar_processor_t *processor); + +/** show or hide the display window owned by the library. + * the size will be adjusted to the input size + */ +extern int zbar_processor_set_visible(zbar_processor_t *processor, int visible); + +/** control the processor in free running video mode. + * only works if video input is initialized. if threading is in use, + * scanning will occur in the background, otherwise this is only + * useful wrapping calls to zbar_processor_user_wait(). if the + * library output window is visible, video display will be enabled. + */ +extern int zbar_processor_set_active(zbar_processor_t *processor, int active); + +/** retrieve decode results for last scanned image/frame. + * @returns the symbol set result container or NULL if no results are + * available + * @note the returned symbol set has its reference count incremented; + * ensure that the count is decremented after use + * @since 0.10 + */ +extern const zbar_symbol_set_t * +zbar_processor_get_results(const zbar_processor_t *processor); + +/** wait for input to the display window from the user + * (via mouse or keyboard). + * @returns >0 when input is received, 0 if timeout ms expired + * with no input or -1 in case of an error + */ +extern int zbar_processor_user_wait(zbar_processor_t *processor, int timeout); + +/** process from the video stream until a result is available, + * or the timeout (in milliseconds) expires. + * specify a timeout of -1 to scan indefinitely + * (zbar_processor_set_active() may still be used to abort the scan + * from another thread). + * if the library window is visible, video display will be enabled. + * @note that multiple results may still be returned (despite the + * name). + * @returns >0 if symbols were successfully decoded, + * 0 if no symbols were found (ie, the timeout expired) + * or -1 if an error occurs + */ +extern int zbar_process_one(zbar_processor_t *processor, int timeout); + +/** process the provided image for barcodes. + * if the library window is visible, the image will be displayed. + * @returns >0 if symbols were successfully decoded, + * 0 if no symbols were found or -1 if an error occurs + */ +extern int zbar_process_image(zbar_processor_t *processor, zbar_image_t *image); + +/** enable dbus IPC API. + * @returns 0 successful + */ +int zbar_processor_request_dbus(zbar_processor_t *proc, int req_dbus_enabled); + +/** display detail for last processor error to stderr. + * @returns a non-zero value suitable for passing to exit() + */ +static inline int zbar_processor_error_spew(const zbar_processor_t *processor, + int verbosity) +{ + return (_zbar_error_spew(processor, verbosity)); +} + +/** retrieve the detail string for the last processor error. */ +static inline const char * +zbar_processor_error_string(const zbar_processor_t *processor, int verbosity) +{ + return (_zbar_error_string(processor, verbosity)); +} + +/** retrieve the type code for the last processor error. */ +static inline zbar_error_t +zbar_processor_get_error_code(const zbar_processor_t *processor) +{ + return (_zbar_get_error_code(processor)); +} + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Video interface + * @anchor c-video + * mid-level video source abstraction. + * captures images from a video device + */ +/*@{*/ + +struct zbar_video_s; +/** opaque video object. */ +typedef struct zbar_video_s zbar_video_t; + +/** constructor. */ +extern zbar_video_t *zbar_video_create(void); + +/** destructor. */ +extern void zbar_video_destroy(zbar_video_t *video); + +/** open and probe a video device. + * the device specified by platform specific unique name + * (v4l device node path in *nix eg "/dev/video", + * DirectShow DevicePath property in windows). + * @returns 0 if successful or -1 if an error occurs + */ +extern int zbar_video_open(zbar_video_t *video, const char *device); + +/** retrieve file descriptor associated with open *nix video device + * useful for using select()/poll() to tell when new images are + * available (NB v4l2 only!!). + * @returns the file descriptor or -1 if the video device is not open + * or the driver only supports v4l1 + */ +extern int zbar_video_get_fd(const zbar_video_t *video); + +/** request a preferred size for the video image from the device. + * the request may be adjusted or completely ignored by the driver. + * @returns 0 if successful or -1 if the video device is already + * initialized + * @since 0.6 + */ +extern int zbar_video_request_size(zbar_video_t *video, unsigned width, + unsigned height); + +/** request a preferred driver interface version for debug/testing. + * @note must be called before zbar_video_open() + * @since 0.6 + */ +extern int zbar_video_request_interface(zbar_video_t *video, int version); + +/** request a preferred I/O mode for debug/testing. You will get + * errors if the driver does not support the specified mode. + * @verbatim + 0 = auto-detect + 1 = force I/O using read() + 2 = force memory mapped I/O using mmap() + 3 = force USERPTR I/O (v4l2 only) +@endverbatim + * @note must be called before zbar_video_open() + * @since 0.7 + */ +extern int zbar_video_request_iomode(zbar_video_t *video, int iomode); + +/** retrieve current output image width. + * @returns the width or 0 if the video device is not open + */ +extern int zbar_video_get_width(const zbar_video_t *video); + +/** retrieve current output image height. + * @returns the height or 0 if the video device is not open + */ +extern int zbar_video_get_height(const zbar_video_t *video); + +/** initialize video using a specific format for debug. + * use zbar_negotiate_format() to automatically select and initialize + * the best available format + */ +extern int zbar_video_init(zbar_video_t *video, unsigned long format); + +/** start/stop video capture. + * all buffered images are retired when capture is disabled. + * @returns 0 if successful or -1 if an error occurs + */ +extern int zbar_video_enable(zbar_video_t *video, int enable); + +/** retrieve next captured image. blocks until an image is available. + * @returns NULL if video is not enabled or an error occurs + */ +extern zbar_image_t *zbar_video_next_image(zbar_video_t *video); + +/** set video control value (integer). + * @returns 0 for success, non-0 for failure + * @since 0.20 + * @see zbar_processor_set_control() + */ +extern int zbar_video_set_control(zbar_video_t *video, const char *control_name, + int value); + +/** get video control value (integer). + * @returns 0 for success, non-0 for failure + * @since 0.20 + * @see zbar_processor_get_control() + */ +extern int zbar_video_get_control(zbar_video_t *video, const char *control_name, + int *value); + +/** get available controls from video source + * @returns 0 for success, non-0 for failure + * @since 0.20 + */ +extern struct video_controls_s * +zbar_video_get_controls(const zbar_video_t *video, int index); + +/** get available video resolutions from video source + * @returns 0 for success, non-0 for failure + * @since 0.22 + */ +extern struct video_resolution_s * +zbar_video_get_resolutions(const zbar_video_t *vdo, int index); + +/** display detail for last video error to stderr. + * @returns a non-zero value suitable for passing to exit() + */ +static inline int zbar_video_error_spew(const zbar_video_t *video, + int verbosity) +{ + return (_zbar_error_spew(video, verbosity)); +} + +/** retrieve the detail string for the last video error. */ +static inline const char *zbar_video_error_string(const zbar_video_t *video, + int verbosity) +{ + return (_zbar_error_string(video, verbosity)); +} + +/** retrieve the type code for the last video error. */ +static inline zbar_error_t zbar_video_get_error_code(const zbar_video_t *video) +{ + return (_zbar_get_error_code(video)); +} + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Window interface + * @anchor c-window + * mid-level output window abstraction. + * displays images to user-specified platform specific output window + */ +/*@{*/ + +struct zbar_window_s; +/** opaque window object. */ +typedef struct zbar_window_s zbar_window_t; + +/** constructor. */ +extern zbar_window_t *zbar_window_create(void); + +/** destructor. */ +extern void zbar_window_destroy(zbar_window_t *window); + +/** associate reader with an existing platform window. + * This can be any "Drawable" for X Windows or a "HWND" for windows. + * input images will be scaled into the output window. + * pass NULL to detach from the resource, further input will be + * ignored + */ +extern int zbar_window_attach(zbar_window_t *window, void *x11_display_w32_hwnd, + unsigned long x11_drawable); + +/** control content level of the reader overlay. + * the overlay displays graphical data for informational or debug + * purposes. higher values increase the level of annotation (possibly + * decreasing performance). @verbatim + 0 = disable overlay + 1 = outline decoded symbols (default) + 2 = also track and display input frame rate +@endverbatim + */ +extern void zbar_window_set_overlay(zbar_window_t *window, int level); + +/** retrieve current content level of reader overlay. + * @see zbar_window_set_overlay() + * @since 0.10 + */ +extern int zbar_window_get_overlay(const zbar_window_t *window); + +/** draw a new image into the output window. */ +extern int zbar_window_draw(zbar_window_t *window, zbar_image_t *image); + +/** redraw the last image (exposure handler). */ +extern int zbar_window_redraw(zbar_window_t *window); + +/** resize the image window (reconfigure handler). + * this does @em not update the contents of the window + * @since 0.3, changed in 0.4 to not redraw window + */ +extern int zbar_window_resize(zbar_window_t *window, unsigned width, + unsigned height); + +/** display detail for last window error to stderr. + * @returns a non-zero value suitable for passing to exit() + */ +static inline int zbar_window_error_spew(const zbar_window_t *window, + int verbosity) +{ + return (_zbar_error_spew(window, verbosity)); +} + +/** retrieve the detail string for the last window error. */ +static inline const char *zbar_window_error_string(const zbar_window_t *window, + int verbosity) +{ + return (_zbar_error_string(window, verbosity)); +} + +/** retrieve the type code for the last window error. */ +static inline zbar_error_t +zbar_window_get_error_code(const zbar_window_t *window) +{ + return (_zbar_get_error_code(window)); +} + +/** select a compatible format between video input and output window. + * the selection algorithm attempts to use a format shared by + * video input and window output which is also most useful for + * barcode scanning. if a format conversion is necessary, it will + * heuristically attempt to minimize the cost of the conversion + */ +extern int zbar_negotiate_format(zbar_video_t *video, zbar_window_t *window); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Image Scanner interface + * @anchor c-imagescanner + * mid-level image scanner interface. + * reads barcodes from 2-D images + */ +/*@{*/ + +struct zbar_image_scanner_s; +/** opaque image scanner object. */ +typedef struct zbar_image_scanner_s zbar_image_scanner_t; + +/** constructor. */ +extern zbar_image_scanner_t *zbar_image_scanner_create(void); + +/** destructor. */ +extern void zbar_image_scanner_destroy(zbar_image_scanner_t *scanner); + +/** setup result handler callback. + * the specified function will be called by the scanner whenever + * new results are available from a decoded image. + * pass a NULL value to disable callbacks. + * @returns the previously registered handler + */ +extern zbar_image_data_handler_t * +zbar_image_scanner_set_data_handler(zbar_image_scanner_t *scanner, + zbar_image_data_handler_t *handler, + const void *userdata); + +/** request sending decoded codes via D-Bus + * @see zbar_processor_parse_config() + * @since 0.21 + */ +extern int zbar_image_scanner_request_dbus(zbar_image_scanner_t *scanner, + int req_dbus_enabled); + +/** set config for indicated symbology (0 for all) to specified value. + * @returns 0 for success, non-0 for failure (config does not apply to + * specified symbology, or value out of range) + * @see zbar_decoder_set_config() + * @since 0.4 + */ +extern int zbar_image_scanner_set_config(zbar_image_scanner_t *scanner, + zbar_symbol_type_t symbology, + zbar_config_t config, int value); + +/** get config for indicated symbology + * @returns 0 for success, non-0 for failure (config does not apply to + * specified symbology, or value out of range). On success, *value is filled. + * @since 0.22 + */ +extern int zbar_image_scanner_get_config(zbar_image_scanner_t *scanner, + zbar_symbol_type_t symbology, + zbar_config_t config, int *value); + +/** parse configuration string using zbar_parse_config() + * and apply to image scanner using zbar_image_scanner_set_config(). + * @returns 0 for success, non-0 for failure + * @see zbar_parse_config() + * @see zbar_image_scanner_set_config() + * @since 0.4 + */ +static inline int zbar_image_scanner_parse_config(zbar_image_scanner_t *scanner, + const char *config_string) +{ + zbar_symbol_type_t sym; + zbar_config_t cfg; + int val; + return (zbar_parse_config(config_string, &sym, &cfg, &val) || + zbar_image_scanner_set_config(scanner, sym, cfg, val)); +} + +/** 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. + * this interface also clears the cache + */ +extern void zbar_image_scanner_enable_cache(zbar_image_scanner_t *scanner, + int enable); + +/** remove any previously decoded results from the image scanner and the + * specified image. somewhat more efficient version of + * zbar_image_set_symbols(image, NULL) which may retain memory for + * subsequent decodes + * @since 0.10 + */ +extern void zbar_image_scanner_recycle_image(zbar_image_scanner_t *scanner, + zbar_image_t *image); + +/** retrieve decode results for last scanned image. + * @returns the symbol set result container or NULL if no results are + * available + * @note the symbol set does not have its reference count adjusted; + * ensure that the count is incremented if the results may be kept + * after the next image is scanned + * @since 0.10 + */ +extern const zbar_symbol_set_t * +zbar_image_scanner_get_results(const zbar_image_scanner_t *scanner); + +/** scan for symbols in provided image. The image format must be + * "Y800" or "GRAY". + * @returns >0 if symbols were successfully decoded from the image, + * 0 if no symbols were found or -1 if an error occurs + * @see zbar_image_convert() + * @since 0.9 - changed to only accept grayscale images + */ +extern int zbar_scan_image(zbar_image_scanner_t *scanner, zbar_image_t *image); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Decoder interface + * @anchor c-decoder + * low-level bar width stream decoder interface. + * identifies symbols and extracts encoded data + */ +/*@{*/ + +struct zbar_decoder_s; +/** opaque decoder object. */ +typedef struct zbar_decoder_s zbar_decoder_t; + +/** decoder data handler callback function. + * called by decoder when new data has just been decoded + */ +typedef void(zbar_decoder_handler_t)(zbar_decoder_t *decoder); + +/** constructor. */ +extern zbar_decoder_t *zbar_decoder_create(void); + +/** destructor. */ +extern void zbar_decoder_destroy(zbar_decoder_t *decoder); + +/** set config for indicated symbology (0 for all) to specified value. + * @returns 0 for success, non-0 for failure (config does not apply to + * specified symbology, or value out of range) + * @since 0.4 + */ +extern int zbar_decoder_set_config(zbar_decoder_t *decoder, + zbar_symbol_type_t symbology, + zbar_config_t config, int value); + +/** get config for indicated symbology + * @returns 0 for success, non-0 for failure (config does not apply to + * specified symbology, or value out of range). On success, *value is filled. + * @since 0.22 + */ +extern int zbar_decoder_get_config(zbar_decoder_t *decoder, + zbar_symbol_type_t symbology, + zbar_config_t config, int *value); + +/** parse configuration string using zbar_parse_config() + * and apply to decoder using zbar_decoder_set_config(). + * @returns 0 for success, non-0 for failure + * @see zbar_parse_config() + * @see zbar_decoder_set_config() + * @since 0.4 + */ +static inline int zbar_decoder_parse_config(zbar_decoder_t *decoder, + const char *config_string) +{ + zbar_symbol_type_t sym; + zbar_config_t cfg; + int val; + return (zbar_parse_config(config_string, &sym, &cfg, &val) || + 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 + */ +extern void zbar_decoder_reset(zbar_decoder_t *decoder); + +/** mark start of a new scan pass. + * clears any intra-symbol state and resets color to ::ZBAR_SPACE. + * any partially decoded symbol state is retained + */ +extern void zbar_decoder_new_scan(zbar_decoder_t *decoder); + +/** process next bar/space width from input stream. + * the width is in arbitrary relative units. first value of a scan + * is ::ZBAR_SPACE width, alternating from there. + * @returns appropriate symbol type if width completes + * decode of a symbol (data is available for retrieval) + * @returns ::ZBAR_PARTIAL as a hint if part of a symbol was decoded + * @returns ::ZBAR_NONE (0) if no new symbol data is available + */ +extern zbar_symbol_type_t zbar_decode_width(zbar_decoder_t *decoder, + unsigned width); + +/** retrieve color of @em next element passed to + * zbar_decode_width(). */ +extern zbar_color_t zbar_decoder_get_color(const zbar_decoder_t *decoder); + +/** retrieve last decoded data. + * @returns the data string or NULL if no new data available. + * the returned data buffer is owned by library, contents are only + * valid between non-0 return from zbar_decode_width and next library + * call + */ +extern const char *zbar_decoder_get_data(const zbar_decoder_t *decoder); + +/** retrieve length of binary data. + * @returns the length of the decoded data or 0 if no new data + * available. + */ +extern unsigned int zbar_decoder_get_data_length(const zbar_decoder_t *decoder); + +/** retrieve last decoded symbol type. + * @returns the type or ::ZBAR_NONE if no new data available + */ +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. + * pass a NULL value to disable callbacks. + * @returns the previously registered handler + */ +extern zbar_decoder_handler_t * +zbar_decoder_set_handler(zbar_decoder_t *decoder, + zbar_decoder_handler_t *handler); + +/** associate user specified data value with the decoder. */ +extern void zbar_decoder_set_userdata(zbar_decoder_t *decoder, void *userdata); + +/** return user specified data value associated with the decoder. */ +extern void *zbar_decoder_get_userdata(const zbar_decoder_t *decoder); + +/*@}*/ + +/*------------------------------------------------------------*/ +/** @name Scanner interface + * @anchor c-scanner + * low-level linear intensity sample stream scanner interface. + * identifies "bar" edges and measures width between them. + * optionally passes to bar width decoder + */ +/*@{*/ + +struct zbar_scanner_s; +/** opaque scanner object. */ +typedef struct zbar_scanner_s zbar_scanner_t; + +/** constructor. + * if decoder is non-NULL it will be attached to scanner + * and called automatically at each new edge + * current color is initialized to ::ZBAR_SPACE + * (so an initial BAR->SPACE transition may be discarded) + */ +extern zbar_scanner_t *zbar_scanner_create(zbar_decoder_t *decoder); + +/** destructor. */ +extern void zbar_scanner_destroy(zbar_scanner_t *scanner); + +/** clear all scanner state. + * also resets an associated decoder + */ +extern zbar_symbol_type_t zbar_scanner_reset(zbar_scanner_t *scanner); + +/** mark start of a new scan pass. resets color to ::ZBAR_SPACE. + * also updates an associated decoder. + * @returns any decode results flushed from the pipeline + * @note when not using callback handlers, the return value should + * be checked the same as zbar_scan_y() + * @note call zbar_scanner_flush() at least twice before calling this + * method to ensure no decode results are lost + */ +extern zbar_symbol_type_t zbar_scanner_new_scan(zbar_scanner_t *scanner); + +/** flush scanner processing pipeline. + * forces current scanner position to be a scan boundary. + * call multiple times (max 3) to completely flush decoder. + * @returns any decode/scan results flushed from the pipeline + * @note when not using callback handlers, the return value should + * be checked the same as zbar_scan_y() + * @since 0.9 + */ +extern zbar_symbol_type_t zbar_scanner_flush(zbar_scanner_t *scanner); + +/** process next sample intensity value. + * intensity (y) is in arbitrary relative units. + * @returns result of zbar_decode_width() if a decoder is attached, + * otherwise @returns (::ZBAR_PARTIAL) when new edge is detected + * or 0 (::ZBAR_NONE) if no new edge is detected + */ +extern zbar_symbol_type_t zbar_scan_y(zbar_scanner_t *scanner, int y); + +/** process next sample from RGB (or BGR) triple. */ +static inline zbar_symbol_type_t zbar_scan_rgb24(zbar_scanner_t *scanner, + unsigned char *rgb) +{ + return (zbar_scan_y(scanner, rgb[0] + rgb[1] + rgb[2])); +} + +/** retrieve last scanned width. */ +extern unsigned zbar_scanner_get_width(const zbar_scanner_t *scanner); + +/** retrieve sample position of last edge. + * @since 0.10 + */ +extern unsigned zbar_scanner_get_edge(const zbar_scanner_t *scn, + unsigned offset, int prec); + +/** retrieve last scanned color. */ +extern zbar_color_t zbar_scanner_get_color(const zbar_scanner_t *scanner); + +/*@}*/ + +#ifdef __cplusplus +} +} + +#include "zbar/Decoder.h" +#include "zbar/Exception.h" +#include "zbar/Image.h" +#include "zbar/ImageScanner.h" +#include "zbar/Processor.h" +#include "zbar/Scanner.h" +#include "zbar/Symbol.h" +#include "zbar/Video.h" +#include "zbar/Window.h" +#endif + +#endif diff --git a/include/zbar/Decoder.h b/include/zbar/Decoder.h new file mode 100644 index 0000000..60d34c3 --- /dev/null +++ b/include/zbar/Decoder.h @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_DECODER_H_ +#define _ZBAR_DECODER_H_ + +/// @file +/// Decoder C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Decoder.h" +#endif + +#include <string> + +namespace zbar +{ +/// low-level bar width stream decoder interface. +/// identifies symbols and extracts encoded data + +class Decoder +{ +public: + /// Decoder result handler. + /// applications should subtype this and pass an instance to + /// set_handler() to implement result processing + class Handler + { + public: + virtual ~Handler() + { + } + + /// invoked by the Decoder as decode results become available. + virtual void decode_callback(Decoder &decoder) = 0; + }; + + /// constructor. + Decoder() : _handler(NULL) + { + _decoder = zbar_decoder_create(); + } + + ~Decoder() + { + zbar_decoder_destroy(_decoder); + } + + /// clear all decoder state. + /// see zbar_decoder_reset() + void reset() + { + zbar_decoder_reset(_decoder); + } + + /// mark start of a new scan pass. + /// see zbar_decoder_new_scan() + void new_scan() + { + zbar_decoder_new_scan(_decoder); + } + + /// process next bar/space width from input stream. + /// see zbar_decode_width() + zbar_symbol_type_t decode_width(unsigned width) + { + return (zbar_decode_width(_decoder, width)); + } + + /// process next bar/space width from input stream. + /// see zbar_decode_width() + Decoder &operator<<(unsigned width) + { + zbar_decode_width(_decoder, width); + return (*this); + } + + /// retrieve color of @em next element passed to Decoder. + /// see zbar_decoder_get_color() + zbar_color_t get_color() const + { + return (zbar_decoder_get_color(_decoder)); + } + + /// retrieve last decoded symbol type. + /// see zbar_decoder_get_type() + zbar_symbol_type_t get_type() const + { + return (zbar_decoder_get_type(_decoder)); + } + + /// retrieve string name of last decoded symbol type. + /// see zbar_get_symbol_name() + const char *get_symbol_name() const + { + return (zbar_get_symbol_name(zbar_decoder_get_type(_decoder))); + } + + /// retrieve string name for last decode addon. + /// see zbar_get_addon_name() + /// @deprecated in 0.11 + const char *get_addon_name() const + { + return (zbar_get_addon_name(zbar_decoder_get_type(_decoder))); + } + + /// retrieve last decoded data in ASCII format as a char array. + /// see zbar_decoder_get_data() + const char *get_data_chars() const + { + return (zbar_decoder_get_data(_decoder)); + } + + /// retrieve last decoded data as a std::string. + /// see zbar_decoder_get_data() + const std::string get_data_string() const + { + return (std::string(zbar_decoder_get_data(_decoder), + zbar_decoder_get_data_length(_decoder))); + } + + /// retrieve last decoded data as a std::string. + /// see zbar_decoder_get_data() + const std::string get_data() const + { + return (get_data_string()); + } + + /// retrieve length of decoded binary data. + /// see zbar_decoder_get_data_length() + int get_data_length() const + { + 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) + { + _handler = &handler; + zbar_decoder_set_handler(_decoder, _cb); + zbar_decoder_set_userdata(_decoder, this); + } + + /// set config for indicated symbology (0 for all) to specified value. + /// @see zbar_decoder_set_config() + /// @since 0.4 + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) + { + return (zbar_decoder_set_config(_decoder, symbology, config, value)); + } + + /// set config parsed from configuration string. + /// @see zbar_decoder_parse_config() + /// @since 0.4 + int set_config(std::string cfgstr) + { + return (zbar_decoder_parse_config(_decoder, cfgstr.c_str())); + } + +private: + friend class Scanner; + zbar_decoder_t *_decoder; + Handler *_handler; + + static void _cb(zbar_decoder_t *cdcode) + { + Decoder *dcode = (Decoder *)zbar_decoder_get_userdata(cdcode); + if (dcode && dcode->_handler) + dcode->_handler->decode_callback(*dcode); + } +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/Exception.h b/include/zbar/Exception.h new file mode 100644 index 0000000..d9fe458 --- /dev/null +++ b/include/zbar/Exception.h @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_EXCEPTION_H_ +#define _ZBAR_EXCEPTION_H_ + +/// @file +/// C++ Exception definitions + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Exception.h" +#endif + +#include <cstddef> +#include <exception> +#include <new> + +namespace zbar +{ +/// base class for exceptions defined by this API. +class Exception : public std::exception +{ +public: + /// create exception from C library error + Exception(const void *obj = NULL) : std::exception(), _obj(obj) + { + } + + ~Exception() throw() + { + } + + /// retrieve error message + virtual const char *what() const throw() + { + if (!_obj) + return ("zbar library unspecified generic error"); + return (_zbar_error_string(_obj, 0)); + } + +private: + const void *_obj; +}; + +/// internal library error. +class InternalError : public Exception +{ +public: + /// create exception from C library error + InternalError(const void *obj) : Exception(obj) + { + } +}; + +/// unsupported request. +class UnsupportedError : public Exception +{ +public: + /// create exception from C library error + UnsupportedError(const void *obj) : Exception(obj) + { + } +}; + +/// invalid request. +class InvalidError : public Exception +{ +public: + /// create exception from C library error + InvalidError(const void *obj) : Exception(obj) + { + } +}; + +/// failed system call. +class SystemError : public Exception +{ +public: + /// create exception from C library error + SystemError(const void *obj) : Exception(obj) + { + } +}; + +/// locking error. +class LockingError : public Exception +{ +public: + /// create exception from C library error + LockingError(const void *obj) : Exception(obj) + { + } +}; + +/// all resources busy. +class BusyError : public Exception +{ +public: + /// create exception from C library error + BusyError(const void *obj) : Exception(obj) + { + } +}; + +/// X11 display error. +class XDisplayError : public Exception +{ +public: + /// create exception from C library error + XDisplayError(const void *obj) : Exception(obj) + { + } +}; + +/// X11 protocol error. +class XProtoError : public Exception +{ +public: + /// create exception from C library error + XProtoError(const void *obj) : Exception(obj) + { + } +}; + +/// output window is closed. +class ClosedError : public Exception +{ +public: + /// create exception from C library error + ClosedError(const void *obj) : Exception(obj) + { + } +}; + +/// image format error +class FormatError : public Exception +{ + // FIXME needs c equivalent + + virtual const char *what() const throw() + { + // FIXME what format? + return ("unsupported format"); + } +}; + +/// @internal + +/// extract error information and create exception. +static inline std::exception throw_exception(const void *obj) +{ + switch (_zbar_get_error_code(obj)) { + case ZBAR_ERR_NOMEM: + throw std::bad_alloc(); + case ZBAR_ERR_INTERNAL: + throw InternalError(obj); + case ZBAR_ERR_UNSUPPORTED: + throw UnsupportedError(obj); + case ZBAR_ERR_INVALID: + throw InvalidError(obj); + case ZBAR_ERR_SYSTEM: + throw SystemError(obj); + case ZBAR_ERR_LOCKING: + throw LockingError(obj); + case ZBAR_ERR_BUSY: + throw BusyError(obj); + case ZBAR_ERR_XDISPLAY: + throw XDisplayError(obj); + case ZBAR_ERR_XPROTO: + throw XProtoError(obj); + case ZBAR_ERR_CLOSED: + throw ClosedError(obj); + default: + throw Exception(obj); + } +} + +} // namespace zbar + +#endif diff --git a/include/zbar/Image.h b/include/zbar/Image.h new file mode 100644 index 0000000..ec2217b --- /dev/null +++ b/include/zbar/Image.h @@ -0,0 +1,322 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_IMAGE_H_ +#define _ZBAR_IMAGE_H_ + +/// @file +/// Image C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Image.h" +#endif + +#include <assert.h> +#include <iterator> +#include "Exception.h" +#include "Symbol.h" + +namespace zbar +{ +class Video; + +/// stores image data samples along with associated format and size +/// metadata + +class Image +{ +public: + /// general Image result handler. + /// applications should subtype this and pass an instance to + /// eg. ImageScanner::set_handler() to implement result processing + class Handler + { + public: + virtual ~Handler() + { + } + + /// invoked by library when Image should be processed + virtual void image_callback(Image &image) = 0; + + /// cast this handler to the C handler + operator zbar_image_data_handler_t *() const + { + return (_cb); + } + + private: + static void _cb(zbar_image_t *zimg, const void *userdata) + { + if (userdata) { + Image *image = (Image *)zbar_image_get_userdata(zimg); + if (image) + ((Handler *)userdata)->image_callback(*image); + else { + Image tmp(zimg, 1); + ((Handler *)userdata)->image_callback(tmp); + } + } + } + }; + + class SymbolIterator : public zbar::SymbolIterator + { + public: + /// default constructor. + SymbolIterator() : zbar::SymbolIterator() + { + } + + /// constructor. + SymbolIterator(const SymbolSet &syms) : zbar::SymbolIterator(syms) + { + } + + /// copy constructor. + SymbolIterator(const SymbolIterator &iter) : zbar::SymbolIterator(iter) + { + } + }; + + /// constructor. + /// create a new Image with the specified parameters + Image(unsigned width = 0, unsigned height = 0, + const std::string &format = "", const void *data = NULL, + unsigned long length = 0) + : _img(zbar_image_create()) + { + zbar_image_set_userdata(_img, this); + if (width && height) + set_size(width, height); + if (format.length()) + set_format(format); + if (data && length) + set_data(data, length); + } + + ~Image() + { + if (zbar_image_get_userdata(_img) == this) + zbar_image_set_userdata(_img, NULL); + zbar_image_ref(_img, -1); + } + + /// cast to C image object + operator const zbar_image_t *() const + { + return (_img); + } + + /// cast to C image object + operator zbar_image_t *() + { + return (_img); + } + + /// retrieve the image format. + /// see zbar_image_get_format() + unsigned long get_format() const + { + return (zbar_image_get_format(_img)); + } + + /// specify the fourcc image format code for image sample data. + /// see zbar_image_set_format() + void set_format(unsigned long format) + { + zbar_image_set_format(_img, format); + } + + /// specify the fourcc image format code for image sample data. + /// see zbar_image_set_format() + void set_format(const std::string &format) + { + unsigned long fourcc = zbar_fourcc_parse(format.c_str()); + zbar_image_set_format(_img, fourcc); + } + + /// retrieve a "sequence" (page/frame) number associated with this + /// image. + /// see zbar_image_get_sequence() + /// @since 0.6 + unsigned get_sequence() const + { + return (zbar_image_get_sequence(_img)); + } + + /// associate a "sequence" (page/frame) number with this image. + /// see zbar_image_set_sequence() + /// @since 0.6 + void set_sequence(unsigned sequence_num) + { + zbar_image_set_sequence(_img, sequence_num); + } + + /// retrieve the width of the image. + /// see zbar_image_get_width() + unsigned get_width() const + { + return (zbar_image_get_width(_img)); + } + + /// retrieve the height of the image. + /// see zbar_image_get_height() + unsigned get_height() const + { + 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, unsigned height) + { + 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 + { + return (zbar_image_get_data(_img)); + } + + /// return the size of the image sample data. + /// see zbar_image_get_data_length() + /// @since 0.6 + unsigned long get_data_length() const + { + return (zbar_image_get_data_length(_img)); + } + + /// specify image sample data. + /// see zbar_image_set_data() + void set_data(const void *data, unsigned long length) + { + zbar_image_set_data(_img, data, length, _cleanup); + } + + /// image format conversion. + /// see zbar_image_convert() + Image convert(unsigned long format) const + { + zbar_image_t *img = zbar_image_convert(_img, format); + if (img) + return (Image(img)); + throw FormatError(); + } + + /// image format conversion. + /// see zbar_image_convert() + /// @since 0.11 + Image convert(std::string format) const + { + unsigned long fourcc = zbar_fourcc_parse(format.c_str()); + return (convert(fourcc)); + } + + /// image format conversion with crop/pad. + /// see zbar_image_convert_resize() + /// @since 0.4 + Image convert(unsigned long format, unsigned width, unsigned height) const + { + zbar_image_t *img; + + img = zbar_image_convert_resize(_img, format, width, height); + if (img) + return (Image(img)); + throw FormatError(); + } + + const SymbolSet get_symbols() const + { + return (SymbolSet(zbar_image_get_symbols(_img))); + } + + void set_symbols(const SymbolSet &syms) + { + zbar_image_set_symbols(_img, syms); + } + + /// create a new SymbolIterator over decoded results. + SymbolIterator symbol_begin() const + { + return (SymbolIterator(get_symbols())); + } + + /// return a SymbolIterator suitable for ending iteration. + SymbolIterator symbol_end() const + { + return (SymbolIterator()); + } + +protected: + friend class Video; + + /// constructor. + /// @internal + /// create a new Image from a zbar_image_t C object + Image(zbar_image_t *src, int refs = 0) : _img(src) + { + if (refs) + zbar_image_ref(_img, refs); + zbar_image_set_userdata(_img, this); + } + + /// default data cleanup (noop) + /// @internal + static void _cleanup(zbar_image_t *img) + { + // by default nothing is cleaned + assert(img); + } + +private: + zbar_image_t *_img; +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/ImageScanner.h b/include/zbar/ImageScanner.h new file mode 100644 index 0000000..157a76d --- /dev/null +++ b/include/zbar/ImageScanner.h @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_IMAGE_SCANNER_H_ +#define _ZBAR_IMAGE_SCANNER_H_ + +/// @file +/// Image Scanner C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/ImageScanner.h" +#endif + +#include "Image.h" + +namespace zbar +{ +/// mid-level image scanner interface. +/// reads barcodes from a 2-D Image + +class ImageScanner +{ +public: + /// constructor. + ImageScanner(zbar_image_scanner_t *scanner = NULL) + { + if (scanner) + _scanner = scanner; + else + _scanner = zbar_image_scanner_create(); + } + + ~ImageScanner() + { + zbar_image_scanner_destroy(_scanner); + } + + /// cast to C image_scanner object + operator zbar_image_scanner_t *() const + { + return (_scanner); + } + + /// setup result handler callback. + void set_handler(Image::Handler &handler) + { + zbar_image_scanner_set_data_handler(_scanner, handler, &handler); + } + + /// request sending decoded codes via D-Bus + /// @see zbar_processor_parse_config() + /// @since 0.21 + int request_dbus(bool enabled) + { + return zbar_image_scanner_request_dbus(_scanner, enabled); + } + + /// set config for indicated symbology (0 for all) to specified value. + /// @see zbar_image_scanner_set_config() + /// @since 0.4 + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) + { + return ( + zbar_image_scanner_set_config(_scanner, symbology, config, value)); + } + + /// set config for indicated symbology (0 for all) to specified value. + /// @see zbar_image_scanner_set_config() + /// @since 0.22 + int get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value) + { + return ( + zbar_image_scanner_get_config(_scanner, symbology, config, &value)); + } + + /// set config parsed from configuration string. + /// @see zbar_image_scanner_parse_config() + /// @since 0.4 + int set_config(std::string cfgstr) + { + return (zbar_image_scanner_parse_config(_scanner, cfgstr.c_str())); + } + + /// enable or disable the inter-image result cache. + /// see zbar_image_scanner_enable_cache() + void enable_cache(bool enable = true) + { + zbar_image_scanner_enable_cache(_scanner, enable); + } + + /// remove previous results from scanner and image. + /// @see zbar_image_scanner_recycle_image() + /// @since 0.10 + void recycle_image(Image &image) + { + zbar_image_scanner_recycle_image(_scanner, image); + } + + /// retrieve decode results for last scanned image. + /// @see zbar_image_scanner_get_results() + /// @since 0.10 + const SymbolSet get_results() const + { + return (SymbolSet(zbar_image_scanner_get_results(_scanner))); + } + + /// scan for symbols in provided image. + /// see zbar_scan_image() + int scan(Image &image) + { + return (zbar_scan_image(_scanner, image)); + } + + /// scan for symbols in provided image. + /// see zbar_scan_image() + ImageScanner &operator<<(Image &image) + { + scan(image); + return (*this); + } + +private: + zbar_image_scanner_t *_scanner; +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/Processor.h b/include/zbar/Processor.h new file mode 100644 index 0000000..4da36ce --- /dev/null +++ b/include/zbar/Processor.h @@ -0,0 +1,220 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_PROCESSOR_H_ +#define _ZBAR_PROCESSOR_H_ + +/// @file +/// Processor C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Processor.h" +#endif + +#include "Exception.h" +#include "Image.h" + +namespace zbar +{ +/// high-level self-contained image processor. +/// processes video and images for barcodes, optionally displaying +/// images to a library owned output window + +class Processor +{ +public: + /// value to pass for no timeout. + static const int FOREVER = -1; + + /// constructor. + Processor(bool threaded = true, const char *video_device = "", + bool enable_display = true) + { + _processor = zbar_processor_create(threaded); + if (!_processor) + throw std::bad_alloc(); + init(video_device, enable_display); + } + + ~Processor() + { + zbar_processor_destroy(_processor); + } + + /// cast to C processor object. + operator zbar_processor_t *() + { + return (_processor); + } + + /// opens a video input device and/or prepares to display output. + /// see zbar_processor_init() + void init(const char *video_device = "", bool enable_display = true) + { + if (zbar_processor_init(_processor, video_device, enable_display)) + throw_exception(_processor); + } + + /// setup result handler callback. + /// see zbar_processor_set_data_handler() + void set_handler(Image::Handler &handler) + { + zbar_processor_set_data_handler(_processor, handler, &handler); + } + + /// set config for indicated symbology (0 for all) to specified value. + /// @see zbar_processor_set_config() + /// @since 0.4 + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) + { + return ( + zbar_processor_set_config(_processor, symbology, config, value)); + } + + /// set config parsed from configuration string. + /// @see zbar_processor_parse_config() + /// @since 0.4 + int set_config(std::string cfgstr) + { + return (zbar_processor_parse_config(_processor, cfgstr.c_str())); + } + + /// retrieve the current state of the output window. + /// see zbar_processor_is_visible() + bool is_visible() + { + int rc = zbar_processor_is_visible(_processor); + if (rc < 0) + throw_exception(_processor); + return (rc != 0); + } + + /// show or hide the display window owned by the library. + /// see zbar_processor_set_visible() + void set_visible(bool visible = true) + { + if (zbar_processor_set_visible(_processor, visible) < 0) + throw_exception(_processor); + } + + /// control the processor in free running video mode. + /// see zbar_processor_set_active() + void set_active(bool active = true) + { + if (zbar_processor_set_active(_processor, active) < 0) + throw_exception(_processor); + } + + /// retrieve decode results for last scanned image. + /// @see zbar_processor_get_results() + /// @since 0.10 + const SymbolSet get_results() const + { + return (SymbolSet(zbar_processor_get_results(_processor))); + } + + /// wait for input to the display window from the user. + /// see zbar_processor_user_wait() + int user_wait(int timeout = FOREVER) + { + int rc = zbar_processor_user_wait(_processor, timeout); + if (rc < 0) + throw_exception(_processor); + return (rc); + } + + /// process from the video stream until a result is available. + /// see zbar_process_one() + void process_one(int timeout = FOREVER) + { + if (zbar_process_one(_processor, timeout) < 0) + throw_exception(_processor); + } + + /// process the provided image for barcodes. + /// see zbar_process_image() + void process_image(Image &image) + { + if (zbar_process_image(_processor, image) < 0) + throw_exception(_processor); + } + + /// process the provided image for barcodes. + /// see zbar_process_image() + Processor &operator<<(Image &image) + { + process_image(image); + return (*this); + } + + /// force specific input and output formats for debug/testing. + /// see zbar_processor_force_format() + void force_format(unsigned long input_format, unsigned long output_format) + { + if (zbar_processor_force_format(_processor, input_format, + output_format)) + throw_exception(_processor); + } + + /// force specific input and output formats for debug/testing. + /// see zbar_processor_force_format() + void force_format(std::string &input_format, std::string &output_format) + { + 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); + } + + /// request a preferred size for the video image from the device. + /// see zbar_processor_request_size() + /// @since 0.6 + void request_size(int width, int height) + { + zbar_processor_request_size(_processor, width, height); + } + + /// request a preferred driver interface version for debug/testing. + /// see zbar_processor_request_interface() + /// @since 0.6 + void request_interface(int version) + { + zbar_processor_request_interface(_processor, version); + } + + /// request a preferred I/O mode for debug/testing. + /// see zbar_processor_request_iomode() + /// @since 0.7 + void request_iomode(int iomode) + { + if (zbar_processor_request_iomode(_processor, iomode)) + throw_exception(_processor); + } + +private: + zbar_processor_t *_processor; +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/QZBar.h b/include/zbar/QZBar.h new file mode 100644 index 0000000..35825c3 --- /dev/null +++ b/include/zbar/QZBar.h @@ -0,0 +1,207 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _QZBAR_H_ +#define _QZBAR_H_ + +/// @file +/// Barcode Reader Qt4 Widget + +#include <QtGui> +#if QT_VERSION >= 0x050000 +#include <QtWidgets> +#else +#include <qwidget.h> +#endif +#include <zbar.h> + +namespace zbar +{ +class QZBarThread; + +/// barcode reader Qt4 widget. +/// embeds a barcode reader directly into a Qt4 based GUI. the widget +/// can process barcodes from a video source (using the QZBar::videoDevice +/// and QZBar::videoEnabled properties) or from individual QImages +/// supplied to the QZBar::scanImage() slot +/// @since 1.5 + +class QZBar : public QWidget +{ + Q_OBJECT + + /// the currently opened video device. + /// + /// setting a new device opens it and automatically sets + /// QZBar::videoEnabled + /// + /// @see videoDevice(), setVideoDevice() + Q_PROPERTY(QString videoDevice READ videoDevice WRITE setVideoDevice + DESIGNABLE false) + + /// video device streaming state. + /// + /// use to pause/resume video scanning. + /// + /// @see isVideoEnabled(), setVideoEnabled() + Q_PROPERTY(bool videoEnabled READ isVideoEnabled WRITE setVideoEnabled + DESIGNABLE false) + + /// video device opened state. + /// + /// (re)setting QZBar::videoDevice should eventually cause it + /// to be opened or closed. any errors while streaming/scanning + /// will also cause the device to be closed + /// + /// @see isVideoOpened() + Q_PROPERTY(bool videoOpened READ isVideoOpened DESIGNABLE false) + +public: + // Should match the types at video_control_type_e + // get_controls() will do the mapping between the two types. + enum ControlType + { + Unknown, + Integer, + Menu, + Button, + Integer64, + String, + Boolean, + }; + + /// constructs a barcode reader widget with the given @a parent + QZBar(QWidget *parent = NULL, int verbosity = 0); + + ~QZBar(); + + /// retrieve the currently opened video device. + /// @returns the current video device or the empty string if no + /// device is opened + QString videoDevice() const; + + /// retrieve the current video enabled state. + /// @returns true if video scanning is currently enabled, false + /// otherwise + bool isVideoEnabled() const; + + /// retrieve the current video opened state. + /// @returns true if video device is currently opened, false otherwise + bool isVideoOpened() const; + + /// @{ + /// @internal + + QSize sizeHint() const; + int heightForWidth(int) const; + QPaintEngine *paintEngine() const; + + /// @} + +public Q_SLOTS: + + /// open a new video device. + /// + /// use an empty string to close a currently opened device. + /// + /// @note since opening a device may take some time, this call will + /// return immediately and the device will be opened asynchronously + void setVideoDevice(const QString &videoDevice); + + /// enable/disable video scanning. + /// has no effect unless a video device is opened + void setVideoEnabled(bool videoEnabled = true); + + /// scan for barcodes in a QImage. + void scanImage(const QImage &image); + + /// get controls from the camera device + int get_controls(int index, char **name = NULL, char **group = NULL, + enum ControlType *type = NULL, int *min = NULL, + int *max = NULL, int *def = NULL, int *step = NULL); + + /// Get items for control menus + QVector<QPair<int, QString> > get_menu(int index); + + // get/set controls from the camera device + int set_control(char *name, bool value); + int set_control(char *name, int value); + int get_control(char *name, bool *value); + int get_control(char *name, int *value); + + int set_config(std::string cfgstr); + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value); + int get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value); + void request_size(unsigned width, unsigned height, bool trigger = true); + int get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps); + unsigned videoWidth(); + unsigned videoHeight(); + int request_dbus(bool enabled); + +Q_SIGNALS: + /// emitted when when a video device is opened or closed. + /// + /// (re)setting QZBar::videoDevice should eventually cause it + /// to be opened or closed. any errors while streaming/scanning + /// will also cause the device to be closed + void videoOpened(bool videoOpened); + + /// emitted when a barcode is decoded from an image. + /// the symbol type and contained data are provided as separate + /// parameters. + void decoded(int type, const QString &data); + + /// emitted when a barcode is decoded from an image. + /// the symbol type name is prefixed to the data, separated by a + /// colon + void decodedText(const QString &text); + + /// @{ + /// @internal + +protected: + void attach(); + void showEvent(QShowEvent *); + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void changeEvent(QEvent *); + void dragEnterEvent(QDragEnterEvent *); + void dropEvent(QDropEvent *); + +protected Q_SLOTS: + void sizeChange(); + + /// @} + +private: + QZBarThread *thread; + QString _videoDevice; + bool _videoEnabled; + bool _attached; +}; + +}; // namespace zbar + +#endif diff --git a/include/zbar/QZBarImage.h b/include/zbar/QZBarImage.h new file mode 100644 index 0000000..ccacb24 --- /dev/null +++ b/include/zbar/QZBarImage.h @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _QZBARIMAGE_H_ +#define _QZBARIMAGE_H_ + +/// @file +/// QImage to Image type conversion wrapper + +#include <qimage.h> +#include <zbar.h> + +namespace zbar +{ +/// wrap a QImage and convert into a format suitable for scanning. + +class QZBarImage : public Image +{ +public: + /// construct a zbar library image based on an existing QImage. + + QZBarImage(const QImage &qimg) : qimg(qimg) + { + QImage::Format fmt = qimg.format(); + if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32 && + fmt != QImage::Format_ARGB32_Premultiplied) + throw FormatError(); + + unsigned bpl = qimg.bytesPerLine(); + unsigned width = bpl / 4; + unsigned height = qimg.height(); + set_size(width, height); + set_format(zbar_fourcc('B', 'G', 'R', '4')); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + unsigned long datalen = qimg.sizeInBytes(); +#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + unsigned long datalen = qimg.byteCount(); +#else + unsigned long datalen = qimg.numBytes(); +#endif + set_data(qimg.bits(), datalen); + + if ((width * 4 != bpl) || (width * height * 4 > datalen)) + throw FormatError(); + } + +private: + QImage qimg; +}; + +}; // namespace zbar + +#endif diff --git a/include/zbar/Scanner.h b/include/zbar/Scanner.h new file mode 100644 index 0000000..a6a38e8 --- /dev/null +++ b/include/zbar/Scanner.h @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_SCANNER_H_ +#define _ZBAR_SCANNER_H_ + +/// @file +/// Scanner C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Scanner.h" +#endif + +#include <stdio.h> + +namespace zbar +{ +/// low-level linear intensity sample stream scanner interface. +/// identifies "bar" edges and measures width between them. +/// optionally passes to bar width Decoder + +class Scanner +{ +public: + /// constructor. + /// @param decoder reference to a Decoder instance which will + /// be passed scan results automatically + Scanner(Decoder &decoder) + { + _scanner = zbar_scanner_create(decoder._decoder); + } + + /// constructor. + /// @param decoder pointer to a Decoder instance which will + /// be passed scan results automatically + Scanner(Decoder *decoder = NULL) + { + zbar_decoder_t *zdcode = NULL; + if (decoder) + zdcode = decoder->_decoder; + _scanner = zbar_scanner_create(zdcode); + } + + ~Scanner() + { + zbar_scanner_destroy(_scanner); + } + + /// clear all scanner state. + /// see zbar_scanner_reset() + void reset() + { + zbar_scanner_reset(_scanner); + } + + /// mark start of a new scan pass. + /// see zbar_scanner_new_scan() + zbar_symbol_type_t new_scan() + { + _type = zbar_scanner_new_scan(_scanner); + return (_type); + } + + /// flush scanner pipeline. + /// see zbar_scanner_flush() + zbar_symbol_type_t flush() + { + _type = zbar_scanner_flush(_scanner); + return (_type); + } + + /// process next sample intensity value. + /// see zbar_scan_y() + zbar_symbol_type_t scan_y(int y) + { + _type = zbar_scan_y(_scanner, y); + return (_type); + } + + /// process next sample intensity value. + /// see zbar_scan_y() + Scanner &operator<<(int y) + { + _type = zbar_scan_y(_scanner, y); + return (*this); + } + + /// process next sample from RGB (or BGR) triple. + /// see zbar_scan_rgb24() + zbar_symbol_type_t scan_rgb24(unsigned char *rgb) + { + _type = zbar_scan_rgb24(_scanner, rgb); + return (_type); + } + + /// process next sample from RGB (or BGR) triple. + /// see zbar_scan_rgb24() + Scanner &operator<<(unsigned char *rgb) + { + _type = zbar_scan_rgb24(_scanner, rgb); + return (*this); + } + + /// retrieve last scanned width. + /// see zbar_scanner_get_width() + unsigned get_width() const + { + return (zbar_scanner_get_width(_scanner)); + } + + /// retrieve last scanned color. + /// see zbar_scanner_get_color() + zbar_color_t get_color() const + { + return (zbar_scanner_get_color(_scanner)); + } + + /// retrieve last scan result. + zbar_symbol_type_t get_type() const + { + return (_type); + } + + /// cast to C scanner + operator zbar_scanner_t *() const + { + return (_scanner); + } + + /// retrieve C scanner + const zbar_scanner_t *get_c_scanner() const + { + return (_scanner); + } + +private: + zbar_scanner_t *_scanner; + zbar_symbol_type_t _type; +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/Symbol.h b/include/zbar/Symbol.h new file mode 100644 index 0000000..f3a5668 --- /dev/null +++ b/include/zbar/Symbol.h @@ -0,0 +1,532 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_SYMBOL_H_ +#define _ZBAR_SYMBOL_H_ + +/// @file +/// Symbol C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Symbol.h" +#endif + +#include <assert.h> +#include <ostream> +#include <stdlib.h> +#include <string> + +namespace zbar +{ +class SymbolIterator; + +/// container for decoded result symbols associated with an image +/// or a composite symbol. + +class SymbolSet +{ +public: + /// constructor. + SymbolSet(const zbar_symbol_set_t *syms = NULL) : _syms(syms) + { + ref(); + } + + /// copy constructor. + SymbolSet(const SymbolSet &syms) : _syms(syms._syms) + { + ref(); + } + + /// destructor. + ~SymbolSet() + { + 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 + { + if (_syms) + zbar_symbol_set_ref((zbar_symbol_set_t *)_syms, delta); + } + + /// cast to C symbol set. + operator const zbar_symbol_set_t *() const + { + return (_syms); + } + + int get_size() const + { + return ((_syms) ? zbar_symbol_set_get_size(_syms) : 0); + } + + /// create a new SymbolIterator over decoded results. + SymbolIterator symbol_begin() const; + + /// return a SymbolIterator suitable for ending iteration. + const SymbolIterator symbol_end() const; + +private: + const zbar_symbol_set_t *_syms; +}; + +/// decoded barcode symbol result object. stores type, data, and +/// image location of decoded symbol + +class Symbol +{ +public: + /// image pixel location (x, y) coordinate tuple. + class Point + { + public: + int x; ///< x-coordinate. + int y; ///< y-coordinate. + + Point() + { + } + + Point(int x, int y) : x(x), y(y) + { + } + + /// 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); + } + }; + + /// iteration over Point objects in a symbol location polygon. + class PointIterator : public std::iterator<std::input_iterator_tag, Point> + { + public: + /// constructor. + PointIterator(const Symbol *sym = NULL, int index = 0) + : _sym(sym), _index(index) + { + if (sym) + sym->ref(1); + if (!sym || (unsigned)_index >= zbar_symbol_get_loc_size(*_sym)) + _index = -1; + } + + /// copy constructor. + PointIterator(const PointIterator &iter) + : _sym(iter._sym), _index(iter._index) + { + if (_sym) + _sym->ref(); + } + + /// destructor. + ~PointIterator() + { + if (_sym) + _sym->ref(-1); + } + + /// assignment. + PointIterator &operator=(const PointIterator &iter) + { + if (iter._sym) + iter._sym->ref(); + if (_sym) + _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++() + { + unsigned int i = ++_index; + if (!_sym || i >= zbar_symbol_get_loc_size(*_sym)) + _index = -1; + return (*this); + } + + /// retrieve currently referenced Point. + const Point operator*() const + { + assert(!!*this); + if (!*this) + return (Point()); + return (Point(zbar_symbol_get_loc_x(*_sym, _index), + zbar_symbol_get_loc_y(*_sym, _index))); + } + + /// test if two iterators refer to the same Point in the same + /// Symbol. + bool operator==(const PointIterator &iter) const + { + return (_index == iter._index && + ((_index < 0) || _sym == iter._sym)); + } + + /// test if two iterators refer to the same Point in the same + /// Symbol. + bool operator!=(const PointIterator &iter) const + { + return (!(*this == iter)); + } + + private: + const Symbol *_sym; + int _index; + }; + + /// constructor. + Symbol(const zbar_symbol_t *sym = NULL) : _xmlbuf(NULL), _xmllen(0) + { + init(sym); + ref(); + } + + /// copy constructor. + Symbol(const Symbol &sym) + : _sym(sym._sym), _type(sym._type), _data(sym._data), _xmlbuf(NULL), + _xmllen(0) + { + ref(); + } + + /// destructor. + ~Symbol() + { + if (_xmlbuf) + free(_xmlbuf); + 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) + zbar_symbol_ref((zbar_symbol_t *)_sym, delta); + } + + /// cast to C symbol. + operator const zbar_symbol_t *() const + { + return (_sym); + } + + /// test if two Symbol objects refer to the same C symbol. + bool operator==(const Symbol &sym) const + { + return (_sym == sym._sym); + } + + /// test if two Symbol objects refer to the same C symbol. + bool operator!=(const Symbol &sym) const + { + return (!(*this == sym)); + } + + /// retrieve type of decoded symbol. + zbar_symbol_type_t get_type() const + { + return (_type); + } + + /// retrieve the string name of the symbol type. + const std::string get_type_name() const + { + return (zbar_get_symbol_name(_type)); + } + + /// retrieve the string name for any addon. + /// @deprecated in 0.11 + const std::string get_addon_name() const + { + return (zbar_get_addon_name(_type)); + } + + /// retrieve data decoded from symbol. + const std::string get_data() const + { + return (_data); + } + + /// retrieve length of binary data + unsigned get_data_length() const + { + return ((_sym) ? zbar_symbol_get_data_length(_sym) : 0); + } + + /// retrieve inter-frame coherency count. + /// see zbar_symbol_get_count() + /// @since 0.5 + int get_count() const + { + return ((_sym) ? zbar_symbol_get_count(_sym) : -1); + } + + /// retrieve loosely defined relative quality metric. + /// see zbar_symbol_get_quality() + /// @since 0.11 + int get_quality() const + { + return ((_sym) ? zbar_symbol_get_quality(_sym) : 0); + } + + SymbolSet get_components() const + { + return (SymbolSet((_sym) ? zbar_symbol_get_components(_sym) : NULL)); + } + + /// create a new PointIterator at the start of the location + /// polygon. + PointIterator point_begin() const + { + return (PointIterator(this)); + } + + /// return a PointIterator suitable for ending iteration. + const PointIterator point_end() const + { + return (PointIterator()); + } + + /// see zbar_symbol_get_loc_size(). + int get_location_size() const + { + return ((_sym) ? zbar_symbol_get_loc_size(_sym) : 0); + } + + /// see zbar_symbol_get_loc_x(). + int get_location_x(unsigned index) const + { + return ((_sym) ? zbar_symbol_get_loc_x(_sym, index) : -1); + } + + /// see zbar_symbol_get_loc_y(). + int get_location_y(unsigned index) const + { + 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 + { + if (!_sym) + return (""); + return (zbar_symbol_xml(_sym, (char **)&_xmlbuf, (unsigned *)&_xmllen)); + } + +protected: + /// (re)initialize Symbol from C symbol object. + void init(const zbar_symbol_t *sym = NULL) + { + _sym = sym; + if (sym) { + _type = zbar_symbol_get_type(sym); + _data = std::string(zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym)); + } else { + _type = ZBAR_NONE; + _data = ""; + } + } + +private: + const zbar_symbol_t *_sym; + zbar_symbol_type_t _type; + std::string _data; + char *_xmlbuf; + unsigned _xmllen; +}; + +/// iteration over Symbol result objects in a scanned Image or SymbolSet. +class SymbolIterator : public std::iterator<std::input_iterator_tag, Symbol> +{ +public: + /// default constructor. + SymbolIterator() + { + } + + /// constructor. + SymbolIterator(const SymbolSet &syms) : _syms(syms) + { + const zbar_symbol_set_t *zsyms = _syms; + if (zsyms) + _sym = zbar_symbol_set_first_symbol(zsyms); + } + + /// copy constructor. + SymbolIterator(const SymbolIterator &iter) : _syms(iter._syms) + { + const zbar_symbol_set_t *zsyms = _syms; + if (zsyms) + _sym = zbar_symbol_set_first_symbol(zsyms); + } + + ~SymbolIterator() + { + } + + /// 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++() + { + if (!!_sym) + _sym = zbar_symbol_next(_sym); + else if (!!_syms) + _sym = zbar_symbol_set_first_symbol(_syms); + return (*this); + } + + /// retrieve currently referenced Symbol. + const Symbol operator*() const + { + return (_sym); + } + + /// access currently referenced Symbol. + const Symbol *operator->() const + { + return (&_sym); + } + + /// test if two iterators refer to the same Symbol + bool operator==(const SymbolIterator &iter) const + { + // it is enough to test the symbols, as they belong + // to only one set (also simplifies invalid case) + return (_sym == iter._sym); + } + + /// test if two iterators refer to the same Symbol + bool operator!=(const SymbolIterator &iter) const + { + return (!(*this == iter)); + } + + const SymbolIterator end() const + { + return (SymbolIterator()); + } + +private: + SymbolSet _syms; + Symbol _sym; +}; + +inline SymbolIterator SymbolSet::symbol_begin() const +{ + return (SymbolIterator(*this)); +} + +inline const SymbolIterator SymbolSet::symbol_end() const +{ + return (SymbolIterator()); +} + +/// @relates Symbol +/// stream the string representation of a Symbol. +static inline std::ostream &operator<<(std::ostream &out, const Symbol &sym) +{ + out << sym.get_type_name() << ":" << sym.get_data(); + return (out); +} + +} // namespace zbar + +#endif diff --git a/include/zbar/Video.h b/include/zbar/Video.h new file mode 100644 index 0000000..07d11c2 --- /dev/null +++ b/include/zbar/Video.h @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_VIDEO_H_ +#define _ZBAR_VIDEO_H_ + +/// @file +/// Video Input C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Video.h" +#endif + +#include "Image.h" + +namespace zbar +{ +/// mid-level video source abstraction. +/// captures images from a video device + +class Video +{ +public: + /// constructor. + Video(zbar_video_t *video = NULL) + { + if (video) + _video = video; + else + _video = zbar_video_create(); + } + + /// constructor. + Video(std::string &device) + { + _video = zbar_video_create(); + open(device); + } + + ~Video() + { + zbar_video_destroy(_video); + } + + /// cast to C video object. + operator zbar_video_t *() const + { + return (_video); + } + + /// open and probe a video device. + void open(std::string &device) + { + if (zbar_video_open(_video, device.c_str())) + throw_exception(_video); + } + + /// close video device if open. + void close() + { + if (zbar_video_open(_video, NULL)) + throw_exception(_video); + } + + /// initialize video using a specific format for debug. + /// see zbar_video_init() + void init(unsigned long fourcc) + { + if (zbar_video_init(_video, fourcc)) + throw_exception(_video); + } + + /// initialize video using a specific format for debug. + /// see zbar_video_init() + void init(std::string &format) + { + unsigned int fourcc = zbar_fourcc_parse(format.c_str()); + if (zbar_video_init(_video, fourcc)) + throw_exception(_video); + } + + /// retrieve file descriptor associated with open *nix video device. + /// see zbar_video_get_fd() + int get_fd() + { + return (zbar_video_get_fd(_video)); + } + + /// retrieve current output image width. + /// see zbar_video_get_width() + int get_width() + { + return (zbar_video_get_width(_video)); + } + + /// retrieve current output image height. + /// see zbar_video_get_height() + int get_height() + { + return (zbar_video_get_height(_video)); + } + + /// start/stop video capture. + /// see zbar_video_enable() + void enable(bool enable = true) + { + if (zbar_video_enable(_video, enable)) + throw_exception(_video); + } + + /// retrieve next captured image. + /// see zbar_video_next_image() + Image next_image() + { + zbar_image_t *img = zbar_video_next_image(_video); + if (!img) + throw_exception(_video); + return (Image(img)); + } + + /// request a preferred size for the video image from the device. + /// see zbar_video_request_size() + /// @since 0.6 + void request_size(int width, int height) + { + zbar_video_request_size(_video, width, height); + } + + /// request a preferred driver interface version for debug/testing. + /// see zbar_video_request_interface() + /// @since 0.6 + void request_interface(int version) + { + zbar_video_request_interface(_video, version); + } + + /// request a preferred I/O mode for debug/testing. + /// see zbar_video_request_iomode() + /// @since 0.7 + void request_iomode(int iomode) + { + if (zbar_video_request_iomode(_video, iomode)) + throw_exception(_video); + } + + /// get the information about a control at a given index + /// see zbar_video_get_controls() + /// @since 0.11 + struct video_controls_s *get_controls(int index) + { + return (zbar_video_get_controls(_video, index)); + } + + /// set the value on an integer control + /// see zbar_video_set_control_n() + /// @since 0.11 + int set_control(const char *name, int value) + { + return zbar_video_set_control(_video, name, value); + } + + /// set the value on a boolean control + /// see zbar_video_set_control_b() + /// @since 0.11 + int set_control(const char *name, bool value) + { + return zbar_video_set_control(_video, name, value ? 1 : 0); + } + + /// get the value on a boolean control + /// see zbar_video_get_control_b() + /// @since 0.11 + int get_control(const char *name, int *value) + { + return zbar_video_get_control(_video, name, value); + } + + /// get the value on an integer control + /// see zbar_video_get_control_n() + /// @since 0.11 + int get_control(const char *name, bool *value) + { + int __value; + int ret = zbar_video_get_control(_video, name, &__value); + + *value = __value ? true : false; + + return ret; + } + + /// get the information about a control at a given index + /// see zbar_video_get_resolutions() + /// @since 0.22 + struct video_resolution_s *get_resolution(int index) + { + return (zbar_video_get_resolutions(_video, index)); + } + +private: + zbar_video_t *_video; +}; + +} // namespace zbar + +#endif diff --git a/include/zbar/Window.h b/include/zbar/Window.h new file mode 100644 index 0000000..a378e02 --- /dev/null +++ b/include/zbar/Window.h @@ -0,0 +1,134 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _ZBAR_WINDOW_H_ +#define _ZBAR_WINDOW_H_ + +/// @file +/// Output Window C++ wrapper + +#ifndef _ZBAR_H_ +#error "include zbar.h in your application, **not** zbar/Window.h" +#endif + +#include "Image.h" + +namespace zbar +{ +/// mid-level output window abstraction. +/// displays images to user-specified platform specific output window + +class Window +{ +public: + /// constructor. + Window(zbar_window_t *window = NULL) + { + if (window) + _window = window; + else + _window = zbar_window_create(); + } + + /// constructor. + Window(void *x11_display_w32_hwnd, unsigned long x11_drawable) + { + _window = zbar_window_create(); + attach(x11_display_w32_hwnd, x11_drawable); + } + + ~Window() + { + zbar_window_destroy(_window); + } + + /// cast to C window object. + operator zbar_window_t *() const + { + return (_window); + } + + /// associate reader with an existing platform window. + /// see zbar_window_attach() + void attach(void *x11_display_w32_hwnd, unsigned long x11_drawable = 0) + { + if (zbar_window_attach(_window, x11_display_w32_hwnd, x11_drawable) < 0) + throw_exception(_window); + } + + /// control content level of the reader overlay. + /// see zbar_window_set_overlay() + void set_overlay(int level) + { + zbar_window_set_overlay(_window, level); + } + + /// retrieve current content level of reader overlay. + /// see zbar_window_get_overlay() + + /// draw a new image into the output window. + /// see zbar_window_draw() + void draw(Image &image) + { + if (zbar_window_draw(_window, image) < 0) + throw_exception(_window); + } + + /// clear the image from the output window. + /// see zbar_window_draw() + void clear() + { + if (zbar_window_draw(_window, NULL) < 0) + throw_exception(_window); + } + + /// redraw the last image. + /// zbar_window_redraw() + void redraw() + { + if (zbar_window_redraw(_window) < 0) + throw_exception(_window); + } + + /// resize the image window. + /// zbar_window_resize() + void resize(unsigned width, unsigned height) + { + if (zbar_window_resize(_window, width, height) < 0) + throw_exception(_window); + } + +private: + zbar_window_t *_window; +}; + +/// select a compatible format between video input and output window. +/// see zbar_negotiate_format() +static inline void negotiate_format(Video &video, Window &window) +{ + if (zbar_negotiate_format(video, window) < 0) + throw_exception(video); +} + +} // namespace zbar + +#endif diff --git a/include/zbar/zbargtk.h b/include/zbar/zbargtk.h new file mode 100644 index 0000000..217f502 --- /dev/null +++ b/include/zbar/zbargtk.h @@ -0,0 +1,253 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef __ZBAR_GTK_H__ +#define __ZBAR_GTK_H__ + +/* + * NOTE: before modifying this file, please see: + * + * https://wiki.gnome.org/Projects/GObjectIntrospection/Annotations/ + * https://gi.readthedocs.io/en/latest/annotations/giannotations.html + * + * For the annotations needed for Gobject Introspection (GIR) to work + */ + +/** + * SECTION:ZBarGtk + * @short_description: barcode reader GTK+ 2.x widget + * @Title: ZBar Gtk bindings + * @include: zbar/zbargtk.h + * + * embeds a barcode reader directly into a GTK+ based GUI. the widget + * can process barcodes from a video source (using the + * #ZBarGtk:video-device and #ZBarGtk:video-enabled properties) or + * from individual GdkPixbufs supplied to zbar_gtk_scan_image() + * + * Since: 1.0 + */ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <zbar.h> + +G_BEGIN_DECLS + +/* --- type macros --- */ +#define ZBAR_TYPE_GTK (zbar_gtk_get_type()) +#define ZBAR_GTK(object) \ + (G_TYPE_CHECK_INSTANCE_CAST((object), ZBAR_TYPE_GTK, ZBarGtk)) +#define ZBAR_GTK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), ZBAR_TYPE_GTK, ZBarGtkClass)) +#define ZBAR_IS_GTK(object) \ + (G_TYPE_CHECK_INSTANCE_TYPE((object), ZBAR_TYPE_GTK)) +#define ZBAR_IS_GTK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), ZBAR_TYPE_GTK)) +#define ZBAR_GTK_GET_CLASS(object) \ + (G_TYPE_INSTANCE_GET_CLASS((object), ZBAR_TYPE_GTK, ZBarGtkClass)) + +/* --- typedefs & structures --- */ + +typedef struct _ZBarGtk ZBarGtk; +typedef struct _ZBarGtkClass ZBarGtkClass; + +/** + * _ZBarGtk: (rename-to ZBarGtk) (ref-func zbar_gtk_new) (get-value-func zbar_gtk_get_type) + * @widget: pointer to GtkWidget + * @_private: used internally + */ +struct _ZBarGtk { + GtkWidget widget; + gpointer *_private; + + /* properties */ + + /* + * ZBarGtk:video-device: + * + * the currently set video device. + * + * setting a new device opens it and automatically sets + * #ZBarGtk:video-enabled. set the empty string ("") or NULL to + * close. + */ + + /* + * ZBarGtk:video-enabled: + * + * video device streaming state. + * + * use to pause/resume video scanning. + */ + + /* + * ZBarGtk:video-opened: + * + * video device opened state. + * + * (re)setting #ZBarGtk:video-device should eventually cause it + * to be opened or closed. any errors while streaming/scanning + * will also cause the device to be closed + */ +}; + +/** + * _ZBarGtkClass: + */ +struct _ZBarGtkClass { + GtkWidgetClass parent_class; + + /* signals */ + + /** + * ZBarGtk::decoded: + * @widget: the object that received the signal + * @symbol_type: the type of symbol decoded (a zbar_symbol_type_t) + * @data: the data decoded from the symbol + * + * emitted when a barcode is decoded from an image. + * the symbol type and contained data are provided as separate + * parameters + */ + void (*decoded)(ZBarGtk *zbar, zbar_symbol_type_t symbol_type, + const char *data); + + /** + * ZBarGtk::decoded-text: + * @widget: the object that received the signal + * @text: the decoded data prefixed by the string name of the + * symbol type (separated by a colon) + * + * emitted when a barcode is decoded from an image. + * the symbol type name is prefixed to the data, separated by a + * colon + */ + void (*decoded_text)(ZBarGtk *zbar, const char *text); + + /** + * ZBarGtk:scan-image: + * @widget: the object that received the signal + * @image: the image to scan for barcodes + */ + void (*scan_image)(ZBarGtk *zbar, GdkPixbuf *image); +}; + +/** + * zbar_gtk_get_type: (skip) + * Returns ZBarGtk type + * @returns: #GType + */ +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. + * + * Returns: (transfer full): a new #ZBarGtk widget instance + */ +GtkWidget *zbar_gtk_new(void); + +/** + * zbar_gtk_scan_image: + * @zbar: pointer to #ZBarGtk + * @image: the GdkPixbuf used to store the image + * + */ +void zbar_gtk_scan_image(ZBarGtk *zbar, GdkPixbuf *image); + +/** + * zbar_gtk_get_video_device: + * retrieve the currently opened video device. + * @zbar: pointer to #ZBarGtk + * + * Returns: the current video device or NULL if no device is opened + */ +const char *zbar_gtk_get_video_device(ZBarGtk *zbar); + +/** + * zbar_gtk_set_video_device: + * open a new video device. + * @zbar: pointer to #ZBarGtk + * @video_device: (nullable) (type filename) : the platform specific name of + * the device to open. use NULL to close a currently opened device. + * + * @note since opening a device may take some time, this call will + * return immediately and the device will be opened asynchronously + */ +void zbar_gtk_set_video_device(ZBarGtk *zbar, const char *video_device); + +/** + * zbar_gtk_get_video_enabled: + * retrieve the current video enabled state. + * @zbar: pointer to #ZBarGtk + * + * Returns: true if video scanning is currently enabled, false otherwise + */ +gboolean zbar_gtk_get_video_enabled(ZBarGtk *zbar); + +/** + * zbar_gtk_set_video_enabled: + * enable/disable video scanning. + * @zbar: pointer to #ZBarGtk + * @video_enabled: true to enable video scanning, false to disable + * + * has no effect unless a video device is opened + */ +void zbar_gtk_set_video_enabled(ZBarGtk *zbar, gboolean video_enabled); + +/** + * zbar_gtk_get_video_opened: + * retrieve the current video opened state. + * @zbar: pointer to #ZBarGtk + * + * Returns: true if video device is currently opened, false otherwise + */ +gboolean zbar_gtk_get_video_opened(ZBarGtk *zbar); + +/** + * zbar_gtk_request_video_size: + * set video camera resolution. + * @zbar: pointer to #ZBarGtk + * @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); + +/** + * zbar_gtk_image_from_pixbuf: + * utility function to populate a zbar_image_t from a GdkPixbuf. + * @image: (type gpointer) : the zbar library image destination to populate + * @pixbuf: the GdkPixbuf source + * + * Returns: TRUE if successful or FALSE if the conversion could not be + * performed for some reason + */ +gboolean zbar_gtk_image_from_pixbuf(zbar_image_t *image, GdkPixbuf *pixbuf); + +G_END_DECLS + +#endif diff --git a/iphone/ChangeLog b/iphone/ChangeLog new file mode 100644 index 0000000..cce05df --- /dev/null +++ b/iphone/ChangeLog @@ -0,0 +1,138 @@ +version 1.3.1: + * fix examples + - fix EmbedReader initial iPad orientation + - fix TabReader shouldn't show controls + * add retry workaround when device lock fails + - add iPhone 5 launch image to examples + - doc updates + * Lion and Xcode updates + - fix new warnings/errors + - find missing buddy + - fix SDK bg image: force resolution to 72dpi + * Fix EmbedReader example rotation interaction + +version 1.2.2: + * reduce controller present and dismiss latency + - add simple shutter to mask video start + +version 1.2.1: + * fix overlay resizing bug + +version 1.2: + * release updates + +version 1.1.3: + * fix UITabViewController rotation interaction + +version 1.1.2: + * add maxZoom for increasing zoom range + - workaround camera preview initial location/size bug + * add emulation for UI videoQuality to adjust camera resolution + * fix several simulator-related bugs + - fix device property missing from simulated ZBarReaderView + - fix AVCaptureDevice referenced from ZBarReaderViewController + - fix simulated camera gesture multiple recognition + +version 1.1.1: + * fix ReaderSample project path to ZBarSDK + * README document new examples + - make ReaderSample scheme public + * documentation updates + * add emulation for UI camera device and flash mode properties + * workaround orientation bugs in parent controllers + - enable orientation and iPad support for all examples + * add orientation detection fall back + * fix captureReader not exposed to simulator + * fix camera preview stale rotation corner + - fix ZBarReaderViewController toolbar layout accomodation + - switch pre-release samples to use debug library + * update docs to note possible link order dependency + * add missing references to new samples + * add IB support to ZBarReaderViewController + - add tabbed reader sample + * add embedded reader sample + - factor out camera simulation logic + * fix readertest max quality result filtering + - thanks to John Boydon for finding this! + * improve support for resizing, rotating and embedding the reader + * add readertest fixed zoom stops + * add manual frame capture + - readertest save as PNG + * enhance readertest to support default/demo pre-config and distribution + * updates for Xcode 4 + * expose tracking box color + +version 1.1: + * fix doc typo (bug #3139087) + - add modifier bit docs + +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 index 0000000..bd2df0e --- /dev/null +++ b/iphone/README @@ -0,0 +1,66 @@ +ZBar iOS 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, Codabar, Interleaved 2 of 5 and QR Code. +These are the Objective C wrappers and integrated widget for developing +with the library on the iOS 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 +-------- + +You should be able to open and build the examples directly from the +disk image (ignore warnings about the read-only volume). If you have +problems with this, please copy the examples to your local drive +instead and build from there. + +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. + +Examples/TabReader shows how you can use Interface Builder to add the +reader as a tab in a UITabBarController. + +Examples/EmbedReader shows how a ZBarReaderView may be embedded +directly in the application view hierarchy. + +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-2011 © Jeff Brown <spadix@users.sourceforge.net> et al + +See the included files COPYING and LICENSE.md for details diff --git a/iphone/ZBarCVImage.h b/iphone/ZBarCVImage.h new file mode 100644 index 0000000..d4d9934 --- /dev/null +++ b/iphone/ZBarCVImage.h @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <CoreVideo/CoreVideo.h> +#import <ZBarSDK/ZBarImage.h> + +// 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 index 0000000..63065dd --- /dev/null +++ b/iphone/ZBarCVImage.m @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#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/ZBarCameraSimulator.m b/iphone/ZBarCameraSimulator.m new file mode 100644 index 0000000..96fd753 --- /dev/null +++ b/iphone/ZBarCameraSimulator.m @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------ +// Copyright 2010-2011 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarCameraSimulator.h> +#import <ZBarSDK/ZBarReaderView.h> + +// hack around missing simulator support for AVCapture interfaces + +@implementation ZBarCameraSimulator + +@synthesize readerView; + +- (id) initWithViewController: (UIViewController*) vc +{ + if(!TARGET_IPHONE_SIMULATOR) { + [self release]; + return(nil); + } + self = [super init]; + if(!self) + return(nil); + + viewController = vc; + + return(self); +} + +- (void) dealloc +{ + viewController = nil; + readerView = nil; + [picker release]; + picker = nil; + [pickerPopover release]; + pickerPopover = nil; + [super dealloc]; +} + +- (void) setReaderView: (ZBarReaderView*) view +{ + ZBarReaderView *oldView = readerView; + readerView = [view retain]; + [oldView release]; + + UILongPressGestureRecognizer *gesture = + [[UILongPressGestureRecognizer alloc] + initWithTarget: self + action: @selector(didLongPress:)]; + gesture.numberOfTouchesRequired = 2; + [view addGestureRecognizer: gesture]; + [gesture release]; +} + +- (void) didLongPress: (UILongPressGestureRecognizer*) gesture +{ + if(gesture.state == UIGestureRecognizerStateBegan) + [self takePicture]; +} + +- (void) takePicture +{ + if(!picker) { + picker = [UIImagePickerController new]; + picker.delegate = self; + } + if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + if(!pickerPopover) + pickerPopover = [[UIPopoverController alloc] + initWithContentViewController: picker]; + [pickerPopover presentPopoverFromRect: CGRectZero + inView: readerView + permittedArrowDirections: UIPopoverArrowDirectionAny + animated: YES]; + } + else + [viewController presentModalViewController: picker + animated: YES]; +} + +- (void) imagePickerController: (UIImagePickerController*) _picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; + + if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + [pickerPopover dismissPopoverAnimated: YES]; + else + [_picker dismissModalViewControllerAnimated: YES]; + + [readerView performSelector: @selector(scanImage:) + withObject: image + afterDelay: .1]; +} + +- (void) imagePickerControllerDidCancel: (UIImagePickerController*) _picker +{ + [_picker dismissModalViewControllerAnimated: YES]; +} + +@end diff --git a/iphone/ZBarCaptureReader.m b/iphone/ZBarCaptureReader.m new file mode 100644 index 0000000..b3dd65c --- /dev/null +++ b/iphone/ZBarCaptureReader.m @@ -0,0 +1,370 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <libkern/OSAtomic.h> +#import <AVFoundation/AVFoundation.h> +#import <CoreMedia/CoreMedia.h> +#import <CoreVideo/CoreVideo.h> +#import <ZBarSDK/ZBarCaptureReader.h> +#import <ZBarSDK/ZBarImageScanner.h> +#import "ZBarCVImage.h" + +#define MODULE ZBarCaptureReader +#import "debug.h" + +enum { + STOPPED = 0, + RUNNING = 1, + PAUSED = 2, + CAPTURE = 4, +}; + +@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<AVCaptureVideoDataOutputSampleBufferDelegate>)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(OSAtomicOr32Barrier(0, &state) & RUNNING); +} + +- (void) setEnableReader: (BOOL) enable +{ + if(!enable) + OSAtomicAnd32Barrier(STOPPED, &state); + else if(!(OSAtomicOr32OrigBarrier(RUNNING, &state) & RUNNING)) { + OSAtomicAnd32Barrier(~PAUSED, &state); + @synchronized(scanner) { + scanner.enableCache = enableCache; + } + } +} + +- (void) willStartRunning +{ + self.enableReader = YES; +} + +- (void) willStopRunning +{ + self.enableReader = NO; +} + +- (void) flushCache +{ + @synchronized(scanner) { + scanner.enableCache = enableCache; + } +} + +- (void) captureFrame +{ + OSAtomicOr32(CAPTURE, &state); +} + +- (void) setCaptureDelegate: (id<ZBarCaptureDelegate>) 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]; + OSAtomicAnd32Barrier(~PAUSED, &state); + 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 + uint32_t _state = OSAtomicOr32Barrier(0, &state); + if((_state & (PAUSED | RUNNING)) != 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 || (_state & CAPTURE)) + zlog(@"scan image: %dx%d crop=%@ ngood=%d nraw=%d st=%d", + w, h, NSStringFromCGRect(image.crop), ngood, nraw, _state); + + if(ngood || (_state & CAPTURE)) { + // copy image data so we can release the buffer + result.size = CGSizeMake(w, h); + result.pixelBuffer = buf; + result.symbols = syms; + t_scan = now; + OSAtomicXor32Barrier((_state & CAPTURE) | PAUSED, &state); + [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 index 0000000..2451843 --- /dev/null +++ b/iphone/ZBarHelpController.m @@ -0,0 +1,305 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarHelpController.h> + +#define MODULE ZBarHelpController +#import "debug.h" + +@implementation ZBarHelpController + +@synthesize delegate; + +- (id) initWithReason: (NSString*) _reason +{ + self = [super init]; + if(!self) + return(nil); + + if(!_reason) + _reason = @"INFO"; + reason = [_reason retain]; + 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) viewDidLoad +{ + [super viewDidLoad]; + + UIView *view = self.view; + CGRect bounds = self.view.bounds; + if(!bounds.size.width || !bounds.size.height) + view.frame = bounds = CGRectMake(0, 0, 320, 480); + view.backgroundColor = [UIColor colorWithWhite: .125f + alpha: 1]; + view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + + webView = [[WKWebView alloc] + initWithFrame: CGRectMake(0, 0, + bounds.size.width, + bounds.size.height - 44)]; + webView.navigationDelegate = self; + webView.backgroundColor = [UIColor colorWithWhite: .125f + alpha: 1]; + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleBottomMargin); + webView.hidden = YES; + [view addSubview: webView]; + + CGRect r = view.bounds; + r.origin.y = r.size.height - 44; + r.size.height = 44; + controls = [[UIView alloc] + initWithFrame: r]; + controls.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleTopMargin; + controls.backgroundColor = [UIColor blackColor]; + + r.origin.y = 0; + toolbar = [[UIToolbar alloc] + initWithFrame: r]; + toolbar.barStyle = UIBarStyleBlackOpaque; + toolbar.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + + 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]; + + [controls addSubview: toolbar]; + toolbar.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + [toolbar release]; + + [view addSubview: controls]; + + if (@available(iOS 11, *)) { + UILayoutGuide *safe = self.view.safeAreaLayoutGuide; + controls.translatesAutoresizingMaskIntoConstraints = NO; + webView.translatesAutoresizingMaskIntoConstraints = NO; + + [NSLayoutConstraint activateConstraints:@[ + [safe.trailingAnchor constraintEqualToAnchor:webView.trailingAnchor], + [webView.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor], + [webView.topAnchor constraintEqualToAnchor:safe.topAnchor], + [webView.bottomAnchor constraintEqualToAnchor:controls.topAnchor] + ]]; + + [NSLayoutConstraint activateConstraints:@[ + [safe.trailingAnchor constraintEqualToAnchor:controls.trailingAnchor], + [controls.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor], + [controls.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor] + ]]; + + [NSLayoutConstraint + constraintWithItem:controls + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:44.0].active = YES; + } + + 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) viewWillAppear: (BOOL) animated +{ + assert(webView); + if(webView.loading) + webView.hidden = YES; + [super viewWillAppear: animated]; +} + +- (void) viewWillDisappear: (BOOL) animated +{ + [webView stopLoading]; + [super viewWillDisappear: animated]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return([self isInterfaceOrientationSupported: orient]); +} + +- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + [webView reload]; +} + +- (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) orient +{ + zlog(@"frame=%@ webView.frame=%@ toolbar.frame=%@", + NSStringFromCGRect(self.view.frame), + NSStringFromCGRect(webView.frame), + NSStringFromCGRect(toolbar.frame)); +} + +- (BOOL) isInterfaceOrientationSupported: (UIInterfaceOrientation) orient +{ + UIViewController *parent = self.parentViewController; + if(parent && !orientations) + return([parent shouldAutorotateToInterfaceOrientation: 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]; + + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)webView:(WKWebView *)view + didFinishNavigation:(WKNavigation *)navigation +{ + if(view.hidden) { + [view evaluateJavaScript:[NSString stringWithFormat: @"onZBarHelp({reason:\"%@\"});", reason] + completionHandler:^(id abc, NSError *error){ + [UIView beginAnimations: @"ZBarHelp" + context: nil]; + view.hidden = NO; + [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: (WKWebView*) view + shouldStartLoadWithRequest: (NSURLRequest*) req + navigationType: (WKNavigationType) 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 index 0000000..f723e7a --- /dev/null +++ b/iphone/ZBarImage.m @@ -0,0 +1,306 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#import <ZBarSDK/ZBarImage.h> +#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 index 0000000..cdc4ea4 --- /dev/null +++ b/iphone/ZBarImageScanner.m @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarImageScanner.h> +#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 index 0000000..6b3d18e --- /dev/null +++ b/iphone/ZBarReaderController.m @@ -0,0 +1,747 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarReaderController.h> +#import <ZBarSDK/ZBarHelpController.h> +#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 <UINavigationControllerDelegate, + UIImagePickerControllerDelegate>) delegate +{ + self.readerDelegate = (id <ZBarReaderDelegate>)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<ZBarHelpDelegate>)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 <NSFastEnumeration>) 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 index 0000000..60fc18d --- /dev/null +++ b/iphone/ZBarReaderView.m @@ -0,0 +1,596 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarReaderView.h> + +#define MODULE ZBarReaderView +#import "debug.h" + +// silence warning +@interface ZBarReaderViewImpl : NSObject +@end + +@implementation ZBarReaderView + +@synthesize readerDelegate, tracksSymbols, trackingColor, torchMode, showsFPS, + zoom, maxZoom, scanCrop, previewTransform, captureReader; +@dynamic scanner, allowsPinchZoom, enableCache, device, session; + ++ (id) alloc +{ + if(self == [ZBarReaderView class]) { + // this is an abstract wrapper for implementation selected + // at compile time. replace with concrete subclass. + return((id)[ZBarReaderViewImpl alloc]); + } + return([super alloc]); +} + +- (void) initSubviews +{ + assert(preview); + + overlay = [CALayer new]; + overlay.backgroundColor = [UIColor clearColor].CGColor; + [preview addSublayer: overlay]; + +#ifndef NDEBUG + overlay.borderWidth = 2; + overlay.borderColor = [UIColor colorWithRed: 1 + green: 0 + blue: 0 + alpha: .5].CGColor; + cropLayer = [CALayer new]; + cropLayer.backgroundColor = [UIColor clearColor].CGColor; + cropLayer.borderWidth = 2; + cropLayer.borderColor = [UIColor colorWithRed: 0 + green: 0 + blue: 1 + alpha: .5].CGColor; + [overlay addSublayer: cropLayer]; +#endif + + tracking = [CALayer new]; + tracking.opacity = 0; + tracking.borderWidth = 1; + tracking.backgroundColor = [UIColor clearColor].CGColor; + [overlay addSublayer: tracking]; + + trackingColor = [[UIColor greenColor] + retain]; + tracking.borderColor = trackingColor.CGColor; + + fpsView = [UIView new]; + fpsView.backgroundColor = [UIColor colorWithWhite: 0 + alpha: .333]; + fpsView.layer.cornerRadius = 12; + fpsView.hidden = YES; + [self addSubview: fpsView]; + + fpsLabel = [[UILabel alloc] + initWithFrame: CGRectMake(0, 0, 80, 32)]; + 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; +} + +- (void) _initWithImageScanner: (ZBarImageScanner*) scanner +{ + assert(scanner); + + tracksSymbols = YES; + interfaceOrientation = UIInterfaceOrientationPortrait; + torchMode = 2; // AVCaptureTorchModeAuto + scanCrop = effectiveCrop = CGRectMake(0, 0, 1, 1); + imageScale = 1; + previewTransform = CGAffineTransformIdentity; + maxZoom = 2; + + pinch = [[UIPinchGestureRecognizer alloc] + initWithTarget: self + action: @selector(handlePinch)]; + [self addGestureRecognizer: pinch]; +} + +- (id) initWithImageScanner: (ZBarImageScanner*) scanner +{ + self = [super initWithFrame: CGRectMake(0, 0, 320, 426)]; + if(!self) + return(nil); + + self.backgroundColor = [UIColor blackColor]; + self.contentMode = UIViewContentModeScaleAspectFill; + self.clipsToBounds = YES; + self.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + + [self _initWithImageScanner: scanner]; + 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); +} + +- (id) initWithCoder: (NSCoder*) decoder +{ + self = [super initWithCoder: decoder]; + if(!self) + return(nil); + ZBarImageScanner *scanner = + [[ZBarImageScanner new] + autorelease]; + [self _initWithImageScanner: scanner]; + + [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; + [cropLayer release]; + cropLayer = nil; + [tracking release]; + tracking = nil; + [trackingColor release]; + trackingColor = 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 = effectiveCrop; + 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) updateCrop +{ +} + +static inline CGFloat rotationForInterfaceOrientation (int orient) +{ + // resolve camera/device image orientation to view/interface orientation + switch(orient) + { + case UIInterfaceOrientationLandscapeLeft: + return(M_PI_2); + case UIInterfaceOrientationPortraitUpsideDown: + return(M_PI); + case UIInterfaceOrientationLandscapeRight: + return(3 * M_PI_2); + case UIInterfaceOrientationPortrait: + return(2 * M_PI); + } + return(0); +} + +- (void) layoutSubviews +{ + CGRect bounds = self.bounds; + if(!bounds.size.width || !bounds.size.height) + return; + + [CATransaction begin]; + if(animationDuration) { + [CATransaction setAnimationDuration: animationDuration]; + [CATransaction setAnimationTimingFunction: + [CAMediaTimingFunction functionWithName: + kCAMediaTimingFunctionEaseInEaseOut]]; + } + else + [CATransaction setDisableActions: YES]; + + [super layoutSubviews]; + fpsView.frame = CGRectMake(bounds.size.width - 80, bounds.size.height - 32, + 80 + 12, 32 + 12); + + // orient view bounds to match camera image + CGSize psize; + if(UIInterfaceOrientationIsPortrait(interfaceOrientation)) + psize = CGSizeMake(bounds.size.height, bounds.size.width); + else + psize = bounds.size; + + // calculate scale from view coordinates to image coordinates + // FIXME assumes AVLayerVideoGravityResizeAspectFill + CGFloat scalex = imageSize.width / psize.width; + CGFloat scaley = imageSize.height / psize.height; + imageScale = (scalex < scaley) ? scalex : scaley; + if(!imageScale) + imageScale = 1; + // apply zoom + imageScale /= zoom; + + // scale crop by zoom factor + CGFloat z = 1 / zoom; + CGFloat t = (1 - z) / 2; + CGRect zoomCrop = + CGRectMake(scanCrop.origin.x * z + t, + scanCrop.origin.y * z + t, + scanCrop.size.width * z, + scanCrop.size.height * z); + + // convert effective preview area to normalized image coordinates + CGRect previewCrop; + if(scalex < scaley && imageSize.height) + previewCrop.size = + CGSizeMake(z, psize.height * imageScale / imageSize.height); + else if(imageSize.width) + previewCrop.size = + CGSizeMake(psize.width * imageScale / imageSize.width, z); + else + previewCrop.size = CGSizeMake(1, 1); + previewCrop.origin = CGPointMake((1 - previewCrop.size.width) / 2, + (1 - previewCrop.size.height) / 2); + + // clip crop to visible preview area + effectiveCrop = CGRectIntersection(zoomCrop, previewCrop); + if(CGRectIsNull(effectiveCrop)) + effectiveCrop = zoomCrop; + + // size preview to match image in view coordinates + CGFloat viewScale = 1 / imageScale; + if(imageSize.width && imageSize.height) + psize = CGSizeMake(imageSize.width * viewScale, + imageSize.height * viewScale); + + preview.bounds = CGRectMake(0, 0, psize.height, psize.width); + // center preview in view + preview.position = CGPointMake(bounds.size.width / 2, + bounds.size.height / 2); + + CGFloat angle = rotationForInterfaceOrientation(interfaceOrientation); + CATransform3D xform = + CATransform3DMakeAffineTransform(previewTransform); + preview.transform = CATransform3DRotate(xform, angle, 0, 0, 1); + + // scale overlay to match actual image + if(imageSize.width && imageSize.height) + overlay.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height); + else + overlay.bounds = CGRectMake(0, 0, psize.width, psize.height); + // center overlay in preview + overlay.position = CGPointMake(psize.height / 2, psize.width / 2); + + // image coordinates rotated from preview + xform = CATransform3DMakeRotation(M_PI_2, 0, 0, 1); + overlay.transform = CATransform3DScale(xform, viewScale, viewScale, 1); + tracking.borderWidth = imageScale; + +#ifndef NDEBUG + preview.backgroundColor = [UIColor yellowColor].CGColor; + overlay.borderWidth = 2 * imageScale; + cropLayer.borderWidth = 2 * imageScale; + cropLayer.frame = CGRectMake(effectiveCrop.origin.x * imageSize.width, + effectiveCrop.origin.y * imageSize.height, + effectiveCrop.size.width * imageSize.width, + effectiveCrop.size.height * imageSize.height); + zlog(@"layoutSubviews: bounds=%@ orient=%d image=%@ crop=%@ zoom=%g\n" + @"=> preview=%@ crop=(z%@ p%@ %@ i%@) scale=%g %c %g = 1/%g", + NSStringFromCGSize(bounds.size), interfaceOrientation, + NSStringFromCGSize(imageSize), NSStringFromCGRect(scanCrop), zoom, + NSStringFromCGSize(psize), NSStringFromCGRect(zoomCrop), + NSStringFromCGRect(previewCrop), NSStringFromCGRect(effectiveCrop), + NSStringFromCGRect(cropLayer.frame), + scalex, (scalex > scaley) ? '>' : '<', scaley, viewScale); +#endif + + [self resetTracking]; + [self updateCrop]; + + [CATransaction commit]; + animationDuration = 0; +} + +- (void) setImageSize: (CGSize) size +{ + zlog(@"imageSize=%@", NSStringFromCGSize(size)); + imageSize = size; + + // FIXME bug in AVCaptureVideoPreviewLayer fails to update preview location + preview.bounds = CGRectMake(0, 0, size.width, size.height); + + [self setNeedsLayout]; +} + +- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + if(interfaceOrientation != orient) { + zlog(@"orient=%d #%g", orient, duration); + interfaceOrientation = orient; + animationDuration = duration; + } +} + +- (void) setScanCrop: (CGRect) r +{ + if(CGRectEqualToRect(scanCrop, r)) + return; + scanCrop = r; + [self setNeedsLayout]; +} + +- (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) setTrackingColor: (UIColor*) color +{ + if(!color) + return; + [color retain]; + [trackingColor release]; + trackingColor = color; + tracking.borderColor = color.CGColor; +} + +- (void) setShowsFPS: (BOOL) show +{ + if(show == showsFPS) + return; + fpsView.hidden = !show; +} + +- (void) setZoom: (CGFloat) z +{ + if(z < 1.0) + z = 1.0; + if(z > maxZoom) + z = maxZoom; + if(z == zoom) + return; + zoom = z; + + [self setNeedsLayout]; +} + +- (void) setZoom: (CGFloat) z + animated: (BOOL) animated +{ + [CATransaction begin]; + if(animated) { + [CATransaction setAnimationDuration: .1]; + [CATransaction setAnimationTimingFunction: + [CAMediaTimingFunction functionWithName: + kCAMediaTimingFunctionLinear]]; + } + else + [CATransaction setDisableActions: YES]; + // FIXME animate from current value + self.zoom = z; + [self layoutIfNeeded]; + [CATransaction commit]; +} + +- (void) setPreviewTransform: (CGAffineTransform) xfrm +{ + previewTransform = xfrm; + [self setNeedsLayout]; +} + +- (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 setZoom: z + animated: YES]; + + 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 index 0000000..03faf70 --- /dev/null +++ b/iphone/ZBarReaderViewController.m @@ -0,0 +1,711 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarReaderViewController.h> +#import <ZBarSDK/ZBarReaderView.h> +#import <ZBarSDK/ZBarCaptureReader.h> +#import <ZBarSDK/ZBarHelpController.h> +#import <ZBarSDK/ZBarCameraSimulator.h> + +#define MODULE ZBarReaderViewController +#import "debug.h" + +static inline AVCaptureDevicePosition +AVPositionForUICamera (UIImagePickerControllerCameraDevice camera) +{ + switch(camera) { + case UIImagePickerControllerCameraDeviceRear: + return(AVCaptureDevicePositionBack); + case UIImagePickerControllerCameraDeviceFront: + return(AVCaptureDevicePositionFront); + } + return(-1); +} + +static inline UIImagePickerControllerCameraDevice +UICameraForAVPosition (AVCaptureDevicePosition position) +{ + switch(position) + { + case AVCaptureDevicePositionBack: + return(UIImagePickerControllerCameraDeviceRear); + case AVCaptureDevicePositionFront: + return(UIImagePickerControllerCameraDeviceFront); + } + return(-1); +} + +static inline AVCaptureDevice* +AVDeviceForUICamera (UIImagePickerControllerCameraDevice camera) +{ + AVCaptureDevicePosition position = AVPositionForUICamera(camera); + if(position < 0) + return(nil); + +#if !TARGET_IPHONE_SIMULATOR + NSArray *allDevices = + [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; + for(AVCaptureDevice *device in allDevices) + // FIXME how to quantify "best" of several (theoretical) possibilities + if(device.position == position) + return(device); +#endif + return(nil); +} + +static inline AVCaptureTorchMode +AVTorchModeForUIFlashMode (UIImagePickerControllerCameraFlashMode mode) +{ + switch(mode) + { + case UIImagePickerControllerCameraFlashModeAuto: + return(AVCaptureTorchModeAuto); + case UIImagePickerControllerCameraFlashModeOn: + return(AVCaptureTorchModeOn); + case UIImagePickerControllerCameraFlashModeOff: + break; + } + return(AVCaptureTorchModeOff); +} + +static inline NSString* +AVSessionPresetForUIVideoQuality (UIImagePickerControllerQualityType quality) +{ +#if !TARGET_IPHONE_SIMULATOR + switch(quality) + { + case UIImagePickerControllerQualityTypeHigh: + return(AVCaptureSessionPresetHigh); + case UIImagePickerControllerQualityType640x480: + return(AVCaptureSessionPreset640x480); + case UIImagePickerControllerQualityTypeMedium: + return(AVCaptureSessionPresetMedium); + case UIImagePickerControllerQualityTypeLow: + return(AVCaptureSessionPresetLow); + case UIImagePickerControllerQualityTypeIFrame1280x720: + return(AVCaptureSessionPresetiFrame1280x720); + case UIImagePickerControllerQualityTypeIFrame960x540: + return(AVCaptureSessionPresetiFrame960x540); + } +#endif + return(nil); +} + + +@implementation ZBarReaderViewController + +@synthesize scanner, readerDelegate, showsZBarControls, + supportedOrientationsMask, tracksSymbols, enableCache, cameraOverlayView, + cameraViewTransform, cameraDevice, cameraFlashMode, videoQuality, + 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]); +} + ++ (BOOL) isCameraDeviceAvailable: (UIImagePickerControllerCameraDevice) camera +{ + return(TARGET_IPHONE_SIMULATOR || + [UIImagePickerController isCameraDeviceAvailable: camera]); +} + ++ (BOOL) isFlashAvailableForCameraDevice: (UIImagePickerControllerCameraDevice) camera +{ + return(TARGET_IPHONE_SIMULATOR || + [UIImagePickerController isFlashAvailableForCameraDevice: camera]); +} + ++ (NSArray*) availableCaptureModesForCameraDevice: (UIImagePickerControllerCameraDevice) camera +{ + if(![self isCameraDeviceAvailable: camera]) + return([NSArray array]); + + // current reader only supports automatic detection + return([NSArray arrayWithObject: + [NSNumber numberWithInteger: + UIImagePickerControllerCameraCaptureModeVideo]]); +} + +- (void) _init +{ + supportedOrientationsMask = + ZBarOrientationMask(UIInterfaceOrientationPortrait); + showsZBarControls = tracksSymbols = enableCache = YES; + scanCrop = CGRectMake(0, 0, 1, 1); + cameraViewTransform = CGAffineTransformIdentity; + + cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto; + videoQuality = UIImagePickerControllerQualityType640x480; + AVCaptureDevice *device = nil; +#if !TARGET_IPHONE_SIMULATOR + device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; +#endif + if(device) + cameraDevice = UICameraForAVPosition(device.position); + else + cameraDevice = UIImagePickerControllerCameraDeviceRear; + + // 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]; +} + +- (id) init +{ + if(!TARGET_IPHONE_SIMULATOR && + !NSClassFromString(@"AVCaptureSession")) { + // fallback to old interface + zlog(@"Falling back to ZBarReaderController"); + [self release]; + return((id)[ZBarReaderController new]); + } + + self = [super init]; + if(!self) + return(nil); + + self.wantsFullScreenLayout = YES; + [self _init]; + return(self); +} + +- (id) initWithCoder: (NSCoder*) decoder +{ + self = [super initWithCoder: decoder]; + if(!self) + return(nil); + + [self _init]; + return(self); +} + +- (void) cleanup +{ + [cameraOverlayView removeFromSuperview]; + cameraSim.readerView = nil; + [cameraSim release]; + cameraSim = nil; + readerView.readerDelegate = nil; + [readerView release]; + readerView = nil; + [controls release]; + controls = nil; + [shutter release]; + shutter = 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 - 44; + r.size.height = 44; + controls = [[UIView alloc] + initWithFrame: r]; + controls.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleTopMargin; + controls.backgroundColor = [UIColor blackColor]; + + UIToolbar *toolbar = + [UIToolbar new]; + r.origin.y = 0; + toolbar.frame = r; + toolbar.barStyle = UIBarStyleBlackOpaque; + toolbar.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + + UIButton *info = + [UIButton buttonWithType: UIButtonTypeInfoLight]; + CGRect frame = info.frame; + frame.size.height = 44; + info.frame = frame; + [info addTarget: self + action: @selector(info) + forControlEvents: UIControlEventTouchUpInside]; + + toolbar.items = + [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemCancel + target: self + action: @selector(cancel)] + autorelease], + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil] + autorelease], + [[[UIBarButtonItem alloc] + initWithCustomView: info] + autorelease], + nil]; + [controls addSubview: toolbar]; + toolbar.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + [toolbar release]; + + [view addSubview: controls]; + + if (@available(iOS 11, *)) { + UILayoutGuide *safe = self.view.safeAreaLayoutGuide; + controls.translatesAutoresizingMaskIntoConstraints = NO; + //toolbar.translatesAutoresizingMaskIntoConstraints = NO; + [NSLayoutConstraint activateConstraints:@[ + [safe.trailingAnchor constraintEqualToAnchor:controls.trailingAnchor], + [controls.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor], + [safe.bottomAnchor constraintEqualToAnchor:controls.bottomAnchor] + ]]; + [NSLayoutConstraint + constraintWithItem:controls + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:44.0].active = YES; + } +} + +- (void) initVideoQuality +{ + if(!readerView) { + assert(0); + return; + } + + AVCaptureSession *session = readerView.session; + NSString *preset = AVSessionPresetForUIVideoQuality(videoQuality); + if(session && preset && [session canSetSessionPreset: preset]) { + zlog(@"set session preset=%@", preset); + session.sessionPreset = preset; + } + else + zlog(@"unable to set session preset=%@", preset); +} + +- (void) loadView +{ + self.view = [[UIView alloc] + initWithFrame: CGRectMake(0, 0, 320, 480)]; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + UIView *view = self.view; + view.backgroundColor = [UIColor blackColor]; + view.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + + readerView = [[ZBarReaderView alloc] + initWithImageScanner: scanner]; + CGRect bounds = view.bounds; + CGRect r = bounds; + NSUInteger autoresize = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + + if(showsZBarControls || + self.parentViewController.modalViewController == self) + { + autoresize |= UIViewAutoresizingFlexibleBottomMargin; + r.size.height -= 44; + } + readerView.frame = r; + readerView.autoresizingMask = autoresize; + AVCaptureDevice *device = AVDeviceForUICamera(cameraDevice); + if(device && device != readerView.device) + readerView.device = device; + readerView.torchMode = AVTorchModeForUIFlashMode(cameraFlashMode); + [self initVideoQuality]; + + readerView.readerDelegate = (id<ZBarReaderViewDelegate>)self; + readerView.scanCrop = scanCrop; + readerView.previewTransform = cameraViewTransform; + readerView.tracksSymbols = tracksSymbols; + readerView.enableCache = enableCache; + [view addSubview: readerView]; + + shutter = [[UIView alloc] + initWithFrame: r]; + shutter.backgroundColor = [UIColor blackColor]; + shutter.opaque = NO; + shutter.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + [view addSubview: shutter]; + + if(cameraOverlayView) { + assert(!cameraOverlayView.superview); + [cameraOverlayView removeFromSuperview]; + [view addSubview: cameraOverlayView]; + } + + [self initControls]; + + if(TARGET_IPHONE_SIMULATOR) { + cameraSim = [[ZBarCameraSimulator alloc] + initWithViewController: self]; + cameraSim.readerView = readerView; + } +} + +- (void) viewDidUnload +{ + [cameraOverlayView removeFromSuperview]; + [self cleanup]; + [super viewDidUnload]; +} + +- (void) viewWillAppear: (BOOL) animated +{ + zlog(@"willAppear: anim=%d orient=%d", + animated, self.interfaceOrientation); + [self initControls]; + [super viewWillAppear: animated]; + + [readerView willRotateToInterfaceOrientation: self.interfaceOrientation + duration: 0]; + [readerView performSelector: @selector(start) + withObject: nil + afterDelay: .001]; + shutter.alpha = 1; + shutter.hidden = NO; + + 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.captureReader.enableReader = NO; + + if(didHideStatusBar) { + [[UIApplication sharedApplication] + setStatusBarHidden: NO + withAnimation: UIStatusBarAnimationFade]; + didHideStatusBar = NO; + } + + [super viewWillDisappear: animated]; +} + +- (void) viewDidDisappear: (BOOL) animated +{ + // stopRunning can take a really long time (>1s observed), + // so defer until the view transitions are complete + [readerView stop]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return((supportedOrientationsMask >> orient) & 1); +} + +- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + zlog(@"willRotate: orient=%d #%g", orient, duration); + rotating = YES; + if(readerView) + [readerView willRotateToInterfaceOrientation: orient + duration: duration]; +} + +- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + zlog(@"willAnimateRotation: orient=%d #%g", orient, duration); + if(helpController) + [helpController willAnimateRotationToInterfaceOrientation: orient + duration: duration]; + if(readerView) + [readerView setNeedsLayout]; +} + +- (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) orient +{ + zlog(@"didRotate(%d): orient=%d", rotating, orient); + if(!rotating && readerView) { + // work around UITabBarController bug: willRotate is not called + // for non-portrait initial interface orientation + [readerView willRotateToInterfaceOrientation: self.interfaceOrientation + duration: 0]; + [readerView setNeedsLayout]; + } + rotating = NO; +} + +- (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 dismissViewControllerAnimated:YES completion:nil]; +} + +- (void) info +{ + [self showHelpWithReason: @"INFO"]; +} + +- (void) showHelpWithReason: (NSString*) reason +{ + if(helpController) + return; + helpController = [[ZBarHelpController alloc] + initWithReason: reason]; + helpController.delegate = (id<ZBarHelpDelegate>)self; + [self presentViewController:helpController animated:YES completion:nil]; +} + +- (void) takePicture +{ + if(TARGET_IPHONE_SIMULATOR) { + [cameraSim takePicture]; + // FIXME return selected image + } + else if(readerView) + [readerView.captureReader captureFrame]; +} + +- (void) setCameraDevice: (UIImagePickerControllerCameraDevice) camera +{ + cameraDevice = camera; + if(readerView) { + AVCaptureDevice *device = AVDeviceForUICamera(camera); + if(device) + readerView.device = device; + } +} + +- (void) setCameraFlashMode: (UIImagePickerControllerCameraFlashMode) mode +{ + cameraFlashMode = mode; + if(readerView) + readerView.torchMode = AVTorchModeForUIFlashMode(mode); +} + +- (UIImagePickerControllerCameraCaptureMode) cameraCaptureMode +{ + return(UIImagePickerControllerCameraCaptureModeVideo); +} + +- (void) setCameraCaptureMode: (UIImagePickerControllerCameraCaptureMode) mode +{ + NSAssert2(mode == UIImagePickerControllerCameraCaptureModeVideo, + @"attempt to set unsupported value (%d)" + @" for %@ property", mode, @"cameraCaptureMode"); +} + +- (void) setVideoQuality: (UIImagePickerControllerQualityType) quality +{ + videoQuality = quality; + if(readerView) + [self initVideoQuality]; +} + + +// ZBarHelpDelegate + +- (void) helpControllerDidFinish: (ZBarHelpController*) help +{ + + [UIView beginAnimations: @"ZBarHelp" + context: NULL]; + [UIView setAnimationDelegate: self]; + [UIView setAnimationDidStopSelector: @selector(removeHelp:done:context:)]; + [UIView commitAnimations]; +} + +- (void) removeHelp: (NSString*) tag + done: (NSNumber*) done + context: (void*) ctx +{ + if([tag isEqualToString: @"ZBarHelp"] && helpController) { + [helpController release]; + helpController = nil; + } +} + + +// ZBarReaderViewDelegate + +- (void) readerView: (ZBarReaderView*) readerView + didReadSymbols: (ZBarSymbolSet*) syms + fromImage: (UIImage*) image +{ + [readerDelegate + imagePickerController: (UIImagePickerController*)self + didFinishPickingMediaWithInfo: + [NSDictionary dictionaryWithObjectsAndKeys: + image, UIImagePickerControllerOriginalImage, + syms, ZBarReaderControllerResults, + nil]]; +} + +- (void) readerViewDidStart: (ZBarReaderView*) readerView +{ + if(!shutter.hidden) + [UIView animateWithDuration: .25 + animations: ^{ + shutter.alpha = 0; + } + completion: ^(BOOL finished) { + shutter.hidden = YES; + }]; +} + + +// "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 index 0000000..dd5a696 --- /dev/null +++ b/iphone/ZBarReaderViewImpl_Capture.m @@ -0,0 +1,402 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <AVFoundation/AVFoundation.h> +#import <CoreMedia/CoreMedia.h> +#import <CoreVideo/CoreVideo.h> +#import <ZBarSDK/ZBarReaderView.h> +#import <ZBarSDK/ZBarCaptureReader.h> + +#define MODULE ZBarReaderView +#import "debug.h" + +// protected APIs +@interface ZBarReaderView() +- (void) _initWithImageScanner: (ZBarImageScanner*) _scanner; +- (void) initSubviews; +- (void) updateCrop; +- (void) setImageSize: (CGSize) size; +- (void) didTrackSymbols: (ZBarSymbolSet*) syms; +@end + +@interface ZBarReaderViewImpl + : ZBarReaderView +{ + AVCaptureSession *session; + AVCaptureDevice *device; + AVCaptureInput *input; +} + +@end + +@implementation ZBarReaderViewImpl + +@synthesize device, session; + +- (void) _initWithImageScanner: (ZBarImageScanner*) scanner +{ + [super _initWithImageScanner: scanner]; + + 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<ZBarCaptureDelegate>)self; + [session addOutput: captureReader.captureOutput]; + + if([session canSetSessionPreset: AVCaptureSessionPreset640x480]) + session.sessionPreset = AVCaptureSessionPreset640x480; + + [captureReader addObserver: self + forKeyPath: @"size" + options: 0 + context: NULL]; + + [self initSubviews]; +} + +- (void) initSubviews +{ + AVCaptureVideoPreviewLayer *videoPreview = + [[AVCaptureVideoPreviewLayer + layerWithSession: session] + retain]; + preview = videoPreview; + CGRect bounds = self.bounds; + bounds.origin = CGPointZero; + preview.bounds = bounds; + preview.position = CGPointMake(bounds.size.width / 2, + bounds.size.height / 2); + videoPreview.videoGravity = AVLayerVideoGravityResizeAspectFill; + [self.layer addSublayer: preview]; + + [super initSubviews]; +} + +- (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) updateCrop +{ + [super updateCrop]; + captureReader.scanCrop = effectiveCrop; +} + +- (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: oldinput]; + 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]; +} + +- (void) configureDevice +{ + if([device isFocusModeSupported: AVCaptureFocusModeContinuousAutoFocus]) + device.focusMode = AVCaptureFocusModeContinuousAutoFocus; + if([device isTorchModeSupported: torchMode]) + device.torchMode = torchMode; +} + +- (void) lockDevice +{ + if(!running || locked) { + assert(0); + return; + } + + // lock device and set focus mode + NSError *error = nil; + if([device lockForConfiguration: &error]) { + locked = YES; + [self configureDevice]; + } + else { + zlog(@"failed to lock device: %@", error); + // just keep trying + [self performSelector: @selector(lockDevice) + withObject: nil + afterDelay: .5]; + } +} + + +// AVCaptureSession notifications + +- (void) onVideoStart: (NSNotification*) note +{ + zlog(@"onVideoStart: running=%d %@", running, note); + if(running) + return; + running = YES; + locked = NO; + + [self lockDevice]; + + if([readerDelegate respondsToSelector: @selector(readerViewDidStart:)]) + [readerDelegate readerViewDidStart: self]; +} + +- (void) onVideoStop: (NSNotification*) note +{ + zlog(@"onVideoStop: %@", note); + if(!running) + return; + running = NO; + + if(locked) + [device unlockForConfiguration]; + else + [NSObject cancelPreviousPerformRequestsWithTarget: self + selector: @selector(lockDevice) + object: nil]; + locked = NO; + + if([readerDelegate respondsToSelector: + @selector(readerView:didStopWithError:)]) + [readerDelegate readerView: self + didStopWithError: nil]; +} + +- (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]; + + if([readerDelegate respondsToSelector: + @selector(readerView:didStopWithError:)]) + [readerDelegate readerView: self + didStopWithError: err]; + else + 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 = [UIDevice currentDevice].orientation; + if(!UIDeviceOrientationIsValidInterfaceOrientation(orient)) { + orient = interfaceOrientation; + if(orient == UIInterfaceOrientationLandscapeLeft) + orient = UIDeviceOrientationLandscapeLeft; + else if(orient == UIInterfaceOrientationLandscapeRight) + orient = UIDeviceOrientationLandscapeRight; + } + switch(orient) + { + 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 index 0000000..328a59e --- /dev/null +++ b/iphone/ZBarReaderViewImpl_Simulator.m @@ -0,0 +1,219 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarReaderView.h> +#import <ZBarSDK/ZBarReaderViewController.h> + +#define MODULE ZBarReaderView +#import "debug.h" + +// hack around missing simulator support for AVCapture interfaces + +// protected APIs +@interface ZBarReaderView() +- (void) _initWithImageScanner: (ZBarImageScanner*) _scanner; +- (void) initSubviews; +- (void) setImageSize: (CGSize) size; +- (void) didTrackSymbols: (ZBarSymbolSet*) syms; +@end + +@interface ZBarReaderViewImpl + : ZBarReaderView +{ + ZBarImageScanner *scanner; + UILabel *simLabel; + UIImage *scanImage; + CALayer *previewImage; + BOOL enableCache; +} +@end + +@implementation ZBarReaderViewImpl + +@synthesize scanner, enableCache; + +- (void) _initWithImageScanner: (ZBarImageScanner*) _scanner +{ + [super _initWithImageScanner: _scanner]; + scanner = [_scanner retain]; + + [self initSubviews]; +} + +- (void) initSubviews +{ + simLabel = [UILabel new]; + simLabel.backgroundColor = [UIColor clearColor]; + simLabel.textColor = [UIColor whiteColor]; + simLabel.font = [UIFont boldSystemFontOfSize: 20]; + simLabel.numberOfLines = 4; + simLabel.textAlignment = UITextAlignmentCenter; + simLabel.text = @"Camera Simulation\n\n" + @"Tap and hold with two \"fingers\" to select image"; + simLabel.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + [self addSubview: simLabel]; + + preview = [CALayer new]; + [self.layer addSublayer: preview]; + + previewImage = [CALayer new]; + [preview addSublayer: previewImage]; + + [super initSubviews]; +} + +- (void) dealloc +{ + [scanner release]; + scanner = nil; + [simLabel release]; + simLabel = nil; + [previewImage release]; + previewImage = nil; + [super dealloc]; +} + +- (AVCaptureDevice*) device +{ + return(nil); +} + +- (void) setDevice: (AVCaptureDevice*) device +{ + // simulated camera does nothing with this +} + +- (AVCaptureSession*) session +{ + return(nil); +} + +- (void) updateCrop +{ + previewImage.frame = preview.bounds; + CGRect bounds = self.bounds; + simLabel.frame = CGRectInset(bounds, + bounds.size.width * .05, + bounds.size.height * .05); +} + +- (void) start +{ + if(started) + return; + [super start]; + running = YES; + + [self performSelector: @selector(onVideoStart) + withObject: nil + afterDelay: 0.5]; +} + +- (void) stop +{ + if(!started) + return; + [super stop]; + running = NO; + + [self performSelector: @selector(onVideoStop) + withObject: nil + afterDelay: 0.5]; +} + +- (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]; + [self layoutIfNeeded]; + + [CATransaction begin]; + [CATransaction setDisableActions: YES]; + previewImage.contentsGravity = kCAGravityResizeAspectFill; + 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(effectiveCrop.origin.x * size.width, + effectiveCrop.origin.y * size.height, + effectiveCrop.size.width * size.width, + effectiveCrop.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; +} + +- (void) onVideoStart +{ + if(running && + [readerDelegate respondsToSelector: @selector(readerViewDidStart:)]) + [readerDelegate readerViewDidStart: self]; +} + +- (void) onVideoStop +{ + if(!running && + [readerDelegate respondsToSelector: + @selector(readerView:didStopWithError:)]) + [readerDelegate readerView: self + didStopWithError: nil]; +} + +@end diff --git a/iphone/ZBarSymbol.m b/iphone/ZBarSymbol.m new file mode 100644 index 0000000..3131205 --- /dev/null +++ b/iphone/ZBarSymbol.m @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <ZBarSDK/ZBarSymbol.h> + +@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 index 0000000..658c633 --- /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 BUILD_DIR=$BUILD_DIR BUILD_ROOT=$BUILD_ROOT\ + || exit 1 +xcodebuild -target $SUBTARGET -configuration $CONFIGURATION -sdk iphonesimulator BUILD_DIR=$BUILD_DIR BUILD_ROOT=$BUILD_ROOT\ + || 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_ROOT/$CONFIGURATION-iphoneos/$SUBTARGET.a \ + $BUILD_ROOT/$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 index 0000000..0290471 --- /dev/null +++ b/iphone/bin/CreateDMG.sh @@ -0,0 +1,44 @@ +#!/bin/sh +set -ux +VOLNAME=${1:?} +shift +RES=$SOURCE_ROOT/res +BUDDY=/usr/libexec/PlistBuddy +if [ ! -x "$BUDDY" ]; then + BUDDY=$(xcrun -find PlistBuddy) \ + || exit 1 +fi +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 -not -name '.*') +do + rm -rf $example/{build,*.xcodeproj/{*.{mode1v3,pbxuser},project.xcworkspace,xcuserdata},ZBarSDK} + cp -af $BUILT_PRODUCTS_DIR/ZBarSDK $example/ +done + +# override subdir .DS_Stores +for dir in $(find $TARGET_BUILD_DIR -type d -depth 1) +do + cp -af $RES/Columns.DS_Store $dir/.DS_Store +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 index 0000000..e6bc7df --- /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.md", 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 100755 index 0000000..06f68f6 --- /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 index 0000000..8a35b62 --- /dev/null +++ b/iphone/debug.h @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include <mach/mach_time.h> +#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 index 0000000..c6a532a --- /dev/null +++ b/iphone/doc/Documentation.html @@ -0,0 +1,11 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> +<meta http-equiv="Refresh" content="0; url=Documentation/index.html"/> +</head> +<body> +<p>redirecting to <a href="Documentation/index.html">Documentation/index.html</a></p> +</body> +</html> diff --git a/iphone/doc/ZBarImage.rst b/iphone/doc/ZBarImage.rst new file mode 100644 index 0000000..59f537e --- /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:<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:<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:<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:<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:>` + + .. _`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:>` + + .. _`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 index 0000000..5835a17 --- /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 index 0000000..cf7ee09 --- /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<ZBarReaderDelegate> 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 <NSFastEnumeration>) 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 index 0000000..34b52c7 --- /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 index 0000000..d434215 --- /dev/null +++ b/iphone/doc/ZBarReaderView.rst @@ -0,0 +1,126 @@ +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<ZBarReaderViewDelegate> 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:: UIColor *trackingColor + + The color of the tracking annotation (default green). + + .. 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,maxZoom]. (default 1.25) + + .. member:: CGFloat maxZoom + + Maximum settable zoom level. The zoom property will be clipped to this + value. + + .. 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. + + .. _`setZoom:animated:`: + .. describe:: - (void) setZoom:(CGFloat)zoom animated:(BOOL)animated + + Set the zoom property with optional animation. + + .. _`willRotateTointerfaceOrientation:duration:`: + .. describe:: - (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration + + Compensate for device / camera / interface orientation. Must be called + by containing view controller that supports any non-portrait orientation + to restore the camera preview to the correct orientation. Call from + view controller method of the same name for correct animation. diff --git a/iphone/doc/ZBarReaderViewController.rst b/iphone/doc/ZBarReaderViewController.rst new file mode 100644 index 0000000..4366079 --- /dev/null +++ b/iphone/doc/ZBarReaderViewController.rst @@ -0,0 +1,190 @@ +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 <ZBarReaderDelegate> 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:: NSUInteger supportedOrientationsMask + + Set of interface orientations that the controller should support. Use + :func:`ZBarOrientationMask` or ``ZBarOrientationMaskAll`` to + generate the mask. + + .. 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:: UIImagePickerControllerCameraDevice cameraDevice + + The camera device to use for scanning. Defaults to the system default + camera. + + .. member:: UIImagePickerControllerCameraFlashMode cameraFlashMode + + The "flash" (aka torch) mode to use while scanning. Defaults to + UIImagePickerControllerCameraFlashModeAuto. + + .. member:: UIImagePickerControllerQualityType videoQuality + + The resolution to use while scanning. Defaults to + UIImagePickerControllerQuality640x480. + + .. 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:: UIImagePickerControllerCameraCaptureMode cameraCaptureMode + + Raises an exception if anything other than + ``UIImagePickerControllerCameraCaptureModeVideo`` is set. + + .. 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``. + + .. describe:: + (BOOL) isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice)cameraDevice + + See the :class:`UImagePickerController` method of the same name. + + .. describe:: + (BOOL) isFlashAvailableForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice + + See the :class:`UImagePickerController` method of the same name. + + .. describe:: + (NSArray*) availableCaptureModesForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice + + Returns an array with the single element + ``UIImagePickerControllerCameraCaptureModeVideo`` if the device is + available, otherwise returns an empty array. + + +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. + + .. _`takePicture`: + .. describe:: - (void) takePicture + + Capture the next available frame and send it over the usual delegate + path. + + +Macros +------ + + .. function:: ZBarOrientationMask(interfaceOrientation) + + Generate a bit-mask for the specified interface orientation, suitable + for setting :member:`supportedOrientationsMask`. + + .. describe:: ZBarOrientationMaskAll + + Combination of :func:`ZBarOrientationMask` for all interface + orientations (Portrait, PortraitUpsideDown, LandscapeLeft and + LandscapeRight) diff --git a/iphone/doc/ZBarReaderViewDelegate.rst b/iphone/doc/ZBarReaderViewDelegate.rst new file mode 100644 index 0000000..e6730cb --- /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 index 0000000..058a7b1 --- /dev/null +++ b/iphone/doc/ZBarSymbol.rst @@ -0,0 +1,186 @@ +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. See + :type:`zbar_modifier_t` for the currently defined modifier bits. + + .. 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_CODABAR + Codabar + + 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 + +.. type:: zbar_modifier_t + + Decoder symbology modifier flags. + + .. note:: + + These are bit indices, use eg, (1 << ZBAR_MOD_GS1) to test the + modifierMask property. + + ZBAR_MOD_GS1 + 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_AIM + Barcode tagged as AIM reserved. diff --git a/iphone/doc/ZBarSymbolSet.rst b/iphone/doc/ZBarSymbolSet.rst new file mode 100644 index 0000000..7983869 --- /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 index 0000000..92920c0 --- /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 index 0000000..9f283dd --- /dev/null +++ b/iphone/doc/camera.rst @@ -0,0 +1,130 @@ +Scanning From the Camera Feed +============================= + +Many iOS 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 more advanced approach + allows you to embed the view directly in your view hierarchy. +* 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. 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 alloc] init]; + +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:: + + // disable QR Code + [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:: + + [self presentModalViewController: reader + animated: YES]; + + Alternatively, it may be added to a container controller. + +5. Process the results. + + The controller will call the + ``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<NSFastEnumeration> 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, even from +Interface Builder. You lose only some of the simulator and rotation hooks. +The documentation is also less complete, so you need to be able to UTSL. See +the :file:`EmbedReader` sample for a working example. + + +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 index 0000000..5fb808e --- /dev/null +++ b/iphone/doc/compat.rst @@ -0,0 +1,190 @@ +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. + + +.. warning:: + + Versions before iOS 4 are no longer supported by the library. We are no + longer able to test anything in this section, so you're on your own if you + try to make use of it. + + +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: + UIImagePickerControllerSourceTypeCamera]) { + // 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 = UIImagePickerControllerSourceTypeCamera; + 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 index 0000000..950defb --- /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-2012, 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 index 0000000..17f337f --- /dev/null +++ b/iphone/doc/custom.rst @@ -0,0 +1,70 @@ +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` 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` 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`. + +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 index 0000000..d794e2f --- /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 index 0000000..7689fd3 --- /dev/null +++ b/iphone/doc/faq.rst @@ -0,0 +1,101 @@ +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 iOS devices that have an auto-focus camera. + Currently, the iPhone 3GS, iPhone 4 and newer devices. The iPad 2 and iPad + 3 will also work in many cases, *iff* the barcode is printed large enough + to achieve good focus. + +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, 5 and 6 are 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 and later for reading + barcodes. + + Note that iOS 3.1 is no longer supported; if you really think you need + that, you should still be able to get it working... See :doc:`compat` for + details about iOS version fallbacks. + + In all cases you should use the latest SDK to build. + +Are any private APIs in use? + No - the binary release of the SDK does not use any private APIs. + +Does this support "automatic" barcode capture? + Yes - with recent iOS versions, the default configuration will capture + barcodes automatically from the video stream. + + +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#UPC-E diff --git a/iphone/doc/getstarted.rst b/iphone/doc/getstarted.rst new file mode 100644 index 0000000..29cf9e4 --- /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 index 0000000..6ded7c8 --- /dev/null +++ b/iphone/doc/index.rst @@ -0,0 +1,20 @@ +################ + ZBar iOS SDK +################ + +Welcome to the ZBar SDK for iOS! + +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 index 0000000..b86ac45 --- /dev/null +++ b/iphone/doc/install.rst @@ -0,0 +1,141 @@ +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 >= 4.5.1 +* iPhone SDK >= 4.0 +* An iPhone 3GS, iPhone 4 or newer iOS device with an auto-focus camera +* iOS >= 4.0 running on the device + +.. warning:: + + *Only* the iPhone 3GS, iPhone 4 and newer models are supported, as they + have a camera with auto-focus. The iPad 2 and iPad 3 will also work, *iff* + the barcode is printed large enough to achieve good 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. + +3. 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: + + * :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 followed by :file:`libzbar.a`. + + .. note:: + + Link order may be important for some versions of Xcode; the referenced + libraries should be listed *before* :file:`libzbar.a` in the link order. + +4. 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 your 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 index 0000000..075ccd4 --- /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 likelihood 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.md, 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 index 0000000..a823fd6 --- /dev/null +++ b/iphone/doc/optimizing.rst @@ -0,0 +1,435 @@ +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). Your 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 newer devices, you can increase this using a capture +:member:`~ZBarReaderView::session` preset. Some older devices do 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 again, there is currently no +way to reduce this delay. + +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 some devices, 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 needed... +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 <resolution>`_. 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 configuration 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. 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 a newer device, you may also try increasing the resolution to support +even longer symbols. 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. 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 index 0000000..fe1ba58 --- /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 or document 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 alloc] init]; + +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: + UIImagePickerControllerSourceTypeCamera]) + reader.sourceType = UIImagePickerControllerSourceTypeCamera; + [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 + ``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<NSFastEnumeration> 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 index 0000000..721b5fb --- /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 index 0000000..7cf8b55 --- /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 index 0000000..3e10ade --- /dev/null +++ b/iphone/doc/tutorial.rst @@ -0,0 +1,228 @@ +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 4.5.1 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]; + } + + - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation + { + return(YES); + } + + 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. Open the target build settings and find ``Link Binary With Libraries``. + Click the ``+`` and add each of these (NB hold down command for multiple + selection): + + * AVFoundation.framework + * CoreMedia.framework + * CoreVideo.framework + * QuartzCore.framework + * libiconv.dylib + + .. warning:: + + Link order may be important for some versions of Xcode; the libraries + referenced above should be listed *before* :file:`libzbar.a` in the + link order. + +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 alloc] init]; + reader.readerDelegate = self; + reader.supportedOrientationsMask = ZBarOrientationMaskAll; + + 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<NSFastEnumeration> 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. Build and Run the project. + +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/EmbedReader/EmbedReader.xcodeproj/project.pbxproj b/iphone/examples/EmbedReader/EmbedReader.xcodeproj/project.pbxproj new file mode 100644 index 0000000..52183f7 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader.xcodeproj/project.pbxproj @@ -0,0 +1,416 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + DC824612162B5E140010B2E6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC824611162B5E140010B2E6 /* Default-568h@2x.png */; }; + DCFB4494136F7202004B3EE0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB4493136F7202004B3EE0 /* UIKit.framework */; }; + DCFB4496136F7202004B3EE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB4495136F7202004B3EE0 /* Foundation.framework */; }; + DCFB4498136F7202004B3EE0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB4497136F7202004B3EE0 /* CoreGraphics.framework */; }; + DCFB449E136F7203004B3EE0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DCFB449C136F7203004B3EE0 /* InfoPlist.strings */; }; + DCFB44A1136F7203004B3EE0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DCFB44A0136F7203004B3EE0 /* main.m */; }; + DCFB44A4136F7203004B3EE0 /* EmbedReaderAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DCFB44A3136F7203004B3EE0 /* EmbedReaderAppDelegate.m */; }; + DCFB44A7136F7203004B3EE0 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44A5136F7203004B3EE0 /* MainWindow.xib */; }; + DCFB44AA136F7203004B3EE0 /* EmbedReaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DCFB44A9136F7203004B3EE0 /* EmbedReaderViewController.m */; }; + DCFB44AD136F7203004B3EE0 /* EmbedReaderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44AB136F7203004B3EE0 /* EmbedReaderViewController.xib */; }; + DCFB44D0136F722A004B3EE0 /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44CA136F722A004B3EE0 /* libzbar.a */; }; + DCFB44D1136F722A004B3EE0 /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44CC136F722A004B3EE0 /* zbar-back.png */; }; + DCFB44D2136F722A004B3EE0 /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44CD136F722A004B3EE0 /* zbar-help.html */; }; + DCFB44D3136F722A004B3EE0 /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44CE136F722A004B3EE0 /* zbar-helpicons.png */; }; + DCFB44D4136F722A004B3EE0 /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DCFB44CF136F722A004B3EE0 /* zbar-samples.png */; }; + DCFB44DA136F72D3004B3EE0 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44D5136F72D3004B3EE0 /* AVFoundation.framework */; }; + DCFB44DB136F72D3004B3EE0 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44D6136F72D3004B3EE0 /* CoreMedia.framework */; }; + DCFB44DC136F72D3004B3EE0 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44D7136F72D3004B3EE0 /* CoreVideo.framework */; }; + DCFB44DD136F72D3004B3EE0 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44D8136F72D3004B3EE0 /* libiconv.dylib */; }; + DCFB44DE136F72D3004B3EE0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCFB44D9136F72D3004B3EE0 /* QuartzCore.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + DC824611162B5E140010B2E6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; + DCFB448F136F7202004B3EE0 /* EmbedReader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EmbedReader.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DCFB4493136F7202004B3EE0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + DCFB4495136F7202004B3EE0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + DCFB4497136F7202004B3EE0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DCFB449B136F7202004B3EE0 /* EmbedReader-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EmbedReader-Info.plist"; sourceTree = "<group>"; }; + DCFB449D136F7203004B3EE0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + DCFB449F136F7203004B3EE0 /* EmbedReader-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EmbedReader-Prefix.pch"; sourceTree = "<group>"; }; + DCFB44A0136F7203004B3EE0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + DCFB44A2136F7203004B3EE0 /* EmbedReaderAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmbedReaderAppDelegate.h; sourceTree = "<group>"; }; + DCFB44A3136F7203004B3EE0 /* EmbedReaderAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EmbedReaderAppDelegate.m; sourceTree = "<group>"; }; + DCFB44A6136F7203004B3EE0 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = "<group>"; }; + DCFB44A8136F7203004B3EE0 /* EmbedReaderViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmbedReaderViewController.h; sourceTree = "<group>"; }; + DCFB44A9136F7203004B3EE0 /* EmbedReaderViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EmbedReaderViewController.m; sourceTree = "<group>"; }; + DCFB44AC136F7203004B3EE0 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/EmbedReaderViewController.xib; sourceTree = "<group>"; }; + DCFB44B7136F722A004B3EE0 /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = "<group>"; }; + DCFB44B8136F722A004B3EE0 /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; }; + DCFB44B9136F722A004B3EE0 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; }; + DCFB44BA136F722A004B3EE0 /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = "<group>"; }; + DCFB44BB136F722A004B3EE0 /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = "<group>"; }; + DCFB44BC136F722A004B3EE0 /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = "<group>"; }; + DCFB44BD136F722A004B3EE0 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; }; + DCFB44BE136F722A004B3EE0 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; }; + DCFB44BF136F722A004B3EE0 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = "<group>"; }; + DCFB44C0136F722A004B3EE0 /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = "<group>"; }; + DCFB44C1136F722A004B3EE0 /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = "<group>"; }; + DCFB44C2136F722A004B3EE0 /* ZBarHelpController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarHelpController.h; sourceTree = "<group>"; }; + DCFB44C3136F722A004B3EE0 /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = "<group>"; }; + DCFB44C4136F722A004B3EE0 /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = "<group>"; }; + DCFB44C5136F722A004B3EE0 /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = "<group>"; }; + DCFB44C6136F722A004B3EE0 /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = "<group>"; }; + DCFB44C7136F722A004B3EE0 /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = "<group>"; }; + DCFB44C8136F722A004B3EE0 /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = "<group>"; }; + DCFB44C9136F722A004B3EE0 /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = "<group>"; }; + DCFB44CA136F722A004B3EE0 /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = "<group>"; }; + DCFB44CC136F722A004B3EE0 /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = "<group>"; }; + DCFB44CD136F722A004B3EE0 /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = "<group>"; }; + DCFB44CE136F722A004B3EE0 /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = "<group>"; }; + DCFB44CF136F722A004B3EE0 /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = "<group>"; }; + DCFB44D5136F72D3004B3EE0 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DCFB44D6136F72D3004B3EE0 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DCFB44D7136F72D3004B3EE0 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DCFB44D8136F72D3004B3EE0 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DCFB44D9136F72D3004B3EE0 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DCFB448C136F7202004B3EE0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCFB4494136F7202004B3EE0 /* UIKit.framework in Frameworks */, + DCFB4496136F7202004B3EE0 /* Foundation.framework in Frameworks */, + DCFB4498136F7202004B3EE0 /* CoreGraphics.framework in Frameworks */, + DCFB44DE136F72D3004B3EE0 /* QuartzCore.framework in Frameworks */, + DCFB44DB136F72D3004B3EE0 /* CoreMedia.framework in Frameworks */, + DCFB44DC136F72D3004B3EE0 /* CoreVideo.framework in Frameworks */, + DCFB44DA136F72D3004B3EE0 /* AVFoundation.framework in Frameworks */, + DCFB44DD136F72D3004B3EE0 /* libiconv.dylib in Frameworks */, + DCFB44D0136F722A004B3EE0 /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DCFB4484136F7202004B3EE0 = { + isa = PBXGroup; + children = ( + DCFB4499136F7202004B3EE0 /* EmbedReader */, + DCFB44B3136F722A004B3EE0 /* ZBarSDK */, + DCFB4492136F7202004B3EE0 /* Frameworks */, + DCFB4490136F7202004B3EE0 /* Products */, + ); + sourceTree = "<group>"; + }; + DCFB4490136F7202004B3EE0 /* Products */ = { + isa = PBXGroup; + children = ( + DCFB448F136F7202004B3EE0 /* EmbedReader.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + DCFB4492136F7202004B3EE0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + DCFB4493136F7202004B3EE0 /* UIKit.framework */, + DCFB4495136F7202004B3EE0 /* Foundation.framework */, + DCFB4497136F7202004B3EE0 /* CoreGraphics.framework */, + DCFB44D9136F72D3004B3EE0 /* QuartzCore.framework */, + DCFB44D6136F72D3004B3EE0 /* CoreMedia.framework */, + DCFB44D7136F72D3004B3EE0 /* CoreVideo.framework */, + DCFB44D5136F72D3004B3EE0 /* AVFoundation.framework */, + DCFB44D8136F72D3004B3EE0 /* libiconv.dylib */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + DCFB4499136F7202004B3EE0 /* EmbedReader */ = { + isa = PBXGroup; + children = ( + DCFB44A2136F7203004B3EE0 /* EmbedReaderAppDelegate.h */, + DCFB44A3136F7203004B3EE0 /* EmbedReaderAppDelegate.m */, + DCFB44A5136F7203004B3EE0 /* MainWindow.xib */, + DCFB44A8136F7203004B3EE0 /* EmbedReaderViewController.h */, + DCFB44A9136F7203004B3EE0 /* EmbedReaderViewController.m */, + DCFB44AB136F7203004B3EE0 /* EmbedReaderViewController.xib */, + DCFB449A136F7202004B3EE0 /* Supporting Files */, + ); + path = EmbedReader; + sourceTree = "<group>"; + }; + DCFB449A136F7202004B3EE0 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + DCFB449B136F7202004B3EE0 /* EmbedReader-Info.plist */, + DCFB449C136F7203004B3EE0 /* InfoPlist.strings */, + DC824611162B5E140010B2E6 /* Default-568h@2x.png */, + DCFB449F136F7203004B3EE0 /* EmbedReader-Prefix.pch */, + DCFB44A0136F7203004B3EE0 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + DCFB44B3136F722A004B3EE0 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DCFB44B4136F722A004B3EE0 /* Headers */, + DCFB44CA136F722A004B3EE0 /* libzbar.a */, + DCFB44CB136F722A004B3EE0 /* Resources */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DCFB44B4136F722A004B3EE0 /* Headers */ = { + isa = PBXGroup; + children = ( + DCFB44B5136F722A004B3EE0 /* ZBarSDK */, + ); + path = Headers; + sourceTree = "<group>"; + }; + DCFB44B5136F722A004B3EE0 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DCFB44B6136F722A004B3EE0 /* zbar */, + DCFB44C0136F722A004B3EE0 /* zbar.h */, + DCFB44C1136F722A004B3EE0 /* ZBarCaptureReader.h */, + DCFB44C2136F722A004B3EE0 /* ZBarHelpController.h */, + DCFB44C3136F722A004B3EE0 /* ZBarImage.h */, + DCFB44C4136F722A004B3EE0 /* ZBarImageScanner.h */, + DCFB44C5136F722A004B3EE0 /* ZBarReaderController.h */, + DCFB44C6136F722A004B3EE0 /* ZBarReaderView.h */, + DCFB44C7136F722A004B3EE0 /* ZBarReaderViewController.h */, + DCFB44C8136F722A004B3EE0 /* ZBarSDK.h */, + DCFB44C9136F722A004B3EE0 /* ZBarSymbol.h */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DCFB44B6136F722A004B3EE0 /* zbar */ = { + isa = PBXGroup; + children = ( + DCFB44B7136F722A004B3EE0 /* Decoder.h */, + DCFB44B8136F722A004B3EE0 /* Exception.h */, + DCFB44B9136F722A004B3EE0 /* Image.h */, + DCFB44BA136F722A004B3EE0 /* ImageScanner.h */, + DCFB44BB136F722A004B3EE0 /* Processor.h */, + DCFB44BC136F722A004B3EE0 /* Scanner.h */, + DCFB44BD136F722A004B3EE0 /* Symbol.h */, + DCFB44BE136F722A004B3EE0 /* Video.h */, + DCFB44BF136F722A004B3EE0 /* Window.h */, + ); + path = zbar; + sourceTree = "<group>"; + }; + DCFB44CB136F722A004B3EE0 /* Resources */ = { + isa = PBXGroup; + children = ( + DCFB44CC136F722A004B3EE0 /* zbar-back.png */, + DCFB44CD136F722A004B3EE0 /* zbar-help.html */, + DCFB44CE136F722A004B3EE0 /* zbar-helpicons.png */, + DCFB44CF136F722A004B3EE0 /* zbar-samples.png */, + ); + path = Resources; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DCFB448E136F7202004B3EE0 /* EmbedReader */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCFB44B0136F7203004B3EE0 /* Build configuration list for PBXNativeTarget "EmbedReader" */; + buildPhases = ( + DCFB448B136F7202004B3EE0 /* Sources */, + DCFB448C136F7202004B3EE0 /* Frameworks */, + DCFB448D136F7202004B3EE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = EmbedReader; + productName = EmbedReader; + productReference = DCFB448F136F7202004B3EE0 /* EmbedReader.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DCFB4486136F7202004B3EE0 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = DCFB4489136F7202004B3EE0 /* Build configuration list for PBXProject "EmbedReader" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = DCFB4484136F7202004B3EE0; + productRefGroup = DCFB4490136F7202004B3EE0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DCFB448E136F7202004B3EE0 /* EmbedReader */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DCFB448D136F7202004B3EE0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCFB449E136F7203004B3EE0 /* InfoPlist.strings in Resources */, + DCFB44A7136F7203004B3EE0 /* MainWindow.xib in Resources */, + DCFB44AD136F7203004B3EE0 /* EmbedReaderViewController.xib in Resources */, + DCFB44D1136F722A004B3EE0 /* zbar-back.png in Resources */, + DCFB44D2136F722A004B3EE0 /* zbar-help.html in Resources */, + DCFB44D3136F722A004B3EE0 /* zbar-helpicons.png in Resources */, + DCFB44D4136F722A004B3EE0 /* zbar-samples.png in Resources */, + DC824612162B5E140010B2E6 /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DCFB448B136F7202004B3EE0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCFB44A1136F7203004B3EE0 /* main.m in Sources */, + DCFB44A4136F7203004B3EE0 /* EmbedReaderAppDelegate.m in Sources */, + DCFB44AA136F7203004B3EE0 /* EmbedReaderViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + DCFB449C136F7203004B3EE0 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + DCFB449D136F7203004B3EE0 /* en */, + ); + name = InfoPlist.strings; + sourceTree = "<group>"; + }; + DCFB44A5136F7203004B3EE0 /* MainWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + DCFB44A6136F7203004B3EE0 /* en */, + ); + name = MainWindow.xib; + sourceTree = "<group>"; + }; + DCFB44AB136F7203004B3EE0 /* EmbedReaderViewController.xib */ = { + isa = PBXVariantGroup; + children = ( + DCFB44AC136F7203004B3EE0 /* en */, + ); + name = EmbedReaderViewController.xib; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DCFB44AE136F7203004B3EE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + SDKROOT = iphoneos; + }; + name = Debug; + }; + DCFB44AF136F7203004B3EE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + }; + name = Release; + }; + DCFB44B1136F7203004B3EE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "EmbedReader/EmbedReader-Prefix.pch"; + INFOPLIST_FILE = "EmbedReader/EmbedReader-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + DCFB44B2136F7203004B3EE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "EmbedReader/EmbedReader-Prefix.pch"; + INFOPLIST_FILE = "EmbedReader/EmbedReader-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DCFB4489136F7202004B3EE0 /* Build configuration list for PBXProject "EmbedReader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCFB44AE136F7203004B3EE0 /* Debug */, + DCFB44AF136F7203004B3EE0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCFB44B0136F7203004B3EE0 /* Build configuration list for PBXNativeTarget "EmbedReader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCFB44B1136F7203004B3EE0 /* Debug */, + DCFB44B2136F7203004B3EE0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DCFB4486136F7202004B3EE0 /* Project object */; +} diff --git a/iphone/examples/EmbedReader/EmbedReader.xcodeproj/xcshareddata/xcschemes/EmbedReader.xcscheme b/iphone/examples/EmbedReader/EmbedReader.xcodeproj/xcshareddata/xcschemes/EmbedReader.xcscheme new file mode 100644 index 0000000..7f08e65 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader.xcodeproj/xcshareddata/xcschemes/EmbedReader.xcscheme @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DCFB448E136F7202004B3EE0" + BuildableName = "EmbedReader.app" + BlueprintName = "EmbedReader" + ReferencedContainer = "container:EmbedReader.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DCFB448E136F7202004B3EE0" + BuildableName = "EmbedReader.app" + BlueprintName = "EmbedReader" + ReferencedContainer = "container:EmbedReader.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DCFB448E136F7202004B3EE0" + BuildableName = "EmbedReader.app" + BlueprintName = "EmbedReader" + ReferencedContainer = "container:EmbedReader.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Info.plist b/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Info.plist new file mode 100644 index 0000000..fd80eb6 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Info.plist @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>net.sourceforge.zbar.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>NSMainNibFile</key> + <string>MainWindow</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + </array> +</dict> +</plist> diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Prefix.pch b/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Prefix.pch new file mode 100644 index 0000000..ed6587b --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReader-Prefix.pch @@ -0,0 +1,11 @@ +#import <Availability.h> + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iPhone SDK 3.0 and later." +#endif + +#ifdef __OBJC__ +# import <UIKit/UIKit.h> +# import <Foundation/Foundation.h> +# import "ZBarSDK.h" +#endif diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.h b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.h new file mode 100644 index 0000000..ab79042 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.h @@ -0,0 +1,19 @@ +// +// EmbedReaderAppDelegate.h +// EmbedReader +// +// Created by spadix on 5/2/11. +// + +#import <UIKit/UIKit.h> + +@class EmbedReaderViewController; + +@interface EmbedReaderAppDelegate : NSObject <UIApplicationDelegate> { +} + +@property (nonatomic, retain) IBOutlet UIWindow *window; +@property (nonatomic, retain) + IBOutlet EmbedReaderViewController *viewController; + +@end diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.m b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.m new file mode 100644 index 0000000..5db5649 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderAppDelegate.m @@ -0,0 +1,34 @@ +// +// EmbedReaderAppDelegate.m +// EmbedReader +// +// Created by spadix on 5/2/11. +// + +#import "EmbedReaderAppDelegate.h" +#import "EmbedReaderViewController.h" + +@implementation EmbedReaderAppDelegate +@synthesize window=_window; +@synthesize viewController=_viewController; + +- (BOOL) application: (UIApplication*) application + didFinishLaunchingWithOptions: (NSDictionary*) launchOptions +{ + self.window.rootViewController = self.viewController; + [self.window makeKeyAndVisible]; + + // force view class to load so it may be referenced directly from NIB + [ZBarReaderView class]; + + return(YES); +} + +- (void) dealloc +{ + [_window release]; + [_viewController release]; + [super dealloc]; +} + +@end diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.h b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.h new file mode 100644 index 0000000..100dc03 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.h @@ -0,0 +1,20 @@ +// +// EmbedReaderViewController.h +// EmbedReader +// +// Created by spadix on 5/2/11. +// + +#import <UIKit/UIKit.h> + +@interface EmbedReaderViewController + : UIViewController <ZBarReaderViewDelegate> { + ZBarReaderView *readerView; + UITextView *resultText; + ZBarCameraSimulator *cameraSim; +} + +@property (nonatomic, retain) IBOutlet ZBarReaderView *readerView; +@property (nonatomic, retain) IBOutlet UITextView *resultText; + +@end diff --git a/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.m b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.m new file mode 100644 index 0000000..177c190 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/EmbedReaderViewController.m @@ -0,0 +1,101 @@ +// +// EmbedReaderViewController.m +// EmbedReader +// +// Created by spadix on 5/2/11. +// + +#import "EmbedReaderViewController.h" + +@implementation EmbedReaderViewController + +@synthesize readerView, resultText; + +- (void) cleanup +{ + [cameraSim release]; + cameraSim = nil; + readerView.readerDelegate = nil; + [readerView release]; + readerView = nil; + [resultText release]; + resultText = nil; +} + +- (void) dealloc +{ + [self cleanup]; + [super dealloc]; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + + // the delegate receives decode results + readerView.readerDelegate = self; + + // ensure initial camera orientation is correctly set + UIApplication *app = [UIApplication sharedApplication]; + [readerView willRotateToInterfaceOrientation: app.statusBarOrientation + duration: 0]; + + // you can use this to support the simulator + if(TARGET_IPHONE_SIMULATOR) { + cameraSim = [[ZBarCameraSimulator alloc] + initWithViewController: self]; + cameraSim.readerView = readerView; + } +} + +- (void) viewDidUnload +{ + [self cleanup]; + [super viewDidUnload]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + // auto-rotation is supported + return(YES); +} + +- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + // compensate for view rotation so camera preview is not rotated + [readerView willRotateToInterfaceOrientation: orient + duration: duration]; +} + +- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + // perform rotation in animation loop so camera preview does not move + // wrt device orientation + [readerView setNeedsLayout]; +} + +- (void) viewDidAppear: (BOOL) animated +{ + // run the reader when the view is visible + [readerView start]; +} + +- (void) viewWillDisappear: (BOOL) animated +{ + [readerView stop]; +} + +- (void) readerView: (ZBarReaderView*) view + didReadSymbols: (ZBarSymbolSet*) syms + fromImage: (UIImage*) img +{ + // do something useful with results + for(ZBarSymbol *sym in syms) { + resultText.text = sym.data; + break; + } +} + +@end diff --git a/iphone/examples/EmbedReader/EmbedReader/en.lproj/EmbedReaderViewController.xib b/iphone/examples/EmbedReader/EmbedReader/en.lproj/EmbedReaderViewController.xib new file mode 100644 index 0000000..8f4e9dc --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/en.lproj/EmbedReaderViewController.xib @@ -0,0 +1,336 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1056</int> + <string key="IBDocument.SystemVersion">10J869</string> + <string key="IBDocument.InterfaceBuilderVersion">1306</string> + <string key="IBDocument.AppKitVersion">1038.35</string> + <string key="IBDocument.HIToolboxVersion">461.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">301</string> + </object> + <object class="NSArray" key="IBDocument.IntegratedClassDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>IBUINavigationItem</string> + <string>IBUIToolbar</string> + <string>IBUITextView</string> + <string>IBUIView</string> + <string>IBUINavigationBar</string> + <string>IBProxyObject</string> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="dict.values" ref="0"/> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="372490531"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="843779117"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIView" id="774585933"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">274</int> + <object class="NSMutableArray" key="NSSubviews"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBUIView" id="461611788"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">306</int> + <string key="NSFrame">{{8, 52}, {304, 228}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="971445553"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MAA</bytes> + </object> + <bool key="IBUIClipsSubviews">YES</bool> + <bool key="IBUIMultipleTouchEnabled">YES</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUINavigationBar" id="583656854"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">290</int> + <string key="NSFrameSize">{320, 44}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="461611788"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSArray" key="IBUIItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBUINavigationItem" id="889758731"> + <reference key="IBUINavigationBar" ref="583656854"/> + <string key="IBUITitle">EmbedReader</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + </object> + <object class="IBUIToolbar" id="89745263"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">266</int> + <string key="NSFrame">{{0, 416}, {320, 44}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView"/> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableArray" key="IBUIItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <object class="IBUITextView" id="971445553"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">314</int> + <string key="NSFrame">{{8, 300}, {304, 94}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="89745263"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MCAwAA</bytes> + </object> + <bool key="IBUIClipsSubviews">YES</bool> + <bool key="IBUIMultipleTouchEnabled">YES</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIEditable">NO</bool> + <string key="IBUIText">No barcode scanned...</string> + <object class="NSFont" key="IBUIFont"> + <string key="NSName">Helvetica</string> + <double key="NSSize">17</double> + <int key="NSfFlags">16</int> + </object> + <object class="IBUITextInputTraits" key="IBUITextInputTraits"> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + </object> + <string key="NSFrame">{{0, 20}, {320, 460}}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="583656854"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MC43NQA</bytes> + <object class="NSColorSpace" key="NSCustomColorSpace"> + <int key="NSID">2</int> + </object> + </object> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">view</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="774585933"/> + </object> + <int key="connectionID">7</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">readerView</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="461611788"/> + </object> + <int key="connectionID">16</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">resultText</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="971445553"/> + </object> + <int key="connectionID">17</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="372490531"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="843779117"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">6</int> + <reference key="object" ref="774585933"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="583656854"/> + <reference ref="89745263"/> + <reference ref="971445553"/> + <reference ref="461611788"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">8</int> + <reference key="object" ref="461611788"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">11</int> + <reference key="object" ref="583656854"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="889758731"/> + </object> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">12</int> + <reference key="object" ref="889758731"/> + <reference key="parent" ref="583656854"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">13</int> + <reference key="object" ref="89745263"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">15</int> + <reference key="object" ref="971445553"/> + <reference key="parent" ref="774585933"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>11.IBPluginDependency</string> + <string>12.IBPluginDependency</string> + <string>13.IBPluginDependency</string> + <string>15.IBPluginDependency</string> + <string>6.IBEditorWindowLastContentRect</string> + <string>6.IBPluginDependency</string> + <string>8.CustomClassName</string> + <string>8.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>EmbedReaderViewController</string> + <string>UIResponder</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>{{239, 654}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>ZBarReaderView</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="sourceID"/> + <int key="maxID">18</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">EmbedReaderViewController</string> + <string key="superclassName">UIViewController</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>readerView</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>ZBarReaderView</string> + <string>UITextView</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>readerView</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">readerView</string> + <string key="candidateClassName">ZBarReaderView</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">resultText</string> + <string key="candidateClassName">UITextView</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/EmbedReaderViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">ZBarReaderView</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/ZBarReaderView.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">301</string> + </data> +</archive> diff --git a/iphone/examples/EmbedReader/EmbedReader/en.lproj/InfoPlist.strings b/iphone/examples/EmbedReader/EmbedReader/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/iphone/examples/EmbedReader/EmbedReader/en.lproj/MainWindow.xib b/iphone/examples/EmbedReader/EmbedReader/en.lproj/MainWindow.xib new file mode 100644 index 0000000..dea1ac4 --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/en.lproj/MainWindow.xib @@ -0,0 +1,444 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1024</int> + <string key="IBDocument.SystemVersion">10D571</string> + <string key="IBDocument.InterfaceBuilderVersion">786</string> + <string key="IBDocument.AppKitVersion">1038.29</string> + <string key="IBDocument.HIToolboxVersion">460.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">112</string> + </object> + <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> + <bool key="EncodedWithXMLCoder">YES</bool> + <integer value="10"/> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="841351856"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="427554174"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUICustomObject" id="664661524"> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIViewController" id="943309135"> + <string key="IBUINibName">EmbedReaderViewController</string> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics"> + <int key="interfaceOrientation">1</int> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIHorizontal">NO</bool> + </object> + <object class="IBUIWindow" id="117978783"> + <nil key="NSNextResponder"/> + <int key="NSvFlags">292</int> + <string key="NSFrameSize">{320, 480}</string> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">1</int> + <bytes key="NSRGB">MSAxIDEAA</bytes> + </object> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIResizesToFullScreen">YES</bool> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">delegate</string> + <reference key="source" ref="841351856"/> + <reference key="destination" ref="664661524"/> + </object> + <int key="connectionID">4</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">viewController</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="943309135"/> + </object> + <int key="connectionID">11</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">window</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="117978783"/> + </object> + <int key="connectionID">14</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="841351856"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">3</int> + <reference key="object" ref="664661524"/> + <reference key="parent" ref="0"/> + <string key="objectName">EmbedReader App Delegate</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="427554174"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">10</int> + <reference key="object" ref="943309135"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">12</int> + <reference key="object" ref="117978783"/> + <reference key="parent" ref="0"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>10.CustomClassName</string> + <string>10.IBEditorWindowLastContentRect</string> + <string>10.IBPluginDependency</string> + <string>12.IBEditorWindowLastContentRect</string> + <string>12.IBPluginDependency</string> + <string>3.CustomClassName</string> + <string>3.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIApplication</string> + <string>UIResponder</string> + <string>EmbedReaderViewController</string> + <string>{{234, 376}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>{{525, 346}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>EmbedReaderAppDelegate</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="sourceID"/> + <int key="maxID">15</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">UIWindow</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBUserSource</string> + <string key="minorKey"/> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">EmbedReaderAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>viewController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>EmbedReaderViewController</string> + <string>UIWindow</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>viewController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">viewController</string> + <string key="candidateClassName">EmbedReaderViewController</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">window</string> + <string key="candidateClassName">UIWindow</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">EmbedReaderAppDelegate.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">EmbedReaderAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBUserSource</string> + <string key="minorKey"/> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">EmbedReaderViewController</string> + <string key="superclassName">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">EmbedReaderViewController.h</string> + </object> + </object> + </object> + <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSError.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSObject.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSThread.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURL.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="356479594"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIResponder.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIApplication</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIApplication.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIResponder</string> + <string key="superclassName">NSObject</string> + <reference key="sourceIdentifier" ref="356479594"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UISearchBar</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UISearchDisplayController</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UITextField.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIView</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIWindow</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIWindow.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string> + <integer value="1024" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <string key="IBDocument.LastKnownRelativeProjectPath">EmbedReader.xcodeproj</string> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">112</string> + </data> +</archive> diff --git a/iphone/examples/EmbedReader/EmbedReader/main.m b/iphone/examples/EmbedReader/EmbedReader/main.m new file mode 100644 index 0000000..e91e4ba --- /dev/null +++ b/iphone/examples/EmbedReader/EmbedReader/main.m @@ -0,0 +1,16 @@ +// +// main.m +// EmbedReader +// +// Created by spadix on 5/2/11. +// + +#import <UIKit/UIKit.h> + +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/EmbedReader/ZBarSDK b/iphone/examples/EmbedReader/ZBarSDK new file mode 100644 index 0000000..7a373ea --- /dev/null +++ b/iphone/examples/EmbedReader/ZBarSDK @@ -0,0 +1 @@ +../../build/Debug-iphoneos/ZBarSDK
\ No newline at end of file diff --git a/iphone/examples/EmbedReader/build b/iphone/examples/EmbedReader/build new file mode 100644 index 0000000..534f8d4 --- /dev/null +++ b/iphone/examples/EmbedReader/build @@ -0,0 +1 @@ +/tmp/EmbedReader.build
\ No newline at end of file diff --git a/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0a0db3e --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj @@ -0,0 +1,419 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + DC8245F5162B549F0010B2E6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8245F4162B549F0010B2E6 /* Default-568h@2x.png */; }; + DC8F289413579EFF005B8862 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F289313579EFF005B8862 /* UIKit.framework */; }; + DC8F289613579EFF005B8862 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F289513579EFF005B8862 /* Foundation.framework */; }; + DC8F289813579EFF005B8862 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F289713579EFF005B8862 /* CoreGraphics.framework */; }; + DC8F289E13579EFF005B8862 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC8F289C13579EFF005B8862 /* InfoPlist.strings */; }; + DC8F28A113579EFF005B8862 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8F28A013579EFF005B8862 /* main.m */; }; + DC8F28A413579EFF005B8862 /* ReaderSampleAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8F28A313579EFF005B8862 /* ReaderSampleAppDelegate.m */; }; + DC8F28A713579EFF005B8862 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28A513579EFF005B8862 /* MainWindow.xib */; }; + DC8F28AA13579EFF005B8862 /* ReaderSampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8F28A913579EFF005B8862 /* ReaderSampleViewController.m */; }; + DC8F28AD13579F00005B8862 /* ReaderSampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28AB13579EFF005B8862 /* ReaderSampleViewController.xib */; }; + DC8F28D01357A29D005B8862 /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28CA1357A29D005B8862 /* libzbar.a */; }; + DC8F28D11357A29D005B8862 /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28CC1357A29D005B8862 /* zbar-back.png */; }; + DC8F28D21357A29D005B8862 /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28CD1357A29D005B8862 /* zbar-help.html */; }; + DC8F28D31357A29D005B8862 /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28CE1357A29D005B8862 /* zbar-helpicons.png */; }; + DC8F28D41357A29D005B8862 /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8F28CF1357A29D005B8862 /* zbar-samples.png */; }; + DC8F28DA1357A42E005B8862 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28D51357A42E005B8862 /* AVFoundation.framework */; }; + DC8F28DB1357A42E005B8862 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28D61357A42E005B8862 /* CoreMedia.framework */; }; + DC8F28DC1357A42E005B8862 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28D71357A42E005B8862 /* CoreVideo.framework */; }; + DC8F28DD1357A42E005B8862 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28D81357A42E005B8862 /* libiconv.dylib */; }; + DC8F28DE1357A42E005B8862 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8F28D91357A42E005B8862 /* QuartzCore.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + DC8245F4162B549F0010B2E6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; + DC8F288F13579EFF005B8862 /* ReaderSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReaderSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DC8F289313579EFF005B8862 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + DC8F289513579EFF005B8862 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + DC8F289713579EFF005B8862 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DC8F289B13579EFF005B8862 /* ReaderSample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ReaderSample-Info.plist"; sourceTree = "<group>"; }; + DC8F289D13579EFF005B8862 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + DC8F289F13579EFF005B8862 /* ReaderSample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReaderSample-Prefix.pch"; sourceTree = "<group>"; }; + DC8F28A013579EFF005B8862 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + DC8F28A213579EFF005B8862 /* ReaderSampleAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReaderSampleAppDelegate.h; sourceTree = "<group>"; }; + DC8F28A313579EFF005B8862 /* ReaderSampleAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReaderSampleAppDelegate.m; sourceTree = "<group>"; }; + DC8F28A613579EFF005B8862 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = "<group>"; }; + DC8F28A813579EFF005B8862 /* ReaderSampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReaderSampleViewController.h; sourceTree = "<group>"; }; + DC8F28A913579EFF005B8862 /* ReaderSampleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReaderSampleViewController.m; sourceTree = "<group>"; }; + DC8F28AC13579F00005B8862 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ReaderSampleViewController.xib; sourceTree = "<group>"; }; + DC8F28B71357A29D005B8862 /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = "<group>"; }; + DC8F28B81357A29D005B8862 /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; }; + DC8F28B91357A29D005B8862 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; }; + DC8F28BA1357A29D005B8862 /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = "<group>"; }; + DC8F28BB1357A29D005B8862 /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = "<group>"; }; + DC8F28BC1357A29D005B8862 /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = "<group>"; }; + DC8F28BD1357A29D005B8862 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; }; + DC8F28BE1357A29D005B8862 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; }; + DC8F28BF1357A29D005B8862 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = "<group>"; }; + DC8F28C01357A29D005B8862 /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = "<group>"; }; + DC8F28C11357A29D005B8862 /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = "<group>"; }; + DC8F28C21357A29D005B8862 /* ZBarHelpController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarHelpController.h; sourceTree = "<group>"; }; + DC8F28C31357A29D005B8862 /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = "<group>"; }; + DC8F28C41357A29D005B8862 /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = "<group>"; }; + DC8F28C51357A29D005B8862 /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = "<group>"; }; + DC8F28C61357A29D005B8862 /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = "<group>"; }; + DC8F28C71357A29D005B8862 /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = "<group>"; }; + DC8F28C81357A29D005B8862 /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = "<group>"; }; + DC8F28C91357A29D005B8862 /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = "<group>"; }; + DC8F28CA1357A29D005B8862 /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = "<group>"; }; + DC8F28CC1357A29D005B8862 /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = "<group>"; }; + DC8F28CD1357A29D005B8862 /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = "<group>"; }; + DC8F28CE1357A29D005B8862 /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = "<group>"; }; + DC8F28CF1357A29D005B8862 /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = "<group>"; }; + DC8F28D51357A42E005B8862 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DC8F28D61357A42E005B8862 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DC8F28D71357A42E005B8862 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DC8F28D81357A42E005B8862 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DC8F28D91357A42E005B8862 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC8F288C13579EFF005B8862 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DC8F28DA1357A42E005B8862 /* AVFoundation.framework in Frameworks */, + DC8F28DB1357A42E005B8862 /* CoreMedia.framework in Frameworks */, + DC8F28DC1357A42E005B8862 /* CoreVideo.framework in Frameworks */, + DC8F28DD1357A42E005B8862 /* libiconv.dylib in Frameworks */, + DC8F28DE1357A42E005B8862 /* QuartzCore.framework in Frameworks */, + DC8F289413579EFF005B8862 /* UIKit.framework in Frameworks */, + DC8F289613579EFF005B8862 /* Foundation.framework in Frameworks */, + DC8F289813579EFF005B8862 /* CoreGraphics.framework in Frameworks */, + DC8F28D01357A29D005B8862 /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DC8F288413579EFF005B8862 = { + isa = PBXGroup; + children = ( + DC8F289913579EFF005B8862 /* ReaderSample */, + DC8F28B31357A29D005B8862 /* ZBarSDK */, + DC8F289213579EFF005B8862 /* Frameworks */, + DC8F289013579EFF005B8862 /* Products */, + ); + sourceTree = "<group>"; + }; + DC8F289013579EFF005B8862 /* Products */ = { + isa = PBXGroup; + children = ( + DC8F288F13579EFF005B8862 /* ReaderSample.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + DC8F289213579EFF005B8862 /* Frameworks */ = { + isa = PBXGroup; + children = ( + DC8F289313579EFF005B8862 /* UIKit.framework */, + DC8F289513579EFF005B8862 /* Foundation.framework */, + DC8F289713579EFF005B8862 /* CoreGraphics.framework */, + DC8F28D91357A42E005B8862 /* QuartzCore.framework */, + DC8F28D61357A42E005B8862 /* CoreMedia.framework */, + DC8F28D71357A42E005B8862 /* CoreVideo.framework */, + DC8F28D51357A42E005B8862 /* AVFoundation.framework */, + DC8F28D81357A42E005B8862 /* libiconv.dylib */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + DC8F289913579EFF005B8862 /* ReaderSample */ = { + isa = PBXGroup; + children = ( + DC8F28A213579EFF005B8862 /* ReaderSampleAppDelegate.h */, + DC8F28A313579EFF005B8862 /* ReaderSampleAppDelegate.m */, + DC8F28A513579EFF005B8862 /* MainWindow.xib */, + DC8F28A813579EFF005B8862 /* ReaderSampleViewController.h */, + DC8F28A913579EFF005B8862 /* ReaderSampleViewController.m */, + DC8F28AB13579EFF005B8862 /* ReaderSampleViewController.xib */, + DC8F289A13579EFF005B8862 /* Supporting Files */, + ); + path = ReaderSample; + sourceTree = "<group>"; + }; + DC8F289A13579EFF005B8862 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + DC8F289B13579EFF005B8862 /* ReaderSample-Info.plist */, + DC8F289C13579EFF005B8862 /* InfoPlist.strings */, + DC8245F4162B549F0010B2E6 /* Default-568h@2x.png */, + DC8F289F13579EFF005B8862 /* ReaderSample-Prefix.pch */, + DC8F28A013579EFF005B8862 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + DC8F28B31357A29D005B8862 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC8F28B41357A29D005B8862 /* Headers */, + DC8F28CA1357A29D005B8862 /* libzbar.a */, + DC8F28CB1357A29D005B8862 /* Resources */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DC8F28B41357A29D005B8862 /* Headers */ = { + isa = PBXGroup; + children = ( + DC8F28B51357A29D005B8862 /* ZBarSDK */, + ); + path = Headers; + sourceTree = "<group>"; + }; + DC8F28B51357A29D005B8862 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC8F28B61357A29D005B8862 /* zbar */, + DC8F28C01357A29D005B8862 /* zbar.h */, + DC8F28C11357A29D005B8862 /* ZBarCaptureReader.h */, + DC8F28C21357A29D005B8862 /* ZBarHelpController.h */, + DC8F28C31357A29D005B8862 /* ZBarImage.h */, + DC8F28C41357A29D005B8862 /* ZBarImageScanner.h */, + DC8F28C51357A29D005B8862 /* ZBarReaderController.h */, + DC8F28C61357A29D005B8862 /* ZBarReaderView.h */, + DC8F28C71357A29D005B8862 /* ZBarReaderViewController.h */, + DC8F28C81357A29D005B8862 /* ZBarSDK.h */, + DC8F28C91357A29D005B8862 /* ZBarSymbol.h */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DC8F28B61357A29D005B8862 /* zbar */ = { + isa = PBXGroup; + children = ( + DC8F28B71357A29D005B8862 /* Decoder.h */, + DC8F28B81357A29D005B8862 /* Exception.h */, + DC8F28B91357A29D005B8862 /* Image.h */, + DC8F28BA1357A29D005B8862 /* ImageScanner.h */, + DC8F28BB1357A29D005B8862 /* Processor.h */, + DC8F28BC1357A29D005B8862 /* Scanner.h */, + DC8F28BD1357A29D005B8862 /* Symbol.h */, + DC8F28BE1357A29D005B8862 /* Video.h */, + DC8F28BF1357A29D005B8862 /* Window.h */, + ); + path = zbar; + sourceTree = "<group>"; + }; + DC8F28CB1357A29D005B8862 /* Resources */ = { + isa = PBXGroup; + children = ( + DC8F28CC1357A29D005B8862 /* zbar-back.png */, + DC8F28CD1357A29D005B8862 /* zbar-help.html */, + DC8F28CE1357A29D005B8862 /* zbar-helpicons.png */, + DC8F28CF1357A29D005B8862 /* zbar-samples.png */, + ); + path = Resources; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DC8F288E13579EFF005B8862 /* ReaderSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC8F28B013579F00005B8862 /* Build configuration list for PBXNativeTarget "ReaderSample" */; + buildPhases = ( + DC8F288B13579EFF005B8862 /* Sources */, + DC8F288C13579EFF005B8862 /* Frameworks */, + DC8F288D13579EFF005B8862 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ReaderSample; + productName = ReaderSample; + productReference = DC8F288F13579EFF005B8862 /* ReaderSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DC8F288613579EFF005B8862 /* Project object */ = { + isa = PBXProject; + attributes = { + ORGANIZATIONNAME = "ZBar Consulting Services"; + }; + buildConfigurationList = DC8F288913579EFF005B8862 /* Build configuration list for PBXProject "ReaderSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = DC8F288413579EFF005B8862; + productRefGroup = DC8F289013579EFF005B8862 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DC8F288E13579EFF005B8862 /* ReaderSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC8F288D13579EFF005B8862 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC8F289E13579EFF005B8862 /* InfoPlist.strings in Resources */, + DC8F28A713579EFF005B8862 /* MainWindow.xib in Resources */, + DC8F28AD13579F00005B8862 /* ReaderSampleViewController.xib in Resources */, + DC8F28D11357A29D005B8862 /* zbar-back.png in Resources */, + DC8F28D21357A29D005B8862 /* zbar-help.html in Resources */, + DC8F28D31357A29D005B8862 /* zbar-helpicons.png in Resources */, + DC8F28D41357A29D005B8862 /* zbar-samples.png in Resources */, + DC8245F5162B549F0010B2E6 /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DC8F288B13579EFF005B8862 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC8F28A113579EFF005B8862 /* main.m in Sources */, + DC8F28A413579EFF005B8862 /* ReaderSampleAppDelegate.m in Sources */, + DC8F28AA13579EFF005B8862 /* ReaderSampleViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + DC8F289C13579EFF005B8862 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + DC8F289D13579EFF005B8862 /* en */, + ); + name = InfoPlist.strings; + sourceTree = "<group>"; + }; + DC8F28A513579EFF005B8862 /* MainWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + DC8F28A613579EFF005B8862 /* en */, + ); + name = MainWindow.xib; + sourceTree = "<group>"; + }; + DC8F28AB13579EFF005B8862 /* ReaderSampleViewController.xib */ = { + isa = PBXVariantGroup; + children = ( + DC8F28AC13579F00005B8862 /* en */, + ); + name = ReaderSampleViewController.xib; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DC8F28AE13579F00005B8862 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + SDKROOT = iphoneos; + }; + name = Debug; + }; + DC8F28AF13579F00005B8862 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + }; + name = Release; + }; + DC8F28B113579F00005B8862 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ReaderSample/ReaderSample-Prefix.pch"; + INFOPLIST_FILE = "ReaderSample/ReaderSample-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + DC8F28B213579F00005B8862 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ReaderSample/ReaderSample-Prefix.pch"; + INFOPLIST_FILE = "ReaderSample/ReaderSample-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DC8F288913579EFF005B8862 /* Build configuration list for PBXProject "ReaderSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC8F28AE13579F00005B8862 /* Debug */, + DC8F28AF13579F00005B8862 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC8F28B013579F00005B8862 /* Build configuration list for PBXNativeTarget "ReaderSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC8F28B113579F00005B8862 /* Debug */, + DC8F28B213579F00005B8862 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DC8F288613579EFF005B8862 /* Project object */; +} diff --git a/iphone/examples/ReaderSample/ReaderSample.xcodeproj/xcshareddata/xcschemes/ReaderSample.xcscheme b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/xcshareddata/xcschemes/ReaderSample.xcscheme new file mode 100644 index 0000000..12fb1da --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/xcshareddata/xcschemes/ReaderSample.xcscheme @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC8F288E13579EFF005B8862" + BuildableName = "ReaderSample.app" + BlueprintName = "ReaderSample" + ReferencedContainer = "container:ReaderSample.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC8F288E13579EFF005B8862" + BuildableName = "ReaderSample.app" + BlueprintName = "ReaderSample" + ReferencedContainer = "container:ReaderSample.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC8F288E13579EFF005B8862" + BuildableName = "ReaderSample.app" + BlueprintName = "ReaderSample" + ReferencedContainer = "container:ReaderSample.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Info.plist b/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Info.plist new file mode 100644 index 0000000..4ceba32 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Info.plist @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>NSMainNibFile</key> + <string>MainWindow</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> +</dict> +</plist> diff --git a/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Prefix.pch b/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Prefix.pch new file mode 100644 index 0000000..2b3e510 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header for all source files of the 'ReaderSample' target in the 'ReaderSample' project +// + +#import <Availability.h> + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iPhone SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import <UIKit/UIKit.h> + #import <Foundation/Foundation.h> + // ADD: import barcode reader APIs + #import "ZBarSDK.h" +#endif diff --git a/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.h b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.h new file mode 100644 index 0000000..9666cd2 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.h @@ -0,0 +1,20 @@ +// +// ReaderSampleAppDelegate.h +// ReaderSample +// +// Created by spadix on 4/14/11. +// + +#import <UIKit/UIKit.h> + +@class ReaderSampleViewController; + +@interface ReaderSampleAppDelegate : NSObject <UIApplicationDelegate> { +} + +@property (nonatomic, retain) IBOutlet UIWindow *window; + +@property (nonatomic, retain) + IBOutlet ReaderSampleViewController *viewController; + +@end diff --git a/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.m b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.m new file mode 100644 index 0000000..c5c2d29 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleAppDelegate.m @@ -0,0 +1,67 @@ +// +// ReaderSampleAppDelegate.m +// ReaderSample +// +// Created by spadix on 4/14/11. +// + +#import "ReaderSampleAppDelegate.h" + +#import "ReaderSampleViewController.h" + +@implementation ReaderSampleAppDelegate + + +@synthesize window=_window; + +@synthesize viewController=_viewController; + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + // Override point for customization after application launch. + + self.window.rootViewController = self.viewController; + [self.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, this method is called instead of applicationWillTerminate: when the user quits. + */ +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + /* + Called as part of the 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. + Save data if appropriate. + See also applicationDidEnterBackground:. + */ +} + +@end diff --git a/iphone/examples/ReaderSample/ReaderSample/ReaderSampleViewController.h b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleViewController.h new file mode 100644 index 0000000..54abd91 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleViewController.h @@ -0,0 +1,19 @@ +// +// ReaderSampleViewController.h +// ReaderSample +// +// Created by spadix on 4/14/11. +// + +#import <UIKit/UIKit.h> + +@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/ReaderSample/ReaderSampleViewController.m b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleViewController.m new file mode 100644 index 0000000..67ddd6d --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/ReaderSampleViewController.m @@ -0,0 +1,68 @@ +// +// ReaderSampleViewController.m +// ReaderSample +// +// Created by spadix on 4/14/11. +// + +#import "ReaderSampleViewController.h" + +@implementation ReaderSampleViewController + +@synthesize resultImage, resultText; + +- (IBAction) scanButtonTapped +{ + // ADD: present a barcode reader that scans from the camera feed + ZBarReaderViewController *reader = [[ZBarReaderViewController alloc] init]; + reader.readerDelegate = self; + reader.supportedOrientationsMask = ZBarOrientationMaskAll; + + 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<NSFastEnumeration> 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]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return(YES); +} + +- (void) dealloc { + self.resultImage = nil; + self.resultText = nil; + [super dealloc]; +} + +@end diff --git a/iphone/examples/ReaderSample/ReaderSample/en.lproj/InfoPlist.strings b/iphone/examples/ReaderSample/ReaderSample/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/iphone/examples/ReaderSample/ReaderSample/en.lproj/MainWindow.xib b/iphone/examples/ReaderSample/ReaderSample/en.lproj/MainWindow.xib new file mode 100644 index 0000000..3b45763 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/en.lproj/MainWindow.xib @@ -0,0 +1,444 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1024</int> + <string key="IBDocument.SystemVersion">10D571</string> + <string key="IBDocument.InterfaceBuilderVersion">786</string> + <string key="IBDocument.AppKitVersion">1038.29</string> + <string key="IBDocument.HIToolboxVersion">460.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">112</string> + </object> + <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> + <bool key="EncodedWithXMLCoder">YES</bool> + <integer value="10"/> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="841351856"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="427554174"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUICustomObject" id="664661524"> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIViewController" id="943309135"> + <string key="IBUINibName">ReaderSampleViewController</string> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics"> + <int key="interfaceOrientation">1</int> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIHorizontal">NO</bool> + </object> + <object class="IBUIWindow" id="117978783"> + <nil key="NSNextResponder"/> + <int key="NSvFlags">292</int> + <string key="NSFrameSize">{320, 480}</string> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">1</int> + <bytes key="NSRGB">MSAxIDEAA</bytes> + </object> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIResizesToFullScreen">YES</bool> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">delegate</string> + <reference key="source" ref="841351856"/> + <reference key="destination" ref="664661524"/> + </object> + <int key="connectionID">4</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">viewController</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="943309135"/> + </object> + <int key="connectionID">11</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">window</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="117978783"/> + </object> + <int key="connectionID">14</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="841351856"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">3</int> + <reference key="object" ref="664661524"/> + <reference key="parent" ref="0"/> + <string key="objectName">ReaderSample App Delegate</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="427554174"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">10</int> + <reference key="object" ref="943309135"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">12</int> + <reference key="object" ref="117978783"/> + <reference key="parent" ref="0"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>10.CustomClassName</string> + <string>10.IBEditorWindowLastContentRect</string> + <string>10.IBPluginDependency</string> + <string>12.IBEditorWindowLastContentRect</string> + <string>12.IBPluginDependency</string> + <string>3.CustomClassName</string> + <string>3.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIApplication</string> + <string>UIResponder</string> + <string>ReaderSampleViewController</string> + <string>{{234, 376}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>{{525, 346}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>ReaderSampleAppDelegate</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="sourceID"/> + <int key="maxID">15</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">UIWindow</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBUserSource</string> + <string key="minorKey"/> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">ReaderSampleAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>viewController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>ReaderSampleViewController</string> + <string>UIWindow</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>viewController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">viewController</string> + <string key="candidateClassName">ReaderSampleViewController</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">window</string> + <string key="candidateClassName">UIWindow</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">ReaderSampleAppDelegate.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">ReaderSampleAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBUserSource</string> + <string key="minorKey"/> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">ReaderSampleViewController</string> + <string key="superclassName">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">ReaderSampleViewController.h</string> + </object> + </object> + </object> + <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSError.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSObject.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSThread.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURL.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="356479594"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIResponder.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIApplication</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIApplication.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIResponder</string> + <string key="superclassName">NSObject</string> + <reference key="sourceIdentifier" ref="356479594"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UISearchBar</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UISearchDisplayController</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UITextField.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIView</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIViewController</string> + <string key="superclassName">UIResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">UIWindow</string> + <string key="superclassName">UIView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">UIKit.framework/Headers/UIWindow.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string> + <integer value="1024" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <string key="IBDocument.LastKnownRelativeProjectPath">ReaderSample.xcodeproj</string> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">112</string> + </data> +</archive> diff --git a/iphone/examples/ReaderSample/ReaderSample/en.lproj/ReaderSampleViewController.xib b/iphone/examples/ReaderSample/ReaderSample/en.lproj/ReaderSampleViewController.xib new file mode 100644 index 0000000..16d9167 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/en.lproj/ReaderSampleViewController.xib @@ -0,0 +1,319 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1056</int> + <string key="IBDocument.SystemVersion">10J869</string> + <string key="IBDocument.InterfaceBuilderVersion">1306</string> + <string key="IBDocument.AppKitVersion">1038.35</string> + <string key="IBDocument.HIToolboxVersion">461.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">301</string> + </object> + <object class="NSArray" key="IBDocument.IntegratedClassDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>IBUITextView</string> + <string>IBUIButton</string> + <string>IBUIImageView</string> + <string>IBUIView</string> + <string>IBProxyObject</string> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="dict.values" ref="0"/> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="372490531"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="843779117"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIView" id="774585933"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">274</int> + <object class="NSMutableArray" key="NSSubviews"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBUIButton" id="973537804"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">282</int> + <string key="NSFrame">{{20, 396}, {280, 44}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <bool key="IBUIOpaque">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <int key="IBUIContentHorizontalAlignment">0</int> + <int key="IBUIContentVerticalAlignment">0</int> + <object class="NSFont" key="IBUIFont"> + <string key="NSName">Helvetica-Bold</string> + <double key="NSSize">19</double> + <int key="NSfFlags">16</int> + </object> + <int key="IBUIButtonType">1</int> + <string key="IBUINormalTitle">Scan</string> + <object class="NSColor" key="IBUIHighlightedTitleColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MQA</bytes> + </object> + <object class="NSColor" key="IBUINormalTitleColor"> + <int key="NSColorSpace">1</int> + <bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes> + </object> + <object class="NSColor" key="IBUINormalTitleShadowColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MC41AA</bytes> + </object> + </object> + <object class="IBUIImageView" id="409141881"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">311</int> + <string key="NSFrame">{{20, 20}, {280, 210}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="790976943"/> + <int key="IBUIContentMode">1</int> + <bool key="IBUIUserInteractionEnabled">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUITextView" id="790976943"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">282</int> + <string key="NSFrame">{{20, 238}, {280, 150}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="973537804"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MCAwAA</bytes> + </object> + <bool key="IBUIClipsSubviews">YES</bool> + <bool key="IBUIMultipleTouchEnabled">YES</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIEditable">NO</bool> + <string key="IBUIText">No barcode scanned...</string> + <object class="NSFont" key="IBUIFont"> + <string key="NSName">Helvetica</string> + <double key="NSSize">17</double> + <int key="NSfFlags">16</int> + </object> + <object class="IBUITextInputTraits" key="IBUITextInputTraits"> + <int key="IBUIAutocapitalizationType">2</int> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + </object> + <string key="NSFrame">{{0, 20}, {320, 460}}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="409141881"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MC43NQA</bytes> + <object class="NSColorSpace" key="NSCustomColorSpace"> + <int key="NSID">2</int> + </object> + </object> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">view</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="774585933"/> + </object> + <int key="connectionID">7</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">resultImage</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="409141881"/> + </object> + <int key="connectionID">11</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">resultText</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="790976943"/> + </object> + <int key="connectionID">12</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchEventConnection" key="connection"> + <string key="label">scanButtonTapped</string> + <reference key="source" ref="973537804"/> + <reference key="destination" ref="372490531"/> + <int key="IBEventType">7</int> + </object> + <int key="connectionID">13</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="372490531"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="843779117"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">6</int> + <reference key="object" ref="774585933"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="973537804"/> + <reference ref="409141881"/> + <reference ref="790976943"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">8</int> + <reference key="object" ref="973537804"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">9</int> + <reference key="object" ref="409141881"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">10</int> + <reference key="object" ref="790976943"/> + <reference key="parent" ref="774585933"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>10.IBPluginDependency</string> + <string>6.IBEditorWindowLastContentRect</string> + <string>6.IBPluginDependency</string> + <string>8.IBPluginDependency</string> + <string>9.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>ReaderSampleViewController</string> + <string>UIResponder</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>{{239, 654}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="sourceID"/> + <int key="maxID">13</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">ReaderSampleViewController</string> + <string key="superclassName">UIViewController</string> + <object class="NSMutableDictionary" key="actions"> + <string key="NS.key.0">scanButtonTapped</string> + <string key="NS.object.0">id</string> + </object> + <object class="NSMutableDictionary" key="actionInfosByName"> + <string key="NS.key.0">scanButtonTapped</string> + <object class="IBActionInfo" key="NS.object.0"> + <string key="name">scanButtonTapped</string> + <string key="candidateClassName">id</string> + </object> + </object> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIImageView</string> + <string>UITextView</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">resultImage</string> + <string key="candidateClassName">UIImageView</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">resultText</string> + <string key="candidateClassName">UITextView</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/ReaderSampleViewController.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">301</string> + </data> +</archive> diff --git a/iphone/examples/ReaderSample/ReaderSample/main.m b/iphone/examples/ReaderSample/ReaderSample/main.m new file mode 100644 index 0000000..4a373e4 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample/main.m @@ -0,0 +1,16 @@ +// +// main.m +// ReaderSample +// +// Created by spadix on 4/14/11. +// + +#import <UIKit/UIKit.h> + +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/ReaderSample/ZBarSDK b/iphone/examples/ReaderSample/ZBarSDK new file mode 100644 index 0000000..7a373ea --- /dev/null +++ b/iphone/examples/ReaderSample/ZBarSDK @@ -0,0 +1 @@ +../../build/Debug-iphoneos/ZBarSDK
\ No newline at end of file diff --git a/iphone/examples/ReaderSample/build b/iphone/examples/ReaderSample/build new file mode 100644 index 0000000..1e5c306 --- /dev/null +++ b/iphone/examples/ReaderSample/build @@ -0,0 +1 @@ +/tmp/ReaderSample.build
\ No newline at end of file diff --git a/iphone/examples/TabReader/TabReader.xcodeproj/project.pbxproj b/iphone/examples/TabReader/TabReader.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5c5bb27 --- /dev/null +++ b/iphone/examples/TabReader/TabReader.xcodeproj/project.pbxproj @@ -0,0 +1,420 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + DC52DED31370F5340048DADA /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC52DECE1370F5340048DADA /* AVFoundation.framework */; }; + DC52DED41370F5340048DADA /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC52DECF1370F5340048DADA /* CoreMedia.framework */; }; + DC52DED51370F5340048DADA /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC52DED01370F5340048DADA /* CoreVideo.framework */; }; + DC52DED61370F5340048DADA /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC52DED11370F5340048DADA /* libiconv.dylib */; }; + DC52DED71370F5340048DADA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC52DED21370F5340048DADA /* QuartzCore.framework */; }; + DC5D76D71370EE8F0069AEF5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5D76D61370EE8F0069AEF5 /* UIKit.framework */; }; + DC5D76D91370EE8F0069AEF5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5D76D81370EE8F0069AEF5 /* Foundation.framework */; }; + DC5D76DB1370EE8F0069AEF5 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5D76DA1370EE8F0069AEF5 /* CoreGraphics.framework */; }; + DC5D76E11370EE8F0069AEF5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC5D76DF1370EE8F0069AEF5 /* InfoPlist.strings */; }; + DC5D76E41370EE8F0069AEF5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DC5D76E31370EE8F0069AEF5 /* main.m */; }; + DC5D76E71370EE8F0069AEF5 /* TabReaderAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DC5D76E61370EE8F0069AEF5 /* TabReaderAppDelegate.m */; }; + DC5D76EA1370EE8F0069AEF5 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC5D76E81370EE8F0069AEF5 /* MainWindow.xib */; }; + DC5D76F01370EE8F0069AEF5 /* ResultsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC5D76EF1370EE8F0069AEF5 /* ResultsViewController.m */; }; + DC5D76F61370EE8F0069AEF5 /* ResultsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC5D76F41370EE8F0069AEF5 /* ResultsView.xib */; }; + DC5D771A1370F0E30069AEF5 /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5D77141370F0E30069AEF5 /* libzbar.a */; }; + DC5D771B1370F0E30069AEF5 /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DC5D77161370F0E30069AEF5 /* zbar-back.png */; }; + DC5D771C1370F0E30069AEF5 /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DC5D77171370F0E30069AEF5 /* zbar-help.html */; }; + DC5D771D1370F0E30069AEF5 /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DC5D77181370F0E30069AEF5 /* zbar-helpicons.png */; }; + DC5D771E1370F0E30069AEF5 /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DC5D77191370F0E30069AEF5 /* zbar-samples.png */; }; + DC82460B162B5A1D0010B2E6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC82460A162B5A1D0010B2E6 /* Default-568h@2x.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + DC52DECE1370F5340048DADA /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DC52DECF1370F5340048DADA /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DC52DED01370F5340048DADA /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DC52DED11370F5340048DADA /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DC52DED21370F5340048DADA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DC5D76D21370EE8F0069AEF5 /* TabReader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabReader.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DC5D76D61370EE8F0069AEF5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + DC5D76D81370EE8F0069AEF5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + DC5D76DA1370EE8F0069AEF5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DC5D76DE1370EE8F0069AEF5 /* TabReader-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TabReader-Info.plist"; sourceTree = "<group>"; }; + DC5D76E01370EE8F0069AEF5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + DC5D76E21370EE8F0069AEF5 /* TabReader-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TabReader-Prefix.pch"; sourceTree = "<group>"; }; + DC5D76E31370EE8F0069AEF5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + DC5D76E51370EE8F0069AEF5 /* TabReaderAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TabReaderAppDelegate.h; sourceTree = "<group>"; }; + DC5D76E61370EE8F0069AEF5 /* TabReaderAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TabReaderAppDelegate.m; sourceTree = "<group>"; }; + DC5D76E91370EE8F0069AEF5 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = "<group>"; }; + DC5D76EE1370EE8F0069AEF5 /* ResultsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ResultsViewController.h; sourceTree = "<group>"; }; + DC5D76EF1370EE8F0069AEF5 /* ResultsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ResultsViewController.m; sourceTree = "<group>"; }; + DC5D76F51370EE8F0069AEF5 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ResultsView.xib; sourceTree = "<group>"; }; + DC5D77001370F0E30069AEF5 /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = "<group>"; }; + DC5D77011370F0E30069AEF5 /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; }; + DC5D77021370F0E30069AEF5 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; }; + DC5D77031370F0E30069AEF5 /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = "<group>"; }; + DC5D77041370F0E30069AEF5 /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = "<group>"; }; + DC5D77051370F0E30069AEF5 /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = "<group>"; }; + DC5D77061370F0E30069AEF5 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; }; + DC5D77071370F0E30069AEF5 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; }; + DC5D77081370F0E30069AEF5 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = "<group>"; }; + DC5D77091370F0E30069AEF5 /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = "<group>"; }; + DC5D770A1370F0E30069AEF5 /* ZBarCameraSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCameraSimulator.h; sourceTree = "<group>"; }; + DC5D770B1370F0E30069AEF5 /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = "<group>"; }; + DC5D770C1370F0E30069AEF5 /* ZBarHelpController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarHelpController.h; sourceTree = "<group>"; }; + DC5D770D1370F0E30069AEF5 /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = "<group>"; }; + DC5D770E1370F0E30069AEF5 /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = "<group>"; }; + DC5D770F1370F0E30069AEF5 /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = "<group>"; }; + DC5D77101370F0E30069AEF5 /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = "<group>"; }; + DC5D77111370F0E30069AEF5 /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = "<group>"; }; + DC5D77121370F0E30069AEF5 /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = "<group>"; }; + DC5D77131370F0E30069AEF5 /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = "<group>"; }; + DC5D77141370F0E30069AEF5 /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = "<group>"; }; + DC5D77161370F0E30069AEF5 /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = "<group>"; }; + DC5D77171370F0E30069AEF5 /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = "<group>"; }; + DC5D77181370F0E30069AEF5 /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = "<group>"; }; + DC5D77191370F0E30069AEF5 /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = "<group>"; }; + DC82460A162B5A1D0010B2E6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC5D76CF1370EE8F0069AEF5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DC52DED31370F5340048DADA /* AVFoundation.framework in Frameworks */, + DC52DED41370F5340048DADA /* CoreMedia.framework in Frameworks */, + DC52DED51370F5340048DADA /* CoreVideo.framework in Frameworks */, + DC52DED61370F5340048DADA /* libiconv.dylib in Frameworks */, + DC52DED71370F5340048DADA /* QuartzCore.framework in Frameworks */, + DC5D76D71370EE8F0069AEF5 /* UIKit.framework in Frameworks */, + DC5D76D91370EE8F0069AEF5 /* Foundation.framework in Frameworks */, + DC5D76DB1370EE8F0069AEF5 /* CoreGraphics.framework in Frameworks */, + DC5D771A1370F0E30069AEF5 /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DC5D76C71370EE8D0069AEF5 = { + isa = PBXGroup; + children = ( + DC5D76DC1370EE8F0069AEF5 /* TabReader */, + DC5D76FC1370F0E30069AEF5 /* ZBarSDK */, + DC5D76D51370EE8F0069AEF5 /* Frameworks */, + DC5D76D31370EE8F0069AEF5 /* Products */, + ); + sourceTree = "<group>"; + }; + DC5D76D31370EE8F0069AEF5 /* Products */ = { + isa = PBXGroup; + children = ( + DC5D76D21370EE8F0069AEF5 /* TabReader.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + DC5D76D51370EE8F0069AEF5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + DC5D76D61370EE8F0069AEF5 /* UIKit.framework */, + DC5D76D81370EE8F0069AEF5 /* Foundation.framework */, + DC5D76DA1370EE8F0069AEF5 /* CoreGraphics.framework */, + DC52DECE1370F5340048DADA /* AVFoundation.framework */, + DC52DECF1370F5340048DADA /* CoreMedia.framework */, + DC52DED01370F5340048DADA /* CoreVideo.framework */, + DC52DED11370F5340048DADA /* libiconv.dylib */, + DC52DED21370F5340048DADA /* QuartzCore.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + DC5D76DC1370EE8F0069AEF5 /* TabReader */ = { + isa = PBXGroup; + children = ( + DC5D76E51370EE8F0069AEF5 /* TabReaderAppDelegate.h */, + DC5D76E61370EE8F0069AEF5 /* TabReaderAppDelegate.m */, + DC5D76E81370EE8F0069AEF5 /* MainWindow.xib */, + DC5D76EE1370EE8F0069AEF5 /* ResultsViewController.h */, + DC5D76EF1370EE8F0069AEF5 /* ResultsViewController.m */, + DC5D76F41370EE8F0069AEF5 /* ResultsView.xib */, + DC5D76DD1370EE8F0069AEF5 /* Supporting Files */, + ); + path = TabReader; + sourceTree = "<group>"; + }; + DC5D76DD1370EE8F0069AEF5 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + DC5D76DE1370EE8F0069AEF5 /* TabReader-Info.plist */, + DC5D76DF1370EE8F0069AEF5 /* InfoPlist.strings */, + DC82460A162B5A1D0010B2E6 /* Default-568h@2x.png */, + DC5D76E21370EE8F0069AEF5 /* TabReader-Prefix.pch */, + DC5D76E31370EE8F0069AEF5 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + DC5D76FC1370F0E30069AEF5 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC5D76FD1370F0E30069AEF5 /* Headers */, + DC5D77141370F0E30069AEF5 /* libzbar.a */, + DC5D77151370F0E30069AEF5 /* Resources */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DC5D76FD1370F0E30069AEF5 /* Headers */ = { + isa = PBXGroup; + children = ( + DC5D76FE1370F0E30069AEF5 /* ZBarSDK */, + ); + path = Headers; + sourceTree = "<group>"; + }; + DC5D76FE1370F0E30069AEF5 /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC5D76FF1370F0E30069AEF5 /* zbar */, + DC5D77091370F0E30069AEF5 /* zbar.h */, + DC5D770A1370F0E30069AEF5 /* ZBarCameraSimulator.h */, + DC5D770B1370F0E30069AEF5 /* ZBarCaptureReader.h */, + DC5D770C1370F0E30069AEF5 /* ZBarHelpController.h */, + DC5D770D1370F0E30069AEF5 /* ZBarImage.h */, + DC5D770E1370F0E30069AEF5 /* ZBarImageScanner.h */, + DC5D770F1370F0E30069AEF5 /* ZBarReaderController.h */, + DC5D77101370F0E30069AEF5 /* ZBarReaderView.h */, + DC5D77111370F0E30069AEF5 /* ZBarReaderViewController.h */, + DC5D77121370F0E30069AEF5 /* ZBarSDK.h */, + DC5D77131370F0E30069AEF5 /* ZBarSymbol.h */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DC5D76FF1370F0E30069AEF5 /* zbar */ = { + isa = PBXGroup; + children = ( + DC5D77001370F0E30069AEF5 /* Decoder.h */, + DC5D77011370F0E30069AEF5 /* Exception.h */, + DC5D77021370F0E30069AEF5 /* Image.h */, + DC5D77031370F0E30069AEF5 /* ImageScanner.h */, + DC5D77041370F0E30069AEF5 /* Processor.h */, + DC5D77051370F0E30069AEF5 /* Scanner.h */, + DC5D77061370F0E30069AEF5 /* Symbol.h */, + DC5D77071370F0E30069AEF5 /* Video.h */, + DC5D77081370F0E30069AEF5 /* Window.h */, + ); + path = zbar; + sourceTree = "<group>"; + }; + DC5D77151370F0E30069AEF5 /* Resources */ = { + isa = PBXGroup; + children = ( + DC5D77161370F0E30069AEF5 /* zbar-back.png */, + DC5D77171370F0E30069AEF5 /* zbar-help.html */, + DC5D77181370F0E30069AEF5 /* zbar-helpicons.png */, + DC5D77191370F0E30069AEF5 /* zbar-samples.png */, + ); + path = Resources; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DC5D76D11370EE8F0069AEF5 /* TabReader */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC5D76F91370EE900069AEF5 /* Build configuration list for PBXNativeTarget "TabReader" */; + buildPhases = ( + DC5D76CE1370EE8F0069AEF5 /* Sources */, + DC5D76CF1370EE8F0069AEF5 /* Frameworks */, + DC5D76D01370EE8F0069AEF5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TabReader; + productName = TabReader; + productReference = DC5D76D21370EE8F0069AEF5 /* TabReader.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DC5D76C91370EE8D0069AEF5 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = DC5D76CC1370EE8D0069AEF5 /* Build configuration list for PBXProject "TabReader" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = DC5D76C71370EE8D0069AEF5; + productRefGroup = DC5D76D31370EE8F0069AEF5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DC5D76D11370EE8F0069AEF5 /* TabReader */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC5D76D01370EE8F0069AEF5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC5D76E11370EE8F0069AEF5 /* InfoPlist.strings in Resources */, + DC5D76EA1370EE8F0069AEF5 /* MainWindow.xib in Resources */, + DC5D76F61370EE8F0069AEF5 /* ResultsView.xib in Resources */, + DC5D771B1370F0E30069AEF5 /* zbar-back.png in Resources */, + DC5D771C1370F0E30069AEF5 /* zbar-help.html in Resources */, + DC5D771D1370F0E30069AEF5 /* zbar-helpicons.png in Resources */, + DC5D771E1370F0E30069AEF5 /* zbar-samples.png in Resources */, + DC82460B162B5A1D0010B2E6 /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DC5D76CE1370EE8F0069AEF5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC5D76E41370EE8F0069AEF5 /* main.m in Sources */, + DC5D76E71370EE8F0069AEF5 /* TabReaderAppDelegate.m in Sources */, + DC5D76F01370EE8F0069AEF5 /* ResultsViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + DC5D76DF1370EE8F0069AEF5 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + DC5D76E01370EE8F0069AEF5 /* en */, + ); + name = InfoPlist.strings; + sourceTree = "<group>"; + }; + DC5D76E81370EE8F0069AEF5 /* MainWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + DC5D76E91370EE8F0069AEF5 /* en */, + ); + name = MainWindow.xib; + sourceTree = "<group>"; + }; + DC5D76F41370EE8F0069AEF5 /* ResultsView.xib */ = { + isa = PBXVariantGroup; + children = ( + DC5D76F51370EE8F0069AEF5 /* en */, + ); + name = ResultsView.xib; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DC5D76F71370EE900069AEF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + SDKROOT = iphoneos; + }; + name = Debug; + }; + DC5D76F81370EE900069AEF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + }; + name = Release; + }; + DC5D76FA1370EE900069AEF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TabReader/TabReader-Prefix.pch"; + INFOPLIST_FILE = "TabReader/TabReader-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + DC5D76FB1370EE900069AEF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TabReader/TabReader-Prefix.pch"; + INFOPLIST_FILE = "TabReader/TabReader-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DC5D76CC1370EE8D0069AEF5 /* Build configuration list for PBXProject "TabReader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC5D76F71370EE900069AEF5 /* Debug */, + DC5D76F81370EE900069AEF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC5D76F91370EE900069AEF5 /* Build configuration list for PBXNativeTarget "TabReader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC5D76FA1370EE900069AEF5 /* Debug */, + DC5D76FB1370EE900069AEF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DC5D76C91370EE8D0069AEF5 /* Project object */; +} diff --git a/iphone/examples/TabReader/TabReader.xcodeproj/xcshareddata/xcschemes/TabReader.xcscheme b/iphone/examples/TabReader/TabReader.xcodeproj/xcshareddata/xcschemes/TabReader.xcscheme new file mode 100644 index 0000000..ae63da8 --- /dev/null +++ b/iphone/examples/TabReader/TabReader.xcodeproj/xcshareddata/xcschemes/TabReader.xcscheme @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.8"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC5D76D11370EE8F0069AEF5" + BuildableName = "TabReader.app" + BlueprintName = "TabReader" + ReferencedContainer = "container:TabReader.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC5D76D11370EE8F0069AEF5" + BuildableName = "TabReader.app" + BlueprintName = "TabReader" + ReferencedContainer = "container:TabReader.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC5D76D11370EE8F0069AEF5" + BuildableName = "TabReader.app" + BlueprintName = "TabReader" + ReferencedContainer = "container:TabReader.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/examples/TabReader/TabReader/ResultsViewController.h b/iphone/examples/TabReader/TabReader/ResultsViewController.h new file mode 100644 index 0000000..c7b3115 --- /dev/null +++ b/iphone/examples/TabReader/TabReader/ResultsViewController.h @@ -0,0 +1,16 @@ +// +// SecondViewController.h +// TabReader +// +// Created by spadix on 5/3/11. +// + +#import <UIKit/UIKit.h> + +@interface ResultsViewController : UIViewController { +} + +@property (nonatomic, retain) IBOutlet UIImageView *resultImage; +@property (nonatomic, retain) IBOutlet UITextView *resultText; + +@end diff --git a/iphone/examples/TabReader/TabReader/ResultsViewController.m b/iphone/examples/TabReader/TabReader/ResultsViewController.m new file mode 100644 index 0000000..e1ee41f --- /dev/null +++ b/iphone/examples/TabReader/TabReader/ResultsViewController.m @@ -0,0 +1,20 @@ +// +// SecondViewController.m +// TabReader +// +// Created by spadix on 5/3/11. +// + +#import "ResultsViewController.h" + + +@implementation ResultsViewController + +@synthesize resultImage, resultText; + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return(YES); +} + +@end diff --git a/iphone/examples/TabReader/TabReader/TabReader-Info.plist b/iphone/examples/TabReader/TabReader/TabReader-Info.plist new file mode 100644 index 0000000..1e72cf2 --- /dev/null +++ b/iphone/examples/TabReader/TabReader/TabReader-Info.plist @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>net.sourceforge.zbar.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>NSMainNibFile</key> + <string>MainWindow</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> +</dict> +</plist> diff --git a/iphone/examples/TabReader/TabReader/TabReader-Prefix.pch b/iphone/examples/TabReader/TabReader/TabReader-Prefix.pch new file mode 100644 index 0000000..701a5b1 --- /dev/null +++ b/iphone/examples/TabReader/TabReader/TabReader-Prefix.pch @@ -0,0 +1,15 @@ +// +// Prefix header for all source files of the 'TabReader' target in the 'TabReader' project +// + +#import <Availability.h> + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iPhone SDK 3.0 and later." +#endif + +#ifdef __OBJC__ +# import <UIKit/UIKit.h> +# import <Foundation/Foundation.h> +# import "ZBarSDK.h" +#endif diff --git a/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.h b/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.h new file mode 100644 index 0000000..fa43cc3 --- /dev/null +++ b/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.h @@ -0,0 +1,18 @@ +// +// TabReaderAppDelegate.h +// TabReader +// +// Created by spadix on 5/3/11. +// + +#import <UIKit/UIKit.h> + +@interface TabReaderAppDelegate + : NSObject <UIApplicationDelegate, UITabBarControllerDelegate, + ZBarReaderDelegate> { +} + +@property (nonatomic, retain) IBOutlet UIWindow *window; +@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController; + +@end diff --git a/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.m b/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.m new file mode 100644 index 0000000..0168f4e --- /dev/null +++ b/iphone/examples/TabReader/TabReader/TabReaderAppDelegate.m @@ -0,0 +1,62 @@ +// +// TabReaderAppDelegate.m +// TabReader +// +// Created by spadix on 5/3/11. +// + +#import "TabReaderAppDelegate.h" +#import "ResultsViewController.h" + +@implementation TabReaderAppDelegate + +@synthesize window=_window; +@synthesize tabBarController=_tabBarController; + +- (BOOL) application: (UIApplication*) application + didFinishLaunchingWithOptions: (NSDictionary*) options +{ + // force class to load so it may be referenced directly from nib + [ZBarReaderViewController class]; + + ZBarReaderViewController *reader = + [self.tabBarController.viewControllers objectAtIndex: 0]; + reader.readerDelegate = self; + reader.showsZBarControls = NO; + reader.supportedOrientationsMask = ZBarOrientationMaskAll; + + self.window.rootViewController = self.tabBarController; + [self.window makeKeyAndVisible]; + + return(YES); +} + +- (void) dealloc +{ + [_window release]; + [_tabBarController release]; + [super dealloc]; +} + + +// ZBarReaderDelegate + +- (void) imagePickerController: (UIImagePickerController*) picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + // do something useful with results + UITabBarController *tabs = self.tabBarController; + tabs.selectedIndex = 1; + ResultsViewController *results = [tabs.viewControllers objectAtIndex: 1]; + UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; + results.resultImage.image = image; + + id <NSFastEnumeration> syms = + [info objectForKey: ZBarReaderControllerResults]; + for(ZBarSymbol *sym in syms) { + results.resultText.text = sym.data; + break; + } +} + +@end diff --git a/iphone/examples/TabReader/TabReader/en.lproj/InfoPlist.strings b/iphone/examples/TabReader/TabReader/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/iphone/examples/TabReader/TabReader/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/iphone/examples/TabReader/TabReader/en.lproj/MainWindow.xib b/iphone/examples/TabReader/TabReader/en.lproj/MainWindow.xib new file mode 100644 index 0000000..4875de8 --- /dev/null +++ b/iphone/examples/TabReader/TabReader/en.lproj/MainWindow.xib @@ -0,0 +1,411 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1024</int> + <string key="IBDocument.SystemVersion">10J869</string> + <string key="IBDocument.InterfaceBuilderVersion">1306</string> + <string key="IBDocument.AppKitVersion">1038.35</string> + <string key="IBDocument.HIToolboxVersion">461.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">301</string> + </object> + <object class="NSArray" key="IBDocument.IntegratedClassDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>IBProxyObject</string> + <string>IBUITabBarItem</string> + <string>IBUIViewController</string> + <string>IBUICustomObject</string> + <string>IBUITabBarController</string> + <string>IBUIWindow</string> + <string>IBUITabBar</string> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="dict.values" ref="0"/> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="841351856"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="532797962"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUICustomObject" id="664661524"> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIWindow" id="380026005"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">1316</int> + <object class="NSPSMatrix" key="NSFrameMatrix"/> + <string key="NSFrameSize">{320, 480}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">1</int> + <bytes key="NSRGB">MSAxIDEAA</bytes> + </object> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIResizesToFullScreen">YES</bool> + </object> + <object class="IBUITabBarController" id="1034742383"> + <object class="IBUISimulatedTabBarMetrics" key="IBUISimulatedBottomBarMetrics"/> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics"> + <int key="IBUIInterfaceOrientation">1</int> + <int key="interfaceOrientation">1</int> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIHorizontal">NO</bool> + <object class="IBUIViewController" key="IBUISelectedViewController" id="538989659"> + <object class="IBUITabBarItem" key="IBUITabBarItem" id="896756232"> + <string key="IBUITitle">Scan</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <reference key="IBUITabBar" ref="795333663"/> + </object> + <reference key="IBUIParentViewController" ref="1034742383"/> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics"> + <int key="IBUIInterfaceOrientation">1</int> + <int key="interfaceOrientation">1</int> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIHorizontal">NO</bool> + </object> + <object class="NSMutableArray" key="IBUIViewControllers"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="538989659"/> + <object class="IBUIViewController" id="268481961"> + <object class="IBUITabBarItem" key="IBUITabBarItem" id="807309489"> + <string key="IBUITitle">Results</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <reference key="IBUITabBar" ref="795333663"/> + </object> + <reference key="IBUIParentViewController" ref="1034742383"/> + <string key="IBUINibName">ResultsView</string> + <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics"> + <int key="IBUIInterfaceOrientation">1</int> + <int key="interfaceOrientation">1</int> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIHorizontal">NO</bool> + </object> + </object> + <object class="IBUITabBar" key="IBUITabBar" id="795333663"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">266</int> + <string key="NSFrame">{{0, 431}, {320, 49}}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MCAwAA</bytes> + </object> + <bool key="IBUIClearsContextBeforeDrawing">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableArray" key="IBUIItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="896756232"/> + <reference ref="807309489"/> + </object> + <reference key="IBUISelectedItem" ref="896756232"/> + </object> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">window</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="380026005"/> + </object> + <int key="connectionID">9</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">delegate</string> + <reference key="source" ref="841351856"/> + <reference key="destination" ref="664661524"/> + </object> + <int key="connectionID">99</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">tabBarController</string> + <reference key="source" ref="664661524"/> + <reference key="destination" ref="1034742383"/> + </object> + <int key="connectionID">113</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">delegate</string> + <reference key="source" ref="1034742383"/> + <reference key="destination" ref="664661524"/> + </object> + <int key="connectionID">127</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">2</int> + <reference key="object" ref="380026005"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="841351856"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">3</int> + <reference key="object" ref="664661524"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">106</int> + <reference key="object" ref="1034742383"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="795333663"/> + <reference ref="538989659"/> + <reference ref="268481961"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">107</int> + <reference key="object" ref="795333663"/> + <reference key="parent" ref="1034742383"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="532797962"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">125</int> + <reference key="object" ref="538989659"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="896756232"/> + </object> + <reference key="parent" ref="1034742383"/> + <string key="objectName">Barcode Reader Controller</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">126</int> + <reference key="object" ref="896756232"/> + <reference key="parent" ref="538989659"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">109</int> + <reference key="object" ref="268481961"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="807309489"/> + </object> + <reference key="parent" ref="1034742383"/> + <string key="objectName">Barcode Results Controller</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">110</int> + <reference key="object" ref="807309489"/> + <reference key="parent" ref="268481961"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>106.IBEditorWindowLastContentRect</string> + <string>106.IBPluginDependency</string> + <string>107.IBPluginDependency</string> + <string>109.CustomClassName</string> + <string>109.IBPluginDependency</string> + <string>110.IBPluginDependency</string> + <string>125.CustomClassName</string> + <string>125.IBPluginDependency</string> + <string>126.IBPluginDependency</string> + <string>2.IBAttributePlaceholdersKey</string> + <string>2.IBEditorWindowLastContentRect</string> + <string>2.IBPluginDependency</string> + <string>3.CustomClassName</string> + <string>3.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIApplication</string> + <string>UIResponder</string> + <string>{{1323, 676}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>ResultsViewController</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>ZBarReaderViewController</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <object class="NSMutableDictionary"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <string>{{229, 373}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>TabReaderAppDelegate</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="sourceID"/> + <int key="maxID">127</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">ResultsViewController</string> + <string key="superclassName">UIViewController</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIImageView</string> + <string>UITextView</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">resultImage</string> + <string key="candidateClassName">UIImageView</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">resultText</string> + <string key="candidateClassName">UITextView</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/ResultsViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">TabReaderAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>tabBarController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UITabBarController</string> + <string>UIWindow</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>tabBarController</string> + <string>window</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">tabBarController</string> + <string key="candidateClassName">UITabBarController</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">window</string> + <string key="candidateClassName">UIWindow</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/TabReaderAppDelegate.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">ZBarReaderViewController</string> + <string key="superclassName">UIViewController</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/ZBarReaderViewController.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string> + <integer value="1024" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">301</string> + </data> +</archive> diff --git a/iphone/examples/TabReader/TabReader/en.lproj/ResultsView.xib b/iphone/examples/TabReader/TabReader/en.lproj/ResultsView.xib new file mode 100644 index 0000000..c9c77bc --- /dev/null +++ b/iphone/examples/TabReader/TabReader/en.lproj/ResultsView.xib @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1024</int> + <string key="IBDocument.SystemVersion">10J869</string> + <string key="IBDocument.InterfaceBuilderVersion">1306</string> + <string key="IBDocument.AppKitVersion">1038.35</string> + <string key="IBDocument.HIToolboxVersion">461.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="NS.object.0">301</string> + </object> + <object class="NSArray" key="IBDocument.IntegratedClassDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>IBUITextView</string> + <string>IBUIImageView</string> + <string>IBUIView</string> + <string>IBUILabel</string> + <string>IBProxyObject</string> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <reference key="dict.values" ref="0"/> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBProxyObject" id="372490531"> + <string key="IBProxiedObjectIdentifier">IBFilesOwner</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBProxyObject" id="263589821"> + <string key="IBProxiedObjectIdentifier">IBFirstResponder</string> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + <object class="IBUIView" id="191373211"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">274</int> + <object class="NSMutableArray" key="NSSubviews"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBUILabel" id="483052203"> + <reference key="NSNextResponder" ref="191373211"/> + <int key="NSvFlags">306</int> + <string key="NSFrame">{{20, 20}, {280, 28}}</string> + <reference key="NSSuperview" ref="191373211"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="255779567"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MQA</bytes> + <object class="NSColorSpace" key="NSCustomColorSpace"> + <int key="NSID">2</int> + </object> + </object> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClipsSubviews">YES</bool> + <bool key="IBUIUserInteractionEnabled">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <string key="IBUIText">Results Tab</string> + <object class="NSFont" key="IBUIFont"> + <string key="NSName">Helvetica</string> + <double key="NSSize">24</double> + <int key="NSfFlags">16</int> + </object> + <object class="NSColor" key="IBUITextColor"> + <int key="NSColorSpace">1</int> + <bytes key="NSRGB">MCAwIDAAA</bytes> + </object> + <nil key="IBUIHighlightedColor"/> + <int key="IBUIBaselineAdjustment">1</int> + <float key="IBUIMinimumFontSize">10</float> + <int key="IBUITextAlignment">1</int> + </object> + <object class="IBUITextView" id="255779567"> + <reference key="NSNextResponder" ref="191373211"/> + <int key="NSvFlags">282</int> + <string key="NSFrame">{{20, 274}, {280, 117}}</string> + <reference key="NSSuperview" ref="191373211"/> + <reference key="NSWindow"/> + <bool key="IBUIOpaque">NO</bool> + <bool key="IBUIClipsSubviews">YES</bool> + <bool key="IBUIMultipleTouchEnabled">YES</bool> + <bool key="IBUIUserInteractionEnabled">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBUIShowsHorizontalScrollIndicator">NO</bool> + <bool key="IBUIDelaysContentTouches">NO</bool> + <bool key="IBUICanCancelContentTouches">NO</bool> + <float key="IBUIMinimumZoomScale">0.0</float> + <float key="IBUIMaximumZoomScale">0.0</float> + <bool key="IBUIBouncesZoom">NO</bool> + <bool key="IBUIEditable">NO</bool> + <string key="IBUIText">No barcode scanned.</string> + <object class="IBUITextInputTraits" key="IBUITextInputTraits"> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + <object class="IBUIImageView" id="809997790"> + <reference key="NSNextResponder" ref="191373211"/> + <int key="NSvFlags">306</int> + <string key="NSFrame">{{20, 56}, {280, 210}}</string> + <reference key="NSSuperview" ref="191373211"/> + <reference key="NSWindow"/> + <int key="IBUIContentMode">1</int> + <bool key="IBUIUserInteractionEnabled">NO</bool> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + <string key="NSFrame">{{0, 20}, {320, 411}}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="483052203"/> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MQA</bytes> + </object> + <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/> + <object class="IBUISimulatedTabBarMetrics" key="IBUISimulatedBottomBarMetrics"/> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">view</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="191373211"/> + </object> + <int key="connectionID">3</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">resultImage</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="809997790"/> + </object> + <int key="connectionID">9</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">resultText</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="255779567"/> + </object> + <int key="connectionID">10</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">1</int> + <reference key="object" ref="191373211"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="255779567"/> + <reference ref="483052203"/> + <reference ref="809997790"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="372490531"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="263589821"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5</int> + <reference key="object" ref="483052203"/> + <reference key="parent" ref="191373211"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">7</int> + <reference key="object" ref="255779567"/> + <reference key="parent" ref="191373211"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">8</int> + <reference key="object" ref="809997790"/> + <reference key="parent" ref="191373211"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-1.CustomClassName</string> + <string>-2.CustomClassName</string> + <string>1.IBEditorWindowLastContentRect</string> + <string>1.IBPluginDependency</string> + <string>5.IBPluginDependency</string> + <string>7.IBPluginDependency</string> + <string>7.IBViewBoundsToFrameTransform</string> + <string>8.IBPluginDependency</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>ResultsViewController</string> + <string>UIResponder</string> + <string>{{187, 376}, {320, 480}}</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <object class="NSAffineTransform"> + <bytes key="NSTransformStruct">P4AAAL+AAABA4AAAw7GAAA</bytes> + </object> + <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <reference key="dict.values" ref="0"/> + </object> + <nil key="sourceID"/> + <int key="maxID">10</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">ResultsViewController</string> + <string key="superclassName">UIViewController</string> + <object class="NSMutableDictionary" key="outlets"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>UIImageView</string> + <string>UITextView</string> + </object> + </object> + <object class="NSMutableDictionary" key="toOneOutletInfosByName"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>resultImage</string> + <string>resultText</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBToOneOutletInfo"> + <string key="name">resultImage</string> + <string key="candidateClassName">UIImageView</string> + </object> + <object class="IBToOneOutletInfo"> + <string key="name">resultText</string> + <string key="candidateClassName">UITextView</string> + </object> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/ResultsViewController.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string> + <integer value="1024" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="3100" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <string key="IBCocoaTouchPluginVersion">301</string> + </data> +</archive> diff --git a/iphone/examples/TabReader/TabReader/main.m b/iphone/examples/TabReader/TabReader/main.m new file mode 100644 index 0000000..5505e3a --- /dev/null +++ b/iphone/examples/TabReader/TabReader/main.m @@ -0,0 +1,16 @@ +// +// main.m +// TabReader +// +// Created by spadix on 5/3/11. +// + +#import <UIKit/UIKit.h> + +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/TabReader/ZBarSDK b/iphone/examples/TabReader/ZBarSDK new file mode 100644 index 0000000..7a373ea --- /dev/null +++ b/iphone/examples/TabReader/ZBarSDK @@ -0,0 +1 @@ +../../build/Debug-iphoneos/ZBarSDK
\ No newline at end of file diff --git a/iphone/examples/TabReader/build b/iphone/examples/TabReader/build new file mode 100644 index 0000000..fc13333 --- /dev/null +++ b/iphone/examples/TabReader/build @@ -0,0 +1 @@ +/tmp/TabReader.build
\ No newline at end of file diff --git a/iphone/examples/readertest/ZBarSDK b/iphone/examples/readertest/ZBarSDK new file mode 100644 index 0000000..7a373ea --- /dev/null +++ b/iphone/examples/readertest/ZBarSDK @@ -0,0 +1 @@ +../../build/Debug-iphoneos/ZBarSDK
\ No newline at end of file diff --git a/iphone/examples/readertest/build b/iphone/examples/readertest/build new file mode 100644 index 0000000..bd23a51 --- /dev/null +++ b/iphone/examples/readertest/build @@ -0,0 +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 index 0000000..ce373e1 --- /dev/null +++ b/iphone/examples/readertest/entitlements.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>get-task-allow</key> + <false/> +</dict> +</plist> diff --git a/iphone/examples/readertest/prefix.pch b/iphone/examples/readertest/prefix.pch new file mode 100644 index 0000000..9efda98 --- /dev/null +++ b/iphone/examples/readertest/prefix.pch @@ -0,0 +1,11 @@ +#ifdef __OBJC__ +# import <Foundation/Foundation.h> +# import <CoreFoundation/CoreFoundation.h> +# import <CoreGraphics/CoreGraphics.h> +# import <UIKit/UIKit.h> +# import <QuartzCore/QuartzCore.h> +# import <AVFoundation/AVFoundation.h> +# import <CoreMedia/CoreMedia.h> +# import <CoreVideo/CoreVideo.h> +# import "ZBarSDK.h" +#endif diff --git a/iphone/examples/readertest/readertest.m b/iphone/examples/readertest/readertest.m new file mode 100644 index 0000000..21ffc28 --- /dev/null +++ b/iphone/examples/readertest/readertest.m @@ -0,0 +1,991 @@ +enum { + CLASS_SECTION = 0, + SOURCE_SECTION, + CAMODE_SECTION, + DEVICE_SECTION, + FLASH_SECTION, + QUALITY_SECTION, + CONFIG_SECTION, + CUSTOM_SECTION, + SYMBOL_SECTION, + RESULT_SECTION, + NUM_SECTIONS +}; + +static NSString* const section_titles[] = { + @"Classes", + @"SourceType", + @"CameraMode", + @"CaptureDevice", + @"CameraFlashMode", + @"VideoQuality", + @"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 +}; + +static const CGFloat const zoom_choices[] = { + 1, 10/9., 10/8., 8/6., 10/7., 9/6., 10/6., 7/4., 2, 0, -1 +}; + +@interface AppDelegate + : UITableViewController + < UIApplicationDelegate, + UINavigationControllerDelegate, + UITableViewDelegate, + UITableViewDataSource, + UIActionSheetDelegate, + ZBarReaderDelegate > +{ + UIWindow *window; + UINavigationController *nav; + + NSSet *defaultSymbologies; + CGFloat zoom; + + NSMutableArray *sections, *symbolEnables; + NSInteger xDensity, yDensity; + + 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 +{ + return([super initWithStyle: UITableViewStyleGrouped]); +} + +- (void) initReader: (NSString*) clsName +{ + [reader release]; + Class cls = [[NSBundle mainBundle] + classNamed: clsName]; + assert(cls); + reader = [cls new]; + assert(reader); + reader.readerDelegate = self; + xDensity = yDensity = 3; + +#if 0 + // apply defaults for demo + ZBarImageScanner *scanner = reader.scanner; + continuous = NO; + zoom = 1; + reader.showsZBarControls = NO; + reader.scanCrop = CGRectMake(0, .35, 1, .3); + + [defaultSymbologies release]; + defaultSymbologies = + [[NSSet alloc] + initWithObjects: + [NSNumber numberWithInteger: ZBAR_CODE128], + nil]; + [scanner setSymbology: 0 + config: ZBAR_CFG_ENABLE + to: 0]; + for(NSNumber *sym in defaultSymbologies) + [scanner setSymbology: sym.integerValue + config: ZBAR_CFG_ENABLE + to: 1]; + + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: (xDensity = 0)]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: (yDensity = 1)]; +#endif +} + +- (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)]; + + + UIButton *info = + [UIButton buttonWithType: UIButtonTypeInfoLight]; + [info addTarget: self + action: @selector(info) + forControlEvents: UIControlEventTouchUpInside]; + + 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], + [[[UIBarButtonItem alloc] + initWithCustomView: info] + autorelease], + nil]; + [overlay addSubview: toolbar]; + [toolbar release]; +} + +- (void) updateCropMask +{ + CGRect r = reader.scanCrop; + 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) 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 deviceNames[] = { + @"Rear", @"Front", nil + }; + NSMutableArray *devices = [NSMutableArray array]; + for(int i = 0; deviceNames[i]; i++) + if([[reader class] + isCameraDeviceAvailable: i]) + [devices addObject: + [self cellWithTitle: deviceNames[i] + tag: i + checked: (reader.cameraDevice == i)]]; + assert(devices.count); + [sections replaceObjectAtIndex: DEVICE_SECTION + withObject: devices]; + + static NSString *const flashNames[] = { + @"Off", @"Auto", @"On", nil + }; + NSMutableArray *flashModes = [NSMutableArray array]; + for(int i = 0; flashNames[i]; i++) + [flashModes addObject: + [self cellWithTitle: flashNames[i] + tag: i - 1 + checked: (reader.cameraFlashMode == i - 1)]]; + [sections replaceObjectAtIndex: FLASH_SECTION + withObject: flashModes]; + + static NSString *const qualityNames[] = { + @"High", @"Medium", @"Low", @"640x480", nil + }; + NSMutableArray *qualities = [NSMutableArray array]; + for(int i = 0; qualityNames[i]; i++) + [qualities addObject: + [self cellWithTitle: qualityNames[i] + tag: i + checked: (reader.videoQuality == i)]]; + [sections replaceObjectAtIndex: QUALITY_SECTION + withObject: qualities]; + + 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 *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", xDensity]; + + 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", yDensity]; + + UITableViewCell *cropCell = + [[[UITableViewCell alloc] + initWithStyle: UITableViewCellStyleValue1 + reuseIdentifier: nil] + autorelease]; + cropCell.textLabel.text = @"scanCrop"; + cropCell.detailTextLabel.text = NSStringFromCGRect(reader.scanCrop); + + UITableViewCell *zoomCell = + [[[UITableViewCell alloc] + initWithStyle: UITableViewCellStyleValue1 + reuseIdentifier: nil] + autorelease]; + zoomCell.textLabel.text = @"zoom"; + zoomCell.detailTextLabel.text = + [NSString stringWithFormat: @"%g", zoom]; + + [sections replaceObjectAtIndex: CUSTOM_SECTION + withObject: [NSArray arrayWithObjects: + xDensityCell, + yDensityCell, + cropCell, + zoomCell, + [self cellWithTitle: @"continuous" + tag: 1 + checked: continuous], + nil]]; + + static const zbar_symbol_type_t allSymbologies[] = { + ZBAR_QRCODE, ZBAR_CODE128, ZBAR_CODE93, ZBAR_CODE39, ZBAR_CODABAR, + ZBAR_I25, ZBAR_DATABAR, ZBAR_DATABAR_EXP, + ZBAR_EAN13, ZBAR_EAN8, + ZBAR_EAN2, ZBAR_EAN5, ZBAR_COMPOSITE, + ZBAR_UPCA, ZBAR_UPCE, + ZBAR_ISBN13, ZBAR_ISBN10, + 0 + }; + NSMutableArray *symbols = [NSMutableArray array]; + [symbolEnables release]; + symbolEnables = [NSMutableArray new]; + BOOL en = YES; + for(int i = 0; allSymbologies[i]; i++) { + zbar_symbol_type_t sym = allSymbologies[i]; + if(defaultSymbologies) + en = !![defaultSymbologies member: + [NSNumber numberWithInteger: sym]]; + else + /* symbologies after ZBAR_EAN5 are disabled by default */ + en = en && (sym != ZBAR_EAN2); + [symbols addObject: + [self cellWithTitle: [ZBarSymbol nameForType: sym] + tag: sym + 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]; + [self updateCropMask]; + + 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]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return(YES); +} + +- (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.sourceType == UIImagePickerControllerSourceTypeCamera) + reader.cameraOverlayView = (reader.showsZBarControls) ? nil : overlay; + if([reader respondsToSelector: @selector(readerView)]) { + reader.readerView.showsFPS = YES; + if(zoom) + reader.readerView.zoom = zoom; + reader.supportedOrientationsMask = (reader.showsZBarControls) + ? ZBarOrientationMaskAll + : ZBarOrientationMask(UIInterfaceOrientationPortrait); // tmp disable + } + manualBtn.enabled = TARGET_IPHONE_SIMULATOR || + (reader.cameraMode == ZBarReaderControllerCameraModeDefault) || + [reader isKindOfClass: [ZBarReaderViewController class]]; + [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); + + [self updateCropMask]; +} + +- (void) advanceZoom: (UILabel*) label +{ + int i; + for(i = 0; zoom_choices[i] >= 0;) + if(zoom == zoom_choices[i++]) + break; + if(zoom_choices[i] < 0) + i = 0; + zoom = zoom_choices[i]; + assert(zoom >= 0); + label.text = [NSString stringWithFormat: @"%g", zoom]; +} + +- (void) advanceDensity: (UILabel*) label + value: (NSInteger*) value +{ + NSInteger d = *value; + int i; + for(i = 0; density_choices[i] >= 0;) + if(d == density_choices[i++]) + break; + if(density_choices[i] < 0) + i = 0; + *value = 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 updateCropMask]; + [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 DEVICE_SECTION: + reader.cameraDevice = cell.tag; + [self setCheckForTag: reader.cameraDevice + inSection: DEVICE_SECTION]; + break; + + case FLASH_SECTION: + reader.cameraFlashMode = cell.tag; + [self setCheckForTag: reader.cameraFlashMode + inSection: FLASH_SECTION]; + break; + + case QUALITY_SECTION: + reader.videoQuality = cell.tag; + [self setCheckForTag: reader.videoQuality + inSection: QUALITY_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: + [self advanceDensity: cell.detailTextLabel + value: &xDensity]; + break; + case 1: + [self advanceDensity: cell.detailTextLabel + value: &yDensity]; + break; + case 2: + [self advanceCrop: cell.detailTextLabel]; + break; + case 3: + [self advanceZoom: cell.detailTextLabel]; + break; + case 4: + [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) { + UIImage *img = + [UIImage imageWithData: + UIImagePNGRepresentation(imageView.image)]; + UIImageWriteToSavedPhotosAlbum(img, nil, NULL, NULL); + } +} + +// ZBarReaderDelegate + +- (void) imagePickerController: (UIImagePickerController*) picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + id <NSFastEnumeration> 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) { + int q = sym.quality; + if(quality < q) { + quality = q; + bestResult = sym; + } + } + + [self performSelector: @selector(presentResult:) + withObject: bestResult + afterDelay: .001]; + if(!continuous) + [picker dismissModalViewControllerAnimated: YES]; +} + +- (void) presentResult: (ZBarSymbol*) sym +{ + found = sym || imageView.image; + NSString *typeName = @"NONE"; + NSString *data = @""; + if(sym) { + typeName = sym.typeName; + data = sym.data; + } + typeLabel.text = typeName; + dataLabel.text = data; + + if(continuous) { + typeOvl.text = typeName; + dataOvl.text = data; + } + + NSLog(@"imagePickerController:didFinishPickingMediaWithInfo:\n"); + NSLog(@" type=%@ data=%@\n", 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[]) +{ + 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 index 0000000..62a8126 --- /dev/null +++ b/iphone/examples/readertest/readertest.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>net.sourceforge.zbar.test.readertest</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSRequiresIPhoneOS</key> + <true/> +</dict> +</plist> diff --git a/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj b/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5d30670 --- /dev/null +++ b/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj @@ -0,0 +1,351 @@ +// !$*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 */; }; + DC824600162B568A0010B2E6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8245FF162B568A0010B2E6 /* Default-568h@2x.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 = "<group>"; }; + DC48C4BF1219E5F70047193B /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; }; + DC48C4C01219E5F70047193B /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; }; + DC48C4C11219E5F70047193B /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = "<group>"; }; + DC48C4C21219E5F70047193B /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = "<group>"; }; + DC48C4C31219E5F70047193B /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = "<group>"; }; + DC48C4C41219E5F70047193B /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; }; + DC48C4C51219E5F70047193B /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; }; + DC48C4C61219E5F70047193B /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = "<group>"; }; + DC48C4C71219E5F70047193B /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = "<group>"; }; + DC48C4C81219E5F70047193B /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = "<group>"; }; + DC48C4C91219E5F70047193B /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = "<group>"; }; + DC48C4CA1219E5F70047193B /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = "<group>"; }; + DC48C4CB1219E5F70047193B /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = "<group>"; }; + DC48C4CC1219E5F70047193B /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = "<group>"; }; + DC48C4CD1219E5F70047193B /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = "<group>"; }; + DC48C4CE1219E5F70047193B /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = "<group>"; }; + DC48C4CF1219E5F70047193B /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = "<group>"; }; + DC48C4D01219E5F70047193B /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = "<group>"; }; + DC48C4D21219E5F70047193B /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = "<group>"; }; + DC48C4D31219E5F70047193B /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = "<group>"; }; + DC48C4D41219E5F70047193B /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = "<group>"; }; + DC48C4D51219E5F70047193B /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = "<group>"; }; + DC8245FF162B568A0010B2E6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; }; + 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 = "<group>"; }; + DCD6E0CF10B0AD41002005CD /* readertest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = readertest.m; sourceTree = "<group>"; }; + DCD6E0D810B0AD55002005CD /* readertest.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; fileEncoding = 4; path = readertest.plist; sourceTree = "<group>"; }; + 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 = "<group>"; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + DCD6E0CE10B0AD41002005CD /* prefix.pch */, + DCD6E0CF10B0AD41002005CD /* readertest.m */, + DC48C4BA1219E5F70047193B /* ZBarSDK */, + DC3CEAC61209C07400D7A786 /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = "<group>"; + }; + 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 = "<group>"; + }; + DC3CEAC61209C07400D7A786 /* Resources */ = { + isa = PBXGroup; + children = ( + DCD6E0D810B0AD55002005CD /* readertest.plist */, + DC8245FF162B568A0010B2E6 /* Default-568h@2x.png */, + ); + name = Resources; + sourceTree = "<group>"; + }; + DC48C4BA1219E5F70047193B /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC48C4BB1219E5F70047193B /* Headers */, + DC48C4D01219E5F70047193B /* libzbar.a */, + DC48C4D11219E5F70047193B /* Resources */, + ); + path = ZBarSDK; + sourceTree = "<group>"; + }; + DC48C4BB1219E5F70047193B /* Headers */ = { + isa = PBXGroup; + children = ( + DC48C4BC1219E5F70047193B /* ZBarSDK */, + ); + path = Headers; + sourceTree = "<group>"; + }; + 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 = "<group>"; + }; + 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 = "<group>"; + }; + DC48C4D11219E5F70047193B /* Resources */ = { + isa = PBXGroup; + children = ( + DC48C4D21219E5F70047193B /* zbar-back.png */, + DC48C4D31219E5F70047193B /* zbar-help.html */, + DC48C4D41219E5F70047193B /* zbar-helpicons.png */, + DC48C4D51219E5F70047193B /* zbar-samples.png */, + ); + path = Resources; + sourceTree = "<group>"; + }; +/* 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 */, + DC824600162B568A0010B2E6 /* Default-568h@2x.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; + OTHER_LDFLAGS = "-all_load"; + 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; + OTHER_LDFLAGS = "-all_load"; + 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/examples/readertest/readertest.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme b/iphone/examples/readertest/readertest.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme new file mode 100644 index 0000000..9264466 --- /dev/null +++ b/iphone/examples/readertest/readertest.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:readertest.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:readertest.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:readertest.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/include/ZBarSDK/ZBarCameraSimulator.h b/iphone/include/ZBarSDK/ZBarCameraSimulator.h new file mode 100644 index 0000000..7351d92 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarCameraSimulator.h @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------ +// Copyright 2010-2011 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +@class ZBarReaderView; + +// hack around missing simulator support for AVCapture interfaces + +@interface ZBarCameraSimulator + : NSObject <UINavigationControllerDelegate, UIImagePickerControllerDelegate, + UIPopoverControllerDelegate> { + UIViewController *viewController; + ZBarReaderView *readerView; + UIImagePickerController *picker; + UIPopoverController *pickerPopover; +} + +- (id)initWithViewController:(UIViewController *)viewController; +- (void)takePicture; + +@property (nonatomic, assign) ZBarReaderView *readerView; + +@end diff --git a/iphone/include/ZBarSDK/ZBarCaptureReader.h b/iphone/include/ZBarSDK/ZBarCaptureReader.h new file mode 100644 index 0000000..e496819 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarCaptureReader.h @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <CoreGraphics/CoreGraphics.h> +#import "ZBarImageScanner.h" + +@class AVCaptureVideoDataOutput, AVCaptureOutput; +@class ZBarCaptureReader, ZBarCVImage; + +@protocol ZBarCaptureDelegate <NSObject> + +// 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 { +#if !TARGET_IPHONE_SIMULATOR + AVCaptureVideoDataOutput *captureOutput; + id<ZBarCaptureDelegate> captureDelegate; + ZBarImageScanner *scanner; + CGRect scanCrop; + CGSize size; + CGFloat framesPerSecond; + BOOL enableCache; + + dispatch_queue_t queue; + ZBarImage *image; + ZBarCVImage *result; + volatile uint32_t state; + int framecnt; + unsigned width, height; + uint64_t t_frame, t_fps, t_scan; + CGFloat dt_frame; +#endif +} + +// 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; + +// capture the next frame after processing. the captured image will +// follow the same delegate path as an image with decoded symbols. +- (void)captureFrame; + +// 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<ZBarCaptureDelegate> 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 index 0000000..1733ebd --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarHelpController.h @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#import <WebKit/WebKit.h> + +@class ZBarHelpController; + +@protocol ZBarHelpDelegate +@optional + +- (void)helpControllerDidFinish:(ZBarHelpController *)help; + +@end + +// failure dialog w/a few useful tips + +@interface ZBarHelpController + : UIViewController <WKNavigationDelegate, UIAlertViewDelegate> { + NSString *reason; + id delegate; + WKWebView *webView; + UIToolbar *toolbar; + UIBarButtonItem *doneBtn, *backBtn, *space; + NSURL *linkURL; + NSUInteger orientations; + UIView *controls; +} + +@property (nonatomic, assign) id<ZBarHelpDelegate> 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 index 0000000..5a9e5bc --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarImage.h @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#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 index 0000000..c3ce1a9 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarImageScanner.h @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <Foundation/Foundation.h> +#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 index 0000000..84015b1 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderController.h @@ -0,0 +1,139 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#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 <UIImagePickerControllerDelegate> +@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<ZBarReaderDelegate> 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<ZBarReaderDelegate> 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<NSFastEnumeration>)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 index 0000000..f07878b --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderView.h @@ -0,0 +1,137 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#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; + +@optional +- (void)readerViewDidStart:(ZBarReaderView *)readerView; +- (void)readerView:(ZBarReaderView *)readerView + didStopWithError:(NSError *)error; + +@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<ZBarReaderViewDelegate> readerDelegate; + ZBarCaptureReader *captureReader; + CGRect scanCrop, effectiveCrop; + CGAffineTransform previewTransform; + CGFloat zoom, zoom0, maxZoom; + UIColor *trackingColor; + BOOL tracksSymbols, showsFPS; + NSInteger torchMode; + UIInterfaceOrientation interfaceOrientation; + NSTimeInterval animationDuration; + + CALayer *preview, *overlay, *tracking, *cropLayer; + UIView *fpsView; + UILabel *fpsLabel; + UIPinchGestureRecognizer *pinch; + CGFloat imageScale; + CGSize imageSize; + BOOL started, running, locked; +} + +// 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; + +// compensate for device/camera/interface orientation +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orient + duration:(NSTimeInterval)duration; + +// delegate is notified of decode results. +@property (nonatomic, assign) id<ZBarReaderViewDelegate> 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; + +// color of the tracking box (default green) +@property (nonatomic, retain) UIColor *trackingColor; + +// 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,maxZoom], +// defaults to 1.25 +@property (nonatomic) CGFloat zoom; +- (void)setZoom:(CGFloat)zoom animated:(BOOL)animated; + +// maximum settable zoom factor. +@property (nonatomic) CGFloat maxZoom; + +// 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 index 0000000..643ff1e --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderViewController.h @@ -0,0 +1,132 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <UIKit/UIKit.h> +#import "ZBarReaderController.h" + +// orientation set support +#define ZBarOrientationMask(orient) (1 << orient) +#define ZBarOrientationMaskAll \ + (ZBarOrientationMask(UIInterfaceOrientationPortrait) | \ + ZBarOrientationMask(UIInterfaceOrientationPortraitUpsideDown) | \ + ZBarOrientationMask(UIInterfaceOrientationLandscapeLeft) | \ + ZBarOrientationMask(UIInterfaceOrientationLandscapeRight)) + +@class ZBarReaderView, ZBarCameraSimulator; + +// 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<ZBarReaderDelegate> readerDelegate; + ZBarReaderView *readerView; + UIView *cameraOverlayView; + CGAffineTransform cameraViewTransform; + CGRect scanCrop; + NSUInteger supportedOrientationsMask; + UIImagePickerControllerCameraDevice cameraDevice; + UIImagePickerControllerCameraFlashMode cameraFlashMode; + UIImagePickerControllerQualityType videoQuality; + BOOL showsZBarControls, tracksSymbols, enableCache; + + ZBarHelpController *helpController; + UIView *controls, *shutter; + BOOL didHideStatusBar, rotating; + ZBarCameraSimulator *cameraSim; +} + +// access to configure image scanner +@property (nonatomic, readonly) ZBarImageScanner *scanner; + +// barcode result recipient +@property (nonatomic, assign) id<ZBarReaderDelegate> 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; + +// interface orientation support. bit-mask of accepted orientations. +// see eg ZBarOrientationMask() and ZBarOrientationMaskAll +@property (nonatomic) NSUInteger supportedOrientationsMask; + +// 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; + +// capture the next frame and send it over the usual delegate path. +- (void)takePicture; + +// these attempt to emulate UIImagePickerController ++ (BOOL)isCameraDeviceAvailable: + (UIImagePickerControllerCameraDevice)cameraDevice; ++ (BOOL)isFlashAvailableForCameraDevice: + (UIImagePickerControllerCameraDevice)cameraDevice; ++ (NSArray *)availableCaptureModesForCameraDevice: + (UIImagePickerControllerCameraDevice)cameraDevice; +@property (nonatomic) UIImagePickerControllerCameraDevice cameraDevice; +@property (nonatomic) UIImagePickerControllerCameraFlashMode cameraFlashMode; +@property (nonatomic) + UIImagePickerControllerCameraCaptureMode cameraCaptureMode; +@property (nonatomic) UIImagePickerControllerQualityType videoQuality; + +// 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 index 0000000..902552b --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarSDK.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#import "zbar.h" + +#import "ZBarCameraSimulator.h" +#import "ZBarCaptureReader.h" +#import "ZBarHelpController.h" +#import "ZBarImage.h" +#import "ZBarImageScanner.h" +#import "ZBarReaderController.h" +#import "ZBarReaderView.h" +#import "ZBarReaderViewController.h" +#import "ZBarSymbol.h" diff --git a/iphone/include/ZBarSDK/ZBarSymbol.h b/iphone/include/ZBarSDK/ZBarSymbol.h new file mode 100644 index 0000000..ab0557f --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarSymbol.h @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import <CoreGraphics/CoreGraphics.h> +#import <Foundation/Foundation.h> +#import "zbar.h" + +#ifdef __cplusplus +using namespace zbar; +#endif + +// Obj-C wrapper for ZBar result types + +@interface ZBarSymbolSet : NSObject <NSFastEnumeration> { + 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 index 0000000..0267780 --- /dev/null +++ b/iphone/include/config.h @@ -0,0 +1,236 @@ +/* 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 Codabar symbology */ +#define ENABLE_CODABAR 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 <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the <features.h> 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 <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <jpeglib.h> 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 <linux/videodev2.h> header file. */ +#undef HAVE_LINUX_VIDEODEV2_H + +/* Define to 1 if you have the <linux/videodev.h> header file. */ +#undef HAVE_LINUX_VIDEODEV_H + +/* Define to 1 if you have the <memory.h> 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 <poll.h> header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the <pthread.h> 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 <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/ipc.h> header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the <sys/shm.h> header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/times.h> header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> 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 <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <vfw.h> header file. */ +#undef HAVE_VFW_H + +/* Define to 1 if you have the <X11/extensions/XShm.h> header file. */ +#undef HAVE_X11_EXTENSIONS_XSHM_H + +/* Define to 1 if you have the <X11/extensions/Xvlib.h> 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 + +/* Program minor version (after the second '.') as a number */ +#define ZBAR_VERSION_PATCH 0 + +/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> 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 <sys/synch.h>, + <pthread.h>, or <semaphore.h> 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 index 0000000..a7bdae5 --- /dev/null +++ b/iphone/include/prefix.pch @@ -0,0 +1,10 @@ +#ifdef __OBJC__ +# import <Foundation/Foundation.h> +# import <CoreFoundation/CoreFoundation.h> +# import <CoreGraphics/CoreGraphics.h> +# import <UIKit/UIKit.h> +# import <QuartzCore/QuartzCore.h> +# import <AVFoundation/AVFoundation.h> +# import <CoreMedia/CoreMedia.h> +# import <CoreVideo/CoreVideo.h> +#endif diff --git a/iphone/res/ZBarSDK-Info.plist b/iphone/res/ZBarSDK-Info.plist new file mode 100644 index 0000000..0902ddb --- /dev/null +++ b/iphone/res/ZBarSDK-Info.plist @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleName</key> + <string>ZBarSDK</string> + <key>CFBundleVersion</key> + <string>1.3.1</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>NSHumanReadableCopyright</key> + <string>Copyright 2010 © Jeff Brown et al</string> +</dict> +</plist> diff --git a/iphone/res/ZBarSDK-bg.svg b/iphone/res/ZBarSDK-bg.svg new file mode 100644 index 0000000..59485e1 --- /dev/null +++ b/iphone/res/ZBarSDK-bg.svg @@ -0,0 +1,78 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved + + icon size = 64 + grid spacing = 32 + + $ java -Xmx2000m -jar batik-rasterizer.jar -w 4096 -h 4864 -d res/ZBarSDK-bg.8x.png res/ZBarSDK-bg.svg + $ convert -resize 12.5% res/ZBarSDK-bg.8x.png res/ZBarSDK-bg.png +--> + +<svg version="1.1" id="top" + width="512px" height="608px" + viewBox="-1,-1 16,19" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>ZBar iPhone SDK DMG Folder Background Image</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: url(#bg-grad) } + #grid { fill: none; stroke: white; stroke-opacity: .25 ; stroke-width: .05 } + #icons { marker: url(#icon) } + #icon *, .text { fill: none; stroke: red; stroke-width: .05 } + .section { fill: white; stroke: black; stroke-width: .1 } + #drag { stroke: none; fill: url(#drag-grad) } + text { text-anchor: middle; font-family: sans-serif; font-size: .78125 } + .small { font-size: 66% } + ]]></style> + <marker id="icon" overflow="visible"><rect x="-1" y="-1" width="2" height="2"/></marker> + <linearGradient id="bg-grad" x2="0%" y1="5%" y2="100%"> + <stop offset="0%" stop-color="#36a"/> + <stop offset="100%" stop-color="#112"/> + </linearGradient> + <linearGradient id="drag-grad" x1="100%" x2="0%"> + <stop offset="0%" stop-color="#4f8" stop-opacity="0.05"/> + <stop offset="72%" stop-color="#4f8" stop-opacity=".9"/> + <stop offset="100%" stop-color="#4f8" stop-opacity="1"/> + </linearGradient> + </defs> + + <rect id="bg" x="-1" y="-1" width="16" height="19"/> + + <rect class="section" x="1.5" y="-.25" width="11" height="4" rx=".5" ry=".5"/> + <rect class="section" x="1.5" y="4.75" width="11" height="4" rx=".5" ry=".5"/> + <rect class="section" x=".5" y="9.75" width="13" height="7.5" rx=".5" ry=".5"/> + + <text y="1.75"> + <tspan x="8.75" dy=".2344">Read this first</tspan> + </text> + <text y="6.75"> + <tspan x="8.75" dy="-.75">Then drag this</tspan> + <tspan x="8.75" dy="1">into your</tspan> + <tspan x="8.75" dy="1">Xcode project</tspan> + </text> + + <path id="drag" + d="M3.5,6.125 v1 h-2.75 v.3 l-1.1,-.8 l1.1,-.8 v.3z"/> + +<!-- + <g id="debug"> + <path id="grid" + d="M0,0h14 M0,2h14 M0,4h14 M0,6h14 M0,8h14 M0,10h14 M0,12h14 M0,14h14 M0,16h14 M0,18h14 + M0,0v17 M2,0v17 M4,0v17 M6,0v17 M8,0v17 M10,0v17 M12,0v17 M14,0v17"/> + <path id="icons" d="M3.5,1.5 M3.5,6.5 + M3,11.5 M7,11.5 M11,11.5 + M3,15 M7,15 M11,15"/> + <rect class="text" x="5.5" y=".25" width="6.5" height="3"/> + <rect class="text" x="5.5" y="5.25" width="6.5" height="3"/> + </g> +--> +</svg> diff --git a/iphone/res/buttondown.svg b/iphone/res/buttondown.svg new file mode 100644 index 0000000..799d5fc --- /dev/null +++ b/iphone/res/buttondown.svg @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="384px" height="384px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>rounded button overlay</title> + + <defs> + <style type="text/css"><![CDATA[ + #bg { fill: #202020 } + #test { fill: #d22 } + #button { fill: black; stroke: none; filter: url(#emboss) } + ]]></style> + + <filter id="emboss" filterUnits="userSpaceOnUse" + x="-1" y="-1" width="3" height="3"> + <feFlood flood-color="black" + x=".05" y=".05" width=".9" height=".9"/> + <feGaussianBlur result="grad" stdDeviation=".1" + x="-1" y="-1" width="3" height="3"/> + + <feComponentTransfer> + <feFuncA type="linear" slope="-1" intercept="1"/> + </feComponentTransfer> + + <feSpecularLighting surfaceScale=".25" specularConstant=".75" + specularExponent="16" lighting-color="#ccc"> + <fePointLight x="6" y="-10" z="2"/> + </feSpecularLighting> + <feGaussianBlur result="spec" stdDeviation="0.015"/> + + <feColorMatrix in="grad" type="matrix" values=" +0 0 0 0 0 +0 0 0 0 0 +0 0 0 0 0 +0 0 0 .8 -.3 +"/> + <feComposite in2="spec" operator="over"/> + <feComposite in2="SourceAlpha" operator="in"/> + </filter> + </defs> +<!-- + <rect id="bg" x="-.5" y="-.5" width="2" height="2"/> + <rect id="test" width="1" height="1" rx=".2" ry=".2"/> +--> + <rect id="button" width="1" height="1" rx=".25" ry=".25"/> +</svg> diff --git a/iphone/res/buttonmask.svg b/iphone/res/buttonmask.svg new file mode 100644 index 0000000..55f0e29 --- /dev/null +++ b/iphone/res/buttonmask.svg @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="384px" height="384px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>mask for button overlays</title> + + <defs> + <style type="text/css"><![CDATA[ + #bg { fill: white; stroke: none } + #mask { fill: black; stroke: none } + ]]></style> + </defs> + <!--<rect id="bg" x="-1" y="-1" width="3" height="3"/>--> + <rect id="mask" width="1" height="1" rx=".25" ry=".25"/> +</svg> diff --git a/iphone/res/buttonup.svg b/iphone/res/buttonup.svg new file mode 100644 index 0000000..861ca9a --- /dev/null +++ b/iphone/res/buttonup.svg @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="384px" height="384px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>rounded button overlay</title> + + <defs> + <style type="text/css"><![CDATA[ + #bg { fill: #202020 } + #test { fill: #d22 } + #button { fill: black; stroke: none; filter: url(#emboss) } + ]]></style> + + <filter id="emboss" filterUnits="userSpaceOnUse" + x="-1" y="-1" width="3" height="3"> + <feFlood flood-color="black" + x=".05" y=".05" width=".9" height=".9"/> + <feGaussianBlur result="grad" stdDeviation=".1" + x="-1" y="-1" width="3" height="3"/> + + <feSpecularLighting surfaceScale=".25" specularConstant="1" + specularExponent="16" lighting-color="#ccc"> + <fePointLight x="6" y="-10" z="5"/> + </feSpecularLighting> + <feGaussianBlur result="spec" stdDeviation="0.015"/> + + <feColorMatrix in="grad" type="matrix" values=" +0 0 0 0 0 +0 0 0 0 0 +0 0 0 0 0 +0 0 0 -1.05 1 +"/> + <feComposite in2="spec" operator="over"/> + <feComposite in2="SourceAlpha" operator="in" result="result"/> + <feFlood flood-color="black"/> + <feComposite in2="SourceAlpha" operator="out"/> + <feComposite in2="result" operator="over"/> + </filter> + </defs> +<!-- + <rect id="bg" x="-.5" y="-.5" width="2" height="2"/> + + <rect id="test" width="1" height="1" rx=".2" ry=".2"/> +--> + <rect id="button" width="1" height="1" rx=".25" ry=".25"/> +</svg> diff --git a/iphone/res/lightbulb.svg b/iphone/res/lightbulb.svg new file mode 100644 index 0000000..1f8628c --- /dev/null +++ b/iphone/res/lightbulb.svg @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="512px" height="512px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>lit light bulb icon</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: #202020 } + #bulb { fill: url(#bulb-on); stroke: #778; stroke-width: .06 } + #support { stroke: #888; stroke-width: .03; stroke-opacity: .4 } + #filament { stroke: #f80; stroke-width: .06; stroke-opacity: .5 } + #rays line { stroke: #fff; stroke-width: .1 } + #base { fill: #ccc; stroke: #777; stroke-width: .14 } + #screw { stroke: #777; stroke-width: .25 } + #nub { fill: #444; stroke: #444; stroke-width: .25 } + ]]></style> + + <radialGradient id="bulb-off" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1.2"> + <stop offset="0%" stop-color="#ccc"/> + <stop offset="33%" stop-color="#ccc"/> + <stop offset="100%" stop-color="#999"/> + </radialGradient> + + <radialGradient id="bulb-on" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1.2"> + <stop offset="0%" stop-color="#ffc"/> + <stop offset="33%" stop-color="#ffb"/> + <stop offset="100%" stop-color="#dda"/> + </radialGradient> + </defs> + + <!--<rect id="bg" width="1" height="1"/>--> + + <g transform="translate(.5,.28) scale(.27)"> + <path id="bulb" d=" +M-.42,1.85 +C-.42,1.4 -.42,1.22 -.866,.5 +A1,1 0 1 1 .866,.5 +C.42,1.22 .42,1.4 .42,1.85 +z +"/> + <path id="support" d=" +M-.42,.1 +C-.15,.9 -.15,1 -.15,1.86 +M.42,.1 +C.15,.9 .15,1 .15,1.86 +"/> + <path id="filament" d=" +M-.42,.1 +a.18,.15 0 1 1 .28,0 +a.18,.15 0 1 1 .28,0 +a.18,.15 0 1 1 .28,0 +"/> + + <g id="rays"> + <line x1="1.2" x2="1.8" transform="rotate(-30)"/> + <line x1="1.2" x2="1.8"/> + <line x1="1.2" x2="1.8" transform="rotate(30)"/> + <line x1="-1.2" x2="-1.8" transform="rotate(-30)"/> + <line x1="-1.2" x2="-1.8"/> + <line x1="-1.2" x2="-1.8" transform="rotate(30)"/> + </g> + </g> + + <g transform="translate(.5,.778) scale(.11)"> + <path id="base" d=" +M-1,-.05 +A2,.75 0 0 0 1,-.05 +V1.2 +L.55,1.6 +H-.55 +L-1,1.2 +z +"/> + <clipPath id="base-clip"><use xlink:href="#base"/></clipPath> + <path id="screw" clip-path="url(#base-clip)" d=" +M-1,.1 +a2.2,1 0 0 0 2,-.14 +M-1,.6 +a2.2,1 0 0 0 2,-.14 +M-1,1.1 +a2.2,1 0 0 0 2,-.14 +M-1,1.6 +a2.2,1 0 0 0 2,-.14 +"/> + </g> + + <path id="nub" transform="translate(.5,.954) scale(.06)" d=" +M-1,-.05 +A2,.333 0 0 0 1,-.05 +L.5,.5 +H-.5 +z +"/> + +</svg> diff --git a/iphone/res/shakyhand.svg b/iphone/res/shakyhand.svg new file mode 100644 index 0000000..2e3c594 --- /dev/null +++ b/iphone/res/shakyhand.svg @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="512px" height="512px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>unstable hand icon</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line, circle { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: #202020 } + #hand-outline { stroke: #a76; stroke-width: .75; stroke-opacity: 1 } + #skin-base { fill: #db9 } + #skin circle { fill: url(#skin-grad);/* stroke: red; stroke-width: .01*/ } + #shakes { stroke: #d5f; stroke-width: .03125 } /* #f43 */ + ]]></style> + + <clipPath id="hand-clip"><use xlink:href="#hand-outline"/></clipPath> + <clipPath id="wrist-clip"><rect x="-20" y="3" width="40" height="42"/></clipPath> + + <radialGradient id="skin-grad" r=".5" cx=".5" cy=".5"> + <stop offset="0%" stop-color="#edc"/> + <stop offset="100%" stop-color="#edc" stop-opacity="0"/> + </radialGradient> + </defs> + + <!--<rect id="bg" width="1" height="1"/>--> + + <g id="hand" transform="translate(.5,-.025) scale(.0225)" + clip-path="url(#wrist-clip)"> + <g id="skin" clip-path="url(#hand-clip)"> + <rect id="skin-base" x="-20" y="0" width="40" height="50"/> + + <circle r="10" transform="translate(4,33) rotate(-50) scale(1,.75)"/> + <circle r="4" transform="translate(-7,32) rotate(-8) scale(1,2)"/> + <circle r="4" transform="translate(-1.5,23) rotate(-8) scale(2.4,.9)"/> + + <g transform="rotate(-27.44 0,40)"> + <circle r="1.5" transform="translate(0,11.5) scale(.85,1.5)"/> + <circle r="1.5" transform="translate(0,15.25) scale(.85,1.5)"/> + <circle r="1.5" transform="translate(-.2,19) scale(.85,1.5)"/> + </g> + <g transform="rotate(-13.83 0,40) translate(0,.5)"> + <circle r="2" transform="translate(0,6.25) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,11.5) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,16.75) scale(.85,1.75)"/> + </g> + <g> + <circle r="2" transform="translate(0,6.25) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,11.5) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,16.75) scale(.85,1.75)"/> + </g> + <g transform="rotate(13.83 0,40) translate(0,.5)"> + <circle r="2" transform="translate(0,6.25) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,11.5) scale(.85,1.75)"/> + <circle r="2" transform="translate(0,16.75) scale(.85,1.75)"/> + </g> + <g transform="rotate(23.025 0,49.75)"> + <circle r="2.25" transform="translate(0,18) scale(.85,1.5)"/> + <circle r="2.25" transform="translate(0,24) scale(.85,1.5)"/> + </g> + </g> + + <path id="hand-outline" d=" +M-8,45 V39 +C-11,36 -11,32 -11,29 +C-11,22 -11,22 -14.7,14.7 +A1.4,1.4 0 0 1 -12.2,13.2 +L-7.75,21.8 +A.6,.6 0 0 0 -6.66,21.3 +L-10,8 +A2,2 0 0 1 -6,7 +L-3,19.2 +A.5,.5 0 0 0 -2,19 +L-2,5.5 +A2,2 0 0 1 2,5.5 +L2,19 +A.5,.5 0 0 0 3,19.2 +L6,7 +A2,2 0 0 1 10,8 +C7,20 7,19 7,25.5 +A.42,.42 0 0 0 7.75,25.75 +L10.6,18.8 +A2.25,2.25 0 0 1 14.8,20.6 +C11.7,28 11.7,27.7 11,30 +C10,34 7,39 3,40 +L3,45 +"/> + </g> + + <path id="shakes" transform="translate(.5,.5) rotate(22.5)" d=" +M.484,0 A.484,.484 0 0 0 .342,-.342 +M.39,0 A.39,.39 0 0 0 .276,-.276 +M-.484,0 A.484,.484 0 0 0 -.342,.342 +M-.39,0 A.39,.39 0 0 0 -.276,.276 +"/> + +</svg> diff --git a/iphone/res/shakyphone.svg b/iphone/res/shakyphone.svg new file mode 100644 index 0000000..96db019 --- /dev/null +++ b/iphone/res/shakyphone.svg @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="512px" height="512px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>iPhone shake gesture icon</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line, circle, rect { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: black } + #outline { fill: black; stroke: #4ef; stroke-width: .1 } + #screen { fill: #045; stroke: none } + #home { fill: #045; stroke: #4ef; stroke-width: .05 } + #speaker { stroke: #4ef; stroke-width: .0625 } + ]]></style> + </defs> + + <!--<rect id="bg" width="1" height="1"/>--> + + <use transform="translate(.5,.5) scale(.2) rotate(-15)" + style="opacity: .25" + xlink:href="#phone"/> + + <use transform="translate(.5,.5) scale(.2) rotate(-5)" + style="opacity: .5" + xlink:href="#phone"/> + + <use transform="translate(.5,.5) scale(.2) rotate(5)" + style="opacity: .75" + xlink:href="#phone"/> + + <g transform="translate(.5,.5) scale(.2) rotate(15)"> + <g id="phone"> + <rect id="outline" x="-1.1875" y="-2.21875" width="2.375" height="4.4375" rx=".25" ry=".25"/> + <rect id="screen" x="-1" y="-1.5" width="2" height="3"/> + <circle id="home" cy="1.84375" r=".21875"/> + <line id="speaker" x1="-.25" x2=".25" y1="-1.84375" y2="-1.84375"/> + </g> + </g> +</svg> diff --git a/iphone/res/stopwatch.svg b/iphone/res/stopwatch.svg new file mode 100644 index 0000000..186f2e9 --- /dev/null +++ b/iphone/res/stopwatch.svg @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="512px" height="512px" + viewBox="0,0 1,1" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>stopwatch icon</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line, circle, rect { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: #202020 } + #face { fill: url(#face-grad); stroke: none } + #button * { stroke: #fd0 } + #btn-top { stroke: url(#btn-grad); stroke-width: .45 } + #btn-stem { stroke-width: .5 } + #btn-base { fill: #fd0; stroke-width: .18 } + #ticks { stroke: #444; stroke-width: .06 } + #swoosh { fill: url(#swoosh-grad); stroke: none } + #hand { stroke: black; stroke-width: .1 } + ]]></style> + + <radialGradient id="face-grad" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1"> + <stop offset="0%" stop-color="#fff"/> + <stop offset="50%" stop-color="#eef"/> + <stop offset="85%" stop-color="#ccd"/> + <stop offset="85%" stop-color="#c93"/> + <stop offset="91.25%" stop-color="#fd0"/> + <stop offset="93.75%" stop-color="#fd0"/> + <stop offset="100%" stop-color="#c93"/> + </radialGradient> + + <radialGradient id="btn-grad" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="2" gradientTransform="scale(1,.2)"> + <stop offset="0%" stop-color="#fd0"/> + <stop offset="10%" stop-color="#fd0"/> + <stop offset="100%" stop-color="#c93"/> + </radialGradient> + + <linearGradient id="swoosh-grad" gradientUnits="userSpaceOnUse" + x1="1" x2="0" gradientTransform="translate(.22) rotate(-18)" + spreadMethod="pad"> + <stop offset="0%" stop-color="#45f"/> + <stop offset="100%" stop-color="#45f" stop-opacity="0"/> + </linearGradient> + </defs> + + <!--<rect id="bg" width="1" height="1"/>--> + + <g transform="translate(.5,.56) scale(.4375)"> + <g id="button" transform="translate(0,-1.28) scale(.24) translate(0,.225)"> + <path id="btn-stem" d="M0,.1v1"/> + <path id="btn-top" d="M-1,0h2"/> + <rect id="btn-base" x="-1" y=".8" width="2" height=".5"/> + </g> + + <circle id="face" r="1"/> + + <g id="ticks"> + <line y2="-.15" transform="translate(0,-.6)"/> + <line y2="-.15" transform="rotate(36) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(72) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(108) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(144) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(180) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(216) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(252) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(288) translate(0,-.6)"/> + <line y2="-.15" transform="rotate(324) translate(0,-.6)"/> + </g> + + <g transform="scale(.8)"> + <path id="swoosh" + d="M0,0 v-1 a1,1 0 0 1 .951,.691z"/> + <line id="hand" transform="scale(.975)" x2=".951" y2="-.309"/> + </g> + </g> + +</svg> diff --git a/iphone/res/zbar-back.png b/iphone/res/zbar-back.png Binary files differnew file mode 100644 index 0000000..6a0681d --- /dev/null +++ b/iphone/res/zbar-back.png diff --git a/iphone/res/zbar-back.svg b/iphone/res/zbar-back.svg new file mode 100644 index 0000000..64f1aba --- /dev/null +++ b/iphone/res/zbar-back.svg @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> + +<!-- + Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + All Rights Reserved +--> + +<svg version="1.1" id="top" + width="29px" height="24px" + viewBox="-.175,-.175 1.45,1.2" preserveAspectRatio="xMidYMid" overflow="visible" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>left pointing arrow toolbar icon</title> + + <defs> + <style type="text/css"><![CDATA[ + path, line { fill: none; stroke-linejoin: round; stroke-linecap: round } + #bg { fill: #78a } + #arrow { fill: white;; stroke: white; stroke-width: .1 } + ]]></style> + </defs> + + <!--<rect id="bg" x="-.5" y="-.5" width="2" height="2"/>--> + + <g> + <path id="arrow" d="M.1,.5 L.9,.1 .9,.9 z"/> + </g> +</svg> diff --git a/iphone/res/zbar-help.html b/iphone/res/zbar-help.html new file mode 100644 index 0000000..886cbb2 --- /dev/null +++ b/iphone/res/zbar-help.html @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<!-- +Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +All Rights Reserved +--> +<html> +<head> +<meta name="viewport" content="width=device-width, shrink-to-fit=YES"> +<title>Barcode Reader Help</title> +<style type="text/css"> +html, body { margin: 0; padding: 0; background: black; color: white; font-family: sans-serif; font-size: 16px } +h1 { margin: .5em 0; text-align: center; font-size: 28px } +h2 { font-size: 24px } +hr { margin: 1em 0; height: 0; border: none; border-top: solid 1px #666 } +.smaller { font-size: 85% } +p { margin: .5em } +a { color: #8af } +p.cen { text-align: center } +p.title { margin: 1em; clear: both; text-align: center } +div.col { width: 50% } +.clear { clear: both } +.iconlist { position: relative; clear: both; margin: 4px 0; padding: 1px 0 } +.icon { width: 64px; height: 64px; overflow: hidden; margin: 12px 16px; padding: 0; background: url("zbar-helpicons.png") no-repeat; font: bold 56px "Marker Felt"; text-align: center } +.sample { display: block; height: 96px; overflow: hidden; margin: 12px auto; padding: 0; background: url("zbar-samples.png") no-repeat; text-align: center} +.left { float: left } +.right { float: right } +.iconlist > p { margin: 10px } +.iconlist > h2 { margin: 10px } +</style> +</head> +<body> +<h1 id="title" style="color: #f88">Barcode Reader Help</h1> +<hr/> +<p class="title">Recognized barcodes should look like</p> +<div class="col left"> +<a class="sample" style="width: 118px; background-position: -96px" href="http://wikipedia.org/wiki/EAN-13"></a> +<p class="cen"><a href="http://wikipedia.org/wiki/EAN-13">EAN/UPC<br/>Product Codes</a></p> +</div> +<div class="col right"> +<a class="sample" style="width: 96px" href="http://wikipedia.org/wiki/QR_Code"></a> +<p class="cen"><a href="http://wikipedia.org/wiki/QR_Code">QR Codes</a></p> +</div> +<p class="clear cen smaller">Also recognized, but not shown: +<a href="http://wikipedia.org/wiki/Code_128">Code 128</a>, +<a href="http://wikipedia.org/wiki/DataBar">DataBar (RSS)</a>, +<a href="http://en.wikipedia.org/wiki/Code_93">Code 93</a>, +<a href="http://wikipedia.org/wiki/Code_39">Code 39</a> and +<a href="http://wikipedia.org/wiki/Codabar">Codabar</a> and +<a href="http://wikipedia.org/wiki/Interleaved_2_of_5">Interleaved 2 of 5</a></p> +<hr/> +<p class="clear title">Hints for successful scanning</p> +<div class="iconlist"> +<div class="icon left"></div> +<p>Ensure there is plenty of</p> +<h2 style="color: #ff4">Light</h2> +</div> +<div class="iconlist"> +<div class="icon left" style="background: none; color: #2d4">4"</div> +<h2 style="color: #4f6">Distance</h2> +<p>should be about 3 to 5 inches</p> +</div> +<div class="iconlist"> +<div class="icon left" style="background-position: 0 -64px"></div> +<h2 style="color: #3ee">Shake</h2> +<p>to force the camera to focus</p> +</div> +<div class="iconlist"> +<div class="icon left" style="background-position: 0 -128px"></div> +<h2 style="color: #59f">Wait</h2> +<p>for the autofocus to finish</p> +</div> +<div class="iconlist"> +<div class="icon left" style="background-position: 0 -192px"></div> +<h2 style="color: #d5f">Hold Still</h2> +<p>while the barcode is scanned</p> +</div> +<script type="text/javascript"> +function onZBarHelp(argv) { +var title; +switch(argv.reason) { +case "INFO": break; +case "FAIL": title = "No Barcode Detected"; break; +} +if(title) document.getElementById('title').textContent = title; +} +</script> +</body> +</html> diff --git a/iphone/res/zbar-helpicons.png b/iphone/res/zbar-helpicons.png Binary files differnew file mode 100644 index 0000000..2a07e7c --- /dev/null +++ b/iphone/res/zbar-helpicons.png diff --git a/iphone/res/zbar-samples.png b/iphone/res/zbar-samples.png Binary files differnew file mode 100644 index 0000000..7c805e2 --- /dev/null +++ b/iphone/res/zbar-samples.png diff --git a/iphone/zbar.xcodeproj/project.pbxproj b/iphone/zbar.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6732f6d --- /dev/null +++ b/iphone/zbar.xcodeproj/project.pbxproj @@ -0,0 +1,1196 @@ +// !$*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 */ + 56286C3324385403000B3E6B /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = 56286C3224385403000B3E6B /* LICENSE.md */; }; + 56286C342438540C000B3E6B /* LICENSE.md in Copy SDK */ = {isa = PBXBuildFile; fileRef = 56286C3224385403000B3E6B /* LICENSE.md */; }; + DC26004C118631C200FA987B /* ZBarCaptureReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DC26004B118631C200FA987B /* ZBarCaptureReader.m */; }; + DC290E281351496400A9B857 /* codabar.c in Sources */ = {isa = PBXBuildFile; fileRef = DC290E261351496400A9B857 /* codabar.c */; }; + 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 */; }; + 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 */; }; + DC5D76C3136FA8C40069AEF5 /* ZBarCameraSimulator.m in Sources */ = {isa = PBXBuildFile; fileRef = DC5D76C2136FA8C40069AEF5 /* ZBarCameraSimulator.m */; }; + DC5D76C6136FA94B0069AEF5 /* ZBarCameraSimulator.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC5D76C5136FA8F20069AEF5 /* ZBarCameraSimulator.h */; }; + 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 */; }; + DC8245EF1629DC340010B2E6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC8245EE1629DC140010B2E6 /* Default-568h@2x.png */; }; + DCB3789B12A43CAC0059B07B /* ZBarHelpController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DCB3789A12A43C7E0059B07B /* ZBarHelpController.h */; }; + DCC93259137B50CB0040A82D /* EmbedReader in Copy Examples */ = {isa = PBXBuildFile; fileRef = DCC9324E137B509B0040A82D /* EmbedReader */; }; + DCC9325A137B50CB0040A82D /* TabReader in Copy Examples */ = {isa = PBXBuildFile; fileRef = DCC9324F137B509B0040A82D /* TabReader */; }; + 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 = ( + DC5D76C6136FA94B0069AEF5 /* ZBarCameraSimulator.h in Copy Headers */, + 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 */, + DCC93259137B50CB0040A82D /* EmbedReader in Copy Examples */, + DCC9325A137B50CB0040A82D /* TabReader in Copy Examples */, + ); + name = "Copy Examples"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC3CF2061218355900D7A786 /* Copy SDK */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 1; + files = ( + 56286C342438540C000B3E6B /* LICENSE.md in Copy SDK */, + DC48C57D121A1F410047193B /* ZBarSDK in Copy SDK */, + DC3CF2021218353B00D7A786 /* README in Copy SDK */, + DC3CF2031218353B00D7A786 /* ChangeLog in Copy SDK */, + DC3CF2041218353B00D7A786 /* COPYING in Copy SDK */, + DC48C5A8121B1F910047193B /* Documentation.html in Copy SDK */, + ); + name = "Copy SDK"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 56286C3224385403000B3E6B /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = LICENSE.md; path = ../LICENSE.md; sourceTree = "<group>"; }; + 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 = "<group>"; }; + DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-helpicons.png"; path = "res/zbar-helpicons.png"; sourceTree = "<group>"; }; + DC1A49A411FF33B300BCDA30 /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-samples.png"; path = "res/zbar-samples.png"; sourceTree = "<group>"; }; + 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 = "<group>"; usesTabs = 0; }; + DC290E261351496400A9B857 /* codabar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = codabar.c; sourceTree = "<group>"; }; + DC290E271351496400A9B857 /* codabar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = codabar.h; sourceTree = "<group>"; }; + DC299AF51208B7BD006A023C /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = include/config.h; sourceTree = "<group>"; }; + 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 = "<group>"; }; + DC3AAB4C11B71A040021C7B1 /* ZBarCVImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCVImage.h; sourceTree = "<group>"; usesTabs = 0; }; + DC3AAB4D11B71A040021C7B1 /* ZBarCVImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarCVImage.m; sourceTree = "<group>"; usesTabs = 0; }; + DC3CE47611FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewImpl_Capture.m; sourceTree = "<group>"; usesTabs = 0; }; + DC3CE47711FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewImpl_Simulator.m; sourceTree = "<group>"; 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 = "<group>"; }; + DC3CEFA61216347100D7A786 /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarImage.h; path = include/ZBarSDK/ZBarImage.h; sourceTree = "<group>"; }; + DC3CEFA71216347100D7A786 /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarImageScanner.h; path = include/ZBarSDK/ZBarImageScanner.h; sourceTree = "<group>"; }; + DC3CEFA81216347100D7A786 /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderController.h; path = include/ZBarSDK/ZBarReaderController.h; sourceTree = "<group>"; }; + DC3CEFA91216347100D7A786 /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderView.h; path = include/ZBarSDK/ZBarReaderView.h; sourceTree = "<group>"; }; + DC3CEFAA1216347100D7A786 /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderViewController.h; path = include/ZBarSDK/ZBarReaderViewController.h; sourceTree = "<group>"; }; + DC3CEFAB1216347100D7A786 /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarSDK.h; path = include/ZBarSDK/ZBarSDK.h; sourceTree = "<group>"; }; + DC3CEFAC1216347100D7A786 /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarSymbol.h; path = include/ZBarSDK/ZBarSymbol.h; sourceTree = "<group>"; }; + DC3CF141121721A100D7A786 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; }; + DC3CF2081218358C00D7A786 /* ReaderSample */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ReaderSample; path = examples/ReaderSample; sourceTree = "<group>"; }; + DC3CF2651218358C00D7A786 /* readertest */ = {isa = PBXFileReference; lastKnownFileType = folder; name = readertest; path = examples/readertest; sourceTree = "<group>"; }; + DC3CF2A212197A7200D7A786 /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-back.png"; path = "res/zbar-back.png"; sourceTree = "<group>"; }; + DC3EBB2C119DDB2100107EE9 /* ZBarReaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewController.m; sourceTree = "<group>"; usesTabs = 0; }; + DC48C5401219FE550047193B /* readertest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = readertest.m; path = examples/readertest/readertest.m; sourceTree = "<group>"; }; + 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 = "<group>"; }; + DC4920EC10A70475000E4D43 /* ZBarImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarImage.m; sourceTree = "<group>"; usesTabs = 0; }; + DC4920ED10A70475000E4D43 /* ZBarImageScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarImageScanner.m; sourceTree = "<group>"; 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 = "<group>"; }; + 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; }; + DC5D76C2136FA8C40069AEF5 /* ZBarCameraSimulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarCameraSimulator.m; sourceTree = "<group>"; }; + DC5D76C5136FA8F20069AEF5 /* ZBarCameraSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarCameraSimulator.h; path = include/ZBarSDK/ZBarCameraSimulator.h; sourceTree = "<group>"; }; + 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 = "<group>"; usesTabs = 0; }; + DC67C12110A07AD30033B702 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C12310A07AD30033B702 /* code128.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code128.c; sourceTree = "<group>"; }; + DC67C12410A07AD30033B702 /* code128.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code128.h; sourceTree = "<group>"; }; + DC67C12510A07AD30033B702 /* code39.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code39.c; sourceTree = "<group>"; }; + DC67C12610A07AD30033B702 /* code39.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code39.h; sourceTree = "<group>"; }; + DC67C12710A07AD30033B702 /* ean.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ean.c; sourceTree = "<group>"; }; + DC67C12810A07AD30033B702 /* ean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ean.h; sourceTree = "<group>"; }; + DC67C12910A07AD30033B702 /* i25.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = i25.c; sourceTree = "<group>"; }; + DC67C12A10A07AD30033B702 /* i25.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = i25.h; sourceTree = "<group>"; }; + DC67C12E10A07AD30033B702 /* qr_finder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qr_finder.c; sourceTree = "<group>"; }; + DC67C12F10A07AD30033B702 /* qr_finder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qr_finder.h; sourceTree = "<group>"; }; + DC67C13010A07AD30033B702 /* decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decoder.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13110A07AD30033B702 /* decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoder.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13210A07AD30033B702 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13310A07AD30033B702 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13510A07AD30033B702 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13610A07AD30033B702 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13710A07AD30033B702 /* img_scanner.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = img_scanner.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C13810A07AD30033B702 /* img_scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = img_scanner.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C14710A07AD30033B702 /* bch15_5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bch15_5.c; sourceTree = "<group>"; }; + DC67C14810A07AD30033B702 /* bch15_5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bch15_5.h; sourceTree = "<group>"; }; + DC67C14910A07AD30033B702 /* binarize.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = binarize.c; sourceTree = "<group>"; }; + DC67C14A10A07AD30033B702 /* binarize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = binarize.h; sourceTree = "<group>"; }; + DC67C14B10A07AD30033B702 /* isaac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isaac.c; sourceTree = "<group>"; }; + DC67C14C10A07AD30033B702 /* isaac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = isaac.h; sourceTree = "<group>"; }; + DC67C14D10A07AD30033B702 /* qrdec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qrdec.c; sourceTree = "<group>"; }; + DC67C14E10A07AD30033B702 /* qrdec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qrdec.h; sourceTree = "<group>"; }; + DC67C14F10A07AD30033B702 /* qrdectxt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qrdectxt.c; sourceTree = "<group>"; }; + DC67C15010A07AD30033B702 /* rs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rs.c; sourceTree = "<group>"; }; + DC67C15110A07AD30033B702 /* rs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rs.h; sourceTree = "<group>"; }; + DC67C15210A07AD30033B702 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = "<group>"; }; + DC67C15310A07AD30033B702 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; }; + DC67C15410A07AD30033B702 /* qrcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qrcode.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C15510A07AD30033B702 /* refcnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = refcnt.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C15610A07AD30033B702 /* refcnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = refcnt.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C15710A07AD30033B702 /* scanner.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scanner.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C15A10A07AD30033B702 /* symbol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = symbol.c; sourceTree = "<group>"; usesTabs = 0; }; + DC67C15B10A07AD30033B702 /* symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = symbol.h; sourceTree = "<group>"; usesTabs = 0; }; + DC67C1BE10A07B6B0033B702 /* ZBarHelpController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarHelpController.m; sourceTree = "<group>"; usesTabs = 0; }; + DC67C1BF10A07B6B0033B702 /* ZBarReaderController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderController.m; sourceTree = "<group>"; usesTabs = 0; }; + DC67C1C010A07B6B0033B702 /* ZBarSymbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarSymbol.m; sourceTree = "<group>"; usesTabs = 0; }; + DC67C1CB10A07BEE0033B702 /* svg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svg.h; sourceTree = "<group>"; usesTabs = 0; }; + DC8245EE1629DC140010B2E6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "examples/readertest/Default-568h@2x.png"; sourceTree = "<group>"; }; + DCB3789A12A43C7E0059B07B /* ZBarHelpController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarHelpController.h; path = include/ZBarSDK/ZBarHelpController.h; sourceTree = "<group>"; }; + DCC9324E137B509B0040A82D /* EmbedReader */ = {isa = PBXFileReference; lastKnownFileType = folder; name = EmbedReader; path = examples/EmbedReader; sourceTree = "<group>"; }; + DCC9324F137B509B0040A82D /* TabReader */ = {isa = PBXFileReference; lastKnownFileType = folder; name = TabReader; path = examples/TabReader; sourceTree = "<group>"; }; + DCDC6E2F11ADCA8E00021380 /* ZBarReaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderView.m; sourceTree = "<group>"; usesTabs = 0; }; + DCE9900B129719F100D2655C /* code93.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code93.c; sourceTree = "<group>"; }; + DCE9900C129719F100D2655C /* code93.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code93.h; sourceTree = "<group>"; }; + DCF5C9AB11EA3AD100E7DC21 /* databar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = databar.c; sourceTree = "<group>"; }; + DCF5C9AC11EA3AD100E7DC21 /* databar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = databar.h; sourceTree = "<group>"; }; +/* 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 = "<group>"; + }; + 0867D691FE84028FC02AAC07 /* zbar */ = { + isa = PBXGroup; + children = ( + DC50467B12034D60009FF359 /* README */, + DC3CF141121721A100D7A786 /* ChangeLog */, + DC50467C12034D71009FF359 /* COPYING */, + DC50467D12034D71009FF359 /* LICENSE */, + 56286C3224385403000B3E6B /* LICENSE.md */, + DC48C5A7121B1F840047193B /* Documentation.html */, + DC299BED1208FE49006A023C /* src */, + DC1A49A111FF338B00BCDA30 /* Resources */, + DC67C11610A07A670033B702 /* include */, + DC3CF2071218356F00D7A786 /* Examples */, + DC48C5361219FE2F0047193B /* readertest */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = zbar; + sourceTree = "<group>"; + }; + 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 = "<group>"; + }; + DC1A49A111FF338B00BCDA30 /* Resources */ = { + isa = PBXGroup; + children = ( + DC3CF2A212197A7200D7A786 /* zbar-back.png */, + DC1A49A211FF33B300BCDA30 /* zbar-help.html */, + DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */, + DC1A49A411FF33B300BCDA30 /* zbar-samples.png */, + ); + name = Resources; + sourceTree = "<group>"; + usesTabs = 0; + }; + DC299BE41208FE09006A023C /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC5D76C5136FA8F20069AEF5 /* ZBarCameraSimulator.h */, + 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 = "<group>"; + }; + 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 */, + DC5D76C2136FA8C40069AEF5 /* ZBarCameraSimulator.m */, + DC67C1BE10A07B6B0033B702 /* ZBarHelpController.m */, + DC3EBB2C119DDB2100107EE9 /* ZBarReaderViewController.m */, + DC67C1BF10A07B6B0033B702 /* ZBarReaderController.m */, + DC67C11E10A07AD30033B702 /* zbar */, + ); + name = src; + sourceTree = "<group>"; + }; + DC3CF2071218356F00D7A786 /* Examples */ = { + isa = PBXGroup; + children = ( + DC3CF2081218358C00D7A786 /* ReaderSample */, + DC3CF2651218358C00D7A786 /* readertest */, + DCC9324E137B509B0040A82D /* EmbedReader */, + DCC9324F137B509B0040A82D /* TabReader */, + ); + name = Examples; + sourceTree = "<group>"; + }; + DC48C5361219FE2F0047193B /* readertest */ = { + isa = PBXGroup; + children = ( + DC8245EE1629DC140010B2E6 /* Default-568h@2x.png */, + DC48C5401219FE550047193B /* readertest.m */, + ); + name = readertest; + sourceTree = "<group>"; + }; + 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 = "<group>"; + }; + DC67C11610A07A670033B702 /* include */ = { + isa = PBXGroup; + children = ( + DC299BEC1208FE40006A023C /* prefix.pch */, + DC299AF51208B7BD006A023C /* config.h */, + DC3AAB4C11B71A040021C7B1 /* ZBarCVImage.h */, + DC299BE41208FE09006A023C /* ZBarSDK */, + ); + name = include; + sourceTree = "<group>"; + 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 */, + DC290E261351496400A9B857 /* codabar.c */, + DC290E271351496400A9B857 /* codabar.h */, + DC67C12910A07AD30033B702 /* i25.c */, + DC67C12A10A07AD30033B702 /* i25.h */, + DC67C12E10A07AD30033B702 /* qr_finder.c */, + DC67C12F10A07AD30033B702 /* qr_finder.h */, + ); + path = decoder; + sourceTree = "<group>"; + 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 = "<group>"; + 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; + attributes = { + LastUpgradeCheck = 1130; + }; + 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 */, + 56286C3324385403000B3E6B /* LICENSE.md in Resources */, + DC8245EF1629DC340010B2E6 /* Default-568h@2x.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\n"; + }; + 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\n"; + }; + 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 */, + DC5D76C3136FA8C40069AEF5 /* ZBarCameraSimulator.m in Sources */, + DC290E281351496400A9B857 /* codabar.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; + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + 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, + ); + INSTALL_PATH = /usr/local/lib; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = zbar; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1DEB922008733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = include/prefix.pch; + HEADER_SEARCH_PATHS = ( + include, + ../include, + ../zbar, + ); + INSTALL_PATH = /usr/local/lib; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = zbar; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 1DEB922308733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + 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_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG_OBJC=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-all_load"; + PROVISIONING_PROFILE = ""; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = iphoneos; + USE_HEADERMAP = NO; + }; + name = Debug; + }; + 1DEB922408733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + 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_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = "-all_load"; + PROVISIONING_PROFILE = ""; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = iphoneos; + USE_HEADERMAP = NO; + }; + name = Release; + }; + DC1A4A4F11FF5D0500BCDA30 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = NO; + PRODUCT_NAME = ZBarSDK.dmg; + TARGETED_DEVICE_FAMILY = "1,2"; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Debug; + }; + DC1A4A5011FF5D0500BCDA30 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = NO; + PRODUCT_NAME = ZBarSDK.dmg; + TARGETED_DEVICE_FAMILY = "1,2"; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Release; + }; + DC299A981208B5D5006A023C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + 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"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PRODUCT_BUNDLE_IDENTIFIER = net.sourceforge.zbar.test.readertest; + PRODUCT_NAME = readertest; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DC299A991208B5D5006A023C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + 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"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PRODUCT_BUNDLE_IDENTIFIER = net.sourceforge.zbar.test.readertest; + PRODUCT_NAME = readertest; + TARGETED_DEVICE_FAMILY = "1,2"; + ZERO_LINK = NO; + }; + name = Release; + }; + DC3748D71357BC69004E69D6 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + 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_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = "-all_load"; + PROVISIONING_PROFILE = ""; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = iphoneos; + USE_HEADERMAP = NO; + }; + name = Distribution; + }; + DC3748D81357BC69004E69D6 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = include/prefix.pch; + HEADER_SEARCH_PATHS = ( + include, + ../include, + ../zbar, + ); + INSTALL_PATH = /usr/local/lib; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = zbar; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Distribution; + }; + DC3748D91357BC69004E69D6 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + PRODUCT_NAME = ZBarSDK; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + ZERO_LINK = NO; + }; + name = Distribution; + }; + DC3748DA1357BC69004E69D6 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = NO; + PRODUCT_NAME = ZBarSDK.dmg; + TARGETED_DEVICE_FAMILY = "1,2"; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Distribution; + }; + DC3748DB1357BC69004E69D6 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + 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"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PRODUCT_BUNDLE_IDENTIFIER = net.sourceforge.zbar.test.readertest; + PRODUCT_NAME = readertest; + TARGETED_DEVICE_FAMILY = "1,2"; + ZERO_LINK = NO; + }; + name = Distribution; + }; + DC3CEE831215C7EF00D7A786 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + 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 = { + CLANG_ENABLE_OBJC_WEAK = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + 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 */, + DC3748D81357BC69004E69D6 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Distribution; + }; + 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "zbar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB922308733DC00010E9CD /* Debug */, + 1DEB922408733DC00010E9CD /* Release */, + DC3748D71357BC69004E69D6 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Distribution; + }; + DC1A4A5311FF5D3D00BCDA30 /* Build configuration list for PBXAggregateTarget "ZBarSDK.dmg" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC1A4A4F11FF5D0500BCDA30 /* Debug */, + DC1A4A5011FF5D0500BCDA30 /* Release */, + DC3748DA1357BC69004E69D6 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Distribution; + }; + DC299A9A1208B5D5006A023C /* Build configuration list for PBXNativeTarget "readertest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC299A981208B5D5006A023C /* Debug */, + DC299A991208B5D5006A023C /* Release */, + DC3748DB1357BC69004E69D6 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Distribution; + }; + DC3CEE851215C83500D7A786 /* Build configuration list for PBXAggregateTarget "ZBarSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC3CEE831215C7EF00D7A786 /* Debug */, + DC3CEE841215C7EF00D7A786 /* Release */, + DC3748D91357BC69004E69D6 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Distribution; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.dmg.xcscheme b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.dmg.xcscheme new file mode 100644 index 0000000..e18d450 --- /dev/null +++ b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.dmg.xcscheme @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.8"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC1A4A4E11FF5D0500BCDA30" + BuildableName = "ZBarSDK.dmg" + BlueprintName = "ZBarSDK.dmg" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Release"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Release"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.xcscheme b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.xcscheme new file mode 100644 index 0000000..1abb409 --- /dev/null +++ b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/ZBarSDK.xcscheme @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.8"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC3CEE821215C7EF00D7A786" + BuildableName = "ZBarSDK" + BlueprintName = "ZBarSDK" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "" + selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Release"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Release"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/zbar.xcodeproj/xcshareddata/xcschemes/libzbar.xcscheme b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/libzbar.xcscheme new file mode 100644 index 0000000..07cb0e8 --- /dev/null +++ b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/libzbar.xcscheme @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "D2AAC07D0554694100DB518D" + BuildableName = "libzbar.a" + BlueprintName = "libzbar" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/iphone/zbar.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme new file mode 100644 index 0000000..e50c0e3 --- /dev/null +++ b/iphone/zbar.xcodeproj/xcshareddata/xcschemes/readertest.xcscheme @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC299A941208B5D4006A023C" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC299A941208B5D4006A023C" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <EnvironmentVariables> + <EnvironmentVariable + key = "NSZombieEnabled" + value = "YES" + isEnabled = "YES"> + </EnvironmentVariable> + </EnvironmentVariables> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DC299A941208B5D4006A023C" + BuildableName = "readertest.app" + BlueprintName = "readertest" + ReferencedContainer = "container:zbar.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Distribution" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/java/Makefile.am b/java/Makefile.am new file mode 100644 index 0000000..005c3b2 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,59 @@ +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 + +nodist_libzbarjni_la_SOURCES = zbarjni.h +libzbarjni_la_SOURCES = zbarjni.c $(nodist_libzbarjni_la_SOURCES) +BUILT_SOURCES = $(nodist_libzbarjni_la_SOURCES) +MAINTAINERCLEANFILES = $(nodist_libzbarjni_la_SOURCES) + +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 + +EXTRA_DIST = $(zbar_jar_SRCS) $(test_SRCS) + +CLEANFILES = zbar.jar $(nodist_libzbarjni_la_SOURCES) $(zbar_jar_CLASSES) $(test_CLASSES:=.class) + +if HAVE_JAVAH + +# Works up to Java 8 +zbarjni.h: $(zbar_jar_SRCS) zbar.jar + classes=`echo $(zbar_jar_CLASSES:.class=) | tr / .` ; \ + $(JAVAH) -o $@ $$classes + +else + +# After Java 8, it is not possible anymore to build single zbarjni.h +# As we don't want to break ABI, we need to join several .h files into one + +PKGH = ${shell echo ${PKG}|sed s,/,_,g} + +zbarjni.h: $(zbar_jar_SRCS) + $(JAVAC) -h $(abs_builddir) $(abs_srcdir)/$(PKG)/*.java + cat $(abs_builddir)/$(PKGH)_*.h > $(abs_builddir)/zbarjni.h + rm $(abs_builddir)/$(PKGH)_*.h + +endif + +zbar.jar: $(zbar_jar_SRCS) + cd $(abs_srcdir); $(JAVAC) -d $(abs_builddir) $(zbar_jar_SRCS) + $(JAR) cf $@ $(zbar_jar_CLASSES) || $(RM) $@ + +#require junit java package +check-java: zbar.jar libzbarjni.la + echo "making check in java" + cd $(abs_srcdir); $(JAVAC) -classpath $(abs_builddir)/zbar.jar:.:$(CLASSPATH) -d $(abs_builddir) $(test_SRCS) + $(abs_top_builddir)/libtool -dlopen $(abs_top_builddir)/zbar/libzbar.la -dlopen $(abs_builddir)/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 index 0000000..019ea6c --- /dev/null +++ b/java/net/sourceforge/zbar/Config.java @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------ + * Config + * + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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 index 0000000..8227a4f --- /dev/null +++ b/java/net/sourceforge/zbar/Image.java @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------ + * Image + * + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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 index 0000000..02a2a1d --- /dev/null +++ b/java/net/sourceforge/zbar/ImageScanner.java @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------ + * ImageScanner + * + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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 index 0000000..451bb2e --- /dev/null +++ b/java/net/sourceforge/zbar/Modifier.java @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------ + * Modifier + * + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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 index 0000000..6643695 --- /dev/null +++ b/java/net/sourceforge/zbar/Orientation.java @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------ + * Orientation + * + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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 index 0000000..54f40f5 --- /dev/null +++ b/java/net/sourceforge/zbar/Symbol.java @@ -0,0 +1,199 @@ +/*------------------------------------------------------------------------ + * Symbol + * + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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; + /** Codabar. */ + public static final int CODABAR = 38; + /** Code 39. */ + public static final int CODE39 = 39; + /** PDF417. */ + public static final int PDF417 = 57; + /** QR Code. */ + public static final int QRCODE = 64; + /** Code 93. */ + public static final int CODE93 = 93; + /** 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 index 0000000..954a5aa --- /dev/null +++ b/java/net/sourceforge/zbar/SymbolIterator.java @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------ + * SymbolIterator + * + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Iterator over a SymbolSet. + */ +public class SymbolIterator + implements java.util.Iterator<Symbol> +{ + /** 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 index 0000000..1c9110a --- /dev/null +++ b/java/net/sourceforge/zbar/SymbolSet.java @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------ + * SymbolSet + * + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +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<Symbol> +{ + /** 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<Symbol> 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 index 0000000..9666f2b --- /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 index 0000000..0d8eccc --- /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 index 0000000..86ed024 --- /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<Symbol> 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 index 0000000..3506a32 --- /dev/null +++ b/java/zbarjni.c @@ -0,0 +1,618 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#include <assert.h> +#include <inttypes.h> +#include <jni.h> +#include <zbar.h> + +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; + + 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/COPYING.LIB b/perl/COPYING.LIB new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/perl/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; 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. + + This library 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/perl/Changes b/perl/Changes new file mode 100644 index 0000000..6707766 --- /dev/null +++ b/perl/Changes @@ -0,0 +1,51 @@ +Revision history for Perl extension Barcode::ZBar. + +0.10 2022-02-08 Official Release to CPAN + * Barcode::ZBar 0.10 update version + +0.09 2022-02-08 + * bdf1a10 Barcode::ZBar 0.09 update version and Changes + * 08ac997 Barcode::ZBar fix test + +0.08 2022-02-07 + * 0a2fa55 Barcode::ZBar update version for release + * 889cc66 perl skip more tests if DISPLAY not set and set prereqs in Makefile.PL + * c77594e Barcode::ZBar Update Changes and Increment Version + +0.07 2022-02-06 + * d0428ef Barcode::ZBar - Image::Magick not installed skip 3 more tests + * 576355e Fixes rt.cpan.org 122061 - test fails when DISPLAY not set + * e9a0cf2 Update Barcode::ZBar Changes + +0.06 2022-02-06 + * 93564a5 Add provides and min-perl version to meta files + * b01a86f perl module Increment version for cpan release and update changes + +0.05 2022-02-05 - Updates from mchehab/zbar + * 0d1d582 perl some packaging improvements + * 5b3c33d Enforce a coding style + * 2b841d5 Update ZBar's main URL location + * d1397ff Fix typos found by codespell. + * edcf08b Add support for using versions with major.minor.patch + * cd5b63e Update to the very latest version of 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 + +0.01 2009-02-28 spadix + * add Barcode:: namespace prefix + * add a few new/missing APIs + * add most documentation + * first draft: Processor, Scanner and Decoder basic function diff --git a/perl/MANIFEST b/perl/MANIFEST new file mode 100644 index 0000000..8be0421 --- /dev/null +++ b/perl/MANIFEST @@ -0,0 +1,26 @@ +Changes +COPYING.LIB +examples/paginate.pl +examples/processor.pl +examples/read_one.pl +examples/scan_image.pl +inc/Devel/CheckLib.pm +Makefile.PL +MANIFEST This list of files +ppport.h +README +t/barcode.png +t/Decoder.t +t/Image.t +t/pod-coverage.t +t/pod.t +t/Processor.t +t/Scanner.t +t/ZBar.t +typemap +ZBar.pm +ZBar.xs +ZBar/Image.pod +ZBar/ImageScanner.pod +ZBar/Processor.pod +ZBar/Symbol.pod diff --git a/perl/MANIFEST.SKIP b/perl/MANIFEST.SKIP new file mode 100644 index 0000000..a888bf9 --- /dev/null +++ b/perl/MANIFEST.SKIP @@ -0,0 +1,4 @@ +MANIFEST.bak +MANIFEST.SKIP +MYMETA.* +^Makefile$ diff --git a/perl/Makefile.PL b/perl/Makefile.PL new file mode 100644 index 0000000..3a4ddf0 --- /dev/null +++ b/perl/Makefile.PL @@ -0,0 +1,64 @@ +use 5.006; +use ExtUtils::MakeMaker; + +use lib qw(inc); +use Devel::CheckLib; + +check_lib_or_exit( + lib => 'zbar', + header => 'zbar.h', + LIBS => join(' ', map({ /^LIBS=(.*)/; $1 } grep(/^LIBS=/, @ARGV))), + INC => join(' ', map({ /^INC=(.*)/; $1 } grep(/^INC=/, @ARGV))), +); + +WriteMakefile( + NAME => 'Barcode::ZBar', + VERSION_FROM => "ZBar.pm", + ABSTRACT_FROM => "ZBar.pm", + AUTHOR => 'Jeff Brown <spadix@users.sourceforge.net>', + LICENSE => 'lgpl_2_1', + LIBS => ['-lzbar'], + MIN_PERL_VERSION => '5.006', + META_MERGE => { + "meta-spec" => { + version => '2', + url => 'https://metacpan.org/pod/CPAN::Meta::Spec', + }, + prereqs => { + build => { + requires => { + 'Test::More' => 0, + }, + }, + test => { + recommends => { + 'Image::Magick' => 0, + }, + }, + develop => { + recommends => { + 'Test::Pod::Coverage' => 0, + 'Test::Pod' => 0, + }, + } + }, + resources => { + homepage => 'https://metacpan.org/pod/Barcode::ZBar/', + repository => { + type => 'git', + url => 'https://github.com/mchehab/zbar.git', + web => 'https://github.com/mchehab/zbar', + } + }, + provides => { + 'Barcode::ZBar' => { + file => 'ZBar.pm', + version => '0.10', + }, + 'Barcode::ZBar::Error' => { + file => 'ZBar.pm', + version => '0.10', + }, + } + }, +); diff --git a/perl/README b/perl/README new file mode 100644 index 0000000..85758fd --- /dev/null +++ b/perl/README @@ -0,0 +1,38 @@ +Barcode::ZBar Perl module +========================= + +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, Codabar, 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. + https://github.com/mchehab/zbar + + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + + +DEPENDENCIES + +This module requires the ZBar Bar Code Reader, which may be obtained +from: + + https://github.com/mchehab/zbar + + +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-2010 (c) Jeff Brown <spadix@users.sourceforge.net> diff --git a/perl/ZBar.pm b/perl/ZBar.pm new file mode 100644 index 0000000..c9c474e --- /dev/null +++ b/perl/ZBar.pm @@ -0,0 +1,216 @@ +#------------------------------------------------------------------------ +# Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ +package Barcode::ZBar; + +use 5.006; +use strict; +use warnings; +use Carp; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(SPACE BAR + version increase_verbosity set_verbosity); + +our $VERSION = '0.10'; + +require XSLoader; +XSLoader::load('Barcode::ZBar', $VERSION); + +package Barcode::ZBar::Error; + +use overload '""' => sub { return($_[0]->error_string()) }; + +1; +__END__ + + +=head1 NAME + +Barcode::ZBar - Perl interface to the ZBar Barcode Reader + + +=head1 SYNOPSIS + +setup: + + use Barcode::ZBar; + + my $reader = Barcode::ZBar::Processor->new(); + $reader->init(); + $reader->set_data_handler(\&my_handler); + +scan an image: + + my $image = Barcode::ZBar::Image->new(); + $image->set_format('422P'); + $image->set_size(114, 80); + $image->set_data($raw_bits); + $reader->process_image($image); + +scan from video: + + $reader->set_visible(); + $reader->set_active(); + $reader->user_wait(); + +collect results: + + my @symbols = $image->get_symbols(); + foreach my $sym (@symbols) { + print("decoded: " . $sym->get_type() . ":" . $sym->get_data()); + } + + +=head1 DESCRIPTION + +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-13/UPC-A, UPC-E, EAN-8, Code 128, +Code 93, Code 39, Codabar, Interleaved 2 of 5 and QR Code. + +These are the bindings for interacting directly with the library from +Perl. + + +=head1 REFERENCE + +=head2 Functions + +=over 4 + +=item version() + +Returns the version of the zbar library as "I<major>.I<minor>". + +=item increase_verbosity() + +Increases global library debug by one level. + +=item set_verbosity(I<level>) + +Sets global library debug to the indicated level. Higher numbers give +more verbosity. + +=item parse_config(I<configstr>) + +Parse a decoder configuration setting into a list containing the +symbology constant, config constant, and value to set. See the +documentation for C<zbarcam>/C<zbarimg> for available configuration +options. + +=back + +=head2 Constants + +Width stream "color" constants: + +=over 4 + +=item SPACE + +Light area or space between bars. + +=item BAR + +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 + +Barcode::ZBar::Processor, Barcode::ZBar::ImageScanner, +Barcode::ZBar::Image, Barcode::ZBar::Symbol, +Barcode::ZBar::Scanner, Barcode::ZBar::Decoder + +zbarimg(1), zbarcam(1) + +http://zbar.sf.net + + +=head1 AUTHOR + +Jeff Brown, E<lt>spadix@users.sourceforge.netE<gt> + + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008-2010 (c) Jeff Brown E<lt>spadix@users.sourceforge.netE<gt> + +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. + +=cut diff --git a/perl/ZBar.xs b/perl/ZBar.xs new file mode 100644 index 0000000..0e340f6 --- /dev/null +++ b/perl/ZBar.xs @@ -0,0 +1,869 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "ppport.h" + +#include <zbar.h> + +typedef zbar_symbol_t *Barcode__ZBar__Symbol; +typedef zbar_image_t *Barcode__ZBar__Image; +typedef zbar_processor_t *Barcode__ZBar__Processor; +typedef zbar_video_t *Barcode__ZBar__Video; +typedef zbar_window_t *Barcode__ZBar__Window; +typedef zbar_image_scanner_t *Barcode__ZBar__ImageScanner; +typedef zbar_decoder_t *Barcode__ZBar__Decoder; +typedef zbar_scanner_t *Barcode__ZBar__Scanner; +typedef void *Barcode__ZBar__Error; + +typedef unsigned long fourcc_t; +typedef int timeout_t; +typedef int config_error; + +typedef struct handler_wrapper_s { + SV *instance; + SV *handler; + SV *closure; +} handler_wrapper_t; + + +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 { \ + SV *c = newSViv(ZBAR_ ## prefix ## sym); \ + sv_setpv(c, name); \ + SvIOK_on(c); \ + newCONSTSUB(stash, #sym, c); \ + av_store(LOOKUP_zbar_ ## typ ## _t, \ + ZBAR_ ## prefix ## sym, \ + SvREFCNT_inc(c)); \ + } while(0) + +#define LOOKUP_ENUM(typ, val) \ + lookup_enum(LOOKUP_zbar_ ## typ ## _t, val) + +static inline SV *lookup_enum (AV *lookup, int val) +{ + SV **tmp = av_fetch(lookup, val, 0); + return((tmp) ? *tmp : sv_newmortal()); +} + +static inline void check_error (int rc, void *obj) +{ + if(rc < 0) { + sv_setref_pv(get_sv("@", TRUE), "Barcode::ZBar::Error", obj); + croak(NULL); + } +} + +#define PUSH_SYMS(x) \ + do { \ + const zbar_symbol_t *sym = (const zbar_symbol_t*)(x); \ + for(; sym; sym = zbar_symbol_next(sym)) { \ + zbar_symbol_t *s = (zbar_symbol_t*)sym; \ + zbar_symbol_ref(s, 1); \ + XPUSHs(sv_setref_pv(sv_newmortal(), "Barcode::ZBar::Symbol", \ + (void*)sym)); \ + } \ + } 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); + if(!data) + /* FIXME this is internal error */ + return; + + /* release reference to cleanup data */ + SvREFCNT_dec(data); +} + +static inline int set_handler (handler_wrapper_t **wrapp, + SV *instance, + SV *handler, + SV *closure) +{ + handler_wrapper_t *wrap = *wrapp; + if(!handler || !SvOK(handler)) { + if(wrap) { + if(wrap->instance) SvREFCNT_dec(wrap->instance); + if(wrap->handler) SvREFCNT_dec(wrap->handler); + if(wrap->closure) SvREFCNT_dec(wrap->closure); + wrap->instance = wrap->handler = wrap->closure = NULL; + } + return(0); + } + + if(!wrap) { + Newxz(wrap, 1, handler_wrapper_t); + wrap->instance = newSVsv(instance); + wrap->closure = newSV(0); + *wrapp = wrap; + } + + if(wrap->handler) + SvSetSV(wrap->handler, handler); + else + wrap->handler = newSVsv(handler); + + if(!closure || !SvOK(closure)) + SvSetSV(wrap->closure, &PL_sv_undef); + else + SvSetSV(wrap->closure, closure); + return(1); +} + +static inline void activate_handler (handler_wrapper_t *wrap, + SV *param) +{ + dSP; + if(!wrap) + /* FIXME this is internal error */ + return; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + EXTEND(SP, 3); + PUSHs(sv_mortalcopy(wrap->instance)); + if(param) + PUSHs(param); + PUSHs(sv_mortalcopy(wrap->closure)); + PUTBACK; + + call_sv(wrap->handler, G_DISCARD); + + FREETMPS; + LEAVE; +} + +static void processor_handler (zbar_image_t *image, + const void *userdata) +{ + SV *img; + zbar_image_ref(image, 1); + img = sv_setref_pv(newSV(0), "Barcode::ZBar::Image", image); + activate_handler((void*)userdata, img); + SvREFCNT_dec(img); +} + +static void decoder_handler (zbar_decoder_t *decoder) +{ + activate_handler(zbar_decoder_get_userdata(decoder), NULL); +} + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar PREFIX = zbar_ + +PROTOTYPES: ENABLE + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar", TRUE); + + LOOKUP_zbar_color_t = newAV(); + CONSTANT(color, , SPACE, "SPACE"); + CONSTANT(color, , BAR, "BAR"); + } + +SV * +zbar_version() + PREINIT: + unsigned major; + unsigned minor; + CODE: + zbar_version(&major, &minor, NULL); + RETVAL = newSVpvf("%u.%u", major, minor); + OUTPUT: + RETVAL + +void +zbar_increase_verbosity() + +void +zbar_set_verbosity(verbosity) + int verbosity + +SV * +parse_config(config_string) + const char * config_string + PREINIT: + zbar_symbol_type_t sym; + zbar_config_t cfg; + int val; + PPCODE: + if(zbar_parse_config(config_string, &sym, &cfg, &val)) + croak("invalid configuration setting: %s", config_string); + EXTEND(SP, 3); + PUSHs(LOOKUP_ENUM(symbol_type, sym)); + PUSHs(LOOKUP_ENUM(config, cfg)); + mPUSHi(val); + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Error PREFIX = zbar_ + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar::Error", TRUE); + + LOOKUP_zbar_error_t = newAV(); + CONSTANT(error, ERR_, NOMEM, "out of memory"); + CONSTANT(error, ERR_, INTERNAL, "internal library error"); + CONSTANT(error, ERR_, UNSUPPORTED, "unsupported request"); + CONSTANT(error, ERR_, INVALID, "invalid request"); + CONSTANT(error, ERR_, SYSTEM, "system error"); + CONSTANT(error, ERR_, LOCKING, "locking error"); + CONSTANT(error, ERR_, BUSY, "all resources busy"); + CONSTANT(error, ERR_, XDISPLAY, "X11 display error"); + CONSTANT(error, ERR_, XPROTO, "X11 protocol error"); + CONSTANT(error, ERR_, CLOSED, "output window is closed"); + CONSTANT(error, ERR_, WINAPI, "windows system error"); + } + +zbar_error_t +get_error_code(err) + Barcode::ZBar::Error err + CODE: + RETVAL = _zbar_get_error_code(err); + OUTPUT: + RETVAL + +const char * +error_string(err) + Barcode::ZBar::Error err + CODE: + RETVAL = _zbar_error_string(err, 1); + OUTPUT: + RETVAL + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Config PREFIX = zbar_config_ + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar::Config", TRUE); + + LOOKUP_zbar_config_t = newAV(); + CONSTANT(config, CFG_, ENABLE, "enable"); + CONSTANT(config, CFG_, ADD_CHECK, "add-check"); + CONSTANT(config, CFG_, EMIT_CHECK, "emit-check"); + 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_ + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar::Symbol", TRUE); + + LOOKUP_zbar_symbol_type_t = newAV(); + CONSTANT(symbol_type, , NONE, "None"); + CONSTANT(symbol_type, , PARTIAL, "Partial"); + CONSTANT(symbol_type, , EAN8, zbar_get_symbol_name(ZBAR_EAN8)); + CONSTANT(symbol_type, , UPCE, zbar_get_symbol_name(ZBAR_UPCE)); + CONSTANT(symbol_type, , ISBN10, zbar_get_symbol_name(ZBAR_ISBN10)); + 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, , CODABAR, zbar_get_symbol_name(ZBAR_CODABAR)); + 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)); + } + +void +DESTROY(symbol) + Barcode::ZBar::Symbol symbol + CODE: + zbar_symbol_ref(symbol, -1); + +zbar_symbol_type_t +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: + RETVAL = newSVpvn(zbar_symbol_get_data(symbol), + zbar_symbol_get_data_length(symbol)); + OUTPUT: + RETVAL + +int +zbar_symbol_get_count(symbol) + Barcode::ZBar::Symbol symbol + +int +zbar_symbol_get_quality(symbol) + Barcode::ZBar::Symbol symbol + +SV * +zbar_symbol_get_loc(symbol) + Barcode::ZBar::Symbol symbol + PREINIT: + unsigned i, size; + PPCODE: + size = zbar_symbol_get_loc_size(symbol); + EXTEND(SP, size); + for(i = 0; i < size; i++) { + AV *pt = (AV*)sv_2mortal((SV*)newAV()); + PUSHs(newRV((SV*)pt)); + av_push(pt, newSVuv(zbar_symbol_get_loc_x(symbol, i))); + 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 + PPCODE: + PUSH_SYMS(zbar_symbol_first_component(symbol)); + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Image PREFIX = zbar_image_ + +Barcode::ZBar::Image +new(package) + char * package + CODE: + RETVAL = zbar_image_create(); + OUTPUT: + RETVAL + +void +DESTROY(image) + Barcode::ZBar::Image image + CODE: + zbar_image_destroy(image); + +Barcode::ZBar::Image +zbar_image_convert(image, format) + Barcode::ZBar::Image image + fourcc_t format + +Barcode::ZBar::Image +zbar_image_convert_resize(image, format, width, height) + Barcode::ZBar::Image image + fourcc_t format + unsigned width + unsigned height + +fourcc_t +zbar_image_get_format(image) + Barcode::ZBar::Image image + +unsigned +zbar_image_get_sequence(image) + Barcode::ZBar::Image image + +void +get_size(image) + Barcode::ZBar::Image image + PPCODE: + EXTEND(SP, 2); + 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 + CODE: + RETVAL = newSVpvn(zbar_image_get_data(image), + zbar_image_get_data_length(image)); + OUTPUT: + RETVAL + +SV * +get_symbols(image) + Barcode::ZBar::Image image + PPCODE: + PUSH_SYMS(zbar_image_first_symbol(image)); + +void +zbar_image_set_format(image, format) + Barcode::ZBar::Image image + fourcc_t format + +void +zbar_image_set_sequence(image, seq_num) + Barcode::ZBar::Image image + unsigned seq_num + +void +zbar_image_set_size(image, width, height) + Barcode::ZBar::Image image + 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) + Barcode::ZBar::Image image + SV * data + PREINIT: + SV *old; + CODE: + if(!data || !SvOK(data)) { + zbar_image_set_data(image, NULL, 0, NULL); + zbar_image_set_userdata(image, NULL); + } + else if(SvPOK(data)) { + /* FIXME is this copy of data or new ref to same data? + * not sure this is correct: + * need to retain a reference to image data, + * but do not really want to copy it...maybe an RV? + */ + SV *copy = newSVsv(data); + STRLEN len; + void *raw = SvPV(copy, len); + zbar_image_set_data(image, raw, len, image_cleanup_handler); + zbar_image_set_userdata(image, copy); + } + else + croak("image data must be binary string"); + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Processor PREFIX = zbar_processor_ + +Barcode::ZBar::Processor +new(package, threaded=0) + char * package + bool threaded + CODE: + RETVAL = zbar_processor_create(threaded); + OUTPUT: + RETVAL + +void +DESTROY(processor) + Barcode::ZBar::Processor processor + CODE: + zbar_processor_destroy(processor); + +void +zbar_processor_init(processor, video_device="", enable_display=1) + Barcode::ZBar::Processor processor + const char * video_device + bool enable_display + CODE: + check_error(zbar_processor_init(processor, video_device, enable_display), + processor); + +void +zbar_processor_request_size(processor, width, height) + Barcode::ZBar::Processor processor + unsigned width + unsigned height + CODE: + check_error(zbar_processor_request_size(processor, width, height), + processor); + +void +zbar_processor_force_format(processor, input_format=0, output_format=0) + Barcode::ZBar::Processor processor + fourcc_t input_format + fourcc_t output_format + CODE: + check_error(zbar_processor_force_format(processor, input_format, output_format), + processor); + +void +zbar_processor_set_config(processor, symbology, config, value=1) + Barcode::ZBar::Processor processor + zbar_symbol_type_t symbology + zbar_config_t config + int value + +config_error +zbar_processor_parse_config(processor, config_string) + Barcode::ZBar::Processor processor + const char *config_string + +bool +zbar_processor_is_visible(processor) + Barcode::ZBar::Processor processor + CODE: + check_error((RETVAL = zbar_processor_is_visible(processor)), + processor); + OUTPUT: + RETVAL + +void +zbar_processor_set_visible(processor, visible=1) + Barcode::ZBar::Processor processor + bool visible + CODE: + check_error(zbar_processor_set_visible(processor, visible), + processor); + +void +zbar_processor_set_active(processor, active=1) + Barcode::ZBar::Processor processor + bool active + CODE: + check_error(zbar_processor_set_active(processor, active), + processor); + +SV * +get_results(processor) + Barcode::ZBar::Processor processor + PREINIT: + const zbar_symbol_set_t *syms; + PPCODE: + syms = zbar_processor_get_results(processor); + PUSH_SYMS(zbar_symbol_set_first_symbol(syms)); + zbar_symbol_set_ref(syms, -1); + +int +zbar_processor_user_wait(processor, timeout=-1) + Barcode::ZBar::Processor processor + timeout_t timeout + CODE: + check_error((RETVAL = zbar_processor_user_wait(processor, timeout)), + processor); + OUTPUT: + RETVAL + +int +process_one(processor, timeout=-1) + Barcode::ZBar::Processor processor + timeout_t timeout + CODE: + check_error((RETVAL = zbar_process_one(processor, timeout)), + processor); + OUTPUT: + RETVAL + +int +process_image(processor, image) + Barcode::ZBar::Processor processor + Barcode::ZBar::Image image + CODE: + check_error((RETVAL = zbar_process_image(processor, image)), + processor); + OUTPUT: + RETVAL + +void +zbar_processor_set_data_handler(processor, handler = 0, closure = 0) + Barcode::ZBar::Processor processor + SV * handler + SV * closure + PREINIT: + handler_wrapper_t *wrap; + zbar_image_data_handler_t *callback = NULL; + CODE: + wrap = zbar_processor_get_userdata(processor); + if(set_handler(&wrap, ST(0), handler, closure)) + callback = processor_handler; + zbar_processor_set_data_handler(processor, callback, wrap); + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::ImageScanner PREFIX = zbar_image_scanner_ + +Barcode::ZBar::ImageScanner +new(package) + char * package + CODE: + RETVAL = zbar_image_scanner_create(); + OUTPUT: + RETVAL + +void +DESTROY(scanner) + Barcode::ZBar::ImageScanner scanner + CODE: + zbar_image_scanner_destroy(scanner); + +void +zbar_image_scanner_set_config(scanner, symbology, config, value=1) + Barcode::ZBar::ImageScanner scanner + zbar_symbol_type_t symbology + zbar_config_t config + int value + +config_error +zbar_image_scanner_parse_config(scanner, config_string) + Barcode::ZBar::ImageScanner scanner + const char *config_string + +void +zbar_image_scanner_enable_cache(scanner, enable) + Barcode::ZBar::ImageScanner scanner + int enable + +void +zbar_image_scanner_recycle_image(scanner, image) + Barcode::ZBar::ImageScanner scanner + Barcode::ZBar::Image image + +SV * +get_results(scanner) + Barcode::ZBar::ImageScanner scanner + PREINIT: + const zbar_symbol_set_t *syms; + PPCODE: + syms = zbar_image_scanner_get_results(scanner); + PUSH_SYMS(zbar_symbol_set_first_symbol(syms)); + +int +scan_image(scanner, image) + Barcode::ZBar::ImageScanner scanner + Barcode::ZBar::Image image + CODE: + RETVAL = zbar_scan_image(scanner, image); + OUTPUT: + RETVAL + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Decoder PREFIX = zbar_decoder_ + +Barcode::ZBar::Decoder +new(package) + char * package + CODE: + RETVAL = zbar_decoder_create(); + OUTPUT: + RETVAL + +void +DESTROY(decoder) + Barcode::ZBar::Decoder decoder + CODE: + /* FIXME cleanup handler wrapper */ + zbar_decoder_destroy(decoder); + +void +zbar_decoder_set_config(decoder, symbology, config, value=1) + Barcode::ZBar::Decoder decoder + zbar_symbol_type_t symbology + zbar_config_t config + int value + +config_error +zbar_decoder_parse_config(decoder, config_string) + Barcode::ZBar::Decoder decoder + const char *config_string + +void +zbar_decoder_reset(decoder) + Barcode::ZBar::Decoder decoder + +void +zbar_decoder_new_scan(decoder) + Barcode::ZBar::Decoder decoder + +zbar_symbol_type_t +decode_width(decoder, width) + Barcode::ZBar::Decoder decoder + unsigned width + CODE: + RETVAL = zbar_decode_width(decoder, width); + OUTPUT: + RETVAL + +zbar_color_t +zbar_decoder_get_color(decoder) + Barcode::ZBar::Decoder decoder + +SV * +zbar_decoder_get_data(decoder) + Barcode::ZBar::Decoder decoder + CODE: + RETVAL = newSVpvn(zbar_decoder_get_data(decoder), + zbar_decoder_get_data_length(decoder)); + OUTPUT: + RETVAL + +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 + SV * handler + SV * closure + PREINIT: + handler_wrapper_t *wrap; + CODE: + wrap = zbar_decoder_get_userdata(decoder); + zbar_decoder_set_handler(decoder, NULL); + if(set_handler(&wrap, ST(0), handler, closure)) { + zbar_decoder_set_userdata(decoder, wrap); + zbar_decoder_set_handler(decoder, decoder_handler); + } + + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Scanner PREFIX = zbar_scanner_ + +Barcode::ZBar::Scanner +new(package, decoder = 0) + char * package + Barcode::ZBar::Decoder decoder + CODE: + RETVAL = zbar_scanner_create(decoder); + OUTPUT: + RETVAL + +void +DESTROY(scanner) + Barcode::ZBar::Scanner scanner + CODE: + zbar_scanner_destroy(scanner); + +zbar_symbol_type_t +zbar_scanner_reset(scanner) + Barcode::ZBar::Scanner scanner + +zbar_symbol_type_t +zbar_scanner_new_scan(scanner) + Barcode::ZBar::Scanner scanner + +zbar_color_t +zbar_scanner_get_color(scanner) + Barcode::ZBar::Scanner scanner + +unsigned +zbar_scanner_get_width(scanner) + Barcode::ZBar::Scanner scanner + +zbar_symbol_type_t +scan_y(scanner, y) + Barcode::ZBar::Scanner scanner + int y + CODE: + RETVAL = zbar_scan_y(scanner, y); + OUTPUT: + RETVAL diff --git a/perl/ZBar/Image.pod b/perl/ZBar/Image.pod new file mode 100644 index 0000000..6848bdd --- /dev/null +++ b/perl/ZBar/Image.pod @@ -0,0 +1,145 @@ +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +=pod + +=head1 NAME + +Barcode::ZBar::Image - image object to scan for bar codes + +=head1 SYNOPSIS + + use Barcode::ZBar; + + my $image = Barcode::ZBar::Image->new(); + $image->set_format('422P'); + $image->set_size(114, 80); + $image->set_data($raw_bits); + + my @symbols = $image->get_symbols(); + +=head1 DESCRIPTION + +Barcode::ZBar::Image is used to pass images to the bar code scanner. +It wraps raw image data with the meta-data required to interpret it +(size, pixel format, etc) + +=head2 Image Formats + +Image data formats are represented by (relatively) standard "Four +Character Codes" (fourcc), represented by four character strings in +Perl. A list of supported formats is available on the project wiki. + +Examples: + +=over 2 + +=item * + +'GREY' - single 8bpp intensity plane + +=item * + +'BGR3' - 24bpp packed RGB component format + +=item * + +'YUYV' - 12bpp packed luminance/chrominance (YCbCr) format + +=back + +=head1 REFERENCE + +=head2 Methods + +=over 4 + +=item new() + +Create a new Barcode::ZBar::Image object. The size, pixel format and +data must be defined before the object may be used. + +=item get_format() + +=item set_format(I<format>) + +Return/specify the fourcc code corresponding to the image pixel format. + +=item get_sequence() + +=item set_sequence(I<seq_num>) + +Return/specify the video frame or page number associated with the image. + +=item get_size() + +=item set_size(I<width>, I<height>) + +Return/specify the (I<width>, I<height>) image size tuple. + +=item get_data() + +=item set_data(I<raw>) + +Return/specify the raw image data as a binary string. + +=item get_symbols() + +Return a list of scanned Barcode::ZBar::Symbol results attached to +this image. + +=item convert_resize(I<format>, I<width>, I<height>) + +=item convert(I<format>) + +Return a new Barcode::ZBar::Image object converted to the indicated +fourcc format. Returns C<undef> if the conversion is not supported. +Conversion complexity ranges from CPU intensive to trivial depending +on the formats involved. Note that only a few conversions retain +color information. convert actually calls convert_resize using the +source width and height. + +=back + +=head1 SEE ALSO + +Barcode::ZBar, Barcode::ZBar::Image, Barcode::ZBar::Symbol + +zbarimg(1), zbarcam(1) + +http://zbar.sf.net + +=head1 AUTHOR + +Jeff Brown, E<lt>spadix@users.sourceforge.netE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008-2010 (c) Jeff Brown E<lt>spadix@users.sourceforge.netE<gt> + +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. + +=cut diff --git a/perl/ZBar/ImageScanner.pod b/perl/ZBar/ImageScanner.pod new file mode 100644 index 0000000..dcbc21c --- /dev/null +++ b/perl/ZBar/ImageScanner.pod @@ -0,0 +1,103 @@ +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +=pod + +=head1 NAME + +Barcode::ZBar::ImageScanner - scan images for bar codes + +=head1 SYNOPSIS + + use Barcode::ZBar; + + my $scanner = Barcode::ZBar::ImageScanner->new(); + $scanner->parse_config('i25.disable'); + $scanner->scan_image($image); + +=head1 DESCRIPTION + +A Barcode::ZBar::ImageScanner is used to scan for bar codes in a +Barcode::ZBar::Image. + +=head1 REFERENCE + +=head2 Methods + +=over 4 + +=item new() + +Create a new bar code image scanner instance. + +=item get_results() + +Return a list of Barcode::ZBar::Symbol results from the last scanned +image. + +=item scan_image([I<image>]) + +Scan a Barcode::ZBar::Image for bar codes. The image must be in the +"Y800" format. If necessary, use C<< I<$image>->convert("Y800") >> to +convert from other supported formats to Y800 before scanning. + +=item enable_cache([I<enable>]) + +Enable the inter-image result consistency cache. + +=item set_config(I<symbology>, I<config>, I<value>) + +Set config for indicated symbology (0 for all) to specified value. + +=item parse_config(I<configstr>) + +Apply a decoder configuration setting. See the documentation for +C<zbarcam>/C<zbarimg> for available configuration options. + +=item recycle_image([I<image>]) + +Remove previously decoded results from a Barcode::ZBar::Image and +recycle the associated memory. + +=back + +=head1 SEE ALSO + +Barcode::ZBar, Barcode::ZBar::Image, zbarimg(1), zbarcam(1) + +http://zbar.sf.net + +=head1 AUTHOR + +Jeff Brown, E<lt>spadix@users.sourceforge.netE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008-2010 (c) Jeff Brown E<lt>spadix@users.sourceforge.netE<gt> + +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. + +=cut diff --git a/perl/ZBar/Processor.pod b/perl/ZBar/Processor.pod new file mode 100644 index 0000000..96b1982 --- /dev/null +++ b/perl/ZBar/Processor.pod @@ -0,0 +1,157 @@ +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +=pod + +=head1 NAME + +Barcode::ZBar::Processor - self-contained bar code reader + +=head1 SYNOPSIS + +setup: + + use Barcode::ZBar; + + my $reader = Barcode::ZBar::Processor->new(); + $reader->init("/dev/video1", 1); + $reader->parse_config('code39.disable'); + $reader->set_data_handler(\&my_handler); + +scan an image: + + $reader->process_image($image); + +scan from video: + + $reader->set_visible(); + $reader->set_active(); + $reader->user_wait(); + +=head1 DESCRIPTION + +A Barcode::ZBar::Processor may be used to quickly create stand-alone +bar code scanning applications. It has interfaces to scan images or +video and to optionally display a video/image preview to a window. + +This interface is not well suited for integration with an existing +GUI, as the library manages the optional preview window and any user +interaction. Use a Barcode::ZBar::ImageScanner or Investigate the +available widget interfaces for GUI applications. + +=head1 REFERENCE + +=head2 Methods + +=over 4 + +=item new() + +Create a new bar code reader instance. + +=item init([I<video_device>], [I<enable_display>]) + +Open a video input device and/or prepare to display output. + +=item set_data_handler([I<handler>], [I<closure>]) + +Setup a callback to process results whenever new results are available +from the video stream or a static image. The specified callable will +be invoked with the associated Barcode::ZBar::Processor object and +I<closure> as arguments. Closure may be achieved either using +standard Perl closure or by manually passing a scalar via I<closure>. + +=item is_visible() + +=item set_visible([I<visible>]) + +Test/set visibility of the output window. + +=item set_active([I<active>]) + +Enable/disable video streaming and scanning for bar codes. + +=item get_results() + +Return a list of Barcode::ZBar::Symbol results from the last scanned +image or video frame. + +=item user_wait([I<timeout>]) + +Wait for the user to press a key/button or close the window. Bar +codes will continue to be processed if video is active. + +=item process_one([I<timeout>]) + +Enable video and scan until at least one barcode is found. Note that +multiple results may still be returned. + +=item process_image([I<image>]) + +Scan a Barcode::ZBar::Image for bar codes. + +=item parse_config(I<configstr>) + +Apply a decoder configuration setting. See the documentation for +C<zbarcam>/C<zbarimg> for available configuration options. + +=item request_size(I<width>, I<height>) + +Request a preferred size for the video image from the device. The +request may be adjusted or completely ignored by the driver. Must be +called before C<init()> + +=item force_format(I<input>, I<output>) + +force specific input and output formats for debug/testing. + +=item set_config(I<symbology>, I<config>, I<value>) + +Set config for indicated symbology (0 for all) to specified value. +@returns 0 for success, non-0 for failure (config does not apply to +specified symbology, or value out of range) + +=back + +=head1 SEE ALSO + +Barcode::ZBar, Barcode::ZBar::Image, Barcode::ZBar::ImageScanner + +zbarimg(1), zbarcam(1) + +http://zbar.sf.net + +=head1 AUTHOR + +Jeff Brown, E<lt>spadix@users.sourceforge.netE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008-2010 (c) Jeff Brown E<lt>spadix@users.sourceforge.netE<gt> + +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. + +=cut diff --git a/perl/ZBar/Symbol.pod b/perl/ZBar/Symbol.pod new file mode 100644 index 0000000..6a21b1e --- /dev/null +++ b/perl/ZBar/Symbol.pod @@ -0,0 +1,179 @@ +#------------------------------------------------------------------------ +# Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +=pod + +=head1 NAME + +Barcode::ZBar::Symbol - bar code scan result object + +=head1 SYNOPSIS + + my @symbols = $image->get_symbols(); + foreach my $sym (@symbols) { + print("decoded: " . $sym->get_type() . + ":" . $sym->get_data(). + "(" . $sym->get_count() . ")"); + } + +=head1 DESCRIPTION + +Barcode::ZBar::Symbol objects are constant results returned for each +bar code scanned from images or video. This object wraps the raw +symbol data with additional information about the decode (symbology, +confidence, location, etc) + +=head1 REFERENCE + +=head2 Methods + +=over 4 + +=item get_type() + +The type of bar code "symbology" from which the data was decoded. + +=item get_data() + +The decoded data string. Note that some symbologies can encode binary +data. + +=item get_quality() + +Confidence metric. An unscaled integer value that indicates something +(intentionally unspecified) about the reliability of this result +relative to another. Larger values are better than smaller values, +where "large" and "small" are application dependent. Expect this +definition to become more specific as the metric is enhanced. + +=item get_count() + +Current cache count of the symbol. This integer value provides +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 +component symbols that were combined to form a composite result. + +=item get_configs() + +Retrieve symbology boolean config settings. Returns a bitmask +indicating which configs were set for the detected +symbology during decoding. + +=item get_modifiers() + +Retrieve symbology modifier flag settings. Returns a bitmask +indicating which characteristics were detected during decoding. + +=item get_loc() + +Retrieve an array of symbol location points (x,y) + +=over 2 + +=item * + +A negative value indicates that this result is still uncertain + +=item * + +A zero value indicates the first occurrence of this result with high +confidence + +=item * + +A positive value indicates a duplicate scan + +=back + +=back + +=head2 Constants + +Bar code type "symbology" constants: + +=over 4 + +=item NONE + +=item PARTIAL + +=item EAN13 + +=item EAN8 + +=item UPCA + +=item UPCE + +=item ISBN10 + +=item ISBN13 + +=item I25 + +=item CODABAR + +=item CODE39 + +=item CODE93 + +=item CODE128 + +=item QRCODE + +=item PDF417 + +=back + +=head1 SEE ALSO + +Barcode::ZBar, Barcode::ZBar::Image + +zbarimg(1), zbarcam(1) + +http://zbar.sf.net + +=head1 AUTHOR + +Jeff Brown, E<lt>spadix@users.sourceforge.netE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008-2010 (c) Jeff Brown E<lt>spadix@users.sourceforge.netE<gt> + +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. + +=cut diff --git a/perl/examples/paginate.pl b/perl/examples/paginate.pl new file mode 100755 index 0000000..68ffe50 --- /dev/null +++ b/perl/examples/paginate.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +#------------------------------------------------------------------------ +# Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ +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/perl/examples/processor.pl b/perl/examples/processor.pl new file mode 100755 index 0000000..d2d1e91 --- /dev/null +++ b/perl/examples/processor.pl @@ -0,0 +1,35 @@ +#!/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/perl/examples/read_one.pl b/perl/examples/read_one.pl new file mode 100755 index 0000000..d64860c --- /dev/null +++ b/perl/examples/read_one.pl @@ -0,0 +1,29 @@ +#!/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/perl/examples/scan_image.pl b/perl/examples/scan_image.pl new file mode 100755 index 0000000..39d460e --- /dev/null +++ b/perl/examples/scan_image.pl @@ -0,0 +1,37 @@ +#!/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/perl/inc/Devel/CheckLib.pm b/perl/inc/Devel/CheckLib.pm new file mode 100644 index 0000000..be40780 --- /dev/null +++ b/perl/inc/Devel/CheckLib.pm @@ -0,0 +1,350 @@ +# $Id: CheckLib.pm,v 1.22 2008/03/12 19:52:50 drhyde Exp $ + +package Devel::CheckLib; + +use strict; +use vars qw($VERSION @ISA @EXPORT); +$VERSION = '0.5'; +use Config; + +use File::Spec; +use File::Temp; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(assert_lib check_lib_or_exit); + +# localising prevents the warningness leaking out of this module +local $^W = 1; # use warnings is a 5.6-ism + +_findcc(); # bomb out early if there's no compiler + +=head1 NAME + +Devel::CheckLib - check that a library is available + +=head1 DESCRIPTION + +Devel::CheckLib is a perl module that checks whether a particular C +library and its headers are available. + +=head1 SYNOPSIS + + # in a Makefile.PL or Build.PL + use lib qw(inc); + use Devel::CheckLib; + + check_lib_or_exit( lib => 'jpeg', header => 'jpeglib.h' ); + check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] ); + + # or prompt for path to library and then do this: + check_lib_or_exit( lib => 'jpeg', libpath => $additional_path ); + +=head1 HOW IT WORKS + +You pass named parameters to a function, describing to it how to build +and link to the libraries. + +It works by trying to compile this: + + int main(void) { return 0; } + +and linking it to the specified libraries. If something pops out the end +which looks executable, then we know that it worked. That tiny program is +built once for each library that you specify, and (without linking) once +for each header file. + +=head1 FUNCTIONS + +All of these take the same named parameters and are exported by default. +To avoid exporting them, C<use Devel::CheckLib ()>. + +=head2 assert_lib + +This takes several named parameters, all of which are optional, and dies +with an error message if any of the libraries listed can +not be found. B<Note>: dying in a Makefile.PL or Build.PL may provoke +a 'FAIL' report from CPAN Testers' automated smoke testers. Use +C<check_lib_or_exit> instead. + +The named parameters are: + +=over + +=item lib + +Must be either a string with the name of a single +library or a reference to an array of strings of library names. Depending +on the compiler found, library names will be fed to the compiler either as +C<-l> arguments or as C<.lib> file names. (E.g. C<-ljpeg> or C<jpeg.lib>) + +=item libpath + +a string or an array of strings +representing additional paths to search for libraries. + +=item LIBS + +a C<ExtUtils::MakeMaker>-style space-seperated list of +libraries (each preceded by '-l') and directories (preceded by '-L'). + +=back + +And libraries are no use without header files, so ... + +=over + +=item header + +Must be either a string with the name of a single +header file or a reference to an array of strings of header file names. + +=item incpath + +a string or an array of strings +representing additional paths to search for headers. + +=item INC + +a C<ExtUtils::MakeMaker>-style space-seperated list of +incpaths, each preceded by '-I'. + +=back + +=head2 check_lib_or_exit + +This behaves exactly the same as C<assert_lib()> except that instead of +dying, it warns (with exactly the same error message) and exits. +This is intended for use in Makefile.PL / Build.PL +when you might want to prompt the user for various paths and +things before checking that what they've told you is sane. + +If any library or header is missing, it exits with an exit value of 0 to avoid +causing a CPAN Testers 'FAIL' report. CPAN Testers should ignore this +result -- which is what you want if an external library dependency is not +available. + +=cut + +sub check_lib_or_exit { + eval 'assert_lib(@_)'; + if($@) { + warn $@; + exit; + } +} + +sub assert_lib { + my %args = @_; + my (@libs, @libpaths, @headers, @incpaths); + + # FIXME: these four just SCREAM "refactor" at me + @libs = (ref($args{lib}) ? @{$args{lib}} : $args{lib}) + if $args{lib}; + @libpaths = (ref($args{libpath}) ? @{$args{libpath}} : $args{libpath}) + if $args{libpath}; + @headers = (ref($args{header}) ? @{$args{header}} : $args{header}) + if $args{header}; + @incpaths = (ref($args{incpath}) ? @{$args{incpath}} : $args{incpath}) + if $args{incpath}; + + # work-a-like for Makefile.PL's LIBS and INC arguments + if(defined($args{LIBS})) { + foreach my $arg (split(/\s+/, $args{LIBS})) { + die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-l/i); + push @{$arg =~ /^-l/ ? \@libs : \@libpaths}, substr($arg, 2); + } + } + if(defined($args{INC})) { + foreach my $arg (split(/\s+/, $args{INC})) { + die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); + push @incpaths, substr($arg, 2); + } + } + + my @cc = _findcc(); + my @missing; + + # first figure out which headers we can't find ... + for my $header (@headers) { + my($ch, $cfile) = File::Temp::tempfile( + 'assertlibXXXXXXXX', SUFFIX => '.c' + ); + print $ch qq{#include <$header>\nint main(void) { return 0; }\n}; + close($ch); + my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; + my @sys_cmd; + # FIXME: re-factor - almost identical code later when linking + if ( $Config{cc} eq 'cl' ) { # Microsoft compiler + require Win32; + @sys_cmd = (@cc, $cfile, "/Fe$exefile", (map { '/I'.Win32::GetShortPathName($_) } @incpaths)); + } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland + @sys_cmd = (@cc, (map { "-I$_" } @incpaths), "-o$exefile", $cfile); + } else { # Unix-ish + # gcc, Sun, AIX (gcc, cc) + @sys_cmd = (@cc, $cfile, (map { "-I$_" } @incpaths), "-o", "$exefile"); + } + warn "# @sys_cmd\n" if $args{debug}; + my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); + push @missing, $header if $rv != 0 || ! -x $exefile; + _cleanup_exe($exefile); + unlink $cfile; + } + + # now do each library in turn with no headers + my($ch, $cfile) = File::Temp::tempfile( + 'assertlibXXXXXXXX', SUFFIX => '.c' + ); + print $ch "int main(void) { return 0; }\n"; + close($ch); + for my $lib ( @libs ) { + my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; + my @sys_cmd; + if ( $Config{cc} eq 'cl' ) { # Microsoft compiler + require Win32; + my @libpath = map { + q{/libpath:} . Win32::GetShortPathName($_) + } @libpaths; + @sys_cmd = (@cc, $cfile, "${lib}.lib", "/Fe$exefile", + "/link", @libpath + ); + } elsif($Config{cc} eq 'CC/DECC') { # VMS + } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland + my @libpath = map { "-L$_" } @libpaths; + @sys_cmd = (@cc, "-o$exefile", "-l$lib", @libpath, $cfile); + } else { # Unix-ish + # gcc, Sun, AIX (gcc, cc) + my @libpath = map { "-L$_" } @libpaths; + @sys_cmd = (@cc, $cfile, "-o", "$exefile", "-l$lib", @libpath); + } + warn "# @sys_cmd\n" if $args{debug}; + my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); + push @missing, $lib if $rv != 0 || ! -x $exefile; + _cleanup_exe($exefile); + } + unlink $cfile; + + my $miss_string = join( q{, }, map { qq{'$_'} } @missing ); + die("Can't link/include $miss_string\n") if @missing; +} + +sub _cleanup_exe { + my ($exefile) = @_; + my $ofile = $exefile; + $ofile =~ s/$Config{_exe}$/$Config{_o}/; + unlink $exefile if -f $exefile; + unlink $ofile if -f $ofile; + unlink "$exefile\.manifest" if -f "$exefile\.manifest"; + return +} + +sub _findcc { + my @paths = split(/$Config{path_sep}/, $ENV{PATH}); + my @cc = split(/\s+/, $Config{cc}); + return @cc if -x $cc[0]; + foreach my $path (@paths) { + my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; + return ($compiler, @cc[1 .. $#cc]) if -x $compiler; + } + die("Couldn't find your C compiler\n"); +} + +# code substantially borrowed from IPC::Run3 +sub _quiet_system { + my (@cmd) = @_; + + # save handles + local *STDOUT_SAVE; + local *STDERR_SAVE; + open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; + open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; + + # redirect to nowhere + local *DEV_NULL; + open DEV_NULL, ">" . File::Spec->devnull + or die "CheckLib: $! opening handle to null device"; + open STDOUT, ">&" . fileno DEV_NULL + or die "CheckLib: $! redirecting STDOUT to null handle"; + open STDERR, ">&" . fileno DEV_NULL + or die "CheckLib: $! redirecting STDERR to null handle"; + + # run system command + my $rv = system(@cmd); + + # restore handles + open STDOUT, ">&" . fileno STDOUT_SAVE + or die "CheckLib: $! restoring STDOUT handle"; + open STDERR, ">&" . fileno STDERR_SAVE + or die "CheckLib: $! restoring STDERR handle"; + + return $rv; +} + +=head1 PLATFORMS SUPPORTED + +You must have a C compiler installed. We check for C<$Config{cc}>, +both literally as it is in Config.pm and also in the $PATH. + +It has been tested with varying degrees on rigourousness on: + +=over + +=item gcc (on Linux, *BSD, Mac OS X, Solaris, Cygwin) + +=item Sun's compiler tools on Solaris + +=item IBM's tools on AIX + +=item Microsoft's tools on Windows + +=item MinGW on Windows (with Strawberry Perl) + +=item Borland's tools on Windows + +=back + +=head1 WARNINGS, BUGS and FEEDBACK + +This is a very early release intended primarily for feedback from +people who have discussed it. The interface may change and it has +not been adequately tested. + +Feedback is most welcome, including constructive criticism. +Bug reports should be made using L<http://rt.cpan.org/> or by email. + +When submitting a bug report, please include the output from running: + + perl -V + perl -MDevel::CheckLib -e0 + +=head1 SEE ALSO + +L<Devel::CheckOS> + +L<Probe::Perl> + +=head1 AUTHORS + +David Cantrell E<lt>david@cantrell.org.ukE<gt> + +David Golden E<lt>dagolden@cpan.orgE<gt> + +Thanks to the cpan-testers-discuss mailing list for prompting us to write it +in the first place; + +to Chris Williams for help with Borland support. + +=head1 COPYRIGHT and LICENCE + +Copyright 2007 David Cantrell. Portions copyright 2007 David Golden. + +This module is free-as-in-speech software, and may be used, distributed, +and modified under the same conditions as perl itself. + +=head1 CONSPIRACY + +This module is also free-as-in-mason software. + +=cut + +1; diff --git a/perl/ppport.h b/perl/ppport.h new file mode 100644 index 0000000..63a8cb1 --- /dev/null +++ b/perl/ppport.h @@ -0,0 +1,5097 @@ +#if 0 +<<'SKIP'; +#endif +/* +---------------------------------------------------------------------- + + ppport.h -- Perl/Pollution/Portability Version 3.06_01 + + Automatically created by Devel::PPPort running under + perl 5.008008 on Fri Nov 14 08:58:38 2008. + + Do NOT edit this file directly! -- Edit PPPort_pm.PL and the + includes in parts/inc/ instead. + + Use 'perldoc ppport.h' to view the documentation below. + +---------------------------------------------------------------------- + +SKIP + +=pod + +=head1 NAME + +ppport.h - Perl/Pollution/Portability version 3.06_01 + +=head1 SYNOPSIS + + perl ppport.h [options] [source files] + + Searches current directory for files if no [source files] are given + + --help show short help + + --patch=file write one patch file with changes + --copy=suffix write changed copies with suffix + --diff=program use diff program and options + + --compat-version=version provide compatibility with Perl version + --cplusplus accept C++ comments + + --quiet don't output anything except fatal errors + --nodiag don't show diagnostics + --nohints don't show hints + --nochanges don't suggest changes + --nofilter don't filter input files + + --list-provided list provided API + --list-unsupported list unsupported API + --api-info=name show Perl API portability information + +=head1 COMPATIBILITY + +This version of F<ppport.h> is designed to support operation with Perl +installations back to 5.003, and has been tested up to 5.9.3. + +=head1 OPTIONS + +=head2 --help + +Display a brief usage summary. + +=head2 --patch=I<file> + +If this option is given, a single patch file will be created if +any changes are suggested. This requires a working diff program +to be installed on your system. + +=head2 --copy=I<suffix> + +If this option is given, a copy of each file will be saved with +the given suffix that contains the suggested changes. This does +not require any external programs. + +If neither C<--patch> or C<--copy> are given, the default is to +simply print the diffs for each file. This requires either +C<Text::Diff> or a C<diff> program to be installed. + +=head2 --diff=I<program> + +Manually set the diff program and options to use. The default +is to use C<Text::Diff>, when installed, and output unified +context diffs. + +=head2 --compat-version=I<version> + +Tell F<ppport.h> to check for compatibility with the given +Perl version. The default is to check for compatibility with Perl +version 5.003. You can use this option to reduce the output +of F<ppport.h> if you intend to be backward compatible only +up to a certain Perl version. + +=head2 --cplusplus + +Usually, F<ppport.h> will detect C++ style comments and +replace them with C style comments for portability reasons. +Using this option instructs F<ppport.h> to leave C++ +comments untouched. + +=head2 --quiet + +Be quiet. Don't print anything except fatal errors. + +=head2 --nodiag + +Don't output any diagnostic messages. Only portability +alerts will be printed. + +=head2 --nohints + +Don't output any hints. Hints often contain useful portability +notes. + +=head2 --nochanges + +Don't suggest any changes. Only give diagnostic output and hints +unless these are also deactivated. + +=head2 --nofilter + +Don't filter the list of input files. By default, files not looking +like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped. + +=head2 --list-provided + +Lists the API elements for which compatibility is provided by +F<ppport.h>. Also lists if it must be explicitly requested, +if it has dependencies, and if there are hints for it. + +=head2 --list-unsupported + +Lists the API elements that are known not to be supported by +F<ppport.h> and below which version of Perl they probably +won't be available or work. + +=head2 --api-info=I<name> + +Show portability information for API elements matching I<name>. +If I<name> is surrounded by slashes, it is interpreted as a regular +expression. + +=head1 DESCRIPTION + +In order for a Perl extension (XS) module to be as portable as possible +across differing versions of Perl itself, certain steps need to be taken. + +=over 4 + +=item * + +Including this header is the first major one. This alone will give you +access to a large part of the Perl API that hasn't been available in +earlier Perl releases. Use + + perl ppport.h --list-provided + +to see which API elements are provided by ppport.h. + +=item * + +You should avoid using deprecated parts of the API. For example, using +global Perl variables without the C<PL_> prefix is deprecated. Also, +some API functions used to have a C<perl_> prefix. Using this form is +also deprecated. You can safely use the supported API, as F<ppport.h> +will provide wrappers for older Perl versions. + +=item * + +If you use one of a few functions that were not present in earlier +versions of Perl, and that can't be provided using a macro, you have +to explicitly request support for these functions by adding one or +more C<#define>s in your source code before the inclusion of F<ppport.h>. + +These functions will be marked C<explicit> in the list shown by +C<--list-provided>. + +Depending on whether you module has a single or multiple files that +use such functions, you want either C<static> or global variants. + +For a C<static> function, use: + + #define NEED_function + +For a global function, use: + + #define NEED_function_GLOBAL + +Note that you mustn't have more than one global request for one +function in your project. + + Function Static Request Global Request + ----------------------------------------------------------------------------------------- + eval_pv() NEED_eval_pv NEED_eval_pv_GLOBAL + grok_bin() NEED_grok_bin NEED_grok_bin_GLOBAL + grok_hex() NEED_grok_hex NEED_grok_hex_GLOBAL + grok_number() NEED_grok_number NEED_grok_number_GLOBAL + grok_numeric_radix() NEED_grok_numeric_radix NEED_grok_numeric_radix_GLOBAL + grok_oct() NEED_grok_oct NEED_grok_oct_GLOBAL + newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL + newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL + sv_2pv_nolen() NEED_sv_2pv_nolen NEED_sv_2pv_nolen_GLOBAL + sv_2pvbyte() NEED_sv_2pvbyte NEED_sv_2pvbyte_GLOBAL + sv_catpvf_mg() NEED_sv_catpvf_mg NEED_sv_catpvf_mg_GLOBAL + sv_catpvf_mg_nocontext() NEED_sv_catpvf_mg_nocontext NEED_sv_catpvf_mg_nocontext_GLOBAL + sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL + sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL + vnewSVpvf() NEED_vnewSVpvf NEED_vnewSVpvf_GLOBAL + +To avoid namespace conflicts, you can change the namespace of the +explicitly exported functions using the C<DPPP_NAMESPACE> macro. +Just C<#define> the macro before including C<ppport.h>: + + #define DPPP_NAMESPACE MyOwnNamespace_ + #include "ppport.h" + +The default namespace is C<DPPP_>. + +=back + +The good thing is that most of the above can be checked by running +F<ppport.h> on your source code. See the next section for +details. + +=head1 EXAMPLES + +To verify whether F<ppport.h> is needed for your module, whether you +should make any changes to your code, and whether any special defines +should be used, F<ppport.h> can be run as a Perl script to check your +source code. Simply say: + + perl ppport.h + +The result will usually be a list of patches suggesting changes +that should at least be acceptable, if not necessarily the most +efficient solution, or a fix for all possible problems. + +If you know that your XS module uses features only available in +newer Perl releases, if you're aware that it uses C++ comments, +and if you want all suggestions as a single patch file, you could +use something like this: + + perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff + +If you only want your code to be scanned without any suggestions +for changes, use: + + perl ppport.h --nochanges + +You can specify a different C<diff> program or options, using +the C<--diff> option: + + perl ppport.h --diff='diff -C 10' + +This would output context diffs with 10 lines of context. + +To display portability information for the C<newSVpvn> function, +use: + + perl ppport.h --api-info=newSVpvn + +Since the argument to C<--api-info> can be a regular expression, +you can use + + perl ppport.h --api-info=/_nomg$/ + +to display portability information for all C<_nomg> functions or + + perl ppport.h --api-info=/./ + +to display information for all known API elements. + +=head1 BUGS + +If this version of F<ppport.h> is causing failure during +the compilation of this module, please check if newer versions +of either this module or C<Devel::PPPort> are available on CPAN +before sending a bug report. + +If F<ppport.h> was generated using the latest version of +C<Devel::PPPort> and is causing failure of this module, please +file a bug report using the CPAN Request Tracker at L<http://rt.cpan.org/>. + +Please include the following information: + +=over 4 + +=item 1. + +The complete output from running "perl -V" + +=item 2. + +This file. + +=item 3. + +The name and version of the module you were trying to build. + +=item 4. + +A full log of the build that failed. + +=item 5. + +Any other information that you think could be relevant. + +=back + +For the latest version of this code, please get the C<Devel::PPPort> +module from CPAN. + +=head1 COPYRIGHT + +Version 3.x, Copyright (c) 2004-2005, Marcus Holland-Moritz. + +Version 2.x, Copyright (C) 2001, Paul Marquess. + +Version 1.x, Copyright (C) 1999, Kenneth Albanowski. + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +See L<Devel::PPPort>. + +=cut + +use strict; + +my %opt = ( + quiet => 0, + diag => 1, + hints => 1, + changes => 1, + cplusplus => 0, + filter => 1, +); + +my($ppport) = $0 =~ /([\w.]+)$/; +my $LF = '(?:\r\n|[\r\n])'; # line feed +my $HS = "[ \t]"; # horizontal whitespace + +eval { + require Getopt::Long; + Getopt::Long::GetOptions(\%opt, qw( + help quiet diag! filter! hints! changes! cplusplus + patch=s copy=s diff=s compat-version=s + list-provided list-unsupported api-info=s + )) or usage(); +}; + +if ($@ and grep /^-/, @ARGV) { + usage() if "@ARGV" =~ /^--?h(?:elp)?$/; + die "Getopt::Long not found. Please don't use any options.\n"; +} + +usage() if $opt{help}; + +if (exists $opt{'compat-version'}) { + my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) }; + if ($@) { + die "Invalid version number format: '$opt{'compat-version'}'\n"; + } + die "Only Perl 5 is supported\n" if $r != 5; + die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000; + $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s; +} +else { + $opt{'compat-version'} = 5; +} + +# Never use C comments in this file!!!!! +my $ccs = '/'.'*'; +my $cce = '*'.'/'; +my $rccs = quotemeta $ccs; +my $rcce = quotemeta $cce; + +my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/ + ? ( $1 => { + ($2 ? ( base => $2 ) : ()), + ($3 ? ( todo => $3 ) : ()), + (index($4, 'v') >= 0 ? ( varargs => 1 ) : ()), + (index($4, 'p') >= 0 ? ( provided => 1 ) : ()), + (index($4, 'n') >= 0 ? ( nothxarg => 1 ) : ()), + } ) + : die "invalid spec: $_" } qw( +AvFILLp|5.004050||p +AvFILL||| +CLASS|||n +CX_CURPAD_SAVE||| +CX_CURPAD_SV||| +CopFILEAV|5.006000||p +CopFILEGV_set|5.006000||p +CopFILEGV|5.006000||p +CopFILESV|5.006000||p +CopFILE_set|5.006000||p +CopFILE|5.006000||p +CopSTASHPV_set|5.006000||p +CopSTASHPV|5.006000||p +CopSTASH_eq|5.006000||p +CopSTASH_set|5.006000||p +CopSTASH|5.006000||p +CopyD|5.009002||p +Copy||| +CvPADLIST||| +CvSTASH||| +CvWEAKOUTSIDE||| +DEFSV|5.004050||p +END_EXTERN_C|5.005000||p +ENTER||| +ERRSV|5.004050||p +EXTEND||| +EXTERN_C|5.005000||p +FREETMPS||| +GIMME_V||5.004000|n +GIMME|||n +GROK_NUMERIC_RADIX|5.007002||p +G_ARRAY||| +G_DISCARD||| +G_EVAL||| +G_NOARGS||| +G_SCALAR||| +G_VOID||5.004000| +GetVars||| +GvSV||| +Gv_AMupdate||| +HEf_SVKEY||5.004000| +HeHASH||5.004000| +HeKEY||5.004000| +HeKLEN||5.004000| +HePV||5.004000| +HeSVKEY_force||5.004000| +HeSVKEY_set||5.004000| +HeSVKEY||5.004000| +HeVAL||5.004000| +HvNAME||| +INT2PTR|5.006000||p +IN_LOCALE_COMPILETIME|5.007002||p +IN_LOCALE_RUNTIME|5.007002||p +IN_LOCALE|5.007002||p +IN_PERL_COMPILETIME|5.008001||p +IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p +IS_NUMBER_INFINITY|5.007002||p +IS_NUMBER_IN_UV|5.007002||p +IS_NUMBER_NAN|5.007003||p +IS_NUMBER_NEG|5.007002||p +IS_NUMBER_NOT_INT|5.007002||p +IVSIZE|5.006000||p +IVTYPE|5.006000||p +IVdf|5.006000||p +LEAVE||| +LVRET||| +MARK||| +MY_CXT_CLONE|5.009002||p +MY_CXT_INIT|5.007003||p +MY_CXT|5.007003||p +MoveD|5.009002||p +Move||| +NEWSV||| +NOOP|5.005000||p +NUM2PTR|5.006000||p +NVTYPE|5.006000||p +NVef|5.006001||p +NVff|5.006001||p +NVgf|5.006001||p +Newc||| +Newz||| +New||| +Nullav||| +Nullch||| +Nullcv||| +Nullhv||| +Nullsv||| +ORIGMARK||| +PAD_BASE_SV||| +PAD_CLONE_VARS||| +PAD_COMPNAME_FLAGS||| +PAD_COMPNAME_GEN_set||| +PAD_COMPNAME_GEN||| +PAD_COMPNAME_OURSTASH||| +PAD_COMPNAME_PV||| +PAD_COMPNAME_TYPE||| +PAD_RESTORE_LOCAL||| +PAD_SAVE_LOCAL||| +PAD_SAVE_SETNULLPAD||| +PAD_SETSV||| +PAD_SET_CUR_NOSAVE||| +PAD_SET_CUR||| +PAD_SVl||| +PAD_SV||| +PERL_BCDVERSION|5.009003||p +PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p +PERL_INT_MAX|5.004000||p +PERL_INT_MIN|5.004000||p +PERL_LONG_MAX|5.004000||p +PERL_LONG_MIN|5.004000||p +PERL_MAGIC_arylen|5.007002||p +PERL_MAGIC_backref|5.007002||p +PERL_MAGIC_bm|5.007002||p +PERL_MAGIC_collxfrm|5.007002||p +PERL_MAGIC_dbfile|5.007002||p +PERL_MAGIC_dbline|5.007002||p +PERL_MAGIC_defelem|5.007002||p +PERL_MAGIC_envelem|5.007002||p +PERL_MAGIC_env|5.007002||p +PERL_MAGIC_ext|5.007002||p +PERL_MAGIC_fm|5.007002||p +PERL_MAGIC_glob|5.007002||p +PERL_MAGIC_isaelem|5.007002||p +PERL_MAGIC_isa|5.007002||p +PERL_MAGIC_mutex|5.007002||p +PERL_MAGIC_nkeys|5.007002||p +PERL_MAGIC_overload_elem|5.007002||p +PERL_MAGIC_overload_table|5.007002||p +PERL_MAGIC_overload|5.007002||p +PERL_MAGIC_pos|5.007002||p +PERL_MAGIC_qr|5.007002||p +PERL_MAGIC_regdata|5.007002||p +PERL_MAGIC_regdatum|5.007002||p +PERL_MAGIC_regex_global|5.007002||p +PERL_MAGIC_shared_scalar|5.007003||p +PERL_MAGIC_shared|5.007003||p +PERL_MAGIC_sigelem|5.007002||p +PERL_MAGIC_sig|5.007002||p +PERL_MAGIC_substr|5.007002||p +PERL_MAGIC_sv|5.007002||p +PERL_MAGIC_taint|5.007002||p +PERL_MAGIC_tiedelem|5.007002||p +PERL_MAGIC_tiedscalar|5.007002||p +PERL_MAGIC_tied|5.007002||p +PERL_MAGIC_utf8|5.008001||p +PERL_MAGIC_uvar_elem|5.007003||p +PERL_MAGIC_uvar|5.007002||p +PERL_MAGIC_vec|5.007002||p +PERL_MAGIC_vstring|5.008001||p +PERL_QUAD_MAX|5.004000||p +PERL_QUAD_MIN|5.004000||p +PERL_REVISION|5.006000||p +PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p +PERL_SCAN_DISALLOW_PREFIX|5.007003||p +PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p +PERL_SCAN_SILENT_ILLDIGIT|5.008001||p +PERL_SHORT_MAX|5.004000||p +PERL_SHORT_MIN|5.004000||p +PERL_SUBVERSION|5.006000||p +PERL_UCHAR_MAX|5.004000||p +PERL_UCHAR_MIN|5.004000||p +PERL_UINT_MAX|5.004000||p +PERL_UINT_MIN|5.004000||p +PERL_ULONG_MAX|5.004000||p +PERL_ULONG_MIN|5.004000||p +PERL_UNUSED_DECL|5.007002||p +PERL_UQUAD_MAX|5.004000||p +PERL_UQUAD_MIN|5.004000||p +PERL_USHORT_MAX|5.004000||p +PERL_USHORT_MIN|5.004000||p +PERL_VERSION|5.006000||p +PL_DBsingle|||pn +PL_DBsub|||pn +PL_DBtrace|||n +PL_Sv|5.005000||p +PL_compiling|5.004050||p +PL_copline|5.005000||p +PL_curcop|5.004050||p +PL_curstash|5.004050||p +PL_debstash|5.004050||p +PL_defgv|5.004050||p +PL_diehook|5.004050||p +PL_dirty|5.004050||p +PL_dowarn|||pn +PL_errgv|5.004050||p +PL_hexdigit|5.005000||p +PL_hints|5.005000||p +PL_last_in_gv|||n +PL_modglobal||5.005000|n +PL_na|5.004050||pn +PL_no_modify|5.006000||p +PL_ofs_sv|||n +PL_perl_destruct_level|5.004050||p +PL_perldb|5.004050||p +PL_ppaddr|5.006000||p +PL_rsfp_filters|5.004050||p +PL_rsfp|5.004050||p +PL_rs|||n +PL_stack_base|5.004050||p +PL_stack_sp|5.004050||p +PL_stdingv|5.004050||p +PL_sv_arenaroot|5.004050||p +PL_sv_no|5.004050||pn +PL_sv_undef|5.004050||pn +PL_sv_yes|5.004050||pn +PL_tainted|5.004050||p +PL_tainting|5.004050||p +POPi|||n +POPl|||n +POPn|||n +POPpbytex||5.007001|n +POPpx||5.005030|n +POPp|||n +POPs|||n +PTR2IV|5.006000||p +PTR2NV|5.006000||p +PTR2UV|5.006000||p +PTR2ul|5.007001||p +PTRV|5.006000||p +PUSHMARK||| +PUSHi||| +PUSHmortal|5.009002||p +PUSHn||| +PUSHp||| +PUSHs||| +PUSHu|5.004000||p +PUTBACK||| +PerlIO_clearerr||5.007003| +PerlIO_close||5.007003| +PerlIO_eof||5.007003| +PerlIO_error||5.007003| +PerlIO_fileno||5.007003| +PerlIO_fill||5.007003| +PerlIO_flush||5.007003| +PerlIO_get_base||5.007003| +PerlIO_get_bufsiz||5.007003| +PerlIO_get_cnt||5.007003| +PerlIO_get_ptr||5.007003| +PerlIO_read||5.007003| +PerlIO_seek||5.007003| +PerlIO_set_cnt||5.007003| +PerlIO_set_ptrcnt||5.007003| +PerlIO_setlinebuf||5.007003| +PerlIO_stderr||5.007003| +PerlIO_stdin||5.007003| +PerlIO_stdout||5.007003| +PerlIO_tell||5.007003| +PerlIO_unread||5.007003| +PerlIO_write||5.007003| +Poison|5.008000||p +RETVAL|||n +Renewc||| +Renew||| +SAVECLEARSV||| +SAVECOMPPAD||| +SAVEPADSV||| +SAVETMPS||| +SAVE_DEFSV|5.004050||p +SPAGAIN||| +SP||| +START_EXTERN_C|5.005000||p +START_MY_CXT|5.007003||p +STMT_END|||p +STMT_START|||p +ST||| +SVt_IV||| +SVt_NV||| +SVt_PVAV||| +SVt_PVCV||| +SVt_PVHV||| +SVt_PVMG||| +SVt_PV||| +Safefree||| +Slab_Alloc||| +Slab_Free||| +StructCopy||| +SvCUR_set||| +SvCUR||| +SvEND||| +SvGETMAGIC|5.004050||p +SvGROW||| +SvIOK_UV||5.006000| +SvIOK_notUV||5.006000| +SvIOK_off||| +SvIOK_only_UV||5.006000| +SvIOK_only||| +SvIOK_on||| +SvIOKp||| +SvIOK||| +SvIVX||| +SvIV_nomg|5.009001||p +SvIV_set||| +SvIVx||| +SvIV||| +SvIsCOW_shared_hash||5.008003| +SvIsCOW||5.008003| +SvLEN_set||| +SvLEN||| +SvLOCK||5.007003| +SvMAGIC_set||5.009003| +SvNIOK_off||| +SvNIOKp||| +SvNIOK||| +SvNOK_off||| +SvNOK_only||| +SvNOK_on||| +SvNOKp||| +SvNOK||| +SvNVX||| +SvNV_set||| +SvNVx||| +SvNV||| +SvOK||| +SvOOK||| +SvPOK_off||| +SvPOK_only_UTF8||5.006000| +SvPOK_only||| +SvPOK_on||| +SvPOKp||| +SvPOK||| +SvPVX||| +SvPV_force_nomg|5.007002||p +SvPV_force||| +SvPV_nolen|5.006000||p +SvPV_nomg|5.007002||p +SvPV_set||| +SvPVbyte_force||5.009002| +SvPVbyte_nolen||5.006000| +SvPVbytex_force||5.006000| +SvPVbytex||5.006000| +SvPVbyte|5.006000||p +SvPVutf8_force||5.006000| +SvPVutf8_nolen||5.006000| +SvPVutf8x_force||5.006000| +SvPVutf8x||5.006000| +SvPVutf8||5.006000| +SvPVx||| +SvPV||| +SvREFCNT_dec||| +SvREFCNT_inc||| +SvREFCNT||| +SvROK_off||| +SvROK_on||| +SvROK||| +SvRV_set||5.009003| +SvRV||| +SvSETMAGIC||| +SvSHARE||5.007003| +SvSTASH_set||5.009003| +SvSTASH||| +SvSetMagicSV_nosteal||5.004000| +SvSetMagicSV||5.004000| +SvSetSV_nosteal||5.004000| +SvSetSV||| +SvTAINTED_off||5.004000| +SvTAINTED_on||5.004000| +SvTAINTED||5.004000| +SvTAINT||| +SvTRUE||| +SvTYPE||| +SvUNLOCK||5.007003| +SvUOK||5.007001| +SvUPGRADE||| +SvUTF8_off||5.006000| +SvUTF8_on||5.006000| +SvUTF8||5.006000| +SvUVXx|5.004000||p +SvUVX|5.004000||p +SvUV_nomg|5.009001||p +SvUV_set||5.009003| +SvUVx|5.004000||p +SvUV|5.004000||p +SvVOK||5.008001| +THIS|||n +UNDERBAR|5.009002||p +UVSIZE|5.006000||p +UVTYPE|5.006000||p +UVXf|5.007001||p +UVof|5.006000||p +UVuf|5.006000||p +UVxf|5.006000||p +XCPT_CATCH|5.009002||p +XCPT_RETHROW|5.009002||p +XCPT_TRY_END|5.009002||p +XCPT_TRY_START|5.009002||p +XPUSHi||| +XPUSHmortal|5.009002||p +XPUSHn||| +XPUSHp||| +XPUSHs||| +XPUSHu|5.004000||p +XSRETURN_EMPTY||| +XSRETURN_IV||| +XSRETURN_NO||| +XSRETURN_NV||| +XSRETURN_PV||| +XSRETURN_UNDEF||| +XSRETURN_UV|5.008001||p +XSRETURN_YES||| +XSRETURN||| +XST_mIV||| +XST_mNO||| +XST_mNV||| +XST_mPV||| +XST_mUNDEF||| +XST_mUV|5.008001||p +XST_mYES||| +XS_VERSION_BOOTCHECK||| +XS_VERSION||| +XS||| +ZeroD|5.009002||p +Zero||| +_aMY_CXT|5.007003||p +_pMY_CXT|5.007003||p +aMY_CXT_|5.007003||p +aMY_CXT|5.007003||p +aTHX_|5.006000||p +aTHX|5.006000||p +add_data||| +allocmy||| +amagic_call||| +any_dup||| +ao||| +append_elem||| +append_list||| +apply_attrs_my||| +apply_attrs_string||5.006001| +apply_attrs||| +apply||| +asIV||| +asUV||| +atfork_lock||5.007003|n +atfork_unlock||5.007003|n +av_arylen_p||5.009003| +av_clear||| +av_delete||5.006000| +av_exists||5.006000| +av_extend||| +av_fake||| +av_fetch||| +av_fill||| +av_len||| +av_make||| +av_pop||| +av_push||| +av_reify||| +av_shift||| +av_store||| +av_undef||| +av_unshift||| +ax|||n +bad_type||| +bind_match||| +block_end||| +block_gimme||5.004000| +block_start||| +boolSV|5.004000||p +boot_core_PerlIO||| +boot_core_UNIVERSAL||| +boot_core_xsutils||| +bytes_from_utf8||5.007001| +bytes_to_utf8||5.006001| +cache_re||| +call_argv|5.006000||p +call_atexit||5.006000| +call_body||| +call_list_body||| +call_list||5.004000| +call_method|5.006000||p +call_pv|5.006000||p +call_sv|5.006000||p +calloc||5.007002|n +cando||| +cast_i32||5.006000| +cast_iv||5.006000| +cast_ulong||5.006000| +cast_uv||5.006000| +check_uni||| +checkcomma||| +checkposixcc||| +ck_anoncode||| +ck_bitop||| +ck_concat||| +ck_defined||| +ck_delete||| +ck_die||| +ck_eof||| +ck_eval||| +ck_exec||| +ck_exists||| +ck_exit||| +ck_ftst||| +ck_fun||| +ck_glob||| +ck_grep||| +ck_index||| +ck_join||| +ck_lengthconst||| +ck_lfun||| +ck_listiob||| +ck_match||| +ck_method||| +ck_null||| +ck_open||| +ck_repeat||| +ck_require||| +ck_retarget||| +ck_return||| +ck_rfun||| +ck_rvconst||| +ck_sassign||| +ck_select||| +ck_shift||| +ck_sort||| +ck_spair||| +ck_split||| +ck_subr||| +ck_substr||| +ck_svconst||| +ck_trunc||| +ck_unpack||| +cl_and||| +cl_anything||| +cl_init_zero||| +cl_init||| +cl_is_anything||| +cl_or||| +closest_cop||| +convert||| +cop_free||| +cr_textfilter||| +croak_nocontext|||vn +croak|||v +csighandler||5.007001|n +custom_op_desc||5.007003| +custom_op_name||5.007003| +cv_ckproto||| +cv_clone||| +cv_const_sv||5.004000| +cv_dump||| +cv_undef||| +cx_dump||5.005000| +cx_dup||| +cxinc||| +dAXMARK||5.009003| +dAX|5.007002||p +dITEMS|5.007002||p +dMARK||| +dMY_CXT_SV|5.007003||p +dMY_CXT|5.007003||p +dNOOP|5.006000||p +dORIGMARK||| +dSP||| +dTHR|5.004050||p +dTHXa|5.006000||p +dTHXoa|5.006000||p +dTHX|5.006000||p +dUNDERBAR|5.009002||p +dXCPT|5.009002||p +dXSARGS||| +dXSI32||| +dXSTARG|5.006000||p +deb_curcv||| +deb_nocontext|||vn +deb_stack_all||| +deb_stack_n||| +debop||5.005000| +debprofdump||5.005000| +debprof||| +debstackptrs||5.007003| +debstack||5.007003| +deb||5.007003|v +del_he||| +del_sv||| +delimcpy||5.004000| +depcom||| +deprecate_old||| +deprecate||| +despatch_signals||5.007001| +die_nocontext|||vn +die_where||| +die|||v +dirp_dup||| +div128||| +djSP||| +do_aexec5||| +do_aexec||| +do_aspawn||| +do_binmode||5.004050| +do_chomp||| +do_chop||| +do_close||| +do_dump_pad||| +do_eof||| +do_exec3||| +do_execfree||| +do_exec||| +do_gv_dump||5.006000| +do_gvgv_dump||5.006000| +do_hv_dump||5.006000| +do_ipcctl||| +do_ipcget||| +do_join||| +do_kv||| +do_magic_dump||5.006000| +do_msgrcv||| +do_msgsnd||| +do_oddball||| +do_op_dump||5.006000| +do_open9||5.006000| +do_openn||5.007001| +do_open||5.004000| +do_pipe||| +do_pmop_dump||5.006000| +do_print||| +do_readline||| +do_seek||| +do_semop||| +do_shmio||| +do_spawn_nowait||| +do_spawn||| +do_sprintf||| +do_sv_dump||5.006000| +do_sysseek||| +do_tell||| +do_trans_complex_utf8||| +do_trans_complex||| +do_trans_count_utf8||| +do_trans_count||| +do_trans_simple_utf8||| +do_trans_simple||| +do_trans||| +do_vecget||| +do_vecset||| +do_vop||| +docatch_body||| +docatch||| +doeval||| +dofile||| +dofindlabel||| +doform||| +doing_taint||5.008001|n +dooneliner||| +doopen_pm||| +doparseform||| +dopoptoeval||| +dopoptolabel||| +dopoptoloop||| +dopoptosub_at||| +dopoptosub||| +dounwind||| +dowantarray||| +dump_all||5.006000| +dump_eval||5.006000| +dump_fds||| +dump_form||5.006000| +dump_indent||5.006000|v +dump_mstats||| +dump_packsubs||5.006000| +dump_sub||5.006000| +dump_vindent||5.006000| +dumpuntil||| +dup_attrlist||| +emulate_eaccess||| +eval_pv|5.006000||p +eval_sv|5.006000||p +expect_number||| +fbm_compile||5.005000| +fbm_instr||5.005000| +fd_on_nosuid_fs||| +filter_add||| +filter_del||| +filter_gets||| +filter_read||| +find_beginning||| +find_byclass||| +find_in_my_stash||| +find_runcv||| +find_rundefsvoffset||5.009002| +find_script||| +find_uninit_var||| +fold_constants||| +forbid_setid||| +force_ident||| +force_list||| +force_next||| +force_version||| +force_word||| +form_nocontext|||vn +form||5.004000|v +fp_dup||| +fprintf_nocontext|||vn +free_global_struct||| +free_tied_hv_pool||| +free_tmps||| +gen_constant_list||| +get_av|5.006000||p +get_context||5.006000|n +get_cv|5.006000||p +get_db_sub||| +get_debug_opts||| +get_hash_seed||| +get_hv|5.006000||p +get_mstats||| +get_no_modify||| +get_num||| +get_op_descs||5.005000| +get_op_names||5.005000| +get_opargs||| +get_ppaddr||5.006000| +get_sv|5.006000||p +get_vtbl||5.005030| +getcwd_sv||5.007002| +getenv_len||| +gp_dup||| +gp_free||| +gp_ref||| +grok_bin|5.007003||p +grok_hex|5.007003||p +grok_number|5.007002||p +grok_numeric_radix|5.007002||p +grok_oct|5.007003||p +group_end||| +gv_AVadd||| +gv_HVadd||| +gv_IOadd||| +gv_autoload4||5.004000| +gv_check||| +gv_dump||5.006000| +gv_efullname3||5.004000| +gv_efullname4||5.006001| +gv_efullname||| +gv_ename||| +gv_fetchfile||| +gv_fetchmeth_autoload||5.007003| +gv_fetchmethod_autoload||5.004000| +gv_fetchmethod||| +gv_fetchmeth||| +gv_fetchpvn_flags||5.009002| +gv_fetchpv||| +gv_fetchsv||5.009002| +gv_fullname3||5.004000| +gv_fullname4||5.006001| +gv_fullname||| +gv_handler||5.007001| +gv_init_sv||| +gv_init||| +gv_share||| +gv_stashpvn|5.006000||p +gv_stashpv||| +gv_stashsv||| +he_dup||| +hek_dup||| +hfreeentries||| +hsplit||| +hv_assert||5.009001| +hv_auxinit||| +hv_clear_placeholders||5.009001| +hv_clear||| +hv_delayfree_ent||5.004000| +hv_delete_common||| +hv_delete_ent||5.004000| +hv_delete||| +hv_eiter_p||5.009003| +hv_eiter_set||5.009003| +hv_exists_ent||5.004000| +hv_exists||| +hv_fetch_common||| +hv_fetch_ent||5.004000| +hv_fetch||| +hv_free_ent||5.004000| +hv_iterinit||| +hv_iterkeysv||5.004000| +hv_iterkey||| +hv_iternext_flags||5.008000| +hv_iternextsv||| +hv_iternext||| +hv_iterval||| +hv_ksplit||5.004000| +hv_magic_check||| +hv_magic||| +hv_name_set||5.009003| +hv_notallowed||| +hv_placeholders_get||5.009003| +hv_placeholders_p||5.009003| +hv_placeholders_set||5.009003| +hv_riter_p||5.009003| +hv_riter_set||5.009003| +hv_scalar||5.009001| +hv_store_ent||5.004000| +hv_store_flags||5.008000| +hv_store||| +hv_undef||| +ibcmp_locale||5.004000| +ibcmp_utf8||5.007003| +ibcmp||| +incl_perldb||| +incline||| +incpush||| +ingroup||| +init_argv_symbols||| +init_debugger||| +init_global_struct||| +init_i18nl10n||5.006000| +init_i18nl14n||5.006000| +init_ids||| +init_interp||| +init_lexer||| +init_main_stash||| +init_perllib||| +init_postdump_symbols||| +init_predump_symbols||| +init_stacks||5.005000| +init_tm||5.007002| +instr||| +intro_my||| +intuit_method||| +intuit_more||| +invert||| +io_close||| +isALNUM||| +isALPHA||| +isDIGIT||| +isLOWER||| +isSPACE||| +isUPPER||| +is_an_int||| +is_gv_magical_sv||| +is_gv_magical||| +is_handle_constructor||| +is_list_assignment||| +is_lvalue_sub||5.007001| +is_uni_alnum_lc||5.006000| +is_uni_alnumc_lc||5.006000| +is_uni_alnumc||5.006000| +is_uni_alnum||5.006000| +is_uni_alpha_lc||5.006000| +is_uni_alpha||5.006000| +is_uni_ascii_lc||5.006000| +is_uni_ascii||5.006000| +is_uni_cntrl_lc||5.006000| +is_uni_cntrl||5.006000| +is_uni_digit_lc||5.006000| +is_uni_digit||5.006000| +is_uni_graph_lc||5.006000| +is_uni_graph||5.006000| +is_uni_idfirst_lc||5.006000| +is_uni_idfirst||5.006000| +is_uni_lower_lc||5.006000| +is_uni_lower||5.006000| +is_uni_print_lc||5.006000| +is_uni_print||5.006000| +is_uni_punct_lc||5.006000| +is_uni_punct||5.006000| +is_uni_space_lc||5.006000| +is_uni_space||5.006000| +is_uni_upper_lc||5.006000| +is_uni_upper||5.006000| +is_uni_xdigit_lc||5.006000| +is_uni_xdigit||5.006000| +is_utf8_alnumc||5.006000| +is_utf8_alnum||5.006000| +is_utf8_alpha||5.006000| +is_utf8_ascii||5.006000| +is_utf8_char_slow||| +is_utf8_char||5.006000| +is_utf8_cntrl||5.006000| +is_utf8_digit||5.006000| +is_utf8_graph||5.006000| +is_utf8_idcont||5.008000| +is_utf8_idfirst||5.006000| +is_utf8_lower||5.006000| +is_utf8_mark||5.006000| +is_utf8_print||5.006000| +is_utf8_punct||5.006000| +is_utf8_space||5.006000| +is_utf8_string_loclen||5.009003| +is_utf8_string_loc||5.008001| +is_utf8_string||5.006001| +is_utf8_upper||5.006000| +is_utf8_xdigit||5.006000| +isa_lookup||| +items|||n +ix|||n +jmaybe||| +keyword||| +leave_scope||| +lex_end||| +lex_start||| +linklist||| +listkids||| +list||| +load_module_nocontext|||vn +load_module||5.006000|v +localize||| +looks_like_number||| +lop||| +mPUSHi|5.009002||p +mPUSHn|5.009002||p +mPUSHp|5.009002||p +mPUSHu|5.009002||p +mXPUSHi|5.009002||p +mXPUSHn|5.009002||p +mXPUSHp|5.009002||p +mXPUSHu|5.009002||p +magic_clear_all_env||| +magic_clearenv||| +magic_clearpack||| +magic_clearsig||| +magic_dump||5.006000| +magic_existspack||| +magic_freearylen_p||| +magic_freeovrld||| +magic_freeregexp||| +magic_getarylen||| +magic_getdefelem||| +magic_getglob||| +magic_getnkeys||| +magic_getpack||| +magic_getpos||| +magic_getsig||| +magic_getsubstr||| +magic_gettaint||| +magic_getuvar||| +magic_getvec||| +magic_get||| +magic_killbackrefs||| +magic_len||| +magic_methcall||| +magic_methpack||| +magic_nextpack||| +magic_regdata_cnt||| +magic_regdatum_get||| +magic_regdatum_set||| +magic_scalarpack||| +magic_set_all_env||| +magic_setamagic||| +magic_setarylen||| +magic_setbm||| +magic_setcollxfrm||| +magic_setdbline||| +magic_setdefelem||| +magic_setenv||| +magic_setfm||| +magic_setglob||| +magic_setisa||| +magic_setmglob||| +magic_setnkeys||| +magic_setpack||| +magic_setpos||| +magic_setregexp||| +magic_setsig||| +magic_setsubstr||| +magic_settaint||| +magic_setutf8||| +magic_setuvar||| +magic_setvec||| +magic_set||| +magic_sizepack||| +magic_wipepack||| +magicname||| +make_trie||| +malloced_size|||n +malloc||5.007002|n +markstack_grow||| +measure_struct||| +memEQ|5.004000||p +memNE|5.004000||p +mem_collxfrm||| +mess_alloc||| +mess_nocontext|||vn +mess||5.006000|v +method_common||| +mfree||5.007002|n +mg_clear||| +mg_copy||| +mg_dup||| +mg_find||| +mg_free||| +mg_get||| +mg_length||5.005000| +mg_localize||| +mg_magical||| +mg_set||| +mg_size||5.005000| +mini_mktime||5.007002| +missingterm||| +mode_from_discipline||| +modkids||| +mod||| +moreswitches||| +mul128||| +mulexp10|||n +my_atof2||5.007002| +my_atof||5.006000| +my_attrs||| +my_bcopy|||n +my_betoh16|||n +my_betoh32|||n +my_betoh64|||n +my_betohi|||n +my_betohl|||n +my_betohs|||n +my_bzero|||n +my_chsize||| +my_exit_jump||| +my_exit||| +my_failure_exit||5.004000| +my_fflush_all||5.006000| +my_fork||5.007003|n +my_htobe16|||n +my_htobe32|||n +my_htobe64|||n +my_htobei|||n +my_htobel|||n +my_htobes|||n +my_htole16|||n +my_htole32|||n +my_htole64|||n +my_htolei|||n +my_htolel|||n +my_htoles|||n +my_htonl||| +my_kid||| +my_letoh16|||n +my_letoh32|||n +my_letoh64|||n +my_letohi|||n +my_letohl|||n +my_letohs|||n +my_lstat||| +my_memcmp||5.004000|n +my_memset|||n +my_ntohl||| +my_pclose||5.004000| +my_popen_list||5.007001| +my_popen||5.004000| +my_setenv||| +my_socketpair||5.007003|n +my_stat||| +my_strftime||5.007002| +my_swabn|||n +my_swap||| +my_unexec||| +my||| +newANONATTRSUB||5.006000| +newANONHASH||| +newANONLIST||| +newANONSUB||| +newASSIGNOP||| +newATTRSUB||5.006000| +newAVREF||| +newAV||| +newBINOP||| +newCONDOP||| +newCONSTSUB|5.006000||p +newCVREF||| +newDEFSVOP||| +newFORM||| +newFOROP||| +newGVOP||| +newGVREF||| +newGVgen||| +newHVREF||| +newHVhv||5.005000| +newHV||| +newIO||| +newLISTOP||| +newLOGOP||| +newLOOPEX||| +newLOOPOP||| +newMYSUB||5.006000| +newNULLLIST||| +newOP||| +newPADOP||5.006000| +newPMOP||| +newPROG||| +newPVOP||| +newRANGE||| +newRV_inc|5.004000||p +newRV_noinc|5.006000||p +newRV||| +newSLICEOP||| +newSTATEOP||| +newSUB||| +newSVOP||| +newSVREF||| +newSVhek||5.009003| +newSViv||| +newSVnv||| +newSVpvf_nocontext|||vn +newSVpvf||5.004000|v +newSVpvn_share||5.007001| +newSVpvn|5.006000||p +newSVpv||| +newSVrv||| +newSVsv||| +newSVuv|5.006000||p +newSV||| +newUNOP||| +newWHILEOP||5.009003| +newXSproto||5.006000| +newXS||5.006000| +new_collate||5.006000| +new_constant||| +new_ctype||5.006000| +new_he||| +new_logop||| +new_numeric||5.006000| +new_stackinfo||5.005000| +new_version||5.009000| +next_symbol||| +nextargv||| +nextchar||| +ninstr||| +no_bareword_allowed||| +no_fh_allowed||| +no_op||| +not_a_number||| +nothreadhook||5.008000| +nuke_stacks||| +num_overflow|||n +oopsAV||| +oopsCV||| +oopsHV||| +op_clear||| +op_const_sv||| +op_dump||5.006000| +op_free||| +op_null||5.007002| +op_refcnt_lock||5.009002| +op_refcnt_unlock||5.009002| +open_script||| +pMY_CXT_|5.007003||p +pMY_CXT|5.007003||p +pTHX_|5.006000||p +pTHX|5.006000||p +pack_cat||5.007003| +pack_rec||| +package||| +packlist||5.008001| +pad_add_anon||| +pad_add_name||| +pad_alloc||| +pad_block_start||| +pad_check_dup||| +pad_compname_type||| +pad_findlex||| +pad_findmy||| +pad_fixup_inner_anons||| +pad_free||| +pad_leavemy||| +pad_new||| +pad_push||| +pad_reset||| +pad_setsv||| +pad_sv||| +pad_swipe||| +pad_tidy||| +pad_undef||| +parse_body||| +parse_unicode_opts||| +path_is_absolute||| +peep||| +pending_ident||| +perl_alloc_using|||n +perl_alloc|||n +perl_clone_using|||n +perl_clone|||n +perl_construct|||n +perl_destruct||5.007003|n +perl_free|||n +perl_parse||5.006000|n +perl_run|||n +pidgone||| +pmflag||| +pmop_dump||5.006000| +pmruntime||| +pmtrans||| +pop_scope||| +pregcomp||| +pregexec||| +pregfree||| +prepend_elem||| +printf_nocontext|||vn +ptr_table_clear||| +ptr_table_fetch||| +ptr_table_free||| +ptr_table_new||| +ptr_table_split||| +ptr_table_store||| +push_scope||| +put_byte||| +pv_display||5.006000| +pv_uni_display||5.007003| +qerror||| +re_croak2||| +re_dup||| +re_intuit_start||5.006000| +re_intuit_string||5.006000| +realloc||5.007002|n +reentrant_free||| +reentrant_init||| +reentrant_retry|||vn +reentrant_size||| +refkids||| +refto||| +ref||| +reg_node||| +reganode||| +regatom||| +regbranch||| +regclass_swash||5.007003| +regclass||| +regcp_set_to||| +regcppop||| +regcppush||| +regcurly||| +regdump||5.005000| +regexec_flags||5.005000| +reghop3||| +reghopmaybe3||| +reghopmaybe||| +reghop||| +reginclass||| +reginitcolors||5.006000| +reginsert||| +regmatch||| +regnext||5.005000| +regoptail||| +regpiece||| +regpposixcc||| +regprop||| +regrepeat_hard||| +regrepeat||| +regtail||| +regtry||| +reguni||| +regwhite||| +reg||| +repeatcpy||| +report_evil_fh||| +report_uninit||| +require_errno||| +require_pv||5.006000| +rninstr||| +rsignal_restore||| +rsignal_save||| +rsignal_state||5.004000| +rsignal||5.004000| +run_body||| +runops_debug||5.005000| +runops_standard||5.005000| +rvpv_dup||| +rxres_free||| +rxres_restore||| +rxres_save||| +safesyscalloc||5.006000|n +safesysfree||5.006000|n +safesysmalloc||5.006000|n +safesysrealloc||5.006000|n +same_dirent||| +save_I16||5.004000| +save_I32||| +save_I8||5.006000| +save_aelem||5.004050| +save_alloc||5.006000| +save_aptr||| +save_ary||| +save_bool||5.008001| +save_clearsv||| +save_delete||| +save_destructor_x||5.006000| +save_destructor||5.006000| +save_freeop||| +save_freepv||| +save_freesv||| +save_generic_pvref||5.006001| +save_generic_svref||5.005030| +save_gp||5.004000| +save_hash||| +save_hek_flags||| +save_helem||5.004050| +save_hints||5.005000| +save_hptr||| +save_int||| +save_item||| +save_iv||5.005000| +save_lines||| +save_list||| +save_long||| +save_magic||| +save_mortalizesv||5.007001| +save_nogv||| +save_op||| +save_padsv||5.007001| +save_pptr||| +save_re_context||5.006000| +save_scalar_at||| +save_scalar||| +save_set_svflags||5.009000| +save_shared_pvref||5.007003| +save_sptr||| +save_svref||| +save_threadsv||5.005000| +save_vptr||5.006000| +savepvn||| +savepv||| +savesharedpv||5.007003| +savestack_grow_cnt||5.008001| +savestack_grow||| +savesvpv||5.009002| +sawparens||| +scalar_mod_type||| +scalarboolean||| +scalarkids||| +scalarseq||| +scalarvoid||| +scalar||| +scan_bin||5.006000| +scan_commit||| +scan_const||| +scan_formline||| +scan_heredoc||| +scan_hex||| +scan_ident||| +scan_inputsymbol||| +scan_num||5.007001| +scan_oct||| +scan_pat||| +scan_str||| +scan_subst||| +scan_trans||| +scan_version||5.009001| +scan_vstring||5.008001| +scan_word||| +scope||| +screaminstr||5.005000| +seed||| +set_context||5.006000|n +set_csh||| +set_numeric_local||5.006000| +set_numeric_radix||5.006000| +set_numeric_standard||5.006000| +setdefout||| +setenv_getix||| +share_hek_flags||| +share_hek||| +si_dup||| +sighandler|||n +simplify_sort||| +skipspace||| +sortsv||5.007003| +ss_dup||| +stack_grow||| +start_glob||| +start_subparse||5.004000| +stashpv_hvname_match||5.009003| +stdize_locale||| +strEQ||| +strGE||| +strGT||| +strLE||| +strLT||| +strNE||| +str_to_version||5.006000| +strnEQ||| +strnNE||| +study_chunk||| +sub_crush_depth||| +sublex_done||| +sublex_push||| +sublex_start||| +sv_2bool||| +sv_2cv||| +sv_2io||| +sv_2iuv_non_preserve||| +sv_2iv_flags||5.009001| +sv_2iv||| +sv_2mortal||| +sv_2nv||| +sv_2pv_flags||5.007002| +sv_2pv_nolen|5.006000||p +sv_2pvbyte_nolen||| +sv_2pvbyte|5.006000||p +sv_2pvutf8_nolen||5.006000| +sv_2pvutf8||5.006000| +sv_2pv||| +sv_2uv_flags||5.009001| +sv_2uv|5.004000||p +sv_add_arena||| +sv_add_backref||| +sv_backoff||| +sv_bless||| +sv_cat_decode||5.008001| +sv_catpv_mg|5.006000||p +sv_catpvf_mg_nocontext|||pvn +sv_catpvf_mg|5.006000|5.004000|pv +sv_catpvf_nocontext|||vn +sv_catpvf||5.004000|v +sv_catpvn_flags||5.007002| +sv_catpvn_mg|5.006000||p +sv_catpvn_nomg|5.007002||p +sv_catpvn||| +sv_catpv||| +sv_catsv_flags||5.007002| +sv_catsv_mg|5.006000||p +sv_catsv_nomg|5.007002||p +sv_catsv||| +sv_chop||| +sv_clean_all||| +sv_clean_objs||| +sv_clear||| +sv_cmp_locale||5.004000| +sv_cmp||| +sv_collxfrm||| +sv_compile_2op||5.008001| +sv_copypv||5.007003| +sv_dec||| +sv_del_backref||| +sv_derived_from||5.004000| +sv_dump||| +sv_dup||| +sv_eq||| +sv_force_normal_flags||5.007001| +sv_force_normal||5.006000| +sv_free2||| +sv_free_arenas||| +sv_free||| +sv_gets||5.004000| +sv_grow||| +sv_inc||| +sv_insert||| +sv_isa||| +sv_isobject||| +sv_iv||5.005000| +sv_len_utf8||5.006000| +sv_len||| +sv_magicext||5.007003| +sv_magic||| +sv_mortalcopy||| +sv_newmortal||| +sv_newref||| +sv_nolocking||5.007003| +sv_nosharing||5.007003| +sv_nounlocking||5.007003| +sv_nv||5.005000| +sv_peek||5.005000| +sv_pos_b2u||5.006000| +sv_pos_u2b||5.006000| +sv_pvbyten_force||5.006000| +sv_pvbyten||5.006000| +sv_pvbyte||5.006000| +sv_pvn_force_flags||5.007002| +sv_pvn_force|||p +sv_pvn_nomg|5.007003||p +sv_pvn|5.006000||p +sv_pvutf8n_force||5.006000| +sv_pvutf8n||5.006000| +sv_pvutf8||5.006000| +sv_pv||5.006000| +sv_recode_to_utf8||5.007003| +sv_reftype||| +sv_release_COW||| +sv_release_IVX||| +sv_replace||| +sv_report_used||| +sv_reset||| +sv_rvweaken||5.006000| +sv_setiv_mg|5.006000||p +sv_setiv||| +sv_setnv_mg|5.006000||p +sv_setnv||| +sv_setpv_mg|5.006000||p +sv_setpvf_mg_nocontext|||pvn +sv_setpvf_mg|5.006000|5.004000|pv +sv_setpvf_nocontext|||vn +sv_setpvf||5.004000|v +sv_setpviv_mg||5.008001| +sv_setpviv||5.008001| +sv_setpvn_mg|5.006000||p +sv_setpvn||| +sv_setpv||| +sv_setref_iv||| +sv_setref_nv||| +sv_setref_pvn||| +sv_setref_pv||| +sv_setref_uv||5.007001| +sv_setsv_cow||| +sv_setsv_flags||5.007002| +sv_setsv_mg|5.006000||p +sv_setsv_nomg|5.007002||p +sv_setsv||| +sv_setuv_mg|5.006000||p +sv_setuv|5.006000||p +sv_tainted||5.004000| +sv_taint||5.004000| +sv_true||5.005000| +sv_unglob||| +sv_uni_display||5.007003| +sv_unmagic||| +sv_unref_flags||5.007001| +sv_unref||| +sv_untaint||5.004000| +sv_upgrade||| +sv_usepvn_mg|5.006000||p +sv_usepvn||| +sv_utf8_decode||5.006000| +sv_utf8_downgrade||5.006000| +sv_utf8_encode||5.006000| +sv_utf8_upgrade_flags||5.007002| +sv_utf8_upgrade||5.007001| +sv_uv|5.006000||p +sv_vcatpvf_mg|5.006000|5.004000|p +sv_vcatpvfn||5.004000| +sv_vcatpvf|5.006000|5.004000|p +sv_vsetpvf_mg|5.006000|5.004000|p +sv_vsetpvfn||5.004000| +sv_vsetpvf|5.006000|5.004000|p +svtype||| +swallow_bom||| +swash_fetch||5.007002| +swash_init||5.006000| +sys_intern_clear||| +sys_intern_dup||| +sys_intern_init||| +taint_env||| +taint_proper||| +tmps_grow||5.006000| +toLOWER||| +toUPPER||| +to_byte_substr||| +to_uni_fold||5.007003| +to_uni_lower_lc||5.006000| +to_uni_lower||5.007003| +to_uni_title_lc||5.006000| +to_uni_title||5.007003| +to_uni_upper_lc||5.006000| +to_uni_upper||5.007003| +to_utf8_case||5.007003| +to_utf8_fold||5.007003| +to_utf8_lower||5.007003| +to_utf8_substr||| +to_utf8_title||5.007003| +to_utf8_upper||5.007003| +tokeq||| +tokereport||| +too_few_arguments||| +too_many_arguments||| +unlnk||| +unpack_rec||| +unpack_str||5.007003| +unpackstring||5.008001| +unshare_hek_or_pvn||| +unshare_hek||| +unsharepvn||5.004000| +upg_version||5.009000| +usage||| +utf16_textfilter||| +utf16_to_utf8_reversed||5.006001| +utf16_to_utf8||5.006001| +utf16rev_textfilter||| +utf8_distance||5.006000| +utf8_hop||5.006000| +utf8_length||5.007001| +utf8_mg_pos_init||| +utf8_mg_pos||| +utf8_to_bytes||5.006001| +utf8_to_uvchr||5.007001| +utf8_to_uvuni||5.007001| +utf8n_to_uvchr||5.007001| +utf8n_to_uvuni||5.007001| +utilize||| +uvchr_to_utf8_flags||5.007003| +uvchr_to_utf8||5.007001| +uvuni_to_utf8_flags||5.007003| +uvuni_to_utf8||5.007001| +validate_suid||| +varname||| +vcmp||5.009000| +vcroak||5.006000| +vdeb||5.007003| +vdie||| +vform||5.006000| +visit||| +vivify_defelem||| +vivify_ref||| +vload_module||5.006000| +vmess||5.006000| +vnewSVpvf|5.006000|5.004000|p +vnormal||5.009002| +vnumify||5.009000| +vstringify||5.009000| +vwarner||5.006000| +vwarn||5.006000| +wait4pid||| +warn_nocontext|||vn +warner_nocontext|||vn +warner||5.006000|v +warn|||v +watch||| +whichsig||| +write_to_stderr||| +yyerror||| +yylex||| +yyparse||| +yywarn||| +); + +if (exists $opt{'list-unsupported'}) { + my $f; + for $f (sort { lc $a cmp lc $b } keys %API) { + next unless $API{$f}{todo}; + print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n"; + } + exit 0; +} + +# Scan for possible replacement candidates + +my(%replace, %need, %hints, %depends); +my $replace = 0; +my $hint = ''; + +while (<DATA>) { + if ($hint) { + if (m{^\s*\*\s(.*?)\s*$}) { + $hints{$hint} ||= ''; # suppress warning with older perls + $hints{$hint} .= "$1\n"; + } + else { + $hint = ''; + } + } + $hint = $1 if m{^\s*$rccs\sHint:\s+(\w+)\s*$}; + + $replace = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$}; + $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)}; + $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce}; + $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$}; + + if (m{^\s*$rccs\s+(\w+)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) { + push @{$depends{$1}}, map { s/\s+//g; $_ } split /,/, $2; + } + + $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)}; +} + +if (exists $opt{'api-info'}) { + my $f; + my $count = 0; + my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$"; + for $f (sort { lc $a cmp lc $b } keys %API) { + next unless $f =~ /$match/; + print "\n=== $f ===\n\n"; + my $info = 0; + if ($API{$f}{base} || $API{$f}{todo}) { + my $base = format_version($API{$f}{base} || $API{$f}{todo}); + print "Supported at least starting from perl-$base.\n"; + $info++; + } + if ($API{$f}{provided}) { + my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003"; + print "Support by $ppport provided back to perl-$todo.\n"; + print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f}; + print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f}; + print "$hints{$f}" if exists $hints{$f}; + $info++; + } + unless ($info) { + print "No portability information available.\n"; + } + $count++; + } + if ($count > 0) { + print "\n"; + } + else { + print "Found no API matching '$opt{'api-info'}'.\n"; + } + exit 0; +} + +if (exists $opt{'list-provided'}) { + my $f; + for $f (sort { lc $a cmp lc $b } keys %API) { + next unless $API{$f}{provided}; + my @flags; + push @flags, 'explicit' if exists $need{$f}; + push @flags, 'depend' if exists $depends{$f}; + push @flags, 'hint' if exists $hints{$f}; + my $flags = @flags ? ' ['.join(', ', @flags).']' : ''; + print "$f$flags\n"; + } + exit 0; +} + +my @files; +my @srcext = qw( xs c h cc cpp ); +my $srcext = join '|', @srcext; + +if (@ARGV) { + my %seen; + @files = grep { -f && !exists $seen{$_} } map { glob $_ } @ARGV; +} +else { + eval { + require File::Find; + File::Find::find(sub { + $File::Find::name =~ /\.($srcext)$/i + and push @files, $File::Find::name; + }, '.'); + }; + if ($@) { + @files = map { glob "*.$_" } @srcext; + } +} + +if (!@ARGV || $opt{filter}) { + my(@in, @out); + my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files; + for (@files) { + my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/\.($srcext)$/i; + push @{ $out ? \@out : \@in }, $_; + } + if (@ARGV && @out) { + warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out); + } + @files = @in; +} + +unless (@files) { + die "No input files given!\n"; +} + +my(%files, %global, %revreplace); +%revreplace = reverse %replace; +my $filename; +my $patch_opened = 0; + +for $filename (@files) { + unless (open IN, "<$filename") { + warn "Unable to read from $filename: $!\n"; + next; + } + + info("Scanning $filename ..."); + + my $c = do { local $/; <IN> }; + close IN; + + my %file = (orig => $c, changes => 0); + + # temporarily remove C comments from the code + my @ccom; + $c =~ s{ + ( + [^"'/]+ + | + (?:"[^"\\]*(?:\\.[^"\\]*)*" [^"'/]*)+ + | + (?:'[^'\\]*(?:\\.[^'\\]*)*' [^"'/]*)+ + ) + | + (/ (?: + \*[^*]*\*+(?:[^$ccs][^*]*\*+)* / + | + /[^\r\n]* + )) + }{ + defined $2 and push @ccom, $2; + defined $1 ? $1 : "$ccs$#ccom$cce"; + }egsx; + + $file{ccom} = \@ccom; + $file{code} = $c; + $file{has_inc_ppport} = ($c =~ /#.*include.*\Q$ppport\E/); + + my $func; + + for $func (keys %API) { + my $match = $func; + $match .= "|$revreplace{$func}" if exists $revreplace{$func}; + if ($c =~ /\b(?:Perl_)?($match)\b/) { + $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func}; + $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/; + if (exists $API{$func}{provided}) { + if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) { + $file{uses}{$func}++; + my @deps = rec_depend($func); + if (@deps) { + $file{uses_deps}{$func} = \@deps; + for (@deps) { + $file{uses}{$_} = 0 unless exists $file{uses}{$_}; + } + } + for ($func, @deps) { + if (exists $need{$_}) { + $file{needs}{$_} = 'static'; + } + } + } + } + if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) { + if ($c =~ /\b$func\b/) { + $file{uses_todo}{$func}++; + } + } + } + } + + while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) { + if (exists $need{$2}) { + $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++; + } + else { + warning("Possibly wrong #define $1 in $filename"); + } + } + + for (qw(uses needs uses_todo needed_global needed_static)) { + for $func (keys %{$file{$_}}) { + push @{$global{$_}{$func}}, $filename; + } + } + + $files{$filename} = \%file; +} + +# Globally resolve NEED_'s +my $need; +for $need (keys %{$global{needs}}) { + if (@{$global{needs}{$need}} > 1) { + my @targets = @{$global{needs}{$need}}; + my @t = grep $files{$_}{needed_global}{$need}, @targets; + @targets = @t if @t; + @t = grep /\.xs$/i, @targets; + @targets = @t if @t; + my $target = shift @targets; + $files{$target}{needs}{$need} = 'global'; + for (@{$global{needs}{$need}}) { + $files{$_}{needs}{$need} = 'extern' if $_ ne $target; + } + } +} + +for $filename (@files) { + exists $files{$filename} or next; + + info("=== Analyzing $filename ==="); + + my %file = %{$files{$filename}}; + my $func; + my $c = $file{code}; + + for $func (sort keys %{$file{uses_Perl}}) { + if ($API{$func}{varargs}) { + my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))} + { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge); + if ($changes) { + warning("Doesn't pass interpreter argument aTHX to Perl_$func"); + $file{changes} += $changes; + } + } + else { + warning("Uses Perl_$func instead of $func"); + $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*} + {$func$1(}g); + } + } + + for $func (sort keys %{$file{uses_replace}}) { + warning("Uses $func instead of $replace{$func}"); + $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g); + } + + for $func (sort keys %{$file{uses}}) { + next unless $file{uses}{$func}; # if it's only a dependency + if (exists $file{uses_deps}{$func}) { + diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}})); + } + elsif (exists $replace{$func}) { + warning("Uses $func instead of $replace{$func}"); + $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g); + } + else { + diag("Uses $func"); + } + hint($func); + } + + for $func (sort keys %{$file{uses_todo}}) { + warning("Uses $func, which may not be portable below perl ", + format_version($API{$func}{todo})); + } + + for $func (sort keys %{$file{needed_static}}) { + my $message = ''; + if (not exists $file{uses}{$func}) { + $message = "No need to define NEED_$func if $func is never used"; + } + elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') { + $message = "No need to define NEED_$func when already needed globally"; + } + if ($message) { + diag($message); + $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg); + } + } + + for $func (sort keys %{$file{needed_global}}) { + my $message = ''; + if (not exists $global{uses}{$func}) { + $message = "No need to define NEED_${func}_GLOBAL if $func is never used"; + } + elsif (exists $file{needs}{$func}) { + if ($file{needs}{$func} eq 'extern') { + $message = "No need to define NEED_${func}_GLOBAL when already needed globally"; + } + elsif ($file{needs}{$func} eq 'static') { + $message = "No need to define NEED_${func}_GLOBAL when only used in this file"; + } + } + if ($message) { + diag($message); + $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg); + } + } + + $file{needs_inc_ppport} = keys %{$file{uses}}; + + if ($file{needs_inc_ppport}) { + my $pp = ''; + + for $func (sort keys %{$file{needs}}) { + my $type = $file{needs}{$func}; + next if $type eq 'extern'; + my $suffix = $type eq 'global' ? '_GLOBAL' : ''; + unless (exists $file{"needed_$type"}{$func}) { + if ($type eq 'global') { + diag("Files [@{$global{needs}{$func}}] need $func, adding global request"); + } + else { + diag("File needs $func, adding static request"); + } + $pp .= "#define NEED_$func$suffix\n"; + } + } + + if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) { + $pp = ''; + $file{changes}++; + } + + unless ($file{has_inc_ppport}) { + diag("Needs to include '$ppport'"); + $pp .= qq(#include "$ppport"\n) + } + + if ($pp) { + $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms) + || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m) + || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m) + || ($c =~ s/^/$pp/); + } + } + else { + if ($file{has_inc_ppport}) { + diag("No need to include '$ppport'"); + $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m); + } + } + + # put back in our C comments + my $ix; + my $cppc = 0; + my @ccom = @{$file{ccom}}; + for $ix (0 .. $#ccom) { + if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) { + $cppc++; + $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/; + } + else { + $c =~ s/$rccs$ix$rcce/$ccom[$ix]/; + } + } + + if ($cppc) { + my $s = $cppc != 1 ? 's' : ''; + warning("Uses $cppc C++ style comment$s, which is not portable"); + } + + if ($file{changes}) { + if (exists $opt{copy}) { + my $newfile = "$filename$opt{copy}"; + if (-e $newfile) { + error("'$newfile' already exists, refusing to write copy of '$filename'"); + } + else { + local *F; + if (open F, ">$newfile") { + info("Writing copy of '$filename' with changes to '$newfile'"); + print F $c; + close F; + } + else { + error("Cannot open '$newfile' for writing: $!"); + } + } + } + elsif (exists $opt{patch} || $opt{changes}) { + if (exists $opt{patch}) { + unless ($patch_opened) { + if (open PATCH, ">$opt{patch}") { + $patch_opened = 1; + } + else { + error("Cannot open '$opt{patch}' for writing: $!"); + delete $opt{patch}; + $opt{changes} = 1; + goto fallback; + } + } + mydiff(\*PATCH, $filename, $c); + } + else { +fallback: + info("Suggested changes:"); + mydiff(\*STDOUT, $filename, $c); + } + } + else { + my $s = $file{changes} == 1 ? '' : 's'; + info("$file{changes} potentially required change$s detected"); + } + } + else { + info("Looks good"); + } +} + +close PATCH if $patch_opened; + +exit 0; + + +sub mydiff +{ + local *F = shift; + my($file, $str) = @_; + my $diff; + + if (exists $opt{diff}) { + $diff = run_diff($opt{diff}, $file, $str); + } + + if (!defined $diff and can_use('Text::Diff')) { + $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' }); + $diff = <<HEADER . $diff; +--- $file ++++ $file.patched +HEADER + } + + if (!defined $diff) { + $diff = run_diff('diff -u', $file, $str); + } + + if (!defined $diff) { + $diff = run_diff('diff', $file, $str); + } + + if (!defined $diff) { + error("Cannot generate a diff. Please install Text::Diff or use --copy."); + return; + } + + print F $diff; + +} + +sub run_diff +{ + my($prog, $file, $str) = @_; + my $tmp = 'dppptemp'; + my $suf = 'aaa'; + my $diff = ''; + local *F; + + while (-e "$tmp.$suf") { $suf++ } + $tmp = "$tmp.$suf"; + + if (open F, ">$tmp") { + print F $str; + close F; + + if (open F, "$prog $file $tmp |") { + while (<F>) { + s/\Q$tmp\E/$file.patched/; + $diff .= $_; + } + close F; + unlink $tmp; + return $diff; + } + + unlink $tmp; + } + else { + error("Cannot open '$tmp' for writing: $!"); + } + + return undef; +} + +sub can_use +{ + eval "use @_;"; + return $@ eq ''; +} + +sub rec_depend +{ + my $func = shift; + my %seen; + return () unless exists $depends{$func}; + grep !$seen{$_}++, map { ($_, rec_depend($_)) } @{$depends{$func}}; +} + +sub parse_version +{ + my $ver = shift; + + if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) { + return ($1, $2, $3); + } + elsif ($ver !~ /^\d+\.[\d_]+$/) { + die "cannot parse version '$ver'\n"; + } + + $ver =~ s/_//g; + $ver =~ s/$/000000/; + + my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; + + $v = int $v; + $s = int $s; + + if ($r < 5 || ($r == 5 && $v < 6)) { + if ($s % 10) { + die "cannot parse version '$ver'\n"; + } + } + + return ($r, $v, $s); +} + +sub format_version +{ + my $ver = shift; + + $ver =~ s/$/000000/; + my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/; + + $v = int $v; + $s = int $s; + + if ($r < 5 || ($r == 5 && $v < 6)) { + if ($s % 10) { + die "invalid version '$ver'\n"; + } + $s /= 10; + + $ver = sprintf "%d.%03d", $r, $v; + $s > 0 and $ver .= sprintf "_%02d", $s; + + return $ver; + } + + return sprintf "%d.%d.%d", $r, $v, $s; +} + +sub info +{ + $opt{quiet} and return; + print @_, "\n"; +} + +sub diag +{ + $opt{quiet} and return; + $opt{diag} and print @_, "\n"; +} + +sub warning +{ + $opt{quiet} and return; + print "*** ", @_, "\n"; +} + +sub error +{ + print "*** ERROR: ", @_, "\n"; +} + +my %given_hints; +sub hint +{ + $opt{quiet} and return; + $opt{hints} or return; + my $func = shift; + exists $hints{$func} or return; + $given_hints{$func}++ and return; + my $hint = $hints{$func}; + $hint =~ s/^/ /mg; + print " --- hint for $func ---\n", $hint; +} + +sub usage +{ + my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms; + my %M = ( 'I' => '*' ); + $usage =~ s/^\s*perl\s+\S+/$^X $0/; + $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g; + + print <<ENDUSAGE; + +Usage: $usage + +See perldoc $0 for details. + +ENDUSAGE + + exit 2; +} + +__DATA__ +*/ + +#ifndef _P_P_PORTABILITY_H_ +#define _P_P_PORTABILITY_H_ + +#ifndef DPPP_NAMESPACE +#define DPPP_NAMESPACE DPPP_ +#endif + +#define DPPP_CAT2(x, y) CAT2(x, y) +#define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name) + +#ifndef PERL_REVISION +#if !defined(__PATCHLEVEL_H_INCLUDED__) && \ + !(defined(PATCHLEVEL) && defined(SUBVERSION)) +#define PERL_PATCHLEVEL_H_IMPLICIT +#include <patchlevel.h> +#endif +#if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL))) +#include <could_not_find_Perl_patchlevel.h> +#endif +#ifndef PERL_REVISION +#define PERL_REVISION (5) +/* Replace: 1 */ +#define PERL_VERSION PATCHLEVEL +#define PERL_SUBVERSION SUBVERSION +/* Replace PERL_PATCHLEVEL with PERL_VERSION */ +/* Replace: 0 */ +#endif +#endif + +#define PERL_BCDVERSION \ + ((PERL_REVISION * 0x1000000L) + (PERL_VERSION * 0x1000L) + PERL_SUBVERSION) + +/* It is very unlikely that anyone will try to use this with Perl 6 + (or greater), but who knows. + */ +#if PERL_REVISION != 5 +#error ppport.h only works with Perl version 5 +#endif /* PERL_REVISION != 5 */ + +#ifdef I_LIMITS +#include <limits.h> +#endif + +#ifndef PERL_UCHAR_MIN +#define PERL_UCHAR_MIN ((unsigned char)0) +#endif + +#ifndef PERL_UCHAR_MAX +#ifdef UCHAR_MAX +#define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX) +#else +#ifdef MAXUCHAR +#define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR) +#else +#define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0) +#endif +#endif +#endif + +#ifndef PERL_USHORT_MIN +#define PERL_USHORT_MIN ((unsigned short)0) +#endif + +#ifndef PERL_USHORT_MAX +#ifdef USHORT_MAX +#define PERL_USHORT_MAX ((unsigned short)USHORT_MAX) +#else +#ifdef MAXUSHORT +#define PERL_USHORT_MAX ((unsigned short)MAXUSHORT) +#else +#ifdef USHRT_MAX +#define PERL_USHORT_MAX ((unsigned short)USHRT_MAX) +#else +#define PERL_USHORT_MAX ((unsigned short)~(unsigned)0) +#endif +#endif +#endif +#endif + +#ifndef PERL_SHORT_MAX +#ifdef SHORT_MAX +#define PERL_SHORT_MAX ((short)SHORT_MAX) +#else +#ifdef MAXSHORT /* Often used in <values.h> */ +#define PERL_SHORT_MAX ((short)MAXSHORT) +#else +#ifdef SHRT_MAX +#define PERL_SHORT_MAX ((short)SHRT_MAX) +#else +#define PERL_SHORT_MAX ((short)(PERL_USHORT_MAX >> 1)) +#endif +#endif +#endif +#endif + +#ifndef PERL_SHORT_MIN +#ifdef SHORT_MIN +#define PERL_SHORT_MIN ((short)SHORT_MIN) +#else +#ifdef MINSHORT +#define PERL_SHORT_MIN ((short)MINSHORT) +#else +#ifdef SHRT_MIN +#define PERL_SHORT_MIN ((short)SHRT_MIN) +#else +#define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3)) +#endif +#endif +#endif +#endif + +#ifndef PERL_UINT_MAX +#ifdef UINT_MAX +#define PERL_UINT_MAX ((unsigned int)UINT_MAX) +#else +#ifdef MAXUINT +#define PERL_UINT_MAX ((unsigned int)MAXUINT) +#else +#define PERL_UINT_MAX (~(unsigned int)0) +#endif +#endif +#endif + +#ifndef PERL_UINT_MIN +#define PERL_UINT_MIN ((unsigned int)0) +#endif + +#ifndef PERL_INT_MAX +#ifdef INT_MAX +#define PERL_INT_MAX ((int)INT_MAX) +#else +#ifdef MAXINT /* Often used in <values.h> */ +#define PERL_INT_MAX ((int)MAXINT) +#else +#define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1)) +#endif +#endif +#endif + +#ifndef PERL_INT_MIN +#ifdef INT_MIN +#define PERL_INT_MIN ((int)INT_MIN) +#else +#ifdef MININT +#define PERL_INT_MIN ((int)MININT) +#else +#define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3)) +#endif +#endif +#endif + +#ifndef PERL_ULONG_MAX +#ifdef ULONG_MAX +#define PERL_ULONG_MAX ((unsigned long)ULONG_MAX) +#else +#ifdef MAXULONG +#define PERL_ULONG_MAX ((unsigned long)MAXULONG) +#else +#define PERL_ULONG_MAX (~(unsigned long)0) +#endif +#endif +#endif + +#ifndef PERL_ULONG_MIN +#define PERL_ULONG_MIN ((unsigned long)0L) +#endif + +#ifndef PERL_LONG_MAX +#ifdef LONG_MAX +#define PERL_LONG_MAX ((long)LONG_MAX) +#else +#ifdef MAXLONG +#define PERL_LONG_MAX ((long)MAXLONG) +#else +#define PERL_LONG_MAX ((long)(PERL_ULONG_MAX >> 1)) +#endif +#endif +#endif + +#ifndef PERL_LONG_MIN +#ifdef LONG_MIN +#define PERL_LONG_MIN ((long)LONG_MIN) +#else +#ifdef MINLONG +#define PERL_LONG_MIN ((long)MINLONG) +#else +#define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3)) +#endif +#endif +#endif + +#if defined(HAS_QUAD) && (defined(convex) || defined(uts)) +#ifndef PERL_UQUAD_MAX +#ifdef ULONGLONG_MAX +#define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX) +#else +#ifdef MAXULONGLONG +#define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG) +#else +#define PERL_UQUAD_MAX (~(unsigned long long)0) +#endif +#endif +#endif + +#ifndef PERL_UQUAD_MIN +#define PERL_UQUAD_MIN ((unsigned long long)0L) +#endif + +#ifndef PERL_QUAD_MAX +#ifdef LONGLONG_MAX +#define PERL_QUAD_MAX ((long long)LONGLONG_MAX) +#else +#ifdef MAXLONGLONG +#define PERL_QUAD_MAX ((long long)MAXLONGLONG) +#else +#define PERL_QUAD_MAX ((long long)(PERL_UQUAD_MAX >> 1)) +#endif +#endif +#endif + +#ifndef PERL_QUAD_MIN +#ifdef LONGLONG_MIN +#define PERL_QUAD_MIN ((long long)LONGLONG_MIN) +#else +#ifdef MINLONGLONG +#define PERL_QUAD_MIN ((long long)MINLONGLONG) +#else +#define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3)) +#endif +#endif +#endif +#endif + +/* This is based on code from 5.003 perl.h */ +#ifdef HAS_QUAD +#ifdef cray +#ifndef IVTYPE +#define IVTYPE int +#endif + +#ifndef IV_MIN +#define IV_MIN PERL_INT_MIN +#endif + +#ifndef IV_MAX +#define IV_MAX PERL_INT_MAX +#endif + +#ifndef UV_MIN +#define UV_MIN PERL_UINT_MIN +#endif + +#ifndef UV_MAX +#define UV_MAX PERL_UINT_MAX +#endif + +#ifdef INTSIZE +#ifndef IVSIZE +#define IVSIZE INTSIZE +#endif + +#endif +#else +#if defined(convex) || defined(uts) +#ifndef IVTYPE +#define IVTYPE long long +#endif + +#ifndef IV_MIN +#define IV_MIN PERL_QUAD_MIN +#endif + +#ifndef IV_MAX +#define IV_MAX PERL_QUAD_MAX +#endif + +#ifndef UV_MIN +#define UV_MIN PERL_UQUAD_MIN +#endif + +#ifndef UV_MAX +#define UV_MAX PERL_UQUAD_MAX +#endif + +#ifdef LONGLONGSIZE +#ifndef IVSIZE +#define IVSIZE LONGLONGSIZE +#endif + +#endif +#else +#ifndef IVTYPE +#define IVTYPE long +#endif + +#ifndef IV_MIN +#define IV_MIN PERL_LONG_MIN +#endif + +#ifndef IV_MAX +#define IV_MAX PERL_LONG_MAX +#endif + +#ifndef UV_MIN +#define UV_MIN PERL_ULONG_MIN +#endif + +#ifndef UV_MAX +#define UV_MAX PERL_ULONG_MAX +#endif + +#ifdef LONGSIZE +#ifndef IVSIZE +#define IVSIZE LONGSIZE +#endif + +#endif +#endif +#endif +#ifndef IVSIZE +#define IVSIZE 8 +#endif + +#ifndef PERL_QUAD_MIN +#define PERL_QUAD_MIN IV_MIN +#endif + +#ifndef PERL_QUAD_MAX +#define PERL_QUAD_MAX IV_MAX +#endif + +#ifndef PERL_UQUAD_MIN +#define PERL_UQUAD_MIN UV_MIN +#endif + +#ifndef PERL_UQUAD_MAX +#define PERL_UQUAD_MAX UV_MAX +#endif + +#else +#ifndef IVTYPE +#define IVTYPE long +#endif + +#ifndef IV_MIN +#define IV_MIN PERL_LONG_MIN +#endif + +#ifndef IV_MAX +#define IV_MAX PERL_LONG_MAX +#endif + +#ifndef UV_MIN +#define UV_MIN PERL_ULONG_MIN +#endif + +#ifndef UV_MAX +#define UV_MAX PERL_ULONG_MAX +#endif + +#endif + +#ifndef IVSIZE +#ifdef LONGSIZE +#define IVSIZE LONGSIZE +#else +#define IVSIZE 4 /* A bold guess, but the best we can make. */ +#endif +#endif +#ifndef UVTYPE +#define UVTYPE unsigned IVTYPE +#endif + +#ifndef UVSIZE +#define UVSIZE IVSIZE +#endif + +#ifndef sv_setuv +#define sv_setuv(sv, uv) \ + STMT_START \ + { \ + UV TeMpUv = uv; \ + if (TeMpUv <= IV_MAX) \ + sv_setiv(sv, TeMpUv); \ + else \ + sv_setnv(sv, (double)TeMpUv); \ + } \ + STMT_END +#endif + +#ifndef newSVuv +#define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv)) +#endif +#ifndef sv_2uv +#define sv_2uv(sv) \ + ((PL_Sv = (sv)), (UV)(SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv))) +#endif + +#ifndef SvUVX +#define SvUVX(sv) ((UV)SvIVX(sv)) +#endif + +#ifndef SvUVXx +#define SvUVXx(sv) SvUVX(sv) +#endif + +#ifndef SvUV +#define SvUV(sv) (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv)) +#endif + +#ifndef SvUVx +#define SvUVx(sv) ((PL_Sv = (sv)), SvUV(PL_Sv)) +#endif + +/* Hint: sv_uv + * Always use the SvUVx() macro instead of sv_uv(). + */ +#ifndef sv_uv +#define sv_uv(sv) SvUVx(sv) +#endif +#ifndef XST_mUV +#define XST_mUV(i, v) (ST(i) = sv_2mortal(newSVuv(v))) +#endif + +#ifndef XSRETURN_UV +#define XSRETURN_UV(v) \ + STMT_START \ + { \ + XST_mUV(0, v); \ + XSRETURN(1); \ + } \ + STMT_END +#endif +#ifndef PUSHu +#define PUSHu(u) \ + STMT_START \ + { \ + sv_setuv(TARG, (UV)(u)); \ + PUSHTARG; \ + } \ + STMT_END +#endif + +#ifndef XPUSHu +#define XPUSHu(u) \ + STMT_START \ + { \ + sv_setuv(TARG, (UV)(u)); \ + XPUSHTARG; \ + } \ + STMT_END +#endif + +#if (PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION <= 5)) +/* Replace: 1 */ +#define PL_DBsingle DBsingle +#define PL_DBsub DBsub +#define PL_Sv Sv +#define PL_compiling compiling +#define PL_copline copline +#define PL_curcop curcop +#define PL_curstash curstash +#define PL_debstash debstash +#define PL_defgv defgv +#define PL_diehook diehook +#define PL_dirty dirty +#define PL_dowarn dowarn +#define PL_errgv errgv +#define PL_hexdigit hexdigit +#define PL_hints hints +#define PL_na na +#define PL_no_modify no_modify +#define PL_perl_destruct_level perl_destruct_level +#define PL_perldb perldb +#define PL_ppaddr ppaddr +#define PL_rsfp_filters rsfp_filters +#define PL_rsfp rsfp +#define PL_stack_base stack_base +#define PL_stack_sp stack_sp +#define PL_stdingv stdingv +#define PL_sv_arenaroot sv_arenaroot +#define PL_sv_no sv_no +#define PL_sv_undef sv_undef +#define PL_sv_yes sv_yes +#define PL_tainted tainted +#define PL_tainting tainting +/* Replace: 0 */ +#endif + +#ifndef PERL_UNUSED_DECL +#ifdef HASATTRIBUTE +#if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER) +#define PERL_UNUSED_DECL +#else +#define PERL_UNUSED_DECL __attribute__((unused)) +#endif +#else +#define PERL_UNUSED_DECL +#endif +#endif +#ifndef NOOP +#define NOOP (void)0 +#endif + +#ifndef dNOOP +#define dNOOP extern int Perl___notused PERL_UNUSED_DECL +#endif + +#ifndef NVTYPE +#if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) +#define NVTYPE long double +#else +#define NVTYPE double +#endif +typedef NVTYPE NV; +#endif + +#ifndef INT2PTR + +#if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE) +#define PTRV UV +#define INT2PTR(any, d) (any)(d) +#else +#if PTRSIZE == LONGSIZE +#define PTRV unsigned long +#else +#define PTRV unsigned +#endif +#define INT2PTR(any, d) (any)(PTRV)(d) +#endif + +#define NUM2PTR(any, d) (any)(PTRV)(d) +#define PTR2IV(p) INT2PTR(IV, p) +#define PTR2UV(p) INT2PTR(UV, p) +#define PTR2NV(p) NUM2PTR(NV, p) + +#if PTRSIZE == LONGSIZE +#define PTR2ul(p) (unsigned long)(p) +#else +#define PTR2ul(p) INT2PTR(unsigned long, p) +#endif + +#endif /* !INT2PTR */ + +#undef START_EXTERN_C +#undef END_EXTERN_C +#undef EXTERN_C +#ifdef __cplusplus +#define START_EXTERN_C extern "C" { +#define END_EXTERN_C } +#define EXTERN_C extern "C" +#else +#define START_EXTERN_C +#define END_EXTERN_C +#define EXTERN_C extern +#endif + +#ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN +#if defined(__STRICT_ANSI__) && defined(PERL_GCC_PEDANTIC) +#define PERL_GCC_BRACE_GROUPS_FORBIDDEN +#endif +#endif + +#undef STMT_START +#undef STMT_END +#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && \ + !defined(__cplusplus) +#define STMT_START (void)( /* gcc supports ``({ STATEMENTS; })'' */ +#define STMT_END ) +#else +#if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && \ + !defined(__GNUC__) +#define STMT_START if (1) +#define STMT_END else(void) 0 +#else +#define STMT_START do +#define STMT_END while (0) +#endif +#endif +#ifndef boolSV +#define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no) +#endif + +/* DEFSV appears first in 5.004_56 */ +#ifndef DEFSV +#define DEFSV GvSV(PL_defgv) +#endif + +#ifndef SAVE_DEFSV +#define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv)) +#endif + +/* Older perls (<=5.003) lack AvFILLp */ +#ifndef AvFILLp +#define AvFILLp AvFILL +#endif +#ifndef ERRSV +#define ERRSV get_sv("@", FALSE) +#endif +#ifndef newSVpvn +#define newSVpvn(data, len) \ + ((data) ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) : newSV(0)) +#endif + +/* Hint: gv_stashpvn + * This function's backport doesn't support the length parameter, but + * rather ignores it. Portability can only be ensured if the length + * parameter is used for speed reasons, but the length can always be + * correctly computed from the string argument. + */ +#ifndef gv_stashpvn +#define gv_stashpvn(str, len, create) gv_stashpv(str, create) +#endif + +/* Replace: 1 */ +#ifndef get_cv +#define get_cv perl_get_cv +#endif + +#ifndef get_sv +#define get_sv perl_get_sv +#endif + +#ifndef get_av +#define get_av perl_get_av +#endif + +#ifndef get_hv +#define get_hv perl_get_hv +#endif + +/* Replace: 0 */ + +#ifdef HAS_MEMCMP +#ifndef memNE +#define memNE(s1, s2, l) (memcmp(s1, s2, l)) +#endif + +#ifndef memEQ +#define memEQ(s1, s2, l) (!memcmp(s1, s2, l)) +#endif + +#else +#ifndef memNE +#define memNE(s1, s2, l) (bcmp(s1, s2, l)) +#endif + +#ifndef memEQ +#define memEQ(s1, s2, l) (!bcmp(s1, s2, l)) +#endif + +#endif +#ifndef MoveD +#define MoveD(s, d, n, t) memmove((char *)(d), (char *)(s), (n) * sizeof(t)) +#endif + +#ifndef CopyD +#define CopyD(s, d, n, t) memcpy((char *)(d), (char *)(s), (n) * sizeof(t)) +#endif + +#ifdef HAS_MEMSET +#ifndef ZeroD +#define ZeroD(d, n, t) memzero((char *)(d), (n) * sizeof(t)) +#endif + +#else +#ifndef ZeroD +#define ZeroD(d, n, t) ((void)memzero((char *)(d), (n) * sizeof(t)), d) +#endif + +#endif +#ifndef Poison +#define Poison(d, n, t) (void)memset((char *)(d), 0xAB, (n) * sizeof(t)) +#endif +#ifndef dUNDERBAR +#define dUNDERBAR dNOOP +#endif + +#ifndef UNDERBAR +#define UNDERBAR DEFSV +#endif +#ifndef dAX +#define dAX I32 ax = MARK - PL_stack_base + 1 +#endif + +#ifndef dITEMS +#define dITEMS I32 items = SP - MARK +#endif +#ifndef dXSTARG +#define dXSTARG SV *targ = sv_newmortal() +#endif +#ifndef dTHR +#define dTHR dNOOP +#endif +#ifndef dTHX +#define dTHX dNOOP +#endif + +#ifndef dTHXa +#define dTHXa(x) dNOOP +#endif +#ifndef pTHX +#define pTHX void +#endif + +#ifndef pTHX_ +#define pTHX_ +#endif + +#ifndef aTHX +#define aTHX +#endif + +#ifndef aTHX_ +#define aTHX_ +#endif +#ifndef dTHXoa +#define dTHXoa(x) dTHXa(x) +#endif +#ifndef PUSHmortal +#define PUSHmortal PUSHs(sv_newmortal()) +#endif + +#ifndef mPUSHp +#define mPUSHp(p, l) sv_setpvn_mg(PUSHmortal, (p), (l)) +#endif + +#ifndef mPUSHn +#define mPUSHn(n) sv_setnv_mg(PUSHmortal, (NV)(n)) +#endif + +#ifndef mPUSHi +#define mPUSHi(i) sv_setiv_mg(PUSHmortal, (IV)(i)) +#endif + +#ifndef mPUSHu +#define mPUSHu(u) sv_setuv_mg(PUSHmortal, (UV)(u)) +#endif +#ifndef XPUSHmortal +#define XPUSHmortal XPUSHs(sv_newmortal()) +#endif + +#ifndef mXPUSHp +#define mXPUSHp(p, l) \ + STMT_START \ + { \ + EXTEND(sp, 1); \ + sv_setpvn_mg(PUSHmortal, (p), (l)); \ + } \ + STMT_END +#endif + +#ifndef mXPUSHn +#define mXPUSHn(n) \ + STMT_START \ + { \ + EXTEND(sp, 1); \ + sv_setnv_mg(PUSHmortal, (NV)(n)); \ + } \ + STMT_END +#endif + +#ifndef mXPUSHi +#define mXPUSHi(i) \ + STMT_START \ + { \ + EXTEND(sp, 1); \ + sv_setiv_mg(PUSHmortal, (IV)(i)); \ + } \ + STMT_END +#endif + +#ifndef mXPUSHu +#define mXPUSHu(u) \ + STMT_START \ + { \ + EXTEND(sp, 1); \ + sv_setuv_mg(PUSHmortal, (UV)(u)); \ + } \ + STMT_END +#endif + +/* Replace: 1 */ +#ifndef call_sv +#define call_sv perl_call_sv +#endif + +#ifndef call_pv +#define call_pv perl_call_pv +#endif + +#ifndef call_argv +#define call_argv perl_call_argv +#endif + +#ifndef call_method +#define call_method perl_call_method +#endif +#ifndef eval_sv +#define eval_sv perl_eval_sv +#endif + +/* Replace: 0 */ + +/* Replace perl_eval_pv with eval_pv */ +/* eval_pv depends on eval_sv */ + +#ifndef eval_pv +#if defined(NEED_eval_pv) +static SV *DPPP_(my_eval_pv)(char *p, I32 croak_on_error); +static +#else +extern SV *DPPP_(my_eval_pv)(char *p, I32 croak_on_error); +#endif + +#ifdef eval_pv +#undef eval_pv +#endif +#define eval_pv(a, b) DPPP_(my_eval_pv)(aTHX_ a, b) +#define Perl_eval_pv DPPP_(my_eval_pv) + +#if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL) + + SV *DPPP_(my_eval_pv)(char *p, I32 croak_on_error) +{ + dSP; + SV *sv = newSVpv(p, 0); + + PUSHMARK(sp); + eval_sv(sv, G_SCALAR); + SvREFCNT_dec(sv); + + SPAGAIN; + sv = POPs; + PUTBACK; + + if (croak_on_error && SvTRUE(GvSV(errgv))) + croak(SvPVx(GvSV(errgv), na)); + + return sv; +} + +#endif +#endif +#ifndef newRV_inc +#define newRV_inc(sv) newRV(sv) /* Replace */ +#endif + +#ifndef newRV_noinc +#if defined(NEED_newRV_noinc) +static SV *DPPP_(my_newRV_noinc)(SV *sv); +static +#else +extern SV *DPPP_(my_newRV_noinc)(SV *sv); +#endif + +#ifdef newRV_noinc +#undef newRV_noinc +#endif +#define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a) +#define Perl_newRV_noinc DPPP_(my_newRV_noinc) + +#if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL) + SV *DPPP_(my_newRV_noinc)(SV *sv) +{ + SV *rv = (SV *)newRV(sv); + SvREFCNT_dec(sv); + return rv; +} +#endif +#endif + +/* Hint: newCONSTSUB + * Returns a CV* as of perl-5.7.1. This return value is not supported + * by Devel::PPPort. + */ + +/* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */ +#if ((PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION < 63))) && \ + ((PERL_VERSION != 4) || (PERL_SUBVERSION != 5)) +#if defined(NEED_newCONSTSUB) +static void DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv); +static +#else +extern void DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv); +#endif + +#ifdef newCONSTSUB +#undef newCONSTSUB +#endif +#define newCONSTSUB(a, b, c) DPPP_(my_newCONSTSUB)(aTHX_ a, b, c) +#define Perl_newCONSTSUB DPPP_(my_newCONSTSUB) + +#if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL) + + void DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv) +{ + U32 oldhints = PL_hints; + HV *old_cop_stash = PL_curcop->cop_stash; + HV *old_curstash = PL_curstash; + line_t oldline = PL_curcop->cop_line; + PL_curcop->cop_line = PL_copline; + + PL_hints &= ~HINT_BLOCK_SCOPE; + if (stash) + PL_curstash = PL_curcop->cop_stash = stash; + + newSUB( + +#if ((PERL_VERSION < 3) || ((PERL_VERSION == 3) && (PERL_SUBVERSION < 22))) + start_subparse(), +#elif ((PERL_VERSION == 3) && (PERL_SUBVERSION == 22)) + start_subparse(0), +#else /* 5.003_23 onwards */ + start_subparse(FALSE, 0), +#endif + + newSVOP(OP_CONST, 0, newSVpv(name, 0)), + newSVOP(OP_CONST, 0, &PL_sv_no), /* SvPV(&PL_sv_no) == "" -- GMB */ + newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv))); + + PL_hints = oldhints; + PL_curcop->cop_stash = old_cop_stash; + PL_curstash = old_curstash; + PL_curcop->cop_line = oldline; +} +#endif +#endif + +/* + * Boilerplate macros for initializing and accessing interpreter-local + * data from C. All statics in extensions should be reworked to use + * this, if you want to make the extension thread-safe. See ext/re/re.xs + * for an example of the use of these macros. + * + * Code that uses these macros is responsible for the following: + * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts" + * 2. Declare a typedef named my_cxt_t that is a structure that contains + * all the data that needs to be interpreter-local. + * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t. + * 4. Use the MY_CXT_INIT macro such that it is called exactly once + * (typically put in the BOOT: section). + * 5. Use the members of the my_cxt_t structure everywhere as + * MY_CXT.member. + * 6. Use the dMY_CXT macro (a declaration) in all the functions that + * access MY_CXT. + */ + +#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || defined(PERL_CAPI) || \ + defined(PERL_IMPLICIT_CONTEXT) + +#ifndef START_MY_CXT + +/* This must appear in all extensions that define a my_cxt_t structure, + * right after the definition (i.e. at file scope). The non-threads + * case below uses it to declare the data as static. */ +#define START_MY_CXT + +#if (PERL_VERSION < 4 || (PERL_VERSION == 4 && PERL_SUBVERSION < 68)) +/* Fetches the SV that keeps the per-interpreter data. */ +#define dMY_CXT_SV SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE) +#else /* >= perl5.004_68 */ +#define dMY_CXT_SV \ + SV *my_cxt_sv = \ + *hv_fetch(PL_modglobal, MY_CXT_KEY, sizeof(MY_CXT_KEY) - 1, TRUE) +#endif /* < perl5.004_68 */ + +/* This declaration should be used within all functions that use the + * interpreter-local data. */ +#define dMY_CXT \ + dMY_CXT_SV; \ + my_cxt_t *my_cxtp = INT2PTR(my_cxt_t *, SvUV(my_cxt_sv)) + +/* Creates and zeroes the per-interpreter data. + * (We allocate my_cxtp in a Perl SV so that it will be released when + * the interpreter goes away.) */ +#define MY_CXT_INIT \ + dMY_CXT_SV; \ + /* newSV() allocates one more than needed */ \ + my_cxt_t *my_cxtp = (my_cxt_t *)SvPVX(newSV(sizeof(my_cxt_t) - 1)); \ + Zero(my_cxtp, 1, my_cxt_t); \ + sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) + +/* This macro must be used to access members of the my_cxt_t structure. + * e.g. MYCXT.some_data */ +#define MY_CXT (*my_cxtp) + +/* Judicious use of these macros can reduce the number of times dMY_CXT + * is used. Use is similar to pTHX, aTHX etc. */ +#define pMY_CXT my_cxt_t *my_cxtp +#define pMY_CXT_ pMY_CXT, +#define _pMY_CXT , pMY_CXT +#define aMY_CXT my_cxtp +#define aMY_CXT_ aMY_CXT, +#define _aMY_CXT , aMY_CXT + +#endif /* START_MY_CXT */ + +#ifndef MY_CXT_CLONE +/* Clones the per-interpreter data. */ +#define MY_CXT_CLONE \ + dMY_CXT_SV; \ + my_cxt_t *my_cxtp = (my_cxt_t *)SvPVX(newSV(sizeof(my_cxt_t) - 1)); \ + Copy(INT2PTR(my_cxt_t *, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \ + sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) +#endif + +#else /* single interpreter */ + +#ifndef START_MY_CXT + +#define START_MY_CXT static my_cxt_t my_cxt; +#define dMY_CXT_SV dNOOP +#define dMY_CXT dNOOP +#define MY_CXT_INIT NOOP +#define MY_CXT my_cxt + +#define pMY_CXT void +#define pMY_CXT_ +#define _pMY_CXT +#define aMY_CXT +#define aMY_CXT_ +#define _aMY_CXT + +#endif /* START_MY_CXT */ + +#ifndef MY_CXT_CLONE +#define MY_CXT_CLONE NOOP +#endif + +#endif + +#ifndef IVdf +#if IVSIZE == LONGSIZE +#define IVdf "ld" +#define UVuf "lu" +#define UVof "lo" +#define UVxf "lx" +#define UVXf "lX" +#else +#if IVSIZE == INTSIZE +#define IVdf "d" +#define UVuf "u" +#define UVof "o" +#define UVxf "x" +#define UVXf "X" +#endif +#endif +#endif + +#ifndef NVef +#if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \ + defined(PERL_PRIfldbl) /* Not very likely, but let's try anyway. */ +#define NVef PERL_PRIeldbl +#define NVff PERL_PRIfldbl +#define NVgf PERL_PRIgldbl +#else +#define NVef "e" +#define NVff "f" +#define NVgf "g" +#endif +#endif + +#ifndef SvPV_nolen + +#if defined(NEED_sv_2pv_nolen) +static char *DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv); +static +#else +extern char *DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv); +#endif + +#ifdef sv_2pv_nolen +#undef sv_2pv_nolen +#endif +#define sv_2pv_nolen(a) DPPP_(my_sv_2pv_nolen)(aTHX_ a) +#define Perl_sv_2pv_nolen DPPP_(my_sv_2pv_nolen) + +#if defined(NEED_sv_2pv_nolen) || defined(NEED_sv_2pv_nolen_GLOBAL) + + char *DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv) +{ + STRLEN n_a; + return sv_2pv(sv, &n_a); +} + +#endif + +/* Hint: sv_2pv_nolen + * Use the SvPV_nolen() macro instead of sv_2pv_nolen(). + */ + +/* SvPV_nolen depends on sv_2pv_nolen */ +#define SvPV_nolen(sv) \ + ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK ? SvPVX(sv) : sv_2pv_nolen(sv)) + +#endif + +#ifdef SvPVbyte + +/* Hint: SvPVbyte + * Does not work in perl-5.6.1, ppport.h implements a version + * borrowed from perl-5.7.3. + */ + +#if ((PERL_VERSION < 7) || ((PERL_VERSION == 7) && (PERL_SUBVERSION < 0))) + +#if defined(NEED_sv_2pvbyte) +static char *DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp); +static +#else +extern char *DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp); +#endif + +#ifdef sv_2pvbyte +#undef sv_2pvbyte +#endif +#define sv_2pvbyte(a, b) DPPP_(my_sv_2pvbyte)(aTHX_ a, b) +#define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte) + +#if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL) + + char *DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp) +{ + sv_utf8_downgrade(sv, 0); + return SvPV(sv, *lp); +} + +#endif + +/* Hint: sv_2pvbyte + * Use the SvPVbyte() macro instead of sv_2pvbyte(). + */ + +#undef SvPVbyte + +/* SvPVbyte depends on sv_2pvbyte */ +#define SvPVbyte(sv, lp) \ + ((SvFLAGS(sv) & (SVf_POK | SVf_UTF8)) == (SVf_POK) ? \ + ((lp = SvCUR(sv)), SvPVX(sv)) : \ + sv_2pvbyte(sv, &lp)) + +#endif + +#else + +#define SvPVbyte SvPV +#define sv_2pvbyte sv_2pv + +#endif + +/* sv_2pvbyte_nolen depends on sv_2pv_nolen */ +#ifndef sv_2pvbyte_nolen +#define sv_2pvbyte_nolen sv_2pv_nolen +#endif + +/* Hint: sv_pvn + * Always use the SvPV() macro instead of sv_pvn(). + */ +#ifndef sv_pvn +#define sv_pvn(sv, len) SvPV(sv, len) +#endif + +/* Hint: sv_pvn_force + * Always use the SvPV_force() macro instead of sv_pvn_force(). + */ +#ifndef sv_pvn_force +#define sv_pvn_force(sv, len) SvPV_force(sv, len) +#endif + +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(vnewSVpvf) +#if defined(NEED_vnewSVpvf) +static SV *DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); +static +#else +extern SV *DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args); +#endif + +#ifdef vnewSVpvf +#undef vnewSVpvf +#endif +#define vnewSVpvf(a, b) DPPP_(my_vnewSVpvf)(aTHX_ a, b) +#define Perl_vnewSVpvf DPPP_(my_vnewSVpvf) + +#if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL) + + SV *DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args) +{ + register SV *sv = newSV(0); + sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV **), 0, Null(bool *)); + return sv; +} + +#endif +#endif + +/* sv_vcatpvf depends on sv_vcatpvfn */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_vcatpvf) +#define sv_vcatpvf(sv, pat, args) \ + sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV **), 0, Null(bool *)) +#endif + +/* sv_vsetpvf depends on sv_vsetpvfn */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_vsetpvf) +#define sv_vsetpvf(sv, pat, args) \ + sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV **), 0, Null(bool *)) +#endif + +/* sv_catpvf_mg depends on sv_vcatpvfn, sv_catpvf_mg_nocontext */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_catpvf_mg) +#if defined(NEED_sv_catpvf_mg) +static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); +static +#else +extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...); +#endif + +#define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg) + +#if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL) + + void DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...) +{ + va_list args; + va_start(args, pat); + sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV **), 0, Null(bool *)); + SvSETMAGIC(sv); + va_end(args); +} + +#endif +#endif + +/* sv_catpvf_mg_nocontext depends on sv_vcatpvfn */ +#ifdef PERL_IMPLICIT_CONTEXT +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_catpvf_mg_nocontext) +#if defined(NEED_sv_catpvf_mg_nocontext) +static void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); +static +#else +extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...); +#endif + +#define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) +#define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext) + +#if defined(NEED_sv_catpvf_mg_nocontext) || \ + defined(NEED_sv_catpvf_mg_nocontext_GLOBAL) + + void DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...) +{ + dTHX; + va_list args; + va_start(args, pat); + sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV **), 0, Null(bool *)); + SvSETMAGIC(sv); + va_end(args); +} + +#endif +#endif +#endif + +#ifndef sv_catpvf_mg +#ifdef PERL_IMPLICIT_CONTEXT +#define sv_catpvf_mg Perl_sv_catpvf_mg_nocontext +#else +#define sv_catpvf_mg Perl_sv_catpvf_mg +#endif +#endif + +/* sv_vcatpvf_mg depends on sv_vcatpvfn */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_vcatpvf_mg) +#define sv_vcatpvf_mg(sv, pat, args) \ + STMT_START \ + { \ + sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV **), 0, Null(bool *)); \ + SvSETMAGIC(sv); \ + } \ + STMT_END +#endif + +/* sv_setpvf_mg depends on sv_vsetpvfn, sv_setpvf_mg_nocontext */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_setpvf_mg) +#if defined(NEED_sv_setpvf_mg) +static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); +static +#else +extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...); +#endif + +#define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg) + +#if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL) + + void DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...) +{ + va_list args; + va_start(args, pat); + sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV **), 0, Null(bool *)); + SvSETMAGIC(sv); + va_end(args); +} + +#endif +#endif + +/* sv_setpvf_mg_nocontext depends on sv_vsetpvfn */ +#ifdef PERL_IMPLICIT_CONTEXT +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_setpvf_mg_nocontext) +#if defined(NEED_sv_setpvf_mg_nocontext) +static void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); +static +#else +extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...); +#endif + +#define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) +#define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext) + +#if defined(NEED_sv_setpvf_mg_nocontext) || \ + defined(NEED_sv_setpvf_mg_nocontext_GLOBAL) + + void DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...) +{ + dTHX; + va_list args; + va_start(args, pat); + sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV **), 0, Null(bool *)); + SvSETMAGIC(sv); + va_end(args); +} + +#endif +#endif +#endif + +#ifndef sv_setpvf_mg +#ifdef PERL_IMPLICIT_CONTEXT +#define sv_setpvf_mg Perl_sv_setpvf_mg_nocontext +#else +#define sv_setpvf_mg Perl_sv_setpvf_mg +#endif +#endif + +/* sv_vsetpvf_mg depends on sv_vsetpvfn */ +#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && \ + !defined(sv_vsetpvf_mg) +#define sv_vsetpvf_mg(sv, pat, args) \ + STMT_START \ + { \ + sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV **), 0, Null(bool *)); \ + SvSETMAGIC(sv); \ + } \ + STMT_END +#endif +#ifndef SvGETMAGIC +#define SvGETMAGIC(x) \ + STMT_START \ + { \ + if (SvGMAGICAL(x)) \ + mg_get(x); \ + } \ + STMT_END +#endif +#ifndef PERL_MAGIC_sv +#define PERL_MAGIC_sv '\0' +#endif + +#ifndef PERL_MAGIC_overload +#define PERL_MAGIC_overload 'A' +#endif + +#ifndef PERL_MAGIC_overload_elem +#define PERL_MAGIC_overload_elem 'a' +#endif + +#ifndef PERL_MAGIC_overload_table +#define PERL_MAGIC_overload_table 'c' +#endif + +#ifndef PERL_MAGIC_bm +#define PERL_MAGIC_bm 'B' +#endif + +#ifndef PERL_MAGIC_regdata +#define PERL_MAGIC_regdata 'D' +#endif + +#ifndef PERL_MAGIC_regdatum +#define PERL_MAGIC_regdatum 'd' +#endif + +#ifndef PERL_MAGIC_env +#define PERL_MAGIC_env 'E' +#endif + +#ifndef PERL_MAGIC_envelem +#define PERL_MAGIC_envelem 'e' +#endif + +#ifndef PERL_MAGIC_fm +#define PERL_MAGIC_fm 'f' +#endif + +#ifndef PERL_MAGIC_regex_global +#define PERL_MAGIC_regex_global 'g' +#endif + +#ifndef PERL_MAGIC_isa +#define PERL_MAGIC_isa 'I' +#endif + +#ifndef PERL_MAGIC_isaelem +#define PERL_MAGIC_isaelem 'i' +#endif + +#ifndef PERL_MAGIC_nkeys +#define PERL_MAGIC_nkeys 'k' +#endif + +#ifndef PERL_MAGIC_dbfile +#define PERL_MAGIC_dbfile 'L' +#endif + +#ifndef PERL_MAGIC_dbline +#define PERL_MAGIC_dbline 'l' +#endif + +#ifndef PERL_MAGIC_mutex +#define PERL_MAGIC_mutex 'm' +#endif + +#ifndef PERL_MAGIC_shared +#define PERL_MAGIC_shared 'N' +#endif + +#ifndef PERL_MAGIC_shared_scalar +#define PERL_MAGIC_shared_scalar 'n' +#endif + +#ifndef PERL_MAGIC_collxfrm +#define PERL_MAGIC_collxfrm 'o' +#endif + +#ifndef PERL_MAGIC_tied +#define PERL_MAGIC_tied 'P' +#endif + +#ifndef PERL_MAGIC_tiedelem +#define PERL_MAGIC_tiedelem 'p' +#endif + +#ifndef PERL_MAGIC_tiedscalar +#define PERL_MAGIC_tiedscalar 'q' +#endif + +#ifndef PERL_MAGIC_qr +#define PERL_MAGIC_qr 'r' +#endif + +#ifndef PERL_MAGIC_sig +#define PERL_MAGIC_sig 'S' +#endif + +#ifndef PERL_MAGIC_sigelem +#define PERL_MAGIC_sigelem 's' +#endif + +#ifndef PERL_MAGIC_taint +#define PERL_MAGIC_taint 't' +#endif + +#ifndef PERL_MAGIC_uvar +#define PERL_MAGIC_uvar 'U' +#endif + +#ifndef PERL_MAGIC_uvar_elem +#define PERL_MAGIC_uvar_elem 'u' +#endif + +#ifndef PERL_MAGIC_vstring +#define PERL_MAGIC_vstring 'V' +#endif + +#ifndef PERL_MAGIC_vec +#define PERL_MAGIC_vec 'v' +#endif + +#ifndef PERL_MAGIC_utf8 +#define PERL_MAGIC_utf8 'w' +#endif + +#ifndef PERL_MAGIC_substr +#define PERL_MAGIC_substr 'x' +#endif + +#ifndef PERL_MAGIC_defelem +#define PERL_MAGIC_defelem 'y' +#endif + +#ifndef PERL_MAGIC_glob +#define PERL_MAGIC_glob '*' +#endif + +#ifndef PERL_MAGIC_arylen +#define PERL_MAGIC_arylen '#' +#endif + +#ifndef PERL_MAGIC_pos +#define PERL_MAGIC_pos '.' +#endif + +#ifndef PERL_MAGIC_backref +#define PERL_MAGIC_backref '<' +#endif + +#ifndef PERL_MAGIC_ext +#define PERL_MAGIC_ext '~' +#endif + +/* That's the best we can do... */ +#ifndef SvPV_force_nomg +#define SvPV_force_nomg SvPV_force +#endif + +#ifndef SvPV_nomg +#define SvPV_nomg SvPV +#endif + +#ifndef sv_catpvn_nomg +#define sv_catpvn_nomg sv_catpvn +#endif + +#ifndef sv_catsv_nomg +#define sv_catsv_nomg sv_catsv +#endif + +#ifndef sv_setsv_nomg +#define sv_setsv_nomg sv_setsv +#endif + +#ifndef sv_pvn_nomg +#define sv_pvn_nomg sv_pvn +#endif + +#ifndef SvIV_nomg +#define SvIV_nomg SvIV +#endif + +#ifndef SvUV_nomg +#define SvUV_nomg SvUV +#endif + +#ifndef sv_catpv_mg +#define sv_catpv_mg(sv, ptr) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_catpv(TeMpSv, ptr); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_catpvn_mg +#define sv_catpvn_mg(sv, ptr, len) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_catpvn(TeMpSv, ptr, len); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_catsv_mg +#define sv_catsv_mg(dsv, ssv) \ + STMT_START \ + { \ + SV *TeMpSv = dsv; \ + sv_catsv(TeMpSv, ssv); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setiv_mg +#define sv_setiv_mg(sv, i) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_setiv(TeMpSv, i); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setnv_mg +#define sv_setnv_mg(sv, num) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_setnv(TeMpSv, num); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setpv_mg +#define sv_setpv_mg(sv, ptr) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_setpv(TeMpSv, ptr); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setpvn_mg +#define sv_setpvn_mg(sv, ptr, len) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_setpvn(TeMpSv, ptr, len); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setsv_mg +#define sv_setsv_mg(dsv, ssv) \ + STMT_START \ + { \ + SV *TeMpSv = dsv; \ + sv_setsv(TeMpSv, ssv); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_setuv_mg +#define sv_setuv_mg(sv, i) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_setuv(TeMpSv, i); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifndef sv_usepvn_mg +#define sv_usepvn_mg(sv, ptr, len) \ + STMT_START \ + { \ + SV *TeMpSv = sv; \ + sv_usepvn(TeMpSv, ptr, len); \ + SvSETMAGIC(TeMpSv); \ + } \ + STMT_END +#endif + +#ifdef USE_ITHREADS +#ifndef CopFILE +#define CopFILE(c) ((c)->cop_file) +#endif + +#ifndef CopFILEGV +#define CopFILEGV(c) (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv) +#endif + +#ifndef CopFILE_set +#define CopFILE_set(c, pv) ((c)->cop_file = savepv(pv)) +#endif + +#ifndef CopFILESV +#define CopFILESV(c) (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv) +#endif + +#ifndef CopFILEAV +#define CopFILEAV(c) (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav) +#endif + +#ifndef CopSTASHPV +#define CopSTASHPV(c) ((c)->cop_stashpv) +#endif + +#ifndef CopSTASHPV_set +#define CopSTASHPV_set(c, pv) ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch)) +#endif + +#ifndef CopSTASH +#define CopSTASH(c) (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c), GV_ADD) : Nullhv) +#endif + +#ifndef CopSTASH_set +#define CopSTASH_set(c, hv) CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch) +#endif + +#ifndef CopSTASH_eq +#define CopSTASH_eq(c, hv) \ + ((hv) && \ + (CopSTASHPV(c) == HvNAME(hv) || \ + (CopSTASHPV(c) && HvNAME(hv) && strEQ(CopSTASHPV(c), HvNAME(hv))))) +#endif + +#else +#ifndef CopFILEGV +#define CopFILEGV(c) ((c)->cop_filegv) +#endif + +#ifndef CopFILEGV_set +#define CopFILEGV_set(c, gv) ((c)->cop_filegv = (GV *)SvREFCNT_inc(gv)) +#endif + +#ifndef CopFILE_set +#define CopFILE_set(c, pv) CopFILEGV_set((c), gv_fetchfile(pv)) +#endif + +#ifndef CopFILESV +#define CopFILESV(c) (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv) +#endif + +#ifndef CopFILEAV +#define CopFILEAV(c) (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav) +#endif + +#ifndef CopFILE +#define CopFILE(c) (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch) +#endif + +#ifndef CopSTASH +#define CopSTASH(c) ((c)->cop_stash) +#endif + +#ifndef CopSTASH_set +#define CopSTASH_set(c, hv) ((c)->cop_stash = (hv)) +#endif + +#ifndef CopSTASHPV +#define CopSTASHPV(c) (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch) +#endif + +#ifndef CopSTASHPV_set +#define CopSTASHPV_set(c, pv) CopSTASH_set((c), gv_stashpv(pv, GV_ADD)) +#endif + +#ifndef CopSTASH_eq +#define CopSTASH_eq(c, hv) (CopSTASH(c) == (hv)) +#endif + +#endif /* USE_ITHREADS */ +#ifndef IN_PERL_COMPILETIME +#define IN_PERL_COMPILETIME (PL_curcop == &PL_compiling) +#endif + +#ifndef IN_LOCALE_RUNTIME +#define IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE) +#endif + +#ifndef IN_LOCALE_COMPILETIME +#define IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE) +#endif + +#ifndef IN_LOCALE +#define IN_LOCALE \ + (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME) +#endif +#ifndef IS_NUMBER_IN_UV +#define IS_NUMBER_IN_UV 0x01 +#endif + +#ifndef IS_NUMBER_GREATER_THAN_UV_MAX +#define IS_NUMBER_GREATER_THAN_UV_MAX 0x02 +#endif + +#ifndef IS_NUMBER_NOT_INT +#define IS_NUMBER_NOT_INT 0x04 +#endif + +#ifndef IS_NUMBER_NEG +#define IS_NUMBER_NEG 0x08 +#endif + +#ifndef IS_NUMBER_INFINITY +#define IS_NUMBER_INFINITY 0x10 +#endif + +#ifndef IS_NUMBER_NAN +#define IS_NUMBER_NAN 0x20 +#endif + +/* GROK_NUMERIC_RADIX depends on grok_numeric_radix */ +#ifndef GROK_NUMERIC_RADIX +#define GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send) +#endif +#ifndef PERL_SCAN_GREATER_THAN_UV_MAX +#define PERL_SCAN_GREATER_THAN_UV_MAX 0x02 +#endif + +#ifndef PERL_SCAN_SILENT_ILLDIGIT +#define PERL_SCAN_SILENT_ILLDIGIT 0x04 +#endif + +#ifndef PERL_SCAN_ALLOW_UNDERSCORES +#define PERL_SCAN_ALLOW_UNDERSCORES 0x01 +#endif + +#ifndef PERL_SCAN_DISALLOW_PREFIX +#define PERL_SCAN_DISALLOW_PREFIX 0x02 +#endif + +#ifndef grok_numeric_radix +#if defined(NEED_grok_numeric_radix) +static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, + const char *send); +static +#else +extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, + const char *send); +#endif + +#ifdef grok_numeric_radix +#undef grok_numeric_radix +#endif +#define grok_numeric_radix(a, b) DPPP_(my_grok_numeric_radix)(aTHX_ a, b) +#define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix) + +#if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL) + bool DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send) +{ +#ifdef USE_LOCALE_NUMERIC +#ifdef PL_numeric_radix_sv + if (PL_numeric_radix_sv && IN_LOCALE) { + STRLEN len; + char *radix = SvPV(PL_numeric_radix_sv, len); + if (*sp + len <= send && memEQ(*sp, radix, len)) { + *sp += len; + return TRUE; + } + } +#else + /* older perls don't have PL_numeric_radix_sv so the radix + * must manually be requested from locale.h + */ +#include <locale.h> + dTHR; /* needed for older threaded perls */ + struct lconv *lc = localeconv(); + char *radix = lc->decimal_point; + if (radix && IN_LOCALE) { + STRLEN len = strlen(radix); + if (*sp + len <= send && memEQ(*sp, radix, len)) { + *sp += len; + return TRUE; + } + } +#endif /* PERL_VERSION */ +#endif /* USE_LOCALE_NUMERIC */ + /* always try "." if numeric radix didn't match because + * we may have data from different locales mixed */ + if (*sp < send && **sp == '.') { + ++*sp; + return TRUE; + } + return FALSE; +} +#endif +#endif + +/* grok_number depends on grok_numeric_radix */ + +#ifndef grok_number +#if defined(NEED_grok_number) +static int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep); +static +#else +extern int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep); +#endif + +#ifdef grok_number +#undef grok_number +#endif +#define grok_number(a, b, c) DPPP_(my_grok_number)(aTHX_ a, b, c) +#define Perl_grok_number DPPP_(my_grok_number) + +#if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL) + int DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep) +{ + const char *s = pv; + const char *send = pv + len; + const UV max_div_10 = UV_MAX / 10; + const char max_mod_10 = UV_MAX % 10; + int numtype = 0; + int sawinf = 0; + int sawnan = 0; + + while (s < send && isSPACE(*s)) + s++; + if (s == send) { + return 0; + } else if (*s == '-') { + s++; + numtype = IS_NUMBER_NEG; + } else if (*s == '+') + s++; + + if (s == send) + return 0; + + /* next must be digit or the radix separator or beginning of infinity */ + if (isDIGIT(*s)) { + /* UVs are at least 32 bits, so the first 9 decimal digits cannot + overflow. */ + UV value = *s - '0'; + /* This construction seems to be more optimiser friendly. + (without it gcc does the isDIGIT test and the *s - '0' separately) + With it gcc on arm is managing 6 instructions (6 cycles) per digit. + In theory the optimiser could deduce how far to unroll the loop + before checking for overflow. */ + if (++s < send) { + int digit = *s - '0'; + if (digit >= 0 && digit <= 9) { + value = value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && digit <= 9) { + value = value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && digit <= 9) { + value = value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && digit <= 9) { + value = value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && digit <= 9) { + value = value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && + digit <= 9) { + value = + value * 10 + digit; + if (++s < send) { + digit = *s - '0'; + if (digit >= 0 && + digit <= 9) { + value = + value * 10 + + digit; + if (++s < + send) { + digit = *s - + '0'; + if (digit >= + 0 && + digit <= + 9) { + value = + value * + 10 + + digit; + if (++s < + send) { + /* Now got 9 digits, so need to check + each time for overflow. */ + digit = + *s - + '0'; + while ( + digit >= + 0 && + digit <= + 9 && + (value < + max_div_10 || + (value == + max_div_10 && + digit <= + max_mod_10))) { + value = + value * + 10 + + digit; + if (++s < + send) + digit = + *s - + '0'; + else + break; + } + if (digit >= + 0 && + digit <= + 9 && + (s < + send)) { + /* value overflowed. + skip the remaining digits, don't + worry about setting *valuep. */ + do { + s++; + } while ( + s < send && + isDIGIT( + *s)); + numtype |= + IS_NUMBER_GREATER_THAN_UV_MAX; + goto skip_value; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + numtype |= IS_NUMBER_IN_UV; + if (valuep) + *valuep = value; + + skip_value: + if (GROK_NUMERIC_RADIX(&s, send)) { + numtype |= IS_NUMBER_NOT_INT; + while (s < send && + isDIGIT(*s)) /* optional digits after the radix */ + s++; + } + } else if (GROK_NUMERIC_RADIX(&s, send)) { + numtype |= IS_NUMBER_NOT_INT | + IS_NUMBER_IN_UV; /* valuep assigned below */ + /* no digits before the radix means we need digits after it */ + if (s < send && isDIGIT(*s)) { + do { + s++; + } while (s < send && isDIGIT(*s)); + if (valuep) { + /* integer approximation is valid - it's 0. */ + *valuep = 0; + } + } else + return 0; + } else if (*s == 'I' || *s == 'i') { + s++; + if (s == send || (*s != 'N' && *s != 'n')) + return 0; + s++; + if (s == send || (*s != 'F' && *s != 'f')) + return 0; + s++; + if (s < send && (*s == 'I' || *s == 'i')) { + s++; + if (s == send || (*s != 'N' && *s != 'n')) + return 0; + s++; + if (s == send || (*s != 'I' && *s != 'i')) + return 0; + s++; + if (s == send || (*s != 'T' && *s != 't')) + return 0; + s++; + if (s == send || (*s != 'Y' && *s != 'y')) + return 0; + s++; + } + sawinf = 1; + } else if (*s == 'N' || *s == 'n') { + /* XXX TODO: There are signaling NaNs and quiet NaNs. */ + s++; + if (s == send || (*s != 'A' && *s != 'a')) + return 0; + s++; + if (s == send || (*s != 'N' && *s != 'n')) + return 0; + s++; + sawnan = 1; + } else + return 0; + + if (sawinf) { + numtype &= IS_NUMBER_NEG; /* Keep track of sign */ + numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT; + } else if (sawnan) { + numtype &= IS_NUMBER_NEG; /* Keep track of sign */ + numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT; + } else if (s < send) { + /* we can have an optional exponent part */ + if (*s == 'e' || *s == 'E') { + /* The only flag we keep is sign. Blow away any "it's UV" */ + numtype &= IS_NUMBER_NEG; + numtype |= IS_NUMBER_NOT_INT; + s++; + if (s < send && (*s == '-' || *s == '+')) + s++; + if (s < send && isDIGIT(*s)) { + do { + s++; + } while (s < send && isDIGIT(*s)); + } else + return 0; + } + } + while (s < send && isSPACE(*s)) + s++; + if (s >= send) + return numtype; + if (len == 10 && memEQ(pv, "0 but true", 10)) { + if (valuep) + *valuep = 0; + return IS_NUMBER_IN_UV; + } + return 0; +} +#endif +#endif + +/* + * The grok_* routines have been modified to use warn() instead of + * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit, + * which is why the stack variable has been renamed to 'xdigit'. + */ + +#ifndef grok_bin +#if defined(NEED_grok_bin) +static UV DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +static +#else +extern UV DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +#endif + +#ifdef grok_bin +#undef grok_bin +#endif +#define grok_bin(a, b, c, d) DPPP_(my_grok_bin)(aTHX_ a, b, c, d) +#define Perl_grok_bin DPPP_(my_grok_bin) + +#if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL) + UV DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result) +{ + const char *s = start; + STRLEN len = *len_p; + UV value = 0; + NV value_nv = 0; + + const UV max_div_2 = UV_MAX / 2; + bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; + bool overflowed = FALSE; + + if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { + /* strip off leading b or 0b. + for compatibility silently suffer "b" and "0b" as valid binary + numbers. */ + if (len >= 1) { + if (s[0] == 'b') { + s++; + len--; + } else if (len >= 2 && s[0] == '0' && s[1] == 'b') { + s += 2; + len -= 2; + } + } + } + + for (; len-- && *s; s++) { + char bit = *s; + if (bit == '0' || bit == '1') { + /* Write it in this wonky order with a goto to attempt to get the + compiler to make the common case integer-only loop pretty tight. + With gcc seems to be much straighter code than old scan_bin. */ + redo: + if (!overflowed) { + if (value <= max_div_2) { + value = (value << 1) | (bit - '0'); + continue; + } + /* Bah. We're just overflowed. */ + warn("Integer overflow in binary number"); + overflowed = TRUE; + value_nv = (NV)value; + } + value_nv *= 2.0; + /* If an NV has not enough bits in its mantissa to + * represent a UV this summing of small low-order numbers + * is a waste of time (because the NV cannot preserve + * the low-order bits anyway): we could just remember when + * did we overflow and in the end just multiply value_nv by the + * right amount. */ + value_nv += (NV)(bit - '0'); + continue; + } + if (bit == '_' && len && allow_underscores && (bit = s[1]) && + (bit == '0' || bit == '1')) { + --len; + ++s; + goto redo; + } + if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) + warn("Illegal binary digit '%c' ignored", *s); + break; + } + + if ((overflowed && value_nv > 4294967295.0) +#if UVSIZE > 4 + || (!overflowed && value > 0xffffffff) +#endif + ) { + warn("Binary number > 0b11111111111111111111111111111111 non-portable"); + } + *len_p = s - start; + if (!overflowed) { + *flags = 0; + return value; + } + *flags = PERL_SCAN_GREATER_THAN_UV_MAX; + if (result) + *result = value_nv; + return UV_MAX; +} +#endif +#endif + +#ifndef grok_hex +#if defined(NEED_grok_hex) +static UV DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +static +#else +extern UV DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +#endif + +#ifdef grok_hex +#undef grok_hex +#endif +#define grok_hex(a, b, c, d) DPPP_(my_grok_hex)(aTHX_ a, b, c, d) +#define Perl_grok_hex DPPP_(my_grok_hex) + +#if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL) + UV DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result) +{ + const char *s = start; + STRLEN len = *len_p; + UV value = 0; + NV value_nv = 0; + + const UV max_div_16 = UV_MAX / 16; + bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; + bool overflowed = FALSE; + const char *xdigit; + + if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) { + /* strip off leading x or 0x. + for compatibility silently suffer "x" and "0x" as valid hex numbers. + */ + if (len >= 1) { + if (s[0] == 'x') { + s++; + len--; + } else if (len >= 2 && s[0] == '0' && s[1] == 'x') { + s += 2; + len -= 2; + } + } + } + + for (; len-- && *s; s++) { + xdigit = strchr((char *)PL_hexdigit, *s); + if (xdigit) { + /* Write it in this wonky order with a goto to attempt to get the + compiler to make the common case integer-only loop pretty tight. + With gcc seems to be much straighter code than old scan_hex. */ + redo: + if (!overflowed) { + if (value <= max_div_16) { + value = (value << 4) | ((xdigit - PL_hexdigit) & 15); + continue; + } + warn("Integer overflow in hexadecimal number"); + overflowed = TRUE; + value_nv = (NV)value; + } + value_nv *= 16.0; + /* If an NV has not enough bits in its mantissa to + * represent a UV this summing of small low-order numbers + * is a waste of time (because the NV cannot preserve + * the low-order bits anyway): we could just remember when + * did we overflow and in the end just multiply value_nv by the + * right amount of 16-tuples. */ + value_nv += (NV)((xdigit - PL_hexdigit) & 15); + continue; + } + if (*s == '_' && len && allow_underscores && s[1] && + (xdigit = strchr((char *)PL_hexdigit, s[1]))) { + --len; + ++s; + goto redo; + } + if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) + warn("Illegal hexadecimal digit '%c' ignored", *s); + break; + } + + if ((overflowed && value_nv > 4294967295.0) +#if UVSIZE > 4 + || (!overflowed && value > 0xffffffff) +#endif + ) { + warn("Hexadecimal number > 0xffffffff non-portable"); + } + *len_p = s - start; + if (!overflowed) { + *flags = 0; + return value; + } + *flags = PERL_SCAN_GREATER_THAN_UV_MAX; + if (result) + *result = value_nv; + return UV_MAX; +} +#endif +#endif + +#ifndef grok_oct +#if defined(NEED_grok_oct) +static UV DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +static +#else +extern UV DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result); +#endif + +#ifdef grok_oct +#undef grok_oct +#endif +#define grok_oct(a, b, c, d) DPPP_(my_grok_oct)(aTHX_ a, b, c, d) +#define Perl_grok_oct DPPP_(my_grok_oct) + +#if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL) + UV DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, + NV *result) +{ + const char *s = start; + STRLEN len = *len_p; + UV value = 0; + NV value_nv = 0; + + const UV max_div_8 = UV_MAX / 8; + bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES; + bool overflowed = FALSE; + + for (; len-- && *s; s++) { + /* gcc 2.95 optimiser not smart enough to figure that this subtraction + out front allows slicker code. */ + int digit = *s - '0'; + if (digit >= 0 && digit <= 7) { + /* Write it in this wonky order with a goto to attempt to get the + compiler to make the common case integer-only loop pretty tight. + */ + redo: + if (!overflowed) { + if (value <= max_div_8) { + value = (value << 3) | digit; + continue; + } + /* Bah. We're just overflowed. */ + warn("Integer overflow in octal number"); + overflowed = TRUE; + value_nv = (NV)value; + } + value_nv *= 8.0; + /* If an NV has not enough bits in its mantissa to + * represent a UV this summing of small low-order numbers + * is a waste of time (because the NV cannot preserve + * the low-order bits anyway): we could just remember when + * did we overflow and in the end just multiply value_nv by the + * right amount of 8-tuples. */ + value_nv += (NV)digit; + continue; + } + if (digit == ('_' - '0') && len && allow_underscores && + (digit = s[1] - '0') && (digit >= 0 && digit <= 7)) { + --len; + ++s; + goto redo; + } + /* Allow \octal to work the DWIM way (that is, stop scanning + * as soon as non-octal characters are seen, complain only iff + * someone seems to want to use the digits eight and nine). */ + if (digit == 8 || digit == 9) { + if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT)) + warn("Illegal octal digit '%c' ignored", *s); + } + break; + } + + if ((overflowed && value_nv > 4294967295.0) +#if UVSIZE > 4 + || (!overflowed && value > 0xffffffff) +#endif + ) { + warn("Octal number > 037777777777 non-portable"); + } + *len_p = s - start; + if (!overflowed) { + *flags = 0; + return value; + } + *flags = PERL_SCAN_GREATER_THAN_UV_MAX; + if (result) + *result = value_nv; + return UV_MAX; +} +#endif +#endif + +#ifdef NO_XSLOCKS +#ifdef dJMPENV +#define dXCPT \ + dJMPENV; \ + int rEtV = 0 +#define XCPT_TRY_START \ + JMPENV_PUSH(rEtV); \ + if (rEtV == 0) +#define XCPT_TRY_END JMPENV_POP; +#define XCPT_CATCH if (rEtV != 0) +#define XCPT_RETHROW JMPENV_JUMP(rEtV) +#else +#define dXCPT \ + Sigjmp_buf oldTOP; \ + int rEtV = 0 +#define XCPT_TRY_START \ + Copy(top_env, oldTOP, 1, Sigjmp_buf); \ + rEtV = Sigsetjmp(top_env, 1); \ + if (rEtV == 0) +#define XCPT_TRY_END Copy(oldTOP, top_env, 1, Sigjmp_buf); +#define XCPT_CATCH if (rEtV != 0) +#define XCPT_RETHROW Siglongjmp(top_env, rEtV) +#endif +#endif + +#endif /* _P_P_PORTABILITY_H_ */ + +/* End of File ppport.h */ diff --git a/perl/t/Decoder.t b/perl/t/Decoder.t new file mode 100755 index 0000000..12ba86f --- /dev/null +++ b/perl/t/Decoder.t @@ -0,0 +1,111 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl Decoder.t' + +use warnings; +use strict; +use Test::More tests => 17; + +######################### + +BEGIN { use_ok('Barcode::ZBar') } + +######################### + +my $decoder = Barcode::ZBar::Decoder->new(); +isa_ok($decoder, 'Barcode::ZBar::Decoder', 'decoder'); + +$decoder->parse_config('enable'); + +######################### + +can_ok($decoder, qw(set_config parse_config reset new_scan decode_width + get_color get_configs get_direction get_data get_modifiers + get_type set_handler)); + +######################### + +my $sym = $decoder->decode_width(5); +is($sym, Barcode::ZBar::Symbol::NONE, 'enum/enum compare'); + +######################### + +ok($sym == 0, 'enum/numeric compare'); + +######################### + +is($sym, 'None', 'enum/string compare'); + +######################### + +my $handler_type = 0; +my $explicit_closure = 0; + +$decoder->set_handler(sub { + if(!$handler_type) { + is($_[0], $decoder, 'handler decoder'); + } + + my $type = $_[0]->get_type(); + $handler_type = $type + if(!$handler_type or $type > Barcode::ZBar::Symbol::PARTIAL); + + ${$_[1]} += 1 +}, \$explicit_closure); + +######################### + +$decoder->reset(); +is($decoder->get_color(), Barcode::ZBar::SPACE, 'reset color'); + +######################### + +is($decoder->get_direction(), 0, 'reset direction'); + +######################### + +$decoder->set_config(Barcode::ZBar::Symbol::QRCODE, + Barcode::ZBar::Config::ENABLE, 0); + +my $encoded = + '9 111 212241113121211311141132 11111 311213121312121332111132 111 9'; + +foreach my $width (split(/ */, $encoded)) { + my $tmp = $decoder->decode_width($width); + if($tmp > Barcode::ZBar::Symbol::PARTIAL) { + $sym = ($sym == Barcode::ZBar::Symbol::NONE) ? $tmp : -1; + } +} +is($sym, Barcode::ZBar::Symbol::EAN13, 'EAN-13 type'); + +######################### + +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'); + +######################### + +is($decoder->get_color(), Barcode::ZBar::BAR, 'post-scan color'); + +######################### + +is($decoder->get_direction(), 1, 'decode direction'); + +######################### + +is($handler_type, Barcode::ZBar::Symbol::EAN13, 'handler type'); + +######################### + +is($explicit_closure, 2, 'handler explicit closure'); + +######################### diff --git a/perl/t/Image.t b/perl/t/Image.t new file mode 100755 index 0000000..a63b985 --- /dev/null +++ b/perl/t/Image.t @@ -0,0 +1,186 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl Image.t' + +use warnings; +use strict; +use Test::More tests => 29; + +######################### + +BEGIN { use_ok('Barcode::ZBar') } + +Barcode::ZBar::set_verbosity(16); + +######################### + +my $image = Barcode::ZBar::Image->new(); +isa_ok($image, 'Barcode::ZBar::Image', 'image'); + +######################### + +my $scanner = Barcode::ZBar::ImageScanner->new(); +isa_ok($scanner, 'Barcode::ZBar::ImageScanner', 'image scanner'); + +######################### + +can_ok($image, qw(convert convert_resize + get_format get_size get_data + set_format set_size set_data)); + +######################### + +can_ok($scanner, qw(set_config parse_config enable_cache scan_image)); + +######################### + +$image->set_format('422P'); +my $fmt = $image->get_format(); +is($fmt, '422P', 'string format accessors'); + +######################### + +ok($fmt == 0x50323234, 'numeric format accessors'); + +######################### + +$image->set_size(114, 80); +is_deeply([$image->get_size()], [114, 80], 'size accessors'); + +######################### + +$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 }; + skip "Image::Magick not installed", 16 if $@; + + my $im = Image::Magick->new(); + my $err = $im->Read('t/barcode.png'); + die($err) if($err); + + $image->set_size($im->Get(qw(columns rows))); + + { + my $data = $im->ImageToBlob( + magick => 'YUV', + 'sampling-factor' => '4:2:2', + interlace => 'Plane'); + $image->set_data($data); + } + + $image = $image->convert('Y800'); + isa_ok($image, 'Barcode::ZBar::Image', 'image'); + + ######################### + + is($image->get_format(), 'Y800', 'converted image format'); + + ######################### + + is_deeply([$image->get_size()], [114, 80], 'converted image size'); + + ######################### + + is($scanner->scan_image($image), 1, 'scan result'); + + ######################### + + my @symbols = $image->get_symbols(); + is(scalar(@symbols), 1, 'result size'); + + ######################### + + my $sym = $symbols[0]; + isa_ok($sym, 'Barcode::ZBar::Symbol', 'symbol'); + + ######################### + + can_ok($sym, qw(get_type get_configs get_modifiers get_data get_quality + get_count get_loc get_orientation)); + + ######################### + + is($sym->get_type(), Barcode::ZBar::Symbol::EAN13, 'result type'); + + ######################### + + 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'); + + ######################### + + ok($sym->get_quality() > 0, 'quality'); + + ######################### + + my @loc = $sym->get_loc(); + ok(scalar(@loc) >= 4, 'location size'); + + ######################### + + my $failure = undef; + foreach my $pt (@loc) { + if(ref($pt) ne 'ARRAY') { + $failure = ("location entry is wrong type:" . + " expecting ARRAY ref, got " . ref($pt)); + last; + } + if(scalar(@{$pt}) != 2) { + $failure = ("location coordinate has too many entries:" . + " expecting 2, got " . scalar(@{$pt})); + last; + } + } + ok(!defined($failure), 'location structure') or + diag($failure); + + ######################### + + is($sym->get_orientation(), Barcode::ZBar::Orient::UP, 'orientation'); + + ######################### + + my @comps = $sym->get_components(); + is(scalar(@comps), 0, 'components size'); + + ######################### +} + +$scanner->recycle_image($image); + +my @symbols = $image->get_symbols(); +is(scalar(@symbols), 0, 'recycled result size'); + +######################### + + +# FIXME more image tests diff --git a/perl/t/Processor.t b/perl/t/Processor.t new file mode 100755 index 0000000..92bf6fe --- /dev/null +++ b/perl/t/Processor.t @@ -0,0 +1,140 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl Processor.t' + +use warnings; +use strict; +use Test::More tests => 20; + +######################### + +BEGIN { use_ok('Barcode::ZBar') } + +Barcode::ZBar::set_verbosity(32); + +######################### + +my $proc = Barcode::ZBar::Processor->new(); +isa_ok($proc, 'Barcode::ZBar::Processor', 'processor'); + +######################### + +can_ok($proc, qw(init set_config parse_config)); + +######################### + +ok(!$proc->parse_config('enable'), 'configuration'); + +######################### + +my $cnt = 0; +my $explicit_closure = 0; + +$proc->set_data_handler(sub { + + ok(!$cnt, 'handler invocations'); + $cnt += 1; + + ######################### + + is($_[0], $proc, 'handler processor'); + + ######################### + + my $image = $_[1]; + isa_ok($image, 'Barcode::ZBar::Image', 'image'); + + ######################### + + my @symbols = $image->get_symbols(); + is(scalar(@symbols), 1, 'result size'); + + ######################### + + my $sym = $symbols[0]; + isa_ok($sym, 'Barcode::ZBar::Symbol', 'symbol'); + + ######################### + + is($sym->get_type(), Barcode::ZBar::Symbol::EAN13, 'result type'); + + ######################### + + is($sym->get_data(), '9876543210128', 'result data'); + + ######################### + + ok($sym->get_quality() > 0, 'quality'); + + ######################### + + my @loc = $sym->get_loc(); + ok(scalar(@loc) >= 4, 'location size'); + + # structure checked by Image.t + + ${$_[2]} += 1 +}, \$explicit_closure); + +######################### + +SKIP: { + skip "no display", 3 unless defined $ENV{'DISPLAY'}; + + $proc->init($ENV{VIDEO_DEVICE}); + ok(!$proc->is_visible(), 'initial visibility'); + + ######################### + + $proc->set_visible(); + ok($proc->is_visible(), 'enabled visiblity'); + + ######################### + + ok($proc->user_wait(1.1) >= 0, 'wait w/timeout'); + + ######################### +} + +SKIP: { + # FIXME factor out image read utility + eval { require Image::Magick }; + skip "Image::Magick not installed", 11 if $@; + my $im = Image::Magick->new(); + my $err = $im->Read('t/barcode.png'); + die($err) if($err); + my $image = Barcode::ZBar::Image->new(); + $image->set_format('422P'); + $image->set_size($im->Get(qw(columns rows))); + $image->set_data($im->ImageToBlob( + magick => 'YUV', + 'sampling-factor' => '4:2:2', + interlace => 'Plane') + ); + +SKIP: { + skip "no display", 11 unless defined $ENV{'DISPLAY'}; + + my $rc = $proc->process_image($image); + ok(!$rc, 'process result'); + + $proc->user_wait(.9); + + ######################### + + is($explicit_closure, 1, 'handler explicit closure'); + } +} + +######################### + +$proc->set_data_handler(); +pass('unset handler'); + +######################### + +# FIXME more processor tests + +$proc = undef; +pass('cleanup'); + +######################### diff --git a/perl/t/Scanner.t b/perl/t/Scanner.t new file mode 100755 index 0000000..99b8942 --- /dev/null +++ b/perl/t/Scanner.t @@ -0,0 +1,23 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl Scanner.t' + +use warnings; +use strict; +use Test::More tests => 3; + +######################### + +BEGIN { use_ok('Barcode::ZBar') } + +######################### + +my $scanner = Barcode::ZBar::Scanner->new(); +isa_ok($scanner, 'Barcode::ZBar::Scanner', 'scanner'); + +######################### + +can_ok($scanner, qw(reset new_scan scan_y get_width get_color)); + +######################### + +# FIXME more scanner tests diff --git a/perl/t/ZBar.t b/perl/t/ZBar.t new file mode 100755 index 0000000..0e3a867 --- /dev/null +++ b/perl/t/ZBar.t @@ -0,0 +1,68 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl ZBar.t' + +use warnings; +use strict; +use Test::More tests => 37; + +######################### + +BEGIN { use_ok('Barcode::ZBar') } + +######################### + +like(Barcode::ZBar::version(), qr<\d.\d>, 'version'); + +######################### + +Barcode::ZBar::set_verbosity(16); +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/t/barcode.png b/perl/t/barcode.png Binary files differnew file mode 100644 index 0000000..72846ce --- /dev/null +++ b/perl/t/barcode.png diff --git a/perl/t/pod-coverage.t b/perl/t/pod-coverage.t new file mode 100644 index 0000000..97c2df8 --- /dev/null +++ b/perl/t/pod-coverage.t @@ -0,0 +1,12 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl pod.t' + +use warnings; +use strict; +use Test::More; + +eval "use Test::Pod::Coverage"; +plan skip_all => "Test::Pod::Coverage required for testing pod coverage" + if $@; + +all_pod_coverage_ok(); diff --git a/perl/t/pod.t b/perl/t/pod.t new file mode 100644 index 0000000..bc0af34 --- /dev/null +++ b/perl/t/pod.t @@ -0,0 +1,12 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl pod.t' + +use warnings; +use strict; +use Test::More; + +eval "use Test::Pod 1.00"; +plan skip_all => "Test::Pod 1.00 required for testing POD" + if $@; + +all_pod_files_ok(); diff --git a/perl/typemap b/perl/typemap new file mode 100644 index 0000000..dda1979 --- /dev/null +++ b/perl/typemap @@ -0,0 +1,66 @@ +# objects +Barcode::ZBar::Error T_PTROBJ +Barcode::ZBar::Symbol T_PTROBJ +Barcode::ZBar::Image T_PTROBJ +Barcode::ZBar::Processor T_PTROBJ +Barcode::ZBar::Video T_PTROBJ +Barcode::ZBar::Window T_PTROBJ +Barcode::ZBar::ImageScanner T_PTROBJ +Barcode::ZBar::Decoder T_PTROBJ +Barcode::ZBar::Scanner T_PTROBJ + +# enums +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 +timeout_t T_TIMEOUT + +# error handling +config_error T_CONFIG_ERROR + + +INPUT + +T_ENUM + $var = ($type)SvIV($arg) +T_FOURCC + { + if(SvPOK($arg)) { + char *str = SvPV_nolen($arg); + $var = zbar_fourcc_parse(str); + } + else + $var = SvUV($arg); + } +T_TIMEOUT + if(($var = ($type)(SvNV($arg) * 1000.)) < 0) + $var = -1; +T_PV + $var = SvOK($arg) ? SvPV_nolen($arg) : NULL; + +OUTPUT + +T_ENUM + $arg = SvREFCNT_inc(lookup_enum(LOOKUP_$ntype, (int)$var)); +T_FOURCC + { + char str[4] = { + $var & 0xff, + ($var >> 8) & 0xff, + ($var >> 16) & 0xff, + ($var >> 24) & 0xff, + }; + sv_setuv($arg, $var); + sv_setpvn($arg, str, 4); + SvIOK_on($arg); + } + +T_CONFIG_ERROR + if($var) + croak("invalid configuration setting: %s", config_string); diff --git a/plugin/Makefile.am.inc b/plugin/Makefile.am.inc new file mode 100644 index 0000000..c1d4425 --- /dev/null +++ b/plugin/Makefile.am.inc @@ -0,0 +1,5 @@ +lib_LTLIBRARIES += plugin/libzbarplugin.la +plugin_libzbarplugin_la_SOURCES = \ + plugin/plugin.c +plugin_libzbarplugin_la_CPPFLAGS = $(MOZILLA_CFLAGS) $(AM_CPPFLAGS) +plugin_libzbarplugin_la_LDFLAGS = $(MOZILLA_LIBS) $(AM_LDFLAGS) diff --git a/plugin/plugin.c b/plugin/plugin.c new file mode 100644 index 0000000..89a8a5e --- /dev/null +++ b/plugin/plugin.c @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <npapi.h> diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..179f0c0 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,2 @@ +# Set of available languages +pt_BR diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..4ba88d1 --- /dev/null +++ b/po/Makevars @@ -0,0 +1,72 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=P_:1,2 + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Mauro Carvalho Chehab + +# This tells whether or not to prepend "GNU " prefix to the package +# name that gets inserted into the header of the $(DOMAIN).pot file. +# Possible values are "yes", "no", or empty. If it is empty, try to +# detect it automatically by scanning the files in $(top_srcdir) for +# "GNU packagename" string. +PACKAGE_GNU = + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = https://github.com/mchehab/zbar/issues + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = no + +# These options get passed to msgmerge. +# Useful options are in particular: +# --previous to keep previous msgids of translated messages, +# --quiet to reduce the verbosity. +MSGMERGE_OPTIONS = + +# This tells whether or not to regenerate a PO file when $(DOMAIN).pot +# has changed. Possible values are "yes" and "no". Set this to no if +# the POT file is checked in the repository and the version control +# program ignores timestamps. +PO_DEPENDS_ON_POT = yes + +# This tells whether or not to forcibly update $(DOMAIN).pot and +# regenerate PO files on "make dist". Possible values are "yes" and +# "no". Set this to no if the POT file and PO files are maintained +# externally. +DIST_DEPENDS_ON_UPDATE_PO = yes diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..f940539 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,5 @@ +# List of source files containing translatable strings. +# Please keep this file sorted alphabetically. + +zbarcam/zbarcam.c +zbarimg/zbarimg.c diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100644 index 0000000..16a836a --- /dev/null +++ b/po/pt_BR.po @@ -0,0 +1,233 @@ +# Portuguese translations for zbar +# Traduções em português brasileiro para zbar. +# Copyright (C) 2020 Mauro Carvalho Chehab +# This file is distributed under the same license as the zbar package. +# Mauro Carvalho Chehab <mchehab@kernel.org>, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: zbar 0.23\n" +"Report-Msgid-Bugs-To: https://github.com/mchehab/zbar/issues\n" +"POT-Creation-Date: 2024-01-09 10:11+0100\n" +"PO-Revision-Date: 2020-04-12 17:01+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.3\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: zbarcam/zbarcam.c:50 +msgid "" +"usage: zbarcam [options] [/dev/video?]\n" +"\n" +"scan and decode bar codes from a video stream\n" +"\n" +"options:\n" +" -h, --help display this help text\n" +" --version display version information and exit\n" +" -q, --quiet disable beep when symbol is decoded\n" +" -v, --verbose increase debug output level\n" +" --verbose=N set specific debug output level\n" +" --xml use XML output format\n" +" --raw output decoded symbol data without converting charsets\n" +" -1, --oneshot exit after scanning one bar code\n" +" --nodisplay disable video display window\n" +" --prescale=<W>x<H>\n" +" request alternate video image size from driver\n" +" -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" +" set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" +"\n" +msgstr "" +"uso: zbarcam [opções] [/dev/video?]\n" +"\n" +"Lê e decodifica códigos de barra em imagens capturadas pela câmera ou outro " +"dispositivo de vÃdeo\n" +"\n" +"opções:\n" +" -h, --help esse texto de auxÃlio\n" +" --version mostra a versão e termina\n" +" -q, --quiet desabilita mensagens informativas e avisos sonoros " +"quando sÃmbolos são decodificados\n" +" -v, --verbose aumenta o nÃvel de mensagens de depuração\n" +" --verbose=N configura um determinado nÃvel de mensagens de " +"depuração\n" +" --xml mostra resultados em formato XML\n" +" --raw mostra sÃmbolos decodificados sem metadados e sem " +"converter o conjunto de caracteres\n" +" -1, --oneshot termina o programa após ler um código de barras\n" +" --nodisplay desabilita janela de apresentação de imagens\n" +" --prescale=<W>x<H>\n" +" solicita modo de vÃdeo diferente\n" +" -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" +" configura parâmetro <CONFIG> para <VALUE> (ou 1)\n" +"\n" + +#: zbarcam/zbarcam.c:73 zbarimg/zbarimg.c:104 +msgid " --nodbus disable dbus message\n" +msgstr " --nodbus desabilita mensagens no dbus\n" + +#: zbarimg/zbarimg.c:81 +#, fuzzy +#| msgid "" +#| "usage: zbarimg [options] <image>...\n" +#| "\n" +#| "scan and decode bar codes from one or more image files\n" +#| "\n" +#| "options:\n" +#| " -h, --help display this help text\n" +#| " --version display version information and exit\n" +#| " -q, --quiet minimal output, only print decoded symbol data\n" +#| " -v, --verbose increase debug output level\n" +#| " --verbose=N set specific debug output level\n" +#| " -d, --display enable display of following images to the screen\n" +#| " -D, --nodisplay disable display of following images (default)\n" +#| " --xml, --noxml enable/disable XML output format\n" +#| " --raw output decoded symbol data without converting " +#| "charsets\n" +#| " -1, --oneshot exit after scanning one bar code\n" +#| " -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" +#| " set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" +#| "\n" +msgid "" +"usage: zbarimg [options] <image>...\n" +"\n" +"scan and decode bar codes from one or more image files\n" +"\n" +"options:\n" +" -h, --help display this help text\n" +" --version display version information and exit\n" +" --polygon output points delimiting code zone with decoded symbol " +"data\n" +" -q, --quiet minimal output, only print decoded symbol data\n" +" -v, --verbose increase debug output level\n" +" --verbose=N set specific debug output level\n" +" -d, --display enable display of following images to the screen\n" +" -D, --nodisplay disable display of following images (default)\n" +" --xml, --noxml enable/disable XML output format\n" +" --raw output decoded symbol data without converting charsets\n" +" -1, --oneshot exit after scanning one bar code\n" +" -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" +" set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" +"\n" +msgstr "" +"uso: zbarimg [opções] <image>...\n" +"\n" +"Lê e decodifica códigos de barra em uma ou mais imagens\n" +"\n" +"opções:\n" +" -h, --help esse texto de auxÃlio\n" +" --version mostra a versão e termina\n" +" -q, --quiet desabilita mensagens informativas\n" +" -v, --verbose aumenta o nÃvel de mensagens de depuração\n" +" --verbose=N configura um determinado nÃvel de mensagens de " +"depuração\n" +" --xml mostra resultados em formato XML\n" +" --raw mostra sÃmbolos decodificados sem metadados e sem " +"converter o conjunto de caracteres\n" +" -1, --oneshot termina o programa após ler um código de barras\n" +" --nodisplay desabilita janela de apresentação de imagens\n" +" --prescale=<W>x<H>\n" +" solicita modo de vÃdeo diferente\n" +" -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" +" configura parâmetro <CONFIG> para <VALUE> (ou 1)\n" +"\n" + +#: zbarimg/zbarimg.c:108 +msgid "" +"\n" +"WARNING: barcode data was not detected in some image(s)\n" +"Things to check:\n" +" - is the barcode type supported? Currently supported symbologies are:\n" +msgstr "" +"\n" +"AVISO: dados de código de barra não foram decodificados em alguma(s) " +"imagem(ns)\n" +"Favor verificar:\n" +" - Há suporte para códigos de barras desse tipo? As simbologias suportadas " +"são:\n" + +#: zbarimg/zbarimg.c:114 +msgid "" +" - is the barcode large enough in the image?\n" +" - is the barcode mostly in focus?\n" +" - is there sufficient contrast/illumination?\n" +" - If the symbol is split in several barcodes, are they combined in one " +"image?\n" +" - Did you enable the barcode type?\n" +" some EAN/UPC codes are disabled by default. To enable all, use:\n" +" $ zbarimg -S*.enable <files>\n" +" Please also notice that some variants take precedence over others.\n" +" Due to that, if you want, for example, ISBN-10, you should do:\n" +" $ zbarimg -Sisbn10.enable <files>\n" +"\n" +msgstr "" +" - O código de barras ficou grande o suficiente na foto?\n" +" - O código de barras está em foco?\n" +" - Existe iluminação e contraste suficientes?\n" +" - Se o sÃmbolo está dividido em diversos códigos, eles foram combinados em " +"uma única imagem?\n" +" - O tipo de código de barras está habilitado?\n" +" Alguns códigos EAN/UPC são desabilitados por padrão. Para habilitar " +"todos, use:\n" +" $ zbarimg -S*.enable <files>\n" +" Note também que algumas variantes são utilizadas primeiro, quando é " +"possÃvel decodificar o mesmo código com múltiplas variantes.\n" +" Por conta disso, se você desejar, por exemplo, utilizar ISBN-10, você " +"deve habilitar a simbologia com:\n" +" $ zbarimg -Sisbn10.enable <files>\n" +"\n" + +#: zbarimg/zbarimg.c:517 +#, c-format +msgid "" +"\t. EAN/UPC (EAN-13, EAN-8, EAN-2, EAN-5, UPC-A, UPC-E, ISBN-10, ISBN-13)\n" +msgstr "" +"\t. EAN/UPC (EAN-13, EAN-8, EAN-2, EAN-5, UPC-A, UPC-E, ISBN-10, ISBN-13)\n" + +#: zbarimg/zbarimg.c:520 +#, c-format +msgid "\t. DataBar, DataBar Expanded\n" +msgstr "\t. DataBar, DataBar Expandido\n" + +#: zbarimg/zbarimg.c:523 +#, c-format +msgid "\t. Code 128\n" +msgstr "\t. Código 128\n" + +#: zbarimg/zbarimg.c:526 +#, c-format +msgid "\t. Code 93\n" +msgstr "\t. Código 93\n" + +#: zbarimg/zbarimg.c:529 +#, c-format +msgid "\t. Code 39\n" +msgstr "\t. Código 39\n" + +#: zbarimg/zbarimg.c:532 +#, c-format +msgid "\t. Codabar\n" +msgstr "\t. Codabar\n" + +#: zbarimg/zbarimg.c:535 +#, c-format +msgid "\t. Interleaved 2 of 5\n" +msgstr "\t. Entrelaçado 2 de 5\n" + +#: zbarimg/zbarimg.c:538 +#, c-format +msgid "\t. QR code\n" +msgstr "\t. Código QR\n" + +#: zbarimg/zbarimg.c:541 +#, c-format +msgid "\t. SQ code\n" +msgstr "\t. Código SQ\n" + +#: zbarimg/zbarimg.c:544 +#, c-format +msgid "\t. PDF 417\n" +msgstr "\t. PDF 417\n" diff --git a/pygtk/Makefile.am.inc b/pygtk/Makefile.am.inc new file mode 100644 index 0000000..033d601 --- /dev/null +++ b/pygtk/Makefile.am.inc @@ -0,0 +1,24 @@ +pyexec_LTLIBRARIES += pygtk/zbarpygtk.la +pygtk_zbarpygtk_la_CPPFLAGS = \ + $(GTK_CFLAGS) $(PYTHON_CFLAGS) $(PYGTK_CFLAGS) $(AM_CPPFLAGS) +pygtk_zbarpygtk_la_LDFLAGS = -shared -module -avoid-version -export-dynamic \ + -export-symbols-regex initzbarpygtk $(PYTHON_LDFLAGS) +pygtk_zbarpygtk_la_LIBADD = \ + $(PYGTK_LIBS) gtk/libzbargtk.la $(AM_LIBADD) + +pygtk_zbarpygtk_la_DEPENDENCIES = gtk/libzbargtk.la +dist_pygtk_zbarpygtk_la_SOURCES = pygtk/zbarpygtkmodule.c +nodist_pygtk_zbarpygtk_la_SOURCES = pygtk/zbarpygtk.c +BUILT_SOURCES += pygtk/zbarpygtk.c pygtk/zbarpygtk.defs +CLEANFILES += pygtk/zbarpygtk.c pygtk/zbarpygtk.defs +EXTRA_DIST += pygtk/zbarpygtk.override + +# FIXME ugly hack to fixup new name... now non-standard? +pygtk/zbarpygtk.defs: include/zbar/zbargtk.h + $(PYTHON) $(PYGTK_H2DEF) $< | \ + $(SED) -e 's/Z_TYPE_BAR_/ZBAR_TYPE_/' > $@ + +pygtk/%.c: pygtk/%.defs $(srcdir)/pygtk/zbarpygtk.override + $(PYGTK_CODEGEN) --prefix zbarpygtk \ + --register $(PYGTK_DEFS)/gdk-types.defs \ + --override $(srcdir)/pygtk/zbarpygtk.override $< > $@ diff --git a/pygtk/zbarpygtk.override b/pygtk/zbarpygtk.override new file mode 100644 index 0000000..a66ceb9 --- /dev/null +++ b/pygtk/zbarpygtk.override @@ -0,0 +1,20 @@ +%% +headers +#include <Python.h> +#include <pygobject.h> +#include <zbar/zbargtk.h> +%% +modulename zbarpygtk +%% +import gtk.Widget as PyGtkWidget_Type +import gtk.gdk.Pixbuf as PyGdkPixbuf_Type +%% +ignore-type + ZBarGtkError +%% +ignore-glob + *_get_type +%% +ignore + zbar_gtk_image_from_pixbuf # until base library wrappers are in place +%% diff --git a/pygtk/zbarpygtkmodule.c b/pygtk/zbarpygtkmodule.c new file mode 100644 index 0000000..5e354e7 --- /dev/null +++ b/pygtk/zbarpygtkmodule.c @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +/* avoid "multiple definition" darwin link errors + * for symbols defined in pygobject.h (bug #2052681) + */ +#define NO_IMPORT_PYGOBJECT + +#include <pygobject.h> + +void zbarpygtk_register_classes(PyObject *); +extern PyMethodDef zbarpygtk_functions[]; + +DL_EXPORT(void) +initzbarpygtk(void) +{ + init_pygobject(); + + PyObject *mod = Py_InitModule("zbarpygtk", zbarpygtk_functions); + PyObject *dict = PyModule_GetDict(mod); + + zbarpygtk_register_classes(dict); + + if (PyErr_Occurred()) + Py_FatalError("unable to initialise module zbarpygtk"); +} diff --git a/python/MANIFEST.in b/python/MANIFEST.in new file mode 100644 index 0000000..93acc53 --- /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 new file mode 100644 index 0000000..f1f30a4 --- /dev/null +++ b/python/Makefile.am.inc @@ -0,0 +1,13 @@ +pyexec_LTLIBRARIES += python/zbar.la +python_zbar_la_CPPFLAGS = $(PYTHON_CFLAGS) $(AM_CPPFLAGS) +python_zbar_la_LDFLAGS = -shared -module -avoid-version -export-dynamic \ + -export-symbols-regex '(initzbar|PyInit_zbar)' $(PYTHON_LDFLAGS) +python_zbar_la_LIBADD = zbar/libzbar.la $(AM_LIBADD) + +python_zbar_la_SOURCES = python/zbarmodule.c python/zbarmodule.h \ + python/enum.c python/exception.c python/symbol.c python/symbolset.c \ + 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 \ + python/examples/processor.py python/examples/read_one.py diff --git a/python/README b/python/README new file mode 100644 index 0000000..876b0c3 --- /dev/null +++ b/python/README @@ -0,0 +1,60 @@ +========================================== +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, Codabar, 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. + +* https://github.com/mchehab/zbar + +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: + +* https://github.com/mchehab/zbar + +Windows users please note: the module *will NOT load* unless the ZBar +library DLL (currently libzbar-0.dll) is available in your Windows system +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 <spadix@users.sourceforge.net> diff --git a/python/decoder.c b/python/decoder.c new file mode 100644 index 0000000..e75ee9f --- /dev/null +++ b/python/decoder.c @@ -0,0 +1,360 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char decoder_doc[] = + PyDoc_STR("low level decode of measured bar/space widths.\n" + "\n" + "FIXME."); + +static zbarDecoder *decoder_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbarDecoder *self = (zbarDecoder *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + + self->zdcode = zbar_decoder_create(); + zbar_decoder_set_userdata(self->zdcode, self); + if (!self->zdcode) { + Py_DECREF(self); + return (NULL); + } + + return (self); +} + +static int decoder_traverse(zbarDecoder *self, visitproc visit, void *arg) +{ + Py_VISIT(self->handler); + Py_VISIT(self->args); + return (0); +} + +static int decoder_clear(zbarDecoder *self) +{ + zbar_decoder_set_handler(self->zdcode, NULL); + zbar_decoder_set_userdata(self->zdcode, NULL); + Py_CLEAR(self->handler); + Py_CLEAR(self->args); + return (0); +} + +static void decoder_dealloc(zbarDecoder *self) +{ + decoder_clear(self); + zbar_decoder_destroy(self->zdcode); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarEnumItem *decoder_get_color(zbarDecoder *self, void *closure) +{ + zbar_color_t zcol = zbar_decoder_get_color(self->zdcode); + assert(zcol == ZBAR_BAR || zcol == ZBAR_SPACE); + + struct module_state *st = GETMODSTATE(); + zbarEnumItem *color = st->color_enum[zcol]; + Py_INCREF((PyObject *)color); + return (color); +} + +static zbarEnumItem *decoder_get_type(zbarDecoder *self, void *closure) +{ + zbar_symbol_type_t sym = zbar_decoder_get_type(self->zdcode); + if (sym == ZBAR_NONE) { + /* hardcode most common case */ + struct module_state *st = GETMODSTATE(); + Py_INCREF((PyObject *)st->symbol_NONE); + return (st->symbol_NONE); + } + return (zbarSymbol_LookupEnum(sym)); +} + +static PyObject *decoder_get_configs(zbarDecoder *self, void *closure) +{ + struct module_state *st = GETMODSTATE(); + unsigned int sym = zbar_decoder_get_type(self->zdcode); + unsigned int mask = zbar_decoder_get_configs(self->zdcode, sym); + return (zbarEnum_SetFromMask(st->config_enum, mask)); +} + +static PyObject *decoder_get_modifiers(zbarDecoder *self, void *closure) +{ + unsigned int mask = zbar_decoder_get_modifiers(self->zdcode); + struct module_state *st = GETMODSTATE(); + return (zbarEnum_SetFromMask(st->modifier_enum, mask)); +} + +static PyObject *decoder_get_data(zbarDecoder *self, void *closure) +{ +#if PY_MAJOR_VERSION >= 3 + return (PyUnicode_FromStringAndSize(zbar_decoder_get_data(self->zdcode), + zbar_decoder_get_data_length( + self->zdcode))); +#else + return ( + PyString_FromStringAndSize(zbar_decoder_get_data(self->zdcode), + zbar_decoder_get_data_length(self->zdcode))); +#endif +} + +static PyObject *decoder_get_direction(zbarDecoder *self, void *closure) +{ +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(zbar_decoder_get_direction(self->zdcode))); +#else + return (PyInt_FromLong(zbar_decoder_get_direction(self->zdcode))); +#endif +} + +static PyGetSetDef decoder_getset[] = { + { + "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, + }, +}; + +static PyObject *decoder_set_config(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + zbar_symbol_type_t sym = ZBAR_NONE; + zbar_config_t cfg = ZBAR_CFG_ENABLE; + int val = 1; + static char *kwlist[] = { "symbology", "config", "value", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &sym, &cfg, + &val)) + return (NULL); + + if (zbar_decoder_set_config(self->zdcode, sym, cfg, val)) { + PyErr_SetString(PyExc_ValueError, "invalid configuration setting"); + return (NULL); + } + Py_RETURN_NONE; +} + +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); + + struct module_state *st = GETMODSTATE(); + unsigned int mask = zbar_decoder_get_configs(self->zdcode, sym); + return (zbarEnum_SetFromMask(st->config_enum, mask)); +} + +static PyObject *decoder_parse_config(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + const char *cfg = NULL; + static char *kwlist[] = { "config", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cfg)) + return (NULL); + + if (zbar_decoder_parse_config(self->zdcode, cfg)) { + PyErr_Format(PyExc_ValueError, "invalid configuration setting: %s", + cfg); + return (NULL); + } + Py_RETURN_NONE; +} + +static PyObject *decoder_reset(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbar_decoder_reset(self->zdcode); + Py_RETURN_NONE; +} + +static PyObject *decoder_new_scan(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbar_decoder_new_scan(self->zdcode); + Py_RETURN_NONE; +} + +void decode_handler(zbar_decoder_t *zdcode) +{ + assert(zdcode); + zbarDecoder *self = zbar_decoder_get_userdata(zdcode); + assert(self); + assert(self->zdcode == zdcode); + assert(self->handler); + assert(self->args); + PyObject *junk = PyObject_Call(self->handler, self->args, NULL); + Py_XDECREF(junk); +} + +static PyObject *decoder_set_handler(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + PyObject *handler = Py_None; + PyObject *closure = Py_None; + + static char *kwlist[] = { "handler", "closure", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &handler, + &closure)) + return (NULL); + + if (handler != Py_None && !PyCallable_Check(handler)) { + PyErr_Format(PyExc_ValueError, "handler %.50s is not callable", + handler->ob_type->tp_name); + return (NULL); + } + Py_CLEAR(self->handler); + Py_CLEAR(self->args); + + if (handler != Py_None) { + self->args = PyTuple_New(2); + if (!self->args) + return (NULL); + Py_INCREF(self); + Py_INCREF(closure); + PyTuple_SET_ITEM(self->args, 0, (PyObject *)self); + PyTuple_SET_ITEM(self->args, 1, closure); + + Py_INCREF(handler); + self->handler = handler; + + zbar_decoder_set_handler(self->zdcode, decode_handler); + } else { + self->handler = self->args = NULL; + zbar_decoder_set_handler(self->zdcode, NULL); + } + Py_RETURN_NONE; +} + +static zbarEnumItem *decoder_decode_width(zbarDecoder *self, PyObject *args, + PyObject *kwds) +{ + unsigned int width = 0; + static char *kwlist[] = { "width", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", kwlist, &width)) + return (NULL); + + zbar_symbol_type_t sym = zbar_decode_width(self->zdcode, width); + if (PyErr_Occurred()) + /* propagate errors during callback */ + return (NULL); + if (sym == ZBAR_NONE) { + /* hardcode most common case */ + struct module_state *st = GETMODSTATE(); + Py_INCREF((PyObject *)st->symbol_NONE); + return (st->symbol_NONE); + } + return (zbarSymbol_LookupEnum(sym)); +} + +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, + METH_VARARGS | METH_KEYWORDS, + }, + { + "new_scan", + (PyCFunction)decoder_new_scan, + METH_VARARGS | METH_KEYWORDS, + }, + { + "set_handler", + (PyCFunction)decoder_set_handler, + METH_VARARGS | METH_KEYWORDS, + }, + { + "decode_width", + (PyCFunction)decoder_decode_width, + METH_VARARGS | METH_KEYWORDS, + }, + { + NULL, + }, +}; + +PyTypeObject zbarDecoder_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Decoder", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = decoder_doc, + .tp_basicsize = sizeof(zbarDecoder), + .tp_new = (newfunc)decoder_new, + .tp_traverse = (traverseproc)decoder_traverse, + .tp_clear = (inquiry)decoder_clear, + .tp_dealloc = (destructor)decoder_dealloc, + .tp_getset = decoder_getset, + .tp_methods = decoder_methods, +}; diff --git a/python/enum.c b/python/enum.c new file mode 100644 index 0000000..b57a880 --- /dev/null +++ b/python/enum.c @@ -0,0 +1,259 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char enumitem_doc[] = + PyDoc_STR("simple enumeration item.\n" + "\n" + "associates an int value with a name for printing."); + +static zbarEnumItem *enumitem_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + int val = 0; + PyObject *name = NULL; + static char *kwlist[] = { "value", "name", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iS", kwlist, &val, &name)) + return (NULL); + + zbarEnumItem *self = (zbarEnumItem *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + +#if PY_MAJOR_VERSION >= 3 + PyLongObject *longval = (PyLongObject *)PyLong_FromLong(val); + if (!longval) { + Py_DECREF(self); + return (NULL); + } + + /* we assume the "fast path" for a single-digit ints (see longobject.c) */ + /* this also holds if we get a small_int preallocated long */ +#if PY_VERSION_HEX >= 0x030900A4 + Py_SET_SIZE(&self->val, Py_SIZE(longval)); +#else + Py_SIZE(&self->val) = Py_SIZE(longval); +#endif +#if PY_VERSION_HEX >= 0x030c0000 + self->val.long_value.ob_digit[0] = longval->long_value.ob_digit[0]; +#else + self->val.ob_digit[0] = longval->ob_digit[0]; +#endif + Py_DECREF(longval); +#else + self->val.ob_ival = val; +#endif + self->name = name; + return (self); +} + +static void enumitem_dealloc(zbarEnumItem *self) +{ + Py_CLEAR(self->name); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static PyObject *enumitem_str(zbarEnumItem *self) +{ + Py_INCREF(self->name); + return (self->name); +} + +#if PY_MAJOR_VERSION < 3 +/* tp_print was dropped on Python 3.9 */ +static int enumitem_print(zbarEnumItem *self, FILE *fp, int flags) +{ + return (self->name->ob_type->tp_print(self->name, fp, flags)); +} +#endif + +static PyObject *enumitem_repr(zbarEnumItem *self) +{ + PyObject *name = PyObject_Repr(self->name); + if (!name) + return (NULL); +#if PY_MAJOR_VERSION >= 3 + PyObject *repr = PyUnicode_FromFormat("%s(%ld, %U)", + ((PyObject *)self)->ob_type->tp_name, + PyLong_AsLong((PyObject *)self), + name); +#else + char *namestr = PyString_AsString(name); + PyObject *repr = PyString_FromFormat("%s(%ld, %s)", + ((PyObject *)self)->ob_type->tp_name, + self->val.ob_ival, namestr); +#endif + Py_DECREF(name); + return ((PyObject *)repr); +} + +PyTypeObject zbarEnumItem_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.EnumItem", + .tp_doc = enumitem_doc, + .tp_basicsize = sizeof(zbarEnumItem), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = (newfunc)enumitem_new, + .tp_dealloc = (destructor)enumitem_dealloc, + .tp_str = (reprfunc)enumitem_str, +#if PY_MAJOR_VERSION < 3 + .tp_print = (printfunc)enumitem_print, +#endif + .tp_repr = (reprfunc)enumitem_repr, +}; + +zbarEnumItem *zbarEnumItem_New(PyObject *byname, PyObject *byvalue, int val, + const char *name) +{ + zbarEnumItem *self = PyObject_New(zbarEnumItem, &zbarEnumItem_Type); + if (!self) + return (NULL); +#if PY_MAJOR_VERSION >= 3 + PyLongObject *longval = (PyLongObject *)PyLong_FromLong(val); + if (!longval) { + Py_DECREF(self); + return (NULL); + } + + /* we assume the "fast path" for a single-digit ints (see longobject.c) */ + /* this also holds if we get a small_int preallocated long */ +#if PY_VERSION_HEX >= 0x030900A4 + Py_SET_SIZE(&self->val, Py_SIZE(longval)); +#else + Py_SIZE(&self->val) = Py_SIZE(longval); +#endif +#if PY_VERSION_HEX >= 0x030c0000 + self->val.long_value.ob_digit[0] = longval->long_value.ob_digit[0]; +#else + self->val.ob_digit[0] = longval->ob_digit[0]; +#endif + Py_DECREF(longval); + + self->name = PyUnicode_FromString(name); +#else + self->val.ob_ival = val; + self->name = PyString_FromString(name); +#endif + if (!self->name || + (byname && PyDict_SetItem(byname, self->name, (PyObject *)self)) || + (byvalue && + PyDict_SetItem(byvalue, (PyObject *)self, (PyObject *)self))) { + Py_DECREF((PyObject *)self); + return (NULL); + } + return (self); +} + +static char enum_doc[] = PyDoc_STR("enumeration container for EnumItems.\n" + "\n" + "exposes items as read-only attributes"); + +/* FIXME add iteration */ + +static int enum_traverse(zbarEnum *self, visitproc visit, void *arg) +{ + Py_VISIT(self->byname); + Py_VISIT(self->byvalue); + return (0); +} + +static int enum_clear(zbarEnum *self) +{ + Py_CLEAR(self->byname); + Py_CLEAR(self->byvalue); + return (0); +} + +static void enum_dealloc(zbarEnum *self) +{ + enum_clear(self); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +PyTypeObject zbarEnum_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Enum", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = enum_doc, + .tp_basicsize = sizeof(zbarEnum), + .tp_dictoffset = offsetof(zbarEnum, byname), + .tp_traverse = (traverseproc)enum_traverse, + .tp_clear = (inquiry)enum_clear, + .tp_dealloc = (destructor)enum_dealloc, +}; + +zbarEnum *zbarEnum_New() +{ + zbarEnum *self = PyObject_GC_New(zbarEnum, &zbarEnum_Type); + if (!self) + return (NULL); + self->byname = PyDict_New(); + self->byvalue = PyDict_New(); + if (!self->byname || !self->byvalue) { + Py_DECREF(self); + return (NULL); + } + return (self); +} + +int zbarEnum_Add(zbarEnum *self, int val, const char *name) +{ + zbarEnumItem *item; + item = zbarEnumItem_New(self->byname, self->byvalue, val, name); + if (!item) + return (-1); + return (0); +} + +zbarEnumItem *zbarEnum_LookupValue(zbarEnum *self, int val) +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *key = PyLong_FromLong(val); +#else + PyObject *key = PyInt_FromLong(val); +#endif + 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)) { +#if PY_MAJOR_VERSION >= 3 + unsigned long val = (unsigned long)PyLong_AsLong(item); +#else + int val = PyInt_AsLong(item); +#endif + if (val < sizeof(mask) * 8 && ((mask >> val) & 1)) + PySet_Add(result, item); + } + return (result); +} diff --git a/python/examples/processor.py b/python/examples/processor.py new file mode 100644 index 0000000..b8f20f0 --- /dev/null +++ b/python/examples/processor.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +from sys import argv +import zbar + +# create a Processor +proc = zbar.Processor() + +# configure the Processor +proc.parse_config('enable') + +# initialize the Processor +device = '/dev/video0' +if len(argv) > 1: + device = argv[1] +proc.init(device) + +# setup a callback +def my_handler(proc, image, closure): + # extract results + for symbol in image.symbols: + # do something useful with results + print('decoded', symbol.type, 'symbol', '"%s"' % symbol.data) + +proc.set_data_handler(my_handler) + +# enable the preview window +proc.visible = True + +# initiate scanning +proc.active = True +try: + # keep scanning until user provides key/mouse input + proc.user_wait() +except zbar.WindowClosed as e: + pass diff --git a/python/examples/read_one.py b/python/examples/read_one.py new file mode 100644 index 0000000..7a18c99 --- /dev/null +++ b/python/examples/read_one.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +from sys import argv +import zbar + +# create a Processor +proc = zbar.Processor() + +# configure the Processor +proc.parse_config('enable') + +# initialize the Processor +device = '/dev/video0' +if len(argv) > 1: + device = argv[1] +proc.init(device) + +# enable the preview window +proc.visible = True + +# read at least one barcode (or until window closed) +proc.process_one() + +# hide the preview window +proc.visible = False + +# extract results +for symbol in proc.results: + # do something useful with results + print('decoded', symbol.type, 'symbol', '"%s"' % symbol.data) diff --git a/python/examples/scan_image.py b/python/examples/scan_image.py new file mode 100644 index 0000000..e26cb42 --- /dev/null +++ b/python/examples/scan_image.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +from sys import argv +import zbar +from PIL import Image + +if len(argv) < 2: exit(1) + +# create a reader +scanner = zbar.ImageScanner() + +# configure the reader +scanner.parse_config('enable') + +# obtain image data +pil = Image.open(argv[1]).convert('L') +width, height = pil.size +raw = pil.tobytes() + +# wrap image data +image = zbar.Image(width, height, 'Y800', raw) + +# scan the image for barcodes +scanner.scan(image) + +# extract results +for symbol in image: + # do something useful with results + print('decoded', symbol.type, 'symbol', '"%s"' % symbol.data) + +# clean up +del(image) diff --git a/python/exception.c b/python/exception.c new file mode 100644 index 0000000..2edc722 --- /dev/null +++ b/python/exception.c @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +#if PY_MAJOR_VERSION < 3 + +static inline PyObject *exc_get_message(zbarException *self, void *closure) +{ + PyBaseExceptionObject *super = (PyBaseExceptionObject *)self; + if (!PyString_Size(super->message)) { + Py_CLEAR(super->message); + if (!self->obj || !zbarProcessor_Check(self->obj)) + super->message = PyString_FromString("unknown zbar error"); + else { + const void *zobj = ((zbarProcessor *)self->obj)->zproc; + super->message = PyString_FromString(_zbar_error_string(zobj, 1)); + } + } + Py_INCREF(super->message); + return (super->message); +} + +static int exc_init(zbarException *self, PyObject *args, PyObject *kwds) +{ + if (!_PyArg_NoKeywords(self->base.ob_type->tp_name, kwds)) + return (-1); + PyBaseExceptionObject *super = (PyBaseExceptionObject *)self; + Py_CLEAR(super->args); + Py_INCREF(args); + super->args = args; + + if (PyTuple_GET_SIZE(args) == 1) { + Py_CLEAR(self->obj); + self->obj = PyTuple_GET_ITEM(args, 0); + Py_INCREF(self->obj); + } + return (0); +} + +static int exc_traverse(zbarException *self, visitproc visit, void *arg) +{ + Py_VISIT(self->obj); + PyTypeObject *base = (PyTypeObject *)PyExc_Exception; + return (base->tp_traverse((PyObject *)self, visit, arg)); +} + +static int exc_clear(zbarException *self) +{ + Py_CLEAR(self->obj); + ((PyTypeObject *)PyExc_Exception)->tp_clear((PyObject *)self); + return (0); +} + +static void exc_dealloc(zbarException *self) +{ + exc_clear(self); + ((PyTypeObject *)PyExc_Exception)->tp_dealloc((PyObject *)self); +} + +static PyObject *exc_str(zbarException *self) +{ + return (exc_get_message(self, NULL)); +} + +static int exc_set_message(zbarException *self, PyObject *value, void *closure) +{ + PyBaseExceptionObject *super = (PyBaseExceptionObject *)self; + Py_CLEAR(super->message); + if (!value) + value = PyString_FromString(""); + else + Py_INCREF(value); + super->message = value; + return (0); +} + +static PyGetSetDef exc_getset[] = { + { + "message", + (getter)exc_get_message, + (setter)exc_set_message, + }, + { + NULL, + }, +}; + +PyTypeObject zbarException_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Exception", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_basicsize = sizeof(zbarException), + .tp_init = (initproc)exc_init, + .tp_traverse = (traverseproc)exc_traverse, + .tp_clear = (inquiry)exc_clear, + .tp_dealloc = (destructor)exc_dealloc, + .tp_str = (reprfunc)exc_str, + .tp_getset = exc_getset, +}; + +#endif diff --git a/python/image.c b/python/image.c new file mode 100644 index 0000000..b3ff5cd --- /dev/null +++ b/python/image.c @@ -0,0 +1,482 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +static char image_doc[] = PyDoc_STR( + "image object.\n" + "\n" + "stores image data samples along with associated format and size metadata."); + +static zbarImage *image_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + zbarImage *self = (zbarImage *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + + self->zimg = zbar_image_create(); + if (!self->zimg) { + Py_DECREF(self); + return (NULL); + } + zbar_image_set_userdata(self->zimg, self); + return (self); +} + +static int image_traverse(zbarImage *self, visitproc visit, void *arg) +{ + Py_VISIT(self->data); + return (0); +} + +static int image_clear(zbarImage *self) +{ + zbar_image_t *zimg = self->zimg; + self->zimg = NULL; + if (zimg) { + assert(zbar_image_get_userdata(zimg) == self); + if (self->data) { + /* attach data directly to zbar image */ + zbar_image_set_userdata(zimg, self->data); + self->data = NULL; + } else + zbar_image_set_userdata(zimg, NULL); + zbar_image_destroy(zimg); + } + return (0); +} + +static void image_dealloc(zbarImage *self) +{ + image_clear(self); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarSymbolSet *image_get_symbols(zbarImage *self, void *closure) +{ + const zbar_symbol_set_t *zsyms = zbar_image_get_symbols(self->zimg); + return (zbarSymbolSet_FromSymbolSet(zsyms)); +} + +static int image_set_symbols(zbarImage *self, PyObject *value, void *closure) +{ + const zbar_symbol_set_t *zsyms; + if (!value || value == Py_None) + zsyms = NULL; + else if (zbarSymbolSet_Check(value)) + zsyms = ((zbarSymbolSet *)value)->zsyms; + else { + PyErr_Format(PyExc_TypeError, + "must set image symbols to a zbar.SymbolSet, not '%.50s'", + value->ob_type->tp_name); + return (-1); + } + + zbar_image_set_symbols(self->zimg, zsyms); + return (0); +} + +static zbarSymbolIter *image_iter(zbarImage *self) +{ + zbarSymbolSet *syms = image_get_symbols(self, NULL); + if (!syms) + return (NULL); + return (zbarSymbolIter_FromSymbolSet(syms)); +} + +static PyObject *image_get_format(zbarImage *self, void *closure) +{ + unsigned long format = zbar_image_get_format(self->zimg); +#if PY_MAJOR_VERSION >= 3 + return (PyBytes_FromStringAndSize((char *)&format, 4)); +#else + return (PyString_FromStringAndSize((char *)&format, 4)); +#endif +} + +static int image_set_format(zbarImage *self, PyObject *value, void *closure) +{ + if (!value) { + PyErr_SetString(PyExc_TypeError, "cannot delete format attribute"); + return (-1); + } + char *format = NULL; + Py_ssize_t len; +#if PY_MAJOR_VERSION >= 3 + PyObject *bytes; + + if (PyUnicode_Check(value)) + bytes = PyUnicode_AsEncodedString(value, "utf-8", "surrogateescape"); + else + bytes = value; + if (PyBytes_AsStringAndSize(bytes, &format, &len) < 0 || !format || + len != 4) { +#else + if (PyString_AsStringAndSize(value, &format, &len) || !format || len != 4) { +#endif + 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, zbar_fourcc_parse(format)); + return (0); +} + +static PyObject *image_get_size(zbarImage *self, void *closure) +{ + unsigned int w, h; + zbar_image_get_size(self->zimg, &w, &h); +#if PY_MAJOR_VERSION >= 3 + return (PyTuple_Pack(2, PyLong_FromLong(w), PyLong_FromLong(h))); +#else + return (PyTuple_Pack(2, PyInt_FromLong(w), PyInt_FromLong(h))); +#endif +} + +static int image_set_size(zbarImage *self, PyObject *value, void *closure) +{ + if (!value) { + PyErr_SetString(PyExc_TypeError, "cannot delete size attribute"); + return (-1); + } + + int dims[2]; + if (parse_dimensions(value, dims, 2) || dims[0] < 0 || dims[1] < 0) { + PyErr_SetString(PyExc_ValueError, + "size must be a sequence of two positive ints"); + return (-1); + } + + zbar_image_set_size(self->zimg, dims[0], dims[1]); + return (0); +} + +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); +#if PY_MAJOR_VERSION >= 3 + return (PyTuple_Pack(4, PyLong_FromLong(x), PyLong_FromLong(y), + PyLong_FromLong(w), PyLong_FromLong(h))); +#else + return (PyTuple_Pack(4, PyInt_FromLong(x), PyInt_FromLong(y), + PyInt_FromLong(w), PyInt_FromLong(h))); +#endif +} + +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 dims[4]; + if (parse_dimensions(value, dims, 4) || dims[2] < 0 || dims[3] < 0) { + PyErr_SetString(PyExc_ValueError, + "crop must be a sequence of four positive ints"); + return (-1); + } + + 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]); + return (0); +} + +static PyObject *image_get_int(zbarImage *self, void *closure) +{ + unsigned int val = -1; + switch ((intptr_t)closure) { + case 0: + val = zbar_image_get_width(self->zimg); + break; + case 1: + val = zbar_image_get_height(self->zimg); + break; + case 2: + val = zbar_image_get_sequence(self->zimg); + break; + default: + assert(0); + } +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(val)); +#else + return (PyInt_FromLong(val)); +#endif +} + +static int image_set_int(zbarImage *self, PyObject *value, void *closure) +{ + unsigned int tmp; +#if PY_MAJOR_VERSION >= 3 + long val = PyLong_AsLong(value); +#else + unsigned int val = PyInt_AsSsize_t(value); +#endif + if (val == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "expecting an integer"); + return (-1); + } + switch ((intptr_t)closure) { + case 0: + tmp = zbar_image_get_height(self->zimg); + zbar_image_set_size(self->zimg, val, tmp); + break; + case 1: + tmp = zbar_image_get_width(self->zimg); + zbar_image_set_size(self->zimg, tmp, val); + break; + case 2: + zbar_image_set_sequence(self->zimg, val); + default: + assert(0); + } + return (0); +} + +static PyObject *image_get_data(zbarImage *self, void *closure) +{ + assert(zbar_image_get_userdata(self->zimg) == self); + if (self->data) { + Py_INCREF(self->data); + return (self->data); + } + + const char *data = zbar_image_get_data(self->zimg); + unsigned long datalen = zbar_image_get_data_length(self->zimg); + if (!data || !datalen) { + Py_INCREF(Py_None); + return (Py_None); + } + +#if PY_MAJOR_VERSION >= 3 + self->data = PyMemoryView_FromMemory((void *)data, datalen, PyBUF_READ); +#else + self->data = PyBuffer_FromMemory((void *)data, datalen); +#endif + Py_INCREF(self->data); + return (self->data); +} + +void image_cleanup(zbar_image_t *zimg) +{ + PyObject *data = zbar_image_get_userdata(zimg); + zbar_image_set_userdata(zimg, NULL); + if (!data) + return; /* FIXME internal error */ + if (PyObject_TypeCheck(data, &zbarImage_Type)) { + zbarImage *self = (zbarImage *)data; + assert(self->zimg == zimg); + Py_CLEAR(self->data); + } else + Py_DECREF(data); +} + +static int image_set_data(zbarImage *self, PyObject *value, void *closure) +{ + if (!value) { + zbar_image_free_data(self->zimg); + return (0); + } + char *data; + Py_ssize_t datalen; +#if PY_MAJOR_VERSION >= 3 + PyObject *bytes; + + if (PyUnicode_Check(value)) + bytes = PyUnicode_AsEncodedString(value, "utf-8", "surrogateescape"); + else + bytes = value; + if (PyBytes_AsStringAndSize(bytes, &data, &datalen)) + return (-1); +#else + if (PyString_AsStringAndSize(value, &data, &datalen)) + return (-1); +#endif + + Py_INCREF(value); + zbar_image_set_data(self->zimg, data, datalen, image_cleanup); + assert(!self->data); + self->data = value; + zbar_image_set_userdata(self->zimg, self); + return (0); +} + +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, NULL, (void *)1 }, + { "sequence", (getter)image_get_int, (setter)image_set_int, NULL, + (void *)2 }, + { + "data", + (getter)image_get_data, + (setter)image_set_data, + }, + { + "symbols", + (getter)image_get_symbols, + (setter)image_set_symbols, + }, + { + NULL, + }, +}; + +static int image_init(zbarImage *self, PyObject *args, PyObject *kwds) +{ + int width = -1, height = -1; + PyObject *format = NULL, *data = NULL; + static char *kwlist[] = { "width", "height", "format", "data", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiOO", kwlist, &width, + &height, &format, &data)) + return (-1); + + if (width > 0 && height > 0) + zbar_image_set_size(self->zimg, width, height); + if (format && image_set_format(self, format, NULL)) + return (-1); + if (data && image_set_data(self, data, NULL)) + return (-1); + return (0); +} + +static zbarImage *image_convert(zbarImage *self, PyObject *args, PyObject *kwds) +{ + const char *format = NULL; + int width = -1, height = -1; + static char *kwlist[] = { "format", "width", "height", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii", kwlist, &format, + &width, &height)) + return (NULL); + assert(format); + + if (strlen(format) != 4) { + PyErr_Format(PyExc_ValueError, + "format '%.50s' is not a valid four character code", + format); + return (NULL); + } + unsigned long fourcc = zbar_fourcc_parse(format); + + zbarImage *img = PyObject_GC_New(zbarImage, &zbarImage_Type); + if (!img) + return (NULL); + img->data = NULL; + if (width > 0 && height > 0) + img->zimg = + zbar_image_convert_resize(self->zimg, fourcc, width, height); + else + img->zimg = zbar_image_convert(self->zimg, fourcc); + + if (!img->zimg) { + /* FIXME propagate exception */ + Py_DECREF(img); + return (NULL); + } + zbar_image_set_userdata(img->zimg, img); + + return (img); +} + +static PyMethodDef image_methods[] = { + { + "convert", + (PyCFunction)image_convert, + METH_VARARGS | METH_KEYWORDS, + }, + { + NULL, + }, +}; + +PyTypeObject zbarImage_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Image", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = image_doc, + .tp_basicsize = sizeof(zbarImage), + .tp_new = (newfunc)image_new, + .tp_init = (initproc)image_init, + .tp_traverse = (traverseproc)image_traverse, + .tp_clear = (inquiry)image_clear, + .tp_dealloc = (destructor)image_dealloc, + .tp_getset = image_getset, + .tp_methods = image_methods, + .tp_iter = (getiterfunc)image_iter, +}; + +zbarImage *zbarImage_FromImage(zbar_image_t *zimg) +{ + zbarImage *self = PyObject_GC_New(zbarImage, &zbarImage_Type); + if (!self) + return (NULL); + zbar_image_ref(zimg, 1); + zbar_image_set_userdata(zimg, self); + self->zimg = zimg; + self->data = NULL; + return (self); +} + +int zbarImage_validate(zbarImage *img) +{ + if (!zbar_image_get_width(img->zimg) || !zbar_image_get_height(img->zimg) || + !zbar_image_get_data(img->zimg) || + !zbar_image_get_data_length(img->zimg)) { + PyErr_Format(PyExc_ValueError, "image size and data must be defined"); + return (-1); + } + return (0); +} diff --git a/python/imagescanner.c b/python/imagescanner.c new file mode 100644 index 0000000..54966b7 --- /dev/null +++ b/python/imagescanner.c @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char imagescanner_doc[] = + PyDoc_STR("scan images for barcodes.\n" + "\n" + "attaches symbols to image for each decoded result."); + +static zbarImageScanner *imagescanner_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbarImageScanner *self = (zbarImageScanner *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + + self->zscn = zbar_image_scanner_create(); + if (!self->zscn) { + Py_DECREF(self); + return (NULL); + } + + return (self); +} + +static void imagescanner_dealloc(zbarImageScanner *self) +{ + zbar_image_scanner_destroy(self->zscn); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarSymbolSet *imagescanner_get_results(zbarImageScanner *self, + void *closure) +{ + const zbar_symbol_set_t *zsyms = zbar_image_scanner_get_results(self->zscn); + return (zbarSymbolSet_FromSymbolSet(zsyms)); +} + +static PyGetSetDef imagescanner_getset[] = { + { "results", (getter)imagescanner_get_results, NULL, NULL, NULL }, + { NULL } /* Sentinel */ +}; + +static PyObject *imagescanner_set_config(zbarImageScanner *self, PyObject *args, + PyObject *kwds) +{ + zbar_symbol_type_t sym = ZBAR_NONE; + zbar_config_t cfg = ZBAR_CFG_ENABLE; + int val = 1; + static char *kwlist[] = { "symbology", "config", "value", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &sym, &cfg, + &val)) + return (NULL); + + if (zbar_image_scanner_set_config(self->zscn, sym, cfg, val)) { + PyErr_SetString(PyExc_ValueError, "invalid configuration setting"); + return (NULL); + } + Py_RETURN_NONE; +} + +static PyObject *imagescanner_parse_config(zbarImageScanner *self, + PyObject *args, PyObject *kwds) +{ + const char *cfg = NULL; + static char *kwlist[] = { "config", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cfg)) + return (NULL); + + if (zbar_image_scanner_parse_config(self->zscn, cfg)) { + PyErr_Format(PyExc_ValueError, "invalid configuration setting: %s", + cfg); + return (NULL); + } + Py_RETURN_NONE; +} + +static PyObject *imagescanner_enable_cache(zbarImageScanner *self, + PyObject *args, PyObject *kwds) +{ + unsigned char enable = 1; + static char *kwlist[] = { "enable", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, object_to_bool, + &enable)) + return (NULL); + + zbar_image_scanner_enable_cache(self->zscn, enable); + Py_RETURN_NONE; +} + +static PyObject *imagescanner_recycle(zbarImageScanner *self, PyObject *args, + PyObject *kwds) +{ + zbarImage *img = NULL; + static char *kwlist[] = { "image", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &zbarImage_Type, + &img)) + return (NULL); + + zbar_image_scanner_recycle_image(self->zscn, img->zimg); + Py_RETURN_NONE; +} + +static PyObject *imagescanner_scan(zbarImageScanner *self, PyObject *args, + PyObject *kwds) +{ + zbarImage *img = NULL; + static char *kwlist[] = { "image", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &zbarImage_Type, + &img)) + return (NULL); + + if (zbarImage_validate(img)) + return (NULL); + + int n = zbar_scan_image(self->zscn, img->zimg); + if (n < 0) { + PyErr_Format(PyExc_ValueError, "unsupported image format"); + return (NULL); + } +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(n)); +#else + return (PyInt_FromLong(n)); +#endif +} + +static PyMethodDef imagescanner_methods[] = { + { + "set_config", + (PyCFunction)imagescanner_set_config, + METH_VARARGS | METH_KEYWORDS, + }, + { + "parse_config", + (PyCFunction)imagescanner_parse_config, + METH_VARARGS | METH_KEYWORDS, + }, + { + "enable_cache", + (PyCFunction)imagescanner_enable_cache, + METH_VARARGS | METH_KEYWORDS, + }, + { + "recycle", + (PyCFunction)imagescanner_recycle, + METH_VARARGS | METH_KEYWORDS, + }, + { + "scan", + (PyCFunction)imagescanner_scan, + METH_VARARGS | METH_KEYWORDS, + }, + { + NULL, + }, +}; + +PyTypeObject zbarImageScanner_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.ImageScanner", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = imagescanner_doc, + .tp_basicsize = sizeof(zbarImageScanner), + .tp_new = (newfunc)imagescanner_new, + .tp_dealloc = (destructor)imagescanner_dealloc, + .tp_getset = imagescanner_getset, + .tp_methods = imagescanner_methods, +}; diff --git a/python/processor.c b/python/processor.c new file mode 100644 index 0000000..a9e4c47 --- /dev/null +++ b/python/processor.c @@ -0,0 +1,456 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +static char processor_doc[] = + PyDoc_STR("low level decode of measured bar/space widths.\n" + "\n" + "FIXME."); + +static zbarProcessor *processor_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { "enable_threads", NULL }; + int threaded = -1; + zbarProcessor *self; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, object_to_bool, + &threaded)) + return (NULL); + +#ifdef WITH_THREAD +#if (PY_MAJOR_VERSION < 3) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9) + /* the processor creates a thread that calls back into python, + * so we must ensure that threads are initialized before attempting + * to manipulate the GIL (bug #3349199) + */ + PyEval_InitThreads(); +#endif +#else + if (threaded > 0 && + PyErr_WarnEx(NULL, "threading requested but not available", 1)) + return (NULL); + threaded = 0; +#endif + + self = (zbarProcessor *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + + self->zproc = zbar_processor_create(threaded); + zbar_processor_set_userdata(self->zproc, self); + if (!self->zproc) { + Py_DECREF(self); + return (NULL); + } + return (self); +} + +static int processor_traverse(zbarProcessor *self, visitproc visit, void *arg) +{ + Py_VISIT(self->handler); + Py_VISIT(self->closure); + return (0); +} + +static int processor_clear(zbarProcessor *self) +{ + zbar_processor_set_data_handler(self->zproc, NULL, NULL); + zbar_processor_set_userdata(self->zproc, NULL); + Py_CLEAR(self->handler); + Py_CLEAR(self->closure); + return (0); +} + +static void processor_dealloc(zbarProcessor *self) +{ + processor_clear(self); + zbar_processor_destroy(self->zproc); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static PyObject *processor_get_bool(zbarProcessor *self, void *closure) +{ + int val; + switch ((intptr_t)closure) { + case 0: + val = zbar_processor_is_visible(self->zproc); + break; + default: + assert(0); + return (NULL); + } + if (val < 0) + return (zbarErr_Set((PyObject *)self)); + return (PyBool_FromLong(val)); +} + +static int processor_set_bool(zbarProcessor *self, PyObject *value, + void *closure) +{ + int rc, val; + + if (!value) { + PyErr_SetString(PyExc_TypeError, "cannot delete attribute"); + return (-1); + } + val = PyObject_IsTrue(value); + if (val < 0) + return (-1); + switch ((intptr_t)closure) { + case 0: + rc = zbar_processor_set_visible(self->zproc, val); + break; + case 1: + rc = zbar_processor_set_active(self->zproc, val); + break; + default: + assert(0); + return (-1); + } + if (rc < 0) { + zbarErr_Set((PyObject *)self); + return (-1); + } + return (0); +} + +static zbarSymbolSet *processor_get_results(zbarProcessor *self, void *closure) +{ + const zbar_symbol_set_t *zsyms = zbar_processor_get_results(self->zproc); + + return (zbarSymbolSet_FromSymbolSet(zsyms)); +} + +static int processor_set_request_size(zbarProcessor *self, PyObject *value, + void *closure) +{ + int dims[2]; + + if (!value) { + zbar_processor_request_size(self->zproc, 0, 0); + return (0); + } + + if (parse_dimensions(value, dims, 2) || dims[0] < 0 || dims[1] < 0) { + PyErr_SetString(PyExc_ValueError, + "request_size must be a sequence of two positive ints"); + return (-1); + } + + zbar_processor_request_size(self->zproc, dims[0], dims[1]); + return (0); +} + +static PyGetSetDef processor_getset[] = { + { "visible", (getter)processor_get_bool, (setter)processor_set_bool, NULL, + (void *)0 }, + { "active", NULL, (setter)processor_set_bool, NULL, (void *)1 }, + { + "results", + (getter)processor_get_results, + }, + { + "request_size", + NULL, + (setter)processor_set_request_size, + }, + { + NULL, + }, +}; + +static PyObject *processor_set_config(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + zbar_symbol_type_t sym = ZBAR_NONE; + zbar_config_t cfg = ZBAR_CFG_ENABLE; + int val = 1; + static char *kwlist[] = { "symbology", "config", "value", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &sym, &cfg, + &val)) + return (NULL); + + if (zbar_processor_set_config(self->zproc, sym, cfg, val)) { + PyErr_SetString(PyExc_ValueError, "invalid configuration setting"); + return (NULL); + } + Py_RETURN_NONE; +} + +static PyObject *processor_init_(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + const char *dev = ""; + int disp = 1; + static char *kwlist[] = { "video_device", "enable_display", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zO&", kwlist, &dev, + object_to_bool, &disp)) + return (NULL); + + if (zbar_processor_init(self->zproc, dev, disp)) + return (zbarErr_Set((PyObject *)self)); + Py_RETURN_NONE; +} + +static PyObject *processor_parse_config(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + const char *cfg = NULL; + static char *kwlist[] = { "config", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cfg)) + return (NULL); + + if (zbar_processor_parse_config(self->zproc, cfg)) { + PyErr_Format(PyExc_ValueError, "invalid configuration setting: %s", + cfg); + return (NULL); + } + Py_RETURN_NONE; +} + +static int object_to_timeout(PyObject *obj, int *val) +{ + long tmp; + + if (PyFloat_Check(obj)) + tmp = PyFloat_AS_DOUBLE(obj) * 1000; + else +#if PY_MAJOR_VERSION >= 3 + tmp = PyLong_AsLong(obj) * 1000; +#else + tmp = PyInt_AsLong(obj) * 1000; +#endif + if (tmp < 0 && PyErr_Occurred()) + return (0); + *val = tmp; + return (1); +} + +static PyObject *processor_user_wait(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + int timeout = -1; + int rc = -1; + static char *kwlist[] = { "timeout", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, + object_to_timeout, &timeout)) + return (NULL); + + Py_BEGIN_ALLOW_THREADS rc = zbar_processor_user_wait(self->zproc, timeout); + Py_END_ALLOW_THREADS + + if (rc < 0) return (zbarErr_Set((PyObject *)self)); +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(rc)); +#else + return (PyInt_FromLong(rc)); +#endif +} + +static PyObject *processor_process_one(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + int timeout = -1; + int rc = -1; + static char *kwlist[] = { "timeout", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, + object_to_timeout, &timeout)) + return (NULL); + + Py_BEGIN_ALLOW_THREADS rc = zbar_process_one(self->zproc, timeout); + Py_END_ALLOW_THREADS + + if (rc < 0) return (zbarErr_Set((PyObject *)self)); +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(rc)); +#else + return (PyInt_FromLong(rc)); +#endif +} + +static PyObject *processor_process_image(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + zbarImage *img = NULL; + static char *kwlist[] = { "image", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &zbarImage_Type, + &img)) + return (NULL); + + if (zbarImage_validate(img)) + return (NULL); + + int n = -1; + Py_BEGIN_ALLOW_THREADS n = zbar_process_image(self->zproc, img->zimg); + Py_END_ALLOW_THREADS + + if (n < 0) return (zbarErr_Set((PyObject *)self)); +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(n)); +#else + return (PyInt_FromLong(n)); +#endif +} + +void process_handler(zbar_image_t *zimg, const void *userdata) +{ + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + zbarImage *img; + + zbarProcessor *self = (zbarProcessor *)userdata; + assert(self); + assert(self->handler); + assert(self->closure); + + img = zbar_image_get_userdata(zimg); + if (!img || img->zimg != zimg) { + img = zbarImage_FromImage(zimg); + if (!img) { + PyErr_NoMemory(); + goto done; + } + } else + Py_INCREF(img); + + PyObject *args = PyTuple_New(3); + Py_INCREF(self); + Py_INCREF(self->closure); + PyTuple_SET_ITEM(args, 0, (PyObject *)self); + PyTuple_SET_ITEM(args, 1, (PyObject *)img); + PyTuple_SET_ITEM(args, 2, self->closure); + + PyObject *junk = PyObject_Call(self->handler, args, NULL); + if (junk) + Py_DECREF(junk); + else { + PySys_WriteStderr("in ZBar Processor data_handler:\n"); + assert(PyErr_Occurred()); + PyErr_Print(); + } + Py_DECREF(args); + +done: + PyGILState_Release(gstate); +} + +static PyObject *processor_set_data_handler(zbarProcessor *self, PyObject *args, + PyObject *kwds) +{ + PyObject *handler = Py_None; + PyObject *closure = Py_None; + + static char *kwlist[] = { "handler", "closure", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &handler, + &closure)) + return (NULL); + + if (handler != Py_None && !PyCallable_Check(handler)) { + PyErr_Format(PyExc_ValueError, "handler %.50s is not callable", + handler->ob_type->tp_name); + return (NULL); + } + Py_CLEAR(self->handler); + Py_CLEAR(self->closure); + + if (handler != Py_None) { + Py_INCREF(handler); + self->handler = handler; + + Py_INCREF(closure); + self->closure = closure; + + zbar_processor_set_data_handler(self->zproc, process_handler, self); + } else { + self->handler = self->closure = NULL; + zbar_processor_set_data_handler(self->zproc, NULL, self); + } + Py_RETURN_NONE; +} + +static PyMethodDef processor_methods[] = { + { + "init", + (PyCFunction)processor_init_, + METH_VARARGS | METH_KEYWORDS, + }, + { + "set_config", + (PyCFunction)processor_set_config, + METH_VARARGS | METH_KEYWORDS, + }, + { + "parse_config", + (PyCFunction)processor_parse_config, + METH_VARARGS | METH_KEYWORDS, + }, + { + "user_wait", + (PyCFunction)processor_user_wait, + METH_VARARGS | METH_KEYWORDS, + }, + { + "process_one", + (PyCFunction)processor_process_one, + METH_VARARGS | METH_KEYWORDS, + }, + { + "process_image", + (PyCFunction)processor_process_image, + METH_VARARGS | METH_KEYWORDS, + }, + { + "set_data_handler", + (PyCFunction)processor_set_data_handler, + METH_VARARGS | METH_KEYWORDS, + }, + { + NULL, + }, +}; + +PyTypeObject zbarProcessor_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Processor", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = processor_doc, + .tp_basicsize = sizeof(zbarProcessor), + .tp_new = (newfunc)processor_new, + .tp_traverse = (traverseproc)processor_traverse, + .tp_clear = (inquiry)processor_clear, + .tp_dealloc = (destructor)processor_dealloc, + .tp_getset = processor_getset, + .tp_methods = processor_methods, +}; diff --git a/python/scanner.c b/python/scanner.c new file mode 100644 index 0000000..e5ad8bf --- /dev/null +++ b/python/scanner.c @@ -0,0 +1,193 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char scanner_doc[] = PyDoc_STR( + "low level intensity sample stream scanner. identifies \"bar\" edges" + "and measures width between them.\n" + "\n" + "FIXME."); + +static zbarScanner *scanner_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + zbarDecoder *decoder = NULL; + static char *kwlist[] = { "decoder", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", kwlist, &decoder, + zbarDecoder_Type)) + return (NULL); + + zbarScanner *self = (zbarScanner *)type->tp_alloc(type, 0); + if (!self) + return (NULL); + + zbar_decoder_t *zdcode = NULL; + if (decoder) { + Py_INCREF(decoder); + self->decoder = decoder; + zdcode = decoder->zdcode; + } + self->zscn = zbar_scanner_create(zdcode); + if (!self->zscn) { + Py_DECREF(self); + return (NULL); + } + + return (self); +} + +static int scanner_traverse(zbarScanner *self, visitproc visit, void *arg) +{ + Py_VISIT(self->decoder); + return (0); +} + +static int scanner_clear(zbarScanner *self) +{ + Py_CLEAR(self->decoder); + return (0); +} + +static void scanner_dealloc(zbarScanner *self) +{ + scanner_clear(self); + zbar_scanner_destroy(self->zscn); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static PyObject *scanner_get_width(zbarScanner *self, void *closure) +{ + unsigned int width = zbar_scanner_get_width(self->zscn); +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(width)); +#else + return (PyInt_FromLong(width)); +#endif +} + +static zbarEnumItem *scanner_get_color(zbarScanner *self, void *closure) +{ + zbar_color_t zcol = zbar_scanner_get_color(self->zscn); + assert(zcol == ZBAR_BAR || zcol == ZBAR_SPACE); + struct module_state *st = GETMODSTATE(); + zbarEnumItem *color = st->color_enum[zcol]; + Py_INCREF((PyObject *)color); + return (color); +} + +static PyGetSetDef scanner_getset[] = { + { + "color", + (getter)scanner_get_color, + }, + { + "width", + (getter)scanner_get_width, + }, + { + NULL, + }, +}; + +static PyObject *scanner_reset(zbarScanner *self, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbar_scanner_reset(self->zscn); + Py_RETURN_NONE; +} + +static PyObject *scanner_new_scan(zbarScanner *self, PyObject *args, + PyObject *kwds) +{ + static char *kwlist[] = { NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) + return (NULL); + + zbar_scanner_new_scan(self->zscn); + Py_RETURN_NONE; +} + +static zbarEnumItem *scanner_scan_y(zbarScanner *self, PyObject *args, + PyObject *kwds) +{ + /* FIXME should accept sequence of values */ + int y = 0; + static char *kwlist[] = { "y", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &y)) + return (NULL); + + zbar_symbol_type_t sym = zbar_scan_y(self->zscn, y); + if (PyErr_Occurred()) + /* propagate errors during callback */ + return (NULL); + if (sym == ZBAR_NONE) { + /* hardcode most common case */ + struct module_state *st = GETMODSTATE(); + Py_INCREF((PyObject *)st->symbol_NONE); + return (st->symbol_NONE); + } + return (zbarSymbol_LookupEnum(sym)); +} + +static PyMethodDef scanner_methods[] = { + { + "reset", + (PyCFunction)scanner_reset, + METH_VARARGS | METH_KEYWORDS, + }, + { + "new_scan", + (PyCFunction)scanner_new_scan, + METH_VARARGS | METH_KEYWORDS, + }, + { + "scan_y", + (PyCFunction)scanner_scan_y, + METH_VARARGS | METH_KEYWORDS, + }, + { + NULL, + }, +}; + +PyTypeObject zbarScanner_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Scanner", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = scanner_doc, + .tp_basicsize = sizeof(zbarScanner), + .tp_new = (newfunc)scanner_new, + .tp_traverse = (traverseproc)scanner_traverse, + .tp_clear = (inquiry)scanner_clear, + .tp_dealloc = (destructor)scanner_dealloc, + .tp_getset = scanner_getset, + .tp_methods = scanner_methods, +}; diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..5c27951 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +from distutils.core import setup, Extension + +setup( + name = 'zbar', + version = '0.22.2', + 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' ], + include_dirs = ['../include'] + ), + ], +) diff --git a/python/symbol.c b/python/symbol.c new file mode 100644 index 0000000..cc7cb83 --- /dev/null +++ b/python/symbol.c @@ -0,0 +1,230 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char symbol_doc[] = + PyDoc_STR("symbol result object.\n" + "\n" + "data and associated information about a successful decode."); + +static int symbol_traverse(zbarSymbol *self, visitproc visit, void *arg) +{ + return (0); +} + +static int symbol_clear(zbarSymbol *self) +{ + if (self->zsym) { + zbar_symbol_t *zsym = (zbar_symbol_t *)self->zsym; + self->zsym = NULL; + zbar_symbol_ref(zsym, -1); + } + Py_CLEAR(self->data); + Py_CLEAR(self->loc); + return (0); +} + +static void symbol_dealloc(zbarSymbol *self) +{ + symbol_clear(self); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarSymbolSet *symbol_get_components(zbarSymbol *self, void *closure) +{ + const zbar_symbol_set_t *zsyms = zbar_symbol_get_components(self->zsym); + return (zbarSymbolSet_FromSymbolSet(zsyms)); +} + +static zbarSymbolIter *symbol_iter(zbarSymbol *self) +{ + zbarSymbolSet *syms = symbol_get_components(self, NULL); + zbarSymbolIter *iter = zbarSymbolIter_FromSymbolSet(syms); + Py_XDECREF(syms); + return (iter); +} + +static zbarEnumItem *symbol_get_type(zbarSymbol *self, void *closure) +{ + return (zbarSymbol_LookupEnum(zbar_symbol_get_type(self->zsym))); +} + +static PyObject *symbol_get_configs(zbarSymbol *self, void *closure) +{ + unsigned int mask = zbar_symbol_get_configs(self->zsym); + struct module_state *st = GETMODSTATE(); + return (zbarEnum_SetFromMask(st->config_enum, mask)); +} + +static PyObject *symbol_get_modifiers(zbarSymbol *self, void *closure) +{ + unsigned int mask = zbar_symbol_get_modifiers(self->zsym); + struct module_state *st = GETMODSTATE(); + return (zbarEnum_SetFromMask(st->modifier_enum, mask)); +} + +static PyObject *symbol_get_long(zbarSymbol *self, void *closure) +{ + int val; + if (!closure) + val = zbar_symbol_get_quality(self->zsym); + else + val = zbar_symbol_get_count(self->zsym); +#if PY_MAJOR_VERSION >= 3 + return (PyLong_FromLong(val)); +#else + return (PyInt_FromLong(val)); +#endif +} + +static PyObject *symbol_get_data(zbarSymbol *self, void *closure) +{ + if (!self->data) { +#if PY_MAJOR_VERSION >= 3 + self->data = PyUnicode_FromStringAndSize( + zbar_symbol_get_data(self->zsym), + zbar_symbol_get_data_length(self->zsym)); +#else + /* FIXME this could be a buffer now */ + self->data = + PyString_FromStringAndSize(zbar_symbol_get_data(self->zsym), + zbar_symbol_get_data_length(self->zsym)); +#endif + if (!self->data) + return (NULL); + } + Py_INCREF(self->data); + return (self->data); +} + +static PyObject *symbol_get_location(zbarSymbol *self, void *closure) +{ + if (!self->loc) { + /* build tuple of 2-tuples representing location polygon */ + unsigned int n = zbar_symbol_get_loc_size(self->zsym); + self->loc = PyTuple_New(n); + unsigned int i; + for (i = 0; i < n; i++) { + PyObject *x, *y; +#if PY_MAJOR_VERSION >= 3 + x = PyLong_FromLong(zbar_symbol_get_loc_x(self->zsym, i)); + y = PyLong_FromLong(zbar_symbol_get_loc_y(self->zsym, i)); +#else + x = PyInt_FromLong(zbar_symbol_get_loc_x(self->zsym, i)); + y = PyInt_FromLong(zbar_symbol_get_loc_y(self->zsym, i)); +#endif + PyTuple_SET_ITEM(self->loc, i, PyTuple_Pack(2, x, y)); + } + } + Py_INCREF(self->loc); + return (self->loc); +} + +static zbarEnumItem *symbol_get_orientation(zbarSymbol *self, void *closure) +{ + struct module_state *st = GETMODSTATE(); + return (zbarEnum_LookupValue(st->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, + }, +}; + +PyTypeObject zbarSymbol_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.Symbol", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = symbol_doc, + .tp_basicsize = sizeof(zbarSymbol), + .tp_traverse = (traverseproc)symbol_traverse, + .tp_clear = (inquiry)symbol_clear, + .tp_dealloc = (destructor)symbol_dealloc, + .tp_iter = (getiterfunc)symbol_iter, + .tp_getset = symbol_getset, +}; + +zbarSymbol *zbarSymbol_FromSymbol(const zbar_symbol_t *zsym) +{ + /* FIXME symbol object recycle cache */ + zbarSymbol *self = PyObject_GC_New(zbarSymbol, &zbarSymbol_Type); + if (!self) + return (NULL); + assert(zsym); + zbar_symbol_t *zs = (zbar_symbol_t *)zsym; + zbar_symbol_ref(zs, 1); + self->zsym = zsym; + self->data = NULL; + self->loc = NULL; + return (self); +} + +zbarEnumItem *zbarSymbol_LookupEnum(zbar_symbol_type_t type) +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *key = PyLong_FromLong(type); +#else + PyObject *key = PyInt_FromLong(type); +#endif + struct module_state *st = GETMODSTATE(); + zbarEnumItem *e = (zbarEnumItem *)PyDict_GetItem(st->symbol_enum, key); + if (!e) + return ((zbarEnumItem *)key); + Py_INCREF((PyObject *)e); + Py_DECREF(key); + return (e); +} diff --git a/python/symboliter.c b/python/symboliter.c new file mode 100644 index 0000000..6ebd040 --- /dev/null +++ b/python/symboliter.c @@ -0,0 +1,103 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char symboliter_doc[] = + PyDoc_STR("symbol iterator.\n" + "\n" + "iterates over decode results attached to an image."); + +static int symboliter_traverse(zbarSymbolIter *self, visitproc visit, void *arg) +{ + Py_VISIT(self->syms); + return (0); +} + +static int symboliter_clear(zbarSymbolIter *self) +{ + if (self->zsym) { + zbar_symbol_t *zsym = (zbar_symbol_t *)self->zsym; + self->zsym = NULL; + zbar_symbol_ref(zsym, -1); + } + Py_CLEAR(self->syms); + return (0); +} + +static void symboliter_dealloc(zbarSymbolIter *self) +{ + symboliter_clear(self); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarSymbolIter *symboliter_iter(zbarSymbolIter *self) +{ + Py_INCREF(self); + return (self); +} + +static zbarSymbol *symboliter_iternext(zbarSymbolIter *self) +{ + if (self->zsym) { + zbar_symbol_t *zsym = (zbar_symbol_t *)self->zsym; + zbar_symbol_ref(zsym, -1); + self->zsym = zbar_symbol_next(self->zsym); + } else if (self->syms->zsyms) + self->zsym = zbar_symbol_set_first_symbol(self->syms->zsyms); + else + self->zsym = NULL; + + zbar_symbol_t *zsym = (zbar_symbol_t *)self->zsym; + if (!zsym) + return (NULL); + zbar_symbol_ref(zsym, 1); + return (zbarSymbol_FromSymbol(self->zsym)); +} + +PyTypeObject zbarSymbolIter_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.SymbolIter", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + + .tp_doc = symboliter_doc, + .tp_basicsize = sizeof(zbarSymbolIter), + .tp_traverse = (traverseproc)symboliter_traverse, + .tp_clear = (inquiry)symboliter_clear, + .tp_dealloc = (destructor)symboliter_dealloc, + .tp_iter = (getiterfunc)symboliter_iter, + .tp_iternext = (iternextfunc)symboliter_iternext, +}; + +zbarSymbolIter *zbarSymbolIter_FromSymbolSet(zbarSymbolSet *syms) +{ + zbarSymbolIter *self; + self = PyObject_GC_New(zbarSymbolIter, &zbarSymbolIter_Type); + if (!self) + return (NULL); + + Py_INCREF(syms); + self->syms = syms; + self->zsym = NULL; + return (self); +} diff --git a/python/symbolset.c b/python/symbolset.c new file mode 100644 index 0000000..9d65e80 --- /dev/null +++ b/python/symbolset.c @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +static char symbolset_doc[] = PyDoc_STR("symbol result container.\n" + "\n" + "collection of symbols."); + +static int symbolset_clear(zbarSymbolSet *self) +{ + if (self->zsyms) { + zbar_symbol_set_t *zsyms = (zbar_symbol_set_t *)self->zsyms; + self->zsyms = NULL; + zbar_symbol_set_ref(zsyms, -1); + } + return (0); +} + +static void symbolset_dealloc(zbarSymbolSet *self) +{ + symbolset_clear(self); + ((PyObject *)self)->ob_type->tp_free((PyObject *)self); +} + +static zbarSymbolIter *symbolset_iter(zbarSymbolSet *self) +{ + return (zbarSymbolIter_FromSymbolSet(self)); +} + +Py_ssize_t symbolset_length(zbarSymbolSet *self) +{ + if (self->zsyms) + return (zbar_symbol_set_get_size(self->zsyms)); + return (0); +} + +static PySequenceMethods symbolset_as_sequence = { + .sq_length = (lenfunc)symbolset_length, +}; + +PyTypeObject zbarSymbolSet_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "zbar.SymbolSet", + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = symbolset_doc, + .tp_basicsize = sizeof(zbarSymbolSet), + .tp_dealloc = (destructor)symbolset_dealloc, + .tp_iter = (getiterfunc)symbolset_iter, + .tp_as_sequence = &symbolset_as_sequence, +}; + +zbarSymbolSet *zbarSymbolSet_FromSymbolSet(const zbar_symbol_set_t *zsyms) +{ + zbarSymbolSet *self = PyObject_New(zbarSymbolSet, &zbarSymbolSet_Type); + if (!self) + return (NULL); + if (zsyms) { + zbar_symbol_set_t *ncsyms = (zbar_symbol_set_t *)zsyms; + zbar_symbol_set_ref(ncsyms, 1); + } + self->zsyms = zsyms; + return (self); +} diff --git a/python/test/barcode.png b/python/test/barcode.png Binary files differnew file mode 100644 index 0000000..72846ce --- /dev/null +++ b/python/test/barcode.png diff --git a/python/test/test_zbar.py b/python/test/test_zbar.py new file mode 100755 index 0000000..5b63de4 --- /dev/null +++ b/python/test/test_zbar.py @@ -0,0 +1,506 @@ +#!/usr/bin/env python +import sys, os, re +import unittest as ut +import zbar + +# FIXME this needs to be conditional +# would be even better to auto-select PIL or ImageMagick (or...) +from PIL import Image + +data = None +size = (0, 0) + +def load_image(): + image = Image.open(os.path.join(sys.path[0], "barcode.png")).convert('L') + return(image.tobytes(), image.size) + +# FIXME this could be integrated w/fixture creation +(data, size) = 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'] + +is_identifier = re.compile(r'^[A-Z][A-Z_0-9]*$') + +class TestZBarFunctions(ut.TestCase): + def test_version(self): + ver = zbar.version() + self.assertTrue(isinstance(ver, tuple)) + self.assertEqual(len(ver), 2) + for v in ver: + self.assertTrue(isinstance(v, int)) + + def test_verbosity(self): + zbar.increase_verbosity() + zbar.set_verbosity(16) + + def test_exceptions(self): + self.assertTrue(isinstance(zbar.Exception, type)) + for err in (zbar.InternalError, + zbar.UnsupportedError, + zbar.InvalidRequestError, + zbar.SystemError, + zbar.LockingError, + zbar.BusyError, + zbar.X11DisplayError, + zbar.X11ProtocolError, + zbar.WindowClosed, + zbar.WinAPIError): + self.assertTrue(issubclass(err, zbar.Exception)) + + def test_configs(self): + for cfg in (zbar.Config.ENABLE, + zbar.Config.ADD_CHECK, + zbar.Config.EMIT_CHECK, + 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): + self.assertTrue(isinstance(cfg, zbar.EnumItem)) + self.assertTrue(int(cfg) >= 0) + self.assertTrue(is_identifier.match(str(cfg))) + + def test_modifiers(self): + for mod in (zbar.Modifier.GS1, + zbar.Modifier.AIM): + self.assertTrue(isinstance(mod, zbar.EnumItem)) + self.assertTrue(int(mod) >= 0) + self.assertTrue(is_identifier.match(str(mod))) + + def test_symbologies(self): + for sym in (zbar.Symbol.NONE, + zbar.Symbol.PARTIAL, + zbar.Symbol.EAN8, + zbar.Symbol.UPCE, + zbar.Symbol.ISBN10, + zbar.Symbol.UPCA, + zbar.Symbol.EAN13, + zbar.Symbol.ISBN13, + zbar.Symbol.DATABAR, + zbar.Symbol.DATABAR_EXP, + zbar.Symbol.I25, + zbar.Symbol.CODABAR, + zbar.Symbol.CODE39, + zbar.Symbol.PDF417, + zbar.Symbol.QRCODE, + zbar.Symbol.CODE93, + zbar.Symbol.CODE128): + self.assertTrue(isinstance(sym, zbar.EnumItem)) + self.assertTrue(int(sym) >= 0) + self.assertTrue(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.assertTrue(isinstance(orient, zbar.EnumItem)) + self.assertTrue(-1 <= int(orient) <= 3) + self.assertTrue(is_identifier.match(str(orient))) + +class TestScanner(ut.TestCase): + def setUp(self): + self.scn = zbar.Scanner() + + def tearDown(self): + del(self.scn) + + def test_type(self): + self.assertTrue(isinstance(self.scn, zbar.Scanner)) + self.assertTrue(callable(self.scn.reset)) + self.assertTrue(callable(self.scn.new_scan)) + self.assertTrue(callable(self.scn.scan_y)) + + def set_color(color): + self.scn.color = color + self.assertRaises(AttributeError, set_color, zbar.BAR) + + def set_width(width): + self.scn.width = width + self.assertRaises(AttributeError, set_width, 1) + + # FIXME more scanner tests + +class TestDecoder(ut.TestCase): + def setUp(self): + self.dcode = zbar.Decoder() + + def tearDown(self): + del(self.dcode) + + def test_type(self): + self.assertTrue(isinstance(self.dcode, zbar.Decoder)) + self.assertTrue(callable(self.dcode.set_config)) + self.assertTrue(callable(self.dcode.parse_config)) + self.assertTrue(callable(self.dcode.reset)) + self.assertTrue(callable(self.dcode.new_scan)) + self.assertTrue(callable(self.dcode.set_handler)) + self.assertTrue(callable(self.dcode.decode_width)) + + def set_type(typ): + self.dcode.type = typ + self.assertRaises(AttributeError, set_type, zbar.Symbol.CODE128) + + def set_color(color): + self.dcode.color = color + self.assertRaises(AttributeError, set_color, zbar.BAR) + + def set_data(data): + 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.assertTrue(sym is zbar.Symbol.NONE) + self.assertTrue(not sym) + self.assertEqual(str(sym), 'NONE') + + typ = self.dcode.type + self.assertTrue(sym is typ) + + def test_reset(self): + self.assertTrue(self.dcode.color is zbar.SPACE) + sym = self.dcode.decode_width(1) + self.assertTrue(self.dcode.color is zbar.BAR) + self.dcode.reset() + self.assertTrue(self.dcode.color is zbar.SPACE) + self.assertEqual(self.dcode.direction, 0) + + def test_decode(self): + inline_sym = [ -1 ] + def handler(dcode, closure): + self.assertTrue(dcode is self.dcode) + if dcode.type > zbar.Symbol.PARTIAL: + inline_sym[0] = dcode.type + closure[0] += 1 + + explicit_closure = [ 0 ] + self.dcode.set_handler(handler, explicit_closure) + self.dcode.set_config(zbar.Symbol.QRCODE, zbar.Config.ENABLE, 0) + + for (i, width) in enumerate(encoded_widths): + if width == ' ': continue + sym = self.dcode.decode_width(int(width)) + if i < len(encoded_widths) - 1: + self.assertTrue(sym is zbar.Symbol.NONE or + sym is zbar.Symbol.PARTIAL) + else: + self.assertTrue(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.assertTrue(self.dcode.color is zbar.BAR) + self.assertEqual(self.dcode.direction, 1) + self.assertTrue(sym is zbar.Symbol.EAN13) + self.assertTrue(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.assertTrue(sym is zbar.Symbol.NONE or + sym is zbar.Symbol.PARTIAL) + + self.assertTrue(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.assertTrue(self.dcode.color is zbar.BAR) + self.assertEqual(self.dcode.direction, 1) + + # FIXME test exception during callback + +class TestImage(ut.TestCase): + def setUp(self): + self.image = zbar.Image(123, 456, 'Y800') + + def tearDown(self): + del(self.image) + + def test_type(self): + self.assertTrue(isinstance(self.image, zbar.Image)) + self.assertTrue(callable(self.image.convert)) + + 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.assertTrue(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): + self.image.format = fmt + self.assertRaises(ValueError, set_format, 10) + self.assertEqual(self.image.format, 'Y800') + self.image.format = 'gOOb' + self.assertEqual(self.image.format, 'gOOb') + self.assertRaises(ValueError, set_format, 'yomama') + self.assertEqual(self.image.format, 'gOOb') + self.assertRaises(ValueError, set_format, 'JPG') + self.assertEqual(self.image.format, 'gOOb') + + def test_size(self): + def set_size(sz): + self.image.size = sz + self.assertRaises(ValueError, set_size, (1,)) + self.assertRaises(ValueError, set_size, 1) + self.image.size = (12, 6) + self.assertRaises(ValueError, set_size, (1, 2, 3)) + self.assertEqual(self.image.size, (12, 6)) + self.assertRaises(ValueError, set_size, "foo") + self.assertEqual(self.image.size, (12, 6)) + self.assertEqual(self.image.width, 12) + self.assertEqual(self.image.height, 6) + self.image.width = 81 + self.assertEqual(self.image.size, (81, 6)) + self.image.height = 64 + self.assertEqual(self.image.size, (81, 64)) + 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() + + def tearDown(self): + del(self.scn) + + def test_type(self): + self.assertTrue(isinstance(self.scn, zbar.ImageScanner)) + self.assertTrue(callable(self.scn.set_config)) + self.assertTrue(callable(self.scn.parse_config)) + self.assertTrue(callable(self.scn.enable_cache)) + self.assertTrue(callable(self.scn.scan)) + + def test_set_config(self): + self.scn.set_config() + self.assertRaises(ValueError, self.scn.set_config, -1) + self.assertRaises(TypeError, self.scn.set_config, "yomama") + self.scn.set_config() + + def test_parse_config(self): + self.scn.parse_config("disable") + self.assertRaises(ValueError, self.scn.set_config, -1) + self.scn.set_config() + +class TestImageScan(ut.TestCase): + def setUp(self): + self.scn = zbar.ImageScanner() + self.image = zbar.Image(size[0], size[1], 'Y800', data) + + def tearDown(self): + del(self.image) + del(self.scn) + + def test_scan(self): + n = self.scn.scan(self.image) + self.assertEqual(n, 1) + + syms = self.image.symbols + self.assertTrue(isinstance(syms, zbar.SymbolSet)) + self.assertEqual(len(syms), 1) + + i = iter(self.image) + j = iter(syms) + self.assertTrue(isinstance(i, zbar.SymbolIter)) + self.assertTrue(isinstance(j, zbar.SymbolIter)) + symi = next(i) + symj = next(j) + self.assertRaises(StopIteration, i.__next__) + self.assertRaises(StopIteration, j.__next__) + + # this is the only way to obtain a Symbol, + # so test Symbol here + for sym in (symi, symj): + self.assertTrue(isinstance(sym, zbar.Symbol)) + self.assertTrue(sym.type is zbar.Symbol.EAN13) + self.assertTrue(sym.type is sym.EAN13) + self.assertEqual(str(sym.type), 'EAN13') + + cfgs = sym.configs + self.assertTrue(isinstance(cfgs, set)) + for cfg in cfgs: + self.assertTrue(isinstance(cfg, zbar.EnumItem)) + self.assertEqual(cfgs, + set((zbar.Config.ENABLE, zbar.Config.EMIT_CHECK))) + + mods = sym.modifiers + self.assertTrue(isinstance(mods, set)) + for mod in mods: + self.assertTrue(isinstance(mod, zbar.EnumItem)) + self.assertEqual(mods, set()) + + self.assertTrue(sym.quality > 0) + self.assertEqual(sym.count, 0) + + # FIXME put a nice QR S-A in here + comps = sym.components + self.assertTrue(isinstance(comps, zbar.SymbolSet)) + self.assertEqual(len(comps), 0) + self.assertTrue(not comps) + self.assertTrue(tuple(comps) is ()) + + data = sym.data + self.assertEqual(data, '9876543210128') + + loc = sym.location + self.assertTrue(len(loc) >= 4) # FIXME + self.assertTrue(isinstance(loc, tuple)) + for pt in loc: + self.assertTrue(isinstance(pt, tuple)) + self.assertEqual(len(pt), 2) + # FIXME test values (API currently in flux) + + self.assertTrue(sym.orientation is zbar.Orient.UP) + self.assertTrue(data is sym.data) + self.assertTrue(loc is sym.location) + + def set_symbols(syms): + self.image.symbols = syms + self.assertRaises(TypeError, set_symbols, ()) + + 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() + +class TestProcessor(ut.TestCase): + def setUp(self): + self.proc = zbar.Processor() + + def tearDown(self): + del(self.proc) + + def test_type(self): + self.assertTrue(isinstance(self.proc, zbar.Processor)) + self.assertTrue(callable(self.proc.init)) + self.assertTrue(callable(self.proc.set_config)) + self.assertTrue(callable(self.proc.parse_config)) + self.assertTrue(callable(self.proc.set_data_handler)) + self.assertTrue(callable(self.proc.user_wait)) + self.assertTrue(callable(self.proc.process_one)) + self.assertTrue(callable(self.proc.process_image)) + + def test_set_config(self): + self.proc.set_config() + self.assertRaises(ValueError, self.proc.set_config, -1) + self.assertRaises(TypeError, self.proc.set_config, "yomama") + self.proc.set_config() + + def test_parse_config(self): + self.proc.parse_config("disable") + self.assertRaises(ValueError, self.proc.set_config, -1) + self.proc.set_config() + + def test_request_size(self): + def set_size(sz): + self.proc.request_size = sz + self.assertRaises(ValueError, set_size, (1,)) + self.assertRaises(ValueError, set_size, 1) + self.proc.request_size = (12, 6) + self.assertRaises(ValueError, set_size, (1, 2, 3)) + self.assertRaises(ValueError, set_size, "foo") + + def test_processing(self): + self.proc.init(VIDEO_DEVICE) + self.assertTrue(self.proc.visible is False) + self.proc.visible = 1 + self.assertTrue(self.proc.visible is True) + self.assertEqual(self.proc.user_wait(1.1), 0) + + self.image = zbar.Image(size[0], size[1], 'Y800', data) + + count = [ 0 ] + def data_handler(proc, image, closure): + self.assertTrue(proc is self.proc) + self.assertTrue(image is self.image) + self.assertEqual(count[0], 0) + count[0] += 1 + + symiter = iter(image) + self.assertTrue(isinstance(symiter, zbar.SymbolIter)) + + syms = tuple(image) + self.assertEqual(len(syms), 1) + for sym in syms: + self.assertTrue(isinstance(sym, zbar.Symbol)) + self.assertTrue(sym.type is zbar.Symbol.EAN13) + self.assertEqual(sym.data, '9876543210128') + self.assertTrue(sym.quality > 0) + self.assertTrue(sym.orientation is zbar.Orient.UP) + closure[0] += 1 + + explicit_closure = [ 0 ] + self.proc.set_data_handler(data_handler, explicit_closure) + + rc = self.proc.process_image(self.image) + self.assertEqual(rc, 0) + self.assertEqual(len(self.image.symbols), 1) + del(self.image.symbols) + self.assertEqual(len(self.image.symbols), 0) + + self.assertEqual(self.proc.user_wait(.9), 0) + + self.assertEqual(explicit_closure, [ 1 ]) + self.proc.set_data_handler() + +if __name__ == '__main__': + ut.main() diff --git a/python/zbarmodule.c b/python/zbarmodule.c new file mode 100644 index 0000000..e8e68ef --- /dev/null +++ b/python/zbarmodule.c @@ -0,0 +1,340 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "zbarmodule.h" + +typedef struct enumdef { + const char *strval; + int intval; +} enumdef; + +static char *exc_names[] = { + "zbar.Exception", NULL, + "zbar.InternalError", "zbar.UnsupportedError", + "zbar.InvalidRequestError", "zbar.SystemError", + "zbar.LockingError", "zbar.BusyError", + "zbar.X11DisplayError", "zbar.X11ProtocolError", + "zbar.WindowClosed", "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 }, + { "CODABAR", ZBAR_CODABAR }, + { "CODE39", ZBAR_CODE39 }, + { "PDF417", ZBAR_PDF417 }, + { "QRCODE", ZBAR_QRCODE }, + { "SQCODE", ZBAR_SQCODE }, + { "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 }, + { "BINARY", ZBAR_CFG_BINARY }, + { "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, + } }; + +#if PY_MAJOR_VERSION < 3 +struct module_state zbar_state; +#endif + +int object_to_bool(PyObject *obj, int *val) +{ + int tmp = PyObject_IsTrue(obj); + if (tmp < 0) + return (0); + *val = tmp; + return (1); +} + +int parse_dimensions(PyObject *seq, int *dims, int n) +{ + if (!PySequence_Check(seq) || PySequence_Size(seq) != n) + return (-1); + + int i; + for (i = 0; i < n; i++, dims++) { + PyObject *dim = PySequence_GetItem(seq, i); + if (!dim) + return (-1); +#if PY_MAJOR_VERSION >= 3 + *dims = PyLong_AsSsize_t(dim); +#else + *dims = PyInt_AsSsize_t(dim); +#endif + Py_DECREF(dim); + if (*dims == -1 && PyErr_Occurred()) + return (-1); + } + return (0); +} + +static PyObject *version(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return (NULL); + + unsigned int major, minor, patch; + zbar_version(&major, &minor, &patch); + + return (Py_BuildValue("III", major, minor, patch)); +} + +static PyObject *set_verbosity(PyObject *self, PyObject *args) +{ + int verbosity; + if (!PyArg_ParseTuple(args, "i", &verbosity)) + return (NULL); + + zbar_set_verbosity(verbosity); + + Py_INCREF(Py_None); + return (Py_None); +} + +static PyObject *increase_verbosity(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return (NULL); + + zbar_increase_verbosity(); + + Py_INCREF(Py_None); + return (Py_None); +} + +static PyMethodDef zbar_functions[] = { + { "version", version, METH_VARARGS, NULL }, + { "set_verbosity", set_verbosity, METH_VARARGS, NULL }, + { "increase_verbosity", increase_verbosity, METH_VARARGS, NULL }, + { + NULL, + }, +}; + +#if PY_MAJOR_VERSION >= 3 + +static int zbar_traverse(PyObject *m, visitproc visit, void *arg) +{ + Py_VISIT(GETSTATE(m)->zbar_exc[0]); + return 0; +} + +static int zbar_clear(PyObject *m) +{ + Py_CLEAR(GETSTATE(m)->zbar_exc[0]); + return 0; +} + +struct PyModuleDef zbar_moduledef = { PyModuleDef_HEAD_INIT, + "zbar", + NULL, + sizeof(struct module_state), + zbar_functions, + NULL, + zbar_traverse, + zbar_clear, + NULL }; + +#define INITERROR return NULL + +PyMODINIT_FUNC PyInit_zbar(void) + +#else +#define INITERROR return + +PyMODINIT_FUNC initzbar(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + /* initialize types */ + zbarEnumItem_Type.tp_base = &PyLong_Type; +#else + zbarEnumItem_Type.tp_base = &PyInt_Type; + zbarException_Type.tp_base = (PyTypeObject *)PyExc_Exception; + + if (PyType_Ready(&zbarException_Type) < 0) + INITERROR; +#endif + + if (PyType_Ready(&zbarEnumItem_Type) < 0 || + PyType_Ready(&zbarEnum_Type) < 0 || PyType_Ready(&zbarImage_Type) < 0 || + PyType_Ready(&zbarSymbol_Type) < 0 || + PyType_Ready(&zbarSymbolSet_Type) < 0 || + PyType_Ready(&zbarSymbolIter_Type) < 0 || + PyType_Ready(&zbarProcessor_Type) < 0 || + PyType_Ready(&zbarImageScanner_Type) < 0 || + PyType_Ready(&zbarDecoder_Type) < 0 || + PyType_Ready(&zbarScanner_Type) < 0) + INITERROR; + + /* initialize module */ +#if PY_MAJOR_VERSION >= 3 + PyObject *mod = PyModule_Create(&zbar_moduledef); +#else + PyObject *mod = Py_InitModule("zbar", zbar_functions); +#endif + if (!mod) + INITERROR; + +#if PY_MAJOR_VERSION >= 3 + if (PyState_AddModule(mod, &zbar_moduledef)) { + Py_DECREF(mod); + INITERROR; + } +#endif + + struct module_state *st = GETSTATE(mod); + + /* initialize constant containers */ + st->config_enum = zbarEnum_New(); + st->modifier_enum = zbarEnum_New(); + st->symbol_enum = PyDict_New(); + st->orient_enum = zbarEnum_New(); + if (!st->config_enum || !st->modifier_enum || !st->symbol_enum || + !st->orient_enum) { + Py_DECREF(mod); + INITERROR; + } + + /* internally created/read-only type overrides */ + zbarEnum_Type.tp_new = NULL; + zbarEnum_Type.tp_setattr = NULL; + zbarEnum_Type.tp_setattro = NULL; + + /* zbar internal exception objects */ +#if PY_MAJOR_VERSION >= 3 + st->zbar_exc[0] = PyErr_NewException("zbar.Exception", NULL, NULL); +#else + st->zbar_exc[0] = (PyObject *)&zbarException_Type; +#endif + if (st->zbar_exc[0] == NULL) { + Py_DECREF(mod); + INITERROR; + } + + st->zbar_exc[ZBAR_ERR_NOMEM] = NULL; + zbar_error_t ei; + for (ei = ZBAR_ERR_INTERNAL; ei < ZBAR_ERR_NUM; ei++) { + st->zbar_exc[ei] = + PyErr_NewException(exc_names[ei], st->zbar_exc[0], NULL); + if (!st->zbar_exc[ei]) { + Py_DECREF(mod); + INITERROR; + } + } + + /* add types to module */ + PyModule_AddObject(mod, "EnumItem", (PyObject *)&zbarEnumItem_Type); + PyModule_AddObject(mod, "Image", (PyObject *)&zbarImage_Type); + PyModule_AddObject(mod, "Config", (PyObject *)st->config_enum); + PyModule_AddObject(mod, "Modifier", (PyObject *)st->modifier_enum); + PyModule_AddObject(mod, "Orient", (PyObject *)st->orient_enum); + PyModule_AddObject(mod, "Symbol", (PyObject *)&zbarSymbol_Type); + PyModule_AddObject(mod, "SymbolSet", (PyObject *)&zbarSymbolSet_Type); + PyModule_AddObject(mod, "SymbolIter", (PyObject *)&zbarSymbolIter_Type); + PyModule_AddObject(mod, "Processor", (PyObject *)&zbarProcessor_Type); + PyModule_AddObject(mod, "ImageScanner", (PyObject *)&zbarImageScanner_Type); + PyModule_AddObject(mod, "Decoder", (PyObject *)&zbarDecoder_Type); + PyModule_AddObject(mod, "Scanner", (PyObject *)&zbarScanner_Type); + + for (ei = 0; ei < ZBAR_ERR_NUM; ei++) + if (st->zbar_exc[ei]) + PyModule_AddObject(mod, exc_names[ei] + 5, st->zbar_exc[ei]); + + /* add constants */ + PyObject *dict = PyModule_GetDict(mod); + st->color_enum[ZBAR_SPACE] = + zbarEnumItem_New(dict, NULL, ZBAR_SPACE, "SPACE"); + st->color_enum[ZBAR_BAR] = zbarEnumItem_New(dict, NULL, ZBAR_BAR, "BAR"); + + const enumdef *item; + for (item = config_defs; item->strval; item++) + zbarEnum_Add(st->config_enum, item->intval, item->strval); + for (item = modifier_defs; item->strval; item++) + zbarEnum_Add(st->modifier_enum, item->intval, item->strval); + for (item = orient_defs; item->strval; item++) + zbarEnum_Add(st->orient_enum, item->intval, item->strval); + + PyObject *tp_dict = zbarSymbol_Type.tp_dict; + for (item = symbol_defs; item->strval; item++) + zbarEnumItem_New(tp_dict, st->symbol_enum, item->intval, item->strval); + st->symbol_NONE = zbarSymbol_LookupEnum(ZBAR_NONE); + +#if PY_MAJOR_VERSION >= 3 + return mod; +#endif +} + +PyObject *zbarErr_Set(PyObject *self) +{ + const void *zobj = ((zbarProcessor *)self)->zproc; + zbar_error_t err = _zbar_get_error_code(zobj); + + struct module_state *st = GETMODSTATE(); + + if (err == ZBAR_ERR_NOMEM) + PyErr_NoMemory(); + else if (err < ZBAR_ERR_NUM) { + PyObject *type = st->zbar_exc[err]; + assert(type); + PyErr_SetObject(type, self); + } else + PyErr_SetObject(st->zbar_exc[0], self); + return (NULL); +} diff --git a/python/zbarmodule.h b/python/zbarmodule.h new file mode 100644 index 0000000..d61c381 --- /dev/null +++ b/python/zbarmodule.h @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <Python.h> +#include <stddef.h> +#include <zbar.h> + +#ifndef _ZBARMODULE_H_ +#define _ZBARMODULE_H_ + +#if PY_MAJOR_VERSION < 3 +typedef struct { + PyBaseExceptionObject base; + PyObject *obj; +} zbarException; + +extern PyTypeObject zbarException_Type; +#endif + +extern struct PyModuleDef zbar_moduledef; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state *)PyModule_GetState(m)) +#define GETMODSTATE() (GETSTATE(PyState_FindModule(&zbar_moduledef))) +#else +extern struct module_state zbar_state; +#define GETSTATE(m) (&zbar_state) +#define GETMODSTATE() (&zbar_state) +#endif + +extern PyObject *zbarErr_Set(PyObject *self); + +typedef struct { +#if PY_MAJOR_VERSION >= 3 + PyLongObject val; /* parent type is the long type */ +#else + PyIntObject val; /* integer value is super type */ +#endif + PyObject *name; /* associated string name */ +} zbarEnumItem; + +extern PyTypeObject zbarEnumItem_Type; + +extern zbarEnumItem *zbarEnumItem_New(PyObject *byname, PyObject *byvalue, + int val, const char *name); + +typedef struct { + PyObject_HEAD PyObject *byname, + *byvalue; /* zbarEnumItem content dictionaries */ +} zbarEnum; + +extern PyTypeObject zbarEnum_Type; + +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 zbar_image_t *zimg; + PyObject *data; +} zbarImage; + +extern PyTypeObject zbarImage_Type; + +extern zbarImage *zbarImage_FromImage(zbar_image_t *zimg); +extern int zbarImage_validate(zbarImage *image); + +typedef struct { + PyObject_HEAD const zbar_symbol_set_t *zsyms; +} zbarSymbolSet; + +extern PyTypeObject zbarSymbolSet_Type; + +extern zbarSymbolSet * +zbarSymbolSet_FromSymbolSet(const zbar_symbol_set_t *zsyms); + +#define zbarSymbolSet_Check(obj) PyObject_TypeCheck(obj, &zbarSymbolSet_Type) + +typedef struct { + PyObject_HEAD const zbar_symbol_t *zsym; + PyObject *data; + PyObject *loc; +} zbarSymbol; + +extern PyTypeObject zbarSymbol_Type; + +extern zbarSymbol *zbarSymbol_FromSymbol(const zbar_symbol_t *zsym); +extern zbarEnumItem *zbarSymbol_LookupEnum(zbar_symbol_type_t type); + +typedef struct { + PyObject_HEAD const zbar_symbol_t *zsym; + zbarSymbolSet *syms; +} zbarSymbolIter; + +extern PyTypeObject zbarSymbolIter_Type; + +extern zbarSymbolIter *zbarSymbolIter_FromSymbolSet(zbarSymbolSet *syms); + +typedef struct { + PyObject_HEAD zbar_processor_t *zproc; + PyObject *handler; + PyObject *closure; +} zbarProcessor; + +extern PyTypeObject zbarProcessor_Type; + +#define zbarProcessor_Check(obj) PyObject_TypeCheck(obj, &zbarProcessor_Type) + +typedef struct { + PyObject_HEAD zbar_image_scanner_t *zscn; +} zbarImageScanner; + +extern PyTypeObject zbarImageScanner_Type; + +typedef struct { + PyObject_HEAD zbar_decoder_t *zdcode; + PyObject *handler; + PyObject *args; +} zbarDecoder; + +extern PyTypeObject zbarDecoder_Type; + +typedef struct { + PyObject_HEAD zbar_scanner_t *zscn; + zbarDecoder *decoder; +} zbarScanner; + +extern PyTypeObject zbarScanner_Type; + +extern int object_to_bool(PyObject *obj, int *val); +extern int parse_dimensions(PyObject *seq, int *dims, int n); + +struct module_state { + 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; +}; + +#endif diff --git a/qt/Makefile.am.inc b/qt/Makefile.am.inc new file mode 100644 index 0000000..fba4fdf --- /dev/null +++ b/qt/Makefile.am.inc @@ -0,0 +1,19 @@ +lib_LTLIBRARIES += qt/libzbarqt.la +qt_libzbarqt_la_CPPFLAGS = -Iqt $(QT_CFLAGS) $(AM_CPPFLAGS) +qt_libzbarqt_la_LDFLAGS = -version-info $(ZQT_LIB_VERSION) $(AM_LDFLAGS) $(LIBQT_EXTRA_LDFLAGS) +qt_libzbarqt_la_LIBADD = $(QT_LIBS) zbar/libzbar.la $(AM_LIBADD) + +qt_libzbarqt_la_SOURCES = qt/QZBar.cpp qt/QZBarThread.h qt/QZBarThread.cpp +nodist_qt_libzbarqt_la_SOURCES = qt/moc_QZBar.cpp qt/moc_QZBarThread.cpp +BUILT_SOURCES += $(nodist_qt_libzbarqt_la_SOURCES) +DISTCLEANFILES += $(nodist_qt_libzbarqt_la_SOURCES) + +clean-local: + -rm -vf qt/moc_*.cpp + +qt/moc_%.cpp: qt/%.h + $(MOC) $(qt_libzbarqt_la_CPPFLAGS) $< -o $@ + +qt/moc_%.cpp: include/zbar/%.h + $(mkdir_p) qt + $(MOC) $(qt_libzbarqt_la_CPPFLAGS) $< -o $@ diff --git a/qt/QZBar.cpp b/qt/QZBar.cpp new file mode 100644 index 0000000..14bdc56 --- /dev/null +++ b/qt/QZBar.cpp @@ -0,0 +1,364 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include <QX11Info> +#include <qevent.h> +#include <qurl.h> +#include <zbar/QZBar.h> +#include "QZBarThread.h" + +using namespace zbar; + +QZBar::QZBar(QWidget *parent, int verbosity) + : QWidget(parent), thread(NULL), _videoDevice(), _videoEnabled(false), + _attached(false) +{ + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_PaintOnScreen); +#if QT_VERSION >= 0x040400 + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_DontCreateNativeAncestors); +#endif + + QSizePolicy sizing(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizing.setHeightForWidth(true); + setSizePolicy(sizing); + + thread = new QZBarThread(verbosity); + if (testAttribute(Qt::WA_WState_Created)) { +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + _attached = 1; + } + connect(thread, SIGNAL(videoOpened(bool)), this, SIGNAL(videoOpened(bool))); + connect(this, SIGNAL(videoOpened(bool)), this, SLOT(sizeChange())); + connect(thread, SIGNAL(update()), this, SLOT(update())); + connect(thread, SIGNAL(decoded(int, const QString &)), this, + SIGNAL(decoded(int, const QString &))); + connect(thread, SIGNAL(decodedText(const QString &)), this, + SIGNAL(decodedText(const QString &))); + thread->start(); +} + +QZBar::~QZBar() +{ + if (thread) { + thread->pushEvent(new QEvent((QEvent::Type)QZBarThread::Exit)); + thread->wait(); + delete thread; + thread = NULL; + } +} + +QPaintEngine *QZBar::paintEngine() const +{ + return (NULL); +} + +QString QZBar::videoDevice() const +{ + return (_videoDevice); +} + +void QZBar::setVideoDevice(const QString &videoDevice) +{ + if (!thread) + return; + if (_videoDevice != videoDevice) { + _videoDevice = videoDevice; + _videoEnabled = _attached && !videoDevice.isEmpty(); + if (_attached) + thread->pushEvent(new QZBarThread::VideoDeviceEvent(videoDevice)); + } +} + +int QZBar::get_controls(int index, char **name, char **group, + enum ControlType *type, int *min, int *max, int *def, + int *step) +{ + if (!thread) + return 0; + + return thread->get_controls(index, name, group, type, min, max, def, step); +} + +void QZBar::request_size(unsigned width, unsigned height, bool trigger) +{ + if (!thread) + return; + + thread->request_size(width, height); + if (trigger) + thread->pushEvent(new QEvent((QEvent::Type)QZBarThread::ReOpen)); +} + +int QZBar::get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps) +{ + if (!thread) + return 0; + + return thread->get_resolution(index, width, height, max_fps); +} + +unsigned QZBar::videoWidth() +{ + if (!thread) + return 0; + + return thread->reqWidth; +} + +unsigned QZBar::videoHeight() +{ + if (!thread) + return 0; + + return thread->reqHeight; +} + +QVector<QPair<int, QString> > QZBar::get_menu(int index) +{ + if (!thread) { + QVector<QPair<int, QString> > empty; + return empty; + } + + return thread->get_menu(index); +} + +int QZBar::set_control(char *name, bool value) +{ + if (!thread) + return 0; + + return thread->set_control(name, value); +} + +int QZBar::set_control(char *name, int value) +{ + if (!thread) + return 0; + + return thread->set_control(name, value); +} + +int QZBar::get_control(char *name, bool *value) +{ + if (!thread) + return 0; + + return thread->get_control(name, value); +} + +int QZBar::get_control(char *name, int *value) +{ + if (!thread) + return 0; + + return thread->get_control(name, value); +} + +int QZBar::set_config(std::string cfgstr) +{ + if (!thread) + return 0; + + return thread->set_config(cfgstr); +} + +int QZBar::set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) +{ + if (!thread) + return 0; + + return thread->set_config(symbology, config, value); +} + +int QZBar::get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value) +{ + if (!thread) + return 0; + + return thread->get_config(symbology, config, value); +} + +int QZBar::request_dbus(bool enabled) +{ + if (!thread) + return 0; + + return thread->request_dbus(enabled); +} + +bool QZBar::isVideoEnabled() const +{ + return (_videoEnabled); +} + +void QZBar::setVideoEnabled(bool videoEnabled) +{ + if (!thread) + return; + if (_videoEnabled != videoEnabled) { + _videoEnabled = videoEnabled; + thread->pushEvent(new QZBarThread::VideoEnabledEvent(videoEnabled)); + } +} + +bool QZBar::isVideoOpened() const +{ + if (!thread) + return (false); + QMutexLocker locker(&thread->mutex); + return (thread->_videoOpened); +} + +void QZBar::scanImage(const QImage &image) +{ + if (!thread) + return; + thread->pushEvent(new QZBarThread::ScanImageEvent(image)); +} + +void QZBar::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void QZBar::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasImage()) { + QImage image = qvariant_cast<QImage>(event->mimeData()->imageData()); + scanImage(image); + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + // FIXME TBD load URIs and queue for processing +#if 0 + std::cerr << "drop: " + << event->mimeData()->formats().join(", ").toStdString() + << std::endl; + QList<QUrl> urls = event->mimeData()->urls(); + for(int i = 0; i < urls.size(); ++i) + std::cerr << "[" << i << "] " + << urls.at(i).toString().toStdString() + << std::endl; +#endif + } +} + +QSize QZBar::sizeHint() const +{ + if (!thread) + return (QSize(640, 480)); + QMutexLocker locker(&thread->mutex); + return (QSize(thread->reqWidth, thread->reqHeight)); +} + +int QZBar::heightForWidth(int width) const +{ + if (thread) { + QMutexLocker locker(&thread->mutex); + int base_width = thread->reqWidth; + int base_height = thread->reqHeight; + if (base_width > 0 && base_height > 0) + return (base_height * width / base_width); + } + return (width * 3 / 4); +} + +void QZBar::paintEvent(QPaintEvent *event) +{ + try { + if (thread) + thread->window.redraw(); + } catch (Exception &) { + // sometimes Qt attempts to paint the widget before it's parented(?) + // just ignore this (can't throw from event anyway) + } +} + +void QZBar::resizeEvent(QResizeEvent *event) +{ + QSize size = event->size(); + try { + if (thread) + thread->window.resize(size.rwidth(), size.rheight()); + } catch (Exception &) { /* ignore */ + } +} + +void QZBar::changeEvent(QEvent *event) +{ + try { + QMutexLocker locker(&thread->mutex); + if (event->type() == QEvent::ParentChange) +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + + } catch (Exception &) { /* ignore (FIXME do something w/error) */ + } +} + +void QZBar::attach() +{ + if (_attached) + return; + + try { +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + thread->window.resize(width(), height()); + _attached = 1; + + _videoEnabled = !_videoDevice.isEmpty(); + if (_videoEnabled) + thread->pushEvent(new QZBarThread::VideoDeviceEvent(_videoDevice)); + } catch (Exception &) { /* ignore (FIXME do something w/error) */ + } +} + +void QZBar::showEvent(QShowEvent *event) +{ + if (thread && !_attached) + attach(); +} + +void QZBar::sizeChange() +{ + update(); + updateGeometry(); +} diff --git a/qt/QZBarThread.cpp b/qt/QZBarThread.cpp new file mode 100644 index 0000000..7675fa3 --- /dev/null +++ b/qt/QZBarThread.cpp @@ -0,0 +1,358 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include "QZBarThread.h" +#include <iostream> + +using namespace zbar; + +static const QString textFormat("%1:%2"); + +QZBarThread::QZBarThread(int verbosity) + : _videoOpened(false), reqWidth(DEFAULT_WIDTH), reqHeight(DEFAULT_HEIGHT), + video(NULL), image(NULL), running(true), videoRunning(false), + videoEnabled(false) +{ + zbar_set_verbosity(verbosity); + scanner.set_handler(*this); +} + +void QZBarThread::image_callback(Image &image) +{ + for (Image::SymbolIterator sym = image.symbol_begin(); + sym != image.symbol_end(); ++sym) + if (!sym->get_count()) { + QString data = QString::fromStdString(sym->get_data()); + emit decoded(sym->get_type(), data); + + emit decodedText(textFormat.arg( + QString::fromStdString(sym->get_type_name()), data)); + } +} + +void QZBarThread::processImage(Image &image) +{ + { + scanner.recycle_image(image); + Image tmp = image.convert(*(long *)"Y800"); + scanner.scan(tmp); + image.set_symbols(tmp.get_symbols()); + } + window.draw(image); + if (this->image && this->image != &image) { + delete this->image; + this->image = NULL; + } + emit update(); +} + +void QZBarThread::enableVideo(bool enable) +{ + if (!video) { + videoRunning = videoEnabled = false; + return; + } + try { + scanner.enable_cache(enable); + video->enable(enable); + videoRunning = enable; + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + } + if (!enable) { + // release video image and revert to logo + clear(); + emit update(); + } +} + +void QZBarThread::openVideo(const QString &device) +{ + if (videoRunning) + enableVideo(false); + + { + QMutexLocker locker(&mutex); + videoEnabled = _videoOpened = false; + } + + // ensure old video doesn't have image ref + // (FIXME handle video destroyed w/images outstanding) + clear(); + emit update(); + + if (video) { + delete video; + video = NULL; + emit videoOpened(false); + } + + if (device.isEmpty()) + return; + + try { + std::string devstr = device.toStdString(); + video = new Video(devstr); + + if (reqWidth != DEFAULT_WIDTH || reqHeight != DEFAULT_HEIGHT) + video->request_size(reqWidth, reqHeight); + + negotiate_format(*video, window); + { + QMutexLocker locker(&mutex); + videoEnabled = _videoOpened = true; + reqWidth = video->get_width(); + reqHeight = video->get_height(); + } + currentDevice = device; + + emit videoOpened(true); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + emit videoOpened(false); + } +} + +void QZBarThread::videoDeviceEvent(VideoDeviceEvent *e) +{ + openVideo(e->device); +} + +void QZBarThread::videoEnabledEvent(VideoEnabledEvent *e) +{ + if (videoRunning && !e->enabled) + enableVideo(false); + videoEnabled = e->enabled; +} + +void QZBarThread::scanImageEvent(ScanImageEvent *e) +{ + if (videoRunning) + enableVideo(false); + + try { + image = new QZBarImage(e->image); + processImage(*image); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + clear(); + } +} + +bool QZBarThread::event(QEvent *e) +{ + switch ((EventType)e->type()) { + case VideoDevice: + videoDeviceEvent((VideoDeviceEvent *)e); + break; + case VideoEnabled: + videoEnabledEvent((VideoEnabledEvent *)e); + break; + case ScanImage: + scanImageEvent((ScanImageEvent *)e); + break; + case Exit: + if (videoRunning) + enableVideo(false); + running = false; + break; + case ReOpen: + openVideo(currentDevice); + break; + default: + return (false); + } + return (true); +} + +void QZBarThread::run() +{ + QEvent *e = NULL; + while (running) { + if (!videoEnabled) { + QMutexLocker locker(&mutex); + while (queue.isEmpty()) + newEvent.wait(&mutex); + e = queue.takeFirst(); + } else { + // release reference to any previous QImage + clear(); + enableVideo(true); + + while (videoRunning && !e) { + try { + Image image = video->next_image(); + processImage(image); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + enableVideo(false); + openVideo(""); + } + QMutexLocker locker(&mutex); + if (!queue.isEmpty()) + e = queue.takeFirst(); + } + + if (videoRunning) + enableVideo(false); + } + if (e) { + event(e); + delete e; + e = NULL; + } + } + clear(); + openVideo(""); +} + +QVector<QPair<int, QString> > QZBarThread::get_menu(int index) +{ + QVector<QPair<int, QString> > vector; + struct video_controls_s *ctrl; + + if (!video) + return vector; + + ctrl = video->get_controls(index); + if (!ctrl) + return vector; + + for (unsigned int i = 0; i < ctrl->menu_size; i++) + vector.append(qMakePair((int)ctrl->menu[i].value, + QString::fromUtf8(ctrl->menu[i].name))); + + return vector; +} + +int QZBarThread::get_controls(int index, char **name, char **group, + enum QZBar::ControlType *type, int *min, int *max, + int *def, int *step) +{ + struct video_controls_s *ctrl; + + if (!video) + return 0; + + ctrl = video->get_controls(index); + if (!ctrl) + return 0; + + if (name) + *name = ctrl->name; + if (group) + *group = ctrl->group; + if (min) + *min = ctrl->min; + if (max) + *max = ctrl->max; + if (def) + *def = ctrl->def; + if (step) + *step = ctrl->step; + + if (type) { + switch (ctrl->type) { + case VIDEO_CNTL_INTEGER: + *type = QZBar::Integer; + break; + case VIDEO_CNTL_MENU: + *type = QZBar::Menu; + break; + case VIDEO_CNTL_BUTTON: + *type = QZBar::Button; + break; + case VIDEO_CNTL_INTEGER64: + *type = QZBar::Integer64; + break; + case VIDEO_CNTL_STRING: + *type = QZBar::String; + break; + case VIDEO_CNTL_BOOLEAN: + *type = QZBar::Boolean; + break; + default: + *type = QZBar::Unknown; + break; + } + } + + return 1; +} + +int QZBarThread::set_control(char *name, bool value) +{ + if (!video) + return 0; + + return video->set_control(name, value); +} + +int QZBarThread::set_control(char *name, int value) +{ + if (!video) + return 0; + + return video->set_control(name, value); +} + +int QZBarThread::get_control(char *name, bool *value) +{ + if (!video) + return 0; + + return video->get_control(name, value); +} + +int QZBarThread::get_control(char *name, int *value) +{ + if (!video) + return 0; + + return video->get_control(name, value); +} + +void QZBarThread::request_size(unsigned width, unsigned height) +{ + reqWidth = width; + reqHeight = height; +} + +int QZBarThread::get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps) +{ + struct video_resolution_s *res; + + if (!video) + return 0; + + res = video->get_resolution(index); + if (!res) + return 0; + + width = res->width; + height = res->height; + max_fps = res->max_fps; + + return 1; +} diff --git a/qt/QZBarThread.h b/qt/QZBarThread.h new file mode 100644 index 0000000..a54ecc8 --- /dev/null +++ b/qt/QZBarThread.h @@ -0,0 +1,192 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _QZBARTHREAD_H_ +#define _QZBARTHREAD_H_ + +#include <QEvent> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <zbar.h> +#include <zbar/QZBar.h> +#include <zbar/QZBarImage.h> + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +namespace zbar +{ +class QZBarThread : public QThread, public Image::Handler +{ + Q_OBJECT + +public: + enum EventType + { + VideoDevice = QEvent::User, + VideoEnabled, + ScanImage, + ReOpen, + Exit = QEvent::MaxUser + }; + + class VideoDeviceEvent : public QEvent + { + public: + VideoDeviceEvent(const QString &device) + : QEvent((QEvent::Type)VideoDevice), device(device) + { + } + const QString device; + }; + + class VideoEnabledEvent : public QEvent + { + public: + VideoEnabledEvent(bool enabled) + : QEvent((QEvent::Type)VideoEnabled), enabled(enabled) + { + } + bool enabled; + }; + + class ScanImageEvent : public QEvent + { + public: + ScanImageEvent(const QImage &image) + : QEvent((QEvent::Type)ScanImage), image(image) + { + } + const QImage image; + }; + + QMutex mutex; + QWaitCondition newEvent; + + // message queue for events passed from main gui thread to processor. + // (NB could(/should?) be QAbstractEventDispatcher except it doesn't + // work as documented!? ): + // protected by mutex + QList<QEvent *> queue; + + // shared state: + // written by processor thread just after opening video or + // scanning an image, read by main gui thread during size_request. + // protected by mutex + + bool _videoOpened; + unsigned reqWidth, reqHeight; + + // window is also shared: owned by main gui thread. + // processor thread only calls draw(), clear() and negotiate_format(). + // protected by its own internal lock + + Window window; + + QZBarThread(int verbosity = 0); + + int get_controls(int index, char **name = NULL, char **group = NULL, + enum QZBar::ControlType *type = NULL, int *min = NULL, + int *max = NULL, int *def = NULL, int *step = NULL); + QVector<QPair<int, QString> > get_menu(int index); + int set_control(char *name, bool value); + int set_control(char *name, int value); + int get_control(char *name, bool *value); + int get_control(char *name, int *value); + + int set_config(std::string cfgstr) + { + return scanner.set_config(cfgstr); + } + + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) + { + return scanner.set_config(symbology, config, value); + } + + int get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value) + { + return scanner.get_config(symbology, config, value); + } + + void request_size(unsigned width, unsigned height); + + int get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps); + + int request_dbus(bool enabled) + { + return scanner.request_dbus(enabled); + } + + void pushEvent(QEvent *e) + { + QMutexLocker locker(&mutex); + queue.append(e); + newEvent.wakeOne(); + } + +Q_SIGNALS: + void videoOpened(bool opened); + void update(); + void decoded(int type, const QString &data); + void decodedText(const QString &data); + +protected: + void run(); + + void openVideo(const QString &device); + void enableVideo(bool enable); + void processImage(Image &image); + + void clear() + { + window.clear(); + if (image) { + delete image; + image = NULL; + } + } + + virtual void image_callback(Image &image); + + virtual bool event(QEvent *e); + virtual void videoDeviceEvent(VideoDeviceEvent *event); + virtual void videoEnabledEvent(VideoEnabledEvent *event); + virtual void scanImageEvent(ScanImageEvent *event); + +private: + Video *video; + ImageScanner scanner; + QZBarImage *image; + QString currentDevice; + bool running; + bool videoRunning; + bool videoEnabled; +}; + +}; // namespace zbar + +#endif diff --git a/test/Makefile.am.inc b/test/Makefile.am.inc new file mode 100644 index 0000000..55b9814 --- /dev/null +++ b/test/Makefile.am.inc @@ -0,0 +1,162 @@ +check_PROGRAMS += test/test_decode +test_test_decode_SOURCES = test/test_decode.c test/pdf417_encode.h +test_test_decode_CFLAGS = -Wno-unused $(AM_CFLAGS) +test_test_decode_LDADD = zbar/libzbar.la $(AM_LDADD) + +TEST_IMAGE_SOURCES = test/test_images.c test/test_images.h + +check_PROGRAMS += test/test_convert +test_test_convert_SOURCES = test/test_convert.c $(TEST_IMAGE_SOURCES) +test_test_convert_LDADD = zbar/libzbar.la $(AM_LDADD) + +#check_PROGRAMS += test/test_window +#test_test_window_SOURCES = test/test_window.c $(TEST_IMAGE_SOURCES) +#test_test_window_CPPFLAGS = -I$(srcdir)/zbar $(AM_CPPFLAGS) +#test_test_window_LDADD = zbar/libzbar.la $(AM_LDADD) + +if HAVE_VIDEO +check_PROGRAMS += test/test_video +test_test_video_SOURCES = test/test_video.c $(TEST_IMAGE_SOURCES) +test_test_video_LDADD = zbar/libzbar.la $(AM_LDADD) +endif + +check_PROGRAMS += test/test_proc +test_test_proc_SOURCES = test/test_proc.c $(TEST_IMAGE_SOURCES) +test_test_proc_LDADD = zbar/libzbar.la $(AM_LDADD) + +check_PROGRAMS += test/test_cpp +test_test_cpp_SOURCES = test/test_cpp.cpp +test_test_cpp_LDADD = zbar/libzbar.la $(AM_LDADD) + +check_PROGRAMS += test/test_cpp_img +test_test_cpp_img_SOURCES = test/test_cpp_img.cpp $(TEST_IMAGE_SOURCES) +test_test_cpp_img_LDADD = zbar/libzbar.la $(AM_LDADD) + +if HAVE_JPEG +check_PROGRAMS += test/test_jpeg +test_test_jpeg_SOURCES = test/test_jpeg.c +test_test_jpeg_LDADD = zbar/libzbar.la $(AM_LDADD) +endif + +if HAVE_MAGICK +EXTRA_PROGRAMS += test/dbg_scan +test_dbg_scan_SOURCES = test/dbg_scan.cpp +test_dbg_scan_CPPFLAGS = $(MAGICK_CFLAGS) $(AM_CPPFLAGS) +test_dbg_scan_LDADD = $(MAGICK_LIBS) -lMagick++ zbar/libzbar.la $(AM_LDADD) +endif + +if HAVE_DBUS +check_PROGRAMS += test/test_dbus +test_test_dbus_SOURCES = test/test_dbus.c +test_test_dbus_LDFLAGS = $(DBUS_LIBS) +endif + +EXTRA_DIST += test/test_pygtk.py test/test_perl.pl test/test_gi.py test/test_python.py + +# automake bug in "monolithic mode"? +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 + + +# Images that work out of the box without needing to enable +# an specific symbology +NORMAL_IMAGES = codabar.png code-128.png code-39.png code-93.png \ + databar.png databar-exp.png ean-13.png ean-8.png i2-5.png \ + qr-code.png sqcode1-generated.png sqcode1-scanned.png + +EXAMPLES = @abs_top_builddir@/examples +ZBARIMG = @abs_top_builddir@/zbarimg/zbarimg --nodbus + +gen_checksum: all + for i in $(NORMAL_IMAGES); do $(ZBARIMG) $(EXAMPLES)/$$i 2>/dev/null|sha1sum|sed "s,-,zbarimg $$i,"; done >$(EXAMPLES)/sha1sum + $(ZBARIMG) -Sean2.enable $(EXAMPLES)/ean-2.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Sean2.enable ean-2.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) -Sean5.enable $(EXAMPLES)/ean-5.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Sean5.enable ean-5.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) -Sisbn10.enable $(EXAMPLES)/ean-13.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Sisbn10.enable ean-13.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) -Sisbn13.enable $(EXAMPLES)/ean-13.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Sisbn13.enable ean-13.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) -Supca.enable $(EXAMPLES)/code-upc-a.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Supca.enable code-upc-a.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) -Stest-inverted $(EXAMPLES)/qr-code-inverted.png 2>/dev/null|sha1sum|sed "s,-,zbarimg -Stest-inverted qr-code-inverted.png," >>$(EXAMPLES)/sha1sum + $(ZBARIMG) --raw -Sbinary $(EXAMPLES)/qr-code-binary.png 2>/dev/null|head -c -1|sha1sum|sed "s,-,zbarimg --raw -Sbinary qr-code-binary.png," >>$(EXAMPLES)/sha1sum + +test_progs: $(check_PROGRAMS) + @$(MAKE) $(check_PROGRAMS) + +# Require X11 to work +check-cpp: test/test_cpp_img + @abs_top_builddir@/test/test_cpp_img + +check-decoder: test/test_decode + @abs_top_builddir@/test/test_decode -q + +regress-decoder: test/test_decode + @abs_top_builddir@/test/test_decode -q -n 100000 + +check-images-py: zbarimg/zbarimg + @PYTHON@ @abs_top_srcdir@/test/barcodetest.py + +check-images: zbarimg/zbarimg + @abs_top_builddir@/test/test_examples.sh + +check-convert: test/test_convert + @abs_top_srcdir@/test/test_convert + @if [ "`sha1sum /tmp/base.I420.zimg |cut -d' ' -f 1`" != \ + "d697b0bb84617bef0f6413b3e5537ee38ba92312" ]; then \ + echo "convert FAILED"; else echo "convert PASSED."; fi + @rm /tmp/base.I420.zimg 2>/dev/null + +if HAVE_PYGTK2 +check-pygtk: pygtk/zbarpygtk.la + PYTHONPATH=@abs_top_srcdir@/pygtk/.libs/ \ + @PYTHON@ @abs_top_srcdir@/test/test_pygtk.py +else +check-pygtk: +endif + +if HAVE_PYTHON +check-python: python/zbar.la + PYTHONPATH=@abs_top_srcdir@/python/.libs/ \ + @PYTHON@ @abs_top_srcdir@/test/test_python.py \ + '@abs_top_srcdir@/examples/ean-13.png' '9789876543217' +else +check-python: +endif + +check-gi: gtk/ZBar-1.0.typelib + LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):@abs_top_srcdir@/gtk/.libs:@abs_top_srcdir@/zbar/.libs \ + GI_TYPELIB_PATH=@abs_top_srcdir@/gtk/ \ + @PYTHON@ @abs_top_srcdir@/test/test_gi.py + +# Require a camera device for it to work +check-video: test/test_video + if [ -d /dev/video0 ]; then @abs_top_srcdir@/test/test_video -q; fi + +check-jpeg: test/test_jpeg + @abs_top_srcdir@/test/test_jpeg -q + +if HAVE_DBUS +# Require a working D-Bus - may fail with containers +check-dbus: test/test_dbus + @abs_top_builddir@/test/check_dbus.sh +else +check-dbus: +endif + +if HAVE_JAVA_UNIT +check-java: zbar/libzbar.la + JAVA_HOME=${JAVA_HOME} $(MAKE) -C java check-java +else +check-java: +endif + +regress: regress-decoder + +check-local: check-images-py check-decoder check-images check-java \ + check-python regress + +other-tests: check-cpp check-convert check-video check-jpeg + +tests: check-local check-dbus other-tests + +.NOTPARALLEL: check-local regress tests + +PHONY += gen_checksum check-cpp check-decoder check-images check-dbus regress-decoder regress-images regress diff --git a/test/barcodetest.py b/test/barcodetest.py new file mode 100755 index 0000000..295832e --- /dev/null +++ b/test/barcodetest.py @@ -0,0 +1,413 @@ +#!/usr/bin/env python +# pylint: disable=C0103,C0114,C0115,C0116,C0209,R0912,R0915,W0511,W0603,W0613,W0707,W0212 + +from __future__ import print_function + +import re +import sys +import unittest as UT +import xml.etree.ElementTree as ET + +from errno import EISDIR, EINVAL, EACCES +from io import StringIO +from os import path, getcwd +from subprocess import Popen, PIPE +from traceback import format_exception + +try: + from urllib.error import HTTPError + from urllib.parse import urljoin, urlunparse + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen, HTTPError + from urlparse import urljoin, urlunparse + +debug = False + +# program to run - None means we still need to search for it +zbarimg = None +# arguments to said program +zbarimg_args = ['-q', '--xml', '--nodbus'] + + +# namespace support +try: + register_namespace = ET.register_namespace +except AttributeError: + def register_namespace(prefix, uri): + ET._namespace_map[uri] = prefix + +# barcode results +BC = 'http://zbar.sourceforge.net/2008/barcode' +register_namespace('bc', BC) + +# testcase extensions +TS = 'http://zbar.sourceforge.net/2009/test-spec' +register_namespace('test', TS) + + +# printing support +def fixtag(node): + return str(node.tag).split('}', 1)[-1] + + +def toxml(node): + s = StringIO() + ET.ElementTree(node).write(s) + return s.getvalue() + + +def hexdump(data): + print(data) + c = 0 + for i, c in enumerate(data): + if i & 0x7 == 0: + print('[%04x]' % i, end=' ') + print(' %04x' % ord(c), end=' ') + if i & 0x7 == 0x7: + print() + if len(c) & 0x7 != 0x7: + print('\n') + + +# search for a file in the distribution +def distdir_search(search_subdir, base, suffixes=('',)): + # start with current dir, + # then try script invocation path + rundir = path.dirname(sys.argv[0]) + search = ['', rundir] + + # finally, attempt to follow VPATH if present + try: + with open('Makefile', 'r') as makefile: + for line in makefile: + if re.match(r'^VPATH\s*=', line): + vpath = line.split('=', 1)[1].strip() + if vpath and vpath != rundir: + search.append(vpath) + break + except IOError: + pass + + # poke around for subdir + subdirs = tuple((search_subdir, path.join('..', search_subdir), '..', '')) + + for prefix in search: + for subdir in subdirs: + for suffix in suffixes: + file = path.realpath(path.join(prefix, subdir, base + suffix)) + if path.isfile(file): + return file + return None + + +def find_zbarimg(): + """search for zbarimg program to run. + """ + global zbarimg + # look in dist dir first + zbarimg = distdir_search('zbarimg', 'zbarimg', ('', '.exe')) + if not zbarimg: + # fall back to PATH + zbarimg = 'zbarimg' + if debug: + print('using zbarimg from PATH') + elif debug: + print('using:', zbarimg) + + +def run_zbarimg(images): + """invoke zbarimg for the specified files. + + return results as an ET.Element + """ + args = [zbarimg] + args.extend(zbarimg_args) + args.extend(images) + if debug: + print('running:', ' '.join(args)) + + # FIXME should be able to pipe (feed) parser straight from output + child = Popen(args, stdout=PIPE, stderr=PIPE) + (xml, err) = child.communicate() + + rc = child.returncode + if debug: + print('zbarimg returned', rc) + + # FIXME trim usage from error msg + assert rc in (0, 4), \ + 'zbarimg returned error status (%d):\n\t%s\n' % (rc, str(err.decode())) + + assert not err, err + + result = ET.XML(xml) + assert result.tag == ET.QName(BC, 'barcodes') + return result + + +class TestCase(UT.TestCase): + """single barcode source test. + + must have source attribute set to an ET.Element representation of a + bc:source tag before test is run. + """ + + def __init__(self, methodName): + UT.TestCase.__init__(self, methodName) + self.source = None + + def shortDescription(self): + return self.source.get('href') + + def setUp(self): + if not zbarimg: + find_zbarimg() + + def runTest(self): + expect = self.source + assert expect is not None + assert expect.tag == ET.QName(BC, 'source') + actual = run_zbarimg((expect.get('href'),)) + + self.assertEqual(len(actual), 1) + + try: + compare_sources(expect, actual[0]) + except AssertionError: + if expect.get(str(ET.QName(TS, 'exception'))) != 'TODO': + raise + # ignore + + +class BuiltinTestCase(TestCase): + def __init__(self, methodName='runTest'): + TestCase.__init__(self, methodName) + + href = distdir_search('examples', 'ean-13.png') + if not href: + href = 'https://git.linuxtv.org/zbar.git/plain/examples/ean-13.png' + + self.source = src = ET.Element(ET.QName(BC, 'source'), href=href) + sym = ET.SubElement(src, ET.QName(BC, 'symbol'), type='EAN-13', + orientation='UP') + data = ET.SubElement(sym, ET.QName(BC, 'data')) + data.text = '9789876543217' + + +def compare_maps(expect, actual, compare_func): + errors = [] + notes = [] + for key, iact in actual.items(): + iexp = expect.pop(key, None) + if iexp is None: + errors.append('bonus unexpected result:\n' + toxml(iact)) + continue + + try: + compare_func(iexp, iact) + except BaseException: + errors.append(''.join(format_exception(*sys.exc_info()))) + + if iexp.get(str(ET.QName(TS, 'exception'))) == 'TODO': + notes.append('TODO unexpected result:\n' + toxml(iact)) + + for key, iexp in expect.items(): + + exc = iexp.get(str(ET.QName(TS, 'exception'))) + + if exc == 'TODO': + notes.append('TODO missing expected result:\n' + toxml(iexp)) + elif exc is not None: + errors.append('missing expected result:\n' + toxml(iexp)) + + if len(notes) == 1: + print('(TODO)', end=' ', file=sys.stderr) + elif notes: + print('(%d TODO)' % len(notes), end=' ', file=sys.stderr) + assert not errors, '\n'.join(errors) + + +def compare_sources(expect, actual): + assert actual.tag == ET.QName(BC, 'source') + assert actual.get('href').endswith(expect.get('href')), \ + 'source href mismatch: %s != %s' % (actual, expect) + + # FIXME process/trim test:* contents + + def map_source(src): + if src == '' or src[0].tag != ET.QName(BC, 'index'): + # insert artificial hierarchy + syms = src[:] + del src[:] + idx = ET.SubElement(src, ET.QName(BC, 'index'), num='0') + idx[:] = syms + exc = src.get(str(ET.QName(TS, 'exception'))) + if exc is not None: + idx.set(str(ET.QName(TS, 'exception')), exc) + return {'0': idx} + elif len(src): + assert src[0].tag != ET.QName(BC, 'symbol'), \ + 'invalid source element: ' + \ + 'expecting "index" or "symbol", got "%s"' % fixtag(src[0]) + + srcmap = {} + for idx in src: + srcmap[idx.get('num')] = idx + return srcmap + + compare_maps(map_source(expect), map_source(actual), compare_indices) + + +def compare_indices(expect, actual): + assert actual.tag == ET.QName(BC, 'index') + assert actual.get('num') == expect.get('num') + + # FIXME process/trim test:* contents + + def map_index(idx): + idxmap = {} + for sym in idx: + assert sym.tag == ET.QName(BC, 'symbol') + typ = sym.get('type') + assert typ is not None + data = sym.find(str(ET.QName(BC, 'data'))).text + idxmap[typ, data] = sym + + return idxmap + + try: + compare_maps(map_index(expect), map_index(actual), compare_symbols) + except AssertionError: + if expect.get(str(ET.QName(TS, 'exception'))) != 'TODO': + raise + + +def compare_symbols(expect, actual): + orient = expect.get('orientation') + if orient: + assert actual.get('orientation') == orient + +# override unittest.TestLoader to populate tests from xml description + + +class TestLoader: + suiteClass = UT.TestSuite + + def __init__(self): + self.cwd = urlunparse(('file', '', getcwd() + '/', '', '', '')) + if debug: + print('cwd =', self.cwd) + + def loadTestsFromModule(self, module): + return self.suiteClass([BuiltinTestCase()]) + + def loadTestsFromNames(self, names, module=None): + suites = [self.loadTestsFromName(name, module) for name in names] + return self.suiteClass(suites) + + def loadTestsFromURL(self, url=None, file=None): + if debug: + print('loading url:', url) + + target = None + if not file: + if not url: + return self.suiteClass([BuiltinTestCase()]) + + content = None + url = urljoin(self.cwd, url) + # FIXME grok fragment + + try: + if debug: + print('trying:', url) + file = urlopen(url) + content = file.info().get('Content-Type') + except HTTPError: + # possible remote directory + pass + except IOError as e: + if e.errno not in (EISDIR, EINVAL, EACCES): + raise + # could be local directory + + if (not file or + content == 'text/html' or + (isinstance(file, HTTPError) and file.code != 200)): + # could be directory, try opening index + try: + tmp = urljoin(url + '/', 'index.xml') + if debug: + print('trying index:', tmp) + file = urlopen(tmp) + content = file.info().get('Content-Type') + url = tmp + except IOError: + raise IOError('no test index found at: %s' % url) + + if debug: + print('\tContent-Type:', content) + + if content not in ('application/xml', 'text/xml'): + # assume url is image to test, try containing index + # FIXME should be able to keep searching up + try: + target = url.rsplit('/', 1)[1] + index = urljoin(url, 'index.xml') + if debug: + print('trying index:', index) + file = urlopen(index) + content = file.info().get('Content-Type') + if debug: + print('\tContent-Type:', content) + assert content in ('application/xml', 'text/xml') + url = index + except IOError: + raise IOError('no index found for: %s' % url) + + index = ET.ElementTree(file=file).getroot() + assert index.tag == ET.QName(BC, 'barcodes') + + suite = self.suiteClass() + for src in index: + # FIXME trim any meta info + href = src.get('href') + if target and target != href: + continue + if src.tag == ET.QName(BC, 'source'): + test = TestCase() + # convert file URLs to filesystem paths + href = urljoin(url, href) + href = re.sub(r'^file://', '', href) + src.set('href', href) + test.source = src + suite.addTest(test) + elif src.tag == ET.QName(TS, 'index'): + suite.addTest(self.loadTestsFromURL(urljoin(url, href))) + else: + raise AssertionError('malformed test index') # FIXME detail + + assert suite.countTestCases(), 'empty test index: %s' % url + return suite + + def loadTestsFromName(self, name=None, module=None): + return self.loadTestsFromURL(name) + + def unsupported(self, *args, **kwargs): + raise TypeError("unsupported TestLoader API") + + # FAKE discover method needed for python3 to work + def discover(self, start_dir, pattern, top_level_dir=None): + return self.loadTestsFromURL() + + loadTestsFromTestCase = unsupported + getTestCaseNames = unsupported + + +if __name__ == '__main__': + if '-d' in sys.argv: + debug = True + sys.argv.remove('-d') + + UT.main(module=None, testLoader=TestLoader()) diff --git a/test/check_dbus.sh.in b/test/check_dbus.sh.in new file mode 100644 index 0000000..3b97b36 --- /dev/null +++ b/test/check_dbus.sh.in @@ -0,0 +1,39 @@ +#!/bin/bash + +DIR="@abs_top_srcdir@" +BDIR="@abs_top_builddir@" +LOG="/tmp/zbar_dbus_test_$$.log" +LOG_BIN="/tmp/zbar_dbus_test_$$.bin" + +EXPECTED="7294377b69fb00c7e0811429ab7a42cc8cecfda0" +EXPECTED_BIN="df896e459e47a7d392031a7d4962722a143e276b" + + +$BDIR/test/test_dbus -c2 -t5 --log=$LOG --bin-log=$LOG_BIN & +PID=$! + +trap "rm -r $LOG $LOG_BIN" EXIT + +$BDIR/zbarimg/zbarimg $DIR/examples/code-128.png 2>/dev/null >/dev/null +$BDIR/zbarimg/zbarimg -Sbinary $DIR/examples/qr-code-binary.png 2>/dev/null >/dev/null + +wait $PID + +if [ ! -s $LOG ] || [ ! -s $LOG_BIN ]; then + echo "FAILED: nothing received via D-Bus" + exit -2 +fi + +CK="`cat $LOG |sha1sum |cut -d" " -f 1`" +if [ "x$CK" != "x$EXPECTED" ]; then + echo "FAILED: $CK instead of $EXPECTED" + exit -2 +fi + +CK_BIN="`cat $LOG_BIN |sha1sum |cut -d" " -f 1`" +if [ "x$CK_BIN" != "x$EXPECTED_BIN" ]; then + echo "FAILED: $CK_BIN instead of $EXPECTED_BIN" + exit -2 +fi + +echo "D-Bus PASSED." diff --git a/test/dbg_scan.cpp b/test/dbg_scan.cpp new file mode 100644 index 0000000..a496112 --- /dev/null +++ b/test/dbg_scan.cpp @@ -0,0 +1,218 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <Magick++.h> +#include <fstream> +#include <iostream> +#include <libgen.h> +#include <zbar.h> + +using namespace std; +using namespace zbar; + +#ifndef ZBAR_FIXED +#define ZBAR_FIXED 5 +#endif + +#define ZBAR_FRAC (1 << ZBAR_FIXED) + +Decoder decoder; +Scanner scanner; + +/* undocumented API for drawing cutesy debug graphics */ +extern "C" void zbar_scanner_get_state(const zbar_scanner_t *scn, unsigned *x, + unsigned *cur_edge, unsigned *last_edge, + int *y0, int *y1, int *y2, + int *y1_thresh); + +void scan_image(const char *filename) +{ + scanner.reset(); + // normally scanner would reset associated decoder, + // but this debug program connects them manually + // (to make intermediate state more readily available) + // so decoder must also be reset manually + decoder.reset(); + + Magick::Image image; + image.read(filename); + string file = image.baseFilename(); + size_t baseidx = file.rfind('/'); + if (baseidx != string::npos) + file = file.substr(baseidx + 1, file.length() - baseidx - 1); + ofstream svg((file + ".svg").c_str()); + + unsigned inwidth = image.columns(); + unsigned width = inwidth + 3; + unsigned height = image.rows(); + unsigned midy = height / 2; + cerr << "x+: " << midy << endl; + + image.crop(Magick::Geometry(inwidth, 1, 0, midy)); + + svg << "<?xml version='1.0'?>" << endl + << "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" + << " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" << endl + << "<svg version='1.1' id='top'" + << " width='10in' height='6in' preserveAspectRatio='xMinYMid slice'" + << " overflow='visible' viewBox='0,0 " << width * 2 << ",384'" + << " xmlns:xlink='http://www.w3.org/1999/xlink'" + << " xmlns='http://www.w3.org/2000/svg'>" << endl + << "<defs><style type='text/css'><![CDATA[" << endl + << " * { stroke-linejoin: round; stroke-linecap: round;" + << " stroke-width: .1; text-anchor: middle;" + << " image-rendering: optimizeSpeed;" + << " font-size: 6; font-weight: bold }" << endl + << " path { fill: none }" << endl + << " #zero { stroke: #00f }" << endl + << " #edges { stroke: #f00 }" << endl + << " #cur-edge { stroke: #f44 }" << endl + << " #raw { stroke: orange }" << endl + << " #y0 { stroke: yellow }" << endl + << " #y1 { stroke: #0c0 }" << endl + << " #y2 { stroke: #0aa }" << endl + << " .y1thr { stroke: #f0f }" << endl + << " rect.bar { fill: black }" << endl + << " text.bar { fill: white }" << endl + << " rect.space { fill: white }" << endl + << " text.space { fill: black }" << endl + << " text.data { fill: #44f; font-size: 16 }" << endl + << "]]></style></defs>" << endl + << "<image width='" << inwidth * 2 << "' height='384'" + << " preserveAspectRatio='none'" + << " xlink:href='" << file << ".png'/>" << endl + << "<g transform='translate(1,384) scale(2,-.5)'>" << endl; + + // brute force + unsigned raw[inwidth]; + { + // extract scan from image pixels + image.modifyImage(); + Magick::Pixels view(image); + Magick::PixelPacket *pxp = view.get(0, 0, inwidth, 1); + Magick::ColorYUV y; + double max = 0; + svg << "<path id='raw' d='M"; + unsigned i; + for (i = 0; i < inwidth; i++, pxp++) { + y = *pxp; + if (max < y.y()) + max = y.y(); + raw[i] = (unsigned)(y.y() * 0x100); + svg << ((i != 1) ? " " : " L ") << i << "," << raw[i]; + y.u(0); + y.v(0); + *pxp = y; + } + view.sync(); + svg << "'/>" << endl << "</g>" << endl; + } + image.depth(8); + image.write(file + ".png"); + + // process scan and capture calculated values + unsigned cur_edge[width], last_edge[width]; + int y0[width], y1[width], y2[width], y1_thr[width]; + + svg << "<g transform='translate(-3)'>" << endl; + for (unsigned i = 0; i < width; i++) { + int edge; + if (i < inwidth) + edge = scanner.scan_y(raw[i]); + else + edge = scanner.flush(); + + unsigned x; + zbar_scanner_get_state(scanner, &x, &cur_edge[i], &last_edge[i], &y0[i], + &y1[i], &y2[i], &y1_thr[i]); + if (edge) { + unsigned w = scanner.get_width(); + if (w) + svg << "<rect x='" << (2. * (last_edge[i] - w) / ZBAR_FRAC) + << "' width='" << (w * 2. / ZBAR_FRAC) + << "' height='32' class='" + << (scanner.get_color() ? "space" : "bar") << "'/>" << endl + << "<text transform='translate(" + << ((2. * last_edge[i] - w) / ZBAR_FRAC) - 3 + << ",16) rotate(90)' class='" + << (scanner.get_color() ? "space" : "bar") << "'>" << endl + << w << "</text>" << endl; + zbar_symbol_type_t sym = decoder.decode_width(w); + if (sym > ZBAR_PARTIAL) { + svg << "<text transform='translate(" + << (2. * (last_edge[i] + w) / ZBAR_FRAC) + << ",208) rotate(90)' class='data'>" + << decoder.get_data_string() << "</text>" << endl; + } + } else if ((!i) ? last_edge[i] : last_edge[i] == last_edge[i - 1]) + last_edge[i] = 0; + } + + svg << "</g>" << endl + << "<g transform='translate(-3,384) scale(2,-.5)'>" << endl + << "<path id='edges' d='"; + for (unsigned i = 0; i < width; i++) + if (last_edge[i]) + svg << " M" << ((double)last_edge[i] / ZBAR_FRAC) << ",0v768"; + svg << "'/>" << endl + << "</g>" << endl + << "<g transform='translate(-1,384) scale(2,-.5)'>" << endl + << "<path id='y0' d='M"; + for (unsigned i = 0; i < width; i++) + svg << ((i != 1) ? " " : " L ") << i << "," << y0[i]; + svg << "'/>" << endl << "</g>" << endl; + + svg << "<g transform='translate(-1,128) scale(2,-1)'>" << endl + << "<line id='zero' x2='" << width << "'/>" << endl + << "<path id='cur-edge' d='"; + for (unsigned i = 1; i < width - 1; i++) + if (!last_edge[i + 1] && (cur_edge[i] != cur_edge[i + 1])) + svg << " M" << ((double)cur_edge[i] / ZBAR_FRAC) - 1 << ",-32v64"; + svg << "'/>" << endl << "<path class='y1thr' d='M"; + for (unsigned i = 0; i < width; i++) + svg << ((i != 1) ? " " : " L ") << i << "," << y1_thr[i]; + svg << "'/>" << endl << "<path class='y1thr' d='M"; + for (unsigned i = 0; i < width; i++) + svg << ((i != 1) ? " " : " L ") << i << "," << -y1_thr[i]; + svg << "'/>" << endl << "<path id='y1' d='M"; + for (unsigned i = 0; i < width; i++) + svg << ((i != 1) ? " " : " L ") << (i - 0.5) << "," << y1[i]; + svg << "'/>" << endl << "<path id='y2' d='M"; + for (unsigned i = 0; i < width; i++) + svg << ((i != 1) ? " " : " L ") << i << "," << y2[i]; + svg << "'/>" << endl << "</g>" << endl; + + svg << "</svg>" << endl; +} + +int main(int argc, const char *argv[]) +{ + if (argc < 2) { + cerr << "ERROR: specify image file(s) to scan" << endl; + return (1); + } + + for (int i = 1; i < argc; i++) + scan_image(argv[i]); + return (0); +} diff --git a/test/pdf417_encode.h b/test/pdf417_encode.h new file mode 100644 index 0000000..eb70cf5 --- /dev/null +++ b/test/pdf417_encode.h @@ -0,0 +1,4674 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _PDF417_ENCODE_H_ +#define _PDF417_ENCODE_H_ + +unsigned long pdf417_encode[929][3] = { + { + 0x31111136, + 0x51111125, + 0x21111155, + }, /* 0 */ + { + 0x41111144, + 0x61111133, + 0x31111163, + }, /* 1 */ + { + 0x51111152, + 0x41111216, + 0x11111246, + }, /* 2 */ + { + 0x31111235, + 0x51111224, + 0x21111254, + }, /* 3 */ + { + 0x41111243, + 0x61111232, + 0x31111262, + }, /* 4 */ + { + 0x51111251, + 0x41111315, + 0x11111345, + }, /* 5 */ + { + 0x21111326, + 0x51111323, + 0x21111353, + }, /* 6 */ + { + 0x31111334, + 0x61111331, + 0x31111361, + }, /* 7 */ + { + 0x21111425, + 0x41111414, + 0x11111444, + }, /* 8 */ + { + 0x11111516, + 0x51111422, + 0x21111452, + }, /* 9 */ + { + 0x21111524, + 0x41111513, + 0x11111543, + }, /* 10 */ + { + 0x11111615, + 0x51111521, + 0x61112114, + }, /* 11 */ + { + 0x21112136, + 0x41111612, + 0x11112155, + }, /* 12 */ + { + 0x31112144, + 0x41112125, + 0x21112163, + }, /* 13 */ + { + 0x41112152, + 0x51112133, + 0x61112213, + }, /* 14 */ + { + 0x21112235, + 0x61112141, + 0x11112254, + }, /* 15 */ + { + 0x31112243, + 0x31112216, + 0x21112262, + }, /* 16 */ + { + 0x41112251, + 0x41112224, + 0x61112312, + }, /* 17 */ + { + 0x11112326, + 0x51112232, + 0x11112353, + }, /* 18 */ + { + 0x21112334, + 0x31112315, + 0x21112361, + }, /* 19 */ + { + 0x11112425, + 0x41112323, + 0x61112411, + }, /* 20 */ + { + 0x11113136, + 0x51112331, + 0x11112452, + }, /* 21 */ + { + 0x21113144, + 0x31112414, + 0x51113114, + }, /* 22 */ + { + 0x31113152, + 0x41112422, + 0x61113122, + }, /* 23 */ + { + 0x11113235, + 0x31112513, + 0x11113163, + }, /* 24 */ + { + 0x21113243, + 0x41112521, + 0x51113213, + }, /* 25 */ + { + 0x31113251, + 0x31112612, + 0x61113221, + }, /* 26 */ + { + 0x11113334, + 0x31113125, + 0x11113262, + }, /* 27 */ + { + 0x21113342, + 0x41113133, + 0x51113312, + }, /* 28 */ + { + 0x11114144, + 0x51113141, + 0x11113361, + }, /* 29 */ + { + 0x21114152, + 0x21113216, + 0x51113411, + }, /* 30 */ + { + 0x11114243, + 0x31113224, + 0x41114114, + }, /* 31 */ + { + 0x21114251, + 0x41113232, + 0x51114122, + }, /* 32 */ + { + 0x11115152, + 0x21113315, + 0x41114213, + }, /* 33 */ + { + 0x51116111, + 0x31113323, + 0x51114221, + }, /* 34 */ + { + 0x31121135, + 0x41113331, + 0x41114312, + }, /* 35 */ + { + 0x41121143, + 0x21113414, + 0x41114411, + }, /* 36 */ + { + 0x51121151, + 0x31113422, + 0x31115114, + }, /* 37 */ + { + 0x21121226, + 0x21113513, + 0x41115122, + }, /* 38 */ + { + 0x31121234, + 0x31113521, + 0x31115213, + }, /* 39 */ + { + 0x41121242, + 0x21113612, + 0x41115221, + }, /* 40 */ + { + 0x21121325, + 0x21114125, + 0x31115312, + }, /* 41 */ + { + 0x31121333, + 0x31114133, + 0x31115411, + }, /* 42 */ + { + 0x11121416, + 0x41114141, + 0x21116114, + }, /* 43 */ + { + 0x21121424, + 0x11114216, + 0x31116122, + }, /* 44 */ + { + 0x31121432, + 0x21114224, + 0x21116213, + }, /* 45 */ + { + 0x11121515, + 0x31114232, + 0x31116221, + }, /* 46 */ + { + 0x21121523, + 0x11114315, + 0x21116312, + }, /* 47 */ + { + 0x11121614, + 0x21114323, + 0x11121146, + }, /* 48 */ + { + 0x21122135, + 0x31114331, + 0x21121154, + }, /* 49 */ + { + 0x31122143, + 0x11114414, + 0x31121162, + }, /* 50 */ + { + 0x41122151, + 0x21114422, + 0x11121245, + }, /* 51 */ + { + 0x11122226, + 0x11114513, + 0x21121253, + }, /* 52 */ + { + 0x21122234, + 0x21114521, + 0x31121261, + }, /* 53 */ + { + 0x31122242, + 0x11115125, + 0x11121344, + }, /* 54 */ + { + 0x11122325, + 0x21115133, + 0x21121352, + }, /* 55 */ + { + 0x21122333, + 0x31115141, + 0x11121443, + }, /* 56 */ + { + 0x31122341, + 0x11115224, + 0x21121451, + }, /* 57 */ + { + 0x11122424, + 0x21115232, + 0x11121542, + }, /* 58 */ + { + 0x21122432, + 0x11115323, + 0x61122113, + }, /* 59 */ + { + 0x11123135, + 0x21115331, + 0x11122154, + }, /* 60 */ + { + 0x21123143, + 0x11115422, + 0x21122162, + }, /* 61 */ + { + 0x31123151, + 0x11116133, + 0x61122212, + }, /* 62 */ + { + 0x11123234, + 0x21116141, + 0x11122253, + }, /* 63 */ + { + 0x21123242, + 0x11116232, + 0x21122261, + }, /* 64 */ + { + 0x11123333, + 0x11116331, + 0x61122311, + }, /* 65 */ + { + 0x21123341, + 0x41121116, + 0x11122352, + }, /* 66 */ + { + 0x11124143, + 0x51121124, + 0x11122451, + }, /* 67 */ + { + 0x21124151, + 0x61121132, + 0x51123113, + }, /* 68 */ + { + 0x11124242, + 0x41121215, + 0x61123121, + }, /* 69 */ + { + 0x11124341, + 0x51121223, + 0x11123162, + }, /* 70 */ + { + 0x21131126, + 0x61121231, + 0x51123212, + }, /* 71 */ + { + 0x31131134, + 0x41121314, + 0x11123261, + }, /* 72 */ + { + 0x41131142, + 0x51121322, + 0x51123311, + }, /* 73 */ + { + 0x21131225, + 0x41121413, + 0x41124113, + }, /* 74 */ + { + 0x31131233, + 0x51121421, + 0x51124121, + }, /* 75 */ + { + 0x41131241, + 0x41121512, + 0x41124212, + }, /* 76 */ + { + 0x11131316, + 0x41121611, + 0x41124311, + }, /* 77 */ + { + 0x21131324, + 0x31122116, + 0x31125113, + }, /* 78 */ + { + 0x31131332, + 0x41122124, + 0x41125121, + }, /* 79 */ + { + 0x11131415, + 0x51122132, + 0x31125212, + }, /* 80 */ + { + 0x21131423, + 0x31122215, + 0x31125311, + }, /* 81 */ + { + 0x11131514, + 0x41122223, + 0x21126113, + }, /* 82 */ + { + 0x11131613, + 0x51122231, + 0x31126121, + }, /* 83 */ + { + 0x11132126, + 0x31122314, + 0x21126212, + }, /* 84 */ + { + 0x21132134, + 0x41122322, + 0x21126311, + }, /* 85 */ + { + 0x31132142, + 0x31122413, + 0x11131145, + }, /* 86 */ + { + 0x11132225, + 0x41122421, + 0x21131153, + }, /* 87 */ + { + 0x21132233, + 0x31122512, + 0x31131161, + }, /* 88 */ + { + 0x31132241, + 0x31122611, + 0x11131244, + }, /* 89 */ + { + 0x11132324, + 0x21123116, + 0x21131252, + }, /* 90 */ + { + 0x21132332, + 0x31123124, + 0x11131343, + }, /* 91 */ + { + 0x11132423, + 0x41123132, + 0x21131351, + }, /* 92 */ + { + 0x11132522, + 0x21123215, + 0x11131442, + }, /* 93 */ + { + 0x11133134, + 0x31123223, + 0x11131541, + }, /* 94 */ + { + 0x21133142, + 0x41123231, + 0x61132112, + }, /* 95 */ + { + 0x11133233, + 0x21123314, + 0x11132153, + }, /* 96 */ + { + 0x21133241, + 0x31123322, + 0x21132161, + }, /* 97 */ + { + 0x11133332, + 0x21123413, + 0x61132211, + }, /* 98 */ + { + 0x11134142, + 0x31123421, + 0x11132252, + }, /* 99 */ + { + 0x21141125, + 0x21123512, + 0x11132351, + }, /* 100 */ + { + 0x31141133, + 0x21123611, + 0x51133112, + }, /* 101 */ + { + 0x41141141, + 0x11124116, + 0x11133161, + }, /* 102 */ + { + 0x11141216, + 0x21124124, + 0x51133211, + }, /* 103 */ + { + 0x21141224, + 0x31124132, + 0x41134112, + }, /* 104 */ + { + 0x31141232, + 0x11124215, + 0x41134211, + }, /* 105 */ + { + 0x11141315, + 0x21124223, + 0x31135112, + }, /* 106 */ + { + 0x21141323, + 0x31124231, + 0x31135211, + }, /* 107 */ + { + 0x31141331, + 0x11124314, + 0x21136112, + }, /* 108 */ + { + 0x11141414, + 0x21124322, + 0x21136211, + }, /* 109 */ + { + 0x21141422, + 0x11124413, + 0x11141144, + }, /* 110 */ + { + 0x11141513, + 0x21124421, + 0x21141152, + }, /* 111 */ + { + 0x21141521, + 0x11124512, + 0x11141243, + }, /* 112 */ + { + 0x11142125, + 0x11125124, + 0x21141251, + }, /* 113 */ + { + 0x21142133, + 0x21125132, + 0x11141342, + }, /* 114 */ + { + 0x31142141, + 0x11125223, + 0x11141441, + }, /* 115 */ + { + 0x11142224, + 0x21125231, + 0x61142111, + }, /* 116 */ + { + 0x21142232, + 0x11125322, + 0x11142152, + }, /* 117 */ + { + 0x11142323, + 0x11125421, + 0x11142251, + }, /* 118 */ + { + 0x21142331, + 0x11126132, + 0x51143111, + }, /* 119 */ + { + 0x11142422, + 0x11126231, + 0x41144111, + }, /* 120 */ + { + 0x11142521, + 0x41131115, + 0x31145111, + }, /* 121 */ + { + 0x21143141, + 0x51131123, + 0x11151143, + }, /* 122 */ + { + 0x11143331, + 0x61131131, + 0x21151151, + }, /* 123 */ + { + 0x11151116, + 0x41131214, + 0x11151242, + }, /* 124 */ + { + 0x21151124, + 0x51131222, + 0x11151341, + }, /* 125 */ + { + 0x31151132, + 0x41131313, + 0x11152151, + }, /* 126 */ + { + 0x11151215, + 0x51131321, + 0x11161142, + }, /* 127 */ + { + 0x21151223, + 0x41131412, + 0x11161241, + }, /* 128 */ + { + 0x31151231, + 0x41131511, + 0x12111146, + }, /* 129 */ + { + 0x11151314, + 0x31132115, + 0x22111154, + }, /* 130 */ + { + 0x21151322, + 0x41132123, + 0x32111162, + }, /* 131 */ + { + 0x11151413, + 0x51132131, + 0x12111245, + }, /* 132 */ + { + 0x21151421, + 0x31132214, + 0x22111253, + }, /* 133 */ + { + 0x11151512, + 0x41132222, + 0x32111261, + }, /* 134 */ + { + 0x11152124, + 0x31132313, + 0x12111344, + }, /* 135 */ + { + 0x11152223, + 0x41132321, + 0x22111352, + }, /* 136 */ + { + 0x11152322, + 0x31132412, + 0x12111443, + }, /* 137 */ + { + 0x11161115, + 0x31132511, + 0x22111451, + }, /* 138 */ + { + 0x31161131, + 0x21133115, + 0x12111542, + }, /* 139 */ + { + 0x21161222, + 0x31133123, + 0x62112113, + }, /* 140 */ + { + 0x21161321, + 0x41133131, + 0x12112154, + }, /* 141 */ + { + 0x11161511, + 0x21133214, + 0x22112162, + }, /* 142 */ + { + 0x32111135, + 0x31133222, + 0x62112212, + }, /* 143 */ + { + 0x42111143, + 0x21133313, + 0x12112253, + }, /* 144 */ + { + 0x52111151, + 0x31133321, + 0x22112261, + }, /* 145 */ + { + 0x22111226, + 0x21133412, + 0x62112311, + }, /* 146 */ + { + 0x32111234, + 0x21133511, + 0x12112352, + }, /* 147 */ + { + 0x42111242, + 0x11134115, + 0x12112451, + }, /* 148 */ + { + 0x22111325, + 0x21134123, + 0x52113113, + }, /* 149 */ + { + 0x32111333, + 0x31134131, + 0x62113121, + }, /* 150 */ + { + 0x42111341, + 0x11134214, + 0x12113162, + }, /* 151 */ + { + 0x12111416, + 0x21134222, + 0x52113212, + }, /* 152 */ + { + 0x22111424, + 0x11134313, + 0x12113261, + }, /* 153 */ + { + 0x12111515, + 0x21134321, + 0x52113311, + }, /* 154 */ + { + 0x22112135, + 0x11134412, + 0x42114113, + }, /* 155 */ + { + 0x32112143, + 0x11134511, + 0x52114121, + }, /* 156 */ + { + 0x42112151, + 0x11135123, + 0x42114212, + }, /* 157 */ + { + 0x12112226, + 0x21135131, + 0x42114311, + }, /* 158 */ + { + 0x22112234, + 0x11135222, + 0x32115113, + }, /* 159 */ + { + 0x32112242, + 0x11135321, + 0x42115121, + }, /* 160 */ + { + 0x12112325, + 0x11136131, + 0x32115212, + }, /* 161 */ + { + 0x22112333, + 0x41141114, + 0x32115311, + }, /* 162 */ + { + 0x12112424, + 0x51141122, + 0x22116113, + }, /* 163 */ + { + 0x12112523, + 0x41141213, + 0x32116121, + }, /* 164 */ + { + 0x12113135, + 0x51141221, + 0x22116212, + }, /* 165 */ + { + 0x22113143, + 0x41141312, + 0x22116311, + }, /* 166 */ + { + 0x32113151, + 0x41141411, + 0x21211145, + }, /* 167 */ + { + 0x12113234, + 0x31142114, + 0x31211153, + }, /* 168 */ + { + 0x22113242, + 0x41142122, + 0x41211161, + }, /* 169 */ + { + 0x12113333, + 0x31142213, + 0x11211236, + }, /* 170 */ + { + 0x12113432, + 0x41142221, + 0x21211244, + }, /* 171 */ + { + 0x12114143, + 0x31142312, + 0x31211252, + }, /* 172 */ + { + 0x22114151, + 0x31142411, + 0x11211335, + }, /* 173 */ + { + 0x12114242, + 0x21143114, + 0x21211343, + }, /* 174 */ + { + 0x12115151, + 0x31143122, + 0x31211351, + }, /* 175 */ + { + 0x31211126, + 0x21143213, + 0x11211434, + }, /* 176 */ + { + 0x41211134, + 0x31143221, + 0x21211442, + }, /* 177 */ + { + 0x51211142, + 0x21143312, + 0x11211533, + }, /* 178 */ + { + 0x31211225, + 0x21143411, + 0x21211541, + }, /* 179 */ + { + 0x41211233, + 0x11144114, + 0x11211632, + }, /* 180 */ + { + 0x51211241, + 0x21144122, + 0x12121145, + }, /* 181 */ + { + 0x21211316, + 0x11144213, + 0x22121153, + }, /* 182 */ + { + 0x31211324, + 0x21144221, + 0x32121161, + }, /* 183 */ + { + 0x41211332, + 0x11144312, + 0x11212145, + }, /* 184 */ + { + 0x21211415, + 0x11144411, + 0x12121244, + }, /* 185 */ + { + 0x31211423, + 0x11145122, + 0x22121252, + }, /* 186 */ + { + 0x41211431, + 0x11145221, + 0x11212244, + }, /* 187 */ + { + 0x21211514, + 0x41151113, + 0x21212252, + }, /* 188 */ + { + 0x31211522, + 0x51151121, + 0x22121351, + }, /* 189 */ + { + 0x22121126, + 0x41151212, + 0x11212343, + }, /* 190 */ + { + 0x32121134, + 0x41151311, + 0x12121442, + }, /* 191 */ + { + 0x42121142, + 0x31152113, + 0x11212442, + }, /* 192 */ + { + 0x21212126, + 0x41152121, + 0x12121541, + }, /* 193 */ + { + 0x22121225, + 0x31152212, + 0x11212541, + }, /* 194 */ + { + 0x32121233, + 0x31152311, + 0x62122112, + }, /* 195 */ + { + 0x42121241, + 0x21153113, + 0x12122153, + }, /* 196 */ + { + 0x21212225, + 0x31153121, + 0x22122161, + }, /* 197 */ + { + 0x31212233, + 0x21153212, + 0x61213112, + }, /* 198 */ + { + 0x41212241, + 0x21153311, + 0x62122211, + }, /* 199 */ + { + 0x11212316, + 0x11154113, + 0x11213153, + }, /* 200 */ + { + 0x12121415, + 0x21154121, + 0x12122252, + }, /* 201 */ + { + 0x22121423, + 0x11154212, + 0x61213211, + }, /* 202 */ + { + 0x32121431, + 0x11154311, + 0x11213252, + }, /* 203 */ + { + 0x11212415, + 0x41161112, + 0x12122351, + }, /* 204 */ + { + 0x21212423, + 0x41161211, + 0x11213351, + }, /* 205 */ + { + 0x11212514, + 0x31162112, + 0x52123112, + }, /* 206 */ + { + 0x12122126, + 0x31162211, + 0x12123161, + }, /* 207 */ + { + 0x22122134, + 0x21163112, + 0x51214112, + }, /* 208 */ + { + 0x32122142, + 0x21163211, + 0x52123211, + }, /* 209 */ + { + 0x11213126, + 0x42111116, + 0x11214161, + }, /* 210 */ + { + 0x12122225, + 0x52111124, + 0x51214211, + }, /* 211 */ + { + 0x22122233, + 0x62111132, + 0x42124112, + }, /* 212 */ + { + 0x32122241, + 0x42111215, + 0x41215112, + }, /* 213 */ + { + 0x11213225, + 0x52111223, + 0x42124211, + }, /* 214 */ + { + 0x21213233, + 0x62111231, + 0x41215211, + }, /* 215 */ + { + 0x31213241, + 0x42111314, + 0x32125112, + }, /* 216 */ + { + 0x11213324, + 0x52111322, + 0x31216112, + }, /* 217 */ + { + 0x12122423, + 0x42111413, + 0x32125211, + }, /* 218 */ + { + 0x11213423, + 0x52111421, + 0x31216211, + }, /* 219 */ + { + 0x12123134, + 0x42111512, + 0x22126112, + }, /* 220 */ + { + 0x22123142, + 0x42111611, + 0x22126211, + }, /* 221 */ + { + 0x11214134, + 0x32112116, + 0x11221136, + }, /* 222 */ + { + 0x12123233, + 0x42112124, + 0x21221144, + }, /* 223 */ + { + 0x22123241, + 0x52112132, + 0x31221152, + }, /* 224 */ + { + 0x11214233, + 0x32112215, + 0x11221235, + }, /* 225 */ + { + 0x21214241, + 0x42112223, + 0x21221243, + }, /* 226 */ + { + 0x11214332, + 0x52112231, + 0x31221251, + }, /* 227 */ + { + 0x12124142, + 0x32112314, + 0x11221334, + }, /* 228 */ + { + 0x11215142, + 0x42112322, + 0x21221342, + }, /* 229 */ + { + 0x12124241, + 0x32112413, + 0x11221433, + }, /* 230 */ + { + 0x11215241, + 0x42112421, + 0x21221441, + }, /* 231 */ + { + 0x31221125, + 0x32112512, + 0x11221532, + }, /* 232 */ + { + 0x41221133, + 0x32112611, + 0x11221631, + }, /* 233 */ + { + 0x51221141, + 0x22113116, + 0x12131144, + }, /* 234 */ + { + 0x21221216, + 0x32113124, + 0x22131152, + }, /* 235 */ + { + 0x31221224, + 0x42113132, + 0x11222144, + }, /* 236 */ + { + 0x41221232, + 0x22113215, + 0x12131243, + }, /* 237 */ + { + 0x21221315, + 0x32113223, + 0x22131251, + }, /* 238 */ + { + 0x31221323, + 0x42113231, + 0x11222243, + }, /* 239 */ + { + 0x41221331, + 0x22113314, + 0x21222251, + }, /* 240 */ + { + 0x21221414, + 0x32113322, + 0x11222342, + }, /* 241 */ + { + 0x31221422, + 0x22113413, + 0x12131441, + }, /* 242 */ + { + 0x21221513, + 0x32113421, + 0x11222441, + }, /* 243 */ + { + 0x21221612, + 0x22113512, + 0x62132111, + }, /* 244 */ + { + 0x22131125, + 0x22113611, + 0x12132152, + }, /* 245 */ + { + 0x32131133, + 0x12114116, + 0x61223111, + }, /* 246 */ + { + 0x42131141, + 0x22114124, + 0x11223152, + }, /* 247 */ + { + 0x21222125, + 0x32114132, + 0x12132251, + }, /* 248 */ + { + 0x22131224, + 0x12114215, + 0x11223251, + }, /* 249 */ + { + 0x32131232, + 0x22114223, + 0x52133111, + }, /* 250 */ + { + 0x11222216, + 0x32114231, + 0x51224111, + }, /* 251 */ + { + 0x12131315, + 0x12114314, + 0x42134111, + }, /* 252 */ + { + 0x31222232, + 0x22114322, + 0x41225111, + }, /* 253 */ + { + 0x32131331, + 0x12114413, + 0x32135111, + }, /* 254 */ + { + 0x11222315, + 0x22114421, + 0x31226111, + }, /* 255 */ + { + 0x12131414, + 0x12114512, + 0x22136111, + }, /* 256 */ + { + 0x22131422, + 0x12115124, + 0x11231135, + }, /* 257 */ + { + 0x11222414, + 0x22115132, + 0x21231143, + }, /* 258 */ + { + 0x21222422, + 0x12115223, + 0x31231151, + }, /* 259 */ + { + 0x22131521, + 0x22115231, + 0x11231234, + }, /* 260 */ + { + 0x12131612, + 0x12115322, + 0x21231242, + }, /* 261 */ + { + 0x12132125, + 0x12115421, + 0x11231333, + }, /* 262 */ + { + 0x22132133, + 0x12116132, + 0x21231341, + }, /* 263 */ + { + 0x32132141, + 0x12116231, + 0x11231432, + }, /* 264 */ + { + 0x11223125, + 0x51211115, + 0x11231531, + }, /* 265 */ + { + 0x12132224, + 0x61211123, + 0x12141143, + }, /* 266 */ + { + 0x22132232, + 0x11211164, + 0x22141151, + }, /* 267 */ + { + 0x11223224, + 0x51211214, + 0x11232143, + }, /* 268 */ + { + 0x21223232, + 0x61211222, + 0x12141242, + }, /* 269 */ + { + 0x22132331, + 0x11211263, + 0x11232242, + }, /* 270 */ + { + 0x11223323, + 0x51211313, + 0x12141341, + }, /* 271 */ + { + 0x12132422, + 0x61211321, + 0x11232341, + }, /* 272 */ + { + 0x12132521, + 0x11211362, + 0x12142151, + }, /* 273 */ + { + 0x12133133, + 0x51211412, + 0x11233151, + }, /* 274 */ + { + 0x22133141, + 0x51211511, + 0x11241134, + }, /* 275 */ + { + 0x11224133, + 0x42121115, + 0x21241142, + }, /* 276 */ + { + 0x12133232, + 0x52121123, + 0x11241233, + }, /* 277 */ + { + 0x11224232, + 0x62121131, + 0x21241241, + }, /* 278 */ + { + 0x12133331, + 0x41212115, + 0x11241332, + }, /* 279 */ + { + 0x11224331, + 0x42121214, + 0x11241431, + }, /* 280 */ + { + 0x11225141, + 0x61212131, + 0x12151142, + }, /* 281 */ + { + 0x21231116, + 0x41212214, + 0x11242142, + }, /* 282 */ + { + 0x31231124, + 0x51212222, + 0x12151241, + }, /* 283 */ + { + 0x41231132, + 0x52121321, + 0x11242241, + }, /* 284 */ + { + 0x21231215, + 0x41212313, + 0x11251133, + }, /* 285 */ + { + 0x31231223, + 0x42121412, + 0x21251141, + }, /* 286 */ + { + 0x41231231, + 0x41212412, + 0x11251232, + }, /* 287 */ + { + 0x21231314, + 0x42121511, + 0x11251331, + }, /* 288 */ + { + 0x31231322, + 0x41212511, + 0x12161141, + }, /* 289 */ + { + 0x21231413, + 0x32122115, + 0x11252141, + }, /* 290 */ + { + 0x31231421, + 0x42122123, + 0x11261132, + }, /* 291 */ + { + 0x21231512, + 0x52122131, + 0x11261231, + }, /* 292 */ + { + 0x21231611, + 0x31213115, + 0x13111145, + }, /* 293 */ + { + 0x12141116, + 0x32122214, + 0x23111153, + }, /* 294 */ + { + 0x22141124, + 0x42122222, + 0x33111161, + }, /* 295 */ + { + 0x32141132, + 0x31213214, + 0x13111244, + }, /* 296 */ + { + 0x11232116, + 0x41213222, + 0x23111252, + }, /* 297 */ + { + 0x12141215, + 0x42122321, + 0x13111343, + }, /* 298 */ + { + 0x22141223, + 0x31213313, + 0x23111351, + }, /* 299 */ + { + 0x32141231, + 0x32122412, + 0x13111442, + }, /* 300 */ + { + 0x11232215, + 0x31213412, + 0x13111541, + }, /* 301 */ + { + 0x21232223, + 0x32122511, + 0x63112112, + }, /* 302 */ + { + 0x31232231, + 0x31213511, + 0x13112153, + }, /* 303 */ + { + 0x11232314, + 0x22123115, + 0x23112161, + }, /* 304 */ + { + 0x12141413, + 0x32123123, + 0x63112211, + }, /* 305 */ + { + 0x22141421, + 0x42123131, + 0x13112252, + }, /* 306 */ + { + 0x11232413, + 0x21214115, + 0x13112351, + }, /* 307 */ + { + 0x21232421, + 0x22123214, + 0x53113112, + }, /* 308 */ + { + 0x11232512, + 0x32123222, + 0x13113161, + }, /* 309 */ + { + 0x12142124, + 0x21214214, + 0x53113211, + }, /* 310 */ + { + 0x22142132, + 0x31214222, + 0x43114112, + }, /* 311 */ + { + 0x11233124, + 0x32123321, + 0x43114211, + }, /* 312 */ + { + 0x12142223, + 0x21214313, + 0x33115112, + }, /* 313 */ + { + 0x22142231, + 0x22123412, + 0x33115211, + }, /* 314 */ + { + 0x11233223, + 0x21214412, + 0x23116112, + }, /* 315 */ + { + 0x21233231, + 0x22123511, + 0x23116211, + }, /* 316 */ + { + 0x11233322, + 0x21214511, + 0x12211136, + }, /* 317 */ + { + 0x12142421, + 0x12124115, + 0x22211144, + }, /* 318 */ + { + 0x11233421, + 0x22124123, + 0x32211152, + }, /* 319 */ + { + 0x11234132, + 0x32124131, + 0x12211235, + }, /* 320 */ + { + 0x11234231, + 0x11215115, + 0x22211243, + }, /* 321 */ + { + 0x21241115, + 0x12124214, + 0x32211251, + }, /* 322 */ + { + 0x31241123, + 0x22124222, + 0x12211334, + }, /* 323 */ + { + 0x41241131, + 0x11215214, + 0x22211342, + }, /* 324 */ + { + 0x21241214, + 0x21215222, + 0x12211433, + }, /* 325 */ + { + 0x31241222, + 0x22124321, + 0x22211441, + }, /* 326 */ + { + 0x21241313, + 0x11215313, + 0x12211532, + }, /* 327 */ + { + 0x31241321, + 0x12124412, + 0x12211631, + }, /* 328 */ + { + 0x21241412, + 0x11215412, + 0x13121144, + }, /* 329 */ + { + 0x21241511, + 0x12124511, + 0x23121152, + }, /* 330 */ + { + 0x12151115, + 0x12125123, + 0x12212144, + }, /* 331 */ + { + 0x22151123, + 0x22125131, + 0x13121243, + }, /* 332 */ + { + 0x32151131, + 0x11216123, + 0x23121251, + }, /* 333 */ + { + 0x11242115, + 0x12125222, + 0x12212243, + }, /* 334 */ + { + 0x12151214, + 0x11216222, + 0x22212251, + }, /* 335 */ + { + 0x22151222, + 0x12125321, + 0x12212342, + }, /* 336 */ + { + 0x11242214, + 0x11216321, + 0x13121441, + }, /* 337 */ + { + 0x21242222, + 0x12126131, + 0x12212441, + }, /* 338 */ + { + 0x22151321, + 0x51221114, + 0x63122111, + }, /* 339 */ + { + 0x11242313, + 0x61221122, + 0x13122152, + }, /* 340 */ + { + 0x12151412, + 0x11221163, + 0x62213111, + }, /* 341 */ + { + 0x11242412, + 0x51221213, + 0x12213152, + }, /* 342 */ + { + 0x12151511, + 0x61221221, + 0x13122251, + }, /* 343 */ + { + 0x12152123, + 0x11221262, + 0x12213251, + }, /* 344 */ + { + 0x11243123, + 0x51221312, + 0x53123111, + }, /* 345 */ + { + 0x11243222, + 0x11221361, + 0x52214111, + }, /* 346 */ + { + 0x11243321, + 0x51221411, + 0x43124111, + }, /* 347 */ + { + 0x31251122, + 0x42131114, + 0x42215111, + }, /* 348 */ + { + 0x31251221, + 0x52131122, + 0x33125111, + }, /* 349 */ + { + 0x21251411, + 0x41222114, + 0x32216111, + }, /* 350 */ + { + 0x22161122, + 0x42131213, + 0x23126111, + }, /* 351 */ + { + 0x12161213, + 0x52131221, + 0x21311135, + }, /* 352 */ + { + 0x11252213, + 0x41222213, + 0x31311143, + }, /* 353 */ + { + 0x11252312, + 0x51222221, + 0x41311151, + }, /* 354 */ + { + 0x11252411, + 0x41222312, + 0x11311226, + }, /* 355 */ + { + 0x23111126, + 0x42131411, + 0x21311234, + }, /* 356 */ + { + 0x33111134, + 0x41222411, + 0x31311242, + }, /* 357 */ + { + 0x43111142, + 0x32132114, + 0x11311325, + }, /* 358 */ + { + 0x23111225, + 0x42132122, + 0x21311333, + }, /* 359 */ + { + 0x33111233, + 0x31223114, + 0x31311341, + }, /* 360 */ + { + 0x13111316, + 0x32132213, + 0x11311424, + }, /* 361 */ + { + 0x23111324, + 0x42132221, + 0x21311432, + }, /* 362 */ + { + 0x33111332, + 0x31223213, + 0x11311523, + }, /* 363 */ + { + 0x13111415, + 0x41223221, + 0x21311531, + }, /* 364 */ + { + 0x23111423, + 0x31223312, + 0x11311622, + }, /* 365 */ + { + 0x13111514, + 0x32132411, + 0x12221135, + }, /* 366 */ + { + 0x13111613, + 0x31223411, + 0x22221143, + }, /* 367 */ + { + 0x13112126, + 0x22133114, + 0x32221151, + }, /* 368 */ + { + 0x23112134, + 0x32133122, + 0x11312135, + }, /* 369 */ + { + 0x33112142, + 0x21224114, + 0x12221234, + }, /* 370 */ + { + 0x13112225, + 0x22133213, + 0x22221242, + }, /* 371 */ + { + 0x23112233, + 0x32133221, + 0x11312234, + }, /* 372 */ + { + 0x33112241, + 0x21224213, + 0x21312242, + }, /* 373 */ + { + 0x13112324, + 0x31224221, + 0x22221341, + }, /* 374 */ + { + 0x23112332, + 0x21224312, + 0x11312333, + }, /* 375 */ + { + 0x13112423, + 0x22133411, + 0x12221432, + }, /* 376 */ + { + 0x13112522, + 0x21224411, + 0x11312432, + }, /* 377 */ + { + 0x13113134, + 0x12134114, + 0x12221531, + }, /* 378 */ + { + 0x23113142, + 0x22134122, + 0x11312531, + }, /* 379 */ + { + 0x13113233, + 0x11225114, + 0x13131143, + }, /* 380 */ + { + 0x23113241, + 0x12134213, + 0x23131151, + }, /* 381 */ + { + 0x13113332, + 0x22134221, + 0x12222143, + }, /* 382 */ + { + 0x13114142, + 0x11225213, + 0x13131242, + }, /* 383 */ + { + 0x13114241, + 0x21225221, + 0x11313143, + }, /* 384 */ + { + 0x32211125, + 0x11225312, + 0x12222242, + }, /* 385 */ + { + 0x42211133, + 0x12134411, + 0x13131341, + }, /* 386 */ + { + 0x52211141, + 0x11225411, + 0x11313242, + }, /* 387 */ + { + 0x22211216, + 0x12135122, + 0x12222341, + }, /* 388 */ + { + 0x32211224, + 0x11226122, + 0x11313341, + }, /* 389 */ + { + 0x42211232, + 0x12135221, + 0x13132151, + }, /* 390 */ + { + 0x22211315, + 0x11226221, + 0x12223151, + }, /* 391 */ + { + 0x32211323, + 0x51231113, + 0x11314151, + }, /* 392 */ + { + 0x42211331, + 0x61231121, + 0x11321126, + }, /* 393 */ + { + 0x22211414, + 0x11231162, + 0x21321134, + }, /* 394 */ + { + 0x32211422, + 0x51231212, + 0x31321142, + }, /* 395 */ + { + 0x22211513, + 0x11231261, + 0x11321225, + }, /* 396 */ + { + 0x32211521, + 0x51231311, + 0x21321233, + }, /* 397 */ + { + 0x23121125, + 0x42141113, + 0x31321241, + }, /* 398 */ + { + 0x33121133, + 0x52141121, + 0x11321324, + }, /* 399 */ + { + 0x43121141, + 0x41232113, + 0x21321332, + }, /* 400 */ + { + 0x22212125, + 0x51232121, + 0x11321423, + }, /* 401 */ + { + 0x23121224, + 0x41232212, + 0x21321431, + }, /* 402 */ + { + 0x33121232, + 0x42141311, + 0x11321522, + }, /* 403 */ + { + 0x12212216, + 0x41232311, + 0x11321621, + }, /* 404 */ + { + 0x13121315, + 0x32142113, + 0x12231134, + }, /* 405 */ + { + 0x32212232, + 0x42142121, + 0x22231142, + }, /* 406 */ + { + 0x33121331, + 0x31233113, + 0x11322134, + }, /* 407 */ + { + 0x12212315, + 0x32142212, + 0x12231233, + }, /* 408 */ + { + 0x22212323, + 0x31233212, + 0x22231241, + }, /* 409 */ + { + 0x23121422, + 0x32142311, + 0x11322233, + }, /* 410 */ + { + 0x12212414, + 0x31233311, + 0x21322241, + }, /* 411 */ + { + 0x13121513, + 0x22143113, + 0x11322332, + }, /* 412 */ + { + 0x12212513, + 0x32143121, + 0x12231431, + }, /* 413 */ + { + 0x13122125, + 0x21234113, + 0x11322431, + }, /* 414 */ + { + 0x23122133, + 0x31234121, + 0x13141142, + }, /* 415 */ + { + 0x33122141, + 0x21234212, + 0x12232142, + }, /* 416 */ + { + 0x12213125, + 0x22143311, + 0x13141241, + }, /* 417 */ + { + 0x13122224, + 0x21234311, + 0x11323142, + }, /* 418 */ + { + 0x32213141, + 0x12144113, + 0x12232241, + }, /* 419 */ + { + 0x12213224, + 0x22144121, + 0x11323241, + }, /* 420 */ + { + 0x22213232, + 0x11235113, + 0x11331125, + }, /* 421 */ + { + 0x23122331, + 0x12144212, + 0x21331133, + }, /* 422 */ + { + 0x12213323, + 0x11235212, + 0x31331141, + }, /* 423 */ + { + 0x13122422, + 0x12144311, + 0x11331224, + }, /* 424 */ + { + 0x12213422, + 0x11235311, + 0x21331232, + }, /* 425 */ + { + 0x13123133, + 0x12145121, + 0x11331323, + }, /* 426 */ + { + 0x23123141, + 0x11236121, + 0x21331331, + }, /* 427 */ + { + 0x12214133, + 0x51241112, + 0x11331422, + }, /* 428 */ + { + 0x13123232, + 0x11241161, + 0x11331521, + }, /* 429 */ + { + 0x12214232, + 0x51241211, + 0x12241133, + }, /* 430 */ + { + 0x13123331, + 0x42151112, + 0x22241141, + }, /* 431 */ + { + 0x13124141, + 0x41242112, + 0x11332133, + }, /* 432 */ + { + 0x12215141, + 0x42151211, + 0x12241232, + }, /* 433 */ + { + 0x31311116, + 0x41242211, + 0x11332232, + }, /* 434 */ + { + 0x41311124, + 0x32152112, + 0x12241331, + }, /* 435 */ + { + 0x51311132, + 0x31243112, + 0x11332331, + }, /* 436 */ + { + 0x31311215, + 0x32152211, + 0x13151141, + }, /* 437 */ + { + 0x41311223, + 0x31243211, + 0x12242141, + }, /* 438 */ + { + 0x51311231, + 0x22153112, + 0x11333141, + }, /* 439 */ + { + 0x31311314, + 0x21244112, + 0x11341124, + }, /* 440 */ + { + 0x41311322, + 0x22153211, + 0x21341132, + }, /* 441 */ + { + 0x31311413, + 0x21244211, + 0x11341223, + }, /* 442 */ + { + 0x41311421, + 0x12154112, + 0x21341231, + }, /* 443 */ + { + 0x31311512, + 0x11245112, + 0x11341322, + }, /* 444 */ + { + 0x22221116, + 0x12154211, + 0x11341421, + }, /* 445 */ + { + 0x32221124, + 0x11245211, + 0x12251132, + }, /* 446 */ + { + 0x42221132, + 0x51251111, + 0x11342132, + }, /* 447 */ + { + 0x21312116, + 0x42161111, + 0x12251231, + }, /* 448 */ + { + 0x22221215, + 0x41252111, + 0x11342231, + }, /* 449 */ + { + 0x41312132, + 0x32162111, + 0x11351123, + }, /* 450 */ + { + 0x42221231, + 0x31253111, + 0x21351131, + }, /* 451 */ + { + 0x21312215, + 0x22163111, + 0x11351222, + }, /* 452 */ + { + 0x31312223, + 0x21254111, + 0x11351321, + }, /* 453 */ + { + 0x41312231, + 0x43111115, + 0x12261131, + }, /* 454 */ + { + 0x21312314, + 0x53111123, + 0x11352131, + }, /* 455 */ + { + 0x22221413, + 0x63111131, + 0x11361122, + }, /* 456 */ + { + 0x32221421, + 0x43111214, + 0x11361221, + }, /* 457 */ + { + 0x21312413, + 0x53111222, + 0x14111144, + }, /* 458 */ + { + 0x31312421, + 0x43111313, + 0x24111152, + }, /* 459 */ + { + 0x22221611, + 0x53111321, + 0x14111243, + }, /* 460 */ + { + 0x13131116, + 0x43111412, + 0x24111251, + }, /* 461 */ + { + 0x23131124, + 0x43111511, + 0x14111342, + }, /* 462 */ + { + 0x33131132, + 0x33112115, + 0x14111441, + }, /* 463 */ + { + 0x12222116, + 0x43112123, + 0x14112152, + }, /* 464 */ + { + 0x13131215, + 0x53112131, + 0x14112251, + }, /* 465 */ + { + 0x23131223, + 0x33112214, + 0x54113111, + }, /* 466 */ + { + 0x33131231, + 0x43112222, + 0x44114111, + }, /* 467 */ + { + 0x11313116, + 0x33112313, + 0x34115111, + }, /* 468 */ + { + 0x12222215, + 0x43112321, + 0x24116111, + }, /* 469 */ + { + 0x22222223, + 0x33112412, + 0x13211135, + }, /* 470 */ + { + 0x32222231, + 0x33112511, + 0x23211143, + }, /* 471 */ + { + 0x11313215, + 0x23113115, + 0x33211151, + }, /* 472 */ + { + 0x21313223, + 0x33113123, + 0x13211234, + }, /* 473 */ + { + 0x31313231, + 0x43113131, + 0x23211242, + }, /* 474 */ + { + 0x23131421, + 0x23113214, + 0x13211333, + }, /* 475 */ + { + 0x11313314, + 0x33113222, + 0x23211341, + }, /* 476 */ + { + 0x12222413, + 0x23113313, + 0x13211432, + }, /* 477 */ + { + 0x22222421, + 0x33113321, + 0x13211531, + }, /* 478 */ + { + 0x11313413, + 0x23113412, + 0x14121143, + }, /* 479 */ + { + 0x13131611, + 0x23113511, + 0x24121151, + }, /* 480 */ + { + 0x13132124, + 0x13114115, + 0x13212143, + }, /* 481 */ + { + 0x23132132, + 0x23114123, + 0x14121242, + }, /* 482 */ + { + 0x12223124, + 0x33114131, + 0x13212242, + }, /* 483 */ + { + 0x13132223, + 0x13114214, + 0x14121341, + }, /* 484 */ + { + 0x23132231, + 0x23114222, + 0x13212341, + }, /* 485 */ + { + 0x11314124, + 0x13114313, + 0x14122151, + }, /* 486 */ + { + 0x12223223, + 0x23114321, + 0x13213151, + }, /* 487 */ + { + 0x22223231, + 0x13114412, + 0x12311126, + }, /* 488 */ + { + 0x11314223, + 0x13114511, + 0x22311134, + }, /* 489 */ + { + 0x21314231, + 0x13115123, + 0x32311142, + }, /* 490 */ + { + 0x13132421, + 0x23115131, + 0x12311225, + }, /* 491 */ + { + 0x12223421, + 0x13115222, + 0x22311233, + }, /* 492 */ + { + 0x13133132, + 0x13115321, + 0x32311241, + }, /* 493 */ + { + 0x12224132, + 0x13116131, + 0x12311324, + }, /* 494 */ + { + 0x13133231, + 0x52211114, + 0x22311332, + }, /* 495 */ + { + 0x11315132, + 0x62211122, + 0x12311423, + }, /* 496 */ + { + 0x12224231, + 0x12211163, + 0x22311431, + }, /* 497 */ + { + 0x31321115, + 0x52211213, + 0x12311522, + }, /* 498 */ + { + 0x41321123, + 0x62211221, + 0x12311621, + }, /* 499 */ + { + 0x51321131, + 0x12211262, + 0x13221134, + }, /* 500 */ + { + 0x31321214, + 0x52211312, + 0x23221142, + }, /* 501 */ + { + 0x41321222, + 0x12211361, + 0x12312134, + }, /* 502 */ + { + 0x31321313, + 0x52211411, + 0x13221233, + }, /* 503 */ + { + 0x41321321, + 0x43121114, + 0x23221241, + }, /* 504 */ + { + 0x31321412, + 0x53121122, + 0x12312233, + }, /* 505 */ + { + 0x31321511, + 0x42212114, + 0x13221332, + }, /* 506 */ + { + 0x22231115, + 0x43121213, + 0x12312332, + }, /* 507 */ + { + 0x32231123, + 0x53121221, + 0x13221431, + }, /* 508 */ + { + 0x42231131, + 0x42212213, + 0x12312431, + }, /* 509 */ + { + 0x21322115, + 0x52212221, + 0x14131142, + }, /* 510 */ + { + 0x22231214, + 0x42212312, + 0x13222142, + }, /* 511 */ + { + 0x41322131, + 0x43121411, + 0x14131241, + }, /* 512 */ + { + 0x21322214, + 0x42212411, + 0x12313142, + }, /* 513 */ + { + 0x31322222, + 0x33122114, + 0x13222241, + }, /* 514 */ + { + 0x32231321, + 0x43122122, + 0x12313241, + }, /* 515 */ + { + 0x21322313, + 0x32213114, + 0x21411125, + }, /* 516 */ + { + 0x22231412, + 0x33122213, + 0x31411133, + }, /* 517 */ + { + 0x21322412, + 0x43122221, + 0x41411141, + }, /* 518 */ + { + 0x22231511, + 0x32213213, + 0x11411216, + }, /* 519 */ + { + 0x21322511, + 0x42213221, + 0x21411224, + }, /* 520 */ + { + 0x13141115, + 0x32213312, + 0x31411232, + }, /* 521 */ + { + 0x23141123, + 0x33122411, + 0x11411315, + }, /* 522 */ + { + 0x33141131, + 0x32213411, + 0x21411323, + }, /* 523 */ + { + 0x12232115, + 0x23123114, + 0x31411331, + }, /* 524 */ + { + 0x13141214, + 0x33123122, + 0x11411414, + }, /* 525 */ + { + 0x23141222, + 0x22214114, + 0x21411422, + }, /* 526 */ + { + 0x11323115, + 0x23123213, + 0x11411513, + }, /* 527 */ + { + 0x12232214, + 0x33123221, + 0x21411521, + }, /* 528 */ + { + 0x22232222, + 0x22214213, + 0x11411612, + }, /* 529 */ + { + 0x23141321, + 0x32214221, + 0x12321125, + }, /* 530 */ + { + 0x11323214, + 0x22214312, + 0x22321133, + }, /* 531 */ + { + 0x21323222, + 0x23123411, + 0x32321141, + }, /* 532 */ + { + 0x13141412, + 0x22214411, + 0x11412125, + }, /* 533 */ + { + 0x11323313, + 0x13124114, + 0x12321224, + }, /* 534 */ + { + 0x12232412, + 0x23124122, + 0x22321232, + }, /* 535 */ + { + 0x13141511, + 0x12215114, + 0x11412224, + }, /* 536 */ + { + 0x12232511, + 0x13124213, + 0x21412232, + }, /* 537 */ + { + 0x13142123, + 0x23124221, + 0x22321331, + }, /* 538 */ + { + 0x23142131, + 0x12215213, + 0x11412323, + }, /* 539 */ + { + 0x12233123, + 0x22215221, + 0x12321422, + }, /* 540 */ + { + 0x13142222, + 0x12215312, + 0x11412422, + }, /* 541 */ + { + 0x11324123, + 0x13124411, + 0x12321521, + }, /* 542 */ + { + 0x12233222, + 0x12215411, + 0x11412521, + }, /* 543 */ + { + 0x13142321, + 0x13125122, + 0x13231133, + }, /* 544 */ + { + 0x11324222, + 0x12216122, + 0x23231141, + }, /* 545 */ + { + 0x12233321, + 0x13125221, + 0x12322133, + }, /* 546 */ + { + 0x13143131, + 0x12216221, + 0x13231232, + }, /* 547 */ + { + 0x11325131, + 0x61311113, + 0x11413133, + }, /* 548 */ + { + 0x31331114, + 0x11311154, + 0x12322232, + }, /* 549 */ + { + 0x41331122, + 0x21311162, + 0x13231331, + }, /* 550 */ + { + 0x31331213, + 0x61311212, + 0x11413232, + }, /* 551 */ + { + 0x41331221, + 0x11311253, + 0x12322331, + }, /* 552 */ + { + 0x31331312, + 0x21311261, + 0x11413331, + }, /* 553 */ + { + 0x31331411, + 0x61311311, + 0x14141141, + }, /* 554 */ + { + 0x22241114, + 0x11311352, + 0x13232141, + }, /* 555 */ + { + 0x32241122, + 0x11311451, + 0x12323141, + }, /* 556 */ + { + 0x21332114, + 0x52221113, + 0x11414141, + }, /* 557 */ + { + 0x22241213, + 0x62221121, + 0x11421116, + }, /* 558 */ + { + 0x32241221, + 0x12221162, + 0x21421124, + }, /* 559 */ + { + 0x21332213, + 0x51312113, + 0x31421132, + }, /* 560 */ + { + 0x31332221, + 0x61312121, + 0x11421215, + }, /* 561 */ + { + 0x21332312, + 0x11312162, + 0x21421223, + }, /* 562 */ + { + 0x22241411, + 0x12221261, + 0x31421231, + }, /* 563 */ + { + 0x21332411, + 0x51312212, + 0x11421314, + }, /* 564 */ + { + 0x13151114, + 0x52221311, + 0x21421322, + }, /* 565 */ + { + 0x23151122, + 0x11312261, + 0x11421413, + }, /* 566 */ + { + 0x12242114, + 0x51312311, + 0x21421421, + }, /* 567 */ + { + 0x13151213, + 0x43131113, + 0x11421512, + }, /* 568 */ + { + 0x23151221, + 0x53131121, + 0x11421611, + }, /* 569 */ + { + 0x11333114, + 0x42222113, + 0x12331124, + }, /* 570 */ + { + 0x12242213, + 0x43131212, + 0x22331132, + }, /* 571 */ + { + 0x22242221, + 0x41313113, + 0x11422124, + }, /* 572 */ + { + 0x11333213, + 0x51313121, + 0x12331223, + }, /* 573 */ + { + 0x21333221, + 0x43131311, + 0x22331231, + }, /* 574 */ + { + 0x13151411, + 0x41313212, + 0x11422223, + }, /* 575 */ + { + 0x11333312, + 0x42222311, + 0x21422231, + }, /* 576 */ + { + 0x12242411, + 0x41313311, + 0x11422322, + }, /* 577 */ + { + 0x11333411, + 0x33132113, + 0x12331421, + }, /* 578 */ + { + 0x12243122, + 0x43132121, + 0x11422421, + }, /* 579 */ + { + 0x11334122, + 0x32223113, + 0x13241132, + }, /* 580 */ + { + 0x11334221, + 0x33132212, + 0x12332132, + }, /* 581 */ + { + 0x41341121, + 0x31314113, + 0x13241231, + }, /* 582 */ + { + 0x31341311, + 0x32223212, + 0x11423132, + }, /* 583 */ + { + 0x32251121, + 0x33132311, + 0x12332231, + }, /* 584 */ + { + 0x22251212, + 0x31314212, + 0x11423231, + }, /* 585 */ + { + 0x22251311, + 0x32223311, + 0x11431115, + }, /* 586 */ + { + 0x13161113, + 0x31314311, + 0x21431123, + }, /* 587 */ + { + 0x12252113, + 0x23133113, + 0x31431131, + }, /* 588 */ + { + 0x11343113, + 0x33133121, + 0x11431214, + }, /* 589 */ + { + 0x13161311, + 0x22224113, + 0x21431222, + }, /* 590 */ + { + 0x12252311, + 0x23133212, + 0x11431313, + }, /* 591 */ + { + 0x24111125, + 0x21315113, + 0x21431321, + }, /* 592 */ + { + 0x14111216, + 0x22224212, + 0x11431412, + }, /* 593 */ + { + 0x24111224, + 0x23133311, + 0x11431511, + }, /* 594 */ + { + 0x14111315, + 0x21315212, + 0x12341123, + }, /* 595 */ + { + 0x24111323, + 0x22224311, + 0x22341131, + }, /* 596 */ + { + 0x34111331, + 0x21315311, + 0x11432123, + }, /* 597 */ + { + 0x14111414, + 0x13134113, + 0x12341222, + }, /* 598 */ + { + 0x24111422, + 0x23134121, + 0x11432222, + }, /* 599 */ + { + 0x14111513, + 0x12225113, + 0x12341321, + }, /* 600 */ + { + 0x24111521, + 0x13134212, + 0x11432321, + }, /* 601 */ + { + 0x14112125, + 0x11316113, + 0x13251131, + }, /* 602 */ + { + 0x24112133, + 0x12225212, + 0x12342131, + }, /* 603 */ + { + 0x34112141, + 0x13134311, + 0x11433131, + }, /* 604 */ + { + 0x14112224, + 0x11316212, + 0x11441114, + }, /* 605 */ + { + 0x24112232, + 0x12225311, + 0x21441122, + }, /* 606 */ + { + 0x14112323, + 0x11316311, + 0x11441213, + }, /* 607 */ + { + 0x24112331, + 0x13135121, + 0x21441221, + }, /* 608 */ + { + 0x14112422, + 0x12226121, + 0x11441312, + }, /* 609 */ + { + 0x14112521, + 0x61321112, + 0x11441411, + }, /* 610 */ + { + 0x14113133, + 0x11321153, + 0x12351122, + }, /* 611 */ + { + 0x24113141, + 0x21321161, + 0x11442122, + }, /* 612 */ + { + 0x14113232, + 0x61321211, + 0x12351221, + }, /* 613 */ + { + 0x14113331, + 0x11321252, + 0x11442221, + }, /* 614 */ + { + 0x14114141, + 0x11321351, + 0x11451113, + }, /* 615 */ + { + 0x23211116, + 0x52231112, + 0x21451121, + }, /* 616 */ + { + 0x33211124, + 0x12231161, + 0x11451212, + }, /* 617 */ + { + 0x43211132, + 0x51322112, + 0x11451311, + }, /* 618 */ + { + 0x23211215, + 0x52231211, + 0x12361121, + }, /* 619 */ + { + 0x33211223, + 0x11322161, + 0x11452121, + }, /* 620 */ + { + 0x23211314, + 0x51322211, + 0x15111143, + }, /* 621 */ + { + 0x33211322, + 0x43141112, + 0x25111151, + }, /* 622 */ + { + 0x23211413, + 0x42232112, + 0x15111242, + }, /* 623 */ + { + 0x33211421, + 0x43141211, + 0x15111341, + }, /* 624 */ + { + 0x23211512, + 0x41323112, + 0x15112151, + }, /* 625 */ + { + 0x14121116, + 0x42232211, + 0x14211134, + }, /* 626 */ + { + 0x24121124, + 0x41323211, + 0x24211142, + }, /* 627 */ + { + 0x34121132, + 0x33142112, + 0x14211233, + }, /* 628 */ + { + 0x13212116, + 0x32233112, + 0x24211241, + }, /* 629 */ + { + 0x14121215, + 0x33142211, + 0x14211332, + }, /* 630 */ + { + 0x33212132, + 0x31324112, + 0x14211431, + }, /* 631 */ + { + 0x34121231, + 0x32233211, + 0x15121142, + }, /* 632 */ + { + 0x13212215, + 0x31324211, + 0x14212142, + }, /* 633 */ + { + 0x23212223, + 0x23143112, + 0x15121241, + }, /* 634 */ + { + 0x33212231, + 0x22234112, + 0x14212241, + }, /* 635 */ + { + 0x13212314, + 0x23143211, + 0x13311125, + }, /* 636 */ + { + 0x14121413, + 0x21325112, + 0x23311133, + }, /* 637 */ + { + 0x24121421, + 0x22234211, + 0x33311141, + }, /* 638 */ + { + 0x13212413, + 0x21325211, + 0x13311224, + }, /* 639 */ + { + 0x23212421, + 0x13144112, + 0x23311232, + }, /* 640 */ + { + 0x14121611, + 0x12235112, + 0x13311323, + }, /* 641 */ + { + 0x14122124, + 0x13144211, + 0x23311331, + }, /* 642 */ + { + 0x24122132, + 0x11326112, + 0x13311422, + }, /* 643 */ + { + 0x13213124, + 0x12235211, + 0x13311521, + }, /* 644 */ + { + 0x14122223, + 0x11326211, + 0x14221133, + }, /* 645 */ + { + 0x24122231, + 0x61331111, + 0x24221141, + }, /* 646 */ + { + 0x13213223, + 0x11331152, + 0x13312133, + }, /* 647 */ + { + 0x23213231, + 0x11331251, + 0x14221232, + }, /* 648 */ + { + 0x13213322, + 0x52241111, + 0x13312232, + }, /* 649 */ + { + 0x14122421, + 0x51332111, + 0x14221331, + }, /* 650 */ + { + 0x14123132, + 0x43151111, + 0x13312331, + }, /* 651 */ + { + 0x13214132, + 0x42242111, + 0x15131141, + }, /* 652 */ + { + 0x14123231, + 0x41333111, + 0x14222141, + }, /* 653 */ + { + 0x13214231, + 0x33152111, + 0x13313141, + }, /* 654 */ + { + 0x32311115, + 0x32243111, + 0x12411116, + }, /* 655 */ + { + 0x42311123, + 0x31334111, + 0x22411124, + }, /* 656 */ + { + 0x52311131, + 0x23153111, + 0x32411132, + }, /* 657 */ + { + 0x32311214, + 0x22244111, + 0x12411215, + }, /* 658 */ + { + 0x42311222, + 0x21335111, + 0x22411223, + }, /* 659 */ + { + 0x32311313, + 0x13154111, + 0x32411231, + }, /* 660 */ + { + 0x42311321, + 0x12245111, + 0x12411314, + }, /* 661 */ + { + 0x32311412, + 0x11336111, + 0x22411322, + }, /* 662 */ + { + 0x32311511, + 0x11341151, + 0x12411413, + }, /* 663 */ + { + 0x23221115, + 0x44111114, + 0x22411421, + }, /* 664 */ + { + 0x33221123, + 0x54111122, + 0x12411512, + }, /* 665 */ + { + 0x22312115, + 0x44111213, + 0x12411611, + }, /* 666 */ + { + 0x23221214, + 0x54111221, + 0x13321124, + }, /* 667 */ + { + 0x33221222, + 0x44111312, + 0x23321132, + }, /* 668 */ + { + 0x22312214, + 0x44111411, + 0x12412124, + }, /* 669 */ + { + 0x32312222, + 0x34112114, + 0x13321223, + }, /* 670 */ + { + 0x33221321, + 0x44112122, + 0x23321231, + }, /* 671 */ + { + 0x22312313, + 0x34112213, + 0x12412223, + }, /* 672 */ + { + 0x23221412, + 0x44112221, + 0x22412231, + }, /* 673 */ + { + 0x22312412, + 0x34112312, + 0x12412322, + }, /* 674 */ + { + 0x23221511, + 0x34112411, + 0x13321421, + }, /* 675 */ + { + 0x22312511, + 0x24113114, + 0x12412421, + }, /* 676 */ + { + 0x14131115, + 0x34113122, + 0x14231132, + }, /* 677 */ + { + 0x24131123, + 0x24113213, + 0x13322132, + }, /* 678 */ + { + 0x13222115, + 0x34113221, + 0x14231231, + }, /* 679 */ + { + 0x14131214, + 0x24113312, + 0x12413132, + }, /* 680 */ + { + 0x33222131, + 0x24113411, + 0x13322231, + }, /* 681 */ + { + 0x12313115, + 0x14114114, + 0x12413231, + }, /* 682 */ + { + 0x13222214, + 0x24114122, + 0x21511115, + }, /* 683 */ + { + 0x23222222, + 0x14114213, + 0x31511123, + }, /* 684 */ + { + 0x24131321, + 0x24114221, + 0x41511131, + }, /* 685 */ + { + 0x12313214, + 0x14114312, + 0x21511214, + }, /* 686 */ + { + 0x22313222, + 0x14114411, + 0x31511222, + }, /* 687 */ + { + 0x14131412, + 0x14115122, + 0x21511313, + }, /* 688 */ + { + 0x12313313, + 0x14115221, + 0x31511321, + }, /* 689 */ + { + 0x13222412, + 0x53211113, + 0x21511412, + }, /* 690 */ + { + 0x14131511, + 0x63211121, + 0x21511511, + }, /* 691 */ + { + 0x13222511, + 0x13211162, + 0x12421115, + }, /* 692 */ + { + 0x14132123, + 0x53211212, + 0x22421123, + }, /* 693 */ + { + 0x24132131, + 0x13211261, + 0x32421131, + }, /* 694 */ + { + 0x13223123, + 0x53211311, + 0x11512115, + }, /* 695 */ + { + 0x14132222, + 0x44121113, + 0x12421214, + }, /* 696 */ + { + 0x12314123, + 0x54121121, + 0x22421222, + }, /* 697 */ + { + 0x13223222, + 0x43212113, + 0x11512214, + }, /* 698 */ + { + 0x14132321, + 0x44121212, + 0x21512222, + }, /* 699 */ + { + 0x12314222, + 0x43212212, + 0x22421321, + }, /* 700 */ + { + 0x13223321, + 0x44121311, + 0x11512313, + }, /* 701 */ + { + 0x14133131, + 0x43212311, + 0x12421412, + }, /* 702 */ + { + 0x13224131, + 0x34122113, + 0x11512412, + }, /* 703 */ + { + 0x12315131, + 0x44122121, + 0x12421511, + }, /* 704 */ + { + 0x41411114, + 0x33213113, + 0x11512511, + }, /* 705 */ + { + 0x51411122, + 0x34122212, + 0x13331123, + }, /* 706 */ + { + 0x41411213, + 0x33213212, + 0x23331131, + }, /* 707 */ + { + 0x51411221, + 0x34122311, + 0x12422123, + }, /* 708 */ + { + 0x41411312, + 0x33213311, + 0x13331222, + }, /* 709 */ + { + 0x41411411, + 0x24123113, + 0x11513123, + }, /* 710 */ + { + 0x32321114, + 0x34123121, + 0x12422222, + }, /* 711 */ + { + 0x42321122, + 0x23214113, + 0x13331321, + }, /* 712 */ + { + 0x31412114, + 0x24123212, + 0x11513222, + }, /* 713 */ + { + 0x41412122, + 0x23214212, + 0x12422321, + }, /* 714 */ + { + 0x42321221, + 0x24123311, + 0x11513321, + }, /* 715 */ + { + 0x31412213, + 0x23214311, + 0x14241131, + }, /* 716 */ + { + 0x41412221, + 0x14124113, + 0x13332131, + }, /* 717 */ + { + 0x31412312, + 0x24124121, + 0x12423131, + }, /* 718 */ + { + 0x32321411, + 0x13215113, + 0x11514131, + }, /* 719 */ + { + 0x31412411, + 0x14124212, + 0x21521114, + }, /* 720 */ + { + 0x23231114, + 0x13215212, + 0x31521122, + }, /* 721 */ + { + 0x33231122, + 0x14124311, + 0x21521213, + }, /* 722 */ + { + 0x22322114, + 0x13215311, + 0x31521221, + }, /* 723 */ + { + 0x23231213, + 0x14125121, + 0x21521312, + }, /* 724 */ + { + 0x33231221, + 0x13216121, + 0x21521411, + }, /* 725 */ + { + 0x21413114, + 0x62311112, + 0x12431114, + }, /* 726 */ + { + 0x22322213, + 0x12311153, + 0x22431122, + }, /* 727 */ + { + 0x32322221, + 0x22311161, + 0x11522114, + }, /* 728 */ + { + 0x21413213, + 0x62311211, + 0x12431213, + }, /* 729 */ + { + 0x31413221, + 0x12311252, + 0x22431221, + }, /* 730 */ + { + 0x23231411, + 0x12311351, + 0x11522213, + }, /* 731 */ + { + 0x21413312, + 0x53221112, + 0x21522221, + }, /* 732 */ + { + 0x22322411, + 0x13221161, + 0x11522312, + }, /* 733 */ + { + 0x21413411, + 0x52312112, + 0x12431411, + }, /* 734 */ + { + 0x14141114, + 0x53221211, + 0x11522411, + }, /* 735 */ + { + 0x24141122, + 0x12312161, + 0x13341122, + }, /* 736 */ + { + 0x13232114, + 0x52312211, + 0x12432122, + }, /* 737 */ + { + 0x14141213, + 0x44131112, + 0x13341221, + }, /* 738 */ + { + 0x24141221, + 0x43222112, + 0x11523122, + }, /* 739 */ + { + 0x12323114, + 0x44131211, + 0x12432221, + }, /* 740 */ + { + 0x13232213, + 0x42313112, + 0x11523221, + }, /* 741 */ + { + 0x23232221, + 0x43222211, + 0x21531113, + }, /* 742 */ + { + 0x11414114, + 0x42313211, + 0x31531121, + }, /* 743 */ + { + 0x12323213, + 0x34132112, + 0x21531212, + }, /* 744 */ + { + 0x22323221, + 0x33223112, + 0x21531311, + }, /* 745 */ + { + 0x14141411, + 0x34132211, + 0x12441113, + }, /* 746 */ + { + 0x11414213, + 0x32314112, + 0x22441121, + }, /* 747 */ + { + 0x21414221, + 0x33223211, + 0x11532113, + }, /* 748 */ + { + 0x13232411, + 0x32314211, + 0x12441212, + }, /* 749 */ + { + 0x11414312, + 0x24133112, + 0x11532212, + }, /* 750 */ + { + 0x14142122, + 0x23224112, + 0x12441311, + }, /* 751 */ + { + 0x13233122, + 0x24133211, + 0x11532311, + }, /* 752 */ + { + 0x14142221, + 0x22315112, + 0x13351121, + }, /* 753 */ + { + 0x12324122, + 0x23224211, + 0x12442121, + }, /* 754 */ + { + 0x13233221, + 0x22315211, + 0x11533121, + }, /* 755 */ + { + 0x11415122, + 0x14134112, + 0x21541112, + }, /* 756 */ + { + 0x12324221, + 0x13225112, + 0x21541211, + }, /* 757 */ + { + 0x11415221, + 0x14134211, + 0x12451112, + }, /* 758 */ + { + 0x41421113, + 0x12316112, + 0x11542112, + }, /* 759 */ + { + 0x51421121, + 0x13225211, + 0x12451211, + }, /* 760 */ + { + 0x41421212, + 0x12316211, + 0x11542211, + }, /* 761 */ + { + 0x41421311, + 0x11411144, + 0x16111142, + }, /* 762 */ + { + 0x32331113, + 0x21411152, + 0x16111241, + }, /* 763 */ + { + 0x42331121, + 0x11411243, + 0x15211133, + }, /* 764 */ + { + 0x31422113, + 0x21411251, + 0x25211141, + }, /* 765 */ + { + 0x41422121, + 0x11411342, + 0x15211232, + }, /* 766 */ + { + 0x31422212, + 0x11411441, + 0x15211331, + }, /* 767 */ + { + 0x32331311, + 0x62321111, + 0x16121141, + }, /* 768 */ + { + 0x31422311, + 0x12321152, + 0x15212141, + }, /* 769 */ + { + 0x23241113, + 0x61412111, + 0x14311124, + }, /* 770 */ + { + 0x33241121, + 0x11412152, + 0x24311132, + }, /* 771 */ + { + 0x22332113, + 0x12321251, + 0x14311223, + }, /* 772 */ + { + 0x23241212, + 0x11412251, + 0x24311231, + }, /* 773 */ + { + 0x21423113, + 0x53231111, + 0x14311322, + }, /* 774 */ + { + 0x22332212, + 0x52322111, + 0x14311421, + }, /* 775 */ + { + 0x23241311, + 0x51413111, + 0x15221132, + }, /* 776 */ + { + 0x21423212, + 0x44141111, + 0x14312132, + }, /* 777 */ + { + 0x22332311, + 0x43232111, + 0x15221231, + }, /* 778 */ + { + 0x21423311, + 0x42323111, + 0x14312231, + }, /* 779 */ + { + 0x14151113, + 0x41414111, + 0x13411115, + }, /* 780 */ + { + 0x24151121, + 0x34142111, + 0x23411123, + }, /* 781 */ + { + 0x13242113, + 0x33233111, + 0x33411131, + }, /* 782 */ + { + 0x23242121, + 0x32324111, + 0x13411214, + }, /* 783 */ + { + 0x12333113, + 0x31415111, + 0x23411222, + }, /* 784 */ + { + 0x13242212, + 0x24143111, + 0x13411313, + }, /* 785 */ + { + 0x14151311, + 0x23234111, + 0x23411321, + }, /* 786 */ + { + 0x11424113, + 0x22325111, + 0x13411412, + }, /* 787 */ + { + 0x12333212, + 0x21416111, + 0x13411511, + }, /* 788 */ + { + 0x13242311, + 0x14144111, + 0x14321123, + }, /* 789 */ + { + 0x11424212, + 0x13235111, + 0x24321131, + }, /* 790 */ + { + 0x12333311, + 0x12326111, + 0x13412123, + }, /* 791 */ + { + 0x11424311, + 0x11421143, + 0x23412131, + }, /* 792 */ + { + 0x13243121, + 0x21421151, + 0x13412222, + }, /* 793 */ + { + 0x11425121, + 0x11421242, + 0x14321321, + }, /* 794 */ + { + 0x41431211, + 0x11421341, + 0x13412321, + }, /* 795 */ + { + 0x31432112, + 0x12331151, + 0x15231131, + }, /* 796 */ + { + 0x31432211, + 0x11422151, + 0x14322131, + }, /* 797 */ + { + 0x22342112, + 0x11431142, + 0x13413131, + }, /* 798 */ + { + 0x21433112, + 0x11431241, + 0x22511114, + }, /* 799 */ + { + 0x21433211, + 0x11441141, + 0x32511122, + }, /* 800 */ + { + 0x13252112, + 0x45111113, + 0x22511213, + }, /* 801 */ + { + 0x12343112, + 0x45111212, + 0x32511221, + }, /* 802 */ + { + 0x11434112, + 0x45111311, + 0x22511312, + }, /* 803 */ + { + 0x11434211, + 0x35112113, + 0x22511411, + }, /* 804 */ + { + 0x15111116, + 0x45112121, + 0x13421114, + }, /* 805 */ + { + 0x15111215, + 0x35112212, + 0x23421122, + }, /* 806 */ + { + 0x25111223, + 0x35112311, + 0x12512114, + }, /* 807 */ + { + 0x15111314, + 0x25113113, + 0x22512122, + }, /* 808 */ + { + 0x15111413, + 0x35113121, + 0x23421221, + }, /* 809 */ + { + 0x15111512, + 0x25113212, + 0x12512213, + }, /* 810 */ + { + 0x15112124, + 0x25113311, + 0x13421312, + }, /* 811 */ + { + 0x15112223, + 0x15114113, + 0x12512312, + }, /* 812 */ + { + 0x15112322, + 0x25114121, + 0x13421411, + }, /* 813 */ + { + 0x15112421, + 0x15114212, + 0x12512411, + }, /* 814 */ + { + 0x15113132, + 0x15114311, + 0x14331122, + }, /* 815 */ + { + 0x15113231, + 0x15115121, + 0x13422122, + }, /* 816 */ + { + 0x24211115, + 0x54211112, + 0x14331221, + }, /* 817 */ + { + 0x24211214, + 0x14211161, + 0x12513122, + }, /* 818 */ + { + 0x34211222, + 0x54211211, + 0x13422221, + }, /* 819 */ + { + 0x24211313, + 0x45121112, + 0x12513221, + }, /* 820 */ + { + 0x34211321, + 0x44212112, + 0x31611113, + }, /* 821 */ + { + 0x24211412, + 0x45121211, + 0x41611121, + }, /* 822 */ + { + 0x24211511, + 0x44212211, + 0x31611212, + }, /* 823 */ + { + 0x15121115, + 0x35122112, + 0x31611311, + }, /* 824 */ + { + 0x25121123, + 0x34213112, + 0x22521113, + }, /* 825 */ + { + 0x14212115, + 0x35122211, + 0x32521121, + }, /* 826 */ + { + 0x24212123, + 0x34213211, + 0x21612113, + }, /* 827 */ + { + 0x25121222, + 0x25123112, + 0x22521212, + }, /* 828 */ + { + 0x14212214, + 0x24214112, + 0x21612212, + }, /* 829 */ + { + 0x24212222, + 0x25123211, + 0x22521311, + }, /* 830 */ + { + 0x14212313, + 0x24214211, + 0x21612311, + }, /* 831 */ + { + 0x24212321, + 0x15124112, + 0x13431113, + }, /* 832 */ + { + 0x14212412, + 0x14215112, + 0x23431121, + }, /* 833 */ + { + 0x15121511, + 0x15124211, + 0x12522113, + }, /* 834 */ + { + 0x14212511, + 0x14215211, + 0x13431212, + }, /* 835 */ + { + 0x15122123, + 0x63311111, + 0x11613113, + }, /* 836 */ + { + 0x25122131, + 0x13311152, + 0x12522212, + }, /* 837 */ + { + 0x14213123, + 0x13311251, + 0x13431311, + }, /* 838 */ + { + 0x24213131, + 0x54221111, + 0x11613212, + }, /* 839 */ + { + 0x14213222, + 0x53312111, + 0x12522311, + }, /* 840 */ + { + 0x15122321, + 0x45131111, + 0x11613311, + }, /* 841 */ + { + 0x14213321, + 0x44222111, + 0x14341121, + }, /* 842 */ + { + 0x15123131, + 0x43313111, + 0x13432121, + }, /* 843 */ + { + 0x14214131, + 0x35132111, + 0x12523121, + }, /* 844 */ + { + 0x33311114, + 0x34223111, + 0x11614121, + }, /* 845 */ + { + 0x33311213, + 0x33314111, + 0x31621112, + }, /* 846 */ + { + 0x33311312, + 0x25133111, + 0x31621211, + }, /* 847 */ + { + 0x33311411, + 0x24224111, + 0x22531112, + }, /* 848 */ + { + 0x24221114, + 0x23315111, + 0x21622112, + }, /* 849 */ + { + 0x23312114, + 0x15134111, + 0x22531211, + }, /* 850 */ + { + 0x33312122, + 0x14225111, + 0x21622211, + }, /* 851 */ + { + 0x34221221, + 0x13316111, + 0x13441112, + }, /* 852 */ + { + 0x23312213, + 0x12411143, + 0x12532112, + }, /* 853 */ + { + 0x33312221, + 0x22411151, + 0x13441211, + }, /* 854 */ + { + 0x23312312, + 0x12411242, + 0x11623112, + }, /* 855 */ + { + 0x24221411, + 0x12411341, + 0x12532211, + }, /* 856 */ + { + 0x23312411, + 0x13321151, + 0x11623211, + }, /* 857 */ + { + 0x15131114, + 0x12412151, + 0x31631111, + }, /* 858 */ + { + 0x14222114, + 0x11511134, + 0x22541111, + }, /* 859 */ + { + 0x15131213, + 0x21511142, + 0x21632111, + }, /* 860 */ + { + 0x25131221, + 0x11511233, + 0x13451111, + }, /* 861 */ + { + 0x13313114, + 0x21511241, + 0x12542111, + }, /* 862 */ + { + 0x14222213, + 0x11511332, + 0x11633111, + }, /* 863 */ + { + 0x15131312, + 0x11511431, + 0x16211132, + }, /* 864 */ + { + 0x13313213, + 0x12421142, + 0x16211231, + }, /* 865 */ + { + 0x14222312, + 0x11512142, + 0x15311123, + }, /* 866 */ + { + 0x15131411, + 0x12421241, + 0x25311131, + }, /* 867 */ + { + 0x13313312, + 0x11512241, + 0x15311222, + }, /* 868 */ + { + 0x14222411, + 0x11521133, + 0x15311321, + }, /* 869 */ + { + 0x15132122, + 0x21521141, + 0x16221131, + }, /* 870 */ + { + 0x14223122, + 0x11521232, + 0x15312131, + }, /* 871 */ + { + 0x15132221, + 0x11521331, + 0x14411114, + }, /* 872 */ + { + 0x13314122, + 0x12431141, + 0x24411122, + }, /* 873 */ + { + 0x14223221, + 0x11522141, + 0x14411213, + }, /* 874 */ + { + 0x13314221, + 0x11531132, + 0x24411221, + }, /* 875 */ + { + 0x42411113, + 0x11531231, + 0x14411312, + }, /* 876 */ + { + 0x42411212, + 0x11541131, + 0x14411411, + }, /* 877 */ + { + 0x42411311, + 0x36112112, + 0x15321122, + }, /* 878 */ + { + 0x33321113, + 0x36112211, + 0x14412122, + }, /* 879 */ + { + 0x32412113, + 0x26113112, + 0x15321221, + }, /* 880 */ + { + 0x42412121, + 0x26113211, + 0x14412221, + }, /* 881 */ + { + 0x32412212, + 0x16114112, + 0x23511113, + }, /* 882 */ + { + 0x33321311, + 0x16114211, + 0x33511121, + }, /* 883 */ + { + 0x32412311, + 0x45212111, + 0x23511212, + }, /* 884 */ + { + 0x24231113, + 0x36122111, + 0x23511311, + }, /* 885 */ + { + 0x34231121, + 0x35213111, + 0x14421113, + }, /* 886 */ + { + 0x23322113, + 0x26123111, + 0x24421121, + }, /* 887 */ + { + 0x33322121, + 0x25214111, + 0x13512113, + }, /* 888 */ + { + 0x22413113, + 0x16124111, + 0x23512121, + }, /* 889 */ + { + 0x23322212, + 0x15215111, + 0x13512212, + }, /* 890 */ + { + 0x24231311, + 0x14311151, + 0x14421311, + }, /* 891 */ + { + 0x22413212, + 0x13411142, + 0x13512311, + }, /* 892 */ + { + 0x23322311, + 0x13411241, + 0x15331121, + }, /* 893 */ + { + 0x22413311, + 0x12511133, + 0x14422121, + }, /* 894 */ + { + 0x15141113, + 0x22511141, + 0x13513121, + }, /* 895 */ + { + 0x25141121, + 0x12511232, + 0x32611112, + }, /* 896 */ + { + 0x14232113, + 0x12511331, + 0x32611211, + }, /* 897 */ + { + 0x24232121, + 0x13421141, + 0x23521112, + }, /* 898 */ + { + 0x13323113, + 0x12512141, + 0x22612112, + }, /* 899 */ + { + 0x14232212, + 0x11611124, + 0x23521211, + }, /* 900 */ + { + 0x15141311, + 0x21611132, + 0x22612211, + }, /* 901 */ + { + 0x12414113, + 0x11611223, + 0x14431112, + }, /* 902 */ + { + 0x13323212, + 0x21611231, + 0x13522112, + }, /* 903 */ + { + 0x14232311, + 0x11611322, + 0x14431211, + }, /* 904 */ + { + 0x12414212, + 0x11611421, + 0x12613112, + }, /* 905 */ + { + 0x13323311, + 0x12521132, + 0x13522211, + }, /* 906 */ + { + 0x15142121, + 0x11612132, + 0x12613211, + }, /* 907 */ + { + 0x14233121, + 0x12521231, + 0x32621111, + }, /* 908 */ + { + 0x13324121, + 0x11612231, + 0x23531111, + }, /* 909 */ + { + 0x12415121, + 0x11621123, + 0x22622111, + }, /* 910 */ + { + 0x51511112, + 0x21621131, + 0x14441111, + }, /* 911 */ + { + 0x51511211, + 0x11621222, + 0x13532111, + }, /* 912 */ + { + 0x42421112, + 0x11621321, + 0x12623111, + }, /* 913 */ + { + 0x41512112, + 0x12531131, + 0x16311122, + }, /* 914 */ + { + 0x42421211, + 0x11622131, + 0x16311221, + }, /* 915 */ + { + 0x41512211, + 0x11631122, + 0x15411113, + }, /* 916 */ + { + 0x33331112, + 0x11631221, + 0x25411121, + }, /* 917 */ + { + 0x32422112, + 0x14411141, + 0x15411212, + }, /* 918 */ + { + 0x33331211, + 0x13511132, + 0x15411311, + }, /* 919 */ + { + 0x31513112, + 0x13511231, + 0x16321121, + }, /* 920 */ + { + 0x32422211, + 0x12611123, + 0x15412121, + }, /* 921 */ + { + 0x31513211, + 0x22611131, + 0x24511112, + }, /* 922 */ + { + 0x24241112, + 0x12611222, + 0x24511211, + }, /* 923 */ + { + 0x23332112, + 0x12611321, + 0x15421112, + }, /* 924 */ + { + 0x24241211, + 0x13521131, + 0x14512112, + }, /* 925 */ + { + 0x22423112, + 0x12612131, + 0x15421211, + }, /* 926 */ + { + 0x23332211, + 0x12621122, + 0x14512211, + }, /* 927 */ + { + 0x21514112, + 0x12621221, + 0x33611111, + }, /* 928 */ +}; + +#endif diff --git a/test/test_convert.c b/test/test_convert.c new file mode 100644 index 0000000..426ac81 --- /dev/null +++ b/test/test_convert.c @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <zbar.h> +#include "test_images.h" + +#if 0 +static uint32_t formats[] = { + +}; +#endif + +int main(int argc, char *argv[]) +{ + zbar_set_verbosity(10); + + uint32_t srcfmt = fourcc('I', '4', '2', '0'); + if (argc > 1) + srcfmt = *(uint32_t *)argv[1]; + + zbar_image_t *img = zbar_image_create(); + zbar_image_set_size(img, 640, 480); + zbar_image_set_format(img, srcfmt); + if (test_image_bars(img)) + return (2); + + if (zbar_image_write(img, "/tmp/base")) + return (1); + return (0); +} diff --git a/test/test_cpp.cpp b/test/test_cpp.cpp new file mode 100644 index 0000000..1c60ece --- /dev/null +++ b/test/test_cpp.cpp @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +// NB do not put anything before this header +// it's here to check that we didn't omit any dependencies +#include <zbar.h> + +int main(int argc, char **argv) +{ + const char *video_dev = "/dev/video0"; + if (argc > 1) + video_dev = argv[1]; + + zbar::Processor proc = zbar::Processor(true, video_dev); + proc.set_visible(); + proc.set_active(); + try { + proc.user_wait(); + } catch (zbar::ClosedError &) { + } + + return (0); +} diff --git a/test/test_cpp_img.cpp b/test/test_cpp_img.cpp new file mode 100644 index 0000000..3b57fcb --- /dev/null +++ b/test/test_cpp_img.cpp @@ -0,0 +1,214 @@ +//------------------------------------------------------------------------ +// Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +// NB do not put anything before this header +// it's here to check that we didn't omit any dependencies +#include <zbar.h> + +#include <iomanip> +#include <iostream> +#include <sstream> +#include "test_images.h" + +bool debug = false; +bool verbose = false; +int errors = 0; +zbar::zbar_symbol_type_t expect_type = zbar::ZBAR_NONE; +std::string expect_data; + +template <class T> inline std::string to_string(const T &t) +{ + std::stringstream ss; + ss << t; + return ss.str(); +} + +static inline int error(const std::string &msg) +{ + errors++; + std::cerr << "ERROR: " << msg << std::endl; + if (debug) + abort(); + return (-1); +} + +static inline int check_loc(const zbar::Image &img, const zbar::Symbol &sym) +{ + int n = 0; + int w = img.get_width(); + int h = img.get_height(); + for (zbar::Symbol::PointIterator p(sym.point_begin()); p != sym.point_end(); + ++p, n++) { + zbar::Symbol::Point q(*p); + if (q.x < 0 || q.x >= w || q.y < 0 || q.y >= h) + error("location point out of range"); + } + return (!n); +} + +static inline int check_symbol(const zbar::Image &img, const zbar::Symbol &sym) +{ + zbar::zbar_symbol_type_t type(sym.get_type()); + std::string data(sym.get_data()); + + bool pass = expect_type && type == expect_type && data == expect_data && + sym.get_data_length() == expect_data.length() && + sym.get_quality() > 4; + if (pass) + pass = !check_loc(img, sym); + + if (verbose || !pass) + std::cerr << "decode Symbol: " << sym << std::endl; + + if (!expect_type) + error("unexpected"); + else if (!pass) + error(std::string("expected: ") + + zbar::zbar_get_symbol_name(expect_type) + " " + expect_data); + + expect_type = zbar::ZBAR_NONE; + expect_data = ""; + return (!pass); +} + +static inline int check_image(const zbar::Image &img) +{ + zbar::SymbolSet syms(img.get_symbols()); + int setn = syms.get_size(), countn = 0; + + int rc = 0; + for (zbar::SymbolIterator sym(syms.symbol_begin()); + sym != syms.symbol_end(); ++sym, ++countn) + rc |= check_symbol(img, *sym); + + if (countn != setn) + rc |= error("SymbolSet size mismatch: exp=" + to_string(setn) + + " act=" + to_string(countn)); + return (rc); +} + +static inline void expect(zbar::zbar_symbol_type_t type, std::string data) +{ + if (expect_type) + error(std::string("missing: ") + zbar_get_symbol_name(expect_type) + + " " + expect_data); + expect_type = type; + expect_data = data; +} + +class Handler : public zbar::Image::Handler +{ + void image_callback(zbar::Image &img); +}; + +void Handler::image_callback(zbar::Image &img) +{ + bool unexpected = !expect_type; + if (unexpected) + error("unexpected image callback"); + check_image(img); +} + +static inline int test_processor() +{ + // create processor w/no video and no window + zbar::Processor proc(debug, NULL); + Handler handler; + proc.set_handler(handler); + if (debug) { + proc.set_visible(); + proc.user_wait(); + } + + // generate barcode test image + zbar::Image rgb3(0, 0, "RGB3"); + + // test cast to C image + if (test_image_ean13(rgb3)) + error("failed to generate image"); + + // test decode + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + proc.process_image(rgb3); + if (debug) + proc.user_wait(); + + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + check_image(rgb3); + + if (rgb3.get_format() != zbar_fourcc('R', 'G', 'B', '3')) + error("image format mismatch"); + + expect(zbar::ZBAR_NONE, ""); + proc.set_config(zbar::ZBAR_EAN13, zbar::ZBAR_CFG_ENABLE, false); + proc.process_image(rgb3); + check_image(rgb3); + if (debug) + proc.user_wait(); + + proc.set_config("ean13.en"); + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + proc << rgb3; + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + check_image(rgb3); + if (debug) + proc.user_wait(); + + { + zbar::Image grey(rgb3.convert(zbar_fourcc('G', 'R', 'E', 'Y'))); + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + proc << grey; + + zbar::Image y800 = grey.convert("Y800"); + expect(zbar::ZBAR_EAN13, test_image_ean13_data); + proc << y800; + } + if (debug) + // check image data retention + proc.user_wait(); + + expect(zbar::ZBAR_NONE, ""); + return (0); +} + +int main(int argc, char **argv) +{ + debug = (argc > 1 && std::string(argv[1]) == "-d"); + verbose = (debug || (argc > 1 && std::string(argv[1]) == "-v")); + + if (test_processor()) { + error("ERROR: Processor test FAILED"); + return (2); + } + + if (test_image_check_cleanup()) + error("cleanup failed"); + + if (errors) { + std::cout << "processor FAILED" << std::endl; + return (2); + } else { + std::cout << "processor PASSED." << std::endl; + return (0); + } +} diff --git a/test/test_dbus.c b/test/test_dbus.c new file mode 100644 index 0000000..60a415e --- /dev/null +++ b/test/test_dbus.c @@ -0,0 +1,254 @@ +/*------------------------------------------------------------------------ + * Copyright 2019 (c) Mauro Carvalho Chehab <mchehab+samsung@kernel.org> + * + * 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. + *------------------------------------------------------------------------*/ + +#include <argp.h> +#include <dbus/dbus.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define ZBAR_INTERFACE "org.linuxtv.Zbar1.Code" +#define ZBAR_SIGNAL_CODE "Code" +#define ZBAR_SIGNAL_TYPE "Type" +#define ZBAR_SIGNAL_DATA "Data" +#define ZBAR_SIGNAL_BINARY_DATA "BinaryData" + +#define PROGRAM_NAME "test_dbus" + +static const char doc[] = "\nTest if ZBar is sending codes via D-Bus\n"; + +static const struct argp_option options[] = { + { "count", 'c', "#codes", 0, "Stop after received #codes", 0 }, + { "time", 't', "#seconds", 0, "Stop after #seconds", 0 }, + { "log", 'l', "#file", 0, "Write log to #file", 0 }, + { "bin-log", 'b', "#file", 0, "Write binary log to #file", 0 }, + { "help", '?', 0, 0, "Give this help list", -1 }, + { "usage", -3, 0, 0, "Give a short usage message", 0 }, + { 0 } +}; + +static int max_msg = 0; +static int timeout = 0; +static FILE *log = NULL; +static FILE *bin_log = NULL; + +static error_t parse_opt(int k, char *optarg, struct argp_state *state) +{ + switch (k) { + case 'c': + max_msg = strtoul(optarg, NULL, 0); + break; + case 't': + timeout = strtoul(optarg, NULL, 0); + break; + case 'l': + log = fopen(optarg, "wb"); + break; + case 'b': + bin_log = fopen(optarg, "wb"); + break; + case '?': + argp_state_help(state, state->out_stream, + ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC); + exit(0); + case -3: + argp_state_help(state, state->out_stream, ARGP_HELP_USAGE); + exit(0); + default: + return ARGP_ERR_UNKNOWN; + }; + return 0; +} + +static const struct argp argp = { + .options = options, + .parser = parse_opt, + .doc = doc, +}; + +int main(int argc, char *argv[]) +{ + DBusMessage *msg; + DBusMessageIter args, entry, dict, val; + DBusConnection *conn; + DBusError err; + char *str, *property; + int count = 0, length = 0; + + if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) { + argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME); + return -1; + } + + if (!log) + log = fdopen(dup(fileno(stderr)), "w+"); + + if (!bin_log) + bin_log = fdopen(dup(fileno(stderr)), "w+"); + + // initialise the error value + dbus_error_init(&err); + + // connect to the DBUS system bus, and check for errors + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Connection Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (!conn) { + fprintf(stderr, "Connection Null\n"); + return -1; + } + + dbus_bus_add_match(conn, "type='signal',interface='" ZBAR_INTERFACE "'", + &err); + dbus_connection_flush(conn); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Match Error (%s)\n", err.message); + exit(1); + } + + if (timeout) + alarm(timeout); + + /* loop listening for signals being emitted */ + fprintf(stderr, "Waiting for Zbar events\n"); + while (true) { + // non blocking read of the next available message + dbus_connection_read_write(conn, 0); + msg = dbus_connection_pop_message(conn); + + // loop again if we haven't read a message + if (NULL == msg) { + sleep(1); + continue; + } + + // check if the message is a signal from the correct interface and with the + // correct name + if (dbus_message_is_signal(msg, ZBAR_INTERFACE, ZBAR_SIGNAL_CODE)) { + // read the parameters + if (!dbus_message_iter_init(msg, &args)) + fprintf(stderr, "Message has no arguments!\n"); + else if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&args)) + fprintf(stderr, "Argument is not array!\n"); + else { + while (dbus_message_iter_get_arg_type(&args) != + DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&args, &entry); + if (DBUS_TYPE_DICT_ENTRY != + dbus_message_iter_get_arg_type(&entry)) { + fprintf(stderr, "Element is not dict entry!\n"); + } else { + while (dbus_message_iter_get_arg_type(&entry) != + DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&entry, &dict); + if (DBUS_TYPE_STRING != + dbus_message_iter_get_arg_type(&dict)) { + fprintf(stderr, + "Dict Entry key is not string!\n"); + } else { + dbus_message_iter_get_basic(&dict, &property); + dbus_message_iter_next(&dict); + if (DBUS_TYPE_VARIANT != + dbus_message_iter_get_arg_type(&dict)) { + fprintf( + stderr, + "Dict Entry value is not variant!\n"); + } else { + dbus_message_iter_recurse(&dict, &val); + if (strcmp(property, ZBAR_SIGNAL_TYPE) == + 0) { + if (DBUS_TYPE_STRING != + dbus_message_iter_get_arg_type( + &val)) { + fprintf( + stderr, + "Dict Entry value for barcode type is not string!\n"); + } else { + dbus_message_iter_get_basic(&val, + &str); + fprintf(stderr, "Type = %s\n", str); + } + } else if (strcmp(property, + ZBAR_SIGNAL_DATA) == 0) { + if (DBUS_TYPE_STRING != + dbus_message_iter_get_arg_type( + &val)) { + fprintf( + stderr, + "Dict Entry value for barcode text data is not string!\n"); + } else { + dbus_message_iter_get_basic(&val, + &str); + fprintf(stderr, "Value = %s\n", + str); + fprintf(log, "%s\n", str); + } + } else if (strcmp(property, + ZBAR_SIGNAL_BINARY_DATA) == + 0) { + if (DBUS_TYPE_ARRAY != + dbus_message_iter_get_arg_type( + &val)) { + fprintf( + stderr, + "Dict Entry value for barcode binary data is not array!\n"); + } else { + dbus_message_iter_recurse(&val, + &val); + if (DBUS_TYPE_BYTE != + dbus_message_iter_get_arg_type( + &val)) { + fprintf( + stderr, + "Dict Entry value for barcode binary data is not array of bytes!\n"); + } else { + dbus_message_iter_get_fixed_array( + &val, &str, &length); + fprintf(stderr, + "BinaryData[%d]\n", + length); + fwrite(str, sizeof(*str), + length, bin_log); + } + } + } + } + } + dbus_message_iter_next(&entry); + } + /* If max_msg > 0, stops after receiving 'count' messages */ + if (++count == max_msg) { + dbus_message_unref(msg); + return 0; + } + } + dbus_message_iter_next(&args); + } + } + } + // free the message + dbus_message_unref(msg); + } + + fclose(log); + fclose(bin_log); + + return 0; +} diff --git a/test/test_decode.c b/test/test_decode.c new file mode 100644 index 0000000..aa526f6 --- /dev/null +++ b/test/test_decode.c @@ -0,0 +1,1356 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <argp.h> +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <zbar.h> + +zbar_decoder_t *decoder; + +zbar_symbol_type_t expect_sym; +char *expect_data = NULL; + +int rnd_size = 9; /* NB should be odd */ +int wrong = 0, spurious = 0, missing = 0; + +#define zprintf(level, format, ...) \ + do { \ + if (verbosity >= (level)) { \ + fprintf(stderr, format, ##__VA_ARGS__); \ + } \ + } while (0) + +#define PROGRAM_NAME "test_video" + +static const char doc[] = + "\nGenerate barcodes and decode them with ZBar decoding logic\n"; + +static const struct argp_option options[] = { + { "quiet", 'q', 0, 0, "Don't be verbose", 0 }, + { "verbose", 'v', 0, 0, "Increases verbosity level", 0 }, + { "random", 'r', 0, 0, "use a random seed", 0 }, + { "seed", 's', "seed", 0, "sets the random seed", 0 }, + { "number", 'n', "count", 0, "sets the number of interactions", 0 }, + { "help", '?', 0, 0, "Give this help list", -1 }, + { "usage", -3, 0, 0, "Give a short usage message", 0 }, + { 0 } +}; + +unsigned seed = 0, rand_seed = 0; +int verbosity = 1; +int iter = 0, num_iter = 0; /* test iteration */ + +static error_t parse_opt(int k, char *optarg, struct argp_state *state) +{ + switch (k) { + case 'q': + verbosity = 0; + break; + case 'v': + verbosity++; + break; + case 'r': + rand_seed = 1; + break; + case 's': + seed = strtol(optarg, NULL, 0); + break; + case 'n': + num_iter = strtol(optarg, NULL, 0); + break; + case '?': + argp_state_help(state, state->out_stream, + ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC); + exit(0); + case -3: + argp_state_help(state, state->out_stream, ARGP_HELP_USAGE); + exit(0); + default: + return ARGP_ERR_UNKNOWN; + }; + return 0; +} + +static const struct argp argp = { + .options = options, + .parser = parse_opt, + .doc = doc, +}; + +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 || sym == ZBAR_QRCODE) + return; + const char *data = zbar_decoder_get_data(decoder); + + if (sym != expect_sym) { + zprintf(0, "[%d] SEED=%d: warning: expecting %s, got spurious %s\n", + iter, seed, zbar_get_symbol_name(expect_sym), + zbar_get_symbol_name(sym)); + spurious++; + return; + } + + 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, "[%d] SEED=%d: ERROR: expecting %s (%s), got %s (%s)\n", + iter, seed, expect_data, zbar_get_symbol_name(expect_sym), data, + zbar_get_symbol_name(sym)); + wrong++; + } + + 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, "[%d] SEED=%d: missing decode: %s (%s)\n", iter, seed, + zbar_get_symbol_name(expect_sym), expect_data); + missing++; + } + expect_sym = sym; + expect_data = (data) ? strdup(data) : NULL; +} + +static void encode_junk(int n) +{ + if (n > 1) + zprintf(3, "encode random junk...\n"); + int i; + for (i = 0; i < n; i++) + zbar_decode_width(decoder, 20. * (rand() / (RAND_MAX + 1.)) + 1); +} + +#define FWD 1 +#define REV 0 + +static void encode(uint64_t units, int fwd) +{ + zprintf(3, " raw=%x%x%c\n", (unsigned)(units >> 32), + (unsigned)(units & 0xffffffff), (fwd) ? '<' : '>'); + if (!fwd) + while (units && !(units >> 0x3c)) + units <<= 4; + + while (units) { + unsigned char w = (fwd) ? units & 0xf : units >> 0x3c; + zbar_decode_width(decoder, w); + if (fwd) + units >>= 4; + else + units <<= 4; + } +} + +/*------------------------------------------------------------*/ +/* Code 128 encoding */ + +typedef enum code128_char_e +{ + FNC3 = 0x60, + FNC2 = 0x61, + SHIFT = 0x62, + CODE_C = 0x63, + CODE_B = 0x64, + CODE_A = 0x65, + FNC1 = 0x66, + START_A = 0x67, + START_B = 0x68, + START_C = 0x69, + STOP = 0x6a, +} code128_char_t; + +static const unsigned int + code128[107] = { + 0x212222, 0x222122, 0x222221, 0x121223, /* 00 */ + 0x121322, 0x131222, 0x122213, 0x122312, + 0x132212, 0x221213, 0x221312, 0x231212, /* 08 */ + 0x112232, 0x122132, 0x122231, 0x113222, + 0x123122, 0x123221, 0x223211, 0x221132, /* 10 */ + 0x221231, 0x213212, 0x223112, 0x312131, + 0x311222, 0x321122, 0x321221, 0x312212, /* 18 */ + 0x322112, 0x322211, 0x212123, 0x212321, + 0x232121, 0x111323, 0x131123, 0x131321, /* 20 */ + 0x112313, 0x132113, 0x132311, 0x211313, + 0x231113, 0x231311, 0x112133, 0x112331, /* 28 */ + 0x132131, 0x113123, 0x113321, 0x133121, + 0x313121, 0x211331, 0x231131, 0x213113, /* 30 */ + 0x213311, 0x213131, 0x311123, 0x311321, + 0x331121, 0x312113, 0x312311, 0x332111, /* 38 */ + 0x314111, 0x221411, 0x431111, 0x111224, + 0x111422, 0x121124, 0x121421, 0x141122, /* 40 */ + 0x141221, 0x112214, 0x112412, 0x122114, + 0x122411, 0x142112, 0x142211, 0x241211, /* 48 */ + 0x221114, 0x413111, 0x241112, 0x134111, + 0x111242, 0x121142, 0x121241, 0x114212, /* 50 */ + 0x124112, 0x124211, 0x411212, 0x421112, + 0x421211, 0x212141, 0x214121, 0x412121, /* 58 */ + 0x111143, 0x111341, 0x131141, 0x114113, + 0x114311, 0x411113, 0x411311, 0x113141, /* 60 */ + 0x114131, 0x311141, 0x411131, 0xa211412, + 0xa211214, 0xa211232, /* START_A-START_C (67-69) */ + 0x2331112a, /* STOP (6a) */ + }; + +static void encode_code128b(char *data) +{ + 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++) { + zprintf(3, " encode '%c': %02x", data[i], data[i] - 0x20); + encode(code128[data[i] - 0x20], 0); + chk += (i + 1) * (data[i] - 0x20); + } + chk %= 103; + zprintf(3, " encode checksum: %02x", chk); + encode(code128[chk], 0); + zprintf(3, " encode STOP: %02x", STOP); + encode(code128[STOP], 0); + print_sep(3); +} + +static void encode_code128c(char *data) +{ + 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'); + zprintf(3, " encode '%c%c': %02d", data[i], data[i + 1], c); + encode(code128[c], 0); + chk += (i / 2 + 1) * c; + } + chk %= 103; + zprintf(3, " encode checksum: %02x", chk); + encode(code128[chk], 0); + zprintf(3, " encode STOP: %02x", STOP); + encode(code128[STOP], 0); + 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); +} + +/*------------------------------------------------------------*/ +/* Code 39 encoding */ + +static const unsigned int code39[91 - 32] = { + 0x0c4, 0x000, 0x000, 0x000, 0x0a8, 0x02a, 0x000, 0x000, /* 20 */ + 0x000, 0x000, 0x094, 0x08a, 0x000, 0x085, 0x184, 0x0a2, /* 28 */ + 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, /* 30 */ + 0x124, 0x064, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, /* 38 */ + 0x000, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00d, /* 40 */ + 0x10c, 0x04c, 0x01c, 0x103, 0x043, 0x142, 0x013, 0x112, /* 48 */ + 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0c1, 0x1c0, /* 50 */ + 0x091, 0x190, 0x0d0, /* 58 */ +}; + +/* 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) +{ + assert(0x20 <= c && c <= 0x5a); + unsigned int raw = code39[c - 0x20]; + if (!raw) + return; /* skip (FIXME) */ + + uint64_t enc = 0; + int j; + for (j = 0; j < 9; j++) { + enc = (enc << 4) | ((raw & 0x100) ? 2 : 1); + raw <<= 1; + } + enc = (enc << 4) | ics; + zprintf(3, " encode '%c': %02x%08x: ", c, (unsigned)(enc >> 32), + (unsigned)(enc & 0xffffffff)); + encode(enc, REV); +} + +static void encode_code39(char *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; + for (i = 0; data[i]; i++) + if (data[i] != '*') /* skip (FIXME) */ + encode_char39(data[i], 1); + encode_char39('*', 0xa); /* w/trailing quiet */ + print_sep(3); +} + +#if 0 +/*------------------------------------------------------------*/ +/* PDF417 encoding */ + +/* hardcoded test message: "hello world" */ +#define PDF417_ROWS 3 +#define PDF417_COLS 3 +static const unsigned pdf417_msg[PDF417_ROWS][PDF417_COLS] = { + { 007, 817, 131 }, + { 344, 802, 437 }, + { 333, 739, 194 }, +}; + +#define PDF417_START UINT64_C(0x81111113) +#define PDF417_STOP UINT64_C(0x711311121) +#include "pdf417_encode.h" + +static int calc_ind417 (int mod, + int r, + int cols) +{ + mod = (mod + 3) % 3; + int cw = 30 * (r / 3); + if(!mod) + return(cw + cols - 1); + else if(mod == 1) + return(cw + (PDF417_ROWS - 1) % 3); + assert(mod == 2); + return(cw + (PDF417_ROWS - 1) / 3); +} + +static void encode_row417 (int r, + const unsigned *cws, + int cols, + int dir) +{ + int k = r % 3; + + zprintf(3, " [%d] encode %s:", r, (dir) ? "stop" : "start"); + encode((dir) ? PDF417_STOP : PDF417_START, dir); + + int cw = calc_ind417(k + !dir, r, cols); + 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]; + zprintf(3, " [%d,%d] encode %03d(%d): ", r, c, cw, k); + encode(pdf417_encode[cw][k], dir); + } + + cw = calc_ind417(k + dir, r, cols); + zprintf(3, " [%d,%c] encode %03d(%d): ", r, (dir) ? 'L' : 'R', cw, k); + encode(pdf417_encode[cw][k], dir); + + zprintf(3, " [%d] encode %s:", r, (dir) ? "start" : "stop"); + encode((dir) ? PDF417_START : PDF417_STOP, dir); +} + +static void encode_pdf417 (char *data) +{ + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "PDF417: hello world\n"); + encode(0xa, 0); + + int r; + for(r = 0; r < PDF417_ROWS; r++) { + encode_row417(r, pdf417_msg[r], PDF417_COLS, r & 1); + encode(0xa, 0); + } + + print_sep(3); +} +#endif + +/*------------------------------------------------------------*/ +/* Codabar encoding */ + +static const unsigned int codabar[20] = { + 0x03, 0x06, 0x09, 0x60, 0x12, 0x42, 0x21, 0x24, 0x30, 0x48, + 0x0c, 0x18, 0x45, 0x51, 0x54, 0x15, 0x1a, 0x29, 0x0b, 0x0e, +}; + +static const char codabar_char[0x14] = "0123456789-$:/.+ABCD"; + +/* FIXME configurable/randomized ratio, ics */ +/* FIXME check digit option */ + +static char *convert_codabar(char *src) +{ + unsigned len = strlen(src); + char tmp[4] = { + 0, + }; + if (len < 2) { + unsigned delim = rand() >> 8; + tmp[0] = delim & 3; + if (len) + tmp[1] = src[0]; + tmp[len + 1] = (delim >> 2) & 3; + len += 2; + src = tmp; + } + + char *result = malloc(len + 1); + char *dst = result; + *(dst++) = ((*(src++) - 1) & 0x3) + 'A'; + for (len--; len > 1; len--) { + char c = *(src++); + if (c >= '0' && c <= '9') + *(dst++) = c; + else if (c == '-' || c == '$' || c == ':' || c == '/' || c == '.' || + c == '+') + *(dst++) = c; + else + *(dst++) = codabar_char[c % 0x10]; + } + *(dst++) = ((*(src++) - 1) & 0x3) + 'A'; + *dst = 0; + return (result); +} + +static void encode_codachar(unsigned char c, unsigned ics, int dir) +{ + unsigned int idx; + if (c >= '0' && c <= '9') + idx = c - '0'; + else if (c >= 'A' && c <= 'D') + idx = c - 'A' + 0x10; + else + switch (c) { + case '-': + idx = 0xa; + break; + case '$': + idx = 0xb; + break; + case ':': + idx = 0xc; + break; + case '/': + idx = 0xd; + break; + case '.': + idx = 0xe; + break; + case '+': + idx = 0xf; + break; + default: + assert(0); + } + + assert(idx < 0x14); + unsigned int raw = codabar[idx]; + + uint32_t enc = 0; + int j; + for (j = 0; j < 7; j++, raw <<= 1) + enc = (enc << 4) | ((raw & 0x40) ? 3 : 1); + zprintf(3, " encode '%c': %07x: ", c, enc); + if (dir) + enc = (enc << 4) | ics; + else + enc |= ics << 28; + encode(enc, 1 - dir); +} + +static void encode_codabar(char *data, int dir) +{ + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "CODABAR: %s\n", data); + encode(0xa, 0); /* leading quiet */ + int i, n = strlen(data); + for (i = 0; i < n; i++) { + int j = (dir) ? i : n - i - 1; + encode_codachar(data[j], (i < n - 1) ? 1 : 0xa, dir); + } + print_sep(3); +} + +/*------------------------------------------------------------*/ +/* Interleaved 2 of 5 encoding */ + +static const unsigned char i25[10] = { + 0x06, 0x11, 0x09, 0x18, 0x05, 0x14, 0x0c, 0x03, 0x12, 0x0a, +}; + +static void encode_i25(char *data, int dir) +{ + 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(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'; + zprintf(3, " encode '%d%d':", c0, c1); + assert(c0 < 10); + assert(c1 < 10); + + c0 = i25[c0]; + c1 = i25[c1]; + + /* interleave */ + uint64_t enc = 0; + int j; + for (j = 0; j < 5; j++) { + enc <<= 8; + enc |= (c0 & 1) ? 0x02 : 0x01; + enc |= (c1 & 1) ? 0x20 : 0x10; + c0 >>= 1; + c1 >>= 1; + } + encode(enc, dir); + } + + zprintf(3, " encode end:"); + encode((dir) ? 0x211a : 0x1111a, 0); + 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 << bar);; + elmWidth++, narrowMask &= ~(1 << bar)) { + subVal = combins(n - elmWidth - 1, elements - bar - 2); + if ((!noNarrow) && !narrowMask && + (n - elmWidth - (elements - bar - 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] = { + 0x1123, 0x1222, 0x2212, 0x1141, 0x2311, + 0x1321, 0x4111, 0x2131, 0x3121, 0x2113, +}; + +static const unsigned int ean_guard[] = { + 0, 0, 0x11, /* [2] add-on delineator */ + 0x1117, /* [3] normal guard bars */ + 0x2117, /* [4] add-on guard bars */ + 0x11111, /* [5] center guard bars */ + 0x111111 /* [6] "special" guard bars */ +}; + +static const unsigned char ean_parity_encode[] = { + 0x3f, /* AAAAAA = 0 */ + 0x34, /* AABABB = 1 */ + 0x32, /* AABBAB = 2 */ + 0x31, /* AABBBA = 3 */ + 0x2c, /* ABAABB = 4 */ + 0x26, /* ABBAAB = 5 */ + 0x23, /* ABBBAA = 6 */ + 0x2a, /* ABABAB = 7 */ + 0x29, /* ABABBA = 8 */ + 0x25, /* ABBABA = 9 */ +}; + +static const unsigned char addon_parity_encode[] = { + 0x07, /* BBAAA = 0 */ + 0x0b, /* BABAA = 1 */ + 0x0d, /* BAABA = 2 */ + 0x0e, /* BAAAB = 3 */ + 0x13, /* ABBAA = 4 */ + 0x19, /* AABBA = 5 */ + 0x1c, /* AAABB = 6 */ + 0x15, /* ABABA = 7 */ + 0x16, /* ABAAB = 8 */ + 0x1a, /* AABAB = 9 */ +}; + +static void calc_ean_parity(char *data, int n) +{ + int i, chk = 0; + for (i = 0; i < n; i++) { + unsigned char c = data[i] - '0'; + chk += ((i ^ n) & 1) ? c * 3 : c; + } + chk %= 10; + if (chk) + chk = 10 - chk; + data[i++] = '0' + chk; + data[i] = 0; +} + +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); + + 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) { + zprintf(3, " encode %x%c:", (par >> 5) & 1, data[i]); + encode(ean_digits[data[i] - '0'], (par >> 5) & 1); + } + zprintf(3, " encode center guard:"); + encode(ean_guard[5], FWD); + for (; i < 13; i++) { + zprintf(3, " encode %x%c:", 0, data[i]); + encode(ean_digits[data[i] - '0'], FWD); + } + zprintf(3, " encode end guard:"); + encode(ean_guard[3], REV); + print_sep(3); +} + +static void encode_ean8(char *data) +{ + int i; + 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++) { + zprintf(3, " encode %c:", data[i]); + encode(ean_digits[data[i] - '0'], FWD); + } + zprintf(3, " encode center guard:"); + encode(ean_guard[5], FWD); + for (; i < 8; i++) { + zprintf(3, " encode %c:", data[i]); + encode(ean_digits[data[i] - '0'], FWD); + } + zprintf(3, " encode end guard:"); + encode(ean_guard[3], REV); + print_sep(3); +} + +static void encode_addon(char *data, unsigned par, int n) +{ + int i; + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + + print_sep(3); + zprintf(2, "EAN-%d: %s (par=%02x)\n", n, data, par); + zprintf(3, " encode start guard:"); + encode(ean_guard[4], FWD); + for (i = 0; i < n; i++, par <<= 1) { + zprintf(3, " encode %x%c:", (par >> (n - 1)) & 1, data[i]); + encode(ean_digits[data[i] - '0'], (par >> (n - 1)) & 1); + if (i < n - 1) { + zprintf(3, " encode delineator:"); + encode(ean_guard[2], FWD); + } + } + zprintf(3, " encode trailing qz:"); + encode(0x7, FWD); + print_sep(3); +} + +static void encode_ean5(char *data) +{ + unsigned chk = ((data[0] - '0' + data[2] - '0' + data[4] - '0') * 3 + + (data[1] - '0' + data[3] - '0') * 9) % + 10; + encode_addon(data, addon_parity_encode[chk], 5); +} + +static void encode_ean2(char *data) +{ + unsigned par = (~(10 * (data[0] - '0') + data[1] - '0')) & 3; + encode_addon(data, par, 2); +} + +/*------------------------------------------------------------*/ +/* main test flow */ + +int test_databar_F_1() +{ + 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); +} + +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); +} + +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); + + expect(ZBAR_DATABAR, data); + data[1] = '0'; + encode_databar(data + 1, FWD); + encode_junk(rnd_size); + return (0); +} + +int test_numeric(char *data) +{ + char tmp[32] = "01"; + strncpy(tmp + 2, data + 1, 13); + tmp[15] = '\0'; + 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[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 + + char *cdb = convert_codabar(data); + expect(ZBAR_CODABAR, cdb); + encode_codabar(cdb, FWD); + encode_junk(rnd_size); + + expect(ZBAR_CODABAR, cdb); + encode_codabar(cdb, REV); + encode_junk(rnd_size); + free(cdb); + + 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); + + data[5] = 0; + expect(ZBAR_EAN5, data); + encode_ean5(data); + + encode_junk(rnd_size); + + data[2] = 0; + expect(ZBAR_EAN2, data); + encode_ean2(data); + 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); + + char *cdb = convert_codabar(data); + expect(ZBAR_CODABAR, cdb); + encode_codabar(cdb, FWD); + encode_junk(rnd_size); + + expect(ZBAR_CODABAR, cdb); + encode_codabar(cdb, REV); + encode_junk(rnd_size); + free(cdb); + + 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]; + for (i = 0; i < 14; i++) { + data[i] = (rand() % 10) + '0'; + } + data[i] = 0; + + zprintf(1, "testing data: %s\n", data); + + test_numeric(data); + + for (i = 0; i < 10; i++) + data[i] = (rand() % 0x5f) + 0x20; + data[i] = 0; + + zprintf(1, "testing alpha: %s\n", data); + + test_alpha(data); + return (0); +} + +/* FIXME TBD: + * - random module width (!= 1.0) + * - simulate scan speed variance + * - simulate dark "swelling" and light "blooming" + * - inject parity errors + */ + +float percent(int count, int iter) +{ + if (iter <= 1) { + if (count) + return 100.0; + else + return 0.0; + } + return (count * 100.0) / iter; +} + +int main(int argc, char *argv[]) +{ + if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) { + argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME); + return -1; + } + + if (rand_seed) { + seed = time(NULL); + srand(seed); + seed = (rand() << 8) ^ rand(); + zprintf(0, "Random SEED=%d\n", seed); + } + + decoder = zbar_decoder_create(); + /* allow empty CODE39 symbologies */ + zbar_decoder_set_config(decoder, ZBAR_CODE39, ZBAR_CFG_MIN_LEN, 0); + /* enable addons */ + zbar_decoder_set_config(decoder, ZBAR_EAN2, ZBAR_CFG_ENABLE, 1); + zbar_decoder_set_config(decoder, ZBAR_EAN5, ZBAR_CFG_ENABLE, 1); + zbar_decoder_set_handler(decoder, symbol_handler); + + encode_junk(rnd_size + 1); + + if (num_iter) { + for (iter = 0; iter < num_iter; iter++) { + test1(); + seed = (rand() << 8) ^ rand(); + } + } else { + test_databar_F_1(); + test_databar_F_3(); + test_orange(); + test1(); + } + + zbar_decoder_destroy(decoder); + + if (!wrong && percent(spurious, num_iter) <= 0.01 && + percent(missing, num_iter) <= 0.01) { + if (spurious || missing) + printf( + "decoder PASSED with %d spurious (%02.4f%%) and %d missing(%02.4f%%).\n", + spurious, percent(spurious, num_iter), missing, + percent(missing, num_iter)); + else + printf("decoder PASSED.\n"); + } else { + printf( + "decoder FAILED with %d wrong decoding(%02.4f%%), %d spurious (%02.4f%%) and %d missing(%02.4f%%).\n", + wrong, percent(wrong, num_iter), spurious, + percent(spurious, num_iter), missing, percent(missing, num_iter)); + return 1; + } + return (0); +} diff --git a/test/test_examples.sh.in b/test/test_examples.sh.in new file mode 100644 index 0000000..f4c18e8 --- /dev/null +++ b/test/test_examples.sh.in @@ -0,0 +1,91 @@ +#!/bin/bash + +unset ERR + +DIR="@abs_top_srcdir@" +ZBARIMG="@abs_top_builddir@/zbarimg/zbarimg --nodbus" + +test() +{ + if [ "$2" != "" ]; then + i="$DIR/examples/$2" + j="$1 $2" + else + i="$DIR/examples/$1" + j="$1" + fi; + if [ "$2" != "" ]; then + CMD="$ZBARIMG $1" + else + CMD="$ZBARIMG" + fi + CK=`$CMD "$i" 2>/dev/null|sha1sum|cut -d" " -f1` + ORG=`grep "zbarimg $j" "$DIR/examples/sha1sum"|cut -d " " -f1` + + if [ "$CK" != "$ORG" ]; then + echo "FAILED: $i ($CK instead of $ORG)" + echo -e "\tcmd: $CMD '$i'" + echo -en "\tresults: " + $CMD "$i" 2>/dev/null + ERR=1 + fi +} + +if [ "@ENABLE_CODE128@" == "1" ]; then + test code-128.png +fi + +if [ "@ENABLE_CODE93@" == "1" ]; then + test code-93.png +fi + +if [ "@ENABLE_CODE39@" == "1" ]; then + test code-39.png +fi + +if [ "@ENABLE_CODABAR@" == "1" ]; then + test codabar.png +fi + +if [ "@ENABLE_DATABAR@" == "1" ]; then + test databar.png + test databar-exp.png +fi + +if [ "@ENABLE_EAN@" == "1" ]; then + test -Sean2.enable ean-2.png + test -Sean5.enable ean-5.png + test ean-8.png + test ean-13.png + test -Sisbn10.enable ean-13.png + test -Sisbn13.enable ean-13.png + test -Supca.enable code-upc-a.png +fi + +if [ "@ENABLE_I25@" == "1" ]; then + test i2-5.png +fi + +if [ "@ENABLE_QRCODE@" == "1" ]; then + test qr-code.png + test -Stest-inverted qr-code-inverted.png + test '--raw --oneshot -Sbinary' qr-code-binary.png +fi + +if [ "@ENABLE_SQCODE@" == "1" ]; then + test sqcode1-generated.png + test sqcode1-scanned.png +fi + +# The pdf417 code is incomplete: it doesn't output any results +# +#if [ "@ENABLE_PDF417@" == "1" ]; then +# test code-pdf417.png +#fi + +if [ "$ERR" == "" ]; then + echo "zbarimg PASSED." +else + exit 1 +fi + diff --git a/test/test_gi.py b/test/test_gi.py new file mode 100755 index 0000000..c64f574 --- /dev/null +++ b/test/test_gi.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ +import sys, os, stat + +import gi +gi.require_version('ZBar', '1.0') + +try: + from gi.repository import ZBar, Gtk, GdkPixbuf +except ImportError: + print("No ZBar integration") + sys.exit() + +# To debug vars on a interactive python3: +# >>> import gi +# >>> gi.require_version('ZBar', '1.0') +# >>> from gi.repository import ZBar +# >>> zbar = ZBar.Gtk.new() +# +# >>> zbar.<tab> + +def decoded(zbar, data): + """callback invoked when a barcode is decoded by the zbar widget. + displays the decoded data in the text box + """ + buf = results.props.buffer + end = buf.get_end_iter() + buf.insert(end, data + "\n") + results.scroll_to_iter(end, 0, 0, 0, 0) + +def video_enabled(zbar, param): + """callback invoked when the zbar widget enables or disables + video streaming. updates the status button state to reflect the + current video state + """ + enabled = zbar.get_video_enabled() + if status_button.get_active() != enabled: + status_button.set_active(enabled) + +def video_opened(zbar, param): + """callback invoked when the zbar widget opens or closes a video + device. also called when a device is closed due to error. + updates the status button state to reflect the current video state + """ + opened = zbar.get_video_opened() + status_button.set_sensitive(opened) + set_status_label(opened, zbar.get_video_enabled()) + +def video_changed(widget): + """callback invoked when a new video device is selected from the + drop-down list. sets the new device for the zbar widget, + which will eventually cause it to be opened and enabled + """ + dev = video_list.get_active_text() + if dev[0] == '<': + dev = '' + zbar.set_video_device(dev) + +def status_button_toggled(button): + """callback invoked when the status button changes state + (interactively or programmatically). ensures the zbar widget + video streaming state is consistent and updates the display of the + button to represent the current state + """ + opened = zbar.get_video_opened() + active = status_button.get_active() + if opened and (active != zbar.get_video_enabled()): + zbar.set_video_enabled(active) + set_status_label(opened, active) + if active: + status_image.set_from_icon_name("gtk-yes", Gtk.IconSize.BUTTON) + else: + status_image.set_from_icon_name("Gtk-no", Gtk.IconSize.BUTTON) + +def open_button_clicked(button): + """callback invoked when the 'Open' button is clicked. pops up an + 'Open File' dialog which the user may use to select an image file. + if the image is successfully opened, it is passed to the zbar + widget which displays it and scans it for barcodes. results are + returned using the same hook used to report video results + """ + dialog = Gtk.FileChooserDialog(title = "Open Image File", parent = window, + action = Gtk.FileChooserAction.OPEN) + + dialog.add_buttons("gtk-cancel", Gtk.ResponseType.CANCEL) + dialog.add_buttons("gtk-open", Gtk.ResponseType.ACCEPT) + + global open_file + if open_file: + dialog.set_filename(open_file) + try: + if dialog.run() == Gtk.ResponseType.ACCEPT: + open_file = dialog.get_filename() + pixbuf = GdkPixbuf.Pixbuf.new_from_file(open_file) + if pixbuf: + zbar.scan_image(pixbuf) + finally: + dialog.destroy() + +def set_status_label(opened, enabled): + """update status button label to reflect indicated state.""" + if not opened: + label = "closed" + elif enabled: + label = "enabled" + else: + label = "disabled" + status_button.set_label(label) + +open_file = None +video_device = None +if len(sys.argv) > 1: + video_device = sys.argv[1] + +window = Gtk.Window() +window.set_title("test_pygtk") +window.set_border_width(8) +window.connect("destroy", Gtk.main_quit) + +zbar = ZBar.Gtk.new() + +print(zbar.get_video_device()) + +try: + print(zbar.get_video_device()) +except: + print(ZBar.get_video_device(zbar)) + +zbar.connect("decoded-text", decoded) + +# video device list combo box +video_list = Gtk.ComboBoxText() +video_list.connect("changed", video_changed) + +# enable/disable status button +status_button = Gtk.ToggleButton(name="closed") +status_image = Gtk.Image.new_from_icon_name("gtk-no", Gtk.IconSize.BUTTON) +status_button.set_image(status_image) +status_button.set_sensitive(False) + +# bind status button state and video state +status_button.connect("toggled", status_button_toggled) +zbar.connect("notify::video-enabled", video_enabled) +zbar.connect("notify::video-opened", video_opened) + +# open image file button +open_button = Gtk.Button.new_with_mnemonic(label="Open") +open_button.connect("clicked", open_button_clicked) + +# populate video devices in combo box +video_list.append_text("<none>") +video_list.set_active(0) +for (root, dirs, files) in os.walk("/dev"): + for dev in files: + path = os.path.join(root, dev) + if not os.access(path, os.F_OK): + continue + info = os.stat(path) + if stat.S_ISCHR(info.st_mode) and os.major(info.st_rdev) == 81: + video_list.append_text(path) + if path == video_device: + video_list.set_active(len(video_list.get_model()) - 1) + video_device = None + +if video_device is not None: + video_list.append_text(video_device) + video_list.set_active(len(video_list.get_model()) - 1) + video_device = None + +# combine combo box and buttons horizontally +hbox = Gtk.HBox(spacing=8) +hbox.pack_start(video_list, True, True, 0) +hbox.pack_start(status_button, False, True, 0) +hbox.pack_start(open_button, False, True, 0) + +# text box for holding results +results = Gtk.TextView() +results.set_size_request(320, 64) +results.props.editable = results.props.cursor_visible = False +results.set_left_margin(4) + +# combine inputs, scanner, and results vertically +vbox = Gtk.VBox(spacing=8) +vbox.pack_start(hbox, False, True, 0) +vbox.pack_start(zbar, True, True, 0) +vbox.pack_start(results, False, True, 0) + +window.add(vbox) + +# FIXME: how to fill the geometry parameter? +#geo = {"min_width": 320, "min_height": 240} +#window.set_geometry_hints(geometry_widget=zbar, geometry=geo ) +window.show_all() + +Gtk.main() diff --git a/test/test_images.c b/test/test_images.c new file mode 100644 index 0000000..68a3251 --- /dev/null +++ b/test/test_images.c @@ -0,0 +1,533 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <zbar.h> +#include "test_images.h" + +typedef enum format_type_e +{ + GRAY, + YUVP, + YVUP, + YUYV, + YVYU, + UYVY, + RGB888, + BGR888, + RGB565B = 0x0565, + RGB565L = 0x1565, + RGB555B = 0x0555, + RGB555L = 0x1555, +} format_type_t; + +typedef struct format_def_s { + uint32_t format; + format_type_t type; + uint8_t bpp; + uint8_t xdiv, ydiv; +} format_def_t; + +typedef union packed_u { + uint32_t u32[3]; + uint16_t u16[6]; + uint8_t u8[12]; +} packed_t; + +/* bar colors */ +static const uint8_t Cr[] = { 0x22, 0x92, 0x80, 0xf0, 0x10, 0x80, 0x6e, 0xde }; +static const uint8_t Cb[] = { 0x36, 0x10, 0x80, 0x5a, 0xa6, 0x80, 0xf0, 0xca }; + +static const format_def_t formats[] = { + { fourcc('G', 'R', 'E', 'Y'), GRAY, 8, 0, 0 }, + { fourcc('Y', '8', '0', '0'), GRAY, 8, 0, 0 }, + { fourcc('Y', '8', ' ', ' '), GRAY, 8, 0, 0 }, + { fourcc('Y', '8', 0, 0), GRAY, 8, 0, 0 }, + + { fourcc('Y', 'U', 'V', '9'), YUVP, 9, 4, 4 }, + { fourcc('Y', 'V', 'U', '9'), YVUP, 9, 4, 4 }, + + { fourcc('I', '4', '2', '0'), YUVP, 12, 2, 2 }, + { fourcc('Y', 'U', '1', '2'), YUVP, 12, 2, 2 }, + { fourcc('Y', 'V', '1', '2'), YVUP, 12, 2, 2 }, + { fourcc('4', '1', '1', 'P'), YUVP, 12, 4, 1 }, + + { fourcc('N', 'V', '1', '2'), YUVP, 12, 2, 2 }, + { fourcc('N', 'V', '2', '1'), YVUP, 12, 2, 2 }, + + { fourcc('4', '2', '2', 'P'), YUVP, 16, 2, 1 }, + + { fourcc('Y', 'U', 'Y', 'V'), YUYV, 16, 2, 1 }, + { fourcc('Y', 'U', 'Y', '2'), YUYV, 16, 2, 1 }, + { fourcc('Y', 'V', 'Y', 'U'), YVYU, 16, 2, 1 }, + { fourcc('U', 'Y', 'V', 'Y'), UYVY, 16, 2, 1 }, + + { + fourcc('R', 'G', 'B', '3'), + RGB888, + 24, + }, + { + fourcc('B', 'G', 'R', '3'), + BGR888, + 24, + }, + { + fourcc(3, 0, 0, 0), + RGB888, + 32, + }, + { + fourcc('R', 'G', 'B', '4'), + RGB888, + 32, + }, + { + fourcc('B', 'G', 'R', '4'), + BGR888, + 32, + }, + { + fourcc('R', 'G', 'B', 'P'), + RGB565L, + 16, + }, + { + fourcc('R', 'G', 'B', 'O'), + RGB555L, + 16, + }, + { + fourcc('R', 'G', 'B', 'R'), + RGB565B, + 16, + }, + { + fourcc('R', 'G', 'B', 'Q'), + RGB555B, + 16, + }, + { 0 } +}; + +static const char *encoded_widths = + "9 111 212241113121211311141132 11111 311213121312121332111132 111 9"; +const char *test_image_ean13_data = "6268964977804"; + +static int allocated_images = 0; + +int test_image_check_cleanup() +{ + if (allocated_images) + fprintf(stderr, "ERROR: %d image data buffers still allocated\n", + allocated_images); + /*else + fprintf(stderr, "all image data buffers freed\n");*/ + return (allocated_images); +} + +static void test_cleanup_handler(zbar_image_t *img) +{ + void *data = (void *)zbar_image_get_data(img); + /*fprintf(stderr, "cleanup image data @%p\n", data);*/ + free(data); + allocated_images--; +} + +static inline const format_def_t *lookup_format(zbar_image_t *img) +{ + uint32_t ifmt = zbar_image_get_format(img); + const format_def_t *fmt; + for (fmt = formats; fmt->format; fmt++) + if (fmt->format == ifmt) + break; + if (!fmt->format) { + fprintf(stderr, "ERROR: no %.4s (%08" PRIx32 ") format\n", + (char *)&ifmt, ifmt); + return (NULL); + } + return (fmt); +} + +static inline const format_def_t *alloc_data(zbar_image_t *img) +{ + allocated_images++; + const format_def_t *fmt = lookup_format(img); + if (!fmt) + return (NULL); + + unsigned w = zbar_image_get_width(img); + unsigned h = zbar_image_get_height(img); + unsigned long planelen = w * h; + unsigned long datalen = planelen * fmt->bpp / 8; + uint8_t *data = malloc(datalen); + + zbar_image_set_data(img, data, datalen, test_cleanup_handler); + + /*fprintf(stderr, "create %.4s(%08"PRIx32") image data %lx bytes @%p\n", + (char*)&fmt->format, fmt->format, datalen, data);*/ + return (fmt); +} + +/* write intensity plane */ +static inline uint8_t *fill_bars_y(uint8_t *p, unsigned w, unsigned h) +{ + unsigned x, y, i; + unsigned y0 = (h + 31) / 30; + for (y = 0; y < y0; y++) + for (x = 0; x < w; x++) + *(p++) = 0xff; + + for (; y < h - y0; y++) + for (x = 0, i = 0; x < w; i++) { + assert(i < 8); + unsigned x0 = (((i + 1) * w) + 7) >> 3; + assert(x0 <= w); + unsigned v = ((((i & 1) ? y : h - y) * 256) + h - 1) / h; + for (; x < x0; x++) + *(p++) = v; + } + + for (; y < h; y++) + for (x = 0; x < w; x++) + *(p++) = 0xff; + + return (p); +} + +/* write Cb (U) or Cr (V) plane */ +static inline uint8_t *fill_bars_uv(uint8_t *p, unsigned w, unsigned h, + const uint8_t *C) +{ + unsigned x, y, i; + unsigned y0 = (h + 31) / 30; + + for (y = 0; y < y0; y++) + for (x = 0; x < w; x++) + *(p++) = 0x80; + + for (; y < h - y0; y++) + for (x = 0, i = 0; x < w; i++) { + assert(i < 8); + unsigned x0 = (((i + 1) * w) + 7) >> 3; + assert(x0 <= w); + for (; x < x0; x++) + *(p++) = C[i]; + } + + for (; y < h; y++) + for (x = 0; x < w; x++) + *(p++) = 0x80; + + return (p); +} + +/* write packed CbCr plane */ +static inline uint8_t *fill_bars_nv(uint8_t *p, unsigned w, unsigned h, + format_type_t order) +{ + unsigned x, y, i; + unsigned y0 = (h + 31) / 30; + + for (y = 0; y < y0; y++) + for (x = 0; x < w; x++) { + *(p++) = 0x80; + *(p++) = 0x80; + } + + for (; y < h - y0; y++) + for (x = 0, i = 0; x < w; i++) { + assert(i < 8); + unsigned x0 = (((i + 1) * w) + 7) >> 3; + assert(x0 <= w); + uint8_t u = (order == YUVP) ? Cb[i] : Cr[i]; + uint8_t v = (order == YUVP) ? Cr[i] : Cb[i]; + for (; x < x0; x++) { + *(p++) = u; + *(p++) = v; + } + } + + for (; y < h; y++) + for (x = 0; x < w; x++) { + *(p++) = 0x80; + *(p++) = 0x80; + } + + return (p); +} + +/* write packed YCbCr plane */ +static inline uint8_t *fill_bars_yuv(uint8_t *p, unsigned w, unsigned h, + format_type_t order) +{ + unsigned x, y, i; + unsigned y0 = (h + 31) / 30; + packed_t yuv; + uint32_t *q = (uint32_t *)p; + w /= 2; + + yuv.u8[0] = yuv.u8[2] = (order == UYVY) ? 0x80 : 0xff; + yuv.u8[1] = yuv.u8[3] = (order == UYVY) ? 0xff : 0x80; + for (y = 0; y < y0; y++) + for (x = 0; x < w; x++) + *(q++) = yuv.u32[0]; + + for (; y < h - y0; y++) + for (x = 0, i = 0; x < w; i++) { + assert(i < 8); + unsigned x0 = (((i + 1) * w) + 7) >> 3; + assert(x0 <= w); + unsigned v = ((((i & 1) ? y : h - y) * 256) + h - 1) / h; + if (order == UYVY) { + yuv.u8[0] = Cb[i]; + yuv.u8[2] = Cr[i]; + yuv.u8[1] = yuv.u8[3] = v; + } else { + yuv.u8[0] = yuv.u8[2] = v; + yuv.u8[1] = (order == YUYV) ? Cb[i] : Cr[i]; + yuv.u8[3] = (order == YVYU) ? Cr[i] : Cb[i]; + } + for (; x < x0; x++) + *(q++) = yuv.u32[0]; + } + + yuv.u8[0] = yuv.u8[2] = (order == UYVY) ? 0x80 : 0xff; + yuv.u8[1] = yuv.u8[3] = (order == UYVY) ? 0xff : 0x80; + for (; y < h; y++) + for (x = 0; x < w; x++) + *(q++) = yuv.u32[0]; + + return ((uint8_t *)q); +} + +static inline uint8_t *fill_bars_rgb(uint8_t *p, unsigned w, unsigned h, + format_type_t order, int bpp) +{ + unsigned x, y, i; + unsigned y0 = (h + 31) / 30; + packed_t rgb; + + unsigned headlen = y0 * w * bpp / 8; + memset(p, 0xff, headlen); + uint32_t *q = (uint32_t *)(p + headlen); + + for (y = y0; y < h - y0; y++) + for (x = 0, i = 0; x < w; i++) { + assert(i < 8); + /* FIXME clean this up... */ + unsigned x0 = (((i + 1) * w) + 7) >> 3; + assert(x0 <= w); + unsigned yi = (i & 1) ? y : h - y; + unsigned v1, v0; + if (yi < h / 2 - 1) { + v1 = ((yi * 0x180) + h - 1) / h + 0x40; + v0 = 0x00; + } else { + v1 = 0xff; + v0 = (((yi - (h / 2)) * 0x180) + h - 1) / h + 0x40; + } + + uint8_t r = (i & 4) ? v1 : v0; + uint8_t g = (i & 2) ? v1 : v0; + uint8_t b = (i & 1) ? v1 : v0; + if (bpp == 32) { + if (order == RGB888) { + rgb.u8[0] = 0xff; + rgb.u8[1] = r; + rgb.u8[2] = g; + rgb.u8[3] = b; + } else { + rgb.u8[0] = b; + rgb.u8[1] = g; + rgb.u8[2] = r; + rgb.u8[3] = 0xff; + } + for (; x < x0; x++) + *(q++) = rgb.u32[0]; + } else if (bpp == 24) { + rgb.u8[0] = rgb.u8[3] = rgb.u8[6] = rgb.u8[9] = + (order == RGB888) ? r : b; + rgb.u8[1] = rgb.u8[4] = rgb.u8[7] = rgb.u8[10] = g; + rgb.u8[2] = rgb.u8[5] = rgb.u8[8] = rgb.u8[11] = + (order == RGB888) ? b : r; + for (; x < x0; x += 4) { + *(q++) = rgb.u32[0]; + *(q++) = rgb.u32[1]; + *(q++) = rgb.u32[2]; + } + } else { + assert(bpp == 16); + r = ((r + 7) / 8) & 0x1f; + b = ((b + 7) / 8) & 0x1f; + if ((order & 0x0fff) == 0x0555) { + g = ((g + 7) / 8) & 0x1f; + rgb.u16[0] = b | (g << 5) | (r << 10); + } else { + g = ((g + 3) / 4) & 0x3f; + rgb.u16[0] = b | (g << 5) | (r << 11); + } + if (order & 0x1000) + rgb.u16[0] = (rgb.u16[0] >> 8) | (rgb.u16[0] << 8); + rgb.u16[1] = rgb.u16[0]; + for (; x < x0; x += 2) + *(q++) = rgb.u32[0]; + } + } + + memset(q, 0xff, headlen); + return (((uint8_t *)q) + headlen); +} + +int test_image_bars(zbar_image_t *img) +{ + const format_def_t *fmt = alloc_data(img); + if (!fmt) + return (-1); + + unsigned w = zbar_image_get_width(img); + unsigned h = zbar_image_get_height(img); + uint8_t *data = (void *)zbar_image_get_data(img); + assert(data); + uint8_t *p = data; + switch (fmt->type) { + case GRAY: + case YUVP: /* planar YUV */ + case YVUP: + p = fill_bars_y(p, w, h); + if (fmt->type != GRAY) { + w = (w + fmt->xdiv - 1) / fmt->xdiv; + h = (h + fmt->ydiv - 1) / fmt->ydiv; + } else + break; + + if (fmt->format == fourcc('N', 'V', '1', '2') || + fmt->format == fourcc('N', 'V', '2', '1')) + p = fill_bars_nv(p, w, h, fmt->type); + else if (fmt->type == YUVP || fmt->type == YVUP) { + p = fill_bars_uv(p, w, h, (fmt->type == YUVP) ? Cb : Cr); + p = fill_bars_uv(p, w, h, (fmt->type == YUVP) ? Cr : Cb); + } + break; + + case YUYV: /* packed YUV */ + case YVYU: + case UYVY: + p = fill_bars_yuv(p, w, h, fmt->type); + break; + + default: /* RGB */ + p = fill_bars_rgb(p, w, h, fmt->type, fmt->bpp); + break; + } + + assert(p == data + zbar_image_get_data_length(img)); + return (0); +} + +int test_image_ean13(zbar_image_t *img) +{ + unsigned w = 114, h = 85; + zbar_image_set_size(img, w, h); + + const format_def_t *fmt = alloc_data(img); + if (!fmt) + return (-1); + + uint8_t *data = (void *)zbar_image_get_data(img); + unsigned int datalen = zbar_image_get_data_length(img); + assert(data && datalen); + + uint8_t *p = data; + /* FIXME randomize? */ + memset(data, 0x80, datalen); + + int nrep = 1, nskip = 0; + switch (fmt->type) { + case YUVP: /* planar YUV */ + case YVUP: + case GRAY: + break; + + case UYVY: /* packed YUV */ + p++; + case YUYV: + case YVYU: + nskip = 1; + break; + + default: /* RGB */ + nrep = fmt->bpp / 8; + } + + int y = 0, x, i; + for (; y < 10 && y < h; y++) + for (x = 0; x < w; x++) { + for (i = 0; i < nrep; i++) + *p++ = 0xff; + p += nskip; + } + + for (; y < h - 10; y++) { + uint8_t color = 0xff; + const char *c; + for (x = 0, c = encoded_widths; *c; c++) { + int dx; + if (*c == ' ') + continue; + for (dx = *c - '0'; dx > 0; dx--) { + for (i = 0; i < nrep; i++) + *p++ = color; + p += nskip; + x++; + } + color = ~color; + } + assert(!color); + for (; x < w; x++) { + for (i = 0; i < nrep; i++) + *p++ = 0xff; + p += nskip; + } + assert(x == w); + } + + for (; y < h; y++) + for (x = 0; x < w; x++) { + for (i = 0; i < nrep; i++) + *p++ = 0xff; + p += nskip; + } + + if (fmt->type == UYVY) + p--; + assert(p == data + datalen); + return (0); +} diff --git a/test/test_images.h b/test/test_images.h new file mode 100644 index 0000000..47ead45 --- /dev/null +++ b/test/test_images.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _TEST_IMAGES_H_ +#define _TEST_IMAGES_H_ + +#define fourcc zbar_fourcc + +#ifdef __cplusplus + +extern "C" { +int test_image_check_cleanup(void); +int test_image_bars(zbar::zbar_image_t *); +int test_image_ean13(zbar::zbar_image_t *); +} + +#else + +int test_image_check_cleanup(void); +int test_image_bars(zbar_image_t *); +int test_image_ean13(zbar_image_t *); + +#endif + +extern const char *test_image_ean13_data; + +#endif diff --git a/test/test_jpeg.c b/test/test_jpeg.c new file mode 100644 index 0000000..9e1bba6 --- /dev/null +++ b/test/test_jpeg.c @@ -0,0 +1,158 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <argp.h> +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include <zbar.h> + +#include "test_images.h" + +unsigned char jpeg[405] = { + 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, + 72, 0, 72, 0, 0, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, + 219, 0, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 255, 192, 0, 17, 8, 0, 8, + 0, 8, 3, 1, 17, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, + 20, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 255, 196, 0, 32, 16, 0, 1, 2, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 20, 22, 0, 8, + 18, 19, 24, 6, 23, 36, 37, 39, 255, 196, 0, 20, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 255, 196, 0, 35, 17, 0, 2, 1, 1, 7, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 21, 19, 22, 0, 1, 7, 18, 23, + 36, 38, 3, 4, 35, 37, 52, 255, 218, 0, 12, 3, 1, 0, 2, + 17, 3, 17, 0, 63, 0, 118, 93, 56, 89, 200, 157, 68, 199, 111, + 134, 71, 23, 12, 215, 215, 130, 197, 136, 103, 143, 117, 170, 97, 48, + 42, 244, 202, 12, 216, 179, 211, 183, 29, 252, 24, 42, 160, 197, 45, + 65, 146, 62, 181, 91, 48, 134, 52, 246, 76, 170, 151, 4, 42, 137, + 198, 104, 56, 214, 96, 193, 7, 120, 197, 15, 154, 194, 128, 216, 207, + 170, 114, 197, 220, 215, 36, 130, 123, 155, 219, 184, 172, 222, 150, 146, + 23, 191, 47, 17, 204, 2, 197, 155, 246, 180, 206, 226, 223, 255, 217, +}; + +unsigned char rgb[8 * 8 * 3] = { + 255, 255, 255, 176, 238, 176, 94, 220, 94, 60, 213, 60, 60, 213, 60, + 94, 220, 94, 176, 238, 176, 255, 255, 255, 176, 238, 176, 46, 210, 46, + 10, 102, 10, 17, 204, 17, 17, 204, 17, 10, 102, 10, 46, 210, 46, + 176, 238, 176, 94, 220, 94, 19, 204, 19, 9, 102, 9, 17, 204, 17, + 17, 204, 17, 9, 102, 9, 19, 204, 19, 94, 220, 94, 60, 213, 60, + 17, 204, 17, 9, 102, 9, 17, 204, 17, 17, 204, 17, 9, 102, 9, + 17, 204, 17, 60, 213, 60, 60, 213, 60, 17, 204, 17, 17, 204, 17, + 17, 204, 17, 17, 204, 17, 17, 204, 17, 17, 204, 17, 60, 213, 60, + 94, 220, 94, 10, 102, 10, 17, 204, 17, 17, 204, 17, 17, 204, 17, + 17, 204, 17, 10, 102, 10, 94, 220, 94, 176, 238, 176, 46, 210, 46, + 10, 102, 10, 9, 102, 9, 9, 102, 9, 10, 102, 10, 46, 210, 46, + 176, 238, 176, 255, 255, 255, 176, 238, 176, 94, 220, 94, 60, 213, 60, + 60, 213, 60, 94, 220, 94, 176, 238, 176, 255, 255, 255, +}; + +#define PROGRAM_NAME "test_video" + +static const char doc[] = + "\nTest if ZBar is able to handle a video input (camera)\n"; + +static const struct argp_option options[] = { + { "quiet", 'q', 0, 0, "Don't be verbose", 0 }, + { "help", '?', 0, 0, "Give this help list", -1 }, + { "usage", -3, 0, 0, "Give a short usage message", 0 }, + { 0 } +}; + +static int quiet = 0; + +static error_t parse_opt(int k, char *optarg, struct argp_state *state) +{ + switch (k) { + case 'q': + quiet = 1; + break; + case '?': + argp_state_help(state, state->out_stream, + ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC); + exit(0); + case -3: + argp_state_help(state, state->out_stream, ARGP_HELP_USAGE); + exit(0); + default: + return ARGP_ERR_UNKNOWN; + }; + return 0; +} + +static const struct argp argp = { + .options = options, + .parser = parse_opt, + .doc = doc, +}; + +int main(int argc, char **argv) +{ + if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) { + argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME); + return -1; + } + if (!quiet) + zbar_set_verbosity(32); + else + zbar_set_verbosity(0); + + zbar_processor_t *proc = zbar_processor_create(0); + assert(proc); + if (zbar_processor_init(proc, NULL, 1)) + return (2); + + zbar_image_t *img = zbar_image_create(); + zbar_image_set_size(img, 8, 8); + zbar_image_set_format(img, fourcc('J', 'P', 'E', 'G')); + zbar_image_set_data(img, jpeg, sizeof(jpeg), NULL); + + zbar_image_t *test = zbar_image_convert(img, fourcc('Y', '8', '0', '0')); + if (!test) + return (2); + if (!quiet) + printf("converted: %d x %d (%lx) %08lx\n", zbar_image_get_width(test), + zbar_image_get_height(test), zbar_image_get_data_length(test), + zbar_image_get_format(test)); + + if (zbar_process_image(proc, test) < 0) + return (3); + if (zbar_processor_set_visible(proc, 1)) + return (4); + + printf("jpeg PASSED.\n"); + return (0); +} diff --git a/test/test_perl.pl b/test/test_perl.pl new file mode 100755 index 0000000..cc28ffb --- /dev/null +++ b/test/test_perl.pl @@ -0,0 +1,47 @@ +#!/usr/bin/env perl +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +use 5.006; +use warnings; +use strict; + +use Barcode::ZBar; + +Barcode::ZBar::set_verbosity(15); + +my $proc = Barcode::ZBar::Processor->new(1); + +$proc->init($ARGV[0] || '/dev/video0'); + +$proc->set_visible(); +$proc->user_wait(2); + +$proc->set_active(); +$proc->user_wait(5); + +$proc->set_active(0); +$proc->user_wait(2); + +$proc->process_one(); +$proc->user_wait(1); diff --git a/test/test_proc.c b/test/test_proc.c new file mode 100644 index 0000000..46eb992 --- /dev/null +++ b/test/test_proc.c @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <zbar.h> +#include "test_images.h" + +zbar_processor_t *proc = NULL; +int use_threads = 1, use_window = 1; + +int input_wait(int timeout) +{ + if (timeout >= 0) + fprintf(stderr, "waiting %d.%03ds for input...", timeout / 1000, + timeout % 1000); + else + fprintf(stderr, "waiting indefinitely for input..."); + fflush(stderr); + int rc = zbar_processor_user_wait(proc, timeout); + if (rc > 0) + fprintf(stderr, "got input (%02x)\n", rc); + else if (!rc) + fprintf(stderr, "timed out\n"); + else if (zbar_processor_get_error_code(proc) == ZBAR_ERR_CLOSED) + use_window = rc = 0; + fflush(stderr); + return (rc); +} + +int main(int argc, char **argv) +{ + zbar_set_verbosity(127); + char *video_dev = NULL; + uint32_t fmt = 0; + int i, j = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + if (!strncmp(argv[i], "-thr", 4)) + use_threads = 0; + else if (!strncmp(argv[i], "-win", 4)) + use_window = 0; + else + return (1); + } else if (!j++) + video_dev = argv[i]; + else if (j++ == 1) { + int n = strlen(argv[i]); + if (n > 4) + n = 4; + memcpy((char *)&fmt, argv[i], n); + } + } + if (!fmt) + fmt = fourcc('B', 'G', 'R', '3'); + + proc = zbar_processor_create(use_threads); + assert(proc); + fprintf(stderr, "created processor (%sthreaded)\n", + (!use_threads) ? "un" : ""); + fflush(stderr); + + if (zbar_processor_init(proc, NULL, 0)) + return (2); + fprintf(stderr, "initialized (video=disabled, window=disabled)\n"); + fflush(stderr); + + zbar_image_t *img = zbar_image_create(); + zbar_image_set_size(img, 640, 480); + zbar_image_set_format(img, fmt); + test_image_bars(img); + + if (zbar_process_image(proc, img) < 0) + return (3); + fprintf(stderr, "processed test image\n"); + fflush(stderr); + + if (zbar_processor_init(proc, video_dev, use_window)) + return (2); + fprintf(stderr, "reinitialized (video=%s, window=%s)\n", + (video_dev) ? video_dev : "disabled", + (use_window) ? "enabled" : "disabled"); + fflush(stderr); + + if (use_window) { + if (zbar_processor_set_visible(proc, 1)) + return (4); + fprintf(stderr, "window visible\n"); + fflush(stderr); + } + + if (input_wait((use_window && !video_dev) ? -1 : 2000) < 0) + return (5); + + if (zbar_process_image(proc, img) < 0) + return (3); + fprintf(stderr, "processed test image\n"); + fflush(stderr); + zbar_image_destroy(img); + + if (input_wait((use_window && !video_dev) ? -1 : 3333) < 0) + return (5); + + if (video_dev) { + if (zbar_processor_set_active(proc, 1)) + return (3); + fprintf(stderr, "video activated\n"); + fflush(stderr); + + if (input_wait((use_window) ? -1 : 4000) < 0) + return (5); + + if (zbar_processor_set_active(proc, 0)) + return (3); + fprintf(stderr, "video deactivated\n"); + fflush(stderr); + + if (input_wait((use_window) ? -1 : 4000) < 0) + return (5); + + /* FIXME test process_one() */ + } + + if (zbar_process_image(proc, NULL)) + return (3); + fprintf(stderr, "flushed image\n"); + fflush(stderr); + + if (input_wait((use_window && !video_dev) ? -1 : 2500) < 0) + return (5); + + fprintf(stderr, "cleaning up...\n"); + fflush(stderr); + + zbar_processor_destroy(proc); + proc = NULL; + if (test_image_check_cleanup()) + return (32); + return (0); +} diff --git a/test/test_pygtk.py b/test/test_pygtk.py new file mode 100755 index 0000000..fc19fa9 --- /dev/null +++ b/test/test_pygtk.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python2 +#------------------------------------------------------------------------ +# Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ +import sys, os, stat +import pygtk, gtk +import zbarpygtk + +def decoded(zbar, data): + """callback invoked when a barcode is decoded by the zbar widget. + displays the decoded data in the text box + """ + buf = results.props.buffer + end = buf.get_end_iter() + buf.insert(end, data + "\n") + results.scroll_to_iter(end, 0) + +def video_enabled(zbar, param): + """callback invoked when the zbar widget enables or disables + video streaming. updates the status button state to reflect the + current video state + """ + enabled = zbar.get_video_enabled() + if status_button.get_active() != enabled: + status_button.set_active(enabled) + +def video_opened(zbar, param): + """callback invoked when the zbar widget opens or closes a video + device. also called when a device is closed due to error. + updates the status button state to reflect the current video state + """ + opened = zbar.get_video_opened() + status_button.set_sensitive(opened) + set_status_label(opened, zbar.get_video_enabled()) + +def video_changed(widget): + """callback invoked when a new video device is selected from the + drop-down list. sets the new device for the zbar widget, + which will eventually cause it to be opened and enabled + """ + dev = video_list.get_active_text() + if dev[0] == '<': + dev = '' + zbar.set_video_device(dev) + +def status_button_toggled(button): + """callback invoked when the status button changes state + (interactively or programmatically). ensures the zbar widget + video streaming state is consistent and updates the display of the + button to represent the current state + """ + opened = zbar.get_video_opened() + active = status_button.get_active() + if opened and (active != zbar.get_video_enabled()): + zbar.set_video_enabled(active) + set_status_label(opened, active) + if active: + status_image.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_BUTTON) + else: + status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_BUTTON) + +def open_button_clicked(button): + """callback invoked when the 'Open' button is clicked. pops up an + 'Open File' dialog which the user may use to select an image file. + if the image is successfully opened, it is passed to the zbar + widget which displays it and scans it for barcodes. results are + returned using the same hook used to report video results + """ + dialog = gtk.FileChooserDialog("Open Image File", window, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)) + global open_file + if open_file: + dialog.set_filename(open_file) + try: + if dialog.run() == gtk.RESPONSE_ACCEPT: + open_file = dialog.get_filename() + pixbuf = gtk.gdk.pixbuf_new_from_file(open_file) + if pixbuf: + zbar.scan_image(pixbuf) + finally: + dialog.destroy() + +def set_status_label(opened, enabled): + """update status button label to reflect indicated state.""" + if not opened: + label = "closed" + elif enabled: + label = "enabled" + else: + label = "disabled" + status_button.set_label(label) + +open_file = None +video_device = None +if len(sys.argv) > 1: + video_device = sys.argv[1] + +# threads *must* be properly initialized to use zbarpygtk +gtk.gdk.threads_init() +gtk.gdk.threads_enter() + +window = gtk.Window() +window.set_title("test_pygtk") +window.set_border_width(8) +window.connect("destroy", gtk.main_quit) + +zbar = zbarpygtk.Gtk() +zbar.connect("decoded-text", decoded) + +# video device list combo box +video_list = gtk.combo_box_new_text() +video_list.connect("changed", video_changed) + +# enable/disable status button +status_button = gtk.ToggleButton("closed") +status_image = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_BUTTON) +status_button.set_image(status_image) +status_button.set_sensitive(False) + +# bind status button state and video state +status_button.connect("toggled", status_button_toggled) +zbar.connect("notify::video-enabled", video_enabled) +zbar.connect("notify::video-opened", video_opened) + +# open image file button +open_button = gtk.Button(stock=gtk.STOCK_OPEN) +open_button.connect("clicked", open_button_clicked) + +# populate video devices in combo box +video_list.append_text("<none>") +video_list.set_active(0) +for (root, dirs, files) in os.walk("/dev"): + for dev in files: + path = os.path.join(root, dev) + if not os.access(path, os.F_OK): + continue + info = os.stat(path) + if stat.S_ISCHR(info.st_mode) and os.major(info.st_rdev) == 81: + video_list.append_text(path) + if path == video_device: + video_list.set_active(len(video_list.get_model()) - 1) + video_device = None + +if video_device is not None: + video_list.append_text(video_device) + video_list.set_active(len(video_list.get_model()) - 1) + video_device = None + +# combine combo box and buttons horizontally +hbox = gtk.HBox(spacing=8) +hbox.pack_start(video_list) +hbox.pack_start(status_button, expand=False) +hbox.pack_start(open_button, expand=False) + +# text box for holding results +results = gtk.TextView() +results.set_size_request(320, 64) +results.props.editable = results.props.cursor_visible = False +results.set_left_margin(4) + +# combine inputs, scanner, and results vertically +vbox = gtk.VBox(spacing=8) +vbox.pack_start(hbox, expand=False) +vbox.pack_start(zbar) +vbox.pack_start(results, expand=False) + +window.add(vbox) +window.set_geometry_hints(zbar, min_width=320, min_height=240) +window.show_all() + +gtk.main() +gtk.gdk.threads_leave() diff --git a/test/test_python.py b/test/test_python.py new file mode 100755 index 0000000..7ffdf9f --- /dev/null +++ b/test/test_python.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +#------------------------------------------------------------------------ +# Copyright 2019 (c) Mauro Carvalho Chehab <mchehab+samsung@kernel.org> +# +# 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. +# +#------------------------------------------------------------------------ + +from __future__ import print_function +import zbar, sys + +try: + from PIL import Image +except: + try: + import Image + except: + print("No image library on python. Aborting test") + sys.exit() + +if len(sys.argv) < 1 or len(sys.argv) > 3: + print("Usage: %s <file name> [<expected text to check>]") + sys.exit(-1) + +filename = sys.argv[1] + +org_image = Image.open(filename) + +image = org_image.convert(mode='L') + +width = image.size[0] +height = image.size[1] +raw_data = image.tobytes() + +scanner = zbar.ImageScanner() +image = zbar.Image(width=width, height=height, format='Y800', data=raw_data) +scanner.scan(image) + +if len(sys.argv) == 3: + text = sys.argv[2] + + found = False + for symbol in image: + found = True + if symbol.data == text: + print("OK") + else: + print("Expecting %s, received %s" % (text, symbol.data)) + + if not found: + print("Can't process file") +else: + for symbol in image: + print("Decoded as %s" % symbol.data) diff --git a/test/test_video.c b/test/test_video.c new file mode 100644 index 0000000..b8618d6 --- /dev/null +++ b/test/test_video.c @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <argp.h> +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <stdio.h> +#include <string.h> +#include <time.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <assert.h> + +#include <zbar.h> +#include "test_images.h" + +#ifdef _WIN32 +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif + +zbar_video_t *video; + +#define PROGRAM_NAME "test_video" + +static const char doc[] = + "\nTest if ZBar is able to handle a video input (camera)\n"; + +static const struct argp_option options[] = { + { "quiet", 'q', 0, 0, "Don't be verbose", 0 }, + { "dev", 'd', "devnode", 0, "open devnode for video in", 0 }, + { "format", 'f', "fourcc", 0, "Stop after #seconds", 0 }, + { "help", '?', 0, 0, "Give this help list", -1 }, + { "usage", -3, 0, 0, "Give a short usage message", 0 }, + { 0 } +}; + +static int quiet = 0; +uint32_t vidfmt = fourcc('B', 'G', 'R', '3'); +char *dev = ""; + +static error_t parse_opt(int k, char *optarg, struct argp_state *state) +{ + switch (k) { + case 'q': + quiet = 1; + break; + case 'f': { + int len = strlen(optarg); + if (len > 4) + len = 4; + memcpy((char *)&vidfmt, optarg, len); + if (len < 4) + memset(len + (char *)&vidfmt, 0, 4 - len); + break; + } + case 'd': + dev = optarg; + break; + case '?': + argp_state_help(state, state->out_stream, + ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC); + exit(0); + case -3: + argp_state_help(state, state->out_stream, ARGP_HELP_USAGE); + exit(0); + default: + return ARGP_ERR_UNKNOWN; + }; + return 0; +} + +static const struct argp argp = { + .options = options, + .parser = parse_opt, + .doc = doc, +}; + +int main(int argc, char *argv[]) +{ + if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) { + argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME); + return -1; + } + if (!quiet) + zbar_set_verbosity(31); + else + zbar_set_verbosity(0); + + video = zbar_video_create(); + if (!video) { + fprintf(stderr, "unable to allocate memory?!\n"); + return (1); + } + + zbar_video_request_size(video, 640, 480); + + if (zbar_video_open(video, dev)) { + zbar_video_error_spew(video, 0); + fprintf(stderr, + "ERROR: unable to access your video device\n" + "this program requires video capture support using" + " v4l version 1 or 2 or VfW\n" + " - is your video device located at \"%s\"?\n" + " - is your video driver installed? (check dmesg)\n" + " - make sure you have the latest drivers\n" + " - do you have read/write permission to access it?\n" + " - is another application is using it?\n" + " - does the device support video capture?\n" + " - does the device work with other programs?\n", + dev); + return (1); + } + if (!quiet) { + fprintf(stderr, "opened video device: %s (fd=%d)\n", dev, + zbar_video_get_fd(video)); + fflush(stderr); + } + + if (zbar_video_init(video, vidfmt)) { + fprintf(stderr, "ERROR: failed to set format: %.4s(%08x)\n", + (char *)&vidfmt, vidfmt); + return (zbar_video_error_spew(video, 0)); + } + + if (zbar_video_enable(video, 1)) { + fprintf(stderr, "ERROR: starting video stream\n"); + return (zbar_video_error_spew(video, 0)); + } + if (!quiet) { + fprintf(stderr, "started video stream...\n"); + fflush(stderr); + } + + zbar_image_t *image = zbar_video_next_image(video); + if (!image) { + fprintf(stderr, "ERROR: unable to capture image\n"); + return (zbar_video_error_spew(video, 0)); + } + uint32_t format = zbar_image_get_format(image); + unsigned width = zbar_image_get_width(image); + unsigned height = zbar_image_get_height(image); + const uint8_t *data = zbar_image_get_data(image); + if (!quiet) { + fprintf(stderr, "captured image: %d x %d %.4s @%p\n", width, height, + (char *)&format, data); + fflush(stderr); + } + + zbar_image_destroy(image); + + if (!quiet) { + fprintf(stderr, "\nstreaming 100 frames...\n"); + fflush(stderr); + } + + struct timespec start, end; +#if _POSIX_TIMERS > 0 + clock_gettime(CLOCK_REALTIME, &start); +#else + struct timeval ustime; + gettimeofday(&ustime, NULL); + start.tv_nsec = ustime.tv_usec * 1000; + start.tv_sec = ustime.tv_sec; +#endif + + int i; + for (i = 0; i < 100; i++) { + zbar_image_t *image = zbar_video_next_image(video); + if (!image) { + fprintf(stderr, "ERROR: unable to capture image\n"); + return (zbar_video_error_spew(video, 0)); + } + zbar_image_destroy(image); + } + +#if _POSIX_TIMERS > 0 + clock_gettime(CLOCK_REALTIME, &end); +#else + gettimeofday(&ustime, NULL); + end.tv_nsec = ustime.tv_usec * 1000; + end.tv_sec = ustime.tv_sec; +#endif + double ms = (end.tv_sec - start.tv_sec + + (end.tv_nsec - start.tv_nsec) / 1000000000.); + double fps = i / ms; + if (!quiet) { + fprintf(stderr, "\nprocessed %d images in %gs @%gfps\n", i, ms, fps); + fflush(stderr); + } + + if (zbar_video_enable(video, 0)) { + fprintf(stderr, "ERROR: while stopping video stream\n"); + return (zbar_video_error_spew(video, 0)); + } + + if (!quiet) { + fprintf(stderr, "\ncleaning up...\n"); + fflush(stderr); + } + + zbar_video_destroy(video); + + if (test_image_check_cleanup()) + return (32); + + fprintf(stderr, "video PASSED.\n"); + return (0); +} diff --git a/test/test_window.c b/test/test_window.c new file mode 100644 index 0000000..894c8af --- /dev/null +++ b/test/test_window.c @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <stdio.h> +#include <string.h> + +#include <zbar.h> +#include "processor.h" +#include "test_images.h" + +zbar_processor_t proc; + +static void input_wait() +{ + fprintf(stderr, "waiting for input...\n"); + if (_zbar_window_handle_events(&proc, 1) < 0) + zbar_processor_error_spew(&proc, 1); +} + +int main(int argc, char *argv[]) +{ + zbar_set_verbosity(32); + + err_init(&proc.err, ZBAR_MOD_PROCESSOR); + proc.window = zbar_window_create(); + if (!proc.window) { + fprintf(stderr, "unable to allocate memory?!\n"); + return (1); + } + + int width = 640; + int height = 480; + + if (_zbar_window_open(&proc, "zbar window test", width, height) || + zbar_window_attach(proc.window, proc.display, proc.xwin) || + _zbar_window_set_visible(&proc, 1)) { + fprintf(stderr, "failed to open test window\n"); + return (1); + } + input_wait(); + + zbar_image_t *img = zbar_image_create(); + zbar_image_set_size(img, width, height); + zbar_image_set_format(img, fourcc('B', 'G', 'R', '4')); + /*fourcc('I','4','2','0')*/ + /*fourcc('Y','V','1','2')*/ + /*fourcc('U','Y','V','Y')*/ + /*fourcc('Y','U','Y','V')*/ + /*fourcc('R','G','B','3')*/ + /*fourcc('Y','8','0','0')*/ + test_image_bars(img); + + if (zbar_window_draw(proc.window, img) || zbar_window_redraw(proc.window)) { + fprintf(stderr, "error drawing image\n"); + return (1); + } + zbar_image_destroy(img); + img = NULL; + + input_wait(); + + /* FIXME display cmd arg images? or formats? */ + + fprintf(stderr, "cleaning up\n"); + zbar_window_destroy(proc.window); + + /* FIXME destructor check? */ + if (test_image_check_cleanup()) + return (32); + return (0); +} diff --git a/zbar-gtk.pc.in b/zbar-gtk.pc.in new file mode 100644 index 0000000..c82c169 --- /dev/null +++ b/zbar-gtk.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: zbar-gtk +Description: bar code scanning and decoding GTK widget +URL: http://zbar.sourceforge.net +Version: @VERSION@ +Requires: zbar, gtk+-@GTK_VERSION_MAJOR@, gthread-2.0 +Libs: -L${libdir} -lzbargtk +Cflags: -I${includedir} diff --git a/zbar-qt5.pc.in b/zbar-qt5.pc.in new file mode 100644 index 0000000..3378993 --- /dev/null +++ b/zbar-qt5.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: zbar-qt +Description: bar code scanning and decoding Qt5 widget +URL: http://zbar.sourceforge.net +Version: @VERSION@ +Requires: zbar, Qt5Core >= 5, Qt5Gui >= 5 +Libs: -L${libdir} -lzbarqt +Cflags: -I${includedir} diff --git a/zbar.ico b/zbar.ico Binary files differnew file mode 100644 index 0000000..82a09fa --- /dev/null +++ b/zbar.ico diff --git a/zbar.nsi b/zbar.nsi new file mode 100644 index 0000000..a5da39b --- /dev/null +++ b/zbar.nsi @@ -0,0 +1,289 @@ +#------------------------------------------------------------------------ +# Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> +# +# This file is part of the ZBar Bar Code Reader. +# +# The ZBar Bar Code Reader is free software; you can redistribute it +# and/or modify it under the terms of the GNU Lesser Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The ZBar Bar Code Reader is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with the ZBar Bar Code Reader; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# http://sourceforge.net/projects/zbar +#------------------------------------------------------------------------ + +!ifndef VERSION + !define VERSION "test" +!endif +!ifndef PREFIX + !define PREFIX "\usr\mingw32\usr" +!endif + +!define ZBAR_KEY "Software\ZBar" +!define UNINSTALL_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\ZBar" + +OutFile zbar-${VERSION}-setup.exe + +SetCompressor /SOLID bzip2 + +InstType "Typical" +InstType "Full" + +InstallDir $PROGRAMFILES\ZBar +InstallDirRegKey HKLM ${ZBAR_KEY} "InstallDir" + +!define SMPROG_ZBAR "$SMPROGRAMS\ZBar Bar Code Reader" + +Icon ${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico +UninstallIcon ${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico + +# do we need admin to install uninstall info? +#RequestExecutionLevel admin + + +!include "MUI2.nsh" +!include "Memento.nsh" + + +Name "ZBar" +Caption "ZBar ${VERSION} Setup" + + +!define MEMENTO_REGISTRY_ROOT HKLM +!define MEMENTO_REGISTRY_KEY ${UNINSTALL_KEY} + +!define MUI_ABORTWARNING +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_UNFINISHPAGE_NOAUTOCLOSE + +!define MUI_ICON ${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico +!define MUI_UNICON ${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico + +!define MUI_WELCOMEFINISHPAGE_BITMAP ${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp +!define MUI_UNWELCOMEFINISHPAGE_BITMAP ${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP ${NSISDIR}\Contrib\Graphics\Header\orange.bmp +!define MUI_HEADERIMAGE_UNBITMAP ${NSISDIR}\Contrib\Graphics\Header\orange-uninstall.bmp + +!define MUI_WELCOMEPAGE_TITLE "Welcome to the ZBar ${VERSION} Setup Wizard" +!define MUI_WELCOMEPAGE_TEXT \ + "This wizard will guide you through the installation of the \ + ZBar Bar Code Reader version ${VERSION}." + +!insertmacro MUI_PAGE_WELCOME + +!insertmacro MUI_PAGE_LICENSE "share\doc\zbar\LICENSE.md" + +!define MUI_COMPONENTSPAGE_SMALLDESC +!define MUI_COMPONENTSPAGE_CHECKBITMAP ${NSISDIR}\Contrib\Graphics\Checks\simple-round2.bmp + +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +Function ShowREADME + Exec '"notepad.exe" "$INSTDIR\README-windows.md"' +FunctionEnd + +!define MUI_FINISHPAGE_NOREBOOTSUPPORT +!define MUI_FINISHPAGE_SHOWREADME +!define MUI_FINISHPAGE_SHOWREADME_FUNCTION ShowREADME +!define MUI_FINISHPAGE_LINK \ + "Visit the ZBar website for the latest news, FAQs and support" +!define MUI_FINISHPAGE_LINK_LOCATION "https://github.com/mchehab/zbar" + +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +Section "ZBar Core Files (required)" SecCore + DetailPrint "Installing ZBar Program and Library..." + SectionIn 1 2 RO + + SetOutPath $INSTDIR + File share\doc\zbar\README-windows.md + File share\doc\zbar\NEWS.md + File share\doc\zbar\TODO.md + File share\doc\zbar\COPYING + File share\doc\zbar\LICENSE.md + + # emit a batch file to add the install directory to the path + FileOpen $0 zbarvars.bat w + FileWrite $0 "@rem Add the ZBar installation directory to the path$\n" + FileWrite $0 "@rem so programs may be run from the command prompt$\n" + FileWrite $0 "@set PATH=%PATH%;$INSTDIR\bin$\n" + FileWrite $0 "@cd /D $INSTDIR$\n" + FileWrite $0 "@echo For basic command instructions type:$\n" + FileWrite $0 "@echo zbarcam --help$\n" + FileWrite $0 "@echo zbarimg --help$\n" + FileWrite $0 "@echo Try running:$\n" + FileWrite $0 "@echo zbarimg -d examples\barcode.png$\n" + FileClose $0 + + SetOutPath $INSTDIR\bin + File bin\libzbar-0.dll + File bin\zbarimg.exe + File bin\zbarcam.exe + + # dependencies + File ${PREFIX}\bin\zlib1.dll + File ${PREFIX}\bin\libjpeg-7.dll + File ${PREFIX}\bin\libpng12-0.dll + File ${PREFIX}\bin\libtiff-3.dll + File ${PREFIX}\bin\libxml2-2.dll + File ${PREFIX}\bin\libiconv-2.dll + File ${PREFIX}\bin\libMagickCore-2.dll + File ${PREFIX}\bin\libMagickWand-2.dll + + FileOpen $0 zbarcam.bat w + FileWrite $0 "@set PATH=%PATH%;$INSTDIR\bin$\n" + FileWrite $0 "@echo This is the zbarcam output window.$\n" + FileWrite $0 "@echo Hold a bar code in front of the camera (make sure it's in focus!)$\n" + FileWrite $0 "@echo and decoded results will appear below.$\n" + FileWrite $0 "@echo.$\n" + FileWrite $0 "@echo Initializing camera, please wait...$\n" + FileWrite $0 "@echo.$\n" + FileWrite $0 "@zbarcam.exe --prescale=640x480$\n" + FileWrite $0 "@if errorlevel 1 pause$\n" + FileClose $0 + + SetOutPath $INSTDIR\doc + File share\doc\zbar\html\* + + SetOutPath $INSTDIR\examples + File share\zbar\barcode.png +SectionEnd + +#SectionGroup "Start Menu and Desktop Shortcuts" SecShortcuts + Section "Start Menu Shortcuts" SecShortcutsStartMenu + DetailPrint "Creating Start Menu Shortcuts..." + SectionIn 1 2 + SetOutPath "${SMPROG_ZBAR}" + #CreateShortCut "${SMPROG_ZBAR}\ZBar.lnk" "$INSTDIR\ZBar.exe" + CreateDirectory "${SMPROG_ZBAR}" + CreateShortCut "zbarcam.lnk" "$\"$INSTDIR\bin\zbarcam.bat$\"" "" \ + "$INSTDIR\bin\zbarcam.exe" + ExpandEnvStrings $0 '%comspec%' + CreateShortCut "ZBar Command Prompt.lnk" \ + $0 "/k $\"$\"$INSTDIR\zbarvars.bat$\"$\"" $0 + CreateShortCut "Command Reference.lnk" \ + "$\"$INSTDIR\doc\ref.html$\"" + SectionEnd + +# Section "Desktop Shortcut" SecShortcutsDesktop +# DetailPrint "Creating Desktop Shortcut..." +# SectionIn 1 2 +# SetOutPath $INSTDIR +# #CreateShortCut "$DESKTOP\ZBar.lnk" "$INSTDIR\ZBar.exe" +# SectionEnd +#SectionGroupEnd + +Section "Development Headers and Libraries" SecDevel + DetailPrint "Installing ZBar Development Files..." + SectionIn 2 + + SetOutPath $INSTDIR\include + File include\zbar.h + + SetOutPath $INSTDIR\include\zbar + File include\zbar\Video.h + File include\zbar\Exception.h + File include\zbar\Symbol.h + File include\zbar\Image.h + File include\zbar\ImageScanner.h + File include\zbar\Window.h + File include\zbar\Processor.h + File include\zbar\Decoder.h + File include\zbar\Scanner.h + + SetOutPath $INSTDIR\lib + File lib\libzbar-0.def + File lib\libzbar-0.lib + File lib\libzbar.dll.a + + SetOutPath $INSTDIR\examples + File share\zbar\scan_image.cpp + File share\zbar\scan_image.vcproj +SectionEnd + +Section -post + DetailPrint "Creating Registry Keys..." + SetOutPath $INSTDIR + WriteRegStr HKLM ${ZBAR_KEY} "InstallDir" $INSTDIR + + # register uninstaller + WriteRegStr HKLM ${UNINSTALL_KEY} "UninstallString" \ + "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM ${UNINSTALL_KEY} "QuietUninstallString" \ + "$\"$INSTDIR\uninstall.exe$\" /S" + WriteRegStr HKLM ${UNINSTALL_KEY} "InstallLocation" "$\"$INSTDIR$\"" + + WriteRegStr HKLM ${UNINSTALL_KEY} "DisplayName" "ZBar Bar Code Reader" + WriteRegStr HKLM ${UNINSTALL_KEY} "DisplayIcon" "$INSTDIR\bin\zbarimg.exe,0" + WriteRegStr HKLM ${UNINSTALL_KEY} "DisplayVersion" "${VERSION}" + + WriteRegStr HKLM ${UNINSTALL_KEY} "URLInfoAbout" "http://zbar.sf.net/" + WriteRegStr HKLM ${UNINSTALL_KEY} "HelpLink" "http://zbar.sf.net/" + WriteRegDWORD HKLM ${UNINSTALL_KEY} "NoModify" "1" + WriteRegDWORD HKLM ${UNINSTALL_KEY} "NoRepair" "1" + + DetailPrint "Generating Uninstaller..." + WriteUninstaller $INSTDIR\uninstall.exe +SectionEnd + + +Section Uninstall + DetailPrint "Uninstalling ZBar Bar Code Reader.." + + DetailPrint "Deleting Files..." + Delete $INSTDIR\examples\barcode.png + Delete $INSTDIR\examples\scan_image.cpp + Delete $INSTDIR\examples\scan_image.vcproj + RMDir $INSTDIR\examples + RMDir /r $INSTDIR\include + RMDir /r $INSTDIR\doc + RMDir /r $INSTDIR\lib + RMDir /r $INSTDIR\bin + Delete $INSTDIR\README-windows.md + Delete $INSTDIR\NEWS.md + Delete $INSTDIR\TODO.md + Delete $INSTDIR\COPYING + Delete $INSTDIR\LICENSE.md + Delete $INSTDIR\zbarvars.bat + Delete $INSTDIR\uninstall.exe + RMDir $INSTDIR + + DetailPrint "Removing Shortcuts..." + RMDir /r "${SMPROG_ZBAR}" + + DetailPrint "Deleting Registry Keys..." + DeleteRegKey HKLM ${ZBAR_KEY} + DeleteRegKey HKLM ${UNINSTALL_KEY} +SectionEnd + + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SecCore} \ + "The core files required to use the bar code reader" +# !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcuts} \ +# "Adds icons to your start menu and/or your desktop for easy access" + !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcutsStartMenu} \ + "Adds shortcuts to your start menu" +# !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcutsDesktop} \ +# "Adds an icon on your desktop" + !insertmacro MUI_DESCRIPTION_TEXT ${SecDevel} \ + "Optional files used to develop other applications using ZBar" +!insertmacro MUI_FUNCTION_DESCRIPTION_END diff --git a/zbar.pc.in b/zbar.pc.in new file mode 100644 index 0000000..953a5ca --- /dev/null +++ b/zbar.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: zbar +Description: bar code scanning and decoding +URL: http://zbar.sourceforge.net +Version: @VERSION@ +Libs: -L${libdir} -lzbar +Cflags: -I${includedir} diff --git a/zbar.spec.in b/zbar.spec.in new file mode 100644 index 0000000..f4f98a5 --- /dev/null +++ b/zbar.spec.in @@ -0,0 +1,214 @@ +Name: zbar +Summary: bar code scanning and decoding +Version: @VERSION@ +Release: 1 +License: LGPL +Group: Development/Libraries +URL: http://zbar.sourceforge.net +Packager: Vit Hrachovy <fangorn34@users.sourceforge.net> +Source: zbar-%{version}.tar.gz +Requires: ImageMagick-c++ +BuildPreReq: pkgconfig, libX11-devel, python-devel, gtk2-devel, pygtk2-devel, qt-devel >= 4, ImageMagick-c++-devel +Prefix: %{_prefix} +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +%description +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +%package devel +Group: Development/Libraries +Summary: bar code library extra development files +Requires: %{name} = %{version} + +%description devel +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains header files and additional libraries used for +developing applications that read bar codes with this library. + +%package gtk +Group: Development/Libraries +Summary: bar code reader GTK widget +Requires: %{name} = %{version}, gtk2 + +%description gtk +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains a bar code scanning widget for use with GUI +applications based on GTK+-2.0. + +%package gtk-devel +Group: Development/Libraries +Summary: bar code reader GTK widget extra development files +Requires: %{name}-gtk = %{version}, %{name}-devel = %{version}, gtk2-devel + +%description gtk-devel +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains header files and additional libraries used for +developing GUI applications based on GTK+-2.0 that include a bar code +scanning widget. + +%package pygtk +Group: Development/Libraries +Summary: bar code reader PyGTK widget +Requires: %{name} = %{version}, pygtk2 + +%description pygtk +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains a bar code scanning widget for use in GUI +applications based on PyGTK. + +%package qt +Group: Development/Libraries +Summary: bar code reader Qt widget +Requires: %{name} = %{version}, qt >= 4 + +%description qt +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains a bar code scanning widget for use with GUI +applications based on Qt4. + +%package qt-devel +Group: Development/Libraries +Summary: bar code reader Qt widget extra development files +Requires: %{name}-qt = %{version}, %{name}-devel = %{version}, qt-devel >= 4 + +%description qt-devel +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 93, Code 39 +and Interleaved 2 of 5. The flexible, layered architecture features a +fast, streaming interface with a minimal memory footprint. + +This package contains header files and additional libraries used for +developing GUI applications based on Qt4 that include a bar code +scanning widget. + +%prep +%setup -q -n zbar-%{version} + +%build +./configure --host=%{_host} --build=%{_build} \ + CFLAGS="${CFLAGS:-%optflags}" \ + CXXFLAGS="${CXXFLAGS:-%optflags}" \ + --prefix=$RPM_BUILD_ROOT/%{_prefix} \ + --docdir=$RPM_BUILD_ROOT/%{_docdir}/%{name}-%{version} +make + +%install +rm -rf $RPM_BUILD_ROOT +make install + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +ldconfig + +%postun +ldconfig + +%files +%defattr(-,root,root) +%{_bindir}/zbarimg +%{_bindir}/zbarcam +%{_libdir}/libzbar.so.* +%{_datadir}/man/man1/* +%doc %{_docdir}/%{name}-%{version}/COPYING +%doc %{_docdir}/%{name}-%{version}/HACKING.md +%doc %{_docdir}/%{name}-%{version}/INSTALL +%doc %{_docdir}/%{name}-%{version}/LICENSE.md +%doc %{_docdir}/%{name}-%{version}/NEWS.md +%doc %{_docdir}/%{name}-%{version}/README +%doc %{_docdir}/%{name}-%{version}/TODO.md + +%files devel +%defattr(-,root,root) +%{_libdir}/libzbar.la +%{_libdir}/libzbar.a +%{_libdir}/libzbar.so +%{_libdir}/pkgconfig/zbar.pc +%{_includedir}/zbar.h +%{_includedir}/zbar/Exception.h +%{_includedir}/zbar/Symbol.h +%{_includedir}/zbar/Image.h +%{_includedir}/zbar/Scanner.h +%{_includedir}/zbar/Decoder.h +%{_includedir}/zbar/ImageScanner.h +%{_includedir}/zbar/Video.h +%{_includedir}/zbar/Window.h +%{_includedir}/zbar/Processor.h + +%files gtk +%defattr(-,root,root) +%{_libdir}/libzbargtk.so.* + +%files gtk-devel +%defattr(-,root,root) +%{_libdir}/libzbargtk.la +%{_libdir}/libzbargtk.a +%{_libdir}/libzbargtk.so +%{_libdir}/pkgconfig/zbar-gtk.pc +%{_includedir}/zbar/zbargtk.h + +%files pygtk +%defattr(-,root,root) +%{_libdir}/python* + +%files qt +%defattr(-,root,root) +%{_libdir}/libzbarqt.so.* + +%files qt-devel +%defattr(-,root,root) +%{_libdir}/libzbarqt.la +%{_libdir}/libzbarqt.a +%{_libdir}/libzbarqt.so +%{_libdir}/pkgconfig/zbar-qt.pc +%{_includedir}/zbar/QZBar*.h + +%changelog +* Thu Apr 21 2009 spadix <spadix@users.sourceforge.net> +- version 0.7 project name change + +* Thu Jul 24 2008 spadix <spadix@users.sourceforge.net> +- version 0.5 updates (new widget packages) + +* Sat May 31 2008 spadix <spadix@users.sourceforge.net> +- version 0.4 updates (description and library version) + +* Tue Feb 19 2008 spadix <spadix@users.sourceforge.net> +- version 0.3 updates (dependencies and files) + +* Fri Jun 08 2007 spadix <spadix@users.sourceforge.net> +- first release + +* Tue May 22 2007 Vít Hrachový <fangorn34@users.sourceforge.net> +- Initial draft diff --git a/zbar/Makefile.am b/zbar/Makefile.am new file mode 100644 index 0000000..ca54d01 --- /dev/null +++ b/zbar/Makefile.am @@ -0,0 +1,137 @@ +lib_LTLIBRARIES = libzbar.la +libzbar_la_CPPFLAGS = $(AM_CPPFLAGS) +libzbar_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION) \ + -export-symbols-regex "^(zbar|_zbar.*_error)_.*" $(AM_LDFLAGS) +libzbar_la_LIBADD = $(LTLIBICONV) + +libzbar_la_SOURCES = debug.h config.c \ + error.h error.c symbol.h symbol.c \ + image.h image.c convert.c \ + processor.c processor.h processor/lock.c \ + refcnt.h refcnt.c timer.h mutex.h \ + event.h thread.h \ + window.h window.c video.h video.c \ + img_scanner.h img_scanner.c scanner.c \ + decoder.h decoder.c misc.h misc.c + +EXTRA_libzbar_la_SOURCES = svg.h svg.c + +if ENABLE_EAN +libzbar_la_SOURCES += decoder/ean.h decoder/ean.c +endif +if ENABLE_DATABAR +libzbar_la_SOURCES += decoder/databar.h decoder/databar.c +endif +if ENABLE_CODE128 +libzbar_la_SOURCES += decoder/code128.h decoder/code128.c +endif +if ENABLE_CODE93 +libzbar_la_SOURCES += decoder/code93.h decoder/code93.c +endif +if ENABLE_CODE39 +libzbar_la_SOURCES += decoder/code39.h decoder/code39.c +endif +if ENABLE_CODABAR +libzbar_la_SOURCES += decoder/codabar.h decoder/codabar.c +endif +if ENABLE_I25 +libzbar_la_SOURCES += decoder/i25.h decoder/i25.c +endif +if ENABLE_PDF417 +libzbar_la_SOURCES += decoder/pdf417.h decoder/pdf417.c \ + decoder/pdf417_hash.h +endif +if ENABLE_QRCODE +libzbar_la_SOURCES += qrcode.h \ + decoder/qr_finder.h decoder/qr_finder.c \ + qrcode/qrdec.h qrcode/qrdec.c qrcode/qrdectxt.c \ + qrcode/rs.h qrcode/rs.c \ + qrcode/isaac.h qrcode/isaac.c \ + qrcode/bch15_5.h qrcode/bch15_5.c \ + qrcode/binarize.h qrcode/binarize.c \ + qrcode/util.h qrcode/util.c +endif +if ENABLE_SQCODE +libzbar_la_SOURCES += sqcode.h sqcode.c \ + decoder/sq_finder.h decoder/sq_finder.c +endif + +if WIN32 + +%-rc.o: %.rc + $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< +%-rc.lo: %.rc + $(LIBTOOL) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< + +libzbar_la_SOURCES += processor/win.c libzbar.rc +libzbar_la_CPPFLAGS += -mthreads +libzbar_la_LDFLAGS += -mthreads +# FIXME broken +libzbar_la_LIBADD += libzbar-rc.lo +else +libzbar_la_SOURCES += processor/posix.h processor/posix.c +endif + +if HAVE_V4L2 +libzbar_la_SOURCES += video/v4l.c video/v4l2.c +endif +if HAVE_V4L1 +if !HAVE_V4L2 +libzbar_la_SOURCES += video/v4l.c +endif +libzbar_la_SOURCES += video/v4l1.c +endif +if WIN32 +if HAVE_VIDEO +if !WITH_DIRECTSHOW +libzbar_la_SOURCES += video/vfw.c +libzbar_la_LIBADD += -lvfw32 +else +libzbar_la_SOURCES += video/dshow.c +libzbar_la_CPPFLAGS += -DCOBJMACROS +libzbar_la_LIBADD += -loleaut32 -lole32 +endif +endif +endif +if !HAVE_VIDEO +libzbar_la_SOURCES += video/null.c +endif + +if HAVE_LIBV4L +libzbar_la_LIBADD += $(V4L2_LIBS) +endif + +if HAVE_JPEG +libzbar_la_SOURCES += jpeg.c +endif + +if HAVE_X +libzbar_la_SOURCES += processor/x.c \ + window/x.h window/x.c window/ximage.c +libzbar_la_CPPFLAGS += $(X_CFLAGS) +libzbar_la_LDFLAGS += $(X_LIBS) +libzbar_la_LIBADD += $(X_PRE_LIBS) -lX11 $(X_EXTRA_LIBS) +if HAVE_XV +libzbar_la_SOURCES += window/xv.c +libzbar_la_LIBADD += $(XV_LIBS) +endif +else +if WIN32 +libzbar_la_SOURCES += window/win.h window/win.c \ + window/dib.c +# window/vfw.c -lvfw32 +libzbar_la_LIBADD += -lgdi32 -lwinmm +else +libzbar_la_SOURCES += processor/null.c window/null.c +endif +endif + +if HAVE_DBUS +libzbar_la_LDFLAGS += $(DBUS_LIBS) +endif + +libzbar_la_LDFLAGS += $(AM_LDFLAGS) +libzbar_la_LIBADD += $(AM_LIBADD) diff --git a/zbar/config.c b/zbar/config.c new file mode 100644 index 0000000..1b2a39b --- /dev/null +++ b/zbar/config.c @@ -0,0 +1,171 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdlib.h> /* strtol */ +#include <string.h> /* strchr, strncmp, strlen */ +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <zbar.h> + +int zbar_parse_config(const char *cfgstr, zbar_symbol_type_t *sym, + zbar_config_t *cfg, int *val) +{ + const char *dot, *eq; + int len; + char negate; + if (!cfgstr) + return (1); + + dot = strchr(cfgstr, '.'); + if (dot) { + int len = dot - cfgstr; + if (!len || (len == 1 && !strncmp(cfgstr, "*", len))) + *sym = 0; + else if (len < 2) + return (1); + else if (!strncmp(cfgstr, "qrcode", len)) + *sym = ZBAR_QRCODE; + else if (!strncmp(cfgstr, "sqcode", len)) + *sym = ZBAR_SQCODE; + else if (!strncmp(cfgstr, "db", len)) + *sym = ZBAR_DATABAR; + else if (len < 3) + return (1); + else if (!strncmp(cfgstr, "upca", len)) + *sym = ZBAR_UPCA; + else if (!strncmp(cfgstr, "upce", len)) + *sym = ZBAR_UPCE; + else if (!strncmp(cfgstr, "ean13", len)) + *sym = ZBAR_EAN13; + else if (!strncmp(cfgstr, "ean8", len)) + *sym = ZBAR_EAN8; + else if (!strncmp(cfgstr, "ean5", len)) + *sym = ZBAR_EAN5; + else if (!strncmp(cfgstr, "ean2", len)) + *sym = ZBAR_EAN2; + else if (!strncmp(cfgstr, "composite", len)) + *sym = ZBAR_COMPOSITE; + else if (!strncmp(cfgstr, "i25", len)) + *sym = ZBAR_I25; + else if (len < 4) + return (1); + else if (!strncmp(cfgstr, "scanner", len)) + *sym = ZBAR_PARTIAL; /* FIXME lame */ + else if (!strncmp(cfgstr, "isbn13", len)) + *sym = ZBAR_ISBN13; + else if (!strncmp(cfgstr, "isbn10", len)) + *sym = ZBAR_ISBN10; + else if (!strncmp(cfgstr, "db-exp", len)) + *sym = ZBAR_DATABAR_EXP; + else if (!strncmp(cfgstr, "codabar", len)) + *sym = ZBAR_CODABAR; + else if (len < 6) + return (1); + else if (!strncmp(cfgstr, "code93", len)) + *sym = ZBAR_CODE93; + else if (!strncmp(cfgstr, "code39", len)) + *sym = ZBAR_CODE39; + else if (!strncmp(cfgstr, "pdf417", len)) + *sym = ZBAR_PDF417; + else if (len < 7) + return (1); + else if (!strncmp(cfgstr, "code128", len)) + *sym = ZBAR_CODE128; + else if (!strncmp(cfgstr, "databar", len)) + *sym = ZBAR_DATABAR; + else if (!strncmp(cfgstr, "databar-exp", len)) + *sym = ZBAR_DATABAR_EXP; + else + return (1); + cfgstr = dot + 1; + } else + *sym = 0; + + len = strlen(cfgstr); + eq = strchr(cfgstr, '='); + if (eq) + len = eq - cfgstr; + else + *val = 1; /* handle this here so we can override later */ + negate = 0; + + if (len > 3 && !strncmp(cfgstr, "no-", 3)) { + negate = 1; + cfgstr += 3; + len -= 3; + } + + if (len < 1) + return (1); + else if (!strncmp(cfgstr, "y-density", len)) + *cfg = ZBAR_CFG_Y_DENSITY; + else if (!strncmp(cfgstr, "x-density", len)) + *cfg = ZBAR_CFG_X_DENSITY; + else if (len < 2) + return (1); + else if (!strncmp(cfgstr, "enable", len)) + *cfg = ZBAR_CFG_ENABLE; + else if (len < 3) + return (1); + else if (!strncmp(cfgstr, "disable", len)) { + *cfg = ZBAR_CFG_ENABLE; + negate = !negate; /* no-disable ?!? */ + } else if (!strncmp(cfgstr, "min-length", len)) + *cfg = ZBAR_CFG_MIN_LEN; + else if (!strncmp(cfgstr, "max-length", len)) + *cfg = ZBAR_CFG_MAX_LEN; + else if (!strncmp(cfgstr, "ascii", len)) + *cfg = ZBAR_CFG_ASCII; + else if (!strncmp(cfgstr, "binary", len)) + *cfg = ZBAR_CFG_BINARY; + else if (!strncmp(cfgstr, "add-check", len)) + *cfg = ZBAR_CFG_ADD_CHECK; + else if (!strncmp(cfgstr, "emit-check", len)) + *cfg = ZBAR_CFG_EMIT_CHECK; + else if (!strncmp(cfgstr, "uncertainty", len)) + *cfg = ZBAR_CFG_UNCERTAINTY; + else if (!strncmp(cfgstr, "test-inverted", len)) + *cfg = ZBAR_CFG_TEST_INVERTED; + else if (!strncmp(cfgstr, "position", len)) + *cfg = ZBAR_CFG_POSITION; + else + return (1); + + if (eq) { +#ifdef HAVE_ERRNO_H + errno = 0; +#endif + *val = strtol(eq + 1, NULL, 0); +#ifdef HAVE_ERRNO_H + if (errno) + return (1); +#endif + } + if (negate) + *val = !*val; + + return (0); +} diff --git a/zbar/convert.c b/zbar/convert.c new file mode 100644 index 0000000..98cd94c --- /dev/null +++ b/zbar/convert.c @@ -0,0 +1,1229 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "image.h" +#include "video.h" +#include "window.h" + +/* pack bit size and location offset of a component into one byte + */ +#define RGB_BITS(off, size) ((((8 - (size)) & 0x7) << 5) | ((off)&0x1f)) + +typedef void(conversion_handler_t)(zbar_image_t *, const zbar_format_def_t *, + const zbar_image_t *, + const zbar_format_def_t *); + +typedef struct conversion_def_s { + int cost; /* conversion "badness" */ + conversion_handler_t *func; /* function that accomplishes it */ +} conversion_def_t; + +/* NULL terminated list of known formats, in order of preference + * (NB Cr=V Cb=U) + */ +const uint32_t _zbar_formats[] = { + + /* planar YUV formats */ + fourcc('4', '2', '2', 'P'), /* FIXME also YV16? */ + fourcc('I', '4', '2', '0'), + fourcc('Y', 'U', '1', '2'), /* FIXME also IYUV? */ + fourcc('Y', 'V', '1', '2'), fourcc('4', '1', '1', 'P'), + + /* planar Y + packed UV plane */ + fourcc('N', 'V', '1', '2'), fourcc('N', 'V', '2', '1'), + + /* packed YUV formats */ + fourcc('Y', 'U', 'Y', 'V'), fourcc('U', 'Y', 'V', 'Y'), + fourcc('Y', 'U', 'Y', '2'), /* FIXME add YVYU */ + fourcc('Y', 'U', 'V', '4'), /* FIXME where is this from? */ + + /* packed rgb formats */ + fourcc('R', 'G', 'B', '3'), fourcc(3, 0, 0, 0), fourcc('B', 'G', 'R', '3'), + fourcc('R', 'G', 'B', '4'), fourcc('B', 'G', 'R', '4'), + + fourcc('R', 'G', 'B', 'P'), fourcc('R', 'G', 'B', 'O'), + fourcc('R', 'G', 'B', 'R'), fourcc('R', 'G', 'B', 'Q'), + + fourcc('Y', 'U', 'V', '9'), fourcc('Y', 'V', 'U', '9'), + + /* basic grayscale format */ + fourcc('G', 'R', 'E', 'Y'), fourcc('Y', '8', '0', '0'), + fourcc('Y', '8', ' ', ' '), fourcc('Y', '8', 0, 0), + + /* low quality RGB formats */ + fourcc('R', 'G', 'B', '1'), fourcc('R', '4', '4', '4'), + fourcc('B', 'A', '8', '1'), + + /* unsupported packed YUV formats */ + fourcc('Y', '4', '1', 'P'), fourcc('Y', '4', '4', '4'), + fourcc('Y', 'U', 'V', 'O'), fourcc('H', 'M', '1', '2'), + + /* unsupported packed RGB format */ + fourcc('H', 'I', '2', '4'), + + /* unsupported compressed formats */ + fourcc('J', 'P', 'E', 'G'), fourcc('M', 'J', 'P', 'G'), + fourcc('M', 'P', 'E', 'G'), + + /* terminator */ + 0 +}; + +const int _zbar_num_formats = sizeof(_zbar_formats) / sizeof(uint32_t); + +/* format definitions */ +static const zbar_format_def_t format_defs[] = { + + { fourcc('R', 'G', 'B', '4'), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(8, 8), RGB_BITS(16, 8), RGB_BITS(24, 8) } } }, + { fourcc('B', 'G', 'R', '1'), + ZBAR_FMT_RGB_PACKED, + { { 1, RGB_BITS(0, 3), RGB_BITS(3, 3), RGB_BITS(6, 2) } } }, + { fourcc('4', '2', '2', 'P'), ZBAR_FMT_YUV_PLANAR, { { 1, 0, 0 /*UV*/ } } }, + { + fourcc('Y', '8', '0', '0'), + ZBAR_FMT_GRAY, + }, + { fourcc('Y', 'U', 'Y', '2'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 0, /*YUYV*/ } } }, + { + fourcc('J', 'P', 'E', 'G'), + ZBAR_FMT_JPEG, + }, + { fourcc('Y', 'V', 'Y', 'U'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 1, /*YVYU*/ } } }, + { + fourcc('Y', '8', 0, 0), + ZBAR_FMT_GRAY, + }, + { fourcc('N', 'V', '2', '1'), ZBAR_FMT_YUV_NV, { { 1, 1, 1 /*VU*/ } } }, + { fourcc('N', 'V', '1', '2'), ZBAR_FMT_YUV_NV, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('B', 'G', 'R', '3'), + ZBAR_FMT_RGB_PACKED, + { { 3, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { fourcc('Y', 'V', 'U', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 1 /*VU*/ } } }, + { fourcc('R', 'G', 'B', 'O'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(10, 5), RGB_BITS(5, 5), RGB_BITS(0, 5) } } }, + { fourcc('R', 'G', 'B', 'Q'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(2, 5), RGB_BITS(13, 5), RGB_BITS(8, 5) } } }, + { + fourcc('G', 'R', 'E', 'Y'), + ZBAR_FMT_GRAY, + }, + { fourcc(3, 0, 0, 0), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { + fourcc('Y', '8', ' ', ' '), + ZBAR_FMT_GRAY, + }, + { fourcc('I', '4', '2', '0'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('R', 'G', 'B', '1'), + ZBAR_FMT_RGB_PACKED, + { { 1, RGB_BITS(5, 3), RGB_BITS(2, 3), RGB_BITS(0, 2) } } }, + { fourcc('Y', 'U', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('Y', 'V', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 1 /*VU*/ } } }, + { fourcc('R', 'G', 'B', '3'), + ZBAR_FMT_RGB_PACKED, + { { 3, RGB_BITS(0, 8), RGB_BITS(8, 8), RGB_BITS(16, 8) } } }, + { fourcc('R', '4', '4', '4'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(8, 4), RGB_BITS(4, 4), RGB_BITS(0, 4) } } }, + { fourcc('B', 'G', 'R', '4'), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { fourcc('Y', 'U', 'V', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 0 /*UV*/ } } }, + { + fourcc('M', 'J', 'P', 'G'), + ZBAR_FMT_JPEG, + }, + { fourcc('4', '1', '1', 'P'), ZBAR_FMT_YUV_PLANAR, { { 2, 0, 0 /*UV*/ } } }, + { fourcc('R', 'G', 'B', 'P'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(11, 5), RGB_BITS(5, 6), RGB_BITS(0, 5) } } }, + { fourcc('R', 'G', 'B', 'R'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(3, 5), RGB_BITS(13, 6), RGB_BITS(8, 5) } } }, + { fourcc('Y', 'U', 'Y', 'V'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 0, /*YUYV*/ } } }, + { fourcc('U', 'Y', 'V', 'Y'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 2, /*UYVY*/ } } }, +}; + +static const int num_format_defs = + sizeof(format_defs) / sizeof(zbar_format_def_t); + +#ifdef DEBUG_CONVERT +static int intsort(const void *a, const void *b) +{ + return (*(uint32_t *)a - *(uint32_t *)b); +} +#endif + +/* verify that format list is in required sort order */ +static inline int verify_format_sort(void) +{ + int i; + for (i = 0; i < num_format_defs; i++) { + int j = i * 2 + 1; + if ((j < num_format_defs && + format_defs[i].format < format_defs[j].format) || + (j + 1 < num_format_defs && + format_defs[j + 1].format < format_defs[i].format)) + break; + } + if (i == num_format_defs) + return (0); + + /* spew correct order for fix */ + fprintf(stderr, "ERROR: image format list is not sorted!?\n"); + +#ifdef DEBUG_CONVERT + assert(num_format_defs); + uint32_t sorted[num_format_defs]; + uint32_t ordered[num_format_defs]; + for (i = 0; i < num_format_defs; i++) + sorted[i] = format_defs[i].format; + qsort(sorted, num_format_defs, sizeof(uint32_t), intsort); + for (i = 0; i < num_format_defs; i = i << 1 | 1) + ; + i = (i - 1) / 2; + ordered[i] = sorted[0]; + int j, k; + for (j = 1; j < num_format_defs; j++) { + k = i * 2 + 2; + if (k < num_format_defs) { + i = k; + for (k = k * 2 + 1; k < num_format_defs; k = k * 2 + 1) + i = k; + } else { + for (k = (i - 1) / 2; i != k * 2 + 1; k = (i - 1) / 2) { + assert(i); + i = k; + } + i = k; + } + ordered[i] = sorted[j]; + } + fprintf(stderr, "correct sort order is:"); + for (i = 0; i < num_format_defs; i++) + fprintf(stderr, " %4.4s", (char *)&ordered[i]); + fprintf(stderr, "\n"); +#endif + return (-1); +} + +static inline void uv_round(zbar_image_t *img, const zbar_format_def_t *fmt) +{ + img->width >>= fmt->p.yuv.xsub2; + img->width <<= fmt->p.yuv.xsub2; + img->height >>= fmt->p.yuv.ysub2; + img->height <<= fmt->p.yuv.ysub2; +} + +static inline void uv_roundup(zbar_image_t *img, const zbar_format_def_t *fmt) +{ + unsigned xmask, ymask; + if (fmt->group == ZBAR_FMT_GRAY) + return; + xmask = (1 << fmt->p.yuv.xsub2) - 1; + if (img->width & xmask) + img->width = (img->width + xmask) & ~xmask; + ymask = (1 << fmt->p.yuv.ysub2) - 1; + if (img->height & ymask) + img->height = (img->height + ymask) & ~ymask; +} + +static inline unsigned long uvp_size(const zbar_image_t *img, + const zbar_format_def_t *fmt) +{ + if (fmt->group == ZBAR_FMT_GRAY) + return (0); + return ((img->width >> fmt->p.yuv.xsub2) * + (img->height >> fmt->p.yuv.ysub2)); +} + +static inline uint32_t convert_read_rgb(const uint8_t *srcp, int bpp) +{ + uint32_t p; + if (bpp == 3) { + p = *srcp; + p |= *(srcp + 1) << 8; + p |= *(srcp + 2) << 16; + } else if (bpp == 4) + p = *((uint32_t *)(srcp)); + else if (bpp == 2) + p = *((uint16_t *)(srcp)); + else + p = *srcp; + return (p); +} + +static inline void convert_write_rgb(uint8_t *dstp, uint32_t p, int bpp) +{ + if (bpp == 3) { + *dstp = p & 0xff; + *(dstp + 1) = (p >> 8) & 0xff; + *(dstp + 2) = (p >> 16) & 0xff; + } else if (bpp == 4) + *((uint32_t *)dstp) = p; + else if (bpp == 2) + *((uint16_t *)dstp) = p; + else + *dstp = p; +} + +/* cleanup linked image by unrefing */ +static void cleanup_ref(zbar_image_t *img) +{ + if (img->next) + _zbar_image_refcnt(img->next, -1); +} + +/* resize y plane, drop extra columns/rows from the right/bottom, + * or duplicate last column/row to pad missing data + */ +static inline void convert_y_resize(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt, size_t n) +{ + uint8_t *psrc, *pdst; + unsigned width, height, xpad, y; + + if (dst->width == src->width && dst->height == src->height) { + memcpy((void *)dst->data, src->data, n); + return; + } + psrc = (void *)src->data; + pdst = (void *)dst->data; + width = (dst->width > src->width) ? src->width : dst->width; + xpad = (dst->width > src->width) ? dst->width - src->width : 0; + height = (dst->height > src->height) ? src->height : dst->height; + for (y = 0; y < height; y++) { + memcpy(pdst, psrc, width); + pdst += width; + psrc += src->width; + if (xpad) { + memset(pdst, *(psrc - 1), xpad); + pdst += xpad; + } + } + psrc -= src->width; + for (; y < dst->height; y++) { + memcpy(pdst, psrc, width); + pdst += width; + if (xpad) { + memset(pdst, *(psrc - 1), xpad); + pdst += xpad; + } + } +} + +/* make new image w/reference to the same image data */ +static void convert_copy(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + if (src->width == dst->width && src->height == dst->height) { + zbar_image_t *s = (zbar_image_t *)src; + dst->data = src->data; + dst->datalen = src->datalen; + dst->cleanup = cleanup_ref; + dst->next = s; + _zbar_image_refcnt(s, 1); + } else + /* NB only for GRAY/YUV_PLANAR formats */ + convert_y_resize(dst, dstfmt, src, srcfmt, dst->width * dst->height); +} + +/* append neutral UV plane to grayscale image */ +static void convert_uvp_append(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long n; + uv_roundup(dst, dstfmt); + dst->datalen = uvp_size(dst, dstfmt) * 2; + n = dst->width * dst->height; + dst->datalen += n; + assert(src->datalen >= src->width * src->height); + zprintf(24, "dst=%dx%d (%lx) %lx src=%dx%d %lx\n", dst->width, dst->height, + n, dst->datalen, src->width, src->height, src->datalen); + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + convert_y_resize(dst, dstfmt, src, srcfmt, n); + memset((uint8_t *)dst->data + n, 0x80, dst->datalen - n); +} + +/* interleave YUV planes into packed YUV */ +static void convert_yuv_pack(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long srcm, srcn; + uint8_t flags, *srcy, *dstp; + const uint8_t *srcu, *srcv; + unsigned srcl, xmask, ymask, x, y; + uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + + uv_roundup(dst, dstfmt); + dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; + assert(src->datalen >= srcn + 2 * srcn); + flags = dstfmt->p.yuv.packorder ^ srcfmt->p.yuv.packorder; + srcy = (void *)src->data; + if (flags & 1) { + srcv = (uint8_t *)src->data + srcn; + srcu = srcv + srcm; + } else { + srcu = (uint8_t *)src->data + srcn; + srcv = srcu + srcm; + } + flags = dstfmt->p.yuv.packorder & 2; + + srcl = src->width >> srcfmt->p.yuv.xsub2; + xmask = (1 << srcfmt->p.yuv.xsub2) - 1; + ymask = (1 << srcfmt->p.yuv.ysub2) - 1; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) { + srcy -= src->width; + srcu -= srcl; + srcv -= srcl; + } else if (y & ymask) { + srcu -= srcl; + srcv -= srcl; + } + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + y0 = *(srcy++); + y1 = *(srcy++); + if (!(x & xmask)) { + u = *(srcu++); + v = *(srcv++); + } + } + if (flags) { + *(dstp++) = u; + *(dstp++) = y0; + *(dstp++) = v; + *(dstp++) = y1; + } else { + *(dstp++) = y0; + *(dstp++) = u; + *(dstp++) = y1; + *(dstp++) = v; + } + } + for (; x < src->width; x += 2) { + srcy += 2; + if (!(x & xmask)) { + srcu++; + srcv++; + } + } + } +} + +/* split packed YUV samples and join into YUV planes + * FIXME currently ignores color and grayscales the image + */ +static void convert_yuv_unpack(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uint8_t *dsty, flags; + const uint8_t *srcp; + unsigned srcl, x, y; + uint8_t y0 = 0, y1 = 0; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); + dsty = (uint8_t *)dst->data; + + flags = srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder; + flags &= 2; + srcp = src->data; + if (flags) + srcp++; + + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + y0 = *(srcp++); + srcp++; + y1 = *(srcp++); + srcp++; + } + *(dsty++) = y0; + *(dsty++) = y1; + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* resample and resize UV plane(s) + * FIXME currently ignores color and grayscales the image + */ +static void convert_uvp_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + convert_y_resize(dst, dstfmt, src, srcfmt, dstn); + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); +} + +/* rearrange interleaved UV componets */ +static void convert_uv_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn; + uint8_t *dstp, flags; + const uint8_t *srcp; + unsigned srcl, x, y; + uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dst->datalen = dstn + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + flags = (srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder) & 1; + srcp = src->data; + + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + if (!(srcfmt->p.yuv.packorder & 2)) { + y0 = *(srcp++); + u = *(srcp++); + y1 = *(srcp++); + v = *(srcp++); + } else { + u = *(srcp++); + y0 = *(srcp++); + v = *(srcp++); + y1 = *(srcp++); + } + if (flags) { + uint8_t tmp = u; + u = v; + v = tmp; + } + } + if (!(dstfmt->p.yuv.packorder & 2)) { + *(dstp++) = y0; + *(dstp++) = u; + *(dstp++) = y1; + *(dstp++) = v; + } else { + *(dstp++) = u; + *(dstp++) = y0; + *(dstp++) = v; + *(dstp++) = y1; + } + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* YUV planes to packed RGB + * FIXME currently ignores color and grayscales the image + */ +static void convert_yuvp_to_rgb(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp, *srcy; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + unsigned long srcm, srcn; + unsigned x, y; + uint32_t p = 0; + + dst->datalen = dst->width * dst->height * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; + assert(src->datalen >= srcn + 2 * srcm); + srcy = (void *)src->data; + + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcy -= src->width; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + /* FIXME color space? */ + unsigned y0 = *(srcy++); + p = (((y0 >> drbits) << drbit0) | ((y0 >> dgbits) << dgbit0) | + ((y0 >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcy += (src->width - x); + } +} + +/* packed RGB to YUV planes + * FIXME currently ignores color and grayscales the image + */ +static void convert_rgb_to_yuvp(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uint8_t *dsty; + const uint8_t *srcp; + int rbits, rbit0, gbits, gbit0, bbits, bbit0; + unsigned srcl, x, y; + uint16_t y0 = 0; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); + dsty = (void *)dst->data; + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + rbits = RGB_SIZE(srcfmt->p.rgb.red); + rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + gbits = RGB_SIZE(srcfmt->p.rgb.green); + gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + bbits = RGB_SIZE(srcfmt->p.rgb.blue); + bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = ((p >> rbit0) << rbits) & 0xff; + g = ((p >> gbit0) << gbits) & 0xff; + b = ((p >> bbit0) << bbits) & 0xff; + + /* FIXME color space? */ + y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8; + } + *(dsty++) = y0; + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +/* packed YUV to packed RGB */ +static void convert_yuv_to_rgb(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp; + unsigned long dstn = dst->width * dst->height; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + const uint8_t *srcp; + unsigned srcl, x, y; + uint32_t p = 0; + + dst->datalen = dstn * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + assert(src->datalen >= + (src->width * src->height + uvp_size(src, srcfmt) * 2)); + srcp = src->data; + if (srcfmt->p.yuv.packorder & 2) + srcp++; + + assert(srcfmt->p.yuv.xsub2 == 1); + srcl = src->width + (src->width >> 1); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t y0 = *(srcp++); + srcp++; + + if (y0 <= 16) + y0 = 0; + else if (y0 >= 235) + y0 = 255; + else + y0 = (uint16_t)(y0 - 16) * 255 / 219; + + p = (((y0 >> drbits) << drbit0) | ((y0 >> dgbits) << dgbit0) | + ((y0 >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* packed RGB to packed YUV + * FIXME currently ignores color and grayscales the image + */ +static void convert_rgb_to_yuv(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp, flags; + const uint8_t *srcp; + int rbits, rbit0, gbits, gbit0, bbits, bbit0; + unsigned srcl, x, y; + uint16_t y0 = 0; + + uv_roundup(dst, dstfmt); + dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + flags = dstfmt->p.yuv.packorder & 2; + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + rbits = RGB_SIZE(srcfmt->p.rgb.red); + rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + gbits = RGB_SIZE(srcfmt->p.rgb.green); + gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + bbits = RGB_SIZE(srcfmt->p.rgb.blue); + bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = ((p >> rbit0) << rbits) & 0xff; + g = ((p >> gbit0) << gbits) & 0xff; + b = ((p >> bbit0) << bbits) & 0xff; + + /* FIXME color space? */ + y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8; + } + if (flags) { + *(dstp++) = 0x80; + *(dstp++) = y0; + } else { + *(dstp++) = y0; + *(dstp++) = 0x80; + } + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +/* resample and resize packed RGB components */ +static void convert_rgb_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn = dst->width * dst->height; + uint8_t *dstp; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + int srbits, srbit0, sgbits, sgbit0, sbbits, sbbit0; + const uint8_t *srcp; + unsigned srcl, x, y; + uint32_t p = 0; + + dst->datalen = dstn * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + srbits = RGB_SIZE(srcfmt->p.rgb.red); + srbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + sgbits = RGB_SIZE(srcfmt->p.rgb.green); + sgbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + sbbits = RGB_SIZE(srcfmt->p.rgb.blue); + sbbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + y -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = (p >> srbit0) << srbits; + g = (p >> sgbit0) << sgbits; + b = (p >> sbbit0) << sbbits; + + p = (((r >> drbits) << drbit0) | ((g >> dgbits) << dgbit0) | + ((b >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +#ifdef HAVE_LIBJPEG +void _zbar_convert_jpeg_to_y(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt); + +static void convert_jpeg(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt); +#endif + +/* group conversion matrix */ +static conversion_def_t conversions[][ZBAR_FMT_NUM] = { + { + /* *from* GRAY */ + { 0, convert_copy }, /* to GRAY */ + { 8, convert_uvp_append }, /* to YUV_PLANAR */ + { 24, convert_yuv_pack }, /* to YUV_PACKED */ + { 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 8, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_PLANAR */ + { 1, convert_copy }, /* to GRAY */ + { 48, convert_uvp_resample }, /* to YUV_PLANAR */ + { 64, convert_yuv_pack }, /* to YUV_PACKED */ + { 128, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 40, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_PACKED */ + { 24, convert_yuv_unpack }, /* to GRAY */ + { 52, convert_yuv_unpack }, /* to YUV_PLANAR */ + { 20, convert_uv_resample }, /* to YUV_PACKED */ + { 144, convert_yuv_to_rgb }, /* to RGB_PACKED */ + { 18, convert_yuv_unpack }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from RGB_PACKED */ + { 112, convert_rgb_to_yuvp }, /* to GRAY */ + { 160, convert_rgb_to_yuvp }, /* to YUV_PLANAR */ + { 144, convert_rgb_to_yuv }, /* to YUV_PACKED */ + { 120, convert_rgb_resample }, /* to RGB_PACKED */ + { 152, convert_rgb_to_yuvp }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_NV (FIXME treated as GRAY) */ + { 1, convert_copy }, /* to GRAY */ + { 8, convert_uvp_append }, /* to YUV_PLANAR */ + { 24, convert_yuv_pack }, /* to YUV_PACKED */ + { 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 8, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#ifdef HAVE_LIBJPEG + { + /* from JPEG */ + { 96, _zbar_convert_jpeg_to_y }, /* to GRAY */ + { 104, convert_jpeg }, /* to YUV_PLANAR */ + { 116, convert_jpeg }, /* to YUV_PACKED */ + { 256, convert_jpeg }, /* to RGB_PACKED */ + { 104, convert_jpeg }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#else + { + /* from JPEG */ + { -1, NULL }, /* to GRAY */ + { -1, NULL }, /* to YUV_PLANAR */ + { -1, NULL }, /* to YUV_PACKED */ + { -1, NULL }, /* to RGB_PACKED */ + { -1, NULL }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#endif +}; + +const zbar_format_def_t *_zbar_format_lookup(uint32_t fmt) +{ + const zbar_format_def_t *def = NULL; + int i = 0; + while (i < num_format_defs) { + def = &format_defs[i]; + if (fmt == def->format) + return (def); + i = i * 2 + 1; + if (fmt > def->format) + i++; + } + return (NULL); +} + +#ifdef HAVE_LIBJPEG +/* convert JPEG data via an intermediate format supported by libjpeg */ +static void convert_jpeg(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + /* define intermediate image in a format supported by libjpeg + * (currently only grayscale) + */ + zbar_image_t *tmp; + if (!src->src) { + tmp = zbar_image_create(); + tmp->format = fourcc('Y', '8', '0', '0'); + _zbar_image_copy_size(tmp, dst); + } else { + tmp = src->src->jpeg_img; + assert(tmp); + _zbar_image_copy_size(dst, tmp); + } + + const zbar_format_def_t *tmpfmt = _zbar_format_lookup(tmp->format); + assert(tmpfmt); + + /* convert to intermediate format */ + _zbar_convert_jpeg_to_y(tmp, tmpfmt, src, srcfmt); + + /* now convert to dst */ + _zbar_image_copy_size(dst, tmp); + + conversion_handler_t *func = conversions[tmpfmt->group][dstfmt->group].func; + + func(dst, dstfmt, tmp, tmpfmt); + + if (!src->src) + zbar_image_destroy(tmp); +} +#endif + +zbar_image_t *zbar_image_convert_resize(const zbar_image_t *src, + unsigned long fmt, unsigned width, + unsigned height) +{ + const zbar_format_def_t *srcfmt, *dstfmt; + conversion_handler_t *func; + zbar_image_t *dst = zbar_image_create(); + dst->format = fmt; + dst->width = width; + dst->height = height; + zbar_image_set_crop(dst, src->crop_x, src->crop_y, src->crop_w, + src->crop_h); + if (src->format == fmt && src->width == width && src->height == height) { + convert_copy(dst, NULL, src, NULL); + return (dst); + } + + srcfmt = _zbar_format_lookup(src->format); + dstfmt = _zbar_format_lookup(dst->format); + if (!srcfmt || !dstfmt) + /* FIXME free dst */ + return (NULL); + + if (srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp && + src->width == width && src->height == height) { + convert_copy(dst, NULL, src, NULL); + return (dst); + } + + func = conversions[srcfmt->group][dstfmt->group].func; + + dst->cleanup = zbar_image_free_data; + func(dst, dstfmt, src, srcfmt); + if (!dst->data) { + /* conversion failed */ + zbar_image_destroy(dst); + return (NULL); + } + return (dst); +} + +zbar_image_t *zbar_image_convert(const zbar_image_t *src, unsigned long fmt) +{ + return (zbar_image_convert_resize(src, fmt, src->width, src->height)); +} + +static inline int has_format(uint32_t fmt, const uint32_t *fmts) +{ + for (; *fmts; fmts++) + if (*fmts == fmt) + return (1); + return (0); +} + +/* select least cost conversion from src format to available dsts */ +int _zbar_best_format(uint32_t src, uint32_t *dst, const uint32_t *dsts) +{ + const zbar_format_def_t *srcfmt; + unsigned min_cost = -1; + + if (dst) + *dst = 0; + if (!dsts) + return (-1); + if (has_format(src, dsts)) { + zprintf(8, "shared format: %4.4s\n", (char *)&src); + if (dst) + *dst = src; + return (0); + } + srcfmt = _zbar_format_lookup(src); + if (!srcfmt) + return (-1); + + zprintf(8, "from %.4s(%08" PRIx32 ") to", (char *)&src, src); + for (; *dsts; dsts++) { + const zbar_format_def_t *dstfmt = _zbar_format_lookup(*dsts); + int cost; + if (!dstfmt) + continue; + if (srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp) + cost = 0; + else + cost = conversions[srcfmt->group][dstfmt->group].cost; + + if (_zbar_verbosity >= 8) + fprintf(stderr, " %.4s(%08" PRIx32 ")=%d", (char *)dsts, *dsts, + cost); + if (cost >= 0 && min_cost > cost) { + min_cost = cost; + if (dst) + *dst = *dsts; + } + } + if (_zbar_verbosity >= 8) + fprintf(stderr, "\n"); + return (min_cost); +} + +int zbar_negotiate_format(zbar_video_t *vdo, zbar_window_t *win) +{ + static const uint32_t y800[2] = { fourcc('Y','8','0','0'), 0 }; + errinfo_t *errdst; + const uint32_t *srcs, *dsts; + unsigned min_cost = -1; + uint32_t min_fmt = 0; + const uint32_t *fmt; + + if (!vdo) + return (0); + + if (win) + (void)window_lock(win); + + errdst = &vdo->err; + if (verify_format_sort()) { + if (win) + (void)window_unlock(win); + return (err_capture(errdst, SEV_FATAL, ZBAR_ERR_INTERNAL, __func__, + "image format list is not sorted!?")); + } + + if (!vdo->formats || (win && !win->formats)) { + if (win) + (void)window_unlock(win); + return (err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no input or output formats available")); + } + + srcs = vdo->formats; + dsts = (win) ? win->formats : y800; + + for (fmt = _zbar_formats; *fmt; fmt++) { + /* only consider formats supported by video device */ + uint32_t win_fmt = 0; + int cost; + if (!has_format(*fmt, srcs)) + continue; + cost = _zbar_best_format(*fmt, &win_fmt, dsts); + if (cost < 0) { + zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n", (char *)fmt, + *fmt); + continue; + } + zprintf(4, "%.4s(%08" PRIx32 ") -> %.4s(%08" PRIx32 ") (%d)\n", + (char *)fmt, *fmt, (char *)&win_fmt, win_fmt, cost); + if (min_cost > cost) { + min_cost = cost; + min_fmt = *fmt; + if (!cost) + break; + } + } + if (!min_fmt && vdo->emu_formats) { + /* As vdo->formats aren't compatible, just free them */ + free(vdo->formats); + vdo->formats = vdo->emu_formats; + vdo->emu_formats = NULL; + + srcs = vdo->formats; + dsts = (win) ? win->formats : y800; + + /* + * Use the same cost algorithm to select emulated formats. + * This might select a sub-optimal conversion, but, in practice, + * it will select a conversion to YUV at libv4l, and a YUY->Y8 + * in zbar, with it is OK. Yet, it is better to not select the + * most performatic conversion than to not support the webcam. + */ + for (fmt = _zbar_formats; *fmt; fmt++) { + /* only consider formats supported by video device */ + uint32_t win_fmt = 0; + int cost; + if (!has_format(*fmt, srcs)) + continue; + cost = _zbar_best_format(*fmt, &win_fmt, dsts); + if (cost < 0) { + zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n", + (char *)fmt, *fmt); + continue; + } + zprintf(4, "%.4s(%08" PRIx32 ") -> %.4s(%08" PRIx32 ") (%d)\n", + (char *)fmt, *fmt, (char *)&win_fmt, win_fmt, cost); + if (min_cost > cost) { + min_cost = cost; + min_fmt = *fmt; + if (!cost) + break; + } + } + } + + if (win) + (void)window_unlock(win); + + if (!min_fmt) + return (err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no supported image formats available")); + + zprintf(2, "setting best format %.4s(%08" PRIx32 ") (%d)\n", + (char *)&min_fmt, min_fmt, min_cost); + return (zbar_video_init(vdo, min_fmt)); +} diff --git a/zbar/debug.h b/zbar/debug.h new file mode 100644 index 0000000..a78419b --- /dev/null +++ b/zbar/debug.h @@ -0,0 +1,93 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +/* varargs variations on compile time debug spew */ + +#include <stdio.h> + +#ifndef DEBUG_LEVEL + +#ifdef __GNUC__ +/* older versions of gcc (< 2.95) require a named varargs parameter */ +#define dbprintf(args...) while (0) +#else +/* unfortunately named vararg parameter is a gcc-specific extension */ +#define dbprintf(...) while (0) +#endif + +#else + +#ifdef __GNUC__ +#define dbprintf(level, args...) \ + do { \ + if ((level) <= DEBUG_LEVEL) \ + fprintf(stderr, args); \ + } while (0) +#else +#define dbprintf(level, ...) \ + do { \ + if ((level) <= DEBUG_LEVEL) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#endif + +#endif /* DEBUG_LEVEL */ + +/* spew warnings for non-fatal assertions. + * returns specified error code if assertion fails. + * NB check/return is still performed for NDEBUG + * only the message is inhibited + * FIXME don't we need varargs hacks here? + */ +#ifndef NDEBUG + +#include <stdio.h> + +#if __STDC_VERSION__ < 199901L && !defined(__func__) +#if __GNUC__ >= 2 +#define __func__ __FUNCTION__ +#else +#define __func__ "<unknown>" +#endif +#endif + +#define zassert(condition, retval, format, ...) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, \ + "WARNING: %s:%d: %s:" \ + " Assertion \"%s\" failed.\n\t" format, \ + __FILE__, __LINE__, __func__, #condition, ##__VA_ARGS__); \ + return (retval); \ + } \ + } while (0) + +#else + +#define zassert(condition, retval, format, ...) \ + do { \ + if (!(condition)) \ + return (retval); \ + } while (0) + +#endif diff --git a/zbar/decoder.c b/zbar/decoder.c new file mode 100644 index 0000000..6c41b7f --- /dev/null +++ b/zbar/decoder.c @@ -0,0 +1,592 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdio.h> /* snprintf */ +#include <stdlib.h> /* malloc, calloc, free */ +#include <string.h> /* memset, strlen */ + +#include <zbar.h> + +#if defined(DEBUG_DECODER) || defined(DEBUG_EAN) || defined(DEBUG_CODE93) || \ + defined(DEBUG_CODE39) || defined(DEBUG_CODABAR) || defined(DEBUG_I25) || \ + defined(DEBUG_DATABAR) || defined(DEBUG_CODE128) || \ + defined(DEBUG_SQ_FINDER) || defined(DEBUG_QR_FINDER) || \ + (defined(DEBUG_PDF417) && (DEBUG_PDF417 >= 4)) +#define DEBUG_LEVEL 1 +#endif +#include "debug.h" +#include "decoder.h" + +zbar_decoder_t *zbar_decoder_create() +{ + zbar_decoder_t *dcode = calloc(1, sizeof(zbar_decoder_t)); + dcode->buf_alloc = BUFFER_MIN; + dcode->buf = malloc(dcode->buf_alloc); + + /* initialize default configs */ +#if ENABLE_EAN == 1 + dcode->ean.enable = 1; + dcode->ean.ean13_config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->ean.ean8_config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->ean.upca_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.upce_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.isbn10_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.isbn13_config = 1 << ZBAR_CFG_EMIT_CHECK; +#ifdef FIXME_ADDON_SYNC + dcode->ean.ean2_config = 1 << ZBAR_CFG_ENABLE; + dcode->ean.ean5_config = 1 << ZBAR_CFG_ENABLE; +#endif +#endif +#if ENABLE_I25 == 1 + dcode->i25.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->i25, ZBAR_CFG_MIN_LEN) = 6; +#endif +#if ENABLE_DATABAR == 1 + dcode->databar.config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->databar.config_exp = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->databar.csegs = 4; + dcode->databar.segs = calloc(4, sizeof(*dcode->databar.segs)); +#endif +#if ENABLE_CODABAR == 1 + dcode->codabar.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->codabar, ZBAR_CFG_MIN_LEN) = 4; +#endif +#if ENABLE_CODE39 == 1 + dcode->code39.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->code39, ZBAR_CFG_MIN_LEN) = 1; +#endif +#if ENABLE_CODE93 == 1 + dcode->code93.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_CODE128 == 1 + dcode->code128.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_PDF417 == 1 + dcode->pdf417.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_QRCODE == 1 + dcode->qrf.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_SQCODE == 1 + dcode->sqf.config = 1 << ZBAR_CFG_ENABLE; +#endif + + zbar_decoder_reset(dcode); + return (dcode); +} + +void zbar_decoder_destroy(zbar_decoder_t *dcode) +{ +#if ENABLE_DATABAR == 1 + if (dcode->databar.segs) + free(dcode->databar.segs); +#endif + if (dcode->buf) + free(dcode->buf); + free(dcode); +} + +void zbar_decoder_reset(zbar_decoder_t *dcode) +{ + memset(dcode, 0, (long)&dcode->buf_alloc - (long)dcode); +#if ENABLE_EAN == 1 + ean_reset(&dcode->ean); +#endif +#if ENABLE_I25 == 1 + i25_reset(&dcode->i25); +#endif +#if ENABLE_DATABAR == 1 + databar_reset(&dcode->databar); +#endif +#if ENABLE_CODABAR == 1 + codabar_reset(&dcode->codabar); +#endif +#if ENABLE_CODE39 == 1 + code39_reset(&dcode->code39); +#endif +#if ENABLE_CODE93 == 1 + code93_reset(&dcode->code93); +#endif +#if ENABLE_CODE128 == 1 + code128_reset(&dcode->code128); +#endif +#if ENABLE_PDF417 == 1 + pdf417_reset(&dcode->pdf417); +#endif +#if ENABLE_QRCODE == 1 + qr_finder_reset(&dcode->qrf); +#endif +} + +void zbar_decoder_new_scan(zbar_decoder_t *dcode) +{ + /* soft reset decoder */ + memset(dcode->w, 0, sizeof(dcode->w)); + dcode->lock = 0; + dcode->idx = 0; + dcode->s6 = 0; +#if ENABLE_EAN == 1 + ean_new_scan(&dcode->ean); +#endif +#if ENABLE_I25 == 1 + i25_reset(&dcode->i25); +#endif +#if ENABLE_DATABAR == 1 + databar_new_scan(&dcode->databar); +#endif +#if ENABLE_CODABAR == 1 + codabar_reset(&dcode->codabar); +#endif +#if ENABLE_CODE39 == 1 + code39_reset(&dcode->code39); +#endif +#if ENABLE_CODE93 == 1 + code93_reset(&dcode->code93); +#endif +#if ENABLE_CODE128 == 1 + code128_reset(&dcode->code128); +#endif +#if ENABLE_PDF417 == 1 + pdf417_reset(&dcode->pdf417); +#endif +#if ENABLE_QRCODE == 1 + qr_finder_reset(&dcode->qrf); +#endif +} + +zbar_color_t zbar_decoder_get_color(const zbar_decoder_t *dcode) +{ + return (get_color(dcode)); +} + +const char *zbar_decoder_get_data(const zbar_decoder_t *dcode) +{ + return ((char *)dcode->buf); +} + +unsigned int zbar_decoder_get_data_length(const zbar_decoder_t *dcode) +{ + return (dcode->buflen); +} + +int zbar_decoder_get_direction(const zbar_decoder_t *dcode) +{ + return (dcode->direction); +} + +zbar_decoder_handler_t * +zbar_decoder_set_handler(zbar_decoder_t *dcode, zbar_decoder_handler_t *handler) +{ + zbar_decoder_handler_t *result = dcode->handler; + dcode->handler = handler; + return (result); +} + +void zbar_decoder_set_userdata(zbar_decoder_t *dcode, void *userdata) +{ + dcode->userdata = userdata; +} + +void *zbar_decoder_get_userdata(const zbar_decoder_t *dcode) +{ + return (dcode->userdata); +} + +zbar_symbol_type_t zbar_decoder_get_type(const zbar_decoder_t *dcode) +{ + return (dcode->type); +} + +unsigned int zbar_decoder_get_modifiers(const zbar_decoder_t *dcode) +{ + return (dcode->modifiers); +} + +zbar_symbol_type_t zbar_decode_width(zbar_decoder_t *dcode, unsigned w) +{ + zbar_symbol_type_t tmp, sym = ZBAR_NONE; + + dcode->w[dcode->idx & (DECODE_WINDOW - 1)] = w; + dbprintf(1, " decode[%x]: w=%d (%g)\n", dcode->idx, w, (w / 32.)); + + /* update shared character width */ + dcode->s6 -= get_width(dcode, 7); + dcode->s6 += get_width(dcode, 1); + + /* each decoder processes width stream in parallel */ +#if ENABLE_QRCODE == 1 + if (TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_find_qr(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_EAN == 1 + if ((dcode->ean.enable) && (tmp = _zbar_decode_ean(dcode))) + sym = tmp; +#endif +#if ENABLE_CODE39 == 1 + if (TEST_CFG(dcode->code39.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODE93 == 1 + if (TEST_CFG(dcode->code93.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code93(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODE128 == 1 + if (TEST_CFG(dcode->code128.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_DATABAR == 1 + if (TEST_CFG(dcode->databar.config | dcode->databar.config_exp, + ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_databar(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODABAR == 1 + if (TEST_CFG(dcode->codabar.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_codabar(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_I25 == 1 + if (TEST_CFG(dcode->i25.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_PDF417 == 1 + if (TEST_CFG(dcode->pdf417.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif + + dcode->idx++; + dcode->type = sym; + if (sym) { + if (dcode->lock && sym > ZBAR_PARTIAL && sym != ZBAR_QRCODE) + release_lock(dcode, sym); + if (dcode->handler) + dcode->handler(dcode); + } + return (sym); +} + +static inline const unsigned int * +decoder_get_configp(const zbar_decoder_t *dcode, zbar_symbol_type_t sym) +{ + const unsigned int *config; + switch (sym) { +#if ENABLE_EAN == 1 + case ZBAR_EAN13: + config = &dcode->ean.ean13_config; + break; + + case ZBAR_EAN2: + config = &dcode->ean.ean2_config; + break; + + case ZBAR_EAN5: + config = &dcode->ean.ean5_config; + break; + + case ZBAR_EAN8: + config = &dcode->ean.ean8_config; + break; + + case ZBAR_UPCA: + config = &dcode->ean.upca_config; + break; + + case ZBAR_UPCE: + config = &dcode->ean.upce_config; + break; + + case ZBAR_ISBN10: + config = &dcode->ean.isbn10_config; + break; + + case ZBAR_ISBN13: + config = &dcode->ean.isbn13_config; + break; +#endif + +#if ENABLE_I25 == 1 + case ZBAR_I25: + config = &dcode->i25.config; + break; +#endif + +#if ENABLE_DATABAR == 1 + case ZBAR_DATABAR: + config = &dcode->databar.config; + break; + case ZBAR_DATABAR_EXP: + config = &dcode->databar.config_exp; + break; +#endif + +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + config = &dcode->codabar.config; + break; +#endif + +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + config = &dcode->code39.config; + break; +#endif + +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + config = &dcode->code93.config; + break; +#endif + +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + config = &dcode->code128.config; + break; +#endif + +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + config = &dcode->pdf417.config; + break; +#endif + +#if ENABLE_QRCODE == 1 + case ZBAR_QRCODE: + config = &dcode->qrf.config; + break; +#endif + +#if ENABLE_SQCODE == 1 + case ZBAR_SQCODE: + config = &dcode->sqf.config; + break; +#endif + + default: + config = NULL; + } + return (config); +} + +unsigned int zbar_decoder_get_configs(const zbar_decoder_t *dcode, + zbar_symbol_type_t sym) +{ + const unsigned *config = decoder_get_configp(dcode, sym); + if (!config) + return (0); + return (*config); +} + +static inline int decoder_set_config_bool(zbar_decoder_t *dcode, + zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + unsigned *config = (void *)decoder_get_configp(dcode, sym); + if (!config || cfg >= ZBAR_CFG_NUM) + return (1); + + if (!val) + *config &= ~(1 << cfg); + else if (val == 1) + *config |= (1 << cfg); + else + return (1); + +#if ENABLE_EAN == 1 + dcode->ean.enable = + TEST_CFG(dcode->ean.ean13_config | dcode->ean.ean2_config | + dcode->ean.ean5_config | dcode->ean.ean8_config | + dcode->ean.upca_config | dcode->ean.upce_config | + dcode->ean.isbn10_config | dcode->ean.isbn13_config, + ZBAR_CFG_ENABLE); +#endif + + return (0); +} + +static inline int decoder_set_config_int(zbar_decoder_t *dcode, + zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + switch (sym) { +#if ENABLE_I25 == 1 + case ZBAR_I25: + CFG(dcode->i25, cfg) = val; + break; +#endif +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + CFG(dcode->codabar, cfg) = val; + break; +#endif +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + CFG(dcode->code39, cfg) = val; + break; +#endif +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + CFG(dcode->code93, cfg) = val; + break; +#endif +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + CFG(dcode->code128, cfg) = val; + break; +#endif +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + CFG(dcode->pdf417, cfg) = val; + break; +#endif + + default: + return (1); + } + return (0); +} + +int zbar_decoder_get_config(zbar_decoder_t *dcode, zbar_symbol_type_t sym, + zbar_config_t cfg, int *val) +{ + const unsigned *config = decoder_get_configp(dcode, sym); + + /* Return error if symbol doesn't have config */ + if (sym <= ZBAR_PARTIAL || sym > ZBAR_CODE128 || sym == ZBAR_COMPOSITE) + return 1; + + /* Return decoder boolean configs */ + if (cfg < ZBAR_CFG_NUM) { + *val = (*config & (1 << cfg)) != 0; + return 0; + } + + /* Return decoder integer configs */ + if (cfg >= ZBAR_CFG_MIN_LEN && cfg <= ZBAR_CFG_MAX_LEN) { + switch (sym) { +#if ENABLE_I25 == 1 + case ZBAR_I25: + *val = CFG(dcode->i25, cfg); + return 0; +#endif +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + *val = CFG(dcode->codabar, cfg); + return 0; +#endif +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + *val = CFG(dcode->code39, cfg); + return 0; +#endif +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + *val = CFG(dcode->code93, cfg); + return 0; +#endif +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + *val = CFG(dcode->code128, cfg); + return 0; +#endif +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + *val = CFG(dcode->pdf417, cfg); + return 0; +#endif + default: + return 1; + } + } + return 1; +} + +int zbar_decoder_set_config(zbar_decoder_t *dcode, zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + if (sym == ZBAR_NONE) { + static const zbar_symbol_type_t all[] = { ZBAR_EAN13, + ZBAR_EAN2, + ZBAR_EAN5, + ZBAR_EAN8, + ZBAR_UPCA, + ZBAR_UPCE, + ZBAR_ISBN10, + ZBAR_ISBN13, + ZBAR_I25, + ZBAR_DATABAR, + ZBAR_DATABAR_EXP, + ZBAR_CODABAR, + ZBAR_CODE39, + ZBAR_CODE93, + ZBAR_CODE128, + ZBAR_QRCODE, + ZBAR_SQCODE, + ZBAR_PDF417, + 0 }; + const zbar_symbol_type_t *symp; + for (symp = all; *symp; symp++) + zbar_decoder_set_config(dcode, *symp, cfg, val); + return (0); + } + + if (cfg >= 0 && cfg < ZBAR_CFG_NUM) + return (decoder_set_config_bool(dcode, sym, cfg, val)); + else if (cfg >= ZBAR_CFG_MIN_LEN && cfg <= ZBAR_CFG_MAX_LEN) + return (decoder_set_config_int(dcode, sym, cfg, val)); + else + return (1); +} + +static char *decoder_dump = NULL; +static unsigned decoder_dumplen = 0; + +const char *_zbar_decoder_buf_dump(unsigned char *buf, unsigned int buflen) +{ + int dumplen = (buflen * 3) + 12; + char *p; + int i; + + if (!decoder_dump || dumplen > decoder_dumplen) { + if (decoder_dump) + free(decoder_dump); + decoder_dump = malloc(dumplen); + decoder_dumplen = dumplen; + } + p = decoder_dump + + snprintf(decoder_dump, 12, + "buf[%04x]=", (buflen > 0xffff) ? 0xffff : buflen); + for (i = 0; i < buflen; i++) + p += snprintf(p, 4, "%s%02x", (i) ? " " : "", buf[i]); + return (decoder_dump); +} diff --git a/zbar/decoder.h b/zbar/decoder.h new file mode 100644 index 0000000..260dc43 --- /dev/null +++ b/zbar/decoder.h @@ -0,0 +1,292 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _DECODER_H_ +#define _DECODER_H_ + +#include "config.h" +#include <limits.h> +#include <stdlib.h> /* realloc */ + +#include <zbar.h> + +#include "debug.h" + +#define NUM_CFGS (ZBAR_CFG_MAX_LEN - ZBAR_CFG_MIN_LEN + 1) + +#if ENABLE_EAN == 1 +#include "decoder/ean.h" +#endif +#if ENABLE_I25 == 1 +#include "decoder/i25.h" +#endif +#if ENABLE_DATABAR == 1 +#include "decoder/databar.h" +#endif +#if ENABLE_CODABAR == 1 +#include "decoder/codabar.h" +#endif +#if ENABLE_CODE39 == 1 +#include "decoder/code39.h" +#endif +#if ENABLE_CODE93 == 1 +#include "decoder/code93.h" +#endif +#if ENABLE_CODE128 == 1 +#include "decoder/code128.h" +#endif +#if ENABLE_PDF417 == 1 +#include "decoder/pdf417.h" +#endif +#if ENABLE_QRCODE == 1 +#include "decoder/qr_finder.h" +#endif +#if ENABLE_SQCODE == 1 +#include "decoder/sq_finder.h" +#endif + +/* size of bar width history (implementation assumes power of two) */ +#ifndef DECODE_WINDOW +#define DECODE_WINDOW 16 +#endif + +/* initial data buffer allocation */ +#ifndef BUFFER_MIN +#define BUFFER_MIN 0x20 +#endif + +/* maximum data buffer allocation + * (longer symbols are rejected) + */ +#ifndef BUFFER_MAX +#define BUFFER_MAX 0x100 +#endif + +/* buffer allocation increment */ +#ifndef BUFFER_INCR +#define BUFFER_INCR 0x10 +#endif + +#define CFG(dcode, cfg) ((dcode).configs[(cfg)-ZBAR_CFG_MIN_LEN]) +#define TEST_CFG(config, cfg) (((config) >> (cfg)) & 1) +#define MOD(mod) (1 << (mod)) + +/* symbology independent decoder state */ +struct zbar_decoder_s { + unsigned char idx; /* current width index */ + unsigned w[DECODE_WINDOW]; /* window of last N bar widths */ + zbar_symbol_type_t type; /* type of last decoded data */ + zbar_symbol_type_t lock; /* buffer lock */ + unsigned modifiers; /* symbology modifier */ + int direction; /* direction of last decoded data */ + unsigned s6; /* 6-element character width */ + + /* everything above here is automatically reset */ + unsigned buf_alloc; /* dynamic buffer allocation */ + unsigned buflen; /* binary data length */ + unsigned char *buf; /* decoded characters */ + void *userdata; /* application data */ + zbar_decoder_handler_t *handler; /* application callback */ + + /* symbology specific state */ +#if ENABLE_EAN == 1 + ean_decoder_t ean; /* EAN/UPC parallel decode attempts */ +#endif +#if ENABLE_I25 == 1 + i25_decoder_t i25; /* Interleaved 2 of 5 decode state */ +#endif +#if ENABLE_DATABAR == 1 + databar_decoder_t databar; /* DataBar decode state */ +#endif +#if ENABLE_CODABAR == 1 + codabar_decoder_t codabar; /* Codabar decode state */ +#endif +#if ENABLE_CODE39 == 1 + code39_decoder_t code39; /* Code 39 decode state */ +#endif +#if ENABLE_CODE93 == 1 + code93_decoder_t code93; /* Code 93 decode state */ +#endif +#if ENABLE_CODE128 == 1 + code128_decoder_t code128; /* Code 128 decode state */ +#endif +#if ENABLE_PDF417 == 1 + pdf417_decoder_t pdf417; /* PDF417 decode state */ +#endif +#if ENABLE_QRCODE == 1 + qr_finder_t qrf; /* QR Code finder state */ +#endif +#if ENABLE_SQCODE == 1 + sq_finder_t sqf; /* SQ Code finder state */ +#endif +}; + +/* return current element color */ +static inline char get_color(const zbar_decoder_t *dcode) +{ + return (dcode->idx & 1); +} + +/* retrieve i-th previous element width */ +static inline unsigned get_width(const zbar_decoder_t *dcode, + unsigned char offset) +{ + return (dcode->w[(dcode->idx - offset) & (DECODE_WINDOW - 1)]); +} + +/* retrieve bar+space pair width starting at offset i */ +static inline unsigned pair_width(const zbar_decoder_t *dcode, + unsigned char offset) +{ + return (get_width(dcode, offset) + get_width(dcode, offset + 1)); +} + +/* calculate total character width "s" + * - start of character identified by context sensitive offset + * (<= DECODE_WINDOW - n) + * - size of character is n elements + */ +static inline unsigned calc_s(const zbar_decoder_t *dcode, unsigned char offset, + unsigned char n) +{ + /* FIXME check that this gets unrolled for constant n */ + unsigned s = 0; + while (n--) + s += get_width(dcode, offset++); + return (s); +} + +/* fixed character width decode assist + * bar+space width are compared as a fraction of the reference dimension "x" + * - +/- 1/2 x tolerance + * - measured total character width (s) compared to symbology baseline (n) + * (n = 7 for EAN/UPC, 11 for Code 128) + * - bar+space *pair width* "e" is used to factor out bad "exposures" + * ("blooming" or "swelling" of dark or light areas) + * => using like-edge measurements avoids these issues + * - n should be > 3 + */ +static inline int decode_e(unsigned e, unsigned s, unsigned n) +{ + /* result is encoded number of units - 2 + * (for use as zero based index) + * or -1 if invalid + */ + unsigned char E = ((e * n * 2 + 1) / s - 3) / 2; + return ((E >= n - 3) ? -1 : E); +} + +/* sort three like-colored elements and return ordering + */ +static inline unsigned decode_sort3(zbar_decoder_t *dcode, int i0) +{ + unsigned w0 = get_width(dcode, i0); + unsigned w2 = get_width(dcode, i0 + 2); + unsigned w4 = get_width(dcode, i0 + 4); + if (w0 < w2) { + if (w2 < w4) + return ((i0 << 8) | ((i0 + 2) << 4) | (i0 + 4)); + if (w0 < w4) + return ((i0 << 8) | ((i0 + 4) << 4) | (i0 + 2)); + return (((i0 + 4) << 8) | (i0 << 4) | (i0 + 2)); + } + if (w4 < w2) + return (((i0 + 4) << 8) | ((i0 + 2) << 4) | i0); + if (w0 < w4) + return (((i0 + 2) << 8) | (i0 << 4) | (i0 + 4)); + return (((i0 + 2) << 8) | ((i0 + 4) << 4) | i0); +} + +/* sort N like-colored elements and return ordering + */ +static inline unsigned decode_sortn(zbar_decoder_t *dcode, int n, int i0) +{ + unsigned mask = 0, sort = 0; + int i; + for (i = n - 1; i >= 0; i--) { + unsigned wmin = UINT_MAX; + int jmin = -1, j; + for (j = n - 1; j >= 0; j--) { + unsigned w; + if ((mask >> j) & 1) + continue; + w = get_width(dcode, i0 + j * 2); + if (wmin >= w) { + wmin = w; + jmin = j; + } + } + zassert(jmin >= 0, 0, "sortn(%d,%d) jmin=%d", n, i0, jmin); + sort <<= 4; + mask |= 1 << jmin; + sort |= i0 + jmin * 2; + } + return (sort); +} + +/* acquire shared state lock */ +static inline char acquire_lock(zbar_decoder_t *dcode, zbar_symbol_type_t req) +{ + if (dcode->lock) { + dbprintf(2, " [locked %d]\n", dcode->lock); + return (1); + } + dcode->lock = req; + return (0); +} + +/* check and release shared state lock */ +static inline char release_lock(zbar_decoder_t *dcode, zbar_symbol_type_t req) +{ + zassert(dcode->lock == req, 1, "lock=%d req=%d\n", dcode->lock, req); + dcode->lock = 0; + return (0); +} + +/* ensure output buffer has sufficient allocation for request */ +static inline char size_buf(zbar_decoder_t *dcode, unsigned len) +{ + unsigned char *buf; + if (len <= BUFFER_MIN) + return (0); + if (len < dcode->buf_alloc) + /* FIXME size reduction heuristic? */ + return (0); + if (len > BUFFER_MAX) + return (1); + if (len < dcode->buf_alloc + BUFFER_INCR) { + len = dcode->buf_alloc + BUFFER_INCR; + if (len > BUFFER_MAX) + len = BUFFER_MAX; + } + buf = realloc(dcode->buf, len); + if (!buf) + return (1); + dcode->buf = buf; + dcode->buf_alloc = len; + return (0); +} + +extern const char *_zbar_decoder_buf_dump(unsigned char *buf, + unsigned int buflen); + +#endif diff --git a/zbar/decoder/codabar.c b/zbar/decoder/codabar.c new file mode 100644 index 0000000..6e2c74a --- /dev/null +++ b/zbar/decoder/codabar.c @@ -0,0 +1,397 @@ +/*------------------------------------------------------------------------ + * Copyright 2011 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODABAR +#define DEBUG_LEVEL (DEBUG_CODABAR) +#endif +#include "debug.h" +#include "decoder.h" + +#define NIBUF 6 /* initial scan buffer size */ + +static const signed char codabar_lo[12] = { 0x0, 0x1, 0x4, 0x5, 0x2, 0xa, + 0xb, 0x9, 0x6, 0x7, 0x8, 0x3 }; + +static const unsigned char codabar_hi[8] = { 0x1, 0x4, 0x7, 0x6, + 0x2, 0x3, 0x0, 0x5 }; + +static const unsigned char codabar_characters[20] = "0123456789-$:/.+ABCD"; + +static inline int check_width(unsigned ref, unsigned w) +{ + unsigned dref = ref; + ref *= 4; + w *= 4; + return (ref - dref <= w && w <= ref + dref); +} + +static inline signed char codabar_decode7(zbar_decoder_t *dcode) +{ + unsigned ibar, wbmax, wbmin, wb1, wb2; + unsigned long b0b3, b1b2; + unsigned ispc, wsmax, wsmid, wsmin; + unsigned long s0s2, s1s1; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + dbprintf(2, " s=%d", s); + if (s < 7) + return (-1); + + /* check width */ + if (!check_width(codabar->width, s)) { + dbprintf(2, " [width]"); + return (-1); + } + + /* extract min/max bar */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]"); + return (-1); + } + + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + b0b3 = wbmin * wbmax; + b1b2 = wb1 * wb2; + if (b1b2 + b1b2 / 8 < b0b3) { + /* single wide bar combinations */ + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [1bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 1) & 0x3; + } else if (b1b2 > b0b3 + b0b3 / 8) { + /* three wide bars, no wide spaces */ + if (4 * wbmin > 3 * wb1 || 8 * wb1 < 5 * wb2 || 8 * wb2 < 5 * wbmax || + wbmin * wb2 >= wb1 * wb1) { + dbprintf(2, " [3bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 13) + 4; + } else { + dbprintf(2, " [bar inner ratios]"); + return (-1); + } + + ispc = decode_sort3(dcode, 2); + dbprintf(2, "(%x) spc=%03x", ibar, ispc); + + wsmax = get_width(dcode, ispc & 0xf); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + wsmin = get_width(dcode, (ispc >> 8) & 0xf); + if (ibar >> 2) { + int c; + /* verify no wide spaces */ + if (8 * wsmin < wsmax || 8 * wsmin < 5 * wsmid || + 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [0space inner ratios]"); + return (-1); + } + ibar &= 0x3; + if (codabar->direction) + ibar = 3 - ibar; + c = (0xfcde >> (ibar << 2)) & 0xf; + dbprintf(2, " ex[%d]=%x", ibar, c); + return (c); + } else if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax) { + dbprintf(2, " [space outer ratio]"); + return (-1); + } + + s0s2 = wsmin * wsmax; + s1s1 = wsmid * wsmid; + if (s1s1 + s1s1 / 8 < s0s2) { + unsigned ic; + int c; + /* single wide space */ + if (8 * wsmin < 5 * wsmid || 4 * wsmid > 3 * wsmax) { + dbprintf(2, " [1space inner ratios]"); + return (-1); + } + ispc = ((ispc & 0xf) >> 1) - 1; + ic = (ispc << 2) | ibar; + if (codabar->direction) + ic = 11 - ic; + c = codabar_lo[ic]; + dbprintf(2, "(%d) lo[%d]=%x", ispc, ic, c); + return (c); + } else if (s1s1 > s0s2 + s0s2 / 8) { + unsigned ic; + unsigned char c; + /* two wide spaces, check start/stop */ + if (4 * wsmin > 3 * wsmid || 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [2space inner ratios]"); + return (-1); + } + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]"); + return (-1); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + ic = ispc * 4 + ibar; + zassert(ic < 8, -1, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + if (c >> 2 != codabar->direction) { + dbprintf(2, " [invalid stop]"); + return (-1); + } + c = (c & 0x3) | 0x10; + dbprintf(2, " hi[%d]=%x", ic, c); + return (c); + } else { + dbprintf(2, " [space inner ratios]"); + return (-1); + } +} + +static inline signed char codabar_decode_start(zbar_decoder_t *dcode) +{ + unsigned qz, ispc, wsmax, wsmin, wsmid, ibar, wbmax, wbmin, wb1, wb2; + int ic, c; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + if (s < 8) + return (ZBAR_NONE); + dbprintf(2, " codabar: s=%d", s); + + /* check leading quiet zone - spec is 10x */ + qz = get_width(dcode, 8); + if ((qz && qz * 2 < s) || 4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [invalid qz/ics]\n"); + return (ZBAR_NONE); + } + + /* check space ratios first */ + ispc = decode_sort3(dcode, 2); + dbprintf(2, " spc=%03x", ispc); + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]\n"); + return (ZBAR_NONE); + } + + /* require 2 wide and 1 narrow spaces */ + wsmax = get_width(dcode, ispc & 0xf); + wsmin = get_width(dcode, ispc >> 8); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax || 4 * wsmin > 3 * wsmid || + 8 * wsmid < 5 * wsmax || wsmid * wsmid <= wsmax * wsmin) { + dbprintf(2, " [space ratio]\n"); + return (ZBAR_NONE); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + + /* check bar ratios */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]\n"); + return (ZBAR_NONE); + } + + /* require 1 wide & 3 narrow bars */ + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb1 * wb2 >= wbmin * wbmax || wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [bar inner ratios]\n"); + return (ZBAR_NONE); + } + ibar = ((ibar & 0xf) - 1) >> 1; + dbprintf(2, "(%d)", ibar); + + /* decode combination */ + ic = ispc * 4 + ibar; + zassert(ic < 8, ZBAR_NONE, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + codabar->buf[0] = (c & 0x3) | 0x10; + + /* set character direction */ + codabar->direction = c >> 2; + + codabar->element = 4; + codabar->character = 1; + codabar->width = codabar->s7; + dbprintf(1, " start=%c dir=%x [valid start]\n", codabar->buf[0] + 0x31, + codabar->direction); + return (ZBAR_PARTIAL); +} + +static inline int codabar_checksum(zbar_decoder_t *dcode, unsigned n) +{ + unsigned chk = 0; + unsigned char *buf = dcode->buf; + while (n--) + chk += *(buf++); + return (!!(chk & 0xf)); +} + +static inline zbar_symbol_type_t codabar_postprocess(zbar_decoder_t *dcode) +{ + int dir, i, n; + codabar_decoder_t *codabar = &dcode->codabar; + dir = codabar->direction; + dcode->direction = 1 - 2 * dir; + n = codabar->character; + for (i = 0; i < NIBUF; i++) + dcode->buf[i] = codabar->buf[i]; + if (dir) + /* reverse buffer */ + for (i = 0; i < n / 2; i++) { + unsigned j = n - 1 - i; + char code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + + if (TEST_CFG(codabar->config, ZBAR_CFG_ADD_CHECK)) { + /* validate checksum */ + if (codabar_checksum(dcode, n)) + return (ZBAR_NONE); + if (!TEST_CFG(codabar->config, ZBAR_CFG_EMIT_CHECK)) { + dcode->buf[n - 2] = dcode->buf[n - 1]; + n--; + } + } + + for (i = 0; i < n; i++) { + unsigned c = dcode->buf[i]; + dcode->buf[i] = ((c < 0x14) ? codabar_characters[c] : '?'); + } + dcode->buflen = i; + dcode->buf[i] = '\0'; + dcode->modifiers = 0; + + codabar->character = -1; + return (ZBAR_CODABAR); +} + +zbar_symbol_type_t _zbar_decode_codabar(zbar_decoder_t *dcode) +{ + signed char c; + unsigned char *buf; + unsigned s; + codabar_decoder_t *codabar = &dcode->codabar; + + /* update latest character width */ + codabar->s7 -= get_width(dcode, 8); + codabar->s7 += get_width(dcode, 1); + + if (get_color(dcode) != ZBAR_SPACE) + return (ZBAR_NONE); + if (codabar->character < 0) + return (codabar_decode_start(dcode)); + if (codabar->character < 2 && codabar_decode_start(dcode)) + return (ZBAR_PARTIAL); + if (--codabar->element) + return (ZBAR_NONE); + codabar->element = 4; + + dbprintf(1, " codabar[%c%02d+%x]", (codabar->direction) ? '<' : '>', + codabar->character, codabar->element); + + c = codabar_decode7(dcode); + dbprintf(1, " %d", c); + if (c < 0) { + dbprintf(1, " [aborted]\n"); + goto reset; + } + + if (codabar->character < NIBUF) + buf = codabar->buf; + else { + if (codabar->character >= BUFFER_MIN && + size_buf(dcode, codabar->character + 1)) { + dbprintf(1, " [overflow]\n"); + goto reset; + } + buf = dcode->buf; + } + buf[codabar->character++] = c; + + /* lock shared resources */ + if (codabar->character == NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + + s = codabar->s7; + if (c & 0x10) { + zbar_symbol_type_t sym; + unsigned n; + unsigned qz = get_width(dcode, 0); + if (qz && qz * 2 < s) { + dbprintf(2, " [invalid qz]\n"); + goto reset; + } + n = codabar->character; + if (n < CFG(*codabar, ZBAR_CFG_MIN_LEN) || + (CFG(*codabar, ZBAR_CFG_MAX_LEN) > 0 && + n > CFG(*codabar, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + goto reset; + } + if (codabar->character < NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + dbprintf(2, " stop=%c", c + 0x31); + + sym = codabar_postprocess(dcode); + if (sym > ZBAR_PARTIAL) + dbprintf(2, " [valid stop]"); + else { + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + } + dbprintf(2, "\n"); + return (sym); + } else if (4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [ics]\n"); + goto reset; + } + + dbprintf(2, "\n"); + return (ZBAR_NONE); + +reset: + if (codabar->character >= NIBUF) + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + return (ZBAR_NONE); +} diff --git a/zbar/decoder/codabar.h b/zbar/decoder/codabar.h new file mode 100644 index 0000000..30ad022 --- /dev/null +++ b/zbar/decoder/codabar.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2011 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _CODABAR_H_ +#define _CODABAR_H_ + +/* Codabar specific decode state */ +typedef struct codabar_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd, 1=rev */ + unsigned element : 4; /* element offset 0-7 */ + int character : 12; /* character position in symbol */ + unsigned s7; /* current character width */ + unsigned width; /* last character width */ + unsigned char buf[6]; /* initial scan buffer */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} codabar_decoder_t; + +/* reset Codabar specific state */ +static inline void codabar_reset(codabar_decoder_t *codabar) +{ + codabar->direction = 0; + codabar->element = 0; + codabar->character = -1; + codabar->s7 = 0; +} + +/* decode Codabar symbols */ +zbar_symbol_type_t _zbar_decode_codabar(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code128.c b/zbar/decoder/code128.c new file mode 100644 index 0000000..c9a6b09 --- /dev/null +++ b/zbar/decoder/code128.c @@ -0,0 +1,582 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODE128 +#define DEBUG_LEVEL (DEBUG_CODE128) +#endif +#include "debug.h" +#include "decoder.h" + +#define NUM_CHARS 108 /* total number of character codes */ + +typedef enum code128_char_e +{ + FNC3 = 0x60, + FNC2 = 0x61, + SHIFT = 0x62, + CODE_C = 0x63, + CODE_B = 0x64, + CODE_A = 0x65, + FNC1 = 0x66, + START_A = 0x67, + START_B = 0x68, + START_C = 0x69, + STOP_FWD = 0x6a, + STOP_REV = 0x6b, + FNC4 = 0x6c, +} code128_char_t; + +static const unsigned char characters[NUM_CHARS] = { + 0x5c, 0xbf, 0xa1, /* [00] 00 */ + 0x2a, 0xc5, 0x0c, 0xa4, /* [03] 01 */ + 0x2d, 0xe3, 0x0f, /* [07] 02 */ + 0x5f, 0xe4, /* [0a] 03 */ + + 0x6b, 0xe8, 0x69, 0xa7, 0xe7, /* [0c] 10 */ + 0xc1, 0x51, 0x1e, 0x83, 0xd9, 0x00, 0x84, 0x1f, /* [11] 11 */ + 0xc7, 0x0d, 0x33, 0x86, 0xb5, 0x0e, 0x15, 0x87, /* [19] 12 */ + 0x10, 0xda, 0x11, /* [21] 13 */ + + 0x36, 0xe5, 0x18, 0x37, /* [24] 20 */ + 0xcc, 0x13, 0x39, 0x89, 0x97, 0x14, 0x1b, 0x8a, 0x3a, 0xbd, /* [28] 21 */ + 0xa2, 0x5e, 0x01, 0x85, 0xb0, 0x02, 0xa3, /* [32] 22 */ + 0xa5, 0x2c, 0x16, 0x88, 0xbc, 0x12, 0xa6, /* [39] 23 */ + + 0x61, 0xe6, 0x56, 0x62, /* [40] 30 */ + 0x19, 0xdb, 0x1a, /* [44] 31 */ + 0xa8, 0x32, 0x1c, 0x8b, 0xcd, 0x1d, 0xa9, /* [47] 32 */ + 0xc3, 0x20, 0xc4, /* [4e] 33 */ + + 0x50, 0x5d, 0xc0, /* [51] 0014 0025 0034 */ + 0x2b, 0xc6, /* [54] 0134 0143 */ + 0x2e, /* [56] 0243 */ + 0x53, 0x60, /* [57] 0341 0352 */ + 0x31, /* [59] 1024 */ + 0x52, 0xc2, /* [5a] 1114 1134 */ + 0x34, 0xc8, /* [5c] 1242 1243 */ + 0x55, /* [5e] 1441 */ + + 0x57, 0x3e, 0xce, /* [5f] 4100 5200 4300 */ + 0x3b, 0xc9, /* [62] 4310 3410 */ + 0x6a, /* [64] 3420 */ + 0x54, 0x4f, /* [65] 1430 2530 */ + 0x38, /* [67] 4201 */ + 0x58, 0xcb, /* [68] 4111 4311 */ + 0x2f, 0xca, /* [6a] 2421 3421 */ +}; + +static const unsigned char lo_base[8] = { 0x00, 0x07, 0x0c, 0x19, + 0x24, 0x32, 0x40, 0x47 }; + +static const unsigned char lo_offset[0x80] = { + 0xff, 0xf0, 0xff, 0x1f, 0xff, 0xf2, 0xff, 0xff, /* 00 [00] */ + 0xff, 0xff, 0xff, 0x3f, 0xf4, 0xf5, 0xff, 0x6f, /* 01 */ + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf1, 0xff, 0x2f, /* 02 [07] */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x4f, /* 03 */ + 0xff, 0x0f, 0xf1, 0xf2, 0xff, 0x3f, 0xff, 0xf4, /* 10 [0c] */ + 0xf5, 0xf6, 0xf7, 0x89, 0xff, 0xab, 0xff, 0xfc, /* 11 */ + 0xff, 0xff, 0x0f, 0x1f, 0x23, 0x45, 0xf6, 0x7f, /* 12 [19] */ + 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xf9, 0xaf, /* 13 */ + + 0xf0, 0xf1, 0xff, 0x2f, 0xff, 0xf3, 0xff, 0xff, /* 20 [24] */ + 0x4f, 0x5f, 0x67, 0x89, 0xfa, 0xbf, 0xff, 0xcd, /* 21 */ + 0xf0, 0xf1, 0xf2, 0x3f, 0xf4, 0x56, 0xff, 0xff, /* 22 [32] */ + 0xff, 0xff, 0x7f, 0x8f, 0x9a, 0xff, 0xbc, 0xdf, /* 23 */ + 0x0f, 0x1f, 0xf2, 0xff, 0xff, 0x3f, 0xff, 0xff, /* 30 [40] */ + 0xf4, 0xff, 0xf5, 0x6f, 0xff, 0xff, 0xff, 0xff, /* 31 */ + 0x0f, 0x1f, 0x23, 0xff, 0x45, 0x6f, 0xff, 0xff, /* 32 [47] */ + 0xf7, 0xff, 0xf8, 0x9f, 0xff, 0xff, 0xff, 0xff, /* 33 */ +}; + +static inline signed char decode_lo(int sig) +{ + unsigned char offset = (((sig >> 1) & 0x01) | ((sig >> 3) & 0x06) | + ((sig >> 5) & 0x18) | ((sig >> 7) & 0x60)); + unsigned char idx = lo_offset[offset]; + unsigned char base, c; + + if (sig & 1) + idx &= 0xf; + else + idx >>= 4; + if (idx == 0xf) + return (-1); + + base = (sig >> 11) | ((sig >> 9) & 1); + zassert(base < 8, -1, "sig=%x offset=%x idx=%x base=%x\n", sig, offset, idx, + base); + idx += lo_base[base]; + + zassert(idx <= 0x50, -1, "sig=%x offset=%x base=%x idx=%x\n", sig, offset, + base, idx); + c = characters[idx]; + dbprintf(2, " %02x(%x(%02x)/%x(%02x)) => %02x", idx, base, lo_base[base], + offset, lo_offset[offset], (unsigned char)c); + return (c); +} + +static inline signed char decode_hi(int sig) +{ + unsigned char rev = (sig & 0x4400) != 0; + unsigned char idx, c; + if (rev) + sig = (((sig >> 12) & 0x000f) | ((sig >> 4) & 0x00f0) | + ((sig << 4) & 0x0f00) | ((sig << 12) & 0xf000)); + dbprintf(2, " rev=%x", rev != 0); + + switch (sig) { + case 0x0014: + idx = 0x0; + break; + case 0x0025: + idx = 0x1; + break; + case 0x0034: + idx = 0x2; + break; + case 0x0134: + idx = 0x3; + break; + case 0x0143: + idx = 0x4; + break; + case 0x0243: + idx = 0x5; + break; + case 0x0341: + idx = 0x6; + break; + case 0x0352: + idx = 0x7; + break; + case 0x1024: + idx = 0x8; + break; + case 0x1114: + idx = 0x9; + break; + case 0x1134: + idx = 0xa; + break; + case 0x1242: + idx = 0xb; + break; + case 0x1243: + idx = 0xc; + break; + case 0x1441: + idx = 0xd; + rev = 0; + break; + default: + return (-1); + } + if (rev) + idx += 0xe; + c = characters[0x51 + idx]; + dbprintf(2, " %02x => %02x", idx, c); + return (c); +} + +static inline unsigned char calc_check(unsigned char c) +{ + if (!(c & 0x80)) + return (0x18); + c &= 0x7f; + if (c < 0x3d) + return ((c < 0x30 && c != 0x17) ? 0x10 : 0x20); + if (c < 0x50) + return ((c == 0x4d) ? 0x20 : 0x10); + return ((c < 0x67) ? 0x20 : 0x10); +} + +static inline signed char decode6(zbar_decoder_t *dcode) +{ + int sig; + signed char c, chk; + unsigned bars; + + /* build edge signature of character */ + unsigned s = dcode->code128.s6; + + dbprintf(2, " s=%d", s); + if (s < 5) + return (-1); + /* calculate similar edge measurements */ + sig = + (get_color(dcode) == ZBAR_BAR) ? + ((decode_e(get_width(dcode, 0) + get_width(dcode, 1), s, 11) + << 12) | + (decode_e(get_width(dcode, 1) + get_width(dcode, 2), s, 11) << 8) | + (decode_e(get_width(dcode, 2) + get_width(dcode, 3), s, 11) << 4) | + (decode_e(get_width(dcode, 3) + get_width(dcode, 4), s, 11))) : + ((decode_e(get_width(dcode, 5) + get_width(dcode, 4), s, 11) + << 12) | + (decode_e(get_width(dcode, 4) + get_width(dcode, 3), s, 11) << 8) | + (decode_e(get_width(dcode, 3) + get_width(dcode, 2), s, 11) << 4) | + (decode_e(get_width(dcode, 2) + get_width(dcode, 1), s, 11))); + if (sig < 0) + return (-1); + dbprintf(2, " sig=%04x", sig); + /* lookup edge signature */ + c = (sig & 0x4444) ? decode_hi(sig) : decode_lo(sig); + if (c == -1) + return (-1); + + /* character validation */ + bars = + (get_color(dcode) == ZBAR_BAR) ? + (get_width(dcode, 0) + get_width(dcode, 2) + get_width(dcode, 4)) : + (get_width(dcode, 1) + get_width(dcode, 3) + get_width(dcode, 5)); + bars = bars * 11 * 4 / s; + chk = calc_check(c); + dbprintf(2, " bars=%d chk=%d", bars, chk); + if (chk - 7 > bars || bars > chk + 7) + return (-1); + + return (c & 0x7f); +} + +static inline unsigned char validate_checksum(zbar_decoder_t *dcode) +{ + unsigned idx, sum, i, acc = 0; + unsigned char check, err; + + code128_decoder_t *dcode128 = &dcode->code128; + if (dcode128->character < 3) + return (1); + + /* add in irregularly weighted start character */ + idx = (dcode128->direction) ? dcode128->character - 1 : 0; + sum = dcode->buf[idx]; + if (sum >= 103) + sum -= 103; + + /* calculate sum in reverse to avoid multiply operations */ + for (i = dcode128->character - 3; i; i--) { + zassert(sum < 103, -1, "dir=%x i=%x sum=%x acc=%x %s\n", + dcode128->direction, i, sum, acc, + _zbar_decoder_buf_dump(dcode->buf, dcode128->character)); + idx = (dcode128->direction) ? dcode128->character - 1 - i : i; + acc += dcode->buf[idx]; + if (acc >= 103) + acc -= 103; + zassert(acc < 103, -1, "dir=%x i=%x sum=%x acc=%x %s\n", + dcode128->direction, i, sum, acc, + _zbar_decoder_buf_dump(dcode->buf, dcode128->character)); + sum += acc; + if (sum >= 103) + sum -= 103; + } + + /* and compare to check character */ + idx = (dcode128->direction) ? 1 : dcode128->character - 2; + check = dcode->buf[idx]; + dbprintf(2, " chk=%02x(%02x)", sum, check); + err = (sum != check); + if (err) + dbprintf(1, " [checksum error]\n"); + return (err); +} + +/* expand and decode character set C */ +static inline unsigned postprocess_c(zbar_decoder_t *dcode, unsigned start, + unsigned end, unsigned dst) +{ + unsigned i, j; + + /* expand buffer to accommodate 2x set C characters (2 digits per-char) */ + unsigned delta = end - start; + unsigned newlen = dcode->code128.character + delta; + if (size_buf(dcode, newlen)) { + dbprintf(2, " [overflow]\n"); + return ZBAR_NONE; + } + + /* relocate unprocessed data to end of buffer */ + memmove(dcode->buf + start + delta, dcode->buf + start, + dcode->code128.character - start); + dcode->code128.character = newlen; + + for (i = 0, j = dst; i < delta; i++, j += 2) { + /* convert each set C character into two ASCII digits */ + unsigned char code = dcode->buf[start + delta + i]; + dcode->buf[j] = '0'; + if (code >= 50) { + code -= 50; + dcode->buf[j] += 5; + } + if (code >= 30) { + code -= 30; + dcode->buf[j] += 3; + } + if (code >= 20) { + code -= 20; + dcode->buf[j] += 2; + } + if (code >= 10) { + code -= 10; + dcode->buf[j] += 1; + } + zassert(dcode->buf[j] <= '9', delta, "start=%x end=%x i=%x j=%x %s\n", + start, end, i, j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + zassert(code <= 9, delta, "start=%x end=%x i=%x j=%x %s\n", start, end, + i, j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + dcode->buf[j + 1] = '0' + code; + } + return (delta); +} + +/* resolve scan direction and convert to ASCII */ +static inline unsigned char postprocess(zbar_decoder_t *dcode) +{ + unsigned i, j, cexp; + unsigned char code = 0, charset; + code128_decoder_t *dcode128 = &dcode->code128; + dbprintf(2, "\n postproc len=%d", dcode128->character); + dcode->modifiers = 0; + dcode->direction = 1 - 2 * dcode128->direction; + if (dcode128->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode128->character / 2; i++) { + unsigned j = dcode128->character - 1 - i; + code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + zassert(dcode->buf[dcode128->character - 1] == STOP_REV, 1, + "dir=%x %s\n", dcode128->direction, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + } else + zassert(dcode->buf[dcode128->character - 1] == STOP_FWD, 1, + "dir=%x %s\n", dcode128->direction, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + code = dcode->buf[0]; + zassert(code >= START_A && code <= START_C, 1, "%s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + charset = code - START_A; + cexp = (code == START_C) ? 1 : 0; + dbprintf(2, " start=%c", 'A' + charset); + + for (i = 1, j = 0; i < dcode128->character - 2; i++) { + unsigned char code = dcode->buf[i]; + zassert(!(code & 0x80), 1, + "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, code, + charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + if ((charset & 0x2) && (code < 100)) + /* defer character set C for expansion */ + continue; + else if (code < 0x60) { + /* convert character set B to ASCII */ + code = code + 0x20; + if ((!charset || (charset == 0x81)) && (code >= 0x60)) + /* convert character set A to ASCII */ + code -= 0x60; + dcode->buf[j++] = code; + if (charset & 0x80) + charset &= 0x7f; + } else { + dbprintf(2, " %02x", code); + if (charset & 0x2) { + unsigned delta; + /* expand character set C to ASCII */ + zassert(cexp, 1, "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", + i, j, code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, + dcode->code128.character)); + delta = postprocess_c(dcode, cexp, i, j); + i += delta; + j += delta * 2; + cexp = 0; + } + if (code < CODE_C) { + if (code == SHIFT) + charset |= 0x80; + else if (code == FNC2) { + /* FIXME FNC2 - message append */ + } else if (code == FNC3) { + /* FIXME FNC3 - initialize */ + } + } else if (code == FNC1) { + /* FNC1 - Code 128 subsets or ASCII 0x1d */ + if (i == 1) + dcode->modifiers |= MOD(ZBAR_MOD_GS1); + else if (i == 2) + dcode->modifiers |= MOD(ZBAR_MOD_AIM); + else if (i < dcode->code128.character - 3) + dcode->buf[j++] = 0x1d; + /*else drop trailing FNC1 */ + } else if (code >= START_A) { + dbprintf(1, " [truncated]\n"); + return (1); + } else { + unsigned char newset = CODE_A - code; + zassert(code >= CODE_C && code <= CODE_A, 1, + "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, + code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, + dcode->code128.character)); + if (newset != charset) + charset = newset; + else { + /* FIXME FNC4 - extended ASCII */ + } + } + if (charset & 0x2) + cexp = i + 1; + } + } + if (charset & 0x2) { + zassert(cexp, 1, "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, + code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + j += postprocess_c(dcode, cexp, i, j) * 2; + } + zassert(j < dcode->buf_alloc, 1, "j=%02x %s\n", j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->code128.character = j; + return (0); +} + +zbar_symbol_type_t _zbar_decode_code128(zbar_decoder_t *dcode) +{ + code128_decoder_t *dcode128 = &dcode->code128; + signed char c; + + /* update latest character width */ + dcode128->s6 -= get_width(dcode, 6); + dcode128->s6 += get_width(dcode, 0); + + if ((dcode128->character < 0) ? + get_color(dcode) != ZBAR_SPACE : + (/* process every 6th element of active symbol */ + ++dcode128->element != 6 || + /* decode color based on direction */ + get_color(dcode) != dcode128->direction)) + return (0); + dcode128->element = 0; + + dbprintf(2, " code128[%c%02d+%x]:", (dcode128->direction) ? '<' : '>', + dcode128->character, dcode128->element); + + c = decode6(dcode); + if (dcode128->character < 0) { + unsigned qz; + dbprintf(2, " c=%02x", c); + if (c < START_A || c > STOP_REV || c == STOP_FWD) { + dbprintf(2, " [invalid]\n"); + return (0); + } + qz = get_width(dcode, 6); + if (qz && qz < (dcode128->s6 * 3) / 4) { + dbprintf(2, " [invalid qz %d]\n", qz); + return (0); + } + /* decoded valid start/stop */ + /* initialize state */ + dcode128->character = 1; + if (c == STOP_REV) { + dcode128->direction = ZBAR_BAR; + dcode128->element = 7; + } else + dcode128->direction = ZBAR_SPACE; + dcode128->start = c; + dcode128->width = dcode128->s6; + dbprintf(2, " dir=%x [valid start]\n", dcode128->direction); + return (0); + } else if (c < 0 || size_buf(dcode, dcode128->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + if (dcode128->character > 1) + release_lock(dcode, ZBAR_CODE128); + dcode128->character = -1; + return (0); + } else { + unsigned dw; + if (dcode128->width > dcode128->s6) + dw = dcode128->width - dcode128->s6; + else + dw = dcode128->s6 - dcode128->width; + dw *= 4; + if (dw > dcode128->width) { + dbprintf(1, " [width var]\n"); + if (dcode128->character > 1) + release_lock(dcode, ZBAR_CODE128); + dcode128->character = -1; + return (0); + } + } + dcode128->width = dcode128->s6; + + zassert(dcode->buf_alloc > dcode128->character, 0, + "alloc=%x idx=%x c=%02x %s\n", dcode->buf_alloc, + dcode128->character, c, + _zbar_decoder_buf_dump(dcode->buf, dcode->buf_alloc)); + + if (dcode128->character == 1) { + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_CODE128)) { + dcode128->character = -1; + return (0); + } + dcode->buf[0] = dcode128->start; + } + + dcode->buf[dcode128->character++] = c; + + if (dcode128->character > 2 && + ((dcode128->direction) ? c >= START_A && c <= START_C : + c == STOP_FWD)) { + /* FIXME STOP_FWD should check extra bar (and QZ!) */ + zbar_symbol_type_t sym = ZBAR_CODE128; + if (validate_checksum(dcode) || postprocess(dcode)) + sym = ZBAR_NONE; + else if (dcode128->character < CFG(*dcode128, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode128, ZBAR_CFG_MAX_LEN) > 0 && + dcode128->character > CFG(*dcode128, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + sym = ZBAR_NONE; + } else + dbprintf(2, " [valid end]\n"); + dcode128->character = -1; + if (!sym) + release_lock(dcode, ZBAR_CODE128); + return (sym); + } + + dbprintf(2, "\n"); + return (0); +} diff --git a/zbar/decoder/code128.h b/zbar/decoder/code128.h new file mode 100644 index 0000000..6f4bd1e --- /dev/null +++ b/zbar/decoder/code128.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _CODE128_H_ +#define _CODE128_H_ + +/* Code 128 specific decode state */ +typedef struct code128_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-5 */ + int character : 12; /* character position in symbol */ + unsigned char start; /* start character */ + unsigned s6; /* character width */ + unsigned width; /* last character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code128_decoder_t; + +/* reset Code 128 specific state */ +static inline void code128_reset(code128_decoder_t *dcode128) +{ + dcode128->direction = 0; + dcode128->element = 0; + dcode128->character = -1; + dcode128->s6 = 0; +} + +/* decode Code 128 symbols */ +zbar_symbol_type_t _zbar_decode_code128(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code39.c b/zbar/decoder/code39.c new file mode 100644 index 0000000..97f7e0e --- /dev/null +++ b/zbar/decoder/code39.c @@ -0,0 +1,336 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODE39 +#define DEBUG_LEVEL (DEBUG_CODE39) +#endif +#include "debug.h" +#include "decoder.h" + +#define NUM_CHARS (0x2c) + +static const unsigned char code39_hi[32] = { + 0x80 | 0x00, /* 2 next */ + 0x40 | 0x02, /* 4 */ + 0x80 | 0x06, /* 2 next */ + 0xc0 | 0x08, /* 2 skip */ + 0x40 | 0x0a, /* 4 */ + 0x80 | 0x0e, /* 2 next */ + 0xc0 | 0x10, /* 2 skip */ + 0x00 | 0x12, /* direct */ + + 0x80 | 0x13, /* 2 next */ + 0xc0 | 0x15, /* 2 skip */ + 0x80 | 0x17, /* 2 next */ + 0xff, 0xc0 | 0x19, /* 2 skip */ + 0x00 | 0x1b, /* direct */ + 0xff, 0xff, + + 0x40 | 0x1c, /* 4 */ + 0x80 | 0x20, /* 2 next */ + 0xc0 | 0x22, /* 2 skip */ + 0x00 | 0x24, /* direct */ + 0x80 | 0x25, /* 2 next */ + 0xff, 0x00 | 0x27, /* direct */ + 0xff, + + 0xc0 | 0x28, /* 2 skip */ + 0x00 | 0x2a, /* direct */ + 0xff, 0xff, 0x00 | 0x2b, /* direct */ + 0xff, 0xff, 0xff, +}; + +typedef struct char39_s { + unsigned char chk, rev, fwd; +} char39_t; + +static const char39_t code39_encodings[NUM_CHARS] = { + { 0x07, 0x1a, 0x20 }, /* 00 */ + { 0x0d, 0x10, 0x03 }, /* 01 */ + { 0x13, 0x17, 0x22 }, /* 02 */ + { 0x16, 0x1d, 0x23 }, /* 03 */ + { 0x19, 0x0d, 0x05 }, /* 04 */ + { 0x1c, 0x13, 0x06 }, /* 05 */ + { 0x25, 0x07, 0x0c }, /* 06 */ + { 0x2a, 0x2a, 0x27 }, /* 07 */ + { 0x31, 0x04, 0x0e }, /* 08 */ + { 0x34, 0x00, 0x0f }, /* 09 */ + { 0x43, 0x15, 0x25 }, /* 0a */ + { 0x46, 0x1c, 0x26 }, /* 0b */ + { 0x49, 0x0b, 0x08 }, /* 0c */ + { 0x4c, 0x12, 0x09 }, /* 0d */ + { 0x52, 0x19, 0x2b }, /* 0e */ + { 0x58, 0x0f, 0x00 }, /* 0f */ + { 0x61, 0x02, 0x11 }, /* 10 */ + { 0x64, 0x09, 0x12 }, /* 11 */ + { 0x70, 0x06, 0x13 }, /* 12 */ + { 0x85, 0x24, 0x16 }, /* 13 */ + { 0x8a, 0x29, 0x28 }, /* 14 */ + { 0x91, 0x21, 0x18 }, /* 15 */ + { 0x94, 0x2b, 0x19 }, /* 16 */ + { 0xa2, 0x28, 0x29 }, /* 17 */ + { 0xa8, 0x27, 0x2a }, /* 18 */ + { 0xc1, 0x1f, 0x1b }, /* 19 */ + { 0xc4, 0x26, 0x1c }, /* 1a */ + { 0xd0, 0x23, 0x1d }, /* 1b */ + { 0x03, 0x14, 0x1e }, /* 1c */ + { 0x06, 0x1b, 0x1f }, /* 1d */ + { 0x09, 0x0a, 0x01 }, /* 1e */ + { 0x0c, 0x11, 0x02 }, /* 1f */ + { 0x12, 0x18, 0x21 }, /* 20 */ + { 0x18, 0x0e, 0x04 }, /* 21 */ + { 0x21, 0x01, 0x0a }, /* 22 */ + { 0x24, 0x08, 0x0b }, /* 23 */ + { 0x30, 0x05, 0x0d }, /* 24 */ + { 0x42, 0x16, 0x24 }, /* 25 */ + { 0x48, 0x0c, 0x07 }, /* 26 */ + { 0x60, 0x03, 0x10 }, /* 27 */ + { 0x81, 0x1e, 0x14 }, /* 28 */ + { 0x84, 0x25, 0x15 }, /* 29 */ + { 0x90, 0x22, 0x17 }, /* 2a */ + { 0xc0, 0x20, 0x1a }, /* 2b */ +}; + +static const unsigned char code39_characters[NUM_CHARS] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; + +static inline unsigned char code39_decode1(unsigned char enc, unsigned e, + unsigned s) +{ + unsigned char E = decode_e(e, s, 72); + if (E > 18) + return (0xff); + enc <<= 1; + if (E > 6) { + enc |= 1; + dbprintf(2, "1"); + } else + dbprintf(2, "0"); + return (enc); +} + +static inline signed char code39_decode9(zbar_decoder_t *dcode) +{ + unsigned char i, enc = 0, idx; + const char39_t *c; + code39_decoder_t *dcode39 = &dcode->code39; + + if (dcode39->s9 < 9) + return (-1); + + /* threshold bar width ratios */ + for (i = 0; i < 5; i++) { + enc = code39_decode1(enc, get_width(dcode, i), dcode39->s9); + if (enc == 0xff) + return (-1); + } + zassert(enc < 0x20, -1, " enc=%x s9=%x\n", enc, dcode39->s9); + + /* lookup first 5 encoded widths for coarse decode */ + idx = code39_hi[enc]; + if (idx == 0xff) + return (-1); + + /* encode remaining widths (NB first encoded width is lost) */ + for (; i < 9; i++) { + enc = code39_decode1(enc, get_width(dcode, i), dcode39->s9); + if (enc == 0xff) + return (-1); + } + + if ((idx & 0xc0) == 0x80) + idx = (idx & 0x3f) + ((enc >> 3) & 1); + else if ((idx & 0xc0) == 0xc0) + idx = (idx & 0x3f) + ((enc >> 2) & 1); + else if (idx & 0xc0) + idx = (idx & 0x3f) + ((enc >> 2) & 3); + zassert(idx < 0x2c, -1, " idx=%x enc=%x s9=%x\n", idx, enc, dcode39->s9); + + c = &code39_encodings[idx]; + dbprintf(2, " i=%02x chk=%02x c=%02x/%02x", idx, c->chk, c->fwd, c->rev); + if (enc != c->chk) + return (-1); + + dcode39->width = dcode39->s9; + return ((dcode39->direction) ? c->rev : c->fwd); +} + +static inline signed char code39_decode_start(zbar_decoder_t *dcode) +{ + signed char c; + unsigned quiet; + code39_decoder_t *dcode39 = &dcode->code39; + dbprintf(2, " s=%d ", dcode39->s9); + + c = code39_decode9(dcode); + if (c != 0x19 && c != 0x2b) { + dbprintf(2, "\n"); + return (ZBAR_NONE); + } + dcode39->direction ^= (c == 0x19); + + /* check leading quiet zone - spec is 10x */ + quiet = get_width(dcode, 9); + if (quiet && quiet < dcode39->s9 / 2) { + dbprintf(2, " [invalid quiet]\n"); + return (ZBAR_NONE); + } + + dcode39->element = 9; + dcode39->character = 0; + dbprintf(1, " dir=%x [valid start]\n", dcode39->direction); + return (ZBAR_PARTIAL); +} + +static inline int code39_postprocess(zbar_decoder_t *dcode) +{ + int i; + code39_decoder_t *dcode39 = &dcode->code39; + dcode->direction = 1 - 2 * dcode39->direction; + if (dcode39->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode39->character / 2; i++) { + unsigned j = dcode39->character - 1 - i; + char code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + } + for (i = 0; i < dcode39->character; i++) + dcode->buf[i] = ((dcode->buf[i] < 0x2b) ? + code39_characters[(unsigned)dcode->buf[i]] : + '?'); + zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i, + _zbar_decoder_buf_dump(dcode->buf, dcode39->character)); + dcode->buflen = i; + dcode->buf[i] = '\0'; + dcode->modifiers = 0; + return (0); +} + +static inline int check_width(unsigned ref, unsigned w) +{ + unsigned dref = ref; + ref *= 4; + w *= 4; + return (ref - dref <= w && w <= ref + dref); +} + +zbar_symbol_type_t _zbar_decode_code39(zbar_decoder_t *dcode) +{ + code39_decoder_t *dcode39 = &dcode->code39; + signed char c; + + /* update latest character width */ + dcode39->s9 -= get_width(dcode, 9); + dcode39->s9 += get_width(dcode, 0); + + if (dcode39->character < 0) { + if (get_color(dcode) != ZBAR_BAR) + return (ZBAR_NONE); + dbprintf(2, " code39:"); + return (code39_decode_start(dcode)); + } + + if (++dcode39->element < 9) + return (ZBAR_NONE); + + dbprintf(2, " code39[%c%02d+%x]", (dcode39->direction) ? '<' : '>', + dcode39->character, dcode39->element); + + if (dcode39->element == 10) { + unsigned space = get_width(dcode, 0); + if (dcode39->character && + dcode->buf[dcode39->character - 1] == 0x2b) { /* STOP */ + zbar_symbol_type_t sym; + /* trim STOP character */ + dcode39->character--; + sym = ZBAR_NONE; + + /* trailing quiet zone check */ + if (space && space < dcode39->width / 2) + dbprintf(2, " [invalid qz]\n"); + else if (dcode39->character < CFG(*dcode39, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode39, ZBAR_CFG_MAX_LEN) > 0 && + dcode39->character > CFG(*dcode39, ZBAR_CFG_MAX_LEN))) + dbprintf(2, " [invalid len]\n"); + else if (!code39_postprocess(dcode)) { + /* FIXME checksum */ + dbprintf(2, " [valid end]\n"); + sym = ZBAR_CODE39; + } + dcode39->character = -1; + if (!sym) + release_lock(dcode, ZBAR_CODE39); + return (sym); + } + if (space > dcode39->width / 2) { + /* inter-character space check failure */ + dbprintf(2, " ics>%d [invalid ics]", dcode39->width); + if (dcode39->character) + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + } + dcode39->element = 0; + dbprintf(2, "\n"); + return (ZBAR_NONE); + } + + dbprintf(2, " s=%d ", dcode39->s9); + if (!check_width(dcode39->width, dcode39->s9)) { + dbprintf(2, " [width]\n"); + if (dcode39->character) + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + return (ZBAR_NONE); + } + + c = code39_decode9(dcode); + dbprintf(2, " c=%d", c); + + /* lock shared resources */ + if (!dcode39->character && acquire_lock(dcode, ZBAR_CODE39)) { + dcode39->character = -1; + return (ZBAR_PARTIAL); + } + + if (c < 0 || size_buf(dcode, dcode39->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + return (ZBAR_NONE); + } else { + zassert(c < 0x2c, ZBAR_NONE, "c=%02x s9=%x\n", c, dcode39->s9); + dbprintf(2, "\n"); + } + + dcode->buf[dcode39->character++] = c; + + return (ZBAR_NONE); +} diff --git a/zbar/decoder/code39.h b/zbar/decoder/code39.h new file mode 100644 index 0000000..d103121 --- /dev/null +++ b/zbar/decoder/code39.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _CODE39_H_ +#define _CODE39_H_ + +/* Code 39 specific decode state */ +typedef struct code39_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd, 1=rev */ + unsigned element : 4; /* element offset 0-8 */ + int character : 12; /* character position in symbol */ + unsigned s9; /* current character width */ + unsigned width; /* last character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code39_decoder_t; + +/* reset Code 39 specific state */ +static inline void code39_reset(code39_decoder_t *dcode39) +{ + dcode39->direction = 0; + dcode39->element = 0; + dcode39->character = -1; + dcode39->s9 = 0; +} + +/* decode Code 39 symbols */ +zbar_symbol_type_t _zbar_decode_code39(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code93.c b/zbar/decoder/code93.c new file mode 100644 index 0000000..a1acff7 --- /dev/null +++ b/zbar/decoder/code93.c @@ -0,0 +1,387 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <zbar.h> + +#ifdef DEBUG_CODE93 +#define DEBUG_LEVEL (DEBUG_CODE93) +#endif +#include "debug.h" +#include "decoder.h" + +static const signed char code93_hash[0x40] = { + 0x0f, 0x2b, 0x30, 0x38, 0x13, 0x1b, 0x11, 0x2a, 0x0a, -1, 0x2f, + 0x0f, 0x38, 0x38, 0x2f, 0x37, 0x24, 0x3a, 0x1b, 0x36, 0x18, 0x26, + 0x02, 0x2c, 0x2b, 0x05, 0x21, 0x3b, 0x04, 0x15, 0x12, 0x0c, 0x00, + 0x26, 0x23, 0x00, -1, 0x2e, 0x3f, 0x13, 0x2e, 0x36, -1, 0x08, + 0x09, -1, 0x15, 0x14, -1, 0x00, 0x21, 0x3b, -1, 0x33, 0x00, + -1, 0x2d, 0x0c, 0x1b, 0x0a, 0x3f, 0x3f, 0x29, 0x1c, +}; + +static inline int check_width(unsigned cur, unsigned prev) +{ + unsigned dw; + if (prev > cur) + dw = prev - cur; + else + dw = cur - prev; + dw *= 4; + return (dw > prev); +} + +static inline int encode6(zbar_decoder_t *dcode) +{ + /* build edge signature of character */ + unsigned s = dcode->s6; + int sig = 0, i; + + dbprintf(2, " s=%d ", s); + if (s < 9) + return (-1); + + for (i = 6; --i > 0;) { + unsigned c = decode_e(pair_width(dcode, i), s, 9); + if (c > 3) + return (-1); + sig = (sig << 2) | c; + dbprintf(2, "%d", c); + } + dbprintf(2, " sig=%03x", sig); + + return (sig); +} + +static inline int validate_sig(int sig) +{ + int i, sum = 0, emin = 0, sig0 = 0, sig1 = 0; + dbprintf(3, " sum=0"); + for (i = 3; --i >= 0;) { + int e = sig & 3; + sig >>= 2; + sum = e - sum; + sig1 <<= 4; + sig1 += sum; + dbprintf(3, "%d", sum); + if (!i) + break; + + e = sig & 3; + sig >>= 2; + sum = e - sum; + sig0 <<= 4; + if (emin > sum) + emin = sum; + sig0 += sum; + dbprintf(3, "%d", sum); + } + + dbprintf(3, " emin=%d sig=%03x/%03x", emin, sig1 & 0xfff, sig0 & 0xfff); + + emin = emin + (emin << 4) + (emin << 8); + sig0 -= emin; + sig1 += emin; + + dbprintf(3, "=%03x/%03x", sig1 & 0xfff, sig0 & 0xfff); + return ((sig0 | sig1) & 0x888); +} + +static inline int decode6(zbar_decoder_t *dcode) +{ + int sig = encode6(dcode); + int g0, g1, c; + if (sig < 0 || (sig & 0x3) + ((sig >> 4) & 0x3) + ((sig >> 8) & 0x3) != 3 || + validate_sig(sig)) + return (-1); + + if (dcode->code93.direction) { + /* reverse signature */ + unsigned tmp = sig & 0x030; + sig = ((sig & 0x3c0) >> 6) | ((sig & 0x00f) << 6); + sig = ((sig & 0x30c) >> 2) | ((sig & 0x0c3) << 2) | tmp; + } + + g0 = code93_hash[(sig - (sig >> 4)) & 0x3f]; + g1 = code93_hash[((sig >> 2) - (sig >> 7)) & 0x3f]; + zassert(g0 >= 0 && g1 >= 0, -1, "dir=%x sig=%03x g0=%03x g1=%03x %s\n", + dcode->code93.direction, sig, g0, g1, + _zbar_decoder_buf_dump(dcode->buf, dcode->code93.character)); + + c = (g0 + g1) & 0x3f; + dbprintf(2, " g0=%x g1=%x c=%02x", g0, g1, c); + return (c); +} + +static inline zbar_symbol_type_t decode_start(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned dir, qz, s = dcode->s6; + int c; + + dbprintf(2, " code93:"); + c = encode6(dcode); + if (c < 0 || (c != 0x00f && c != 0x0f0)) + return (ZBAR_NONE); + + dir = (c >> 7); + + if (dir) { + if (decode_e(pair_width(dcode, 0), s, 9)) + return (ZBAR_NONE); + qz = get_width(dcode, 8); + } + + qz = get_width(dcode, 7); + if (qz && qz < (s * 3) / 4) { + dbprintf(2, " [invalid qz %d]", qz); + return (ZBAR_NONE); + } + + /* decoded valid start/stop - initialize state */ + dcode93->direction = dir; + dcode93->element = (!dir) ? 0 : 7; + dcode93->character = 0; + dcode93->width = s; + + dbprintf(2, " dir=%x [valid start]", dir); + return (ZBAR_PARTIAL); +} + +static inline zbar_symbol_type_t decode_abort(zbar_decoder_t *dcode, + const char *reason) +{ + code93_decoder_t *dcode93 = &dcode->code93; + if (dcode93->character > 1) + release_lock(dcode, ZBAR_CODE93); + dcode93->character = -1; + if (reason) + dbprintf(1, " [%s]\n", reason); + return (ZBAR_NONE); +} + +static inline zbar_symbol_type_t check_stop(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned n = dcode93->character, s = dcode->s6; + int max_len = CFG(*dcode93, ZBAR_CFG_MAX_LEN); + if (n < 2 || n < CFG(*dcode93, ZBAR_CFG_MIN_LEN) || + (max_len && n > max_len)) + return (decode_abort(dcode, "invalid len")); + + if (dcode93->direction) { + unsigned qz = get_width(dcode, 0); + if (qz && qz < (s * 3) / 4) + return (decode_abort(dcode, "invalid qz")); + } else if (decode_e(pair_width(dcode, 0), s, 9)) + /* FIXME forward-trailing QZ check */ + return (decode_abort(dcode, "invalid stop")); + + return (ZBAR_CODE93); +} + +#define CHKMOD (47) + +static inline int plusmod47(int acc, int add) +{ + acc += add; + if (acc >= CHKMOD) + acc -= CHKMOD; + return (acc); +} + +static inline int validate_checksums(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned d, i, n = dcode93->character; + unsigned sum_c = 0, acc_c = 0, i_c = (n - 2) % 20; + unsigned sum_k = 0, acc_k = 0, i_k = (n - 1) % 15; + + for (i = 0; i < n - 2; i++) { + d = dcode->buf[(dcode93->direction) ? n - 1 - i : i]; + + if (!i_c--) { + acc_c = 0; + i_c = 19; + } + acc_c = plusmod47(acc_c, d); + sum_c = plusmod47(sum_c, acc_c); + + if (!i_k--) { + acc_k = 0; + i_k = 14; + } + acc_k = plusmod47(acc_k, d); + sum_k = plusmod47(sum_k, acc_k); + } + + d = dcode->buf[(dcode93->direction) ? 1 : n - 2]; + dbprintf(2, " C=%02x?=%02x", d, sum_c); + if (d != sum_c) + return (1); + + acc_k = plusmod47(acc_k, sum_c); + sum_k = plusmod47(sum_k, acc_k); + d = dcode->buf[(dcode93->direction) ? 0 : n - 1]; + dbprintf(2, " K=%02x?=%02x", d, sum_k); + if (d != sum_k) + return (1); + + return (0); +} + +/* resolve scan direction and convert to ASCII */ +static inline int postprocess(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned i, j, n = dcode93->character; + static const unsigned char code93_graph[] = "-. $/+%"; + static const unsigned char code93_s2[] = + "\x1b\x1c\x1d\x1e\x1f;<=>?[\\]^_{|}~\x7f\x00\x40`\x7f\x7f\x7f"; + + dbprintf(2, "\n postproc len=%d", n); + dcode->direction = 1 - 2 * dcode93->direction; + if (dcode93->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < n / 2; i++) { + unsigned j = n - 1 - i; + unsigned char d = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = d; + } + } + + n -= 2; + for (i = 0, j = 0; i < n;) { + unsigned char d = dcode->buf[i++]; + if (d < 0xa) + d = '0' + d; + else if (d < 0x24) + d = 'A' + d - 0xa; + else if (d < 0x2b) + d = code93_graph[d - 0x24]; + else { + unsigned shift = d; + zassert(shift < 0x2f, -1, "%s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode93->character)); + d = dcode->buf[i++]; + if (d < 0xa || d >= 0x24) + return (1); + d -= 0xa; + switch (shift) { + case 0x2b: + d++; + break; + case 0x2c: + d = code93_s2[d]; + break; + case 0x2d: + d += 0x21; + break; + case 0x2e: + d += 0x61; + break; + default: + return (1); + } + } + dcode->buf[j++] = d; + } + + zassert(j < dcode->buf_alloc, 1, "j=%02x %s\n", j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code93.character)); + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->modifiers = 0; + return (0); +} + +zbar_symbol_type_t _zbar_decode_code93(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + int c; + + if (dcode93->character < 0) { + zbar_symbol_type_t sym; + if (get_color(dcode) != ZBAR_BAR) + return (ZBAR_NONE); + sym = decode_start(dcode); + dbprintf(2, "\n"); + return (sym); + } + + if (/* process every 6th element of active symbol */ + ++dcode93->element != 6 || + /* decode color based on direction */ + get_color(dcode) == dcode93->direction) + return (ZBAR_NONE); + + dcode93->element = 0; + + dbprintf(2, " code93[%c%02d+%x]:", (dcode93->direction) ? '<' : '>', + dcode93->character, dcode93->element); + + if (check_width(dcode->s6, dcode93->width)) + return (decode_abort(dcode, "width var")); + + c = decode6(dcode); + if (c < 0) + return (decode_abort(dcode, "aborted")); + + if (c == 0x2f) { + if (!check_stop(dcode)) + return (ZBAR_NONE); + if (validate_checksums(dcode)) + return (decode_abort(dcode, "checksum error")); + if (postprocess(dcode)) + return (decode_abort(dcode, "invalid encoding")); + + dbprintf(2, " [valid end]\n"); + dbprintf(3, " %s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode93->character)); + + dcode93->character = -1; + return (ZBAR_CODE93); + } + + if (size_buf(dcode, dcode93->character + 1)) + return (decode_abort(dcode, "overflow")); + + dcode93->width = dcode->s6; + + if (dcode93->character == 1) { + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_CODE93)) + return (decode_abort(dcode, NULL)); + dcode->buf[0] = dcode93->buf; + } + + if (!dcode93->character) + dcode93->buf = c; + else + dcode->buf[dcode93->character] = c; + dcode93->character++; + + dbprintf(2, "\n"); + return (ZBAR_NONE); +} diff --git a/zbar/decoder/code93.h b/zbar/decoder/code93.h new file mode 100644 index 0000000..cf4ef3a --- /dev/null +++ b/zbar/decoder/code93.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _CODE93_H_ +#define _CODE93_H_ + +/* Code 93 specific decode state */ +typedef struct code93_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-5 */ + int character : 12; /* character position in symbol */ + unsigned width; /* last character width */ + unsigned char buf; /* first character */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code93_decoder_t; + +/* reset Code 93 specific state */ +static inline void code93_reset(code93_decoder_t *dcode93) +{ + dcode93->direction = 0; + dcode93->element = 0; + dcode93->character = -1; +} + +/* decode Code 93 symbols */ +zbar_symbol_type_t _zbar_decode_code93(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/databar.c b/zbar/decoder/databar.c new file mode 100644 index 0000000..5722ef6 --- /dev/null +++ b/zbar/decoder/databar.c @@ -0,0 +1,1240 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <zbar.h> +#include <stdlib.h> +#include <stdio.h> + +#ifdef DEBUG_DATABAR +#define DEBUG_LEVEL (DEBUG_DATABAR) +#endif +#include "debug.h" +#include "decoder.h" + +#define GS ('\035') + +enum +{ + SCH_NUM, + SCH_ALNUM, + SCH_ISO646 +}; + +static const signed char finder_hash[0x20] = { + 0x16, 0x1f, 0x02, 0x00, 0x03, 0x00, 0x06, 0x0b, 0x1f, 0x0e, 0x17, + 0x0c, 0x0b, 0x14, 0x11, 0x0c, 0x1f, 0x03, 0x13, 0x08, 0x00, 0x0a, + -1, 0x16, 0x0c, 0x09, -1, 0x1a, 0x1f, 0x1c, 0x00, -1, +}; + +/* DataBar character encoding groups */ +struct group_s { + unsigned short sum; + unsigned char wmax; + unsigned char todd; + unsigned char teven; +} groups[] = { + /* (17,4) DataBar Expanded character groups */ + { 0, 7, 87, 4 }, + { 348, 5, 52, 20 }, + { 1388, 4, 30, 52 }, + { 2948, 3, 10, 104 }, + { 3988, 1, 1, 204 }, + + /* (16,4) DataBar outer character groups */ + { 0, 8, 161, 1 }, + { 161, 6, 80, 10 }, + { 961, 4, 31, 34 }, + { 2015, 3, 10, 70 }, + { 2715, 1, 1, 126 }, + + /* (15,4) DataBar inner character groups */ + { 1516, 8, 81, 1 }, + { 1036, 6, 48, 10 }, + { 336, 4, 20, 35 }, + { 0, 2, 4, 84 }, +}; + +static const unsigned char exp_sequences[] = { + /* sequence Group 1 */ + 0x01, 0x23, 0x25, 0x07, 0x29, 0x47, 0x29, 0x67, 0x0b, 0x29, 0x87, 0xab, + /* sequence Group 2 */ + 0x21, 0x43, 0x65, 0x07, 0x21, 0x43, 0x65, 0x89, 0x21, 0x43, 0x65, 0xa9, + 0x0b, 0x21, 0x43, 0x67, 0x89, 0xab +}; + +/* DataBar expanded checksum multipliers */ +static const unsigned char exp_checksums[] = { 1, 189, 62, 113, 46, 43, + 109, 134, 6, 79, 161, 45 }; + +static inline void append_check14(unsigned char *buf) +{ + unsigned char chk = 0, d; + int i; + for (i = 13; --i >= 0;) { + d = *(buf++) - '0'; + chk += d; + if (!(i & 1)) + chk += d << 1; + } + chk %= 10; + if (chk) + chk = 10 - chk; + *buf = chk + '0'; +} + +static inline void decode10(unsigned char *buf, unsigned long n, int i) +{ + buf += i; + while (--i >= 0) { + unsigned char d = n % 10; + n /= 10; + *--buf = '0' + d; + } +} + +#define VAR_MAX(l, i) ((((l)*12 + (i)) * 2 + 6) / 7) + +#define FEED_BITS(b) \ + while (i < (b) && len) { \ + d = (d << 12) | (*(data++) & 0xfff); \ + i += 12; \ + len--; \ + dbprintf(2, " %03lx", d & 0xfff); \ + } + +#define PUSH_CHAR(c) *(buf++) = (c) + +#define PUSH_CHAR4(c0, c1, c2, c3) \ + do { \ + PUSH_CHAR(c0); \ + PUSH_CHAR(c1); \ + PUSH_CHAR(c2); \ + PUSH_CHAR(c3); \ + } while (0); + +static inline int databar_postprocess_exp(zbar_decoder_t *dcode, int *data) +{ + int i = 0, enc; + unsigned n; + unsigned char *buf; + unsigned long d = *(data++); + int len = d / 211 + 4, buflen; + + /* grok encodation method */ + d = *(data++); + dbprintf(2, "\n len=%d %03lx", len, d & 0xfff); + n = (d >> 4) & 0x7f; + if (n >= 0x40) { + i = 10; + enc = 1; + buflen = 2 + 14 + VAR_MAX(len, 10 - 2 - 44 + 6) + 2; + } else if (n >= 0x38) { + i = 4; + enc = 6 + (n & 7); + buflen = 2 + 14 + 4 + 6 + 2 + 6 + 2; + } else if (n >= 0x30) { + i = 6; + enc = 2 + ((n >> 2) & 1); + buflen = 2 + 14 + 4 + 3 + VAR_MAX(len, 6 - 2 - 44 - 2 - 10) + 2; + } else if (n >= 0x20) { + i = 7; + enc = 4 + ((n >> 3) & 1); + buflen = 2 + 14 + 4 + 6; + } else { + i = 9; + enc = 0; + buflen = VAR_MAX(len, 9 - 2) + 2; + } + dbprintf(2, " buflen=%d enc=%d", buflen, enc); + zassert(buflen > 2, -1, "buflen=%d\n", buflen); + + if (enc < 4) { + /* grok variable length symbol bit field */ + if ((len ^ (d >> (--i))) & 1) + /* even/odd length mismatch */ + return (-1); + if (((d >> (--i)) & 1) != (len > 14)) + /* size group mismatch */ + return (-1); + } + len -= 2; + dbprintf(2, " [%d+%d]", i, len); + + if (size_buf(dcode, buflen)) + return (-1); + buf = dcode->buf; + + /* handle compressed fields */ + if (enc) { + PUSH_CHAR('0'); + PUSH_CHAR('1'); + } + + if (enc == 1) { + i -= 4; + n = (d >> i) & 0xf; + if (i >= 10) + return (-1); + PUSH_CHAR('0' + n); + } else if (enc) + PUSH_CHAR('9'); + + if (enc) { + int j; + for (j = 0; j < 4; j++) { + FEED_BITS(10); + i -= 10; + n = (d >> i) & 0x3ff; + if (n >= 1000) + return (-1); + decode10(buf, n, 3); + buf += 3; + } + append_check14(buf - 13); + buf++; + } + + switch (enc) { + case 2: /* 01100: AI 392x */ + FEED_BITS(2); + i -= 2; + n = (d >> i) & 0x3; + PUSH_CHAR4('3', '9', '2', '0' + n); + break; + + case 3: /* 01101: AI 393x */ + FEED_BITS(12); + i -= 2; + n = (d >> i) & 0x3; + PUSH_CHAR4('3', '9', '3', '0' + n); + i -= 10; + n = (d >> i) & 0x3ff; + if (n >= 1000) + return (-1); + decode10(buf, n, 3); + buf += 3; + break; + + case 4: /* 0100: AI 3103 */ + FEED_BITS(15); + i -= 15; + n = (d >> i) & 0x7fff; + PUSH_CHAR4('3', '1', '0', '3'); + decode10(buf, n, 6); + buf += 6; + break; + + case 5: /* 0101: AI 3202/3203 */ + FEED_BITS(15); + i -= 15; + n = (d >> i) & 0x7fff; + dbprintf(2, " v=%d", n); + PUSH_CHAR4('3', '2', '0', (n >= 10000) ? '3' : '2'); + if (n >= 10000) + n -= 10000; + decode10(buf, n, 6); + buf += 6; + break; + } + if (enc >= 6) { + /* 0111000 - 0111111: AI 310x/320x + AI 11/13/15/17 */ + PUSH_CHAR4('3', '1' + (enc & 1), '0', 'x'); + FEED_BITS(20); + i -= 20; + n = (d >> i) & 0xfffff; + dbprintf(2, " [%d+%d] %d", i, len, n); + if (n >= 1000000) + return (-1); + decode10(buf, n, 6); + *(buf - 1) = *buf; + *buf = '0'; + buf += 6; + + FEED_BITS(16); + i -= 16; + n = (d >> i) & 0xffff; + if (n < 38400) { + int dd, mm, yy; + dd = n % 32; + n /= 32; + mm = n % 12 + 1; + n /= 12; + yy = n; + PUSH_CHAR('1'); + PUSH_CHAR('0' + ((enc - 6) | 1)); + decode10(buf, yy, 2); + buf += 2; + decode10(buf, mm, 2); + buf += 2; + decode10(buf, dd, 2); + buf += 2; + } else if (n > 38400) + return (-1); + } + + if (enc < 4) { + /* remainder is general-purpose data compaction */ + int scheme = SCH_NUM; + while (i > 0 || len > 0) { + FEED_BITS(8); + dbprintf(2, " [%d+%d]", i, len); + + if (scheme == SCH_NUM) { + int n1; + i -= 4; + if (i < 0) + break; + if (!((d >> i) & 0xf)) { + scheme = SCH_ALNUM; + dbprintf(2, ">A"); + continue; + } + if (!len && i < 3) { + /* special case last digit */ + n = ((d >> i) & 0xf) - 1; + if (n > 9) + return (-1); + *(buf++) = '0' + n; + break; + } + i -= 3; + zassert(i >= 0, -1, "\n"); + n = ((d >> i) & 0x7f) - 8; + n1 = n % 11; + n = n / 11; + dbprintf(2, "N%d%d", n, n1); + *(buf++) = (n < 10) ? '0' + n : GS; + *(buf++) = (n1 < 10) ? '0' + n1 : GS; + } else { + unsigned c = 0; + i -= 3; + if (i < 0) + break; + if (!((d >> i) & 0x7)) { + scheme = SCH_NUM; + continue; + } + i -= 2; + if (i < 0) + break; + n = (d >> i) & 0x1f; + if (n == 0x04) { + scheme ^= 0x3; + dbprintf(2, ">%d", scheme); + } else if (n == 0x0f) + c = GS; + else if (n < 0x0f) + c = 43 + n; + else if (scheme == SCH_ALNUM) { + i--; + if (i < 0) + return (-1); + n = (d >> i) & 0x1f; + if (n < 0x1a) + c = 'A' + n; + else if (n == 0x1a) + c = '*'; + else if (n < 0x1f) + c = ',' + n - 0x1b; + else + return (-1); + } else if (scheme == SCH_ISO646 && n < 0x1d) { + i -= 2; + if (i < 0) + return (-1); + n = (d >> i) & 0x3f; + if (n < 0x1a) + c = 'A' + n; + else if (n < 0x34) + c = 'a' + n - 0x1a; + else + return (-1); + } else if (scheme == SCH_ISO646) { + i -= 3; + if (i < 0) + return (-1); + n = ((d >> i) & 0x1f); + dbprintf(2, "(%02x)", n); + if (n < 0xa) + c = '!' + n - 8; + else if (n < 0x15) + c = '%' + n - 0xa; + else if (n < 0x1b) + c = ':' + n - 0x15; + else if (n == 0x1b) + c = '_'; + else if (n == 0x1c) + c = ' '; + else + return (-1); + } else + return (-1); + + if (c) { + dbprintf(2, "%d%c", scheme, c); + *(buf++) = c; + } + } + } + /* FIXME check pad? */ + } + + i = buf - dcode->buf; + zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i, + _zbar_decoder_buf_dump(dcode->buf, i)); + + *buf = 0; + dcode->buflen = i; + if (i && *--buf == GS) { + *buf = 0; + dcode->buflen--; + } + + dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, dcode->buflen)); + return (0); +} +#undef FEED_BITS + +/* convert from heterogeneous base {1597,2841} + * to base 10 character representation + */ +static inline void databar_postprocess(zbar_decoder_t *dcode, unsigned d[4]) +{ + unsigned long r; + databar_decoder_t *db = &dcode->databar; + int i; + unsigned c, chk = 0; + unsigned char *buf = dcode->buf; + *(buf++) = '0'; + *(buf++) = '1'; + buf += 15; + *--buf = '\0'; + *--buf = '\0'; + + dbprintf(2, "\n d={%d,%d,%d,%d}", d[0], d[1], d[2], d[3]); + r = d[0] * 1597 + d[1]; + d[1] = r / 10000; + r %= 10000; + r = r * 2841 + d[2]; + d[2] = r / 10000; + r %= 10000; + r = r * 1597 + d[3]; + d[3] = r / 10000; + dbprintf(2, " r=%ld", r); + + for (i = 4; --i >= 0;) { + c = r % 10; + chk += c; + if (i & 1) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + dbprintf(2, " d={%d,%d,%d}", d[1], d[2], d[3]); + r = d[1] * 2841 + d[2]; + d[2] = r / 10000; + r %= 10000; + r = r * 1597 + d[3]; + d[3] = r / 10000; + dbprintf(2, " r=%ld", r); + + for (i = 4; --i >= 0;) { + c = r % 10; + chk += c; + if (i & 1) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + r = d[2] * 1597 + d[3]; + dbprintf(2, " d={%d,%d} r=%ld", d[2], d[3], r); + + for (i = 5; --i >= 0;) { + c = r % 10; + chk += c; + if (!(i & 1)) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + /* NB linkage flag not supported */ + if (TEST_CFG(db->config, ZBAR_CFG_EMIT_CHECK)) { + chk %= 10; + if (chk) + chk = 10 - chk; + buf[13] = chk + '0'; + dcode->buflen = buf - dcode->buf + 14; + } else + dcode->buflen = buf - dcode->buf + 13; + + dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, 16)); +} + +static inline int check_width(unsigned wf, unsigned wd, unsigned n) +{ + unsigned dwf = wf * 3; + wd *= 14; + wf *= n; + return (wf - dwf <= wd && wd <= wf + dwf); +} + +static inline void merge_segment(databar_decoder_t *db, databar_segment_t *seg) +{ + unsigned csegs = db->csegs; + int i; + for (i = 0; i < csegs; i++) { + databar_segment_t *s = db->segs + i; + if (s != seg && s->finder == seg->finder && s->exp == seg->exp && + s->color == seg->color && s->side == seg->side && + s->data == seg->data && s->check == seg->check && + check_width(seg->width, s->width, 14)) { + /* merge with existing segment */ + unsigned cnt = s->count; + if (cnt < 0x7f) + cnt++; + seg->count = cnt; + seg->partial &= s->partial; + seg->width = (3 * seg->width + s->width + 2) / 4; + s->finder = -1; + dbprintf(2, " dup@%d(%d,%d)", i, cnt, + (db->epoch - seg->epoch) & 0xff); + } else if (s->finder >= 0) { + unsigned age = (db->epoch - s->epoch) & 0xff; + if (age >= 248 || (age >= 128 && s->count < 2)) + s->finder = -1; + } + } +} + +static inline zbar_symbol_type_t match_segment(zbar_decoder_t *dcode, + databar_segment_t *seg) +{ + databar_decoder_t *db = &dcode->databar; + unsigned csegs = db->csegs, maxage = 0xfff; + int i0, i1, i2, maxcnt = 0; + databar_segment_t *smax[3] = { + NULL, + }; + unsigned d[4]; + + if (seg->partial && seg->count < 4) + return (ZBAR_PARTIAL); + + for (i0 = 0; i0 < csegs; i0++) { + databar_segment_t *s0 = db->segs + i0; + if (s0 == seg || s0->finder != seg->finder || s0->exp || + s0->color != seg->color || s0->side == seg->side || + (s0->partial && s0->count < 4) || + !check_width(seg->width, s0->width, 14)) + continue; + + for (i1 = 0; i1 < csegs; i1++) { + databar_segment_t *s1 = db->segs + i1; + int chkf, chks, chk; + unsigned age1; + if (i1 == i0 || s1->finder < 0 || s1->exp || + s1->color == seg->color || (s1->partial && s1->count < 4) || + !check_width(seg->width, s1->width, 14)) + continue; + dbprintf(2, "\n\t[%d,%d] f=%d(0%xx)/%d(%x%x%x)", i0, i1, + seg->finder, seg->color, s1->finder, s1->exp, s1->color, + s1->side); + + if (seg->color) + chkf = seg->finder + s1->finder * 9; + else + chkf = s1->finder + seg->finder * 9; + if (chkf > 72) + chkf--; + if (chkf > 8) + chkf--; + + chks = (seg->check + s0->check + s1->check) % 79; + + if (chkf >= chks) + chk = chkf - chks; + else + chk = 79 + chkf - chks; + + dbprintf(2, " chk=(%d,%d) => %d", chkf, chks, chk); + age1 = (((db->epoch - s0->epoch) & 0xff) + + ((db->epoch - s1->epoch) & 0xff)); + + for (i2 = i1 + 1; i2 < csegs; i2++) { + databar_segment_t *s2 = db->segs + i2; + unsigned cnt, age2, age; + if (i2 == i0 || s2->finder != s1->finder || s2->exp || + s2->color != s1->color || s2->side == s1->side || + s2->check != chk || (s2->partial && s2->count < 4) || + !check_width(seg->width, s2->width, 14)) + continue; + age2 = (db->epoch - s2->epoch) & 0xff; + age = age1 + age2; + cnt = s0->count + s1->count + s2->count; + dbprintf(2, " [%d] MATCH cnt=%d age=%d", i2, cnt, age); + if (maxcnt < cnt || (maxcnt == cnt && maxage > age)) { + maxcnt = cnt; + maxage = age; + smax[0] = s0; + smax[1] = s1; + smax[2] = s2; + } + } + } + } + + if (!smax[0]) + return (ZBAR_PARTIAL); + + d[(seg->color << 1) | seg->side] = seg->data; + for (i0 = 0; i0 < 3; i0++) { + d[(smax[i0]->color << 1) | smax[i0]->side] = smax[i0]->data; + if (!--(smax[i0]->count)) + smax[i0]->finder = -1; + } + seg->finder = -1; + + if (size_buf(dcode, 18)) + return (ZBAR_PARTIAL); + + if (acquire_lock(dcode, ZBAR_DATABAR)) + return (ZBAR_PARTIAL); + + databar_postprocess(dcode, d); + dcode->modifiers = MOD(ZBAR_MOD_GS1); + dcode->direction = 1 - 2 * (seg->side ^ seg->color ^ 1); + return (ZBAR_DATABAR); +} + +static inline signed lookup_sequence(databar_segment_t *seg, int fixed, + int seq[22], const size_t maxsize) +{ + unsigned n = seg->data / 211, i; + const unsigned char *p; + i = (n + 1) / 2 + 1; + n += 4; + i = (i * i) / 4; + dbprintf(2, " {%d,%d:", i, n); + p = exp_sequences + i; + + if (n >= maxsize-1) { + // The loop below checks i<n and increments i by one within the loop + // when accessing seq[22]. For this to be safe, n needs to be < 21. + // See CVE-2023-40890. + return -1; + } + + fixed >>= 1; + seq[0] = 0; + seq[1] = 1; + for (i = 2; i < n;) { + int s = *p; + if (!(i & 2)) { + p++; + s >>= 4; + } else + s &= 0xf; + if (s == fixed) + fixed = -1; + s <<= 1; + dbprintf(2, "%x", s); + seq[i++] = s++; + seq[i++] = s; + } + dbprintf(2, "}"); + seq[n] = -1; + return (fixed < 1); +} + +#define IDX(s) \ + (((s)->finder << 2) | ((s)->color << 1) | ((s)->color ^ (s)->side)) + +static inline zbar_symbol_type_t +match_segment_exp(zbar_decoder_t *dcode, databar_segment_t *seg, int dir) +{ + databar_decoder_t *db = &dcode->databar; + int bestsegs[22], i = 0, segs[22], seq[22]; + int ifixed = seg - db->segs, fixed = IDX(seg), maxcnt = 0; + int iseg[DATABAR_MAX_SEGMENTS]; + unsigned csegs = db->csegs, width = seg->width, maxage = 0x7fff; + + bestsegs[0] = segs[0] = seq[1] = -1; + seq[0] = 0; + + dbprintf(2, "\n fixed=%d@%d: ", fixed, ifixed); + for (i = csegs, seg = db->segs + csegs - 1; --i >= 0; seg--) { + if (seg->exp && seg->finder >= 0 && (!seg->partial || seg->count >= 4)) + iseg[i] = IDX(seg); + else + iseg[i] = -1; + dbprintf(2, " %d", iseg[i]); + } + + for (i = 0;; i--) { + unsigned cnt, chk, age; + unsigned data0, chk0; + if (!i) + dbprintf(2, "\n "); + for (; i >= 0 && seq[i] >= 0; i--) { + int j; + dbprintf(2, " [%d]%d", i, seq[i]); + + if (seq[i] == fixed) { + seg = db->segs + ifixed; + if (segs[i] < 0 && check_width(width, seg->width, 14)) { + dbprintf(2, "*"); + j = ifixed; + } else + continue; + } else { + for (j = segs[i] + 1; j < csegs; j++) { + if (iseg[j] == seq[i] && + (!i || check_width(width, db->segs[j].width, 14))) { + seg = db->segs + j; + break; + } + } + if (j == csegs) + continue; + } + + if (!i) { + signed int lu = lookup_sequence(seg, fixed, seq, sizeof(seq)/sizeof(seq[0])); + if(!lu) { + dbprintf(2, "[nf]"); + continue; + } + if(lu < 0) { + dbprintf(1, " [aborted]\n"); + goto abort; + } + width = seg->width; + dbprintf(2, " A00@%d", j); + } else { + width = (width + seg->width) / 2; + dbprintf(2, " %c%x%x@%d", 'A' + seg->finder, seg->color, + seg->side, j); + } + segs[i++] = j; + segs[i++] = -1; + } + if (i < 0) + break; + + seg = db->segs + segs[0]; + cnt = 0, chk = 0, age = (db->epoch - seg->epoch) & 0xff; + for (i = 1; segs[i] >= 0; i++) { + seg = db->segs + segs[i]; + chk += seg->check; + cnt += seg->count; + age += (db->epoch - seg->epoch) & 0xff; + } + + data0 = db->segs[segs[0]].data; + chk0 = data0 % 211; + chk %= 211; + + dbprintf(2, " chk=%d ?= %d", chk, chk0); + if (chk != chk0) + continue; + + dbprintf(2, " cnt=%d age=%d", cnt, age); + if (maxcnt > cnt || (maxcnt == cnt && maxage <= age)) + continue; + + dbprintf(2, " !"); + maxcnt = cnt; + maxage = age; + for (i = 0; segs[i] >= 0; i++) + bestsegs[i] = segs[i]; + bestsegs[i] = -1; + } + + if (bestsegs[0] < 0) + return (ZBAR_PARTIAL); + + if (acquire_lock(dcode, ZBAR_DATABAR_EXP)) + return (ZBAR_PARTIAL); + + for (i = 0; bestsegs[i] >= 0; i++) + segs[i] = db->segs[bestsegs[i]].data; + + if (databar_postprocess_exp(dcode, segs)) { + release_lock(dcode, ZBAR_DATABAR_EXP); + return (ZBAR_PARTIAL); + } + + for (i = 0; bestsegs[i] >= 0; i++) + if (bestsegs[i] != ifixed) { + seg = db->segs + bestsegs[i]; + if (!--seg->count) + seg->finder = -1; + } + + /* FIXME stacked rows are frequently reversed, + * so direction is impossible to determine at this level + */ + dcode->direction = (1 - 2 * (seg->side ^ seg->color)) * dir; + dcode->modifiers = MOD(ZBAR_MOD_GS1); + return (ZBAR_DATABAR_EXP); +abort: + return (ZBAR_NONE); +} +#undef IDX + +static inline unsigned calc_check(unsigned sig0, unsigned sig1, unsigned side, + unsigned mod) +{ + unsigned chk = 0; + int i; + for (i = 4; --i >= 0;) { + chk = (chk * 3 + (sig1 & 0xf) + 1) * 3 + (sig0 & 0xf) + 1; + sig1 >>= 4; + sig0 >>= 4; + if (!(i & 1)) + chk %= mod; + } + dbprintf(2, " chk=%d", chk); + + if (side) + chk = (chk * (6561 % mod)) % mod; + return (chk); +} + +static inline int calc_value4(unsigned sig, unsigned n, unsigned wmax, + unsigned nonarrow) +{ + unsigned w0, w1, w2, w3; + unsigned v = 0; + n--; + + w0 = (sig >> 12) & 0xf; + if (w0 > 1) { + unsigned n0, sk20, sk21; + if (w0 > wmax) + return (-1); + n0 = n - w0; + sk20 = (n - 1) * n * (2 * n - 1); + sk21 = n0 * (n0 + 1) * (2 * n0 + 1); + v = sk20 - sk21 - 3 * (w0 - 1) * (2 * n - w0); + + if (!nonarrow && w0 > 2 && n > 4) { + unsigned k = (n - 2) * (n - 1) * (2 * n - 3) - sk21; + k -= 3 * (w0 - 2) * (14 * n - 7 * w0 - 31); + v -= k; + } + + if (n - 2 > wmax) { + unsigned wm20 = 2 * wmax * (wmax + 1); + unsigned wm21 = (2 * wmax + 1); + unsigned k = sk20; + if (n0 > wmax) { + k -= sk21; + k += 3 * (w0 - 1) * (wm20 - wm21 * (2 * n - w0)); + } else { + k -= (wmax + 1) * (wmax + 2) * (2 * wmax + 3); + k += 3 * (n - wmax - 2) * (wm20 - wm21 * (n + wmax + 1)); + } + k *= 3; + v -= k; + } + v /= 12; + } else + nonarrow = 1; + n -= w0; + + w1 = (sig >> 8) & 0xf; + if (w1 > 1) { + if (w1 > wmax) + return (-1); + v += (2 * n - w1) * (w1 - 1) / 2; + if (!nonarrow && w1 > 2 && n > 3) + v -= (2 * n - w1 - 5) * (w1 - 2) / 2; + if (n - 1 > wmax) { + if (n - w1 > wmax) + v -= (w1 - 1) * (2 * n - w1 - 2 * wmax); + else + v -= (n - wmax) * (n - wmax - 1); + } + } else + nonarrow = 1; + n -= w1; + + w2 = (sig >> 4) & 0xf; + if (w2 > 1) { + if (w2 > wmax) + return (-1); + v += w2 - 1; + if (!nonarrow && w2 > 2 && n > 2) + v -= n - 2; + if (n > wmax) + v -= n - wmax; + } else + nonarrow = 1; + + w3 = sig & 0xf; + if (w3 == 1) + nonarrow = 1; + else if (w3 > wmax) + return (-1); + + if (!nonarrow) + return (-1); + + return (v); +} + +static inline zbar_symbol_type_t +decode_char(zbar_decoder_t *dcode, databar_segment_t *seg, int off, int dir) +{ + databar_decoder_t *db = &dcode->databar; + unsigned s = calc_s(dcode, (dir > 0) ? off : off - 6, 8); + int n, i, emin[2] = { 0, }, sum = 0; + unsigned sig0 = 0, sig1 = 0; + int diff, vodd, veven, v; + unsigned sum0, sum1, chk; + struct group_s *g; + + if (seg->exp) + n = 17; + else if (seg->side) + n = 15; + else + n = 16; + emin[1] = -n; + + dbprintf(2, + "\n char[%c%d]: n=%d s=%d w=%d sig=", (dir < 0) ? '>' : '<', + off, n, s, seg->width); + if (s < 13 || !check_width(seg->width, s, n)) + return (ZBAR_NONE); + + for (i = 4; --i >= 0;) { + int e = decode_e(pair_width(dcode, off), s, n); + if (e < 0) + return (ZBAR_NONE); + dbprintf(2, "%d", e); + sum = e - sum; + off += dir; + sig1 <<= 4; + if (emin[1] < -sum) + emin[1] = -sum; + sig1 += sum; + if (!i) + break; + + e = decode_e(pair_width(dcode, off), s, n); + if (e < 0) + return (ZBAR_NONE); + dbprintf(2, "%d", e); + sum = e - sum; + off += dir; + sig0 <<= 4; + if (emin[0] > sum) + emin[0] = sum; + sig0 += sum; + } + + diff = emin[~n & 1]; + diff = diff + (diff << 4); + diff = diff + (diff << 8); + + sig0 -= diff; + sig1 += diff; + + dbprintf(2, " emin=%d,%d el=%04x/%04x", emin[0], emin[1], sig0, sig1); + + sum0 = sig0 + (sig0 >> 8); + sum1 = sig1 + (sig1 >> 8); + sum0 += sum0 >> 4; + sum1 += sum1 >> 4; + sum0 &= 0xf; + sum1 &= 0xf; + + dbprintf(2, " sum=%d/%d", sum0, sum1); + + if (sum0 + sum1 + 8 != n) { + dbprintf(2, " [SUM]"); + return (ZBAR_NONE); + } + + if (((sum0 ^ (n >> 1)) | (sum1 ^ (n >> 1) ^ n)) & 1) { + dbprintf(2, " [ODD]"); + return (ZBAR_NONE); + } + + i = ((n & 0x3) ^ 1) * 5 + (sum1 >> 1); + zassert(i < sizeof(groups) / sizeof(*groups), -1, + "n=%d sum=%d/%d sig=%04x/%04x g=%d", n, sum0, sum1, sig0, sig1, i); + g = groups + i; + dbprintf(2, "\n g=%d(%d,%d,%d/%d)", i, g->sum, g->wmax, g->todd, + g->teven); + + vodd = calc_value4(sig0 + 0x1111, sum0 + 4, g->wmax, ~n & 1); + dbprintf(2, " v=%d", vodd); + if (vodd < 0 || vodd > g->todd) + return (ZBAR_NONE); + + veven = calc_value4(sig1 + 0x1111, sum1 + 4, 9 - g->wmax, n & 1); + dbprintf(2, "/%d", veven); + if (veven < 0 || veven > g->teven) + return (ZBAR_NONE); + + v = g->sum; + if (n & 2) + v += vodd + veven * g->todd; + else + v += veven + vodd * g->teven; + + dbprintf(2, " f=%d(%x%x%x)", seg->finder, seg->exp, seg->color, seg->side); + + chk = 0; + if (seg->exp) { + unsigned side = seg->color ^ seg->side ^ 1; + if (v >= 4096) + return (ZBAR_NONE); + /* skip A1 left */ + chk = calc_check(sig0, sig1, side, 211); + if (seg->finder || seg->color || seg->side) { + i = (seg->finder << 1) - side + seg->color; + zassert(i >= 0 && i < 12, ZBAR_NONE, "f=%d(%x%x%x) side=%d i=%d\n", + seg->finder, seg->exp, seg->color, seg->side, side, i); + chk = (chk * exp_checksums[i]) % 211; + } else if (v >= 4009) + return (ZBAR_NONE); + else + chk = 0; + } else { + chk = calc_check(sig0, sig1, seg->side, 79); + if (seg->color) + chk = (chk * 16) % 79; + } + dbprintf(2, " => %d val=%d", chk, v); + + seg->check = chk; + seg->data = v; + + merge_segment(db, seg); + + if (seg->exp) + return (match_segment_exp(dcode, seg, dir)); + else if (dir > 0) + return (match_segment(dcode, seg)); + return (ZBAR_PARTIAL); +} + +static inline int alloc_segment(databar_decoder_t *db) +{ + unsigned maxage = 0, csegs = db->csegs; + int i, old = -1; + for (i = 0; i < csegs; i++) { + databar_segment_t *seg = db->segs + i; + unsigned age; + if (seg->finder < 0) { + dbprintf(2, " free@%d", i); + return (i); + } + age = (db->epoch - seg->epoch) & 0xff; + if (age >= 128 && seg->count < 2) { + seg->finder = -1; + dbprintf(2, " stale@%d (%d - %d = %d)", i, db->epoch, seg->epoch, + age); + return (i); + } + + /* score based on both age and count */ + if (age > seg->count) + age = age - seg->count + 1; + else + age = 1; + + if (maxage < age) { + maxage = age; + old = i; + dbprintf(2, " old@%d(%u)", i, age); + } + } + + if (csegs < DATABAR_MAX_SEGMENTS) { + dbprintf(2, " new@%d", i); + i = csegs; + csegs *= 2; + if (csegs > DATABAR_MAX_SEGMENTS) + csegs = DATABAR_MAX_SEGMENTS; + if (csegs != db->csegs) { + databar_segment_t *seg; + db->segs = realloc(db->segs, csegs * sizeof(*db->segs)); + db->csegs = csegs; + seg = db->segs + csegs; + while (--seg, --csegs >= i) { + seg->finder = -1; + seg->exp = 0; + seg->color = 0; + seg->side = 0; + seg->partial = 0; + seg->count = 0; + seg->epoch = 0; + seg->check = 0; + } + return (i); + } + } + zassert(old >= 0, -1, "\n"); + + db->segs[old].finder = -1; + return (old); +} + +static inline zbar_symbol_type_t decode_finder(zbar_decoder_t *dcode) +{ + databar_decoder_t *db = &dcode->databar; + databar_segment_t *seg; + unsigned e0 = pair_width(dcode, 1); + unsigned e2 = pair_width(dcode, 3); + unsigned e1, e3, s, finder, dir; + int sig, iseg; + int rc, i; + + dbprintf(2, " databar: e0=%d e2=%d", e0, e2); + if (e0 < e2) { + unsigned e = e2 * 4; + if (e < 15 * e0 || e > 34 * e0) + return (ZBAR_NONE); + dir = 0; + e3 = pair_width(dcode, 4); + } else { + unsigned e = e0 * 4; + if (e < 15 * e2 || e > 34 * e2) + return (ZBAR_NONE); + dir = 1; + e2 = e0; + e3 = pair_width(dcode, 0); + } + e1 = pair_width(dcode, 2); + + s = e1 + e3; + dbprintf(2, " e1=%d e3=%d dir=%d s=%d", e1, e3, dir, s); + if (s < 12) + return (ZBAR_NONE); + + sig = ((decode_e(e3, s, 14) << 8) | (decode_e(e2, s, 14) << 4) | + decode_e(e1, s, 14)); + dbprintf(2, " sig=%04x", sig & 0xfff); + if (sig < 0 || ((sig >> 4) & 0xf) < 8 || ((sig >> 4) & 0xf) > 10 || + (sig & 0xf) >= 10 || ((sig >> 8) & 0xf) >= 10 || + (((sig >> 8) + sig) & 0xf) != 10) + return (ZBAR_NONE); + + finder = (finder_hash[(sig - (sig >> 5)) & 0x1f] + + finder_hash[(sig >> 1) & 0x1f]) & + 0x1f; + dbprintf(2, " finder=%d", finder); + if (finder == 0x1f || + !TEST_CFG((finder < 9) ? db->config : db->config_exp, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + zassert(finder >= 0, ZBAR_NONE, "dir=%d sig=%04x f=%d\n", dir, sig & 0xfff, + finder); + + iseg = alloc_segment(db); + if (iseg < 0) + return (ZBAR_NONE); + + seg = db->segs + iseg; + seg->finder = (finder >= 9) ? finder - 9 : finder; + seg->exp = (finder >= 9); + seg->color = get_color(dcode) ^ dir ^ 1; + seg->side = dir; + seg->partial = 0; + seg->count = 1; + seg->width = s; + seg->epoch = db->epoch; + + rc = decode_char(dcode, seg, 12 - dir, -1); + if (!rc) + seg->partial = 1; + else + db->epoch++; + + i = (dcode->idx + 8 + dir) & 0xf; + zassert(db->chars[i] == -1, ZBAR_NONE, "\n"); + db->chars[i] = iseg; + return (rc); +} + +zbar_symbol_type_t _zbar_decode_databar(zbar_decoder_t *dcode) +{ + databar_decoder_t *db = &dcode->databar; + databar_segment_t *seg, *pair; + zbar_symbol_type_t sym; + int iseg, i = dcode->idx & 0xf; + + sym = decode_finder(dcode); + dbprintf(2, "\n"); + + iseg = db->chars[i]; + if (iseg < 0) + return (sym); + + db->chars[i] = -1; + seg = db->segs + iseg; + dbprintf(2, " databar: i=%d part=%d f=%d(%x%x%x)", iseg, + seg->partial, seg->finder, seg->exp, seg->color, seg->side); + zassert(seg->finder >= 0, ZBAR_NONE, "i=%d f=%d(%x%x%x) part=%x\n", iseg, + seg->finder, seg->exp, seg->color, seg->side, seg->partial); + + if (seg->partial) { + pair = NULL; + seg->side = !seg->side; + } else { + int jseg = alloc_segment(db); + pair = db->segs + iseg; + seg = db->segs + jseg; + seg->finder = pair->finder; + seg->exp = pair->exp; + seg->color = pair->color; + seg->side = !pair->side; + seg->partial = 0; + seg->count = 1; + seg->width = pair->width; + seg->epoch = db->epoch; + } + + sym = decode_char(dcode, seg, 1, 1); + if (!sym) { + seg->finder = -1; + if (pair) + pair->partial = 1; + } else + db->epoch++; + dbprintf(2, "\n"); + + return (sym); +} diff --git a/zbar/decoder/databar.h b/zbar/decoder/databar.h new file mode 100644 index 0000000..2ed8820 --- /dev/null +++ b/zbar/decoder/databar.h @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _DATABAR_H_ +#define _DATABAR_H_ + +#define DATABAR_MAX_SEGMENTS 32 + +/* active DataBar (partial) segment entry */ +typedef struct databar_segment_s { + signed finder : 5; /* finder pattern */ + unsigned exp : 1; /* DataBar expanded finder */ + unsigned color : 1; /* finder coloring */ + unsigned side : 1; /* data character side of finder */ + + unsigned partial : 1; /* unpaired partial segment */ + unsigned count : 7; /* times encountered */ + unsigned epoch : 8; /* age, in characters scanned */ + unsigned check : 8; /* bar checksum */ + signed short data; /* decoded character data */ + unsigned short width; /* measured width of finder (14 modules) */ +} databar_segment_t; + +/* DataBar specific decode state */ +typedef struct databar_decoder_s { + unsigned config; /* decoder configuration flags */ + unsigned config_exp; + + unsigned csegs : 8; /* allocated segments */ + unsigned epoch : 8; /* current scan */ + + databar_segment_t *segs; /* active segment list */ + signed char chars[16]; /* outstanding character indices */ +} databar_decoder_t; + +/* reset DataBar segment decode state */ +static inline void databar_new_scan(databar_decoder_t *db) +{ + int i; + for (i = 0; i < 16; i++) + if (db->chars[i] >= 0) { + databar_segment_t *seg = db->segs + db->chars[i]; + if (seg->partial) + seg->finder = -1; + db->chars[i] = -1; + } +} + +/* reset DataBar accumulated segments */ +static inline void databar_reset(databar_decoder_t *db) +{ + int i, n = db->csegs; + databar_new_scan(db); + for (i = 0; i < n; i++) + db->segs[i].finder = -1; +} + +/* decode DataBar symbols */ +zbar_symbol_type_t _zbar_decode_databar(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/ean.c b/zbar/decoder/ean.c new file mode 100644 index 0000000..024d7f8 --- /dev/null +++ b/zbar/decoder/ean.c @@ -0,0 +1,735 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <zbar.h> + +#ifdef DEBUG_EAN +#define DEBUG_LEVEL (DEBUG_EAN) +#endif +#include "debug.h" +#include "decoder.h" + +/* partial decode symbol location */ +typedef enum symbol_partial_e +{ + EAN_LEFT = 0x0000, + EAN_RIGHT = 0x1000, +} symbol_partial_t; + +/* convert compact encoded D2E1E2 to character (bit4 is parity) */ +static const unsigned char digits[] = { + /* E1 E2 */ + 0x06, 0x10, 0x04, 0x13, /* 2 2-5 */ + 0x19, 0x08, 0x11, 0x05, /* 3 2-5 (d2 <= thr) */ + 0x09, 0x12, 0x07, 0x15, /* 4 2-5 (d2 <= thr) */ + 0x16, 0x00, 0x14, 0x03, /* 5 2-5 */ + 0x18, 0x01, 0x02, 0x17, /* E1E2=43,44,33,34 (d2 > thr) */ +}; + +static const unsigned char parity_decode[] = { + 0xf0, /* [xx] BBBBBB = RIGHT half EAN-13 */ + + /* UPC-E check digit encoding */ + 0xff, 0xff, 0x0f, /* [07] BBBAAA = 0 */ + 0xff, 0x1f, /* [0b] BBABAA = 1 */ + 0x2f, /* [0d] BBAABA = 2 */ + 0xf3, /* [0e] BBAAAB = 3 */ + 0xff, 0x4f, /* [13] BABBAA = 4 */ + 0x7f, /* [15] BABABA = 7 */ + 0xf8, /* [16] BABAAB = 8 */ + 0x5f, /* [19] BAABBA = 5 */ + 0xf9, /* [1a] BAABAB = 9 */ + 0xf6, /* [1c] BAAABB = 6 */ + 0xff, + + /* LEFT half EAN-13 leading digit */ + 0xff, 0x6f, /* [23] ABBBAA = 6 */ + 0x9f, /* [25] ABBABA = 9 */ + 0xf5, /* [26] ABBAAB = 5 */ + 0x8f, /* [29] ABABBA = 8 */ + 0xf7, /* [2a] ABABAB = 7 */ + 0xf4, /* [2c] ABAABB = 4 */ + 0xff, 0x3f, /* [31] AABBBA = 3 */ + 0xf2, /* [32] AABBAB = 2 */ + 0xf1, /* [34] AABABB = 1 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, /* [3f] AAAAAA = 0 */ +}; + +#ifdef DEBUG_EAN +static unsigned char debug_buf[0x18]; + +static inline const unsigned char *dsprintbuf(ean_decoder_t *ean) +{ + int i; + for (i = 0; i < 7; i++) + debug_buf[i] = + ((ean->buf[0] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i] = ' '; + for (; i < 13; i++) + debug_buf[i + 1] = + ((ean->buf[7] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 1] = ' '; + for (; i < 18; i++) + debug_buf[i + 2] = + ((ean->buf[13] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 2] = '\0'; + return (debug_buf); +} +#endif + +static inline int check_width(unsigned w0, unsigned w1) +{ + unsigned dw0 = w0; + w0 *= 8; + w1 *= 8; + return (w0 - dw0 <= w1 && w1 <= w0 + dw0); +} + +/* evaluate previous N (>= 2) widths as auxiliary pattern, + * using preceding 4 as character width + */ +static inline signed char aux_end(zbar_decoder_t *dcode, unsigned char fwd) +{ + signed char code, i; + + /* reference width from previous character */ + unsigned s = calc_s(dcode, 4 + fwd, 4); + + /* check quiet zone */ + unsigned qz = get_width(dcode, 0); + if (!fwd && qz && qz <= s * 3 / 4) { + dbprintf(2, " [invalid quiet]"); + return (-1); + } + + dbprintf(2, " ("); + code = 0; + for (i = 1 - fwd; i < 3 + fwd; i++) { + unsigned e = get_width(dcode, i) + get_width(dcode, i + 1); + dbprintf(2, " %d", e); + code = (code << 2) | decode_e(e, s, 7); + if (code < 0) { + dbprintf(2, " [invalid end guard]"); + return (-1); + } + } + dbprintf(2, ") s=%d aux=%x", s, code); + return (code); +} + +/* determine possible auxiliary pattern + * using current 4 as possible character + */ +static inline signed char aux_start(zbar_decoder_t *dcode) +{ + /* FIXME NB add-on has no guard in reverse */ + unsigned e1, e2 = get_width(dcode, 5) + get_width(dcode, 6); + unsigned char E1; + if (dcode->ean.s4 < 6) + return (-1); + + if (decode_e(e2, dcode->ean.s4, 7)) { + dbprintf(2, " [invalid any]"); + return (-1); + } + + e1 = get_width(dcode, 4) + get_width(dcode, 5); + E1 = decode_e(e1, dcode->ean.s4, 7); + + if (get_color(dcode) == ZBAR_BAR) { + /* check for quiet-zone */ + unsigned qz = get_width(dcode, 7); + if (!qz || qz > dcode->ean.s4 * 3 / 4) { + if (!E1) { + dbprintf(2, " [valid normal]"); + return (0); /* normal symbol start */ + } else if (E1 == 1) { + dbprintf(2, " [valid add-on]"); + return (STATE_ADDON); /* add-on symbol start */ + } + } + dbprintf(2, " [invalid start]"); + return (-1); + } + + if (!E1) { + /* attempting decode from SPACE => validate center guard */ + unsigned e3 = get_width(dcode, 6) + get_width(dcode, 7); + unsigned e4 = get_width(dcode, 7) + get_width(dcode, 8); + if (!decode_e(e3, dcode->ean.s4, 7) && + !decode_e(e4, dcode->ean.s4, 7)) { + dbprintf(2, " [valid center]"); + return (0); /* start after center guard */ + } + } + dbprintf(2, " [invalid center]"); + return (-1); +} + +/* check addon delimiter using current 4 as character + */ +static inline signed char aux_mid(zbar_decoder_t *dcode) +{ + unsigned e = get_width(dcode, 4) + get_width(dcode, 5); + return (decode_e(e, dcode->ean.s4, 7)); +} + +/* attempt to decode previous 4 widths (2 bars and 2 spaces) as a character */ +static inline signed char decode4(zbar_decoder_t *dcode) +{ + signed char code; + + /* calculate similar edge measurements */ + unsigned e1 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 1) : + get_width(dcode, 2) + get_width(dcode, 3)); + unsigned e2 = get_width(dcode, 1) + get_width(dcode, 2); + dbprintf(2, "\n e1=%d e2=%d", e1, e2); + + if (dcode->ean.s4 < 6) + return (-1); + + /* create compacted encoding for direct lookup */ + code = ((decode_e(e1, dcode->ean.s4, 7) << 2) | + decode_e(e2, dcode->ean.s4, 7)); + if (code < 0) + return (-1); + dbprintf(2, " code=%x", code); + + /* 4 combinations require additional determinant (D2) + E1E2 == 34 (0110) + E1E2 == 43 (1001) + E1E2 == 33 (0101) + E1E2 == 44 (1010) + */ + if ((1 << code) & 0x0660) { + unsigned char mid, alt; + /* use sum of bar widths */ + unsigned d2 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 2) : + get_width(dcode, 1) + get_width(dcode, 3)); + d2 *= 7; + mid = (((1 << code) & 0x0420) ? 3 /* E1E2 in 33,44 */ + : + 4); /* E1E2 in 34,43 */ + alt = d2 > (mid * dcode->ean.s4); + if (alt) + code = ((code >> 1) & 3) | 0x10; /* compress code space */ + dbprintf(2, " (d2=%d(%d) alt=%d)", d2, mid * dcode->ean.s4, alt); + } + dbprintf(2, " char=%02x", digits[(unsigned char)code]); + zassert(code < 0x14, -1, "code=%02x e1=%x e2=%x s4=%x color=%x\n", code, e1, + e2, dcode->ean.s4, get_color(dcode)); + return (code); +} + +static inline char ean_part_end2(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk; + if (!TEST_CFG(ean->ean2_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) >> 3 | (pass->raw[2] & 0x10) >> 4); + /* calculate "checksum" */ + chk = ~((pass->raw[1] & 0xf) * 10 + (pass->raw[2] & 0xf)) & 0x3; + dbprintf(2, " par=%x chk=%x", par, chk); + if (par != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode2=%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf); + return (ZBAR_EAN2); +} + +static inline zbar_symbol_type_t ean_part_end4(ean_pass_t *pass, + unsigned char fwd) +{ + /* extract parity bits */ + unsigned char par = + ((pass->raw[1] & 0x10) >> 1 | (pass->raw[2] & 0x10) >> 2 | + (pass->raw[3] & 0x10) >> 3 | (pass->raw[4] & 0x10) >> 4); + + dbprintf(2, " par=%x", par); + if (par && par != 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + /* reverse sampled digits */ + unsigned char tmp = pass->raw[1]; + pass->state |= STATE_REV; + pass->raw[1] = pass->raw[4]; + pass->raw[4] = tmp; + tmp = pass->raw[2]; + pass->raw[2] = pass->raw[3]; + pass->raw[3] = tmp; + } + + dbprintf(2, "\n"); + dbprintf(1, "decode4=%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf); + if (!par) + return (ZBAR_EAN8 | EAN_RIGHT); + return (ZBAR_EAN8 | EAN_LEFT); +} + +static inline char ean_part_end5(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk, parchk; + if (!TEST_CFG(ean->ean5_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) | (pass->raw[2] & 0x10) >> 1 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 3 | + (pass->raw[5] & 0x10) >> 4); + /* calculate checksum */ + chk = (((pass->raw[1] & 0x0f) + (pass->raw[2] & 0x0f) * 3 + + (pass->raw[3] & 0x0f) + (pass->raw[4] & 0x0f) * 3 + + (pass->raw[5] & 0x0f)) * + 3) % + 10; + + parchk = parity_decode[par >> 1]; + if (par & 1) + parchk >>= 4; + parchk &= 0xf; + dbprintf(2, " par=%x(%d) chk=%d", par, parchk, chk); + if (parchk != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode5=%x%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf, pass->raw[5] & 0xf); + + return (ZBAR_EAN5); +} + +static inline zbar_symbol_type_t +ean_part_end7(ean_decoder_t *ean, ean_pass_t *pass, unsigned char fwd) +{ + /* calculate parity index */ + unsigned char par = + ((fwd) ? ((pass->raw[1] & 0x10) << 1 | (pass->raw[2] & 0x10) | + (pass->raw[3] & 0x10) >> 1 | (pass->raw[4] & 0x10) >> 2 | + (pass->raw[5] & 0x10) >> 3 | (pass->raw[6] & 0x10) >> 4) : + ((pass->raw[1] & 0x10) >> 4 | (pass->raw[2] & 0x10) >> 3 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 1 | + (pass->raw[5] & 0x10) | (pass->raw[6] & 0x10) << 1)); + + /* lookup parity combination */ + pass->raw[0] = parity_decode[par >> 1]; + if (par & 1) + pass->raw[0] >>= 4; + pass->raw[0] &= 0xf; + dbprintf(2, " par=%02x(%x)", par, pass->raw[0]); + + if (pass->raw[0] == 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + unsigned char i; + pass->state |= STATE_REV; + /* reverse sampled digits */ + for (i = 1; i < 4; i++) { + unsigned char tmp = pass->raw[i]; + pass->raw[i] = pass->raw[7 - i]; + pass->raw[7 - i] = tmp; + } + } + + dbprintf(2, "\n"); + dbprintf(1, "decode=%x%x%x%x%x%x%x(%02x)\n", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, pass->raw[6] & 0xf, par); + + if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) { + if (!par) + return (ZBAR_EAN13 | EAN_RIGHT); + if (par & 0x20) + return (ZBAR_EAN13 | EAN_LEFT); + } + if (par && !(par & 0x20)) + return (ZBAR_UPCE); + + return (ZBAR_NONE); +} + +/* update state for one of 4 parallel passes */ +static inline zbar_symbol_type_t decode_pass(zbar_decoder_t *dcode, + ean_pass_t *pass) +{ + unsigned char idx, fwd; + pass->state++; + idx = pass->state & STATE_IDX; + fwd = pass->state & 1; + + if (get_color(dcode) == ZBAR_SPACE) { + if (pass->state & STATE_ADDON) { + dbprintf(2, " i=%d", idx); + if (idx == 0x09 || idx == 0x21) { + unsigned qz = get_width(dcode, 0); + unsigned s = calc_s(dcode, 1, 4); + zbar_symbol_type_t part = !qz || (qz >= s * 3 / 4); + if (part && idx == 0x09) + part = ean_part_end2(&dcode->ean, pass); + else if (part) + part = ean_part_end5(&dcode->ean, pass); + + if (part || idx == 0x21) { + dcode->ean.direction = 0; + pass->state = -1; + return (part); + } + } + if ((idx & 7) == 1) { + dbprintf(2, " +"); + pass->state += 2; + idx += 2; + } + } else if ((idx == 0x10 || idx == 0x11) && + TEST_CFG(dcode->ean.ean8_config, ZBAR_CFG_ENABLE) && + !aux_end(dcode, fwd)) { + zbar_symbol_type_t part; + dbprintf(2, " fwd=%x", fwd); + part = ean_part_end4(pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } else if ((idx == 0x18 || idx == 0x19)) { + zbar_symbol_type_t part = ZBAR_NONE; + dbprintf(2, " fwd=%x", fwd); + if (!aux_end(dcode, fwd) && pass->raw[5] != 0xff) + part = ean_part_end7(&dcode->ean, pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } + } + + if (pass->state & STATE_ADDON) + idx >>= 1; + + if (!(idx & 0x03) && idx <= 0x14) { + signed char code = -1; + unsigned w = pass->width; + if (!dcode->ean.s4) + return (0); + /* validate guard bars before decoding first char of symbol */ + if (!pass->state) { + pass->state = aux_start(dcode); + pass->width = dcode->ean.s4; + if (pass->state < 0) + return (0); + idx = pass->state & STATE_IDX; + } else { + w = check_width(w, dcode->ean.s4); + if (w) + pass->width = (pass->width + dcode->ean.s4 * 3) / 4; + } + + if (w) + code = decode4(dcode); + else + dbprintf(2, " [bad width]"); + + if ((code < 0 && idx != 0x10) || + (idx > 0 && (pass->state & STATE_ADDON) && aux_mid(dcode))) + pass->state = -1; + else if (code < 0) + pass->raw[5] = 0xff; + else { + dbprintf(2, "\n raw[%x]=%02x =>", idx >> 2, + digits[(unsigned char)code]); + pass->raw[(idx >> 2) + 1] = digits[(unsigned char)code]; + dbprintf(2, " raw=%d%d%d%d%d%d%d", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, + pass->raw[6] & 0xf); + } + } + return (0); +} + +static inline signed char ean_verify_checksum(ean_decoder_t *ean, int n) +{ + unsigned char chk = 0; + unsigned char i, d; + for (i = 0; i < n; i++) { + unsigned char d = ean->buf[i]; + zassert(d < 10, -1, "i=%x d=%x chk=%x %s\n", i, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d; + if ((i ^ n) & 1) { + chk += d << 1; + if (chk >= 20) + chk -= 20; + } + if (chk >= 10) + chk -= 10; + } + zassert(chk < 10, -1, "chk=%x n=%x %s", chk, n, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk) + chk = 10 - chk; + d = ean->buf[n]; + zassert(d < 10, -1, "n=%x d=%x chk=%x %s\n", n, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk != d) { + dbprintf(1, "\nchecksum mismatch %d != %d (%s)\n", chk, d, + dsprintbuf(ean)); + return (-1); + } + return (0); +} + +static inline unsigned char isbn10_calc_checksum(ean_decoder_t *ean) +{ + unsigned int chk = 0; + unsigned char w; + for (w = 10; w > 1; w--) { + unsigned char d = ean->buf[13 - w]; + zassert(d < 10, '?', "w=%x d=%x chk=%x %s\n", w, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d * w; + } + chk = chk % 11; + if (!chk) + return ('0'); + chk = 11 - chk; + if (chk < 10) + return (chk + '0'); + return ('X'); +} + +static inline void ean_expand_upce(ean_decoder_t *ean, ean_pass_t *pass) +{ + int i = 0; + unsigned char decode; + /* parity encoded digit is checksum */ + ean->buf[12] = pass->raw[i++]; + + decode = pass->raw[6] & 0xf; + ean->buf[0] = 0; + ean->buf[1] = 0; + ean->buf[2] = pass->raw[i++] & 0xf; + ean->buf[3] = pass->raw[i++] & 0xf; + ean->buf[4] = (decode < 3) ? decode : pass->raw[i++] & 0xf; + ean->buf[5] = (decode < 4) ? 0 : pass->raw[i++] & 0xf; + ean->buf[6] = (decode < 5) ? 0 : pass->raw[i++] & 0xf; + ean->buf[7] = 0; + ean->buf[8] = 0; + ean->buf[9] = (decode < 3) ? pass->raw[i++] & 0xf : 0; + ean->buf[10] = (decode < 4) ? pass->raw[i++] & 0xf : 0; + ean->buf[11] = (decode < 5) ? pass->raw[i] & 0xf : decode; +} + +static inline zbar_symbol_type_t +integrate_partial(ean_decoder_t *ean, ean_pass_t *pass, zbar_symbol_type_t part) +{ + signed char i, j; + /* copy raw data into holding buffer */ + /* if same partial is not consistent, reset others */ + dbprintf(2, " integrate part=%x (%s)", part, dsprintbuf(ean)); + + if ((ean->left && ((part & ZBAR_SYMBOL) != ean->left)) || + (ean->right && ((part & ZBAR_SYMBOL) != ean->right))) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(type %x %x)", ean->left, ean->right); + ean->left = ean->right = ZBAR_NONE; + } + + if ((ean->left || ean->right) && !check_width(ean->width, pass->width)) { + dbprintf(2, " rst(width %d)", pass->width); + ean->left = ean->right = ZBAR_NONE; + } + + if (part & EAN_RIGHT) { + part &= ZBAR_SYMBOL; + j = part - 1; + for (i = part >> 1; i; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->right && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(right)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->right = part; + part &= ean->left; /* FIXME!? */ + } else if (part == ZBAR_EAN13 || part == ZBAR_EAN8) /* EAN_LEFT */ { + j = (part - 1) >> 1; + for (i = part >> 1; j >= 0; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->left && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(left)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->left = part; + part &= ean->right; /* FIXME!? */ + } else if (part != ZBAR_UPCE) /* add-ons */ { + for (i = part; i > 0; i--) + ean->buf[i - 1] = pass->raw[i] & 0xf; + ean->left = part; + } else + ean_expand_upce(ean, pass); + + ean->width = pass->width; + + if (!part) + part = ZBAR_PARTIAL; + + if (((part == ZBAR_EAN13 || part == ZBAR_UPCE) && + ean_verify_checksum(ean, 12)) || + (part == ZBAR_EAN8 && ean_verify_checksum(ean, 7))) { + /* invalid checksum */ + if (ean->right) + ean->left = ZBAR_NONE; + else + ean->right = ZBAR_NONE; + part = ZBAR_NONE; + } + + if (part == ZBAR_EAN13) { + /* special case EAN-13 subsets */ + if (!ean->buf[0] && TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + part = ZBAR_UPCA; + else if (ean->buf[0] == 9 && ean->buf[1] == 7) { + if ((ean->buf[2] == 8 || ean->buf[2] == 9) && + TEST_CFG(ean->isbn13_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN13; + } else if (ean->buf[2] == 8 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN10; + } + } + } else if (part == ZBAR_UPCE) { + if (TEST_CFG(ean->upce_config, ZBAR_CFG_ENABLE)) { + /* UPC-E was decompressed for checksum verification, + * but user requested compressed result + */ + ean->buf[0] = ean->buf[1] = 0; + for (i = 2; i < 8; i++) + ean->buf[i] = pass->raw[i - 1] & 0xf; + ean->buf[i] = pass->raw[0] & 0xf; + } else if (TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + /* UPC-E reported as UPC-A has priority over EAN-13 */ + part = ZBAR_UPCA; + else if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) + part = ZBAR_EAN13; + else + part = ZBAR_NONE; + } + + dbprintf(2, " dir=%d %x/%x=%x", ean->direction, ean->left, ean->right, + part); + return (part); +} + +/* copy result to output buffer */ +static inline void postprocess(zbar_decoder_t *dcode, zbar_symbol_type_t sym) +{ + ean_decoder_t *ean = &dcode->ean; + zbar_symbol_type_t base = sym; + int i = 0, j = 0; + if (base > ZBAR_PARTIAL) { + if (base == ZBAR_UPCA) + i = 1; + else if (base == ZBAR_UPCE) { + i = 1; + base--; + } else if (base == ZBAR_ISBN13) + base = ZBAR_EAN13; + else if (base == ZBAR_ISBN10) + i = 3; + + if (base == ZBAR_ISBN10 || + (base > ZBAR_EAN5 && + !TEST_CFG(ean_get_config(ean, sym), ZBAR_CFG_EMIT_CHECK))) + base--; + + for (; j < base && ean->buf[i] >= 0; i++, j++) + dcode->buf[j] = ean->buf[i] + '0'; + + if (sym == ZBAR_ISBN10 && j == 9 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_EMIT_CHECK)) + /* recalculate ISBN-10 check digit */ + dcode->buf[j++] = isbn10_calc_checksum(ean); + } + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->direction = 1 - 2 * ean->direction; + dcode->modifiers = 0; + dbprintf(2, " base=%d j=%d (%s)", base, j, dcode->buf); +} + +zbar_symbol_type_t _zbar_decode_ean(zbar_decoder_t *dcode) +{ + /* process upto 4 separate passes */ + zbar_symbol_type_t sym = ZBAR_NONE; + unsigned char pass_idx = dcode->idx & 3; + unsigned char i; + + /* update latest character width */ + dcode->ean.s4 -= get_width(dcode, 4); + dcode->ean.s4 += get_width(dcode, 0); + + for (i = 0; i < 4; i++) { + ean_pass_t *pass = &dcode->ean.pass[i]; + if (pass->state >= 0 || i == pass_idx) { + zbar_symbol_type_t part; + dbprintf(2, " ean[%x/%x]: idx=%x st=%d s=%d", i, pass_idx, + dcode->idx, pass->state, dcode->ean.s4); + part = decode_pass(dcode, pass); + if (part) { + /* update accumulated data from new partial decode */ + sym = integrate_partial(&dcode->ean, pass, part); + if (sym) { + /* this pass valid => _reset_ all passes */ + dbprintf(2, " sym=%x", sym); + dcode->ean.pass[0].state = dcode->ean.pass[1].state = -1; + dcode->ean.pass[2].state = dcode->ean.pass[3].state = -1; + if (sym > ZBAR_PARTIAL) { + if (!acquire_lock(dcode, sym)) + postprocess(dcode, sym); + else { + dbprintf(1, " [locked %d]", dcode->lock); + sym = ZBAR_PARTIAL; + } + } + } + } + dbprintf(2, "\n"); + } + } + return (sym); +} diff --git a/zbar/decoder/ean.h b/zbar/decoder/ean.h new file mode 100644 index 0000000..351245b --- /dev/null +++ b/zbar/decoder/ean.h @@ -0,0 +1,99 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _EAN_H_ +#define _EAN_H_ + +/* state of each parallel decode attempt */ +typedef struct ean_pass_s { + signed char state; /* module position of w[idx] in symbol */ +#define STATE_REV 0x80 /* scan direction reversed */ +#define STATE_ADDON 0x40 /* scanning add-on */ +#define STATE_IDX 0x3f /* element offset into symbol */ + unsigned width; /* width of last character */ + unsigned char raw[7]; /* decode in process */ +} ean_pass_t; + +/* EAN/UPC specific decode state */ +typedef struct ean_decoder_s { + ean_pass_t pass[4]; /* state of each parallel decode attempt */ + zbar_symbol_type_t left; /* current holding buffer contents */ + zbar_symbol_type_t right; + int direction; /* scan direction */ + unsigned s4, width; /* character width */ + signed char buf[18]; /* holding buffer */ + + signed char enable; + unsigned ean13_config; + unsigned ean8_config; + unsigned upca_config; + unsigned upce_config; + unsigned isbn10_config; + unsigned isbn13_config; + unsigned ean5_config; + unsigned ean2_config; +} ean_decoder_t; + +/* reset EAN/UPC pass specific state */ +static inline void ean_new_scan(ean_decoder_t *ean) +{ + ean->pass[0].state = ean->pass[1].state = -1; + ean->pass[2].state = ean->pass[3].state = -1; + ean->s4 = 0; +} + +/* reset all EAN/UPC state */ +static inline void ean_reset(ean_decoder_t *ean) +{ + ean_new_scan(ean); + ean->left = ean->right = ZBAR_NONE; +} + +static inline unsigned ean_get_config(ean_decoder_t *ean, + zbar_symbol_type_t sym) +{ + switch (sym) { + case ZBAR_EAN2: + return (ean->ean2_config); + case ZBAR_EAN5: + return (ean->ean5_config); + case ZBAR_EAN8: + return (ean->ean8_config); + case ZBAR_UPCE: + return (ean->upce_config); + case ZBAR_ISBN10: + return (ean->isbn10_config); + case ZBAR_UPCA: + return (ean->upca_config); + case ZBAR_EAN13: + return (ean->ean13_config); + case ZBAR_ISBN13: + return (ean->isbn13_config); + default: + return (0); + } +} + +/* decode EAN/UPC symbols */ +zbar_symbol_type_t _zbar_decode_ean(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/i25.c b/zbar/decoder/i25.c new file mode 100644 index 0000000..8fdea25 --- /dev/null +++ b/zbar/decoder/i25.c @@ -0,0 +1,265 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_I25 +#define DEBUG_LEVEL (DEBUG_I25) +#endif +#include "debug.h" +#include "decoder.h" + +static inline unsigned char i25_decode1(unsigned char enc, unsigned e, + unsigned s) +{ + unsigned char E = decode_e(e, s, 45); + if (E > 7) + return (0xff); + enc <<= 1; + if (E > 2) + enc |= 1; + return (enc); +} + +static inline unsigned char i25_decode10(zbar_decoder_t *dcode, + unsigned char offset) +{ + unsigned char enc = 0, par = 0; + signed char i; + + i25_decoder_t *dcode25 = &dcode->i25; + dbprintf(2, " s=%d", dcode25->s10); + if (dcode25->s10 < 10) + return (0xff); + + /* threshold bar width ratios */ + for (i = 8; i >= 0; i -= 2) { + unsigned char j = offset + ((dcode25->direction) ? i : 8 - i); + enc = i25_decode1(enc, get_width(dcode, j), dcode25->s10); + if (enc == 0xff) + return (0xff); + if (enc & 1) + par++; + } + + dbprintf(2, " enc=%02x par=%x", enc, par); + + /* parity check */ + if (par != 2) { + dbprintf(2, " [bad parity]"); + return (0xff); + } + + /* decode binary weights */ + enc &= 0xf; + if (enc & 8) { + if (enc == 12) + enc = 0; + else if (--enc > 9) { + dbprintf(2, " [invalid encoding]"); + return (0xff); + } + } + + dbprintf(2, " => %x", enc); + return (enc); +} + +static inline signed char i25_decode_start(zbar_decoder_t *dcode) +{ + unsigned char enc = 0; + unsigned char i = 10; + unsigned quiet; + + i25_decoder_t *dcode25 = &dcode->i25; + if (dcode25->s10 < 10) + return (ZBAR_NONE); + + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + + if ((get_color(dcode) == ZBAR_BAR) ? + enc != 4 : + (enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10))) { + dbprintf(4, " i25: s=%d enc=%x [invalid]\n", dcode25->s10, enc); + return (ZBAR_NONE); + } + + /* check leading quiet zone - spec is 10n(?) + * we require 5.25n for w=2n to 6.75n for w=3n + * (FIXME should really factor in w:n ratio) + */ + quiet = get_width(dcode, i); + if (quiet && quiet < dcode25->s10 * 3 / 8) { + dbprintf(3, " i25: s=%d enc=%x q=%d [invalid qz]\n", dcode25->s10, + enc, quiet); + return (ZBAR_NONE); + } + + dcode25->direction = get_color(dcode); + dcode25->element = 1; + dcode25->character = 0; + return (ZBAR_PARTIAL); +} + +static inline int i25_acquire_lock(zbar_decoder_t *dcode) +{ + int i; + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_I25)) { + dcode->i25.character = -1; + return (1); + } + + /* copy holding buffer */ + for (i = 4; --i >= 0;) + dcode->buf[i] = dcode->i25.buf[i]; + return (0); +} + +static inline signed char i25_decode_end(zbar_decoder_t *dcode) +{ + unsigned char E; + i25_decoder_t *dcode25 = &dcode->i25; + + /* check trailing quiet zone */ + unsigned quiet = get_width(dcode, 0); + if ((quiet && quiet < dcode25->width * 3 / 8) || + decode_e(get_width(dcode, 1), dcode25->width, 45) > 2 || + decode_e(get_width(dcode, 2), dcode25->width, 45) > 2) { + dbprintf(3, " i25: s=%d q=%d [invalid qz]\n", dcode25->width, + quiet); + return (ZBAR_NONE); + } + + /* check exit condition */ + E = decode_e(get_width(dcode, 3), dcode25->width, 45); + if ((!dcode25->direction) ? + E - 3 > 4 : + (E > 2 || decode_e(get_width(dcode, 4), dcode25->width, 45) > 2)) + return (ZBAR_NONE); + + if (dcode25->character <= 4 && i25_acquire_lock(dcode)) + return (ZBAR_PARTIAL); + + dcode->direction = 1 - 2 * dcode25->direction; + if (dcode25->direction) { + int i; + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode25->character / 2; i++) { + unsigned j = dcode25->character - 1 - i; + char c = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = c; + } + } + + if (dcode25->character < CFG(*dcode25, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode25, ZBAR_CFG_MAX_LEN) > 0 && + dcode25->character > CFG(*dcode25, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + release_lock(dcode, ZBAR_I25); + dcode25->character = -1; + return (ZBAR_NONE); + } + + zassert(dcode25->character < dcode->buf_alloc, ZBAR_NONE, "i=%02x %s\n", + dcode25->character, + _zbar_decoder_buf_dump(dcode->buf, dcode25->character)); + dcode->buflen = dcode25->character; + dcode->buf[dcode25->character] = '\0'; + dcode->modifiers = 0; + dbprintf(2, " [valid end]\n"); + dcode25->character = -1; + return (ZBAR_I25); +} + +zbar_symbol_type_t _zbar_decode_i25(zbar_decoder_t *dcode) +{ + unsigned char c; + unsigned char *buf; + + i25_decoder_t *dcode25 = &dcode->i25; + + /* update latest character width */ + dcode25->s10 -= get_width(dcode, 10); + dcode25->s10 += get_width(dcode, 0); + + if (dcode25->character < 0 && !i25_decode_start(dcode)) + return (ZBAR_NONE); + + if (--dcode25->element == 6 - dcode25->direction) + return (i25_decode_end(dcode)); + else if (dcode25->element) + return (ZBAR_NONE); + + /* FIXME check current character width against previous */ + dcode25->width = dcode25->s10; + + dbprintf(2, " i25[%c%02d+%x]", (dcode25->direction) ? '<' : '>', + dcode25->character, dcode25->element); + + if (dcode25->character == 4 && i25_acquire_lock(dcode)) + return (ZBAR_PARTIAL); + + c = i25_decode10(dcode, 1); + dbprintf(2, " c=%x", c); + if (c > 9) { + dbprintf(2, " [aborted]\n"); + goto reset; + } + + if (size_buf(dcode, dcode25->character + 3)) { + dbprintf(2, " [overflow]\n"); + goto reset; + } + + if (dcode25->character >= 4) + buf = dcode->buf; + else + buf = dcode25->buf; + buf[dcode25->character++] = c + '0'; + + c = i25_decode10(dcode, 0); + dbprintf(2, " c=%x", c); + if (c > 9) { + dbprintf(2, " [aborted]\n"); + goto reset; + } else + dbprintf(2, "\n"); + + buf[dcode25->character++] = c + '0'; + dcode25->element = 10; + return ((dcode25->character == 2) ? ZBAR_PARTIAL : ZBAR_NONE); + +reset: + if (dcode25->character >= 4) + release_lock(dcode, ZBAR_I25); + dcode25->character = -1; + return (ZBAR_NONE); +} diff --git a/zbar/decoder/i25.h b/zbar/decoder/i25.h new file mode 100644 index 0000000..57403e8 --- /dev/null +++ b/zbar/decoder/i25.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _I25_H_ +#define _I25_H_ + +/* interleaved 2 of 5 specific decode state */ +typedef struct i25_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 4; /* element offset 0-8 */ + int character : 12; /* character position in symbol */ + unsigned s10; /* current character width */ + unsigned width; /* last character width */ + unsigned char buf[4]; /* initial scan buffer */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} i25_decoder_t; + +/* reset interleaved 2 of 5 specific state */ +static inline void i25_reset(i25_decoder_t *i25) +{ + i25->direction = 0; + i25->element = 0; + i25->character = -1; + i25->s10 = 0; +} + +/* decode interleaved 2 of 5 symbols */ +zbar_symbol_type_t _zbar_decode_i25(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/pdf417.c b/zbar/decoder/pdf417.c new file mode 100644 index 0000000..436a7bd --- /dev/null +++ b/zbar/decoder/pdf417.c @@ -0,0 +1,224 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" + +#include <zbar.h> + +#ifdef DEBUG_PDF417 +#define DEBUG_LEVEL (DEBUG_PDF417) +#endif +#include "debug.h" +#include "decoder.h" + +#include "pdf417_hash.h" + +#define PDF417_STOP 0xbff + +static inline signed short pdf417_decode8(zbar_decoder_t *dcode) +{ + long sig = 0; + signed char e; + unsigned char i; + int clst; + signed short g[3]; + unsigned short c; + /* build edge signature of character + * from similar edge measurements + */ + unsigned s = dcode->pdf417.s8; + dbprintf(2, " s=%d ", s); + if (s < 8) + return (-1); + + for (i = 0; i < 7; i++) { + if (get_color(dcode) == ZBAR_SPACE) + e = decode_e(get_width(dcode, i) + get_width(dcode, i + 1), s, 17); + else + e = decode_e(get_width(dcode, 7 - i) + get_width(dcode, 6 - i), s, + 17); + dbprintf(4, "%x", e); + if (e < 0 || e > 8) + return (-1); + sig = (sig << 3) ^ e; + } + dbprintf(2, " sig=%06lx", sig); + + /* determine cluster number */ + clst = + ((sig & 7) - ((sig >> 3) & 7) + ((sig >> 12) & 7) - ((sig >> 15) & 7)); + if (clst < 0) + clst += 9; + dbprintf(2, " k=%d", clst); + zassert(clst >= 0 && clst < 9, -1, "dir=%x sig=%lx k=%x %s\n", + dcode->pdf417.direction, sig, clst, + _zbar_decoder_buf_dump(dcode->buf, dcode->pdf417.character)); + + if (clst != 0 && clst != 3 && clst != 6) { + if (get_color(dcode) && clst == 7 && sig == 0x080007) + return (PDF417_STOP); + return (-1); + } + + sig &= 0x3ffff; + g[0] = pdf417_hash[(sig - (sig >> 10)) & PDF417_HASH_MASK]; + g[1] = pdf417_hash[((sig >> 8) - sig) & PDF417_HASH_MASK]; + g[2] = pdf417_hash[((sig >> 14) - (sig >> 1)) & PDF417_HASH_MASK]; + zassert(g[0] >= 0 && g[1] >= 0 && g[2] >= 0, -1, + "dir=%x sig=%lx k=%x g0=%03x g1=%03x g2=%03x %s\n", + dcode->pdf417.direction, sig, clst, g[0], g[1], g[2], + _zbar_decoder_buf_dump(dcode->buf, dcode->pdf417.character)); + + c = (g[0] + g[1] + g[2]) & PDF417_HASH_MASK; + dbprintf(2, " g0=%x g1=%x g2=%x c=%03d(%d)", g[0], g[1], g[2], c & 0x3ff, + c >> 10); + return (c); +} + +static inline signed char pdf417_decode_start(zbar_decoder_t *dcode) +{ + int ei, ex; + pdf417_decoder_t *dcode417; + unsigned s = dcode->pdf417.s8; + if (s < 8) + return (0); + + ei = decode_e(get_width(dcode, 0) + get_width(dcode, 1), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 2 : 6; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 1) + get_width(dcode, 2), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 2) + get_width(dcode, 3), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 0 : 2; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 3) + get_width(dcode, 4), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 0 : 2; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 4) + get_width(dcode, 5), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 5) + get_width(dcode, 6), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 6) + get_width(dcode, 7), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 7 : 1; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 7) + get_width(dcode, 8), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 8 : 1; + + if (get_color(dcode) == ZBAR_BAR) { + /* stop character has extra bar */ + if (ei != 1) + return (0); + ei = decode_e(get_width(dcode, 8) + get_width(dcode, 9), s, 17); + } + + dbprintf(2, " pdf417[%c]: s=%d", (get_color(dcode)) ? '<' : '>', s); + + /* check quiet zone */ + if (ei >= 0 && ei < ex) { + dbprintf(2, " [invalid quiet]\n"); + return (0); + } + + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_PDF417)) { + dbprintf(2, " [locked %d]\n", dcode->lock); + return (0); + } + + dcode417 = &dcode->pdf417; + dcode417->direction = get_color(dcode); + dcode417->element = 0; + dcode417->character = 0; + + dbprintf(2, " [valid start]\n"); + return (ZBAR_PARTIAL); +} + +zbar_symbol_type_t _zbar_decode_pdf417(zbar_decoder_t *dcode) +{ + signed short c; + pdf417_decoder_t *dcode417 = &dcode->pdf417; + + /* update latest character width */ + dcode417->s8 -= get_width(dcode, 8); + dcode417->s8 += get_width(dcode, 0); + + if (dcode417->character < 0) { + pdf417_decode_start(dcode); + dbprintf(4, "\n"); + return (0); + } + + /* process every 8th element of active symbol */ + if (++dcode417->element) + return (0); + dcode417->element = 0; + + dbprintf(2, " pdf417[%c%02d]:", (dcode417->direction) ? '<' : '>', + dcode417->character); + + if (get_color(dcode) != dcode417->direction) { + int c = dcode417->character; + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + zassert(get_color(dcode) == dcode417->direction, ZBAR_NONE, + "color=%x dir=%x char=%d elem=0 %s\n", get_color(dcode), + dcode417->direction, c, _zbar_decoder_buf_dump(dcode->buf, c)); + } + + c = pdf417_decode8(dcode); + if (c < 0 || size_buf(dcode, dcode417->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + return (0); + } + + /* FIXME TBD infer dimensions, save codewords */ + + if (c == PDF417_STOP) { + dbprintf(1, " [valid stop]"); + /* FIXME check trailing bar and qz */ + dcode->direction = 1 - 2 * dcode417->direction; + dcode->modifiers = 0; + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + } + + dbprintf(2, "\n"); + return (0); +} diff --git a/zbar/decoder/pdf417.h b/zbar/decoder/pdf417.h new file mode 100644 index 0000000..6c5d3aa --- /dev/null +++ b/zbar/decoder/pdf417.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _PDF417_H_ +#define _PDF417_H_ + +/* PDF417 specific decode state */ +typedef struct pdf417_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-7 */ + int character : 12; /* character position in symbol */ + unsigned s8; /* character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} pdf417_decoder_t; + +/* reset PDF417 specific state */ +static inline void pdf417_reset(pdf417_decoder_t *pdf417) +{ + pdf417->direction = 0; + pdf417->element = 0; + pdf417->character = -1; + pdf417->s8 = 0; +} + +/* decode PDF417 symbols */ +zbar_symbol_type_t _zbar_decode_pdf417(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/pdf417_hash.h b/zbar/decoder/pdf417_hash.h new file mode 100644 index 0000000..6c2c5e8 --- /dev/null +++ b/zbar/decoder/pdf417_hash.h @@ -0,0 +1,406 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _PDF417_HASH_H_ +#define _PDF417_HASH_H_ + +/* PDF417 bar to codeword decode table */ + +#define PDF417_HASH_MASK 0xfff + +static const signed short pdf417_hash[PDF417_HASH_MASK + 1] = { + 0x170, 0xd8e, 0x02e, 0x000, 0xa21, 0xc99, 0x000, 0xf06, 0xdaa, 0x7a1, 0xc5f, + 0x7ff, 0xbcf, 0xac8, 0x000, 0xc51, 0x49a, 0x5c7, 0x000, 0xef2, 0x000, 0x7dd, + 0x9ee, 0xe32, 0x1b7, 0x489, 0x3b7, 0xe70, 0x9c8, 0xe5e, 0xdf4, 0x599, 0x4e0, + 0x608, 0x639, 0xead, 0x0ac, 0x57c, 0x000, 0x20d, 0x61b, 0x000, 0x7d1, 0x80f, + 0x803, 0x000, 0x946, 0x093, 0x79c, 0xf9c, 0xb34, 0x6d8, 0x4f1, 0x975, 0x886, + 0x313, 0xe8a, 0xf20, 0x3c9, 0xa92, 0xb90, 0xa1d, 0x091, 0x0ac, 0xb50, 0x3af, + 0x90a, 0x45a, 0x815, 0xf29, 0xb20, 0xb6d, 0xc5c, 0x1cd, 0x1e2, 0x1bf, 0x963, + 0x80b, 0xa7c, 0x9b7, 0xb65, 0x6b7, 0x117, 0xc04, 0x000, 0x18e, 0x000, 0x77f, + 0xe0e, 0xf48, 0x370, 0x818, 0x379, 0x000, 0x090, 0xe77, 0xd99, 0x8b8, 0xb95, + 0x8a9, 0x94c, 0xc48, 0x679, 0x000, 0x41a, 0x9ea, 0xb0e, 0x9c1, 0x1b4, 0x000, + 0x630, 0x811, 0x4b1, 0xc05, 0x98f, 0xa68, 0x485, 0x706, 0xfff, 0x0d9, 0xddc, + 0x000, 0x83f, 0x54e, 0x290, 0xfe7, 0x64f, 0xf36, 0x000, 0x151, 0xb9b, 0x5cd, + 0x961, 0x690, -1, 0xa7a, 0x328, 0x707, 0xe6d, 0xe1f, -1, 0x6a0, 0xf3e, + 0xb27, 0x315, 0xc8c, 0x6de, 0x996, 0x2f9, 0xc4c, 0x90f, -1, 0xaa7, 0x9e9, + 0xfff, 0x0bb, 0x33b, 0xbc6, 0xe17, 0x000, 0x85d, 0x912, 0x5f7, 0x000, 0xff1, + 0xba1, 0x086, 0xa1e, 0x85a, 0x4cf, 0xd47, 0x5a9, 0x5dc, 0x0bc, -1, 0x544, + 0x522, 0x1ff, 0xfa6, 0xa83, 0xc7d, 0x545, 0xd75, 0xb6f, 0x284, 0xf11, 0xe46, + -1, 0x900, 0x0f3, 0xe31, 0x705, 0x06d, 0xd59, 0x67b, 0xe56, -1, 0xde2, + 0x000, 0xd42, -1, 0x24b, 0x000, 0xf87, 0x842, -1, 0xbb9, 0x065, 0x626, + 0x86a, 0x9f8, -1, 0x7ac, 0xe20, 0xbe9, 0x357, 0xfff, 0xf82, 0x219, 0x9d4, + 0x269, 0x8a6, 0x251, 0x0af, 0xd02, 0x09a, 0x803, 0x0a5, 0xfed, 0x278, -1, + 0x338, 0x1e5, 0xcad, 0xf9e, 0x73e, 0xb39, 0xe48, 0x754, -1, 0x680, 0xd99, + 0x4d4, 0x80b, 0x4be, 0xb0d, 0x5f2, -1, 0x4b1, 0x38a, 0xff5, 0x000, 0xa1b, + 0xece, 0xa06, 0x8e6, 0xdcb, 0xcb8, 0xc63, 0x98c, 0x346, 0x69c, 0x299, 0xa52, + 0xfff, 0x000, -1, 0x7b2, 0xbf8, 0x2d1, 0xaff, 0x2f2, 0xd69, 0xf20, -1, + 0xdcf, 0x9fb, 0x68f, 0x24e, 0xfd7, 0xfdb, 0x894, 0xc8f, 0x615, 0xa25, 0x36d, + 0x1bb, 0x064, 0xb80, 0x280, 0xd7a, -1, 0xd75, 0xc90, 0xdce, 0xdce, 0x011, + 0x869, 0xb2f, 0xd24, 0xe26, 0x492, 0xe0a, 0xcae, -1, 0x2ac, 0x38c, 0x0b9, + 0xc4f, -1, 0x32b, 0x415, 0x49c, 0x11c, 0x816, 0xd08, 0xf5c, 0x356, 0x2b3, + 0xfbf, 0x7ff, 0x35d, 0x276, 0x292, 0x4f5, 0x0e2, 0xc68, 0x4c4, 0x000, 0xb5e, + 0xd0b, 0xca7, 0x624, 0x247, 0xf0d, 0x017, 0x7ec, 0x2a6, 0x62c, 0x192, 0x610, + 0xd98, 0x7a4, 0xfa3, 0x80b, 0x043, 0xd7b, 0x301, 0x69d, 0x7e4, 0x10c, 0xacb, + 0x6eb, 0xea7, 0xe65, 0x75d, 0x4f5, 0x5b0, 0xa50, 0x7b6, 0x0ec, -1, 0xcf9, + 0x4b4, 0x639, 0x111, 0xbdf, 0xe89, 0x9fa, 0x76b, 0xdf6, 0x2d0, 0x857, 0x3a3, + 0x000, 0xa3e, 0x8cb, 0x35f, 0x4f0, 0x022, 0xb38, 0xc12, 0x93c, 0x2fc, 0x546, + 0xe6e, 0x91f, 0x145, 0xfff, 0x1af, 0x957, 0xbde, 0x09d, 0xfd2, 0x9df, 0x2dc, + 0x07f, 0x115, 0x7bf, 0xa35, 0x061, 0x9bf, 0xc85, 0x918, 0x0c8, 0x317, 0xce5, + 0xf28, 0x108, 0x51b, 0x621, 0x188, 0x000, 0x28c, 0xf67, 0x6ef, 0x000, 0xd72, + 0xce2, 0x1be, 0x000, 0x000, 0x282, 0x357, -1, 0x4e5, 0x246, 0x859, 0x66c, + 0x5d3, 0x9fd, 0x000, 0x000, 0x82f, 0xc29, 0x331, 0xa93, 0x000, 0xae4, 0x48a, + 0x254, 0x000, 0x0ba, 0xe83, 0x7c7, 0xb6e, 0x88e, 0x774, 0xf6f, 0x85d, 0x47f, + 0xcd6, 0xe41, 0xdb6, 0x000, 0x0f4, 0xb4d, 0x77f, 0x000, 0x901, 0x1a2, 0x44a, + 0x482, 0x000, 0xe99, 0xa75, 0x000, 0x7ab, 0x000, 0x0b6, 0x35c, 0x306, 0x11c, + 0x08e, 0x6eb, 0x11c, 0x771, 0xff9, 0x1c8, 0x63b, 0x58b, 0x9d2, 0x250, 0x198, + 0xfe7, 0xebc, 0x000, 0xa97, 0xacc, 0xd4b, 0x28b, 0x892, 0x150, 0xcf4, 0xbc1, + 0x000, 0x662, 0xdd8, 0x61f, 0x903, 0x083, 0x000, 0xc55, 0x02f, 0xc29, 0x4f5, + 0xbcf, 0xe27, 0x9e3, 0xb13, 0xadc, 0x845, 0x415, 0x0ae, 0x000, 0xe30, 0x931, + 0x84a, 0xb09, 0x250, 0x631, 0x7aa, 0x026, 0xdc9, 0x486, 0x3a7, 0xab0, 0xe04, + 0xe1a, 0xe17, 0x611, 0x556, 0xfac, 0x3c6, 0x5ab, 0x002, 0xc16, 0xe60, -1, + 0xc51, 0x772, 0x67f, 0xfa9, 0x83c, 0x974, 0x96a, 0xe94, 0x250, 0xa20, 0xc95, + 0x65b, 0x479, 0xe48, 0xa35, 0x23f, 0x5cf, 0x40a, 0xcf0, 0xe82, 0x1da, 0x390, + 0xc86, 0xa92, 0x433, 0xbed, 0x4a7, 0x09a, 0x15a, 0xb8d, 0x9c7, 0x5fb, 0x8a0, + 0x000, 0xf9a, 0xf3c, 0x11c, 0x20c, 0xf23, 0x79d, 0xc79, 0xb71, 0x7af, 0xc5b, + 0x771, 0x629, 0x834, 0xb34, 0x20c, 0x940, 0x2ca, 0x60b, 0x000, 0x4cb, 0x70b, + 0x000, 0x000, 0x9e8, 0x000, 0xdca, 0x000, 0x1ae, 0xb21, 0xfe3, 0x191, 0x9e1, + 0x7f6, 0x04f, 0x64a, 0xba2, 0x59e, 0x1ae, 0x000, 0x728, 0x000, 0x081, 0xecd, + 0x946, 0x000, 0xdee, 0x3ff, 0xdf9, 0x1bf, 0x01a, 0x1a9, 0xc58, 0xe05, 0x3bf, + 0x5e8, 0x39d, 0xbfa, 0x23f, 0xb8d, -1, 0x000, 0x779, 0x540, 0xf2c, 0x7cc, + 0x340, 0x77a, 0xa8e, 0xe8d, 0x2fd, 0xfed, 0x5d1, 0x308, 0x00f, 0xf4a, 0x39b, + 0xbe2, 0x0e5, -1, 0xf4d, 0x1fe, 0xf00, 0x867, 0x195, 0x2de, 0x712, 0x000, + 0x00c, 0x0a3, 0x1f3, 0x4ee, 0x317, 0x665, 0x000, 0x5d8, 0x291, 0x6c4, 0xa46, + 0x492, 0x8d4, 0x647, 0x57f, 0x000, 0x259, 0xd87, 0x5c2, 0x1d8, 0xfad, -1, + -1, 0x79f, 0x43a, 0xfd1, 0x164, 0x6e1, 0x350, 0xf00, 0x0e9, 0xac4, 0xe35, + 0x307, 0xfff, 0xabb, 0xc1a, 0x768, 0x000, 0x372, 0x839, 0xf4b, 0x1c3, 0xab0, + 0xcb6, 0x943, 0xbe9, 0x20f, 0xddc, 0xe18, 0x4eb, 0x21d, 0x530, 0x24c, 0x000, + 0xf79, -1, 0x1bd, -1, 0x155, 0x435, -1, 0x132, 0x5c2, 0xb3d, 0x802, + 0x733, -1, 0x336, 0xf19, 0xfea, 0xd2a, 0x07f, 0x8e9, 0x000, 0xdab, -1, + 0x088, 0x4b1, 0x7ac, 0x000, 0xe66, 0xde0, 0x73c, 0xfff, 0x02f, -1, 0x000, + -1, 0x000, 0x562, 0x389, 0xb20, 0x9ea, -1, 0x3f8, 0x567, 0x035, 0xa55, + 0x255, 0xc98, 0x65f, -1, 0x1ac, 0x571, 0x13d, 0xf57, 0x32a, 0xbdb, 0x0ec, + 0x47d, 0x43a, -1, 0x1aa, 0x9d6, 0x843, -1, 0x244, 0xb03, 0xd0d, 0x579, + 0x1b1, 0xea7, 0x000, 0x062, -1, 0x533, 0x1db, 0xf1f, 0x2f7, 0x2df, 0x3e5, + 0xdec, 0xc5c, 0x55a, 0xf6c, 0x4c1, 0x5a8, 0xcd4, 0x6fd, 0x1a6, 0x4b8, 0x98a, + 0xe17, 0xeb9, 0xfd1, -1, 0x175, 0x4d6, 0xba2, 0x000, 0x614, 0x147, 0x429, + 0xfee, -1, 0x0d8, -1, 0x98a, 0xdd2, 0xedd, 0x255, 0xef3, 0x345, 0x000, + 0xf3e, -1, -1, 0x210, 0x88a, 0x699, -1, 0x02c, 0xfee, 0x1c1, 0xb38, + 0x000, 0x7cc, 0x165, 0x536, -1, 0x1ae, 0xefb, 0x734, -1, 0x1a4, 0x984, + 0x804, 0x487, -1, -1, 0x31e, 0x9f2, 0x966, 0x000, 0xcb0, 0x552, 0x0c9, + -1, 0x750, 0x650, 0x064, 0xffe, 0xe84, 0x537, 0xee7, 0x834, -1, 0x998, + 0xa03, -1, 0xcdf, 0x4be, 0x310, 0x051, 0xf3f, 0x040, 0x973, 0x925, 0x000, + 0x000, 0xe51, 0x8b1, 0x468, 0xe11, 0xd4f, 0x374, 0x33a, 0x126, 0x88b, 0x43a, + 0xc9b, 0xdb9, 0x3c2, 0x3bd, 0x1ae, 0x000, 0xc4a, 0x000, 0x4c4, 0x859, 0xe5a, + 0x000, 0xeb4, 0xd40, 0x87d, 0xc79, 0xe13, 0x50b, -1, 0x724, 0x000, 0x7be, + 0x062, 0xe7f, 0xad0, 0x5f3, 0x69e, 0x381, 0x272, 0x50f, 0xac8, 0x053, 0x55e, + 0xf19, 0xd71, 0x75b, 0xbf2, 0x000, 0x3ac, 0xdf0, 0xd75, 0x7e3, 0xe75, 0xa13, + 0xfd8, 0xbdc, 0x1d9, 0x15f, 0x8cc, 0xba4, 0xb79, 0xb7f, 0x812, 0xfe6, 0x000, + 0x2d3, 0xd7b, 0x5d4, 0xad2, 0x316, 0x908, 0x323, 0x758, 0xb0b, 0x965, 0x1a9, + 0xdce, 0x660, 0x625, 0xeff, 0x0ed, 0x000, 0x323, 0x986, 0x831, 0x5c5, 0x22f, + 0xd49, 0xec6, 0x90e, 0x234, 0x000, 0x80f, 0x16c, 0x528, 0x1f8, 0x2bd, 0x97d, + 0xe20, 0xf29, 0x97d, 0x3a0, 0x7fc, 0x086, 0x720, 0x1f9, 0x3eb, 0xf67, 0x423, + 0xa55, 0x69e, 0xede, 0x206, 0x7fa, 0x809, 0xfa8, 0xe22, 0x15e, 0x2a0, 0x04a, + 0xf7b, 0x4ea, 0xd9a, -1, 0x1d8, 0x0b4, 0xb87, 0x406, -1, 0xcdf, 0x187, + 0xf6d, 0x914, 0x4b1, 0x000, 0x104, 0x67e, 0xc74, 0x6da, 0xe67, 0x7d2, 0xd1f, + 0x64c, 0x19d, 0x000, 0xa17, 0xfd5, 0x000, 0x8ad, 0xf38, 0xd65, 0xabd, 0x75e, + 0x667, 0x632, 0x346, 0xc48, 0xa77, 0x45e, 0x2b5, 0xded, 0x7da, 0x160, 0x560, + -1, 0xf4e, 0xb0c, 0xdb0, 0x287, 0x34a, 0x065, 0x439, 0x2ec, 0x679, 0xefa, + 0x208, 0xeb1, 0x1b0, 0x8c8, 0xca6, 0x62c, 0xa10, 0x673, 0x000, 0x000, 0xc6a, + 0x7b2, 0xbd7, 0xb2b, 0x17a, 0x6f3, 0x1ab, 0xffa, 0x5e0, 0x1fa, 0xb8f, 0xe5c, + 0xcab, 0xdbc, 0x10f, 0x000, 0x000, 0xefe, 0x34b, 0x1d9, 0x834, 0x52f, 0xb58, + 0x82b, 0x6e8, 0x1f3, 0x719, 0x64e, 0xf55, 0xccd, 0x531, 0x0de, 0x3aa, 0x150, + 0x89a, 0x3b9, 0x26e, 0xebc, 0x7ae, 0x670, 0x315, 0x8a9, 0x03b, 0x896, 0x247, + 0x2f4, 0x450, 0xd10, 0xb79, 0x0ed, 0x041, -1, 0x707, 0x9e1, 0xed6, 0x6d2, + 0x000, 0xfff, 0xb1a, 0x084, 0xaf3, 0x47f, 0x02f, 0xac3, 0x751, 0x8c4, 0x291, + 0xadd, 0x000, 0xea1, 0x8ec, 0xf9f, 0x5c2, 0x000, 0xd6b, 0x71e, 0x000, 0xcea, + 0x971, 0x5f8, 0x4b9, 0x7c6, 0xb7e, 0x353, 0xd25, 0x423, 0x6ec, 0xb71, 0xf93, + 0x000, 0x795, 0xc43, 0xaa2, 0x96a, 0xcbd, 0xb55, 0x184, 0xdf0, 0x3d9, 0xbfe, + 0xf79, 0x8f0, 0x22c, 0xeeb, 0x000, 0xa4b, 0xe07, 0xf34, 0xc9d, 0x4be, 0x95b, + 0x371, 0x78c, 0x9e9, 0xde6, 0x072, 0xf0d, 0x60b, 0x5a5, 0xab1, 0x000, 0x260, + 0x000, 0xd2a, 0xd90, 0x154, 0x4c6, 0x438, 0x5d9, 0x736, 0x062, 0x000, 0x000, + 0xb84, 0x72e, 0x0b7, 0x000, 0x050, 0x063, 0xa95, 0x89b, 0x917, 0x049, 0xb14, + 0x9a0, 0x734, 0x0c3, 0xd50, 0x917, 0xb02, 0x8cf, 0x453, 0x0af, 0x8e5, 0x000, + 0x7aa, 0x5d5, 0x81b, 0x788, 0xb9c, 0x01a, 0x974, 0x000, 0x000, 0x37f, 0xd9f, + 0x000, 0xec4, 0x4f4, 0xbff, 0x4fe, 0x860, 0x11c, 0x74e, 0x34a, 0x281, 0x52f, + 0xb05, 0xa89, 0xbee, 0x6ad, 0x9fc, 0x9ba, 0xb0b, 0x515, 0x1c7, 0x330, 0xfde, + 0x97e, 0x6e7, 0xc45, -1, 0x658, 0x710, 0x28a, 0x921, 0x1de, 0x4a1, 0x9d7, + 0xe32, 0xa2d, 0xb0f, 0x545, 0xd6f, 0x329, 0x9b8, 0xb4d, 0x9a0, 0x938, 0x783, + 0xfa7, 0xd0a, 0xdc9, 0x0fe, 0x000, 0x249, 0x000, 0x8cd, 0x922, 0x7cd, 0x021, + 0xa89, 0x3d5, 0xcee, 0x0a1, 0x6d6, 0x000, -1, 0x48b, 0x000, 0x87a, 0x8bb, + 0x9ed, 0x01f, 0xe20, 0xb7f, -1, 0xe95, 0x593, 0x1da, 0x57a, -1, 0xf3a, + 0x000, 0x000, -1, -1, 0x160, 0x501, 0x7a3, 0xb59, -1, -1, 0xc7f, + -1, 0xf79, -1, -1, 0x48d, 0x781, -1, -1, 0xb74, -1, 0x3c4, + 0xbe9, -1, -1, 0x9a4, 0x9ae, 0xa75, -1, -1, 0x9cd, 0x000, -1, + -1, -1, 0xc3c, 0x2d4, -1, 0x173, 0xf38, 0x000, -1, 0xee9, -1, + 0xb91, 0xcc1, 0x86d, 0x8ab, 0xeb0, 0xec7, 0x687, 0xd98, 0xa95, 0x744, 0xe7c, + 0x826, 0x80e, 0x599, 0x3d9, 0xf2f, -1, 0x96a, 0xfd1, 0x174, -1, 0x000, + 0x1aa, 0x50e, -1, 0x5a2, 0xbcd, 0x000, -1, 0x019, 0x588, 0x18d, 0x470, + 0x812, 0xeec, 0xf63, 0x05c, -1, 0x000, 0xb7f, 0x357, 0x436, 0xbb4, 0x1fb, + 0x425, 0x1ed, 0xe13, 0x66c, 0x555, 0xb11, 0x7b5, 0x48d, 0x38d, 0xf72, 0x000, + 0x000, 0xa66, 0x4fa, 0xf36, 0x1eb, 0x000, 0x95f, 0x000, 0xd9a, 0x82f, 0x07f, + 0x253, 0x70f, 0x915, -1, 0x12d, 0x040, 0x2ca, 0x446, 0x90a, 0x7a8, 0x687, + 0x000, 0x04e, 0x74f, 0x1ca, 0x793, 0x3c7, 0x3f0, 0x4c7, 0x000, 0xc30, 0x533, + 0x889, 0x9ef, 0xebd, 0x984, 0x18f, 0xfe1, 0x8ea, 0x185, 0x410, 0x107, 0x000, + 0x73e, 0xd4b, 0x8fc, 0xd34, 0x1e6, 0x4bf, 0xbac, 0x7c3, 0x000, 0x7c8, 0xb2f, + 0x02c, 0xa46, 0x000, 0x0f9, 0x680, 0x94d, 0x6ad, 0x767, 0xfeb, 0x6c7, 0x2d5, + 0x43f, 0x9af, 0x261, 0xe83, 0xfa7, 0xb7b, 0xf2d, 0x2f5, 0x4d7, 0x494, 0xbc2, + 0x45b, 0x000, 0x17d, 0x5c6, 0xe2b, 0xb20, 0x19e, 0x6ba, 0x973, 0xedd, 0xea8, + 0x000, 0x9f3, 0xd9a, 0x7fa, 0xb78, 0x556, 0xbb6, 0xc58, 0x210, 0x000, 0xf9a, + 0x56d, 0x48b, 0xf12, 0x000, 0x54d, 0x5f4, 0x1ad, 0x86e, 0xe16, 0x6ff, 0xa35, + 0x47e, 0x4c7, 0x93c, -1, -1, 0xc98, 0xd3f, 0x000, 0x788, 0x6ef, 0x959, + 0xec2, 0x45e, 0xa4d, 0xa90, 0x000, 0x768, 0x8bb, 0x6ee, 0x7f5, 0x770, 0xfa8, + 0xba4, 0xf49, 0x7b8, 0x616, 0x2bd, 0x23f, 0xe8c, 0x9fa, 0xa49, 0x213, 0x98a, + 0x2c1, 0x595, 0x885, 0x6de, 0x057, 0x1bc, 0x000, 0xc58, 0x7a8, 0x5c1, 0x3d0, + 0xa78, 0xb80, 0x000, 0xc06, -1, 0x428, 0xe92, 0xfa3, 0x341, -1, 0x000, + 0x000, 0x1ca, 0x27c, 0xdeb, 0x835, 0x4c8, 0xdb3, 0x000, 0xf9d, 0x000, 0xe81, + 0xc22, 0xfce, -1, 0xe6e, 0x96e, 0x161, -1, 0x3b9, 0x945, 0xa95, 0x13d, + 0x748, 0x184, 0x588, 0x636, 0xf7e, 0xb44, 0x2b7, 0x217, 0xee5, 0x65a, 0xc47, + -1, 0xca3, 0x83e, 0x431, 0xc64, 0x636, 0x06e, 0x404, 0x993, -1, 0xeb3, + 0x134, 0x8a3, 0xca9, -1, -1, 0x2ab, 0x000, 0x8ed, 0x877, 0x1a8, 0xc89, + 0x000, 0x000, 0xf94, 0x000, 0x709, 0x249, 0x9ac, 0x22a, 0x605, 0x000, 0x000, + 0x6b4, 0x00c, 0xc53, 0xf23, 0x005, 0x29f, 0x865, 0xf79, 0x000, 0x5fa, 0x764, + 0xe51, 0xbdc, 0xb64, 0x0f3, 0xf29, 0x2f7, 0x5da, 0x000, 0x16f, 0xb8b, 0x255, + 0x9cc, 0xe43, 0x279, 0x2c2, 0x483, -1, 0xf7d, 0x7bb, 0x000, 0x9e3, 0xd84, + 0xe36, 0x6e6, 0x000, -1, 0x33f, 0x41d, 0x5b5, 0x83e, 0x2f4, 0xf5b, 0x9fc, + 0xb1e, -1, 0x8f4, 0xb26, 0x856, 0x3b6, 0x126, 0x4c2, 0x274, 0x0c1, 0xfa9, + 0x57d, 0x000, 0x100, 0x7af, 0xc62, 0x000, 0xa55, 0x416, 0x93f, 0x78c, 0xfba, + 0x5a2, 0x0c2, 0x4d4, 0xa3e, 0xcc3, 0xe73, 0xd02, 0x8df, 0x3e9, 0xe9a, 0x0f6, + 0x32c, 0x23d, 0xdab, 0xf50, 0xfc2, 0x000, 0x065, 0xc23, 0xd3d, 0xc84, 0x35e, + 0x000, 0xa24, 0x634, 0x4b4, 0xa52, 0x098, 0xb39, 0x9a4, 0xe71, 0x8aa, 0x741, + 0x000, 0xb16, 0x5c2, 0xea1, 0xc01, 0x5c1, 0x30d, 0xca4, 0x201, 0xc9c, 0x717, + 0x000, 0xba0, 0x537, 0x619, 0x000, 0xfd9, 0x6dc, 0xdaa, 0x1da, 0xe51, 0xd39, + 0xb4c, 0x8a1, 0x098, 0x2f8, 0x191, 0x9dc, 0xdb0, 0x5e1, 0x000, 0xe97, 0xef1, + 0x8d3, 0xb0d, 0xfce, 0x336, 0xee1, 0x7a2, 0xbc8, 0x494, 0x580, 0xba7, 0x000, + 0x62a, 0x96a, 0x527, 0x859, 0x811, 0xef0, 0x429, 0xef4, 0xf3d, 0x000, 0x9d6, + 0xb71, 0x000, 0x14b, 0xf3d, 0xb16, 0x204, 0x0c1, 0xcd4, 0x339, 0x39d, 0xfe3, + 0x837, 0x8c7, 0x955, 0x69a, 0x5f6, 0x4c6, -1, 0x3d5, 0x000, 0x0e7, 0x4b1, + -1, 0xa3e, 0xb03, 0x1ea, 0xac8, -1, 0x000, 0xed8, -1, 0x4e0, 0x9f7, + 0xc91, 0x6b3, -1, -1, 0xa53, 0x290, 0xa64, 0x0e3, 0x3dc, 0xed3, 0xf2f, + 0x000, 0xd7c, 0xf44, -1, 0x205, 0x900, 0x864, -1, -1, 0xed3, 0x7d2, + 0x000, -1, 0xdd2, 0x79b, 0x000, -1, 0xae6, 0x5cf, 0xde8, 0x000, 0x1f2, + -1, 0x2f3, 0x000, -1, 0x2ce, 0xcf2, 0x8f4, 0xee8, 0x165, 0x309, 0x15f, + -1, 0x714, 0xbfc, 0x532, 0xad0, 0x151, 0x2d5, 0x0a4, 0x391, -1, 0x0dc, + 0x0c1, 0x451, -1, -1, 0x6a0, 0x250, -1, 0xab8, 0x977, 0xa86, 0x407, + 0x72f, -1, 0x05f, 0x000, 0xefe, 0x950, 0x4f4, 0x957, -1, 0xd68, 0x26c, + 0xa30, 0x4f1, 0x279, 0x584, 0xb34, -1, 0x4d7, 0x258, 0x000, 0x518, 0x685, + 0x91c, 0x3ac, 0x0fa, -1, 0x979, 0x40c, 0x506, 0x000, -1, 0x7bd, 0xb97, + 0x87f, 0xc06, 0x050, 0x7bf, 0xe3e, 0xc81, 0x000, 0x65e, 0x000, -1, 0xb76, + 0xc37, 0x4c4, 0xfc9, 0x336, 0x9fa, 0xaa2, 0x32c, 0xb8b, 0xaa9, 0xc95, 0x85a, + 0xa9a, 0x260, 0x4cd, 0x8fe, 0xd3c, 0x982, 0x0d7, 0xbc1, 0xdcf, 0xe62, 0xe0d, + 0xf8f, 0xd7b, 0x91a, 0x3e0, 0x33a, 0x1c5, 0xf00, 0xde5, 0xad1, 0xebc, 0xebc, + 0x942, 0xd86, 0x3bf, 0x8ce, 0xb8c, 0x000, 0x8d6, 0x784, 0xb74, -1, 0x818, + 0x000, 0xfff, 0x07e, 0x029, 0xf48, 0xb65, 0xd81, 0x220, 0x095, 0x21f, 0xac4, + 0xb31, -1, 0x864, 0x000, 0x3bd, 0xf85, 0x237, 0x369, 0x2d9, 0xfdf, 0x25a, + 0x782, 0x7b8, 0xabd, 0x5e3, 0x438, 0x230, 0xbc4, 0x7ad, 0x00a, 0x441, 0x6dc, + 0x2c4, 0xf16, 0x0b3, 0x04c, 0xfd2, 0x8aa, 0xad8, 0x3e4, 0x142, 0x585, 0xc8f, + 0x9bf, 0x29b, 0xac9, 0x743, 0xfb5, 0x7fc, 0x05e, 0xd38, 0x002, -1, 0xb4e, + 0xd0c, 0x84c, 0xf93, 0x91f, 0xcd2, 0x04f, 0x569, 0xd1b, 0xfc6, 0x630, 0x6f6, + 0x1d8, 0x91a, 0x4da, 0x9f5, 0x07a, 0xcf5, 0x634, 0x42f, 0xfff, 0x951, 0x0f9, + 0xc01, 0x491, 0xbd6, 0x730, 0xfea, 0x9f4, 0xbfc, 0xf1a, 0x413, 0xa2a, 0xdc6, + 0xc87, 0x9db, 0xc2c, 0x30f, 0xdb5, 0x785, 0xbaa, 0x000, 0x000, 0xa49, 0x000, + 0x61d, 0xf6f, -1, 0x031, -1, 0x441, 0x7bf, 0x53e, -1, 0x6fd, 0x0f6, + -1, 0xadb, -1, 0x000, 0x432, 0x187, 0xd37, 0x154, 0x539, 0xc08, 0xe51, + 0x219, 0x1e9, 0x897, 0xa0e, 0x201, 0x447, 0x89f, 0x000, 0x463, 0x726, 0xa05, + 0xab9, 0xd01, 0x1e4, 0xfea, 0x895, 0x816, 0x313, 0xae3, 0x3a4, -1, 0x70f, + -1, 0xa42, 0x5e9, 0x78e, -1, 0x317, 0x6c8, 0x000, 0xbf7, 0xefd, -1, + 0xb17, 0x382, 0xd26, 0x5ff, 0xf81, 0x20b, 0x373, 0x774, 0x081, 0xaae, 0xfdb, + 0xe5d, -1, -1, 0xcb7, 0x738, 0x919, 0x933, 0x398, 0x000, 0x14e, -1, + 0xe14, 0xbf8, 0x11c, 0x94b, 0x031, -1, 0x000, 0x2d4, 0xd41, 0xdc6, 0x9f6, + 0xea7, 0x9e8, 0x2ec, 0x10a, 0x50d, 0xeae, 0xdb0, 0xef0, 0x9c8, 0x000, -1, + 0x82e, 0x9d3, 0xdb7, 0x46d, -1, 0x230, 0x73b, 0x45b, -1, -1, 0x000, + 0x4a7, -1, -1, 0x47c, 0x10e, 0x4b4, -1, -1, -1, 0x1d7, 0xa5d, + 0x233, 0x6b2, 0x6bd, 0x387, 0x7ca, 0xb1a, 0xf75, 0xea4, 0xdc9, 0x98b, 0x80c, + 0x702, 0xe22, 0xa6e, 0x6f8, 0x05b, 0x17c, -1, 0x000, 0xebe, 0xc8e, 0xaec, + -1, 0x42b, 0xdce, -1, -1, -1, 0xef3, 0xc52, 0x31b, -1, 0xdff, + 0xbd0, 0x000, 0xa72, 0x525, 0x9cf, 0x2ff, 0xfc8, 0x37c, 0xbce, 0xd8c, 0xd88, + 0x3a6, 0xed8, 0x4ab, 0x000, 0x449, 0x9d7, -1, -1, 0x9be, 0x59f, 0x000, + 0x882, -1, 0x742, 0x000, -1, -1, -1, 0xe8b, 0x0f3, 0x771, -1, + 0x3ea, 0x8f9, 0xcbb, 0x548, 0x46d, 0x000, -1, 0xf74, 0xa23, 0x15b, -1, + 0xaeb, 0x7f8, 0xbe2, 0x000, -1, 0x023, 0x61e, 0x95d, 0x7ac, 0x024, 0x141, + 0x561, 0x9fe, 0xb10, -1, 0x623, 0xc47, 0x413, 0x0e7, 0x663, 0xcdf, 0xebe, + 0x5c9, 0x573, 0x21d, 0xb28, 0x280, 0xb9f, 0xd1a, 0xecf, 0xff0, 0x000, 0xfc0, + 0x085, 0x9c4, 0x48c, 0x000, 0xb0b, 0x43d, -1, 0x73b, 0x802, 0x633, 0x6ef, + -1, -1, 0x5c1, 0xea6, 0x0a9, 0xab4, 0xacd, 0xb81, 0xa32, -1, -1, + 0xa26, 0x9d5, 0xf7c, -1, 0xf69, 0xdbb, 0x6d5, 0x405, -1, 0xd0a, 0xfe0, + 0xf5e, 0xbd7, -1, 0x89a, 0x868, 0xeb2, 0x792, 0x7fe, 0x115, 0x000, 0x8bb, + 0xdd1, 0xc40, 0x453, 0xbb3, 0x7cc, 0x3e6, 0x071, 0x0f1, 0xbae, 0xf67, 0x896, + 0x38e, 0x86e, 0xfaa, 0xccc, -1, 0x411, 0x8e5, 0x699, 0x2ef, 0x785, 0x9d4, + 0xe30, 0xb2e, 0x976, 0x203, 0x035, 0x75d, 0x8f1, 0x144, 0x092, 0x1a5, -1, + 0x55f, 0x000, 0xa43, 0x5be, 0x68d, 0x852, 0xb87, 0x9af, 0x0c0, -1, 0xa50, + 0x9ca, 0x15f, 0xf06, 0x869, 0x0f3, -1, 0x000, -1, 0x9a9, -1, -1, + -1, -1, 0xf05, 0x000, -1, 0x000, 0x4a9, 0xf9d, -1, 0x000, 0xab1, + 0x04c, -1, 0xd17, 0x893, 0x763, 0x332, -1, 0xc41, 0x5bd, 0xa72, 0x67c, + 0xb78, 0x973, 0x6c7, 0x569, -1, 0x96a, 0xc68, 0x48c, -1, 0x6fa, -1, + 0xa2a, 0x44f, -1, 0x73f, 0x28f, 0x536, 0xd91, 0xc86, 0xef8, 0x1f5, 0xfb4, + 0x060, 0x230, 0xe10, -1, 0x000, 0x305, 0x0e6, 0xb19, 0x1e2, 0x7fc, 0xf35, + -1, 0x7d9, 0x000, 0x000, 0xd2f, 0xb3a, 0x0a2, 0x7c9, 0xda6, 0x37c, 0xe43, + -1, 0x7da, 0x0d6, 0x000, -1, 0xd40, -1, 0x156, 0xee9, -1, 0x239, + 0x10f, 0x60c, 0x9d4, 0x663, 0x565, 0x603, 0x38b, -1, 0x606, 0x13c, 0x681, + 0x436, 0xc29, 0x9c7, 0x1d9, 0x000, 0x0a6, 0x996, 0x231, 0x055, 0x01f, 0x0a3, + 0xd96, 0x7c8, 0x0f3, 0xaa7, 0xd99, -1, 0x3be, 0x476, 0x25f, 0x38c, 0xdf3, + 0x6d5, 0xcb5, 0xadd, 0x000, 0x136, 0x64d, 0xc0d, 0xe61, 0xd0b, -1, 0x000, + 0x535, 0x9c3, 0x279, 0x00c, 0xa87, 0xa31, 0xc4a, 0x167, 0x423, 0xec8, -1, + 0x926, 0xa4d, 0x5ba, -1, -1, 0x9bf, 0x000, 0x47f, 0x8f3, 0xd5b, 0xc3b, + 0xa18, -1, 0x548, 0x8f7, 0x8cf, 0x000, 0x9bd, 0xaa2, 0x7ec, 0x000, 0xfb8, + 0xafd, 0xe68, -1, 0xfa7, 0x31c, 0xef3, 0x288, 0xdf0, 0x1bc, 0xfe9, 0x1ea, + 0xa9f, 0x000, 0x53f, 0x000, 0xda6, 0x09c, 0x1bf, 0x09c, 0x31c, 0x0c8, 0x31c, + -1, 0x689, 0x211, -1, 0x77f, 0x723, 0xb8f, 0x683, 0x351, -1, 0xb33, + 0xce0, -1, 0x61c, 0x073, 0x783, 0x6b2, 0x6a8, 0x729, 0x81b, 0xabf, 0xd15, + 0x563, 0x433, -1, 0x823, 0xf99, 0x2c5, -1, 0x367, 0xcc4, 0x934, 0x6f2, + 0xdf0, 0xa1f, 0x579, 0x012, -1, 0x508, 0x070, -1, 0x018, 0x270, 0xa6f, + -1, 0x7a7, -1, 0x826, 0x4b5, -1, 0x7d8, 0xb20, -1, 0xc28, 0x463, + -1, 0xdf9, 0x22c, 0xe17, 0x4f2, 0xe13, 0x4ff, 0x40a, 0xdcb, 0x9ed, 0x34a, + 0xeb8, 0xa0e, 0x5f2, 0x594, 0x60d, 0x4b6, 0xd3c, 0x675, 0x1c4, 0xbb5, 0xc73, + 0xfad, 0xead, -1, 0xfb6, -1, 0x146, 0xd40, 0x02f, 0x000, 0x302, -1, + -1, 0x6e5, 0x000, 0xed7, 0xd8c, 0x7a3, 0x0fc, 0x259, 0x34b, 0xa1b, 0x882, + -1, 0x211, 0x000, 0xd30, 0xe02, 0x5cd, 0x53e, 0x11b, 0xa16, -1, 0x24e, + -1, 0xace, 0xe9a, -1, 0x5c6, 0x9be, 0x000, 0x169, 0x982, -1, 0x3fd, + 0x457, 0x06f, 0x7e7, 0xed1, 0x5ee, 0xcef, 0x62b, 0x26c, 0xc9f, 0xe68, 0x59f, + 0x0b5, 0x000, 0x0bc, 0x086, 0x890, 0x005, 0xc42, 0x939, 0xaca, 0xdd9, -1, + -1, 0x6e5, 0x0dd, 0x434, 0x297, 0xe21, 0x0f5, -1, 0xa6c, 0x4ad, 0x978, + 0x433, 0xa41, 0xd6f, 0x8bf, 0xfb8, -1, 0x928, 0x85e, 0xfb6, 0x5c7, 0x99a, + 0x8ec, 0xebc, 0x226, 0x7d4, 0xdcd, 0xc0b, 0x000, 0x7f4, 0xc6f, 0x000, 0x3ad, + 0x5b2, -1, 0x67b, -1, 0x568, 0x6e2, -1, -1, -1, 0x3f3, 0xaf5, + 0x33f, -1, 0x022, 0x1bd, 0xae5, -1, 0x9c3, 0x000, 0x92b, 0xee5, 0x29c, + 0x000, 0x15e, 0xe71, 0xacb, 0x9d2, 0x1a3, 0xb7f, 0xa5b, 0x095, -1, 0xb6e, + 0x79f, 0x3d1, 0x7d0, 0x131, 0xcd7, -1, 0x2c3, -1, 0x396, 0x4d2, 0x297, + 0x405, 0x634, -1, -1, -1, 0x928, 0xbca, 0xb6c, 0x011, 0xfc0, 0xbaf, + 0xbd2, -1, 0x585, 0x000, 0xb8a, 0x7f9, 0xd6b, 0x4eb, 0x9a3, 0x000, -1, + 0xaeb, 0xa47, 0xcef, 0x9c6, -1, 0x000, -1, 0x2a9, 0x371, 0xca6, -1, + 0xb7d, 0x15f, 0x2a9, -1, 0xe80, 0x7a7, 0x23a, 0x000, 0x000, 0xcc9, 0x60e, + -1, 0x130, 0x9cd, 0x498, 0xe25, 0x366, 0x34b, 0x899, 0xe49, 0x1a8, 0xc93, + 0x94d, 0x05e, -1, 0x0c2, 0x757, 0xb9d, 0xaa3, 0x086, 0x395, 0x3c3, 0xa2e, + 0xf77, 0xcb1, 0x45e, 0x169, 0xbba, 0x367, 0x8cb, 0x260, 0x5a0, 0x8cb, 0x737, + 0xa1f, 0xaaf, 0xf92, 0x430, 0x97d, 0x542, 0xb09, -1, 0x774, 0x084, 0x4c0, + 0x2b3, 0xaf6, 0x93c, 0x32d, 0xee2, -1, 0x605, 0xece, 0x8eb, 0xc62, 0x01d, + 0x000, 0xaba, 0xfc5, 0xb8e, 0xc07, 0xfb6, 0xbca, 0x8f0, 0xd33, -1, 0x283, + 0x6d6, 0x6ad, 0x678, 0x51a, 0xc95, 0xda4, 0x962, 0x9ed, 0x307, 0x94a, 0x052, + 0xf4e, 0x3bd, 0x210, 0x71a, 0x3c7, 0x5a4, 0x6e7, 0x23a, 0x523, 0x1dc, 0x6b2, + 0x5e0, 0xbb0, 0xae4, 0xdb1, 0xd40, 0xc0d, 0x59e, 0x21b, 0x4e6, 0x8be, 0x3b1, + 0xc71, 0x5e4, 0x4aa, 0xaf3, 0xa27, 0x43c, 0x9ea, 0x2ee, 0x6b2, 0xd51, 0x59d, + 0xd3a, 0xd43, 0x59d, 0x405, 0x2d4, 0x05b, 0x1b9, 0x68b, 0xbfa, 0xbb9, 0x77a, + 0xf91, 0xfcb, -1, 0x949, 0x177, 0x68d, 0xcc3, 0xcf2, 0x000, 0xa87, 0x2e6, + 0xd2f, 0x111, 0x168, 0x94c, 0x54c, 0x000, 0x0c5, 0x829, 0xcbc, 0xc0b, 0x1ed, + 0x836, 0x9d8, 0xbdc, 0xc5e, 0x4e5, 0xb94, 0x6f2, 0x74f, 0x878, 0x3b2, 0x48d, + 0xc72, 0xcff, 0xccb, 0x8f9, 0x7ee, 0x869, 0x228, 0x035, 0x81e, 0xcf9, 0x309, + 0xdf2, -1, 0x047, 0xdd3, 0xcab, 0x11d, 0xe76, 0xb52, 0xbbd, 0x12d, 0xf37, + 0x552, 0x61d, 0xdd8, 0x2cd, 0x298, 0x9e2, 0xf2c, 0x9f7, 0xf41, 0xcb4, 0x277, + 0xfde, 0xe7e, 0x82a, 0x86b, 0x9cc, 0x580, 0xfcc, 0x33a, 0x438, 0xd6e, 0x000, + 0xc04, 0xd50, 0x681, 0x1b3, 0x322, 0x86c, 0x4a6, 0x000, 0xa17, 0xd53, 0xdc0, + 0xb61, 0x323, 0x3d1, 0x3fb, 0x929, 0xa6e, 0x919, 0xae0, 0x283, 0xe6a, 0xed3, + 0x371, 0xd51, 0x309, 0x510, 0xd50, 0x6f4, 0xc84, 0x566, 0xba7, 0x75b, 0xbeb, + 0x793, 0x58f, 0x974, 0xe77, 0x52c, 0xcef, 0x942, 0xa7c, 0x56a, 0xaa0, 0x784, + 0x0ec, 0xad3, 0xccf, 0xecf, 0xc3f, 0x846, 0x1b2, 0x112, 0x4ee, 0x18b, 0xa76, + 0xe14, -1, 0xfb1, 0xb4c, -1, 0xd54, 0x0e0, 0x72d, 0xdf0, 0xf0c, 0x881, + 0xc60, 0xe1b, 0x000, 0x453, 0xe21, 0xb2a, 0x6df, 0x84f, 0x000, 0x12a, 0x991, + 0x587, 0xa4e, 0x522, 0x000, 0xe17, 0xc64, 0x994, 0x4cc, 0x0e5, 0xc2b, 0x8cf, + 0x458, 0x992, 0xec0, 0xc80, 0xefb, 0x7b7, 0x863, 0xc0a, 0x250, 0x338, 0xa44, + 0x8ab, 0x873, 0x134, 0x23c, 0x4f6, 0x066, 0xd0f, 0xdd6, 0x93d, 0xf20, 0x000, + 0x9bb, 0x255, 0xe7b, 0x916, 0x4f3, 0x68e, 0xb82, 0x2b4, 0x9d7, 0xfd1, 0x0fb, + 0x11e, 0x204, 0x241, 0x67f, 0x1c4, 0xe91, 0xf41, 0xb4a, -1, 0x6d2, 0xce6, + 0xdfb, -1, 0xdd0, 0xb8d, 0x8db, 0x86c, 0x224, 0xdeb, 0x7bb, 0x50e, 0x000, + 0xb79, 0x11e, 0x486, 0xd4c, 0x000, 0xa54, 0x946, 0x83a, 0x537, 0x875, 0x000, + 0x8e4, 0x95a, 0xdd5, 0x9d5, 0x910, 0x95b, 0x8c8, 0xd22, 0x07c, 0xac0, 0x000, + 0x048, 0x170, 0x9b2, 0xcea, 0xb0f, 0x958, 0x0f9, 0xa9e, 0x87a, 0x166, 0x69c, + 0x112, 0xde0, 0x487, 0xeca, 0x639, 0x4ee, 0x7fa, 0x2cc, 0x709, 0x87a, 0x5bb, + 0xf64, 0x173, 0xdc6, 0xaaf, 0xbff, 0xf2a, 0x8fb, 0xd44, 0x0ca, 0xf34, 0xb3a, + 0xeb3, 0xfc5, 0x61d, 0x92f, 0x6fb, 0x1a1, 0xd85, 0x8fe, 0xb6a, 0x0a1, 0x44f, + 0x89a, 0xc5d, 0x13b, 0x5cc, 0x000, 0x9ac, 0x9e6, 0xf95, 0x511, 0xf2e, 0xf3c, + 0x707, 0xec5, 0xaec, 0xc72, 0xeb1, 0x105, 0xda3, 0xbcb, 0x1d6, 0xf16, 0xd50, + 0x306, 0x03f, 0xe6a, 0x25c, 0x9fe, 0xd3f, 0x8a4, 0x7bc, 0x0bc, 0x532, 0x62e, + 0x111, 0x797, 0xc2c, 0x9d0, 0x338, 0xbc7, 0xd64, 0xb09, 0x4d6, 0xcba, 0x62c, + 0xef2, 0x32b, 0x9c5, 0xc06, 0x38b, 0xbb2, 0x8b7, 0x6f2, 0x7ec, 0xa77, -1, + 0x7a3, 0xc95, 0xa91, 0x5d3, 0xffc, -1, 0xe27, 0xa0a, 0x071, 0x9da, 0x000, + 0xba3, 0x3fd, 0x120, 0x845, 0x151, 0xb5f, 0x193, 0xe99, 0x0f6, 0x640, 0xef4, + 0xe17, 0x46b, 0x432, 0x8a4, 0x415, 0x252, 0x79c, 0xbe5, 0x3f0, 0xb64, 0x984, + 0x5a7, 0x1be, 0xedc, -1, 0xd7e, 0x5b4, -1, 0xd27, 0x03e, 0x136, 0xb30, + 0xfff, 0x1dc, 0xc32, 0x84a, 0x392, 0xf4f, 0x911, 0x501, 0x836, 0x700, 0x9ac, + 0x000, 0xdb5, 0xfa3, 0x5d3, 0x1f8, 0x888, -1, 0xfa4, 0xfe7, 0xd87, 0x9fe, + 0x6af, 0x9a5, 0xfd5, 0xf49, 0xded, 0x416, 0x2fc, 0x078, 0xd2e, 0xbf1, 0xcd9, + 0x733, -1, 0xb50, 0xd57, 0xaa1, -1, 0x9b0, 0x874, 0x18f, -1, -1, + 0x2f9, 0xfbb, 0xf73, 0x646, 0x868, 0x000, 0x000, 0xba2, 0xd74, 0xc8f, 0xe36, + 0xcfd, 0x5b8, 0x850, 0x48a, 0x689, 0x7ad, 0xefd, 0x7e1, 0xf45, 0xd9e, 0x0f0, + 0x7c0, 0x675, -1, 0xf26, 0x3c0, 0xe2d, 0xb62, 0x276, -1, 0xf90, 0x837, + 0xc7c, 0x8ed, 0x08d, 0x1d6, 0x506, 0xac9, 0xd81, 0x1be, 0xf7e, 0x1a3, 0xb2a, + 0x5e2, 0x217, 0x1df, 0x5a3, 0x498, 0xfb1, 0x432, 0x2ca, 0x5a1, 0x5d5, 0x135, + 0x6f0, -1, 0xf70, 0x000, 0xd4c, 0x634, -1, 0x8ca, 0x198, -1, 0x7b9, + 0x629, 0xc16, 0x68c, 0xea2, -1, 0xba0, 0x6d9, 0xce9, 0x7b2, 0x000, 0xf59, + 0x252, 0x575, 0x0a8, 0x894, 0x000, 0x824, 0xf63, 0xd70, 0xdd8, 0x856, 0xc77, + 0x334, 0xeb2, 0x2b8, 0x307, 0xc34, 0x2e7, 0xa74, 0x6b9, 0x0e1, 0x20f, 0x3ee, + 0xf80, 0xa1f, 0x6e6, 0xa03, 0x3c7, 0x72f, 0xfff, 0x156, 0x273, 0x1b7, 0xd90, + 0x998, 0x474, 0xf1b, 0x80f, 0xe4c, 0x0ba, 0xfff, 0x85e, 0x3af, 0x58f, 0x000, + 0xf6b, 0x71c, 0x4ed, 0xec3, 0x4cb, 0x000, 0xa68, 0x6ca, 0x086, 0x000, 0x6e4, + 0xab3, 0xff5, 0x281, 0xf0a, 0xc92, 0x8d5, 0x486, 0xdd1, 0x903, 0x8e3, 0x9df, + 0x2ab, 0xd08, 0x144, 0xdcd, 0x281, 0x046, 0x161, 0xe83, 0xf24, 0xce7, 0x30a, + 0xf89, 0xf01, 0x308, 0x142, 0x9df, 0x44d, 0x9dd, 0x3ed, 0x81b, 0xd9d, 0x000, + 0x8c2, 0xe01, 0xfe6, 0xa20, 0x167, 0xedd, 0xdb1, 0x470, 0xe70, 0x3aa, 0x0ff, + 0x4d1, 0xd30, 0x67a, 0xc56, 0x3d8, 0xf2e, 0x86a, 0x18b, 0x3f5, 0x1a7, 0xd97, + 0x652, 0x000, 0x00d, 0xbc7, 0xed3, 0x39e, 0xb0d, 0xd8d, 0xc49, 0x2db, 0x44e, + 0x820, 0x189, 0xd51, 0x523, 0x161, 0x2eb, 0x41c, 0x951, -1, 0xbb7, -1, + -1, 0x0a7, 0x3ed, 0xfaa, 0x18e, 0xa34, 0x820, 0x2b4, -1, 0x8c2, 0x3ee, + 0x59d, 0x97b, 0x209, 0x3a2, 0x102, 0x351, 0x6bf, 0xd3f, 0x4fc, 0x55f, 0x4b5, + 0xe22, 0xf13, 0x53a, 0x08a, 0x38d, 0xf4b, 0x424, 0xea6, 0x48e, 0x11c, 0x339, + 0x5bd, 0xf7c, 0x3bd, 0x15a, 0x35c, 0x854, 0x71b, 0x30f, 0x065, 0x97e, 0x354, + 0x28e, 0x344, 0x926, 0xc0b, 0xae0, 0x5db, 0xb3e, 0x661, 0x432, 0x3c8, 0xf5e, + 0x368, 0xc85, 0xfff, 0x7f5, 0x0b6, 0x98b, -1, 0x28c, 0x784, 0xb78, 0x50a, + 0x696, 0x47c, 0x40d, -1, 0xe4d, 0x5fc, -1, -1, 0xadb, 0x1db, 0x830, + 0xd48, -1, 0xf3a, 0xee4, 0xed4, 0xb1a, 0xa14, 0x36d, 0xf1c, 0x774, 0x000, + 0x942, 0x278, 0x7ee, 0x000, 0x550, 0x57c, 0x343, 0x22b, 0x324, 0xa34, 0x0ea, + 0x230, 0x51b, 0x2d1, 0x500, 0x59f, 0xd56, 0x540, 0x2f4, 0x87d, 0x9e5, 0x9c5, + 0x5ea, 0x771, 0x491, 0x206, 0xa4b, 0x4bf, 0xdaf, 0x308, 0xb25, 0x261, 0x991, + 0x000, 0x88e, 0x7e8, 0x3d6, 0x15d, 0xebc, 0x6c2, 0xd45, 0x000, 0x3c6, 0x48d, + 0x622, 0x758, 0xfa9, 0x3cf, 0x401, 0xcdb, 0xe3f, 0x544, 0xf1f, 0x000, -1, + 0x4d4, 0x000, 0x7f1, 0xba4, 0x81c, 0x92f, 0x7d1, 0xa83, 0xa31, 0xe74, 0xa20, + 0x475, 0x489, 0xf20, 0x3d1, 0xac1, 0xb2d, 0x6b2, 0x1b6, 0x063, 0xd00, 0xfeb, + 0x5ca, 0xb2c, 0xcb2, 0x1cb, 0x251, 0x82b, 0x8ba, 0x40b, 0xf1e, 0xa8a, 0xd24, + 0x880, 0x84e, 0x8cb, 0x0a3, 0x000, 0xaf7, 0xf99, 0x6a1, 0x156, 0x382, 0x0a0, + 0x000, 0xed1, 0xd07, 0xbf5, 0x000, 0x295, 0xe48, 0x760, 0x019, 0x97f, 0xb46, + 0xff5, 0x7c9, 0x1cf, 0xba4, 0x630, 0xe58, 0xda6, 0xd4b, 0xc02, 0xf9f, 0x11c, + 0x000, 0xb99, 0x51f, 0x43e, 0x199, 0xdfb, 0x72f, 0x913, 0x509, 0xac5, 0xa2e, + 0xcdb, 0x348, 0xb15, 0x472, 0x95d, 0x67f, 0x000, 0x4b9, 0xd78, 0xc87, 0x0f6, + 0x281, 0x0bd, 0x924, 0x35e, 0x129, 0xffd, 0xe24, 0x000, 0x640, 0x09b, 0xd10, + 0xa0d, 0x000, 0x474, 0x189, 0x49f, 0x62d, 0xcba, 0x561, 0x000, 0x58a, 0xaa1, + 0x603, 0x5ab, 0x0c7, 0x00a, 0x784, 0x5e4, 0x7e4, 0xe4d, -1, 0x276, 0x465, + 0xee9, 0xe51, 0xdae, 0xbb1, 0x51f, 0xcba, 0x1c3, 0xd70, 0x000, 0x5be, 0x4ea, + 0x3cc, 0xf13, 0x811, 0x813, 0x234, 0x7e4, 0xbae, 0xd97, 0xb74, 0x000, 0x76a, + 0xda1, 0x000, 0xd8c, 0x53a, 0xc5a, 0x000, 0x000, 0x61b, 0xd87, 0x141, 0x383, + 0xe06, 0x6a3, 0x6c3, 0xbcc, 0xc44, 0xf63, 0xd8b, 0x58d, 0x000, 0x839, 0x77a, + 0x000, 0x8e4, 0x000, 0xdbe, 0x483, 0xd5b, 0xa9d, 0xca5, 0x431, 0x491, 0x29a, + 0x27d, 0x2d2, 0x691, 0x000, 0x19a, 0xa0d, 0xb0b, 0xf32, 0xe49, 0xfbf, 0x399, + 0xd20, 0x000, 0x66a, 0x000, 0x447, 0xb20, 0x894, 0x038, 0xc9c, 0xff0, 0x000, + 0x0d4, 0xad4, 0x768, 0x65c, 0x000, 0x27b, 0x6c6, 0x9be, 0xd35, 0xc6a, 0xdd3, + 0x000, 0x2a7, 0x158, 0x38d, 0x8ef, 0x7b6, 0xd49, 0x30c, 0xec3, 0x211, 0x17c, + 0xcd0, 0x61f, 0x000, 0xe6e, 0x1d4, 0x6e9, 0x000, 0xc2d, 0x5c3, 0xcd4, 0x760, + 0x532, 0xc94, 0x590, 0x000, 0x4a3, 0xc33, 0x000, 0x426, 0x604, 0xa06, 0xa99, + 0x917, 0x0c4, 0xc8d, 0x9e5, 0xcc7, 0x415, 0xf79, 0x000, 0xaf4, 0x622, 0x756, + 0x9c2, 0xa51, 0xb0f, 0x4ef, 0xbc4, 0xe15, 0x29e, 0x055, 0x6c9, 0x695, 0x94f, + 0x9d6, 0x000, 0xb9f, 0xd46, 0x1d4, 0x000, 0xcb2, 0x9e8, 0x000, 0xa5e, 0xce0, + 0x000, 0x098, 0xa98, 0x6d9, 0x5e2, 0x95f, 0x791, 0xeb8, 0x5fa, 0x60a, 0xacc, + 0x3d3, 0x4df, 0x0df, 0x9ca, 0x972, 0x3cc, 0x583, 0xca5, 0xe1a, 0x000, 0x2d3, + 0x266, 0x000, 0x06c, 0xfff, 0x62d, 0x64e, 0x40c, 0x599, 0x475, 0xaa9, 0xba6, + 0x96f, 0xe32, 0x059, 0x342, 0x36d, 0xfd1, 0x09b, 0x878, 0x9f8, 0x000, 0x3ad, + 0xdba, 0x000, 0x544, 0xc1a, 0x000, 0xee8, 0x492, 0xa6b, 0x447, 0xd2a, 0xb4e, + 0x02c, 0xadb, 0xde2, 0x904, 0x62d, 0xf01, 0xbb8, 0x255, 0x382, 0xfff, 0x29e, + 0x000, 0x000, 0x011, 0xfff, +}; + +#endif diff --git a/zbar/decoder/qr_finder.c b/zbar/decoder/qr_finder.c new file mode 100644 index 0000000..c932c03 --- /dev/null +++ b/zbar/decoder/qr_finder.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <assert.h> + +#include <zbar.h> + +#ifdef DEBUG_QR_FINDER +#define DEBUG_LEVEL (DEBUG_QR_FINDER) +#endif +#include "debug.h" +#include "decoder.h" + +/* at this point lengths are all decode unit offsets from the decode edge + * NB owned by finder + */ +qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t *dcode) +{ + return (&dcode->qrf.line); +} + +zbar_symbol_type_t _zbar_find_qr(zbar_decoder_t *dcode) +{ + qr_finder_t *qrf = &dcode->qrf; + unsigned s, qz, w; + int ei; + + /* update latest finder pattern width */ + qrf->s5 -= get_width(dcode, 6); + qrf->s5 += get_width(dcode, 1); + s = qrf->s5; + + /*TODO: The 2005 standard allows reflectance-reversed codes (light on dark + instead of dark on light). + If we find finder patterns with the opposite polarity, we should invert + the final binarized image and use them to search for QR codes in that.*/ + if (get_color(dcode) != ZBAR_SPACE || s < 7) + return (0); + + dbprintf(2, " qrf: s=%d", s); + + ei = decode_e(pair_width(dcode, 1), s, 7); + dbprintf(2, " %d", ei); + if (ei) + goto invalid; + + ei = decode_e(pair_width(dcode, 2), s, 7); + dbprintf(2, "%d", ei); + if (ei != 2) + goto invalid; + + ei = decode_e(pair_width(dcode, 3), s, 7); + dbprintf(2, "%d", ei); + if (ei != 2) + goto invalid; + + ei = decode_e(pair_width(dcode, 4), s, 7); + dbprintf(2, "%d", ei); + if (ei) + goto invalid; + + /* valid QR finder symbol + * mark positions needed by decoder + */ + qz = get_width(dcode, 0); + w = get_width(dcode, 1); + qrf->line.eoffs = qz + (w + 1) / 2; + qrf->line.len = qz + w + get_width(dcode, 2); + qrf->line.pos[0] = qrf->line.len + get_width(dcode, 3); + qrf->line.pos[1] = qrf->line.pos[0]; + w = get_width(dcode, 5); + qrf->line.boffs = qrf->line.pos[0] + get_width(dcode, 4) + (w + 1) / 2; + + dbprintf(2, " boff=%d pos=%d len=%d eoff=%d [valid]\n", qrf->line.boffs, + qrf->line.pos[0], qrf->line.len, qrf->line.eoffs); + + dcode->direction = 0; + dcode->buflen = 0; + return (ZBAR_QRCODE); + +invalid: + dbprintf(2, " [invalid]\n"); + return (0); +} diff --git a/zbar/decoder/qr_finder.h b/zbar/decoder/qr_finder.h new file mode 100644 index 0000000..552ad4d --- /dev/null +++ b/zbar/decoder/qr_finder.h @@ -0,0 +1,23 @@ +#ifndef _DECODER_QR_FINDER_H_ +#define _DECODER_QR_FINDER_H_ + +#include "qrcode.h" + +/* QR Code symbol finder state */ +typedef struct qr_finder_s { + unsigned s5; /* finder pattern width */ + qr_finder_line line; /* position info needed by decoder */ + + unsigned config; +} qr_finder_t; + +/* reset QR finder specific state */ +static inline void qr_finder_reset(qr_finder_t *qrf) +{ + qrf->s5 = 0; +} + +/* find QR Code symbols */ +zbar_symbol_type_t _zbar_find_qr(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/sq_finder.c b/zbar/decoder/sq_finder.c new file mode 100644 index 0000000..ee75e56 --- /dev/null +++ b/zbar/decoder/sq_finder.c @@ -0,0 +1,7 @@ +#include "sq_finder.h" +#include "decoder.h" + +unsigned _zbar_decoder_get_sq_finder_config(zbar_decoder_t *dcode) +{ + return dcode->sqf.config; +} diff --git a/zbar/decoder/sq_finder.h b/zbar/decoder/sq_finder.h new file mode 100644 index 0000000..97232dd --- /dev/null +++ b/zbar/decoder/sq_finder.h @@ -0,0 +1,9 @@ +#ifndef _DECODER_SQ_FINDER_H_ +#define _DECODER_SQ_FINDER_H_ + +/* SQ Code symbol finder state */ +typedef struct sq_finder_s { + unsigned config; +} sq_finder_t; + +#endif diff --git a/zbar/error.c b/zbar/error.c new file mode 100644 index 0000000..1becc1a --- /dev/null +++ b/zbar/error.c @@ -0,0 +1,175 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "error.h" +#include <string.h> + +int _zbar_verbosity = 0; + +static const char *const sev_str[] = { "FATAL ERROR", "ERROR", "OK", "WARNING", + "NOTE" }; +#define SEV_MAX (strlen(sev_str[0])) + +static const char *const mod_str[] = { "processor", "video", "window", + "image scanner", "<unknown>" }; +#define MOD_MAX (strlen(mod_str[ZBAR_MOD_IMAGE_SCANNER])) + +static const char *const err_str[] = { + "no error", /* OK */ + "out of memory", /* NOMEM */ + "internal library error", /* INTERNAL */ + "unsupported request", /* UNSUPPORTED */ + "invalid request", /* INVALID */ + "system error", /* SYSTEM */ + "locking error", /* LOCKING */ + "all resources busy", /* BUSY */ + "X11 display error", /* XDISPLAY */ + "X11 protocol error", /* XPROTO */ + "output window is closed", /* CLOSED */ + "windows system error", /* WINAPI */ + "unknown error" /* NUM */ +}; +#define ERR_MAX (strlen(err_str[ZBAR_ERR_CLOSED])) + +int zbar_version(unsigned *major, unsigned *minor, unsigned *patch) +{ + if (major) + *major = ZBAR_VERSION_MAJOR; + if (minor) + *minor = ZBAR_VERSION_MINOR; + if (patch) + *patch = ZBAR_VERSION_PATCH; + return (0); +} + +void zbar_set_verbosity(int level) +{ + _zbar_verbosity = level; +} + +void zbar_increase_verbosity() +{ + if (!_zbar_verbosity) + _zbar_verbosity++; + else + _zbar_verbosity <<= 1; +} + +int _zbar_error_spew(const void *container, int verbosity) +{ + const errinfo_t *err = container; + assert(err->magic == ERRINFO_MAGIC); + fprintf(stderr, "%s", _zbar_error_string(err, verbosity)); + return (-err->sev); +} + +zbar_error_t _zbar_get_error_code(const void *container) +{ + const errinfo_t *err = container; + assert(err->magic == ERRINFO_MAGIC); + return (err->type); +} + +/* ERROR: zbar video in v4l1_set_format(): + * system error: blah[: blah] + */ + +const char *_zbar_error_string(const void *container, int verbosity) +{ + static const char basefmt[] = "%s: zbar %s in %s():\n %s: "; + errinfo_t *err = (errinfo_t *)container; + const char *sev, *mod, *func, *type; + int len; + + assert(err->magic == ERRINFO_MAGIC); + + if (err->sev >= SEV_FATAL && err->sev <= SEV_NOTE) + sev = sev_str[err->sev + 2]; + else + sev = sev_str[1]; + + if (err->module >= ZBAR_MOD_PROCESSOR && err->module < ZBAR_MOD_UNKNOWN) + mod = mod_str[err->module]; + else + mod = mod_str[ZBAR_MOD_UNKNOWN]; + + func = (err->func) ? err->func : "<unknown>"; + + if (err->type >= 0 && err->type < ZBAR_ERR_NUM) + type = err_str[err->type]; + else + type = err_str[ZBAR_ERR_NUM]; + + len = SEV_MAX + MOD_MAX + ERR_MAX + strlen(func) + sizeof(basefmt); + err->buf = realloc(err->buf, len); + len = sprintf(err->buf, basefmt, sev, mod, func, type); + if (len <= 0) + return ("<unknown>"); + + if (err->detail) { + int newlen = len + strlen(err->detail) + 1; + if (strstr(err->detail, "%s")) { + if (!err->arg_str) + err->arg_str = strdup("<?>"); + err->buf = realloc(err->buf, newlen + strlen(err->arg_str)); + len += sprintf(err->buf + len, err->detail, err->arg_str); + } else if (strstr(err->detail, "%d") || strstr(err->detail, "%x")) { + err->buf = realloc(err->buf, newlen + 32); + len += sprintf(err->buf + len, err->detail, err->arg_int); + } else { + err->buf = realloc(err->buf, newlen); + len += sprintf(err->buf + len, "%s", err->detail); + } + if (len <= 0) + return ("<unknown>"); + } + +#ifdef HAVE_ERRNO_H + if (err->type == ZBAR_ERR_SYSTEM) { + static const char sysfmt[] = ": %s (%d)\n"; + const char *syserr = strerror(err->errnum); + err->buf = realloc(err->buf, len + strlen(sysfmt) + strlen(syserr)); + len += sprintf(err->buf + len, sysfmt, syserr, err->errnum); + } +#endif +#ifdef _WIN32 + else if (err->type == ZBAR_ERR_WINAPI) { + char *syserr = NULL; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err->errnum, 0, (LPTSTR)&syserr, 1, NULL) && + syserr) { + char sysfmt[] = ": %s (%d)\n"; + err->buf = realloc(err->buf, len + strlen(sysfmt) + strlen(syserr)); + len += sprintf(err->buf + len, sysfmt, syserr, err->errnum); + LocalFree(syserr); + } + } +#endif + else { + err->buf = realloc(err->buf, len + 2); + len += sprintf(err->buf + len, "\n"); + } + return (err->buf); +} diff --git a/zbar/error.h b/zbar/error.h new file mode 100644 index 0000000..baf759c --- /dev/null +++ b/zbar/error.h @@ -0,0 +1,236 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ERROR_H_ +#define _ERROR_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <assert.h> + +#include <zbar.h> + +#ifdef _WIN32 +#include <windows.h> +#endif + +#if __STDC_VERSION__ < 199901L +#if __GNUC__ >= 2 +#define __func__ __FUNCTION__ +#else +#define __func__ "<unknown>" +#endif +#endif + +#define ERRINFO_MAGIC (0x5252457a) /* "zERR" (LE) */ + +typedef enum errsev_e +{ + SEV_FATAL = -2, /* application must terminate */ + SEV_ERROR = -1, /* might be able to recover and continue */ + SEV_OK = 0, + SEV_WARNING = 1, /* unexpected condition */ + SEV_NOTE = 2, /* fyi */ +} errsev_t; + +typedef enum errmodule_e +{ + ZBAR_MOD_PROCESSOR, + ZBAR_MOD_VIDEO, + ZBAR_MOD_WINDOW, + ZBAR_MOD_IMAGE_SCANNER, + ZBAR_MOD_UNKNOWN, +} errmodule_t; + +typedef struct errinfo_s { + uint32_t magic; /* just in case */ + errmodule_t module; /* reporting module */ + char *buf; /* formatted and passed to application */ + int errnum; /* errno for system errors */ + + errsev_t sev; + zbar_error_t type; + const char *func; /* reporting function */ + const char *detail; /* description */ + char *arg_str; /* single string argument */ + int arg_int; /* single integer argument */ +} errinfo_t; + +extern int _zbar_verbosity; + +/* FIXME don't we need varargs hacks here? */ + +#ifdef _WIN32 +#define ZFLUSH fflush(stderr); +#else +#define ZFLUSH +#endif + +#ifdef ZNO_MESSAGES + +#ifdef __GNUC__ +/* older versions of gcc (< 2.95) require a named varargs parameter */ +#define zprintf(args...) +#else +/* unfortunately named vararg parameter is a gcc-specific extension */ +#define zprintf(...) +#endif + +#else + +#ifdef __GNUC__ +#define zprintf(level, format, args...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: " format, __func__, ##args); \ + ZFLUSH \ + } \ + } while (0) +#define zwprintf(level, format, args...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: ", __func__); \ + fwprintf(stderr, format, ##args); \ + ZFLUSH \ + } \ + } while (0) +#else +#define zprintf(level, format, ...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: " format, __func__, ##__VA_ARGS__); \ + ZFLUSH \ + } \ + } while (0) +#define zwprintf(level, format, ...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: ", __func__); \ + fwprintf(stderr, format, ##__VA_ARGS__); \ + ZFLUSH \ + } \ + } while (0) +#endif + +#endif + +static inline int err_copy(void *dst_c, void *src_c) +{ + errinfo_t *dst = dst_c; + errinfo_t *src = src_c; + assert(dst->magic == ERRINFO_MAGIC); + assert(src->magic == ERRINFO_MAGIC); + + dst->errnum = src->errnum; + dst->sev = src->sev; + dst->type = src->type; + dst->func = src->func; + dst->detail = src->detail; + dst->arg_str = src->arg_str; + src->arg_str = NULL; /* unused at src, avoid double free */ + dst->arg_int = src->arg_int; + return (-1); +} + +static inline int err_capture(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); +#ifdef HAVE_ERRNO_H + if (type == ZBAR_ERR_SYSTEM) + err->errnum = errno; +#endif +#ifdef _WIN32 + if (type == ZBAR_ERR_WINAPI) + err->errnum = GetLastError(); +#endif + err->sev = sev; + err->type = type; + err->func = func; + err->detail = detail; + if (_zbar_verbosity >= 1) + _zbar_error_spew(err, 0); + return (-1); +} + +static inline int err_capture_str(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, const char *arg) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + if (err->arg_str) + free(err->arg_str); + err->arg_str = strdup(arg); + return (err_capture(container, sev, type, func, detail)); +} + +static inline int err_capture_int(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, int arg) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + err->arg_int = arg; + return (err_capture(container, sev, type, func, detail)); +} + +static inline int err_capture_num(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, int num) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + err->errnum = num; + return (err_capture(container, sev, type, func, detail)); +} + +static inline void err_init(errinfo_t *err, errmodule_t module) +{ + err->magic = ERRINFO_MAGIC; + err->module = module; +} + +static inline void err_cleanup(errinfo_t *err) +{ + assert(err->magic == ERRINFO_MAGIC); + if (err->buf) { + free(err->buf); + err->buf = NULL; + } + if (err->arg_str) { + free(err->arg_str); + err->arg_str = NULL; + } +} + +#endif diff --git a/zbar/event.h b/zbar/event.h new file mode 100644 index 0000000..aef5a07 --- /dev/null +++ b/zbar/event.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_EVENT_H_ +#define _ZBAR_EVENT_H_ + +#include "config.h" +#include "mutex.h" +#include "timer.h" + +/* platform synchronization "event" abstraction + */ + +#if defined(_WIN32) + +#include <windows.h> + +typedef HANDLE zbar_event_t; + +#else + +#ifdef HAVE_LIBPTHREAD +#include <pthread.h> +#endif + +typedef struct zbar_event_s { + int state; +#ifdef HAVE_LIBPTHREAD + pthread_cond_t cond; +#endif + int pollfd; +} zbar_event_t; + +#endif + +extern int _zbar_event_init(zbar_event_t *); +extern void _zbar_event_destroy(zbar_event_t *); +extern void _zbar_event_trigger(zbar_event_t *); +extern int _zbar_event_wait(zbar_event_t *, zbar_mutex_t *, zbar_timer_t *); + +#endif diff --git a/zbar/image.c b/zbar/image.c new file mode 100644 index 0000000..82aeeb8 --- /dev/null +++ b/zbar/image.c @@ -0,0 +1,342 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> + +#include "error.h" +#include "image.h" +#include "refcnt.h" + +zbar_image_t *zbar_image_create() +{ + zbar_image_t *img = calloc(1, sizeof(zbar_image_t)); + _zbar_refcnt_init(); + _zbar_image_refcnt(img, 1); + img->srcidx = -1; + return (img); +} + +void _zbar_image_free(zbar_image_t *img) +{ + if (img->syms) { + zbar_symbol_set_ref(img->syms, -1); + img->syms = NULL; + } + free(img); +} + +void zbar_image_destroy(zbar_image_t *img) +{ + _zbar_image_refcnt(img, -1); +} + +void zbar_image_ref(zbar_image_t *img, int refs) +{ + _zbar_image_refcnt(img, refs); +} + +unsigned long zbar_image_get_format(const zbar_image_t *img) +{ + return (img->format); +} + +unsigned zbar_image_get_sequence(const zbar_image_t *img) +{ + return (img->seq); +} + +unsigned zbar_image_get_width(const zbar_image_t *img) +{ + return (img->width); +} + +unsigned zbar_image_get_height(const zbar_image_t *img) +{ + return (img->height); +} + +void zbar_image_get_size(const zbar_image_t *img, unsigned *w, unsigned *h) +{ + if (w) + *w = img->width; + if (h) + *h = img->height; +} + +void zbar_image_get_crop(const zbar_image_t *img, unsigned *x, unsigned *y, + unsigned *w, unsigned *h) +{ + if (x) + *x = img->crop_x; + if (y) + *y = img->crop_y; + if (w) + *w = img->crop_w; + if (h) + *h = img->crop_h; +} + +const void *zbar_image_get_data(const zbar_image_t *img) +{ + return (img->data); +} + +unsigned long zbar_image_get_data_length(const zbar_image_t *img) +{ + return (img->datalen); +} + +void zbar_image_set_format(zbar_image_t *img, unsigned long fmt) +{ + img->format = fmt; +} + +void zbar_image_set_sequence(zbar_image_t *img, unsigned seq) +{ + img->seq = seq; +} + +void zbar_image_set_size(zbar_image_t *img, unsigned w, unsigned h) +{ + img->crop_x = img->crop_y = 0; + img->width = img->crop_w = w; + img->height = img->crop_h = h; +} + +void zbar_image_set_crop(zbar_image_t *img, unsigned x, unsigned y, unsigned w, + unsigned h) +{ + unsigned img_h; + unsigned img_w = img->width; + if (x > img_w) + x = img_w; + if (x + w > img_w) + w = img_w - x; + img->crop_x = x; + img->crop_w = w; + + img_h = img->height; + if (y > img_h) + y = img_h; + if (y + h > img_h) + h = img_h - y; + img->crop_y = y; + img->crop_h = h; +} + +inline void zbar_image_free_data(zbar_image_t *img) +{ + if (!img) + return; + if (img->src) { + zbar_image_t *newimg; + /* replace video image w/new copy */ + assert(img->refcnt); /* FIXME needs lock */ + newimg = zbar_image_create(); + memcpy(newimg, img, sizeof(zbar_image_t)); + /* recycle video image */ + newimg->cleanup(newimg); + /* detach old image from src */ + img->cleanup = NULL; + img->src = NULL; + img->srcidx = -1; + } else if (img->cleanup && img->data) { + if (img->cleanup != zbar_image_free_data) { + /* using function address to detect this case is a bad idea; + * windows link libraries add an extra layer of indirection... + * this works around that problem (bug #2796277) + */ + zbar_image_cleanup_handler_t *cleanup = img->cleanup; + img->cleanup = zbar_image_free_data; + cleanup(img); + } else + free((void *)img->data); + } + img->data = NULL; +} + +void zbar_image_set_data(zbar_image_t *img, const void *data, unsigned long len, + zbar_image_cleanup_handler_t *cleanup) +{ + zbar_image_free_data(img); + img->data = data; + img->datalen = len; + img->cleanup = cleanup; +} + +void zbar_image_set_userdata(zbar_image_t *img, void *userdata) +{ + img->userdata = userdata; +} + +void *zbar_image_get_userdata(const zbar_image_t *img) +{ + return (img->userdata); +} + +zbar_image_t *zbar_image_copy(const zbar_image_t *src) +{ + return _zbar_image_copy(src, 0); +} + +const zbar_symbol_set_t *zbar_image_get_symbols(const zbar_image_t *img) +{ + return (img->syms); +} + +void zbar_image_set_symbols(zbar_image_t *img, const zbar_symbol_set_t *syms) +{ + if (syms) + zbar_symbol_set_ref(syms, 1); + if (img->syms) + zbar_symbol_set_ref(img->syms, -1); + img->syms = (zbar_symbol_set_t *)syms; +} + +const zbar_symbol_t *zbar_image_first_symbol(const zbar_image_t *img) +{ + return ((img->syms) ? img->syms->head : NULL); +} + +typedef struct zimg_hdr_s { + uint32_t magic, format; + uint16_t width, height; + uint32_t size; +} zimg_hdr_t; + +int zbar_image_write(const zbar_image_t *img, const char *filebase) +{ + int len = strlen(filebase) + 16; + char *filename = malloc(len); + int n = 0, rc = 0; + FILE *f; + zimg_hdr_t hdr; + strcpy(filename, filebase); + if ((img->format & 0xff) >= ' ') + n = snprintf(filename, len, "%s.%.4s.zimg", filebase, + (char *)&img->format); + else + n = snprintf(filename, len, "%s.%08" PRIx32 ".zimg", filebase, + img->format); + assert(n < len - 1); + filename[len - 1] = '\0'; + + zprintf(1, "dumping %.4s(%08" PRIx32 ") image to %s\n", + (char *)&img->format, img->format, filename); + + f = fopen(filename, "w"); + if (!f) { +#ifdef HAVE_ERRNO_H + rc = errno; + zprintf(1, "ERROR opening %s: %s\n", filename, strerror(rc)); +#else + rc = 1; +#endif + goto error; + } + + hdr.magic = 0x676d697a; + hdr.format = img->format; + hdr.width = img->width; + hdr.height = img->height; + hdr.size = img->datalen; + + if (fwrite(&hdr, sizeof(hdr), 1, f) != 1 || + fwrite(img->data, 1, img->datalen, f) != img->datalen) { +#ifdef HAVE_ERRNO_H + rc = errno; + zprintf(1, "ERROR writing %s: %s\n", filename, strerror(rc)); +#else + rc = 1; +#endif + fclose(f); + goto error; + } + + rc = fclose(f); + +error: + free(filename); + return (rc); +} + +#ifdef DEBUG_SVG +#include <png.h> + +int zbar_image_write_png(const zbar_image_t *img, const char *filename) +{ + int rc = -1; + FILE *file = NULL; + png_struct *png = NULL; + png_info *info = NULL; + const uint8_t **rows = NULL; + + rows = malloc(img->height * sizeof(*rows)); + if (!rows) + goto done; + + rows[0] = img->data; + int y; + for (y = 1; y < img->height; y++) + rows[y] = rows[y - 1] + img->width; + + file = fopen(filename, "wb"); + if (!file) + goto done; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + goto done; + + info = png_create_info_struct(png); + if (!info) + goto done; + + if (setjmp(png_jmpbuf(png))) + goto done; + + png_init_io(png, file); + png_set_compression_level(png, 9); + png_set_IHDR(png, info, img->width, img->height, 8, PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_set_rows(png, info, (void *)rows); + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + + png_write_end(png, info); + rc = 0; + +done: + if (png) + png_destroy_write_struct(&png, &info); + if (rows) + free(rows); + if (file) + fclose(file); + return (rc); +} + +#endif diff --git a/zbar/image.h b/zbar/image.h new file mode 100644 index 0000000..8d85fba --- /dev/null +++ b/zbar/image.h @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdlib.h> + +#include <zbar.h> +#include "error.h" +#include "refcnt.h" +#include "symbol.h" + +#define fourcc zbar_fourcc + +/* unpack size/location of component */ +#define RGB_SIZE(c) ((c) >> 5) +#define RGB_OFFSET(c) ((c)&0x1f) + +/* coarse image format categorization. + * to limit conversion variations + */ +typedef enum zbar_format_group_e +{ + ZBAR_FMT_GRAY, + ZBAR_FMT_YUV_PLANAR, + ZBAR_FMT_YUV_PACKED, + ZBAR_FMT_RGB_PACKED, + ZBAR_FMT_YUV_NV, + ZBAR_FMT_JPEG, + + /* enum size */ + ZBAR_FMT_NUM +} zbar_format_group_t; + +struct zbar_image_s { + uint32_t format; /* fourcc image format code */ + unsigned width, height; /* image size */ + const void *data; /* image sample data */ + unsigned long datalen; /* allocated/mapped size of data */ + unsigned crop_x, crop_y; /* crop rectangle */ + unsigned crop_w, crop_h; + void *userdata; /* user specified data associated w/image */ + + /* cleanup handler */ + zbar_image_cleanup_handler_t *cleanup; + refcnt_t refcnt; /* reference count */ + zbar_video_t *src; /* originator */ + int srcidx; /* index used by originator */ + zbar_image_t *next; /* internal image lists */ + + unsigned seq; /* page/frame sequence number */ + zbar_symbol_set_t *syms; /* decoded result set */ +}; + +/* description of an image format */ +typedef struct zbar_format_def_s { + uint32_t format; /* fourcc */ + zbar_format_group_t group; /* coarse categorization */ + union { + uint8_t gen[4]; /* raw bytes */ + struct { + uint8_t bpp; /* bits per pixel */ + uint8_t red, green, blue; /* size/location a la RGB_BITS() */ + } rgb; + struct { + uint8_t xsub2, ysub2; /* chroma subsampling in each axis */ + uint8_t packorder; /* channel ordering flags + * bit0: 0=UV, 1=VU + * bit1: 0=Y/chroma, 1=chroma/Y + */ + } yuv; + uint32_t cmp; /* quick compare equivalent formats */ + } p; +} zbar_format_def_t; + +extern int _zbar_best_format(uint32_t, uint32_t *, const uint32_t *); +extern const zbar_format_def_t *_zbar_format_lookup(uint32_t); +extern void _zbar_image_free(zbar_image_t *); + +#ifdef DEBUG_SVG +extern int zbar_image_write_png(const zbar_image_t *, const char *); +#else +#define zbar_image_write_png(...) +#endif + +static inline void _zbar_image_refcnt(zbar_image_t *img, int delta) +{ + if (!_zbar_refcnt(&img->refcnt, delta) && delta <= 0) { + if (img->cleanup) + img->cleanup(img); + if (!img->src) + _zbar_image_free(img); + } +} + +static inline void _zbar_image_swap_symbols(zbar_image_t *a, zbar_image_t *b) +{ + zbar_symbol_set_t *tmp = a->syms; + a->syms = b->syms; + b->syms = tmp; +} + +static inline void _zbar_image_copy_size(zbar_image_t *dst, + const zbar_image_t *src) +{ + dst->width = src->width; + dst->height = src->height; + dst->crop_x = src->crop_x; + dst->crop_y = src->crop_y; + dst->crop_w = src->crop_w; + dst->crop_h = src->crop_h; +} + +static inline zbar_image_t *_zbar_image_copy(const zbar_image_t *src, + int inverted) +{ + zbar_image_t *dst; + + if (inverted && (src->format != fourcc('Y', '8', '0', '0')) && + (src->format != fourcc('G', 'R', 'E', 'Y'))) + return NULL; + + dst = zbar_image_create(); + dst->format = src->format; + _zbar_image_copy_size(dst, src); + dst->datalen = src->datalen; + dst->data = malloc(src->datalen); + assert(dst->data); + + if (!inverted) { + memcpy((void *)dst->data, src->data, src->datalen); + } else { + int i, len = src->datalen; + long *sp = (void *)src->data, *dp = (void *)dst->data; + char *spc, *dpc; + + /* Do it word per word, in order to speedup */ + for (i = 0; i < len; i += sizeof(long)) + *dp++ = ~(*sp++); + + /* Deal with non-aligned remains, if any */ + len -= i; + spc = (char *)sp; + dpc = (char *)dp; + for (i = 0; i < len; i++) + *dpc++ = ~(*spc++); + } + dst->cleanup = zbar_image_free_data; + return (dst); +} + +#endif diff --git a/zbar/img_scanner.c b/zbar/img_scanner.c new file mode 100644 index 0000000..d1bf8a3 --- /dev/null +++ b/zbar/img_scanner.c @@ -0,0 +1,1174 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +#endif + +#include <assert.h> +#include <stdlib.h> /* malloc, free */ +#include <string.h> /* memcmp, memset, memcpy */ + +#include <zbar.h> +#include "error.h" +#include "image.h" +#include "timer.h" +#if ENABLE_QRCODE == 1 +#include "qrcode.h" +#endif +#if ENABLE_SQCODE == 1 +#include "sqcode.h" +#endif +#include "img_scanner.h" +#include "svg.h" + +#if 1 +#define ASSERT_POS assert(p == data + x + y * (intptr_t)w) +#else +#define ASSERT_POS +#endif + +/* FIXME cache setting configurability */ + +/* time interval for which two images are considered "nearby" + */ +#define CACHE_PROXIMITY 1000 /* ms */ + +/* time that a result must *not* be detected before + * it will be reported again + */ +#define CACHE_HYSTERESIS 2000 /* ms */ + +/* time after which cache entries are invalidated + */ +#define CACHE_TIMEOUT (CACHE_HYSTERESIS * 2) /* ms */ + +#define NUM_SCN_CFGS (ZBAR_CFG_Y_DENSITY - ZBAR_CFG_X_DENSITY + 1) + +#define CFG(iscn, cfg) ((iscn)->configs[(cfg)-ZBAR_CFG_X_DENSITY]) +#define TEST_CFG(iscn, cfg) (((iscn)->config >> ((cfg)-ZBAR_CFG_POSITION)) & 1) + +#ifndef NO_STATS +#define STAT(x) iscn->stat_##x++ +#else +#define STAT(...) +#define dump_stats(...) +#endif + +#define RECYCLE_BUCKETS 5 + +typedef struct recycle_bucket_s { + int nsyms; + zbar_symbol_t *head; +} recycle_bucket_t; + +/* image scanner state */ +struct zbar_image_scanner_s { + zbar_scanner_t *scn; /* associated linear intensity scanner */ + zbar_decoder_t *dcode; /* associated symbol decoder */ +#if ENABLE_QRCODE == 1 + qr_reader *qr; /* QR Code 2D reader */ +#endif +#if ENABLE_SQCODE == 1 + sq_reader *sq; /* SQ Code 2D reader */ +#endif + + const void *userdata; /* application data */ + /* user result callback */ + zbar_image_data_handler_t *handler; + + unsigned long time; /* scan start time */ + zbar_image_t *img; /* currently scanning image *root* */ + int dx, dy, du, umin, v; /* current scan direction */ + zbar_symbol_set_t *syms; /* previous decode results */ + /* recycled symbols in 4^n size buckets */ + recycle_bucket_t recycle[RECYCLE_BUCKETS]; + + int enable_cache; /* current result cache state */ + zbar_symbol_t *cache; /* inter-image result cache entries */ + + /* configuration settings */ + unsigned config; /* config flags */ + unsigned ean_config; + int configs[NUM_SCN_CFGS]; /* int valued configurations */ + int sym_configs[1][NUM_SYMS]; /* per-symbology configurations */ + +#ifndef NO_STATS + int stat_syms_new; + int stat_iscn_syms_inuse, stat_iscn_syms_recycle; + int stat_img_syms_inuse, stat_img_syms_recycle; + int stat_sym_new; + int stat_sym_recycle[RECYCLE_BUCKETS]; +#endif + +#ifdef HAVE_DBUS + int is_dbus_enabled; /* dbus enabled flag */ +#endif +}; + +void _zbar_image_scanner_recycle_syms(zbar_image_scanner_t *iscn, + zbar_symbol_t *sym) +{ + zbar_symbol_t *next = NULL; + for (; sym; sym = next) { + next = sym->next; + if (sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) { + /* unlink referenced symbol */ + /* FIXME handle outstanding component refs (currently unsupported) + */ + assert(sym->data_alloc); + sym->next = NULL; + } else { + int i; + recycle_bucket_t *bucket; + /* recycle unreferenced symbol */ + if (!sym->data_alloc) { + sym->data = NULL; + sym->datalen = 0; + } + if (sym->syms) { + if (_zbar_refcnt(&sym->syms->refcnt, -1)) + assert(0); + _zbar_image_scanner_recycle_syms(iscn, sym->syms->head); + sym->syms->head = NULL; + _zbar_symbol_set_free(sym->syms); + sym->syms = NULL; + } + for (i = 0; i < RECYCLE_BUCKETS; i++) + if (sym->data_alloc < 1 << (i * 2)) + break; + if (i == RECYCLE_BUCKETS) { + assert(sym->data); + free(sym->data); + sym->data = NULL; + sym->data_alloc = 0; + i = 0; + } + bucket = &iscn->recycle[i]; + /* FIXME cap bucket fill */ + bucket->nsyms++; + sym->next = bucket->head; + bucket->head = sym; + } + } +} + +static inline int recycle_syms(zbar_image_scanner_t *iscn, + zbar_symbol_set_t *syms) +{ + if (_zbar_refcnt(&syms->refcnt, -1)) + return (1); + + _zbar_image_scanner_recycle_syms(iscn, syms->head); + syms->head = syms->tail = NULL; + syms->nsyms = 0; + return (0); +} + +inline void zbar_image_scanner_recycle_image(zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + zbar_symbol_set_t *syms = iscn->syms; + if (syms && syms->refcnt) { + if (recycle_syms(iscn, syms)) { + STAT(iscn_syms_inuse); + iscn->syms = NULL; + } else + STAT(iscn_syms_recycle); + } + + syms = img->syms; + img->syms = NULL; + if (syms && recycle_syms(iscn, syms)) + STAT(img_syms_inuse); + else if (syms) { + STAT(img_syms_recycle); + + /* select one set to resurrect, destroy the other */ + if (iscn->syms) + _zbar_symbol_set_free(syms); + else + iscn->syms = syms; + } +} + +inline zbar_symbol_t *_zbar_image_scanner_alloc_sym(zbar_image_scanner_t *iscn, + zbar_symbol_type_t type, + int datalen) +{ + /* recycle old or alloc new symbol */ + zbar_symbol_t *sym = NULL; + int i; + for (i = 0; i < RECYCLE_BUCKETS - 1; i++) + if (datalen <= 1 << (i * 2)) + break; + + for (; i >= 0; i--) + if ((sym = iscn->recycle[i].head)) { + STAT(sym_recycle[i]); + break; + } + + if (sym) { + iscn->recycle[i].head = sym->next; + sym->next = NULL; + assert(iscn->recycle[i].nsyms); + iscn->recycle[i].nsyms--; + } else { + sym = calloc(1, sizeof(zbar_symbol_t)); + STAT(sym_new); + } + + /* init new symbol */ + sym->type = type; + sym->quality = 1; + sym->npts = 0; + sym->orient = ZBAR_ORIENT_UNKNOWN; + sym->cache_count = 0; + sym->time = iscn->time; + assert(!sym->syms); + + if (datalen > 0) { + sym->datalen = datalen - 1; + if (sym->data_alloc < datalen) { + if (sym->data) + free(sym->data); + sym->data_alloc = datalen; + sym->data = malloc(datalen); + } + } else { + if (sym->data) + free(sym->data); + sym->data = NULL; + sym->datalen = sym->data_alloc = 0; + } + return (sym); +} + +static inline zbar_symbol_t *cache_lookup(zbar_image_scanner_t *iscn, + zbar_symbol_t *sym) +{ + /* search for matching entry in cache */ + zbar_symbol_t **entry = &iscn->cache; + while (*entry) { + if ((*entry)->type == sym->type && (*entry)->datalen == sym->datalen && + !memcmp((*entry)->data, sym->data, sym->datalen)) + break; + if ((sym->time - (*entry)->time) > CACHE_TIMEOUT) { + /* recycle stale cache entry */ + zbar_symbol_t *next = (*entry)->next; + (*entry)->next = NULL; + _zbar_image_scanner_recycle_syms(iscn, *entry); + *entry = next; + } else + entry = &(*entry)->next; + } + return (*entry); +} + +static inline void cache_sym(zbar_image_scanner_t *iscn, zbar_symbol_t *sym) +{ + if (iscn->enable_cache) { + uint32_t age, near_thresh, far_thresh, dup; + zbar_symbol_t *entry = cache_lookup(iscn, sym); + if (!entry) { + /* FIXME reuse sym */ + entry = _zbar_image_scanner_alloc_sym(iscn, sym->type, + sym->datalen + 1); + entry->configs = sym->configs; + entry->modifiers = sym->modifiers; + memcpy(entry->data, sym->data, sym->datalen); + entry->time = sym->time - CACHE_HYSTERESIS; + entry->cache_count = 0; + /* add to cache */ + entry->next = iscn->cache; + iscn->cache = entry; + } + + /* consistency check and hysteresis */ + age = sym->time - entry->time; + entry->time = sym->time; + near_thresh = (age < CACHE_PROXIMITY); + far_thresh = (age >= CACHE_HYSTERESIS); + dup = (entry->cache_count >= 0); + if ((!dup && !near_thresh) || far_thresh) { + int type = sym->type; + int h = _zbar_get_symbol_hash(type); + entry->cache_count = -iscn->sym_configs[0][h]; + } else if (dup || near_thresh) + entry->cache_count++; + + sym->cache_count = entry->cache_count; + } else + sym->cache_count = 0; +} + +void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn, zbar_symbol_t *sym) +{ + zbar_symbol_set_t *syms; + cache_sym(iscn, sym); + + syms = iscn->syms; + if (sym->cache_count || !syms->tail) { + sym->next = syms->head; + syms->head = sym; + } else { + sym->next = syms->tail->next; + syms->tail->next = sym; + } + + if (!sym->cache_count) + syms->nsyms++; + else if (!syms->tail) + syms->tail = sym; + + _zbar_symbol_refcnt(sym, 1); +} + +#if ENABLE_QRCODE == 1 +extern qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t *); + +#define QR_FIXED(v, rnd) ((((v) << 1) + (rnd)) << (QR_FINDER_SUBPREC - 1)) +#define PRINT_FIXED(val, prec) \ + ((val) >> (prec)), (1000 * ((val) & ((1 << (prec)) - 1)) / (1 << (prec))) + +static inline void qr_handler(zbar_image_scanner_t *iscn) +{ + unsigned u; + int vert; + qr_finder_line *line = _zbar_decoder_get_qr_finder_line(iscn->dcode); + assert(line); + u = zbar_scanner_get_edge(iscn->scn, line->pos[0], QR_FINDER_SUBPREC); + line->boffs = + u - zbar_scanner_get_edge(iscn->scn, line->boffs, QR_FINDER_SUBPREC); + line->len = zbar_scanner_get_edge(iscn->scn, line->len, QR_FINDER_SUBPREC); + line->eoffs = + zbar_scanner_get_edge(iscn->scn, line->eoffs, QR_FINDER_SUBPREC) - + line->len; + line->len -= u; + + u = QR_FIXED(iscn->umin, 0) + iscn->du * u; + if (iscn->du < 0) { + int tmp = line->boffs; + line->boffs = line->eoffs; + line->eoffs = tmp; + u -= line->len; + } + vert = !iscn->dx; + line->pos[vert] = u; + line->pos[!vert] = QR_FIXED(iscn->v, 1); + + _zbar_qr_found_line(iscn->qr, vert, line); +} +#endif + +#if ENABLE_SQCODE == 1 +extern unsigned _zbar_decoder_get_sq_finder_config(zbar_decoder_t *); + +static void sq_handler(zbar_image_scanner_t *iscn) +{ + unsigned config = _zbar_decoder_get_sq_finder_config(iscn->dcode); + _zbar_sq_new_config(iscn->sq, config); +} +#endif + +static void symbol_handler(zbar_decoder_t *dcode) +{ + zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode); + zbar_symbol_type_t type = zbar_decoder_get_type(dcode); + int x = 0, y = 0, dir; + const char *data; + unsigned datalen; + zbar_symbol_t *sym; + +#if ENABLE_QRCODE == 1 + if (type == ZBAR_QRCODE) { + qr_handler(iscn); + return; + } +#else + assert(type != ZBAR_QRCODE); +#endif + + if (TEST_CFG(iscn, ZBAR_CFG_POSITION)) { + /* tmp position fixup */ + int w = zbar_scanner_get_width(iscn->scn); + int u = iscn->umin + iscn->du * zbar_scanner_get_edge(iscn->scn, w, 0); + if (iscn->dx) { + x = u; + y = iscn->v; + } else { + x = iscn->v; + y = u; + } + } + + /* FIXME debug flag to save/display all PARTIALs */ + if (type <= ZBAR_PARTIAL) { + zprintf(256, "partial symbol @(%d,%d)\n", x, y); + return; + } + + data = zbar_decoder_get_data(dcode); + datalen = zbar_decoder_get_data_length(dcode); + + /* FIXME need better symbol matching */ + for (sym = iscn->syms->head; sym; sym = sym->next) + if (sym->type == type && sym->datalen == datalen && + !memcmp(sym->data, data, datalen)) { + sym->quality++; + zprintf(224, "dup symbol @(%d,%d): dup %s: %.20s\n", x, y, + zbar_get_symbol_name(type), data); + if (TEST_CFG(iscn, ZBAR_CFG_POSITION)) + /* add new point to existing set */ + /* FIXME should be polygon */ + sym_add_point(sym, x, y); + return; + } + + sym = _zbar_image_scanner_alloc_sym(iscn, type, datalen + 1); + sym->configs = zbar_decoder_get_configs(dcode, type); + sym->modifiers = zbar_decoder_get_modifiers(dcode); + /* FIXME grab decoder buffer */ + memcpy(sym->data, data, datalen + 1); + + /* initialize first point */ + if (TEST_CFG(iscn, ZBAR_CFG_POSITION)) { + zprintf(192, "new symbol @(%d,%d): %s: %.20s\n", x, y, + zbar_get_symbol_name(type), data); + sym_add_point(sym, x, y); + } + + dir = zbar_decoder_get_direction(dcode); + if (dir) + sym->orient = (iscn->dy != 0) + ((iscn->du ^ dir) & 2); + + _zbar_image_scanner_add_sym(iscn, sym); +} + +zbar_image_scanner_t *zbar_image_scanner_create() +{ + zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t)); + if (!iscn) + return (NULL); + iscn->dcode = zbar_decoder_create(); + iscn->scn = zbar_scanner_create(iscn->dcode); + if (!iscn->dcode || !iscn->scn) { + zbar_image_scanner_destroy(iscn); + return (NULL); + } + zbar_decoder_set_userdata(iscn->dcode, iscn); + zbar_decoder_set_handler(iscn->dcode, symbol_handler); + +#if ENABLE_QRCODE == 1 + iscn->qr = _zbar_qr_create(); +#endif + +#if ENABLE_SQCODE == 1 + iscn->sq = _zbar_sq_create(); +#endif + + /* apply default configuration */ + CFG(iscn, ZBAR_CFG_X_DENSITY) = 1; + CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1; + zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1); + zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_UNCERTAINTY, 2); + zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_TEST_INVERTED, 0); + zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_BINARY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE128, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE93, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE39, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODABAR, ZBAR_CFG_UNCERTAINTY, 1); + zbar_image_scanner_set_config(iscn, ZBAR_COMPOSITE, ZBAR_CFG_UNCERTAINTY, + 0); + return (iscn); +} + +#ifndef NO_STATS +static inline void dump_stats(const zbar_image_scanner_t *iscn) +{ + int i; + zprintf(1, "symbol sets allocated = %-4d\n", iscn->stat_syms_new); + zprintf(1, " scanner syms in use = %-4d\trecycled = %-4d\n", + iscn->stat_iscn_syms_inuse, iscn->stat_iscn_syms_recycle); + zprintf(1, " image syms in use = %-4d\trecycled = %-4d\n", + iscn->stat_img_syms_inuse, iscn->stat_img_syms_recycle); + zprintf(1, "symbols allocated = %-4d\n", iscn->stat_sym_new); + for (i = 0; i < RECYCLE_BUCKETS; i++) + zprintf(1, " recycled[%d] = %-4d\n", i, + iscn->stat_sym_recycle[i]); +} +#endif + +void zbar_image_scanner_destroy(zbar_image_scanner_t *iscn) +{ + int i; + dump_stats(iscn); + if (iscn->syms) { + if (iscn->syms->refcnt) + zbar_symbol_set_ref(iscn->syms, -1); + else + _zbar_symbol_set_free(iscn->syms); + iscn->syms = NULL; + } + if (iscn->scn) + zbar_scanner_destroy(iscn->scn); + iscn->scn = NULL; + if (iscn->dcode) + zbar_decoder_destroy(iscn->dcode); + iscn->dcode = NULL; + for (i = 0; i < RECYCLE_BUCKETS; i++) { + zbar_symbol_t *sym, *next; + for (sym = iscn->recycle[i].head; sym; sym = next) { + next = sym->next; + _zbar_symbol_free(sym); + } + } +#if ENABLE_QRCODE == 1 + if (iscn->qr) { + _zbar_qr_destroy(iscn->qr); + iscn->qr = NULL; + } +#endif +#if ENABLE_SQCODE == 1 + if (iscn->sq) { + _zbar_sq_destroy(iscn->sq); + iscn->sq = NULL; + } +#endif + free(iscn); +} + +zbar_image_data_handler_t * +zbar_image_scanner_set_data_handler(zbar_image_scanner_t *iscn, + zbar_image_data_handler_t *handler, + const void *userdata) +{ + zbar_image_data_handler_t *result = iscn->handler; + iscn->handler = handler; + iscn->userdata = userdata; + return (result); +} + +int zbar_image_scanner_set_config(zbar_image_scanner_t *iscn, + zbar_symbol_type_t sym, zbar_config_t cfg, + int val) +{ + if ((sym == 0 || sym == ZBAR_COMPOSITE) && cfg == ZBAR_CFG_ENABLE) { + iscn->ean_config = !!val; + if (sym) + return (0); + } + + if (cfg < ZBAR_CFG_UNCERTAINTY) + return (zbar_decoder_set_config(iscn->dcode, sym, cfg, val)); + + if (cfg < ZBAR_CFG_POSITION) { + int c, i; + if (cfg > ZBAR_CFG_UNCERTAINTY) + return (1); + c = cfg - ZBAR_CFG_UNCERTAINTY; + if (sym > ZBAR_PARTIAL) { + i = _zbar_get_symbol_hash(sym); + iscn->sym_configs[c][i] = val; + } else + for (i = 0; i < NUM_SYMS; i++) + iscn->sym_configs[c][i] = val; + return (0); + } + + /* Image scanner parameters apply only to ZBAR_PARTIAL */ + if (sym > ZBAR_PARTIAL) + return (1); + + if (cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) { + CFG(iscn, cfg) = val; + return (0); + } + + cfg -= ZBAR_CFG_POSITION; + + if (!val) + iscn->config &= ~(1 << cfg); + else if (val == 1) + iscn->config |= (1 << cfg); + else + return (1); + + return (0); +} + +int zbar_image_scanner_get_config(zbar_image_scanner_t *iscn, + zbar_symbol_type_t sym, zbar_config_t cfg, + int *val) +{ + /* Return error if symbol doesn't have config */ + if (sym < ZBAR_PARTIAL || sym > ZBAR_CODE128 || sym == ZBAR_COMPOSITE) + return 1; + + if (cfg < ZBAR_CFG_UNCERTAINTY) + return zbar_decoder_get_config(iscn->dcode, sym, cfg, val); + + if (cfg < ZBAR_CFG_POSITION) { + int i; + if (sym == ZBAR_PARTIAL) + return (1); + + i = _zbar_get_symbol_hash(sym); + + *val = iscn->sym_configs[cfg - ZBAR_CFG_UNCERTAINTY][i]; + return 0; + } + + /* Image scanner parameters apply only to ZBAR_PARTIAL */ + if (sym > ZBAR_PARTIAL) + return (1); + + if (cfg < ZBAR_CFG_X_DENSITY) { + *val = (iscn->config & (1 << (cfg - ZBAR_CFG_POSITION))) != 0; + return 0; + } + + if (cfg <= ZBAR_CFG_Y_DENSITY) { + *val = CFG(iscn, cfg); + return 0; + } + + return 1; +} + +void zbar_image_scanner_enable_cache(zbar_image_scanner_t *iscn, int enable) +{ + if (iscn->cache) { + /* recycle all cached syms */ + _zbar_image_scanner_recycle_syms(iscn, iscn->cache); + iscn->cache = NULL; + } + iscn->enable_cache = (enable) ? 1 : 0; +} + +const zbar_symbol_set_t * +zbar_image_scanner_get_results(const zbar_image_scanner_t *iscn) +{ + return (iscn->syms); +} + +static inline void quiet_border(zbar_image_scanner_t *iscn) +{ + /* flush scanner pipeline */ + zbar_scanner_t *scn = iscn->scn; + zbar_scanner_flush(scn); + zbar_scanner_flush(scn); + zbar_scanner_new_scan(scn); +} + +#ifdef HAVE_DBUS +static int dict_add_property(DBusMessageIter *property, const char *key, + const char *value, unsigned int value_length, + int is_binary) +{ + DBusMessageIter dict_entry, dict_val, array_val; + DBusError err; + dbus_error_init(&err); + dbus_message_iter_open_container(property, DBUS_TYPE_DICT_ENTRY, NULL, + &dict_entry); + if (!dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key)) { + fprintf(stderr, "Key Error\n"); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + + if (is_binary) { + dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, "ay", + &dict_val); + dbus_message_iter_open_container(&dict_val, DBUS_TYPE_ARRAY, "y", + &array_val); + if (!dbus_message_iter_append_fixed_array(&array_val, DBUS_TYPE_BYTE, + &value, value_length)) { + fprintf(stderr, "Byte Array Value Error\n"); + dbus_message_iter_close_container(&dict_val, &array_val); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + } else { + dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, &dict_val); + if (!dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_STRING, + &value)) { + fprintf(stderr, "String Value Error\n"); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + } + + if (is_binary) + dbus_message_iter_close_container(&dict_val, &array_val); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + return (1); + +error: + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + return (0); +} + +static void zbar_send_dbus(int type, const char *sigvalue, unsigned int length, + int is_binary) +{ + DBusMessage *msg; + DBusMessageIter args, dict; + DBusConnection *conn; + const char *type_name; + const char *value_key = is_binary ? "BinaryData" : "Data"; + DBusError err; + int ret; + dbus_uint32_t serial = 0; + + // initialise the error value + dbus_error_init(&err); + + // connect to the DBUS system bus, and check for errors + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Connection Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (NULL == conn) { + fprintf(stderr, "Connection Null\n"); + return; + } + + // register our name on the bus, and check for errors + ret = dbus_bus_request_name(conn, "org.linuxtv.Zbar", + DBUS_NAME_FLAG_REPLACE_EXISTING, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { + return; + } + + // create a signal & check for errors + msg = dbus_message_new_signal( + "/org/linuxtv/Zbar1/Code", // object name of the signal + "org.linuxtv.Zbar1.Code", // interface name of the signal + "Code"); // name of the signal + if (NULL == msg) { + fprintf(stderr, "Message Null\n"); + return; + } + + // append arguments onto signal + dbus_message_iter_init_append(msg, &args); + if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", + &dict)) { + fprintf(stderr, "Out Of Dict Container Memory!\n"); + dbus_message_unref(msg); + return; + } + + type_name = zbar_get_symbol_name(type); + if (!dict_add_property(&dict, "Type", type_name, 0, 0)) { + fprintf(stderr, "Out Of Property Memory!\n"); + dbus_message_unref(msg); + return; + } + + if (!dict_add_property(&dict, value_key, sigvalue, length, is_binary)) { + fprintf(stderr, "Out Of Property Memory!\n"); + dbus_message_unref(msg); + return; + } + + dbus_message_iter_close_container(&args, &dict); + + // send the message and flush the connection + if (!dbus_connection_send(conn, msg, &serial)) { + fprintf(stderr, "Out Of Memory!\n"); + dbus_message_unref(msg); + return; + } + + dbus_connection_flush(conn); + dbus_bus_release_name(conn, "org.linuxtv.Zbar", &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Release Error (%s)\n", err.message); + dbus_error_free(&err); + } + + // free the message + dbus_message_unref(msg); +} + +static void zbar_send_code_via_dbus(zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + + if (!sym) + return; + for (; sym; sym = zbar_symbol_next(sym)) { + if (zbar_symbol_get_count(sym)) + continue; + + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + if (type == ZBAR_PARTIAL) + continue; + + int is_binary = 0; + zbar_image_scanner_get_config(iscn, type, ZBAR_CFG_BINARY, &is_binary); + + zbar_send_dbus(type, zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), is_binary); + } +} +#endif + +#define movedelta(dx, dy) \ + do { \ + x += (dx); \ + y += (dy); \ + p += (dx) + ((uintptr_t)(dy)*w); \ + } while (0); + +static void *_zbar_scan_image(zbar_image_scanner_t *iscn, zbar_image_t *img) +{ + zbar_symbol_set_t *syms; + const uint8_t *data; + zbar_scanner_t *scn = iscn->scn; + unsigned w, h, cx1, cy1; + int density; + char filter; + int nean, naddon; + + /* timestamp image + * FIXME prefer video timestamp + */ + iscn->time = _zbar_timer_now(); + +#if ENABLE_QRCODE == 1 + _zbar_qr_reset(iscn->qr); +#endif + +#if ENABLE_SQCODE == 1 + _zbar_sq_reset(iscn->sq); +#endif + + /* image must be in grayscale format */ + if (img->format != fourcc('Y', '8', '0', '0') && + img->format != fourcc('G', 'R', 'E', 'Y')) + return NULL; + iscn->img = img; + + /* recycle previous scanner and image results */ + zbar_image_scanner_recycle_image(iscn, img); + syms = iscn->syms; + if (!syms) { + syms = iscn->syms = _zbar_symbol_set_create(); + STAT(syms_new); + zbar_symbol_set_ref(syms, 1); + } else + zbar_symbol_set_ref(syms, 2); + img->syms = syms; + + w = img->width; + h = img->height; + cx1 = img->crop_x + img->crop_w; + assert(cx1 <= w); + cy1 = img->crop_y + img->crop_h; + assert(cy1 <= h); + data = img->data; + + zbar_image_write_png(img, "debug.png"); + svg_open("debug.svg", 0, 0, w, h); + svg_image("debug.png", w, h); + + zbar_scanner_new_scan(scn); + + density = CFG(iscn, ZBAR_CFG_Y_DENSITY); + if (density > 0) { + const uint8_t *p = data; + int x = 0, y = 0; + + int border = (((img->crop_h - 1) % density) + 1) / 2; + if (border > img->crop_h / 2) + border = img->crop_h / 2; + border += img->crop_y; + assert(border <= h); + svg_group_start("scanner", 0, 1, 1, 0, 0); + iscn->dy = 0; + + movedelta(img->crop_x, border); + iscn->v = y; + + while (y < cy1) { + int cx0 = img->crop_x; + ; + zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p); + svg_path_start("vedge", 1. / 32, 0, y + 0.5); + iscn->dx = iscn->du = 1; + iscn->umin = cx0; + while (x < cx1) { + uint8_t d = *p; + movedelta(1, 0); + zbar_scan_y(scn, d); + } + ASSERT_POS; + quiet_border(iscn); + svg_path_end(); + + movedelta(-1, density); + iscn->v = y; + if (y >= cy1) + break; + + zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p); + svg_path_start("vedge", -1. / 32, w, y + 0.5); + iscn->dx = iscn->du = -1; + iscn->umin = cx1; + while (x >= cx0) { + uint8_t d = *p; + movedelta(-1, 0); + zbar_scan_y(scn, d); + } + ASSERT_POS; + quiet_border(iscn); + svg_path_end(); + + movedelta(1, density); + iscn->v = y; + } + svg_group_end(); + } + iscn->dx = 0; + + density = CFG(iscn, ZBAR_CFG_X_DENSITY); + if (density > 0) { + const uint8_t *p = data; + int x = 0, y = 0; + + int border = (((img->crop_w - 1) % density) + 1) / 2; + if (border > img->crop_w / 2) + border = img->crop_w / 2; + border += img->crop_x; + assert(border <= w); + svg_group_start("scanner", 90, 1, -1, 0, 0); + movedelta(border, img->crop_y); + iscn->v = x; + + while (x < cx1) { + int cy0 = img->crop_y; + zprintf(128, "img_y+: %04d,%04d @%p\n", x, y, p); + svg_path_start("vedge", 1. / 32, 0, x + 0.5); + iscn->dy = iscn->du = 1; + iscn->umin = cy0; + while (y < cy1) { + uint8_t d = *p; + movedelta(0, 1); + zbar_scan_y(scn, d); + } + ASSERT_POS; + quiet_border(iscn); + svg_path_end(); + + movedelta(density, -1); + iscn->v = x; + if (x >= cx1) + break; + + zprintf(128, "img_y-: %04d,%04d @%p\n", x, y, p); + svg_path_start("vedge", -1. / 32, h, x + 0.5); + iscn->dy = iscn->du = -1; + iscn->umin = cy1; + while (y >= cy0) { + uint8_t d = *p; + movedelta(0, -1); + zbar_scan_y(scn, d); + } + ASSERT_POS; + quiet_border(iscn); + svg_path_end(); + + movedelta(density, 1); + iscn->v = x; + } + svg_group_end(); + } + iscn->dy = 0; + iscn->img = NULL; + +#if ENABLE_QRCODE == 1 + _zbar_qr_decode(iscn->qr, iscn, img); +#endif + +#if ENABLE_SQCODE == 1 + sq_handler(iscn); + _zbar_sq_decode(iscn->sq, iscn, img); +#endif + + /* FIXME tmp hack to filter bad EAN results */ + /* FIXME tmp hack to merge simple case EAN add-ons */ + filter = (!iscn->enable_cache && + (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1)); + nean = 0; + naddon = 0; + if (syms->nsyms) { + zbar_symbol_t **symp; + for (symp = &syms->head; *symp;) { + zbar_symbol_t *sym = *symp; + if (sym->cache_count <= 0 && + ((sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) || + sym->type == ZBAR_DATABAR || sym->type == ZBAR_DATABAR_EXP || + sym->type == ZBAR_CODABAR)) { + if ((sym->type == ZBAR_CODABAR || filter) && sym->quality < 4) { + if (iscn->enable_cache) { + /* revert cache update */ + zbar_symbol_t *entry = cache_lookup(iscn, sym); + if (entry) + entry->cache_count--; + else + assert(0); + } + + /* recycle */ + *symp = sym->next; + syms->nsyms--; + sym->next = NULL; + _zbar_image_scanner_recycle_syms(iscn, sym); + continue; + } else if (sym->type < ZBAR_COMPOSITE && + sym->type != ZBAR_ISBN10) { + if (sym->type > ZBAR_EAN5) + nean++; + else + naddon++; + } + } + symp = &sym->next; + } + + if (nean == 1 && naddon == 1 && iscn->ean_config) { + int datalen; + zbar_symbol_t *ean_sym; + /* create container symbol for composite result */ + zbar_symbol_t *ean = NULL, *addon = NULL; + for (symp = &syms->head; *symp;) { + zbar_symbol_t *sym = *symp; + if (sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) { + /* move to composite */ + *symp = sym->next; + syms->nsyms--; + sym->next = NULL; + if (sym->type <= ZBAR_EAN5) + addon = sym; + else + ean = sym; + } else + symp = &sym->next; + } + assert(ean); + assert(addon); + + datalen = ean->datalen + addon->datalen + 1; + ean_sym = + _zbar_image_scanner_alloc_sym(iscn, ZBAR_COMPOSITE, datalen); + ean_sym->orient = ean->orient; + ean_sym->syms = _zbar_symbol_set_create(); + memcpy(ean_sym->data, ean->data, ean->datalen); + memcpy(ean_sym->data + ean->datalen, addon->data, + addon->datalen + 1); + ean_sym->syms->head = ean; + ean->next = addon; + ean_sym->syms->nsyms = 2; + _zbar_image_scanner_add_sym(iscn, ean_sym); + } + } + return syms; +} + +int zbar_scan_image(zbar_image_scanner_t *iscn, zbar_image_t *img) +{ + zbar_symbol_set_t *syms; + zbar_image_t *inv = NULL; + + syms = _zbar_scan_image(iscn, img); + if (!syms) + return -1; + + if (!syms->nsyms && TEST_CFG(iscn, ZBAR_CFG_TEST_INVERTED)) { + inv = _zbar_image_copy(img, 1); + if (inv) { + if (iscn->cache) { + /* recycle all cached syms, if any */ + _zbar_image_scanner_recycle_syms(iscn, iscn->cache); + iscn->cache = NULL; + } + syms = _zbar_scan_image(iscn, inv); + _zbar_image_swap_symbols(img, inv); + } + } + + if (syms->nsyms && iscn->handler) + iscn->handler(img, iscn->userdata); +#ifdef HAVE_DBUS + if (iscn->is_dbus_enabled) + zbar_send_code_via_dbus(iscn, img); +#endif + + svg_close(); + + if (inv) + zbar_image_destroy(inv); + + return (syms->nsyms); +} + +int zbar_image_scanner_request_dbus(zbar_image_scanner_t *scanner, + int req_dbus_enabled) +{ +#ifdef HAVE_DBUS + scanner->is_dbus_enabled = req_dbus_enabled; + return 0; +#else + return 1; +#endif +} + +#ifdef DEBUG_SVG +/* FIXME lame...*/ +#include "svg.c" +#endif diff --git a/zbar/img_scanner.h b/zbar/img_scanner.h new file mode 100644 index 0000000..2f7f9f1 --- /dev/null +++ b/zbar/img_scanner.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _IMG_SCANNER_H_ +#define _IMG_SCANNER_H_ + +#include <zbar.h> + +/* internal image scanner APIs for 2D readers */ + +extern zbar_symbol_t *_zbar_image_scanner_alloc_sym(zbar_image_scanner_t *, + zbar_symbol_type_t, int); +extern void _zbar_image_scanner_add_sym(zbar_image_scanner_t *, + zbar_symbol_t *); +extern void _zbar_image_scanner_recycle_syms(zbar_image_scanner_t *, + zbar_symbol_t *); + +#endif diff --git a/zbar/jpeg.c b/zbar/jpeg.c new file mode 100644 index 0000000..d6b2e59 --- /dev/null +++ b/zbar/jpeg.c @@ -0,0 +1,245 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <assert.h> /* FIXME tmp debug */ +#include <jerror.h> +#include <setjmp.h> +#include <stdio.h> + +#include <jpeglib.h> + +#undef HAVE_STDLIB_H +#include <zbar.h> +#include "image.h" +#include "video.h" + +#define HAVE_LONGJMP +#ifdef HAVE_LONGJMP + +typedef struct errenv_s { + struct jpeg_error_mgr err; + int valid; + jmp_buf env; +} errenv_t; + +void zbar_jpeg_error(j_common_ptr cinfo) +{ + errenv_t *jerr = (errenv_t *)cinfo->err; + assert(jerr->valid); + jerr->valid = 0; + longjmp(jerr->env, 1); + assert(0); +} + +#endif + +typedef struct zbar_src_mgr_s { + struct jpeg_source_mgr src; + const zbar_image_t *img; +} zbar_src_mgr_t; + +static const JOCTET fake_eoi[2] = { 0xff, JPEG_EOI }; + +void init_source(j_decompress_ptr cinfo) +{ + /* buffer/length refer to compressed data */ + /* FIXME find img */ + const zbar_image_t *img = ((zbar_src_mgr_t *)cinfo->src)->img; + cinfo->src->next_input_byte = img->data; + cinfo->src->bytes_in_buffer = img->datalen; +} + +boolean fill_input_buffer(j_decompress_ptr cinfo) +{ + /* buffer underrun error case */ + cinfo->src->next_input_byte = fake_eoi; + cinfo->src->bytes_in_buffer = 2; + return (1); +} + +void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes > 0) { + if (num_bytes < cinfo->src->bytes_in_buffer) { + cinfo->src->next_input_byte += num_bytes; + cinfo->src->bytes_in_buffer -= num_bytes; + } else { + fill_input_buffer(cinfo); + } + } +} + +void term_source(j_decompress_ptr cinfo) +{ + /* nothing todo */ +} + +struct jpeg_decompress_struct *_zbar_jpeg_decomp_create(void) +{ + j_decompress_ptr cinfo = calloc(1, sizeof(struct jpeg_decompress_struct)); + if (!cinfo) + return (NULL); + + errenv_t *jerr = calloc(1, sizeof(errenv_t)); + if (!jerr) { + free(cinfo); + return (NULL); + } + + cinfo->err = jpeg_std_error(&jerr->err); + jerr->err.error_exit = zbar_jpeg_error; + + jerr->valid = 1; + if (setjmp(jerr->env)) { + jpeg_destroy_decompress(cinfo); + + /* FIXME TBD save error to errinfo_t */ + (*cinfo->err->output_message)((j_common_ptr)cinfo); + + free(jerr); + free(cinfo); + return (NULL); + } + + jpeg_create_decompress(cinfo); + + jerr->valid = 0; + return (cinfo); +} + +void _zbar_jpeg_decomp_destroy(struct jpeg_decompress_struct *cinfo) +{ + if (cinfo->err) { + free(cinfo->err); + cinfo->err = NULL; + } + if (cinfo->src) { + free(cinfo->src); + cinfo->src = NULL; + } + /* FIXME can this error? */ + jpeg_destroy_decompress(cinfo); + free(cinfo); +} + +/* invoke libjpeg to decompress JPEG format to luminance plane */ +void _zbar_convert_jpeg_to_y(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + /* create decompressor, or use cached video stream decompressor */ + errenv_t *jerr = NULL; + j_decompress_ptr cinfo; + if (!src->src) + cinfo = _zbar_jpeg_decomp_create(); + else { + cinfo = src->src->jpeg; + assert(cinfo); + } + if (!cinfo) + goto error; + + jerr = (errenv_t *)cinfo->err; + jerr->valid = 1; + if (setjmp(jerr->env)) { + /* FIXME TBD save error to src->src->err */ + (*cinfo->err->output_message)((j_common_ptr)cinfo); + if (dst->data) { + free((void *)dst->data); + dst->data = NULL; + } + dst->datalen = 0; + goto error; + } + + /* setup input image */ + if (!cinfo->src) { + cinfo->src = calloc(1, sizeof(zbar_src_mgr_t)); + cinfo->src->init_source = init_source; + cinfo->src->fill_input_buffer = fill_input_buffer; + cinfo->src->skip_input_data = skip_input_data; + cinfo->src->resync_to_restart = jpeg_resync_to_restart; + cinfo->src->term_source = term_source; + } + cinfo->src->next_input_byte = NULL; + cinfo->src->bytes_in_buffer = 0; + ((zbar_src_mgr_t *)cinfo->src)->img = src; + + int rc = jpeg_read_header(cinfo, TRUE); + zprintf(30, "header: %s\n", (rc == 2) ? "tables-only" : "normal"); + + /* supporting color with jpeg became...complicated, + * so we skip that for now + */ + cinfo->out_color_space = JCS_GRAYSCALE; + + /* FIXME set scaling based on dst->{width,height} + * then pass bigger buffer... + */ + + jpeg_start_decompress(cinfo); + + /* adjust dst image parameters to match(?) decompressor */ + if (dst->width < cinfo->output_width) { + dst->width = cinfo->output_width; + if (dst->crop_x + dst->crop_w > dst->width) + dst->crop_w = dst->width - dst->crop_x; + } + if (dst->height < cinfo->output_height) { + dst->height = cinfo->output_height; + if (dst->crop_y + dst->crop_h > dst->height) + dst->crop_h = dst->height - dst->crop_y; + } + unsigned long datalen = (cinfo->output_width * cinfo->output_height * + cinfo->out_color_components); + + zprintf(24, "dst=%dx%d %lx src=%dx%d %lx dct=%x\n", dst->width, dst->height, + dst->datalen, src->width, src->height, src->datalen, + cinfo->dct_method); + if (!dst->data) { + dst->datalen = datalen; + dst->data = malloc(dst->datalen); + dst->cleanup = zbar_image_free_data; + } else + assert(datalen <= dst->datalen); + if (!dst->data) + return; + + unsigned bpl = dst->width * cinfo->output_components; + JSAMPROW buf = (void *)dst->data; + JSAMPARRAY line = &buf; + for (; cinfo->output_scanline < cinfo->output_height; buf += bpl) { + jpeg_read_scanlines(cinfo, line, 1); + /* FIXME pad out to dst->width */ + } + + /* FIXME always do this? */ + jpeg_finish_decompress(cinfo); + +error: + if (jerr) + jerr->valid = 0; + if (!src->src && cinfo) + /* cleanup only if we allocated locally */ + _zbar_jpeg_decomp_destroy(cinfo); +} diff --git a/zbar/libzbar.rc b/zbar/libzbar.rc new file mode 100644 index 0000000..c241f82 --- /dev/null +++ b/zbar/libzbar.rc @@ -0,0 +1,31 @@ +#include <config.h> +#include <winver.h> + +#define STR(s) #s +#define XSTR(s) STR(s) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION LIB_VERSION_MAJOR, LIB_VERSION_MINOR, LIB_VERSION_REVISION, 0 + PRODUCTVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ZBar Bar Code Reader" + VALUE "Company Name", "ZBar Bar Code Reader" + VALUE "InternalName", "libzbar" + VALUE "OriginalFilename", "libzbar-" XSTR(LIB_VERSION_MAJOR) ".dll" + + VALUE "FileVersion", XSTR(LIB_VERSION_MAJOR) "." XSTR(LIB_VERSION_MINOR) "." XSTR(LIB_VERSION_REVISION) + VALUE "ProductVersion", PACKAGE_VERSION + + VALUE "FileDescription", "Bar code reader library" + + VALUE "LegalCopyright", "Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>" + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 0x04e4 + } +} diff --git a/zbar/misc.c b/zbar/misc.c new file mode 100644 index 0000000..d4add31 --- /dev/null +++ b/zbar/misc.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "misc.h" +#include "error.h" + +static int module_initialized = 0; +static errinfo_t err; + +static void module_init() +{ + if (module_initialized) + return; + err_init(&err, ZBAR_MOD_UNKNOWN); + module_initialized = 1; +} + +void resolution_list_init(resolution_list_t *list) +{ + module_init(); + list->cnt = 0; + // an empty list consists of 1 zeroed element + list->resolutions = calloc(1, sizeof(resolution_t)); + if (!list->resolutions) { + err_capture(&err, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating resources"); + } +} + +void resolution_list_cleanup(resolution_list_t *list) +{ + free(list->resolutions); +} + +void resolution_list_add(resolution_list_t *list, resolution_t *resolution) +{ + list->cnt++; + list->resolutions = + realloc(list->resolutions, (list->cnt + 1) * sizeof(resolution_t)); + if (!list->resolutions) { + err_capture(&err, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating resources"); + } + list->resolutions[list->cnt - 1] = *resolution; + memset(&list->resolutions[list->cnt], 0, sizeof(resolution_t)); +} + +void get_closest_resolution(resolution_t *resolution, resolution_list_t *list) +{ + resolution_t *test_res; + long min_diff = 0; + long idx_best = -1; // the index of best resolution in resolutions + int i = 0; + for (test_res = list->resolutions; !is_struct_null(test_res); test_res++) { + long diff; + if (resolution->cx) { + diff = test_res->cx - resolution->cx; + if (diff < 0) + diff = -diff; + } else { + // empty resolution, looking for the biggest + diff = -test_res->cx; + } + if (idx_best < 0 || diff < min_diff) { + idx_best = i; + min_diff = diff; + } + i++; + } + + if (idx_best >= 0) { + *resolution = list->resolutions[idx_best]; + } +} + +int is_struct_null_fun(const void *pdata, const int len) +{ + int i; + for (i = 0; i < len; i++) { + if (((char *)pdata)[i] != 0) { + return 0; + } + } + return 1; +} diff --git a/zbar/misc.h b/zbar/misc.h new file mode 100644 index 0000000..d6e7d96 --- /dev/null +++ b/zbar/misc.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _MISC_H_ +#define _MISC_H_ + +struct resolution_s { + long cx; + long cy; +}; +typedef struct resolution_s resolution_t; + +struct resolution_list_s { + resolution_t *resolutions; + long cnt; +}; +typedef struct resolution_list_s resolution_list_t; + +void resolution_list_init(resolution_list_t *list); +void resolution_list_cleanup(resolution_list_t *list); +void resolution_list_add(resolution_list_t *list, resolution_t *resolution); +/// Fill <code>resolution</code> with the closest resolution found in +/// <code>list</code>. +/** If <code>list</code> is empty, + * the <code>resolution</code> is unchanged. + * If <code>resolution</code> is empty, + * the biggest resolution is chosen. */ +void get_closest_resolution(resolution_t *resolution, resolution_list_t *list); + +/// Returns 1 if the struct is null, otherwise 0 +int is_struct_null_fun(const void *pdata, const int len); +/// Returns 1 if the struct is null, otherwise 0 +#define is_struct_null(pdata) is_struct_null_fun(pdata, sizeof(*pdata)) + +#endif diff --git a/zbar/mutex.h b/zbar/mutex.h new file mode 100644 index 0000000..4514b88 --- /dev/null +++ b/zbar/mutex.h @@ -0,0 +1,160 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_MUTEX_H_ +#define _ZBAR_MUTEX_H_ + +#include "config.h" + +#include <assert.h> + +/* simple platform mutex abstraction + */ + +#if defined(_WIN32) + +#include <windows.h> + +#define DEBUG_LOCKS +#ifdef DEBUG_LOCKS + +typedef struct zbar_mutex_s { + int count; + CRITICAL_SECTION mutex; +} zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ + lock->count = 1; + InitializeCriticalSection(&lock->mutex); + return (0); +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + DeleteCriticalSection(&lock->mutex); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + EnterCriticalSection(&lock->mutex); + if (lock->count++ < 1) + assert(0); + return (0); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + if (lock->count-- <= 1) + assert(0); + LeaveCriticalSection(&lock->mutex); + return (0); +} + +#else + +typedef CRITICAL_SECTION zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ + InitializeCriticalSection(lock); + return (0); +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + DeleteCriticalSection(lock); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + EnterCriticalSection(lock); + return (0); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + LeaveCriticalSection(lock); + return (0); +} + +#endif + +#elif defined(HAVE_LIBPTHREAD) + +#include <pthread.h> + +typedef pthread_mutex_t zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ +#ifdef DEBUG_LOCKS + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + int rc = pthread_mutex_init(lock, &attr); + pthread_mutexattr_destroy(&attr); + return (rc); +#else + return (pthread_mutex_init(lock, NULL)); +#endif +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + pthread_mutex_destroy(lock); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + int rc = pthread_mutex_lock(lock); +#ifdef DEBUG_LOCKS + assert(!rc); +#endif + /* FIXME save system code */ + /*rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_LOCKING, __func__, + "unable to lock processor");*/ + return (rc); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + int rc = pthread_mutex_unlock(lock); +#ifdef DEBUG_LOCKS + assert(!rc); +#endif + /* FIXME save system code */ + return (rc); +} + +#else + +typedef int zbar_mutex_t[0]; + +#define _zbar_mutex_init(l) -1 +#define _zbar_mutex_destroy(l) +#define _zbar_mutex_lock(l) 0 +#define _zbar_mutex_unlock(l) 0 + +#endif + +#endif diff --git a/zbar/processor.c b/zbar/processor.c new file mode 100644 index 0000000..cecd182 --- /dev/null +++ b/zbar/processor.c @@ -0,0 +1,727 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "processor.h" +#include "image.h" +#include "img_scanner.h" +#include "window.h" + +static inline int proc_enter(zbar_processor_t *proc) +{ + _zbar_mutex_lock(&proc->mutex); + return (_zbar_processor_lock(proc)); +} + +static inline int proc_leave(zbar_processor_t *proc) +{ + int rc = _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_open(zbar_processor_t *proc) +{ + /* arbitrary default */ + int width = 640, height = 480; + if (proc->video) { + width = zbar_video_get_width(proc->video); + height = zbar_video_get_height(proc->video); + } + return (_zbar_processor_open(proc, "zbar barcode reader", width, height)); +} + +/* API lock is already held */ +int _zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc; + uint32_t force_fmt = proc->force_output; + if (img) { + uint32_t format; + zbar_image_t *tmp; + int nsyms; + if (proc->dumping) { + zbar_image_write(proc->window->image, "zbar"); + proc->dumping = 0; + } + + format = zbar_image_get_format(img); + zprintf(16, "processing: %.4s(%08" PRIx32 ") %dx%d @%p\n", + (char *)&format, format, zbar_image_get_width(img), + zbar_image_get_height(img), zbar_image_get_data(img)); + + /* FIXME locking all other interfaces while processing is conservative + * but easier for now and we don't expect this to take long... + */ + tmp = zbar_image_convert(img, fourcc('Y', '8', '0', '0')); + if (!tmp) + goto error; + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + zbar_image_scanner_recycle_image(proc->scanner, img); + nsyms = zbar_scan_image(proc->scanner, tmp); + _zbar_image_swap_symbols(img, tmp); + + zbar_image_destroy(tmp); + tmp = NULL; + if (nsyms < 0) + goto error; + + proc->syms = zbar_image_scanner_get_results(proc->scanner); + if (proc->syms) + zbar_symbol_set_ref(proc->syms, 1); + + if (_zbar_verbosity >= 8) { + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + while (sym) { + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + int count = zbar_symbol_get_count(sym); + zprintf(8, "%s: %s (%d pts) (dir=%d) (q=%d) (%s)\n", + zbar_get_symbol_name(type), zbar_symbol_get_data(sym), + zbar_symbol_get_loc_size(sym), + zbar_symbol_get_orientation(sym), + zbar_symbol_get_quality(sym), + (count < 0) ? "uncertain" : + (count > 0) ? "duplicate" : + "new"); + sym = zbar_symbol_next(sym); + } + } + + if (nsyms) { + /* FIXME only call after filtering */ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_notify(proc, EVENT_OUTPUT); + _zbar_mutex_unlock(&proc->mutex); + if (proc->handler) + proc->handler(img, proc->userdata); + } + + if (force_fmt) { + zbar_symbol_set_t *syms = img->syms; + img = zbar_image_convert(img, force_fmt); + if (!img) + goto error; + img->syms = syms; + zbar_symbol_set_ref(syms, 1); + } + } + + /* display to window if enabled */ + rc = 0; + if (proc->window) { + if ((rc = zbar_window_draw(proc->window, img))) + err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + if (force_fmt && img) + zbar_image_destroy(img); + return (rc); + +error: + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "unknown image format")); +} + +int _zbar_processor_handle_input(zbar_processor_t *proc, int input) +{ + int event = EVENT_INPUT; + switch (input) { + case -1: + event |= EVENT_CANCELED; + _zbar_processor_set_visible(proc, 0); + err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "user closed display window"); + break; + + case 'd': + proc->dumping = 1; + return (0); + + case '+': + case '=': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl + 1); + } + break; + + case '-': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl - 1); + } + break; + } + + _zbar_mutex_lock(&proc->mutex); + proc->input = input; + if (input == -1 && proc->visible && proc->streaming) + /* also cancel outstanding output waiters */ + event |= EVENT_OUTPUT; + _zbar_processor_notify(proc, event); + _zbar_mutex_unlock(&proc->mutex); + return (input); +} + +#ifdef ZTHREAD + +static ZTHREAD proc_video_thread(void *arg) +{ + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->video_thread; + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_init(thread); + zprintf(4, "spawned video thread\n"); + + while (thread->started) { + zbar_image_t *img; + /* wait for video stream to be active */ + while (thread->started && !proc->streaming) + _zbar_event_wait(&thread->notify, &proc->mutex, NULL); + if (!thread->started) + break; + + /* blocking capture image from video */ + _zbar_mutex_unlock(&proc->mutex); + img = zbar_video_next_image(proc->video); + _zbar_mutex_lock(&proc->mutex); + + if (!img && !proc->streaming) + continue; + else if (!img) + /* FIXME could abort streaming and keep running? */ + break; + + /* acquire API lock */ + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (thread->started && proc->streaming) + _zbar_process_image(proc, img); + + zbar_image_destroy(img); + + _zbar_mutex_lock(&proc->mutex); + /* release API lock */ + _zbar_processor_unlock(proc, 0); + } + + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +static ZTHREAD proc_input_thread(void *arg) +{ + int rc = 0; + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->input_thread; + if (proc->window && proc_open(proc)) + goto done; + + _zbar_mutex_lock(&proc->mutex); + thread->running = 1; + _zbar_event_trigger(&thread->activity); + zprintf(4, "spawned input thread\n"); + + rc = 0; + while (thread->started && rc >= 0) { + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_input_wait(proc, &thread->notify, -1); + _zbar_mutex_lock(&proc->mutex); + } + + _zbar_mutex_unlock(&proc->mutex); + _zbar_processor_close(proc); + _zbar_mutex_lock(&proc->mutex); + +done: + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +#endif + +zbar_processor_t *zbar_processor_create(int threaded) +{ + zbar_processor_t *proc = calloc(1, sizeof(zbar_processor_t)); + if (!proc) + return (NULL); + err_init(&proc->err, ZBAR_MOD_PROCESSOR); + + proc->scanner = zbar_image_scanner_create(); + if (!proc->scanner) { + free(proc); + return (NULL); + } + + proc->threaded = !_zbar_mutex_init(&proc->mutex) && threaded; + _zbar_processor_init(proc); + return (proc); +} + +void zbar_processor_destroy(zbar_processor_t *proc) +{ + proc_waiter_t *w, *next; + + zbar_processor_init(proc, NULL, 0); + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + if (proc->scanner) { + zbar_image_scanner_destroy(proc->scanner); + proc->scanner = NULL; + } + + _zbar_mutex_destroy(&proc->mutex); + _zbar_processor_cleanup(proc); + + assert(!proc->wait_head); + assert(!proc->wait_tail); + assert(!proc->wait_next); + + for (w = proc->free_waiter; w; w = next) { + next = w->next; + _zbar_event_destroy(&w->notify); + free(w); + } + + err_cleanup(&proc->err); + free(proc); +} + +int zbar_processor_init(zbar_processor_t *proc, const char *dev, + int enable_display) +{ + int rc = 0; + int video_threaded, input_threaded; + + if (proc->video) + zbar_processor_set_active(proc, 0); + + if (proc->window && !proc->input_thread.started) + _zbar_processor_close(proc); + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_stop(&proc->input_thread, &proc->mutex); + _zbar_thread_stop(&proc->video_thread, &proc->mutex); + + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + zbar_window_destroy(proc->window); + proc->window = NULL; + } + + rc = 0; + if (proc->video) { + zbar_video_destroy(proc->video); + proc->video = NULL; + } + + if (!dev && !enable_display) + /* nothing to do */ + goto done; + + if (enable_display) { + proc->window = zbar_window_create(); + if (!proc->window) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating window resources"); + goto done; + } + } + + if (dev) { + proc->video = zbar_video_create(); + if (!proc->video) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating video resources"); + goto done; + } + if (proc->req_width || proc->req_height) + zbar_video_request_size(proc->video, proc->req_width, + proc->req_height); + if (proc->req_intf) + zbar_video_request_interface(proc->video, proc->req_intf); + if ((proc->req_iomode && + zbar_video_request_iomode(proc->video, proc->req_iomode)) || + zbar_video_open(proc->video, dev)) { + rc = err_copy(proc, proc->video); + goto done; + } + } + + /* spawn blocking video thread */ + video_threaded = + (proc->threaded && proc->video && zbar_video_get_fd(proc->video) < 0); + if (video_threaded && + _zbar_thread_start(&proc->video_thread, proc_video_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning video thread"); + goto done; + } + + /* spawn input monitor thread */ + input_threaded = + (proc->threaded && (proc->window || (proc->video && !video_threaded))); + if (input_threaded && + _zbar_thread_start(&proc->input_thread, proc_input_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning input thread"); + goto done; + } + + if (proc->window && !input_threaded && (rc = proc_open(proc))) + goto done; + + if (proc->video && proc->force_input) { + if (zbar_video_init(proc->video, proc->force_input)) + rc = err_copy(proc, proc->video); + } else if (proc->video) { + int retry = -1; + if (proc->window) { + retry = zbar_negotiate_format(proc->video, proc->window); + if (retry) + fprintf(stderr, + "WARNING: no compatible input to output format\n" + "...trying again with output disabled\n"); + } + if (retry) + retry = zbar_negotiate_format(proc->video, NULL); + + if (retry) { + zprintf(1, "ERROR: no compatible %s format\n", + (proc->video) ? "video input" : "window output"); + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no compatible image format"); + } + } + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +zbar_image_data_handler_t * +zbar_processor_set_data_handler(zbar_processor_t *proc, + zbar_image_data_handler_t *handler, + const void *userdata) +{ + zbar_image_data_handler_t *result = NULL; + proc_enter(proc); + + result = proc->handler; + proc->handler = handler; + proc->userdata = userdata; + + proc_leave(proc); + return (result); +} + +void zbar_processor_set_userdata(zbar_processor_t *proc, void *userdata) +{ + _zbar_mutex_lock(&proc->mutex); + proc->userdata = userdata; + _zbar_mutex_unlock(&proc->mutex); +} + +void *zbar_processor_get_userdata(const zbar_processor_t *proc) +{ + void *userdata; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + _zbar_mutex_lock(&ncproc->mutex); + userdata = (void *)ncproc->userdata; + _zbar_mutex_unlock(&ncproc->mutex); + return (userdata); +} + +int zbar_processor_set_config(zbar_processor_t *proc, zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + int rc; + proc_enter(proc); + rc = zbar_image_scanner_set_config(proc->scanner, sym, cfg, val); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_control(zbar_processor_t *proc, const char *control_name, + int value) +{ + int rc; + int value_before, value_after; + proc_enter(proc); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_before) == + 0) + zprintf(0, "value of %s before a set: %d\n", control_name, + value_before); + rc = zbar_video_set_control(proc->video, control_name, value); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_after) == + 0) + zprintf(0, "value of %s after a set: %d\n", control_name, + value_after); + proc_leave(proc); + return (rc); +} + +int zbar_processor_get_control(zbar_processor_t *proc, const char *control_name, + int *value) +{ + int rc; + proc_enter(proc); + rc = zbar_video_get_control(proc->video, control_name, value); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + proc_enter(proc); + proc->req_width = width; + proc->req_height = height; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_interface(zbar_processor_t *proc, int ver) +{ + proc_enter(proc); + proc->req_intf = ver; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_iomode(zbar_processor_t *proc, int iomode) +{ + proc_enter(proc); + proc->req_iomode = iomode; + proc_leave(proc); + return (0); +} + +int zbar_processor_force_format(zbar_processor_t *proc, unsigned long input, + unsigned long output) +{ + proc_enter(proc); + proc->force_input = input; + proc->force_output = output; + proc_leave(proc); + return (0); +} + +int zbar_processor_is_visible(zbar_processor_t *proc) +{ + int visible; + proc_enter(proc); + visible = proc->window && proc->visible; + proc_leave(proc); + return (visible); +} + +int zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + int rc = 0; + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + if (proc->video) + rc = _zbar_processor_set_size(proc, + zbar_video_get_width(proc->video), + zbar_video_get_height(proc->video)); + if (!rc) + rc = _zbar_processor_set_visible(proc, visible); + + if (!rc) + proc->visible = (visible != 0); + } else if (visible) + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "processor display window not initialized"); + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +const zbar_symbol_set_t * +zbar_processor_get_results(const zbar_processor_t *proc) +{ + const zbar_symbol_set_t *syms; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + proc_enter(ncproc); + syms = proc->syms; + if (syms) + zbar_symbol_set_ref(syms, 1); + proc_leave(ncproc); + return (syms); +} + +int zbar_processor_user_wait(zbar_processor_t *proc, int timeout) +{ + int rc = -1; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->visible || proc->streaming || timeout >= 0) { + zbar_timer_t timer; + rc = _zbar_processor_wait(proc, EVENT_INPUT, + _zbar_timer_init(&timer, timeout)); + } + + if (!proc->visible) + rc = err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "display window not available for input"); + + if (rc > 0) + rc = proc->input; + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_active(zbar_processor_t *proc, int active) +{ + int rc; + proc_enter(proc); + + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_scanner_enable_cache(proc->scanner, active); + + rc = zbar_video_enable(proc->video, active); + if (!rc) { + _zbar_mutex_lock(&proc->mutex); + proc->streaming = active; + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_enable(proc); + } else + err_copy(proc, proc->video); + + if (!proc->streaming && proc->window) { + if (zbar_window_draw(proc->window, NULL) && !rc) + rc = err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + _zbar_mutex_lock(&proc->mutex); + if (proc->video_thread.started) + _zbar_event_trigger(&proc->video_thread.notify); + +done: + proc_leave(proc); + return (rc); +} + +int zbar_process_one(zbar_processor_t *proc, int timeout) +{ + int streaming, rc; + zbar_timer_t timer; + + proc_enter(proc); + streaming = proc->streaming; + _zbar_mutex_unlock(&proc->mutex); + + rc = 0; + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + + if (!streaming) { + rc = zbar_processor_set_active(proc, 1); + if (rc) + goto done; + } + + rc = _zbar_processor_wait(proc, EVENT_OUTPUT, + _zbar_timer_init(&timer, timeout)); + + if (!streaming && zbar_processor_set_active(proc, 0)) + rc = -1; + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc = 0; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (img && proc->window) + rc = _zbar_processor_set_size(proc, zbar_image_get_width(img), + zbar_image_get_height(img)); + if (!rc) { + zbar_image_scanner_enable_cache(proc->scanner, 0); + zbar_image_scanner_request_dbus(proc->scanner, proc->is_dbus_enabled); + rc = _zbar_process_image(proc, img); + if (proc->streaming) + zbar_image_scanner_enable_cache(proc->scanner, 1); + } + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_dbus(zbar_processor_t *proc, int req_dbus_enabled) +{ +#ifdef HAVE_DBUS + proc_enter(proc); + proc->is_dbus_enabled = req_dbus_enabled; + proc_leave(proc); + return (0); +#else + return (1); +#endif +} diff --git a/zbar/processor.h b/zbar/processor.h new file mode 100644 index 0000000..a8289e0 --- /dev/null +++ b/zbar/processor.h @@ -0,0 +1,128 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _PROCESSOR_H_ +#define _PROCESSOR_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <zbar.h> +#include "error.h" +#include "event.h" +#include "thread.h" + +/* max time to wait for input before looking for the next frame. + * only used in unthreaded mode with blocking (non-pollable) video. + * NB subject to precision of whatever timer is in use + */ +#define MAX_INPUT_BLOCK 15 /*ms*/ + +/* platform specific state wrapper */ +typedef struct processor_state_s processor_state_t; + +/* specific notification tracking */ +typedef struct proc_waiter_s { + struct proc_waiter_s *next; + zbar_event_t notify; + zbar_thread_id_t requester; + unsigned events; +} proc_waiter_t; + +/* high-level API events */ +#define EVENT_INPUT 0x01 /* user input */ +#define EVENT_OUTPUT 0x02 /* decoded output data available */ +#define EVENT_CANCELED 0x80 /* cancellation flag */ +#define EVENTS_PENDING (EVENT_INPUT | EVENT_OUTPUT) + +struct zbar_processor_s { + errinfo_t err; /* error reporting */ + const void *userdata; /* application data */ + + zbar_video_t *video; /* input video device abstraction */ + zbar_window_t *window; /* output window abstraction */ + zbar_image_scanner_t *scanner; /* barcode scanner */ + + zbar_image_data_handler_t *handler; /* application data handler */ + + unsigned req_width, req_height; /* application requested video size */ + int req_intf, req_iomode; /* application requested interface */ + uint32_t force_input; /* force input format (debug) */ + uint32_t force_output; /* force format conversion (debug) */ + + int input; /* user input status */ + + /* state flags */ + int threaded; + int visible; /* output window mapped to display */ + int streaming; /* video enabled */ + int dumping; /* debug image dump */ + + void *display; /* X display connection */ + unsigned long xwin; /* toplevel window */ + + zbar_thread_t input_thread; /* video input handler */ + zbar_thread_t video_thread; /* window event handler */ + + const zbar_symbol_set_t *syms; /* previous decode results */ + + zbar_mutex_t mutex; /* shared data mutex */ + + /* API serialization lock */ + int lock_level; + zbar_thread_id_t lock_owner; + proc_waiter_t *wait_head, *wait_tail, *wait_next; + proc_waiter_t *free_waiter; + + processor_state_t *state; + + int is_dbus_enabled; /* dbus enabled flag */ +}; + +/* processor lock API */ +extern int _zbar_processor_lock(zbar_processor_t *); +extern int _zbar_processor_unlock(zbar_processor_t *, int); +extern void _zbar_processor_notify(zbar_processor_t *, unsigned); +extern int _zbar_processor_wait(zbar_processor_t *, unsigned, zbar_timer_t *); + +/* platform API */ +extern int _zbar_processor_init(zbar_processor_t *); +extern int _zbar_processor_cleanup(zbar_processor_t *); +extern int _zbar_processor_input_wait(zbar_processor_t *, zbar_event_t *, int); +extern int _zbar_processor_enable(zbar_processor_t *); + +extern int _zbar_process_image(zbar_processor_t *, zbar_image_t *); +extern int _zbar_processor_handle_input(zbar_processor_t *, int); + +/* windowing platform API */ +extern int _zbar_processor_open(zbar_processor_t *, char *, unsigned, unsigned); +extern int _zbar_processor_close(zbar_processor_t *); +extern int _zbar_processor_set_visible(zbar_processor_t *, int); +extern int _zbar_processor_set_size(zbar_processor_t *, unsigned, unsigned); +extern int _zbar_processor_invalidate(zbar_processor_t *); + +#endif diff --git a/zbar/processor/lock.c b/zbar/processor/lock.c new file mode 100644 index 0000000..2668b4e --- /dev/null +++ b/zbar/processor/lock.c @@ -0,0 +1,228 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <assert.h> +#include "processor.h" + +/* the processor api lock is a recursive mutex with added capabilities + * to completely drop all lock levels before blocking and atomically + * unblock a waiting set. the lock is implemented using a variation + * of the "specific notification pattern" [cargill], which makes it + * easy to provide these features across platforms with consistent, + * predictable semantics. probably overkill, but additional overhead + * associated with this mechanism should fall in the noise, as locks + * are only exchanged O(frame/image) + * + * [cargill] + * http://www.profcon.com/profcon/cargill/jgf/9809/SpecificNotification.html + */ + +static inline proc_waiter_t *proc_waiter_queue(zbar_processor_t *proc) +{ + proc_waiter_t *waiter = proc->free_waiter; + if (waiter) { + proc->free_waiter = waiter->next; + waiter->events = 0; + } else { + waiter = calloc(1, sizeof(proc_waiter_t)); + _zbar_event_init(&waiter->notify); + } + + waiter->next = NULL; + waiter->requester = _zbar_thread_self(); + + if (proc->wait_head) + proc->wait_tail->next = waiter; + else + proc->wait_head = waiter; + proc->wait_tail = waiter; + return (waiter); +} + +static inline proc_waiter_t *proc_waiter_dequeue(zbar_processor_t *proc) +{ + proc_waiter_t *prev = proc->wait_next, *waiter; + if (prev) + waiter = prev->next; + else + waiter = proc->wait_head; + while (waiter && (waiter->events & EVENTS_PENDING)) { + prev = waiter; + proc->wait_next = waiter; + waiter = waiter->next; + } + + if (waiter) { + if (prev) + prev->next = waiter->next; + else + proc->wait_head = waiter->next; + if (!waiter->next) + proc->wait_tail = prev; + waiter->next = NULL; + + proc->lock_level = 1; + proc->lock_owner = waiter->requester; + } + return (waiter); +} + +static inline void proc_waiter_release(zbar_processor_t *proc, + proc_waiter_t *waiter) +{ + if (waiter) { + waiter->next = proc->free_waiter; + proc->free_waiter = waiter; + } +} + +int _zbar_processor_lock(zbar_processor_t *proc) +{ + proc_waiter_t *waiter; + if (!proc->lock_level) { + proc->lock_owner = _zbar_thread_self(); + proc->lock_level = 1; + return (0); + } + + if (_zbar_thread_is_self(proc->lock_owner)) { + proc->lock_level++; + return (0); + } + + waiter = proc_waiter_queue(proc); + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc_waiter_release(proc, waiter); + return (0); +} + +int _zbar_processor_unlock(zbar_processor_t *proc, int all) +{ + assert(proc->lock_level > 0); + assert(_zbar_thread_is_self(proc->lock_owner)); + + if (all) + proc->lock_level = 0; + else + proc->lock_level--; + + if (!proc->lock_level) { + proc_waiter_t *waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } + return (0); +} + +void _zbar_processor_notify(zbar_processor_t *proc, unsigned events) +{ + proc_waiter_t *waiter; + proc->wait_next = NULL; + for (waiter = proc->wait_head; waiter; waiter = waiter->next) + waiter->events = + ((waiter->events & ~events) | (events & EVENT_CANCELED)); + + if (!proc->lock_level) { + waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } +} + +static inline int proc_wait_unthreaded(zbar_processor_t *proc, + proc_waiter_t *waiter, + zbar_timer_t *timeout) +{ + int rc; + int blocking = proc->streaming && zbar_video_get_fd(proc->video) < 0; + _zbar_mutex_unlock(&proc->mutex); + + rc = 1; + while (rc > 0 && (waiter->events & EVENTS_PENDING)) { + int reltime; + /* FIXME lax w/the locking (though shouldn't matter...) */ + if (blocking) { + zbar_image_t *img = zbar_video_next_image(proc->video); + if (!img) { + rc = -1; + break; + } + + /* FIXME reacquire API lock! (refactor w/video thread?) */ + _zbar_mutex_lock(&proc->mutex); + _zbar_process_image(proc, img); + zbar_image_destroy(img); + _zbar_mutex_unlock(&proc->mutex); + } + reltime = _zbar_timer_check(timeout); + if (blocking && (reltime < 0 || reltime > MAX_INPUT_BLOCK)) + reltime = MAX_INPUT_BLOCK; + rc = _zbar_processor_input_wait(proc, NULL, reltime); + } + _zbar_mutex_lock(&proc->mutex); + return (rc); +} + +int _zbar_processor_wait(zbar_processor_t *proc, unsigned events, + zbar_timer_t *timeout) +{ + int save_level; + proc_waiter_t *waiter; + int rc; + + _zbar_mutex_lock(&proc->mutex); + save_level = proc->lock_level; + waiter = proc_waiter_queue(proc); + waiter->events = events & EVENTS_PENDING; + + _zbar_processor_unlock(proc, 1); + if (proc->threaded) + rc = _zbar_event_wait(&waiter->notify, &proc->mutex, timeout); + else + rc = proc_wait_unthreaded(proc, waiter, timeout); + + if (rc <= 0 || !proc->threaded) { + /* reacquire api lock */ + waiter->events &= EVENT_CANCELED; + proc->wait_next = NULL; + if (!proc->lock_level) { + proc_waiter_t *w = proc_waiter_dequeue(proc); + assert(w == waiter); + } else + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + } + if (rc > 0 && (waiter->events & EVENT_CANCELED)) + rc = -1; + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc->lock_level = save_level; + proc_waiter_release(proc, waiter); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} diff --git a/zbar/processor/null.c b/zbar/processor/null.c new file mode 100644 index 0000000..5ca8d42 --- /dev/null +++ b/zbar/processor/null.c @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "processor.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with output window support")); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *name, unsigned w, + unsigned h) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int vis) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + return (null_error(proc, __func__)); +} diff --git a/zbar/processor/posix.c b/zbar/processor/posix.c new file mode 100644 index 0000000..06b8d9a --- /dev/null +++ b/zbar/processor/posix.c @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "posix.h" +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include "processor.h" + +static inline int proc_sleep(int timeout) +{ + assert(timeout > 0); + struct timespec sleepns, remns; + sleepns.tv_sec = timeout / 1000; + sleepns.tv_nsec = (timeout % 1000) * 1000000; + while (nanosleep(&sleepns, &remns) && errno == EINTR) + sleepns = remns; + return (1); +} + +int _zbar_event_init(zbar_event_t *event) +{ + event->state = 0; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_init(&event->cond, NULL); +#endif + return (0); +} + +void _zbar_event_destroy(zbar_event_t *event) +{ + event->state = -1; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_destroy(&event->cond); +#endif +} + +/* lock must be held */ +void _zbar_event_trigger(zbar_event_t *event) +{ + event->state = 1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_broadcast(&event->cond); +#endif + if (event->pollfd >= 0) { + unsigned i = 0; /* unused */ + if (write(event->pollfd, &i, sizeof(unsigned)) < 0) + perror(""); + event->pollfd = -1; + } +} + +#ifdef HAVE_LIBPTHREAD + +/* lock must be held */ +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = 0; + while (!rc && !event->state) { + if (!timeout) + rc = pthread_cond_wait(&event->cond, lock); + else { + struct timespec *timer; +#if _POSIX_TIMERS > 0 + timer = timeout; +#else + struct timespec tmp; + tmp.tv_sec = timeout->tv_sec; + tmp.tv_nsec = timeout->tv_usec * 1000; + timer = &tmp; +#endif + rc = pthread_cond_timedwait(&event->cond, lock, timer); + } + } + + /* consume/reset event */ + event->state = 0; + + if (!rc) + return (1); /* got event */ + if (rc == ETIMEDOUT) + return (0); /* timed out */ + return (-1); /* error (FIXME save info) */ +} + +int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg, + zbar_mutex_t *lock) +{ + if (thr->started || thr->running) + return (-1 /*FIXME*/); + thr->started = 1; + _zbar_event_init(&thr->notify); + _zbar_event_init(&thr->activity); + + int rc = 0; + _zbar_mutex_lock(lock); + if (pthread_create(&thr->tid, NULL, proc, arg) || + _zbar_event_wait(&thr->activity, lock, NULL) < 0 || !thr->running) { + thr->started = 0; + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + /*rc = err_capture_num(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, + __func__, "spawning thread", rc);*/ + rc = -1 /*FIXME*/; + } + _zbar_mutex_unlock(lock); + return (rc); +} + +int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock) +{ + if (thr->started) { + thr->started = 0; + _zbar_event_trigger(&thr->notify); + while (thr->running) + /* FIXME time out and abandon? */ + _zbar_event_wait(&thr->activity, lock, NULL); + pthread_join(thr->tid, NULL); + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + } + return (0); +} + +#else + +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = !event->state; + if (rc) { + if (!timeout) + /* FIXME was that error or hang? */ + return (-1); + + int sleep = _zbar_timer_check(timeout); + if (sleep) + proc_sleep(sleep); + } + + rc = !event->state; + + /* consume/reset event */ + event->state = 0; + + return (rc); +} + +#endif + +/* used by poll interface. lock is already held */ +static int proc_video_handler(zbar_processor_t *proc, int i) +{ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_t *img = NULL; + if (proc->streaming) { + /* not expected to block */ + img = zbar_video_next_image(proc->video); + if (img) + _zbar_process_image(proc, img); + } + + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + if (img) + zbar_image_destroy(img); + return (0); +} + +static inline void proc_cache_polling(processor_state_t *state) +{ + /* make a thread-local copy of polling data */ + int n = state->thr_polling.num = state->polling.num; + alloc_polls(&state->thr_polling); + memcpy(state->thr_polling.fds, state->polling.fds, + n * sizeof(struct pollfd)); + memcpy(state->thr_polling.handlers, state->polling.handlers, + n * sizeof(poll_handler_t *)); +} + +static int proc_kick_handler(zbar_processor_t *proc, int i) +{ + processor_state_t *state = proc->state; + zprintf(5, "kicking %d fds\n", state->polling.num); + + unsigned junk[2]; + int rc = read(state->kick_fds[0], junk, 2 * sizeof(unsigned)); + + assert(proc->threaded); + _zbar_mutex_lock(&proc->mutex); + proc_cache_polling(proc->state); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_poll_inputs(zbar_processor_t *proc, int timeout) +{ + processor_state_t *state = proc->state; + if (state->pre_poll_handler) + state->pre_poll_handler(proc, -1); + + poll_desc_t *p = &state->thr_polling; + assert(p->num); + int rc = poll(p->fds, p->num, timeout); + if (rc <= 0) + /* FIXME detect and handle fatal errors (somehow) */ + return (rc); + int i; + for (i = p->num - 1; i >= 0; i--) + if (p->fds[i].revents) { + if (p->handlers[i]) + p->handlers[i](proc, i); + p->fds[i].revents = 0; /* debug */ + rc--; + } + assert(!rc); + return (1); +} + +int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event, + int timeout) +{ + processor_state_t *state = proc->state; + if (state->thr_polling.num) { + if (event) { + _zbar_mutex_lock(&proc->mutex); + event->pollfd = state->kick_fds[1]; + _zbar_mutex_unlock(&proc->mutex); + } + return (proc_poll_inputs(proc, timeout)); + } else if (timeout) + return (proc_sleep(timeout)); + return (-1); +} + +int _zbar_processor_init(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state = + calloc(1, sizeof(processor_state_t)); + state->kick_fds[0] = state->kick_fds[1] = -1; + + if (proc->threaded) { + /* FIXME check errors */ + if (pipe(state->kick_fds)) + return (err_capture(proc, SEV_FATAL, ZBAR_ERR_SYSTEM, __func__, + "failed to open pipe")); + add_poll(proc, state->kick_fds[0], proc_kick_handler); + proc_cache_polling(proc->state); + } + return (0); +} + +int _zbar_processor_cleanup(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state; + if (proc->threaded) { + close(state->kick_fds[0]); + close(state->kick_fds[1]); + state->kick_fds[0] = state->kick_fds[1] = -1; + } + if (state->polling.fds) { + free(state->polling.fds); + state->polling.fds = NULL; + if (!proc->threaded) + state->thr_polling.fds = NULL; + } + if (state->polling.handlers) { + free(state->polling.handlers); + state->polling.handlers = NULL; + if (!proc->threaded) + state->thr_polling.handlers = NULL; + } + if (state->thr_polling.fds) { + free(state->thr_polling.fds); + state->thr_polling.fds = NULL; + } + if (state->thr_polling.handlers) { + free(state->thr_polling.handlers); + state->thr_polling.handlers = NULL; + } + free(proc->state); + proc->state = NULL; + return (0); +} + +int _zbar_processor_enable(zbar_processor_t *proc) +{ + int vid_fd = zbar_video_get_fd(proc->video); + if (vid_fd < 0) + return (0); + + if (proc->streaming) + add_poll(proc, vid_fd, proc_video_handler); + else + remove_poll(proc, vid_fd); + /* FIXME failure recovery? */ + return (0); +} diff --git a/zbar/processor/posix.h b/zbar/processor/posix.h new file mode 100644 index 0000000..7ed05cc --- /dev/null +++ b/zbar/processor/posix.h @@ -0,0 +1,132 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _PROCESSOR_POSIX_H_ +#define _PROCESSOR_POSIX_H_ + +#include "processor.h" + +#ifdef HAVE_POLL_H +#include <poll.h> +#endif + +#ifdef HAVE_POLL_H +typedef int(poll_handler_t)(zbar_processor_t *, int); + +/* poll information */ +typedef struct poll_desc_s { + int num; /* number of descriptors */ + struct pollfd *fds; /* poll descriptors */ + poll_handler_t **handlers; /* poll handlers */ +} poll_desc_t; +#endif + +struct processor_state_s { +#ifdef HAVE_POLL_H + poll_desc_t polling; /* polling registration */ + poll_desc_t thr_polling; /* thread copy */ +#endif + int kick_fds[2]; /* poll kicker */ + poll_handler_t *pre_poll_handler; /* special case */ +}; + +#ifdef HAVE_POLL_H +static inline int alloc_polls(volatile poll_desc_t *p) +{ + p->fds = realloc(p->fds, p->num * sizeof(struct pollfd)); + p->handlers = realloc(p->handlers, p->num * sizeof(poll_handler_t *)); + /* FIXME should check for ENOMEM */ + return (0); +} + +static inline int add_poll(zbar_processor_t *proc, int fd, + poll_handler_t *handler) +{ + processor_state_t *state = proc->state; + + _zbar_mutex_lock(&proc->mutex); + + poll_desc_t *polling = &state->polling; + unsigned i = polling->num++; + zprintf(5, "[%d] fd=%d handler=%p\n", i, fd, handler); + if (!alloc_polls(polling)) { + memset(&polling->fds[i], 0, sizeof(struct pollfd)); + polling->fds[i].fd = fd; + polling->fds[i].events = POLLIN; + polling->handlers[i] = handler; + } else + i = -1; + + _zbar_mutex_unlock(&proc->mutex); + + if (proc->input_thread.started) { + assert(state->kick_fds[1] >= 0); + if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0) + return (-1); + } else if (!proc->threaded) { + state->thr_polling.num = polling->num; + state->thr_polling.fds = polling->fds; + state->thr_polling.handlers = polling->handlers; + } + return (i); +} + +static inline int remove_poll(zbar_processor_t *proc, int fd) +{ + processor_state_t *state = proc->state; + + _zbar_mutex_lock(&proc->mutex); + + poll_desc_t *polling = &state->polling; + int i; + for (i = polling->num - 1; i >= 0; i--) + if (polling->fds[i].fd == fd) + break; + zprintf(5, "[%d] fd=%d n=%d\n", i, fd, polling->num); + + if (i >= 0) { + if (i + 1 < polling->num) { + int n = polling->num - i - 1; + memmove(&polling->fds[i], &polling->fds[i + 1], + n * sizeof(struct pollfd)); + memmove(&polling->handlers[i], &polling->handlers[i + 1], + n * sizeof(poll_handler_t)); + } + polling->num--; + i = alloc_polls(polling); + } + + _zbar_mutex_unlock(&proc->mutex); + + if (proc->input_thread.started) { + if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0) + return (-1); + } else if (!proc->threaded) { + state->thr_polling.num = polling->num; + state->thr_polling.fds = polling->fds; + state->thr_polling.handlers = polling->handlers; + } + return (i); +} +#endif + +#endif diff --git a/zbar/processor/win.c b/zbar/processor/win.c new file mode 100644 index 0000000..48fa9ac --- /dev/null +++ b/zbar/processor/win.c @@ -0,0 +1,332 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <assert.h> +#if defined(_MSC_VER) +#include <WinSock2.h> +#endif +#include <windows.h> +#include "processor.h" + +#define WIN_STYLE \ + (WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX) +#define EXT_STYLE (WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW) + +struct processor_state_s { + ATOM registeredClass; +}; + +int _zbar_event_init(zbar_event_t *event) +{ + *event = CreateEvent(NULL, 0, 0, NULL); + return ((*event) ? 0 : -1); +} + +void _zbar_event_destroy(zbar_event_t *event) +{ + if (*event) { + CloseHandle(*event); + *event = NULL; + } +} + +void _zbar_event_trigger(zbar_event_t *event) +{ + SetEvent(*event); +} + +/* lock must be held */ +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc; + if (lock) + _zbar_mutex_unlock(lock); + rc = WaitForSingleObject(*event, _zbar_timer_check(timeout)); + if (lock) + _zbar_mutex_lock(lock); + + if (!rc) + return (1); /* got event */ + if (rc == WAIT_TIMEOUT) + return (0); /* timed out */ + return (-1); /* error (FIXME save info) */ +} + +int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg, + zbar_mutex_t *lock) +{ + HANDLE hthr; + int rc; + if (thr->started || thr->running) + return (-1 /*FIXME*/); + thr->started = 1; + _zbar_event_init(&thr->notify); + _zbar_event_init(&thr->activity); + + hthr = CreateThread(NULL, 0, proc, arg, 0, NULL); + rc = (!hthr || _zbar_event_wait(&thr->activity, NULL, NULL) < 0 || + !thr->running); + CloseHandle(hthr); + if (rc) { + thr->started = 0; + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + return (-1 /*FIXME*/); + } + return (0); +} + +int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock) +{ + if (thr->started) { + thr->started = 0; + _zbar_event_trigger(&thr->notify); + while (thr->running) + /* FIXME time out and abandon? */ + _zbar_event_wait(&thr->activity, lock, NULL); + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + } + return (0); +} + +static LRESULT CALLBACK win_handle_event(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) +{ + zbar_processor_t *proc = + (zbar_processor_t *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + /* initialized during window creation */ + if (message == WM_NCCREATE) { + proc = ((LPCREATESTRUCT)lparam)->lpCreateParams; + assert(proc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)proc); + proc->display = hwnd; + + zbar_window_attach(proc->window, proc->display, proc->xwin); + } else if (!proc) + return (DefWindowProc(hwnd, message, wparam, lparam)); + + switch (message) { + case WM_SIZE: { + RECT r; + GetClientRect(hwnd, &r); + zprintf(3, "WM_SIZE %ldx%ld\n", r.right, r.bottom); + assert(proc); + zbar_window_resize(proc->window, r.right, r.bottom); + InvalidateRect(hwnd, NULL, 0); + return (0); + } + + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + if (zbar_window_redraw(proc->window)) { + HDC hdc = GetDC(hwnd); + RECT r; + GetClientRect(hwnd, &r); + FillRect(hdc, &r, GetStockObject(BLACK_BRUSH)); + ReleaseDC(hwnd, hdc); + } + EndPaint(hwnd, &ps); + return (0); + } + + case WM_CHAR: { + _zbar_processor_handle_input(proc, wparam); + return (0); + } + + case WM_LBUTTONDOWN: { + _zbar_processor_handle_input(proc, 1); + return (0); + } + + case WM_MBUTTONDOWN: { + _zbar_processor_handle_input(proc, 2); + return (0); + } + + case WM_RBUTTONDOWN: { + _zbar_processor_handle_input(proc, 3); + return (0); + } + + case WM_CLOSE: { + zprintf(3, "WM_CLOSE\n"); + _zbar_processor_handle_input(proc, -1); + return (1); + } + + case WM_DESTROY: { + zprintf(3, "WM_DESTROY\n"); + proc->display = NULL; + zbar_window_attach(proc->window, NULL, 0); + return (0); + } + } + return (DefWindowProc(hwnd, message, wparam, lparam)); +} + +static inline int win_handle_events(zbar_processor_t *proc) +{ + int rc = 0; + while (1) { + MSG msg; + rc = PeekMessage(&msg, proc->display, 0, 0, PM_NOYIELD | PM_REMOVE); + if (!rc) + return (0); + if (rc < 0) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to obtain event")); + + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +int _zbar_processor_init(zbar_processor_t *proc) +{ + proc->state = calloc(1, sizeof(processor_state_t)); + return (0); +} + +int _zbar_processor_cleanup(zbar_processor_t *proc) +{ + free(proc->state); + proc->state = 0; + return (0); +} + +int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event, + int timeout) +{ + int n = (event) ? 1 : 0; + int rc = MsgWaitForMultipleObjects(n, event, 0, timeout, QS_ALLINPUT); + + if (rc == n) { + if (win_handle_events(proc) < 0) + return (-1); + return (1); + } + if (!rc) + return (1); + if (rc == WAIT_TIMEOUT) + return (0); + return (-1); +} + +int _zbar_processor_enable(zbar_processor_t *proc) +{ + return (0); +} + +static inline ATOM win_register_class(HINSTANCE hmod) +{ + BYTE and_mask[1] = { 0xff }; + BYTE xor_mask[1] = { 0x00 }; + + WNDCLASSEX wc = { + sizeof(WNDCLASSEX), + 0, + }; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hInstance = hmod; + wc.lpfnWndProc = win_handle_event; + wc.lpszClassName = "_ZBar Class"; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hCursor = CreateCursor(hmod, 0, 0, 1, 1, and_mask, xor_mask); + + return (RegisterClassEx(&wc)); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width, + unsigned height) +{ + ATOM wca; + RECT r = { 0, 0, width, height }; + HMODULE hmod = NULL; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (void *)_zbar_processor_open, (HINSTANCE *)&hmod)) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to obtain module handle")); + + wca = win_register_class(hmod); + if (!wca) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to register window class")); + + proc->state->registeredClass = wca; + AdjustWindowRectEx(&r, WIN_STYLE, 0, EXT_STYLE); + proc->display = CreateWindowEx(EXT_STYLE, (LPCTSTR)(long)wca, "ZBar", + WIN_STYLE, CW_USEDEFAULT, CW_USEDEFAULT, + r.right - r.left, r.bottom - r.top, NULL, + NULL, hmod, proc); + + if (!proc->display) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to open window")); + return (0); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + if (proc->display) { + DestroyWindow(proc->display); + UnregisterClass((LPCTSTR)(long)proc->state->registeredClass, 0); + proc->display = NULL; + } + return (0); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + ShowWindow(proc->display, (visible) ? SW_SHOWNORMAL : SW_HIDE); + if (visible) + InvalidateRect(proc->display, NULL, 0); + /* no error conditions */ + return (0); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + RECT r = { 0, 0, width, height }; + AdjustWindowRectEx(&r, GetWindowLong(proc->display, GWL_STYLE), 0, + GetWindowLong(proc->display, GWL_EXSTYLE)); + if (!SetWindowPos( + proc->display, NULL, 0, 0, r.right - r.left, r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREPOSITION)) + return (-1 /*FIXME*/); + + return (0); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + if (!InvalidateRect(proc->display, NULL, 0)) + return (-1 /*FIXME*/); + + return (0); +} diff --git a/zbar/processor/x.c b/zbar/processor/x.c new file mode 100644 index 0000000..7cb1a6e --- /dev/null +++ b/zbar/processor/x.c @@ -0,0 +1,266 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "posix.h" +#include "processor.h" +#include "window.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +static inline int x_handle_event(zbar_processor_t *proc) +{ + XEvent ev; + XNextEvent(proc->display, &ev); + + switch (ev.type) { + case Expose: { + /* FIXME ignore when running(?) */ + XExposeEvent *exp = (XExposeEvent *)&ev; + while (1) { + assert(ev.type == Expose); + _zbar_window_expose(proc->window, exp->x, exp->y, exp->width, + exp->height); + if (!exp->count) + break; + XNextEvent(proc->display, &ev); + } + zbar_window_redraw(proc->window); + break; + } + + case ConfigureNotify: + zprintf(3, "resized to %d x %d\n", ev.xconfigure.width, + ev.xconfigure.height); + zbar_window_resize(proc->window, ev.xconfigure.width, + ev.xconfigure.height); + _zbar_processor_invalidate(proc); + break; + + case ClientMessage: + if ((ev.xclient.message_type == + XInternAtom(proc->display, "WM_PROTOCOLS", 0)) && + ev.xclient.format == 32 && + (ev.xclient.data.l[0] == + XInternAtom(proc->display, "WM_DELETE_WINDOW", 0))) { + zprintf(3, "WM_DELETE_WINDOW\n"); + return (_zbar_processor_handle_input(proc, -1)); + } + break; + + case KeyPress: { + KeySym key = XLookupKeysym(&ev.xkey, 0); + if (IsModifierKey(key)) + break; + if ((key & 0xff00) == 0xff00) + key &= 0x00ff; + zprintf(16, "KeyPress(%04lx)\n", key); + /* FIXME this doesn't really work... */ + return (_zbar_processor_handle_input(proc, key & 0xffff)); + } + case ButtonPress: { + zprintf(16, "ButtonPress(%d)\n", ev.xbutton.button); + int idx = 1; + switch (ev.xbutton.button) { + case Button2: + idx = 2; + break; + case Button3: + idx = 3; + break; + case Button4: + idx = 4; + break; + case Button5: + idx = 5; + break; + } + return (_zbar_processor_handle_input(proc, idx)); + } + + case DestroyNotify: + zprintf(16, "DestroyNotify\n"); + zbar_window_attach(proc->window, NULL, 0); + proc->xwin = 0; + return (0); + + default: + /* ignored */; + } + return (0); +} + +static int x_handle_events(zbar_processor_t *proc) +{ + int rc = 0; + while (rc >= 0 && XPending(proc->display)) + rc = x_handle_event(proc); + return (rc); +} + +static int x_connection_handler(zbar_processor_t *proc, int i) +{ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + x_handle_events(proc); + + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +static int x_internal_handler(zbar_processor_t *proc, int i) +{ + XProcessInternalConnection(proc->display, proc->state->polling.fds[i].fd); + x_connection_handler(proc, i); + return (0); +} + +static void x_internal_watcher(Display *display, XPointer client_arg, int fd, + Bool opening, XPointer *watch_arg) +{ + zbar_processor_t *proc = (void *)client_arg; + if (opening) + add_poll(proc, fd, x_internal_handler); + else + remove_poll(proc, fd); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width, + unsigned height) +{ + proc->display = XOpenDisplay(NULL); + if (!proc->display) + return (err_capture_str(proc, SEV_ERROR, ZBAR_ERR_XDISPLAY, __func__, + "unable to open X display", + XDisplayName(NULL))); + + add_poll(proc, ConnectionNumber(proc->display), x_connection_handler); + XAddConnectionWatch(proc->display, x_internal_watcher, (void *)proc); + /* must also flush X event queue before polling */ + proc->state->pre_poll_handler = x_connection_handler; + + int screen = DefaultScreen(proc->display); + XSetWindowAttributes attr; + attr.event_mask = + (ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask); + + proc->xwin = XCreateWindow(proc->display, RootWindow(proc->display, screen), + 0, 0, width, height, 0, CopyFromParent, + InputOutput, CopyFromParent, CWEventMask, &attr); + if (!proc->xwin) { + XCloseDisplay(proc->display); + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "creating window")); + } + + XStoreName(proc->display, proc->xwin, title); + + XClassHint *class_hint = XAllocClassHint(); + class_hint->res_name = "zbar"; + class_hint->res_class = "zbar"; + XSetClassHint(proc->display, proc->xwin, class_hint); + XFree(class_hint); + class_hint = NULL; + + Atom wm_delete_window = XInternAtom(proc->display, "WM_DELETE_WINDOW", 0); + if (wm_delete_window) + XSetWMProtocols(proc->display, proc->xwin, &wm_delete_window, 1); + + if (zbar_window_attach(proc->window, proc->display, proc->xwin)) + return (err_copy(proc, proc->window)); + return (0); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + if (proc->window) + zbar_window_attach(proc->window, NULL, 0); + + if (proc->display) { + if (proc->xwin) { + XDestroyWindow(proc->display, proc->xwin); + proc->xwin = 0; + } + proc->state->pre_poll_handler = NULL; + remove_poll(proc, ConnectionNumber(proc->display)); + XCloseDisplay(proc->display); + proc->display = NULL; + } + return (0); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + if (!proc->display || !proc->xwin) + return (0); + XClearArea(proc->display, proc->xwin, 0, 0, 0, 0, 1); + XFlush(proc->display); + return (0); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + if (!proc->display || !proc->xwin) + return (0); + + /* refuse to resize greater than (default) screen size */ + XWindowAttributes attr; + XGetWindowAttributes(proc->display, proc->xwin, &attr); + + int maxw = WidthOfScreen(attr.screen); + int maxh = HeightOfScreen(attr.screen); + int w, h; + if (width > maxw) { + h = (maxw * height + width - 1) / width; + w = maxw; + } else { + w = width; + h = height; + } + if (h > maxh) { + w = (maxh * width + height - 1) / height; + h = maxh; + } + assert(w <= maxw); + assert(h <= maxh); + + XResizeWindow(proc->display, proc->xwin, w, h); + XFlush(proc->display); + return (0); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + if (visible) + XMapRaised(proc->display, proc->xwin); + else + XUnmapWindow(proc->display, proc->xwin); + XFlush(proc->display); + return (0); +} diff --git a/zbar/qrcode.h b/zbar/qrcode.h new file mode 100644 index 0000000..01a2fa9 --- /dev/null +++ b/zbar/qrcode.h @@ -0,0 +1,64 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#ifndef _QRCODE_H_ +#define _QRCODE_H_ + +#include <zbar.h> + +typedef struct qr_reader qr_reader; + +typedef int qr_point[2]; +typedef struct qr_finder_line qr_finder_line; + +/*The number of bits of subpel precision to store image coordinates in. + This helps when estimating positions in low-resolution images, which may have + a module pitch only a pixel or two wide, making rounding errors matter a + great deal.*/ +#define QR_FINDER_SUBPREC (2) + +/*A line crossing a finder pattern. + Whether the line is horizontal or vertical is determined by context. + The offsts to various parts of the finder pattern are as follows: + |*****| |*****|*****|*****| |*****| + |*****| |*****|*****|*****| |*****| + ^ ^ ^ ^ + | | | | + | | | pos[v]+len+eoffs + | | pos[v]+len + | pos[v] + pos[v]-boffs + Here v is 0 for horizontal and 1 for vertical lines.*/ +struct qr_finder_line { + /*The location of the upper/left endpoint of the line. + The left/upper edge of the center section is used, since other lines must + cross in this region.*/ + qr_point pos; + /*The length of the center section. + This extends to the right/bottom of the center section, since other lines + must cross in this region.*/ + int len; + /*The offset to the midpoint of the upper/left section (part of the outside + ring), or 0 if we couldn't identify the edge of the beginning section. + We use the midpoint instead of the edge because it can be located more + reliably.*/ + int boffs; + /*The offset to the midpoint of the end section (part of the outside ring), + or 0 if we couldn't identify the edge of the end section. + We use the midpoint instead of the edge because it can be located more + reliably.*/ + int eoffs; +}; + +qr_reader *_zbar_qr_create(void); +void _zbar_qr_destroy(qr_reader *reader); +void _zbar_qr_reset(qr_reader *reader); + +int _zbar_qr_found_line(qr_reader *reader, int direction, + const qr_finder_line *line); +int _zbar_qr_decode(qr_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img); + +#endif diff --git a/zbar/qrcode/bch15_5.c b/zbar/qrcode/bch15_5.c new file mode 100644 index 0000000..2295ad9 --- /dev/null +++ b/zbar/qrcode/bch15_5.c @@ -0,0 +1,207 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "bch15_5.h" + +/*A cycle in GF(2**4) generated by alpha=(x**4+x+1). + It is extended an extra 16 entries to avoid some expensive mod operations.*/ +static const unsigned char gf16_exp[31] = { 1, 2, 4, 8, 3, 6, 12, 11, + 5, 10, 7, 14, 15, 13, 9, 1, + 2, 4, 8, 3, 6, 12, 11, 5, + 10, 7, 14, 15, 13, 9, 1 }; + +/*The location of each integer 1...16 in the cycle.*/ +static const signed char gf16_log[16] = { -1, 0, 1, 4, 2, 8, 5, 10, + 3, 14, 9, 7, 6, 13, 11, 12 }; + +/*Multiplication in GF(2**4) using logarithms.*/ +static unsigned gf16_mul(unsigned _a, unsigned _b) +{ + return _a == 0 || _b == 0 ? 0 : gf16_exp[gf16_log[_a] + gf16_log[_b]]; +} + +/*Division in GF(2**4) using logarithms. + The result when dividing by zero is undefined.*/ +static unsigned gf16_div(unsigned _a, unsigned _b) +{ + return _a == 0 ? 0 : gf16_exp[gf16_log[_a] + 15 - gf16_log[_b]]; +} + +/*Multiplication in GF(2**4) when the second argument is known to be non-zero + (proven by representing it by its logarithm).*/ +static unsigned gf16_hmul(unsigned _a, unsigned _logb) +{ + return _a == 0 ? 0 : gf16_exp[gf16_log[_a] + _logb]; +} + +/*The syndrome normally has five values, S_1 ... S_5. + We only calculate and store the odd ones in _s, since S_2=S_1**2 and + S_4=S_2**2. + Returns zero iff all the syndrome values are zero.*/ +static int bch15_5_calc_syndrome(unsigned _s[3], unsigned _y) +{ + unsigned p; + int i; + int j; + p = 0; + for (i = 0; i < 15; i++) + if (_y & 1 << i) + p ^= gf16_exp[i]; + _s[0] = p; + p = 0; + for (i = 0; i < 3; i++) + for (j = 0; j < 5; j++) + if (_y & 1 << 5 * i + j) + p ^= gf16_exp[j * 3]; + _s[1] = p; + p = 0; + for (i = 0; i < 5; i++) + for (j = 0; j < 3; j++) + if (_y & 1 << 3 * i + j) + p ^= gf16_exp[j * 5]; + _s[2] = p; + return _s[0] != 0 || _s[1] != 0 || _s[2] != 0; +} + +/*Compute the coefficients of the error-locator polynomial. + Returns the number of errors (the degree of the polynomial).*/ +static int bch15_5_calc_omega(unsigned _o[3], unsigned _s[3]) +{ + unsigned s02; + unsigned tt; + unsigned dd; + int d; + _o[0] = _s[0]; + s02 = gf16_mul(_s[0], _s[0]); + dd = _s[1] ^ gf16_mul(_s[0], s02); + tt = _s[2] ^ gf16_mul(s02, _s[1]); + _o[1] = dd ? gf16_div(tt, dd) : 0; + _o[2] = dd ^ gf16_mul(_s[0], _o[1]); + for (d = 3; d > 0 && !_o[d - 1]; d--) + ; + return d; +} + +/*Find the roots of the error polynomial. + Returns the number of roots found, or a negative value if the polynomial did + not have enough roots, indicating a decoding error.*/ +static int bch15_5_calc_epos(unsigned _epos[3], unsigned _s[3]) +{ + unsigned o[3]; + int nerrors; + int d; + int i; + d = bch15_5_calc_omega(o, _s); + nerrors = 0; + if (d == 1) + _epos[nerrors++] = gf16_log[o[0]]; + else if (d > 0) { + for (i = 0; i < 15; i++) { + int i2; + i2 = gf16_log[gf16_exp[i << 1]]; + if (!(gf16_exp[i + i2] ^ gf16_hmul(o[0], i2) ^ gf16_hmul(o[1], i) ^ + o[2])) { + _epos[nerrors++] = i; + } + } + if (nerrors < d) + return -1; + } + return nerrors; +} + +int bch15_5_correct(unsigned *_y) +{ + unsigned s[3]; + unsigned epos[3]; + unsigned y; + int nerrors; + int i; + y = *_y; + if (!bch15_5_calc_syndrome(s, y)) + return 0; + nerrors = bch15_5_calc_epos(epos, s); + if (nerrors > 0) { + /*If we had a non-zero syndrome value, we should always find at least one + error location, or we've got a decoding error.*/ + for (i = 0; i < nerrors; i++) + y ^= 1 << epos[i]; + /*If there were too many errors, we may not find enough roots to reduce the + syndrome to zero. + We could recompute it to check, but it's much faster just to check that + we have a valid codeword.*/ + if (bch15_5_encode(y >> 10) == y) { + /*Decoding succeeded.*/ + *_y = y; + return nerrors; + } + } + /*Decoding failed due to too many bit errors.*/ + return -1; +} + +unsigned bch15_5_encode(unsigned _x) +{ + return (-(_x & 1) & 0x0537) ^ (-(_x >> 1 & 1) & 0x0A6E) ^ + (-(_x >> 2 & 1) & 0x11EB) ^ (-(_x >> 3 & 1) & 0x23D6) ^ + (-(_x >> 4 & 1) & 0x429B); +} + +#if 0 +#include <stdio.h> + +static unsigned codes[32]; + +static int hamming(int _a,int _b){ + int d; + int n; + d=_a^_b; + for(n=0;d;n++)d&=d-1; + return n; +} + +static int closest(int _y){ + int min_i; + int min_d; + int i; + int d; + min_i=0; + min_d=hamming(_y,codes[0]); + for(i=1;i<32;i++){ + d=hamming(_y,codes[i]); + if(d<min_d){ + min_d=d; + min_i=i; + } + } + return codes[min_i]; +} + +int main(void){ + int i; + /*Print a list of the valid (uncorrupt) codewords.*/ + for(i=0;i<32;i++)codes[i]=bch15_5_encode(i); + for(i=0;i<32;i++)printf("0x%04X%s",codes[i],i+1<32?" ":"\n"); + /*Try to decode all receivable (possibly corrupt) codewords.*/ + for(i=0;i<0x8000;i++){ + unsigned y; + unsigned z; + int nerrors; + int j; + y=i; + nerrors=bch15_5_correct(&y); + z=closest(i); + if(nerrors<0){ + printf("0x%04X->Failed\n",i); + if(hamming(i,z)<=3)printf("Error: 0x%04X should map to 0x%04X\n",i,z); + } + else{ + printf("0x%04X->0x%04X\n",i,y); + if(z!=y)printf("Error: 0x%04X should map to 0x%04X\n",i,z); + } + } + return 0; +} +#endif diff --git a/zbar/qrcode/bch15_5.h b/zbar/qrcode/bch15_5.h new file mode 100644 index 0000000..bf95e12 --- /dev/null +++ b/zbar/qrcode/bch15_5.h @@ -0,0 +1,20 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_bch15_5_H) +#define _bch15_5_H (1) + +/*Encodes a raw 5-bit value _x into a 15-bit BCH(15,5) code. + This is capable of correcting up to 3 bit errors, and detecting as many as + 5 bit errors in some cases.*/ +unsigned bch15_5_encode(unsigned _x); + +/*Corrects the received code *_y, if possible. + The original data is located in the top five bits. + Returns the number of errors corrected, or a negative value if decoding + failed due to too many bit errors, in which case *_y is left unchanged.*/ +int bch15_5_correct(unsigned *_y); + +#endif diff --git a/zbar/qrcode/binarize.c b/zbar/qrcode/binarize.c new file mode 100644 index 0000000..33a6fe4 --- /dev/null +++ b/zbar/qrcode/binarize.c @@ -0,0 +1,646 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "binarize.h" +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include "image.h" +#include "util.h" + +#if 0 +/*Binarization based on~\cite{GPP06}. + @ARTICLE{GPP06, + author="Basilios Gatos and Ioannis E. Pratikakis and Stavros J. Perantonis", + title="Adaptive Degraded Document Image Binarization", + journal="Pattern Recognition", + volume=39, + number=3, + pages="317-327", + month=Mar, + year=2006 + }*/ + +#if 0 +/*Applies a 5x5 Wiener filter to the image, in-place, emphasizing differences + where the local variance is small, and de-emphasizing them where it is + large.*/ +void qr_wiener_filter(unsigned char *_img,int _width,int _height){ + unsigned *m_buf[8]; + unsigned *sn2_buf[8]; + unsigned char g; + int x; + int y; + if(_width<=0||_height<=0)return; + m_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*m_buf)); + sn2_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*sn2_buf)); + for(y=1;y<8;y++){ + m_buf[y]=m_buf[y-1]+_width+4; + sn2_buf[y]=sn2_buf[y-1]+_width+4; + } + for(y=-4;y<_height;y++){ + unsigned *pm; + unsigned *psn2; + int i; + int j; + pm=m_buf[y+2&7]; + psn2=sn2_buf[y+2&7]; + for(x=-4;x<_width;x++){ + unsigned m; + unsigned m2; + m=m2=0; + if(y>=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){ + g=_img[(y+i)*_width+x+j]; + m+=g; + m2+=g*g; + } + else for(i=0;i<5;i++)for(j=0;j<5;j++){ + g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; + m+=g; + m2+=g*g; + } + pm[x+4]=m; + psn2[x+4]=(m2*25-m*m); + } + pm=m_buf[y&7]; + if(y>=0)for(x=0;x<_width;x++){ + int sn2; + sn2=sn2_buf[y&7][x+2]; + if(sn2){ + int vn3; + int m; + /*Gatos et al. give the expression + mu+(s2-v2)*(g-mu)/s2 , + which we reduce to + mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , + g-(v2/s2)*g+(v2/s2)*mu , + g+(mu-g)*(v2/s2) . + However, s2 is much noisier than v2, and dividing by it often gives + extremely large adjustments, causing speckle near edges. + Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ + vn3=0; + for(i=-2;i<3;i++){ + psn2=sn2_buf[y+i&7]; + for(j=0;j<5;j++)vn3+=psn2[x+j]; + } + m=m_buf[y&7][x+2]; + vn3=vn3+1023>>10; + sn2=25*sn2+1023>>10; + if(vn3<sn2){ + int a; + g=_img[y*_width+x]; + a=(m-25*g)*vn3; + sn2*=25; + _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2)); + } + else _img[y*_width+x]=(unsigned char)(((m<<1)+25)/50); + } + } + } + free(sn2_buf[0]); + free(m_buf[0]); +} + +#else +/*Applies a 3x3 Wiener filter to the image, in-place, emphasizing differences + where the local variance is small, and de-emphasizing them where it is + large.*/ +void qr_wiener_filter(unsigned char *_img,int _width,int _height){ + unsigned *m_buf[4]; + unsigned *sn2_buf[4]; + unsigned char g; + int x; + int y; + if(_width<=0||_height<=0)return; + m_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*m_buf)); + sn2_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*sn2_buf)); + for(y=1;y<4;y++){ + m_buf[y]=m_buf[y-1]+_width+2; + sn2_buf[y]=sn2_buf[y-1]+_width+2; + } + for(y=-2;y<_height;y++){ + unsigned *pm; + unsigned *psn2; + int i; + int j; + pm=m_buf[y+1&3]; + psn2=sn2_buf[y+1&3]; + for(x=-2;x<_width;x++){ + unsigned m; + unsigned m2; + m=m2=0; + if(y>=0&&y<_height-2&&x>=0&&x<_width-2)for(i=0;i<3;i++)for(j=0;j<3;j++){ + g=_img[(y+i)*_width+x+j]; + m+=g; + m2+=g*g; + } + else for(i=0;i<3;i++)for(j=0;j<3;j++){ + g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; + m+=g; + m2+=g*g; + } + pm[x+2]=m; + psn2[x+2]=(m2*9-m*m); + } + pm=m_buf[y&3]; + if(y>=0)for(x=0;x<_width;x++){ + int sn2; + sn2=sn2_buf[y&3][x+1]; + if(sn2){ + int m; + int vn3; + /*Gatos et al. give the expression + mu+(s2-v2)*(g-mu)/s2 , + which we reduce to + mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , + g-(v2/s2)*g+(v2/s2)*mu , + g+(mu-g)*(v2/s2) . + However, s2 is much noisier than v2, and dividing by it often gives + extremely large adjustments, causing speckle near edges. + Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ + vn3=0; + for(i=-1;i<2;i++){ + psn2=sn2_buf[y+i&3]; + for(j=0;j<3;j++)vn3+=psn2[x+j]; + } + m=m_buf[y&3][x+1]; + vn3=vn3+31>>5; + sn2=9*sn2+31>>5; + if(vn3<sn2){ + int a; + g=_img[y*_width+x]; + a=m-9*g; + sn2*=9; + _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2)); + } + else _img[y*_width+x]=(unsigned char)(((m<<1)+9)/18); + } + } + } + free(sn2_buf[0]); + free(m_buf[0]); +} +#endif + +/*Computes a (conservative) foreground mask using the adaptive binarization + threshold given in~\cite{SP00}, but knocking the threshold parameter down to + k=0.2. + Note on dynamic range: we assume _width*_height<=0x1000000 (24 bits). + Returns the average background value. + @ARTICLE{SP00, + author="Jaakko J. Sauvola and Matti Pietik\"{a}inen", + title="Adaptive Document Image Binarization", + volume=33, + number=2, + pages="225--236", + month=Feb, + year=2000 + }*/ +static void qr_sauvola_mask(unsigned char *_mask,unsigned *_b,int *_nb, + const unsigned char *_img,int _width,int _height){ + unsigned b; + int nb; + b=0; + nb=0; + if(_width>0&&_height>0){ + unsigned *col_sums; + unsigned *col2_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned g; + unsigned g2; + int x; + int y; + /*We keep the window size fairly large to ensure it doesn't fit completely + inside the center of a finder pattern of a version 1 QR code at full + resolution.*/ + for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++); + for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++); + windw=1<<logwindw; + windh=1<<logwindh; + col_sums=(unsigned *)malloc(_width*sizeof(*col_sums)); + col2_sums=(unsigned *)malloc(_width*sizeof(*col2_sums)); + /*Initialize sums down each column.*/ + for(x=0;x<_width;x++){ + g=_img[x]; + g2=g*g; + col_sums[x]=(g<<logwindh-1)+g; + col2_sums[x]=(g2<<logwindh-1)+g2; + } + for(y=1;y<(windh>>1);y++){ + y1offs=QR_MINI(y,_height-1)*_width; + for(x=0;x<_width;x++){ + g=_img[y1offs+x]; + col_sums[x]+=g; + col2_sums[x]+=g*g; + } + } + for(y=0;y<_height;y++){ + unsigned m; + unsigned m2; + int x0; + int x1; + /*Initialize the sums over the window.*/ + m=(col_sums[0]<<logwindw-1)+col_sums[0]; + m2=(col2_sums[0]<<logwindw-1)+col2_sums[0]; + for(x=1;x<(windw>>1);x++){ + x1=QR_MINI(x,_width-1); + m+=col_sums[x1]; + m2+=col2_sums[x1]; + } + for(x=0;x<_width;x++){ + int d; + /*Perform the test against the threshold T = (m/n)*(1+k*(s/R-1)), + where n=windw*windh, s=sqrt((m2-(m*m)/n)/n), and R=128. + We don't actually compute the threshold directly, as that would + require a square root. + Instead we perform the equivalent test: + (m/n)*(m/n)*(m2/n-(m/n)*(m/n))/16 > (((1/k)*g-((1-k)/k)*(m/n))*32)**2 + R is split up across each side of the inequality to maximize the + dynamic range available for the right hand side, which requires + 31 bits in the worst case.*/ + /*(m/n)*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g + m*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g*n + m*sqrt((m2-m*m/n)/n) > 5*g*n-4*m<<7 + m*m*(m2*n-m*m) > (5*g*n-4*m<<7)**2*n*n || 5*g*n-4*m < 0 */ + g=_img[y*_width+x]; + d=(5*g<<logwindw+logwindh)-4*m; + if(d>=0){ + unsigned mm; + unsigned mms2; + unsigned d2; + mm=(m>>logwindw)*(m>>logwindh); + mms2=(m2-mm>>logwindw+logwindh)*(mm>>logwindw+logwindh)+15>>4; + d2=d>>logwindw+logwindh-5; + d2*=d2; + if(d2>=mms2){ + /*Update the background average.*/ + b+=g; + nb++; + _mask[y*_width+x]=0; + } + else _mask[y*_width+x]=0xFF; + } + else _mask[y*_width+x]=0xFF; + /*Update the window sums.*/ + if(x+1<_width){ + x0=QR_MAXI(0,x-(windw>>1)); + x1=QR_MINI(x+(windw>>1),_width-1); + m+=col_sums[x1]-col_sums[x0]; + m2+=col2_sums[x1]-col2_sums[x0]; + } + } + /*Update the column sums.*/ + if(y+1<_height){ + y0offs=QR_MAXI(0,y-(windh>>1))*_width; + y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; + for(x=0;x<_width;x++){ + g=_img[y0offs+x]; + col_sums[x]-=g; + col2_sums[x]-=g*g; + g=_img[y1offs+x]; + col_sums[x]+=g; + col2_sums[x]+=g*g; + } + } + } + free(col2_sums); + free(col_sums); + } + *_b=b; + *_nb=nb; +} + +/*Interpolates a background image given the source and a conservative + foreground mask. + If the current window contains no foreground pixels, the average background + value over the whole image is used. + Note on dynamic range: we assume _width*_height<=0x8000000 (23 bits). + Returns the average difference between the foreground and the interpolated + background.*/ +static void qr_interpolate_background(unsigned char *_dst, + int *_delta,int *_ndelta,const unsigned char *_img,const unsigned char *_mask, + int _width,int _height,unsigned _b,int _nb){ + int delta; + int ndelta; + delta=ndelta=0; + if(_width>0&&_height>0){ + unsigned *col_sums; + unsigned *ncol_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned b; + unsigned g; + int x; + int y; + b=_nb>0?((_b<<1)+_nb)/(_nb<<1):0xFF; + for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+15>>4);logwindw++); + for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+15>>4);logwindh++); + windw=1<<logwindw; + windh=1<<logwindh; + col_sums=(unsigned *)malloc(_width*sizeof(*col_sums)); + ncol_sums=(unsigned *)malloc(_width*sizeof(*ncol_sums)); + /*Initialize sums down each column.*/ + for(x=0;x<_width;x++){ + if(!_mask[x]){ + g=_img[x]; + col_sums[x]=(g<<logwindh-1)+g; + ncol_sums[x]=(1<<logwindh-1)+1; + } + else col_sums[x]=ncol_sums[x]=0; + } + for(y=1;y<(windh>>1);y++){ + y1offs=QR_MINI(y,_height-1)*_width; + for(x=0;x<_width;x++)if(!_mask[y1offs+x]){ + col_sums[x]+=_img[y1offs+x]; + ncol_sums[x]++; + } + } + for(y=0;y<_height;y++){ + unsigned n; + unsigned m; + int x0; + int x1; + /*Initialize the sums over the window.*/ + m=(col_sums[0]<<logwindw-1)+col_sums[0]; + n=(ncol_sums[0]<<logwindw-1)+ncol_sums[0]; + for(x=1;x<(windw>>1);x++){ + x1=QR_MINI(x,_width-1); + m+=col_sums[x1]; + n+=ncol_sums[x1]; + } + for(x=0;x<_width;x++){ + if(!_mask[y*_width+x])g=_img[y*_width+x]; + else{ + g=n>0?((m<<1)+n)/(n<<1):b; + delta+=(int)g-_img[y*_width+x]; + ndelta++; + } + _dst[y*_width+x]=(unsigned char)g; + /*Update the window sums.*/ + if(x+1<_width){ + x0=QR_MAXI(0,x-(windw>>1)); + x1=QR_MINI(x+(windw>>1),_width-1); + m+=col_sums[x1]-col_sums[x0]; + n+=ncol_sums[x1]-ncol_sums[x0]; + } + } + /*Update the column sums.*/ + if(y+1<_height){ + y0offs=QR_MAXI(0,y-(windh>>1))*_width; + y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; + for(x=0;x<_width;x++){ + if(!_mask[y0offs+x]){ + col_sums[x]-=_img[y0offs+x]; + ncol_sums[x]--; + } + if(!_mask[y1offs+x]){ + col_sums[x]+=_img[y1offs+x]; + ncol_sums[x]++; + } + } + } + } + free(ncol_sums); + free(col_sums); + } + *_delta=delta; + *_ndelta=ndelta; +} + +/*Parameters of the logistic sigmoid function that defines the threshold based + on the background intensity. + They should all be between 0 and 1.*/ +#define QR_GATOS_Q (0.7) +#define QR_GATOS_P1 (0.5) +#define QR_GATOS_P2 (0.8) + +/*Compute the final binarization mask according to Gatos et al.'s + method~\cite{GPP06}.*/ +static void qr_gatos_mask(unsigned char *_mask,const unsigned char *_img, + const unsigned char *_background,int _width,int _height, + unsigned _b,int _nb,int _delta,int _ndelta){ + unsigned thresh[256]; + unsigned g; + double delta; + double b; + int x; + int y; + /*Construct a lookup table for the thresholds. + This bit uses floating point, but doesn't need to do much calculation, so + emulation should be fine.*/ + b=_nb>0?(_b+0.5)/_nb:0xFF; + delta=_ndelta>0?(_delta+0.5)/_ndelta:0xFF; + for(g=0;g<256;g++){ + double d; + d=QR_GATOS_Q*delta*(QR_GATOS_P2+(1-QR_GATOS_P2)/ + (1+exp(2*(1+QR_GATOS_P1)/(1-QR_GATOS_P1)-4*g/(b*(1-QR_GATOS_P1))))); + if(d<1)d=1; + else if(d>0xFF)d=0xFF; + thresh[g]=(unsigned)floor(d); + } + /*Apply the adaptive threshold.*/ + for(y=0;y<_height;y++)for(x=0;x<_width;x++){ + g=_background[y*_width+x]; + /*_background[y*_width+x]=thresh[g];*/ + _mask[y*_width+x]=(unsigned char)(-(g-_img[y*_width+x]>thresh[g])&0xFF); + } + /*{ + FILE *fout; + fout=fopen("thresh.png","wb"); + image_write_png(_background,_width,_height,fout); + fclose(fout); + }*/ +} + +/*Binarizes a grayscale image.*/ +void qr_binarize(unsigned char *_img,int _width,int _height){ + unsigned char *mask; + unsigned char *background; + unsigned b; + int nb; + int delta; + int ndelta; + /*qr_wiener_filter(_img,_width,_height); + { + FILE *fout; + fout=fopen("wiener.png","wb"); + image_write_png(_img,_width,_height,fout); + fclose(fout); + }*/ + mask=(unsigned char *)malloc(_width*_height*sizeof(*mask)); + qr_sauvola_mask(mask,&b,&nb,_img,_width,_height); + /*{ + FILE *fout; + fout=fopen("foreground.png","wb"); + image_write_png(mask,_width,_height,fout); + fclose(fout); + }*/ + background=(unsigned char *)malloc(_width*_height*sizeof(*mask)); + qr_interpolate_background(background,&delta,&ndelta, + _img,mask,_width,_height,b,nb); + /*{ + FILE *fout; + fout=fopen("background.png","wb"); + image_write_png(background,_width,_height,fout); + fclose(fout); + }*/ + qr_gatos_mask(_img,_img,background,_width,_height,b,nb,delta,ndelta); + free(background); + free(mask); +} + +#else +/*The above algorithms are computationally expensive, and do not work as well + as the simple algorithm below. + Sauvola by itself does an excellent job of classifying regions outside the + QR code as background, which greatly reduces the chance of false alarms. + However, it also tends to over-shrink isolated black dots inside the code, + making them easy to miss with even slight mis-alignment. + Since the Gatos method uses Sauvola as input to its background interpolation + method, it cannot possibly mark any pixels as foreground which Sauvola + classified as background, and thus suffers from the same problem. + The following simple adaptive threshold method does not have this problem, + though it produces essentially random noise outside the QR code region. + QR codes are structured well enough that this does not seem to lead to any + actual false alarms in practice, and it allows many more codes to be + detected and decoded successfully than the Sauvola or Gatos binarization + methods.*/ + +/*A simplified adaptive thresholder. + This compares the current pixel value to the mean value of a (large) window + surrounding it.*/ +unsigned char *qr_binarize(const unsigned char *_img, int _width, int _height) +{ + unsigned char *mask = NULL; + if (_width > 0 && _height > 0) { + unsigned *col_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned g; + int x; + int y; + mask = (unsigned char *)malloc(_width * _height * sizeof(*mask)); + /*We keep the window size fairly large to ensure it doesn't fit completely + inside the center of a finder pattern of a version 1 QR code at full + resolution.*/ + for (logwindw = 4; logwindw < 8 && (1 << logwindw) < (_width + 7 >> 3); + logwindw++) + ; + for (logwindh = 4; logwindh < 8 && (1 << logwindh) < (_height + 7 >> 3); + logwindh++) + ; + windw = 1 << logwindw; + windh = 1 << logwindh; + col_sums = (unsigned *)malloc(_width * sizeof(*col_sums)); + /*Initialize sums down each column.*/ + for (x = 0; x < _width; x++) { + g = _img[x]; + col_sums[x] = (g << logwindh - 1) + g; + } + for (y = 1; y < (windh >> 1); y++) { + y1offs = QR_MINI(y, _height - 1) * _width; + for (x = 0; x < _width; x++) { + g = _img[y1offs + x]; + col_sums[x] += g; + } + } + for (y = 0; y < _height; y++) { + unsigned m; + int x0; + int x1; + /*Initialize the sum over the window.*/ + m = (col_sums[0] << logwindw - 1) + col_sums[0]; + for (x = 1; x < (windw >> 1); x++) { + x1 = QR_MINI(x, _width - 1); + m += col_sums[x1]; + } + for (x = 0; x < _width; x++) { + /*Perform the test against the threshold T = (m/n)-D, + where n=windw*windh and D=3.*/ + g = _img[y * _width + x]; + mask[y * _width + x] = -(g + 3 << logwindw + logwindh < m) & + 0xFF; + /*Update the window sum.*/ + if (x + 1 < _width) { + x0 = QR_MAXI(0, x - (windw >> 1)); + x1 = QR_MINI(x + (windw >> 1), _width - 1); + m += col_sums[x1] - col_sums[x0]; + } + } + /*Update the column sums.*/ + if (y + 1 < _height) { + y0offs = QR_MAXI(0, y - (windh >> 1)) * _width; + y1offs = QR_MINI(y + (windh >> 1), _height - 1) * _width; + for (x = 0; x < _width; x++) { + col_sums[x] -= _img[y0offs + x]; + col_sums[x] += _img[y1offs + x]; + } + } + } + free(col_sums); + } +#if defined(QR_DEBUG) + { + FILE *fout; + fout = fopen("binary.png", "wb"); + image_write_png(_img, _width, _height, fout); + fclose(fout); + } +#endif + return (mask); +} +#endif + +#if defined(TEST_BINARIZE) +#include <stdio.h> +#include "image.c" + +int main(int _argc, char **_argv) +{ + unsigned char *img; + int width; + int height; + int x; + int y; + if (_argc < 2) { + fprintf(stderr, "usage: %s <image>.png\n", _argv[0]); + return EXIT_FAILURE; + } + /*width=1182; + height=1181; + img=(unsigned char *)malloc(width*height*sizeof(*img)); + for(y=0;y<height;y++)for(x=0;x<width;x++){ + img[y*width+x]=(unsigned char)(-((x&1)^(y&1))&0xFF); + }*/ + { + FILE *fin; + fin = fopen(_argv[1], "rb"); + image_read_png(&img, &width, &height, fin); + fclose(fin); + } + qr_binarize(img, width, height); + /*{ + FILE *fout; + fout=fopen("binary.png","wb"); + image_write_png(img,width,height,fout); + fclose(fout); + }*/ + free(img); + return EXIT_SUCCESS; +} +#endif diff --git a/zbar/qrcode/binarize.h b/zbar/qrcode/binarize.h new file mode 100644 index 0000000..3ec5bc4 --- /dev/null +++ b/zbar/qrcode/binarize.h @@ -0,0 +1,17 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_binarize_H) +#define _qrcode_binarize_H (1) + +void qr_image_cross_masking_median_filter(unsigned char *_img, int _width, + int _height); + +void qr_wiener_filter(unsigned char *_img, int _width, int _height); + +/*Binarizes a grayscale image.*/ +unsigned char *qr_binarize(const unsigned char *_img, int _width, int _height); + +#endif diff --git a/zbar/qrcode/isaac.c b/zbar/qrcode/isaac.c new file mode 100644 index 0000000..88889f3 --- /dev/null +++ b/zbar/qrcode/isaac.c @@ -0,0 +1,145 @@ +/*Written by Timothy B. Terriberry (tterribe@xiph.org) 1999-2009 public domain. + Based on the public domain implementation by Robert J. Jenkins Jr.*/ +#include "isaac.h" +#include <float.h> +#include <math.h> +#include <string.h> + +#define ISAAC_MASK (0xFFFFFFFFU) + +static void isaac_update(isaac_ctx *_ctx) +{ + unsigned *m; + unsigned *r; + unsigned a; + unsigned b; + unsigned x; + unsigned y; + int i; + m = _ctx->m; + r = _ctx->r; + a = _ctx->a; + b = _ctx->b + (++_ctx->c) & ISAAC_MASK; + for (i = 0; i < ISAAC_SZ / 2; i++) { + x = m[i]; + a = (a ^ a << 13) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 6) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a << 2) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 16) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + } + for (i = ISAAC_SZ / 2; i < ISAAC_SZ; i++) { + x = m[i]; + a = (a ^ a << 13) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 6) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a << 2) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 16) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + } + _ctx->b = b; + _ctx->a = a; + _ctx->n = ISAAC_SZ; +} + +static void isaac_mix(unsigned _x[8]) +{ + static const unsigned char SHIFT[8] = { 11, 2, 8, 16, 10, 4, 8, 9 }; + int i; + for (i = 0; i < 8; i++) { + _x[i] ^= _x[i + 1 & 7] << SHIFT[i]; + _x[i + 3 & 7] += _x[i]; + _x[i + 1 & 7] += _x[i + 2 & 7]; + i++; + _x[i] ^= _x[i + 1 & 7] >> SHIFT[i]; + _x[i + 3 & 7] += _x[i]; + _x[i + 1 & 7] += _x[i + 2 & 7]; + } +} + +void isaac_init(isaac_ctx *_ctx, const void *_seed, int _nseed) +{ + const unsigned char *seed; + unsigned *m; + unsigned *r; + unsigned x[8]; + int i; + int j; + _ctx->a = _ctx->b = _ctx->c = 0; + m = _ctx->m; + r = _ctx->r; + x[0] = x[1] = x[2] = x[3] = x[4] = x[5] = x[6] = x[7] = 0x9E3779B9; + for (i = 0; i < 4; i++) + isaac_mix(x); + if (_nseed > ISAAC_SEED_SZ_MAX) + _nseed = ISAAC_SEED_SZ_MAX; + seed = (const unsigned char *)_seed; + for (i = 0; i < (_nseed >> 2); i++) { + r[i] = seed[i << 2 | 3] << 24 | seed[i << 2 | 2] << 16 | + seed[i << 2 | 1] << 8 | seed[i << 2]; + } + if (_nseed & 3) { + r[i] = seed[i << 2]; + for (j = 1; j < (_nseed & 3); j++) + r[i] += seed[i << 2 | j] << (j << 3); + i++; + } + memset(r + i, 0, (ISAAC_SZ - i) * sizeof(*r)); + for (i = 0; i < ISAAC_SZ; i += 8) { + for (j = 0; j < 8; j++) + x[j] += r[i + j]; + isaac_mix(x); + memcpy(m + i, x, sizeof(x)); + } + for (i = 0; i < ISAAC_SZ; i += 8) { + for (j = 0; j < 8; j++) + x[j] += m[i + j]; + isaac_mix(x); + memcpy(m + i, x, sizeof(x)); + } + isaac_update(_ctx); +} + +unsigned isaac_next_uint32(isaac_ctx *_ctx) +{ + if (!_ctx->n) + isaac_update(_ctx); + return _ctx->r[--_ctx->n]; +} + +/*Returns a uniform random integer less than the given maximum value. + _n: The upper bound on the range of numbers returned (not inclusive). + This must be strictly less than 2**32. + Return: An integer uniformly distributed between 0 (inclusive) and _n + (exclusive).*/ +unsigned isaac_next_uint(isaac_ctx *_ctx, unsigned _n) +{ + unsigned r; + unsigned v; + unsigned d; + do { + r = isaac_next_uint32(_ctx); + v = r % _n; + d = r - v; + } while ((d + _n - 1 & ISAAC_MASK) < d); + return v; +} diff --git a/zbar/qrcode/isaac.h b/zbar/qrcode/isaac.h new file mode 100644 index 0000000..e0b10e7 --- /dev/null +++ b/zbar/qrcode/isaac.h @@ -0,0 +1,34 @@ +/*Written by Timothy B. Terriberry (tterribe@xiph.org) 1999-2009 public domain. + Based on the public domain implementation by Robert J. Jenkins Jr.*/ +#if !defined(_isaac_H) +#define _isaac_H (1) + +typedef struct isaac_ctx isaac_ctx; + +#define ISAAC_SZ_LOG (8) +#define ISAAC_SZ (1 << ISAAC_SZ_LOG) +#define ISAAC_SEED_SZ_MAX (ISAAC_SZ << 2) + +/*ISAAC is the most advanced of a series of Pseudo-Random Number Generators + designed by Robert J. Jenkins Jr. in 1996. + http://www.burtleburtle.net/bob/rand/isaac.html + To quote: + No efficient method is known for deducing their internal states. + ISAAC requires an amortized 18.75 instructions to produce a 32-bit value. + There are no cycles in ISAAC shorter than 2**40 values. + The expected cycle length is 2**8295 values.*/ +struct isaac_ctx { + unsigned n; + unsigned r[ISAAC_SZ]; + unsigned m[ISAAC_SZ]; + unsigned a; + unsigned b; + unsigned c; +}; + +void isaac_init(isaac_ctx *_ctx, const void *_seed, int _nseed); + +unsigned isaac_next_uint32(isaac_ctx *_ctx); +unsigned isaac_next_uint(isaac_ctx *_ctx, unsigned _n); + +#endif diff --git a/zbar/qrcode/qrdec.c b/zbar/qrcode/qrdec.c new file mode 100644 index 0000000..e4c922c --- /dev/null +++ b/zbar/qrcode/qrdec.c @@ -0,0 +1,4395 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ + +#include "config.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "bch15_5.h" +#include "binarize.h" +#include "error.h" +#include "image.h" +#include "isaac.h" +#include "qrcode.h" +#include "rs.h" +#include "svg.h" +#include "util.h" + +#include "qrdec.h" + +typedef int qr_line[3]; + +typedef struct qr_finder_cluster qr_finder_cluster; +typedef struct qr_finder_edge_pt qr_finder_edge_pt; +typedef struct qr_finder_center qr_finder_center; + +typedef struct qr_aff qr_aff; +typedef struct qr_hom qr_hom; + +typedef struct qr_finder qr_finder; + +typedef struct qr_hom_cell qr_hom_cell; +typedef struct qr_sampling_grid qr_sampling_grid; +typedef struct qr_pack_buf qr_pack_buf; + +/*The number of bits in an int. + Note the cast to (int): this prevents this value from "promoting" whole + expressions to an (unsigned) size_t.*/ +#define QR_INT_BITS ((int)sizeof(int) * CHAR_BIT) +#define QR_INT_LOGBITS (QR_ILOG(QR_INT_BITS)) + +/*A 14 bit resolution for a homography ensures that the ideal module size for a + version 40 code differs from that of a version 39 code by at least 2.*/ +#define QR_HOM_BITS (14) + +/*The number of bits of sub-module precision to use when searching for + alignment patterns. + Two bits allows an alignment pattern to be found even if the modules have + been eroded by up to 50% (due to blurring, etc.). + This must be at least one, since it affects the dynamic range of the + transforms, and we sample at half-module resolution to compute a bounding + quadrilateral for the code.*/ +#define QR_ALIGN_SUBPREC (2) + +/* collection of finder lines */ +typedef struct qr_finder_lines { + qr_finder_line *lines; + int nlines, clines; +} qr_finder_lines; + +struct qr_reader { + /*The GF(256) representation used in Reed-Solomon decoding.*/ + rs_gf256 gf; + /*The random number generator used by RANSAC.*/ + isaac_ctx isaac; + /* current finder state, horizontal and vertical lines */ + qr_finder_lines finder_lines[2]; +}; + +/*Initializes a client reader handle.*/ +static void qr_reader_init(qr_reader *reader) +{ + /*time_t now; + now=time(NULL); + isaac_init(&_reader->isaac,&now,sizeof(now));*/ + isaac_init(&reader->isaac, NULL, 0); + rs_gf256_init(&reader->gf, QR_PPOLY); +} + +/*Allocates a client reader handle.*/ +qr_reader *_zbar_qr_create(void) +{ + qr_reader *reader = (qr_reader *)calloc(1, sizeof(*reader)); + qr_reader_init(reader); + return (reader); +} + +/*Frees a client reader handle.*/ +void _zbar_qr_destroy(qr_reader *reader) +{ + zprintf(1, "max finder lines = %dx%d\n", reader->finder_lines[0].clines, + reader->finder_lines[1].clines); + if (reader->finder_lines[0].lines) + free(reader->finder_lines[0].lines); + if (reader->finder_lines[1].lines) + free(reader->finder_lines[1].lines); + free(reader); +} + +/* reset finder state between scans */ +void _zbar_qr_reset(qr_reader *reader) +{ + reader->finder_lines[0].nlines = 0; + reader->finder_lines[1].nlines = 0; +} + +/*A cluster of lines crossing a finder pattern (all in the same direction).*/ +struct qr_finder_cluster { + /*Pointers to the lines crossing the pattern.*/ + qr_finder_line **lines; + /*The number of lines in the cluster.*/ + int nlines; +}; + +/*A point on the edge of a finder pattern. + These are obtained from the endpoints of the lines crossing this particular + pattern.*/ +struct qr_finder_edge_pt { + /*The location of the edge point.*/ + qr_point pos; + /*A label classifying which edge this belongs to: + 0: negative u edge (left) + 1: positive u edge (right) + 2: negative v edge (top) + 3: positive v edge (bottom)*/ + int edge; + /*The (signed) perpendicular distance of the edge point from a line parallel + to the edge passing through the finder center, in (u,v) coordinates. + This is also re-used by RANSAC to store inlier flags.*/ + int extent; +}; + +/*The center of a finder pattern obtained from the crossing of one or more + clusters of horizontal finder lines with one or more clusters of vertical + finder lines.*/ +struct qr_finder_center { + /*The estimated location of the finder center.*/ + qr_point pos; + /*The list of edge points from the crossing lines.*/ + qr_finder_edge_pt *edge_pts; + /*The number of edge points from the crossing lines.*/ + int nedge_pts; +}; + +static int qr_finder_vline_cmp(const void *_a, const void *_b) +{ + const qr_finder_line *a; + const qr_finder_line *b; + a = (const qr_finder_line *)_a; + b = (const qr_finder_line *)_b; + return ((a->pos[0] > b->pos[0]) - (a->pos[0] < b->pos[0]) << 1) + + (a->pos[1] > b->pos[1]) - (a->pos[1] < b->pos[1]); +} + +/*Clusters adjacent lines into groups that are large enough to be crossing a + finder pattern (relative to their length). + _clusters: The buffer in which to store the clusters found. + _neighbors: The buffer used to store the lists of lines in each cluster. + _lines: The list of lines to cluster. + Horizontal lines must be sorted in ascending order by Y + coordinate, with ties broken by X coordinate. + Vertical lines must be sorted in ascending order by X coordinate, + with ties broken by Y coordinate. + _nlines: The number of lines in the set of lines to cluster. + _v: 0 for horizontal lines, or 1 for vertical lines. + Return: The number of clusters.*/ +static int qr_finder_cluster_lines(qr_finder_cluster *_clusters, + qr_finder_line **_neighbors, + qr_finder_line *_lines, int _nlines, int _v) +{ + unsigned char *mark; + qr_finder_line **neighbors; + int nneighbors; + int nclusters; + int i; + /*TODO: Kalman filters!*/ + mark = (unsigned char *)calloc(_nlines, sizeof(*mark)); + neighbors = _neighbors; + nclusters = 0; + for (i = 0; i < _nlines - 1; i++) + if (!mark[i]) { + int len; + int j; + nneighbors = 1; + neighbors[0] = _lines + i; + len = _lines[i].len; + for (j = i + 1; j < _nlines; j++) + if (!mark[j]) { + const qr_finder_line *a; + const qr_finder_line *b; + int thresh; + a = neighbors[nneighbors - 1]; + b = _lines + j; + /*The clustering threshold is proportional to the size of the lines, + since minor noise in large areas can interrupt patterns more easily + at high resolutions.*/ + thresh = a->len + 7 >> 2; + if (abs(a->pos[1 - _v] - b->pos[1 - _v]) > thresh) + break; + if (abs(a->pos[_v] - b->pos[_v]) > thresh) + continue; + if (abs(a->pos[_v] + a->len - b->pos[_v] - b->len) > thresh) + continue; + if (a->boffs > 0 && b->boffs > 0 && + abs(a->pos[_v] - a->boffs - b->pos[_v] + b->boffs) > + thresh) { + continue; + } + if (a->eoffs > 0 && b->eoffs > 0 && + abs(a->pos[_v] + a->len + a->eoffs - b->pos[_v] - + b->len - b->eoffs) > thresh) { + continue; + } + neighbors[nneighbors++] = _lines + j; + len += b->len; + } + /*We require at least three lines to form a cluster, which eliminates a + large number of false positives, saving considerable decoding time. + This should still be sufficient for 1-pixel codes with no noise.*/ + if (nneighbors < 3) + continue; + /*The expected number of lines crossing a finder pattern is equal to their + average length. + We accept the cluster if size is at least 1/3 their average length (this + is a very small threshold, but was needed for some test images).*/ + len = ((len << 1) + nneighbors) / (nneighbors << 1); + if (nneighbors * (5 << QR_FINDER_SUBPREC) >= len) { + _clusters[nclusters].lines = neighbors; + _clusters[nclusters].nlines = nneighbors; + for (j = 0; j < nneighbors; j++) + mark[neighbors[j] - _lines] = 1; + neighbors += nneighbors; + nclusters++; + } + } + free(mark); + return nclusters; +} + +/*Adds the coordinates of the edge points from the lines contained in the + given list of clusters to the list of edge points for a finder center. + Only the edge point position is initialized. + The edge label and extent are set by qr_finder_edge_pts_aff_classify() + or qr_finder_edge_pts_hom_classify(). + _edge_pts: The buffer in which to store the edge points. + _nedge_pts: The current number of edge points in the buffer. + _neighbors: The list of lines in the cluster. + _nneighbors: The number of lines in the list of lines in the cluster. + _v: 0 for horizontal lines and 1 for vertical lines. + Return: The new total number of edge points.*/ +static int qr_finder_edge_pts_fill(qr_finder_edge_pt *_edge_pts, int _nedge_pts, + qr_finder_cluster **_neighbors, + int _nneighbors, int _v) +{ + int i; + for (i = 0; i < _nneighbors; i++) { + qr_finder_cluster *c; + int j; + c = _neighbors[i]; + for (j = 0; j < c->nlines; j++) { + qr_finder_line *l; + l = c->lines[j]; + if (l->boffs > 0) { + _edge_pts[_nedge_pts].pos[0] = l->pos[0]; + _edge_pts[_nedge_pts].pos[1] = l->pos[1]; + _edge_pts[_nedge_pts].pos[_v] -= l->boffs; + _nedge_pts++; + } + if (l->eoffs > 0) { + _edge_pts[_nedge_pts].pos[0] = l->pos[0]; + _edge_pts[_nedge_pts].pos[1] = l->pos[1]; + _edge_pts[_nedge_pts].pos[_v] += l->len + l->eoffs; + _nedge_pts++; + } + } + } + return _nedge_pts; +} + +static int qr_finder_center_cmp(const void *_a, const void *_b) +{ + const qr_finder_center *a; + const qr_finder_center *b; + a = (const qr_finder_center *)_a; + b = (const qr_finder_center *)_b; + return ((b->nedge_pts > a->nedge_pts) - (b->nedge_pts < a->nedge_pts) + << 2) + + ((a->pos[1] > b->pos[1]) - (a->pos[1] < b->pos[1]) << 1) + + (a->pos[0] > b->pos[0]) - (a->pos[0] < b->pos[0]); +} + +/*Determine if a horizontal line crosses a vertical line. + _hline: The horizontal line. + _vline: The vertical line. + Return: A non-zero value if the lines cross, or zero if they do not.*/ +static int qr_finder_lines_are_crossing(const qr_finder_line *_hline, + const qr_finder_line *_vline) +{ + return _hline->pos[0] <= _vline->pos[0] && + _vline->pos[0] < _hline->pos[0] + _hline->len && + _vline->pos[1] <= _hline->pos[1] && + _hline->pos[1] < _vline->pos[1] + _vline->len; +} + +/*Finds horizontal clusters that cross corresponding vertical clusters, + presumably corresponding to a finder center. + _center: The buffer in which to store putative finder centers. + _edge_pts: The buffer to use for the edge point lists for each finder + center. + _hclusters: The clusters of horizontal lines crossing finder patterns. + _nhclusters: The number of horizontal line clusters. + _vclusters: The clusters of vertical lines crossing finder patterns. + _nvclusters: The number of vertical line clusters. + Return: The number of putative finder centers.*/ +static int qr_finder_find_crossings(qr_finder_center *_centers, + qr_finder_edge_pt *_edge_pts, + qr_finder_cluster *_hclusters, + int _nhclusters, + qr_finder_cluster *_vclusters, + int _nvclusters) +{ + qr_finder_cluster **hneighbors; + qr_finder_cluster **vneighbors; + unsigned char *hmark; + unsigned char *vmark; + int ncenters; + int i; + int j; + hneighbors = + (qr_finder_cluster **)malloc(_nhclusters * sizeof(*hneighbors)); + vneighbors = + (qr_finder_cluster **)malloc(_nvclusters * sizeof(*vneighbors)); + hmark = (unsigned char *)calloc(_nhclusters, sizeof(*hmark)); + vmark = (unsigned char *)calloc(_nvclusters, sizeof(*vmark)); + ncenters = 0; + /*TODO: This may need some re-working. + We should be finding groups of clusters such that _all_ horizontal lines in + _all_ horizontal clusters in the group cross _all_ vertical lines in _all_ + vertical clusters in the group. + This is equivalent to finding the maximum bipartite clique in the + connectivity graph, which requires linear progamming to solve efficiently. + In principle, that is easy to do, but a realistic implementation without + floating point is a lot of work (and computationally expensive). + Right now we are relying on a sufficient border around the finder patterns + to prevent false positives.*/ + for (i = 0; i < _nhclusters; i++) + if (!hmark[i]) { + qr_finder_line *a; + qr_finder_line *b; + int nvneighbors; + int nedge_pts; + int y; + a = _hclusters[i].lines[_hclusters[i].nlines >> 1]; + y = nvneighbors = 0; + for (j = 0; j < _nvclusters; j++) + if (!vmark[j]) { + b = _vclusters[j].lines[_vclusters[j].nlines >> 1]; + if (qr_finder_lines_are_crossing(a, b)) { + vmark[j] = 1; + y += (b->pos[1] << 1) + b->len; + if (b->boffs > 0 && b->eoffs > 0) + y += b->eoffs - b->boffs; + vneighbors[nvneighbors++] = _vclusters + j; + } + } + if (nvneighbors > 0) { + qr_finder_center *c; + int nhneighbors; + int x; + x = (a->pos[0] << 1) + a->len; + if (a->boffs > 0 && a->eoffs > 0) + x += a->eoffs - a->boffs; + hneighbors[0] = _hclusters + i; + nhneighbors = 1; + j = nvneighbors >> 1; + b = vneighbors[j]->lines[vneighbors[j]->nlines >> 1]; + for (j = i + 1; j < _nhclusters; j++) + if (!hmark[j]) { + a = _hclusters[j].lines[_hclusters[j].nlines >> 1]; + if (qr_finder_lines_are_crossing(a, b)) { + hmark[j] = 1; + x += (a->pos[0] << 1) + a->len; + if (a->boffs > 0 && a->eoffs > 0) + x += a->eoffs - a->boffs; + hneighbors[nhneighbors++] = _hclusters + j; + } + } + c = _centers + ncenters++; + c->pos[0] = (x + nhneighbors) / (nhneighbors << 1); + c->pos[1] = (y + nvneighbors) / (nvneighbors << 1); + c->edge_pts = _edge_pts; + nedge_pts = qr_finder_edge_pts_fill(_edge_pts, 0, hneighbors, + nhneighbors, 0); + nedge_pts = qr_finder_edge_pts_fill(_edge_pts, nedge_pts, + vneighbors, nvneighbors, 1); + c->nedge_pts = nedge_pts; + _edge_pts += nedge_pts; + } + } + free(vmark); + free(hmark); + free(vneighbors); + free(hneighbors); + /*Sort the centers by decreasing numbers of edge points.*/ + qsort(_centers, ncenters, sizeof(*_centers), qr_finder_center_cmp); + return ncenters; +} + +/*Locates a set of putative finder centers in the image. + First we search for horizontal and vertical lines that have + (dark:light:dark:light:dark) runs with size ratios of roughly (1:1:3:1:1). + Then we cluster them into groups such that each subsequent pair of endpoints + is close to the line before it in the cluster. + This will locate many line clusters that don't cross a finder pattern, but + qr_finder_find_crossings() will filter most of them out. + Where horizontal and vertical clusters cross, a prospective finder center is + returned. + _centers: Returns a pointer to a freshly-allocated list of finder centers. + This must be freed by the caller. + _edge_pts: Returns a pointer to a freshly-allocated list of edge points + around those centers. + This must be freed by the caller. + _img: The binary image to search. + _width: The width of the image. + _height: The height of the image. + Return: The number of putative finder centers located.*/ +static int qr_finder_centers_locate(qr_finder_center **_centers, + qr_finder_edge_pt **_edge_pts, + qr_reader *reader, int _width, int _height) +{ + qr_finder_line *hlines = reader->finder_lines[0].lines; + int nhlines = reader->finder_lines[0].nlines; + qr_finder_line *vlines = reader->finder_lines[1].lines; + int nvlines = reader->finder_lines[1].nlines; + + qr_finder_line **hneighbors; + qr_finder_cluster *hclusters; + int nhclusters; + qr_finder_line **vneighbors; + qr_finder_cluster *vclusters; + int nvclusters; + int ncenters; + + /*Cluster the detected lines.*/ + hneighbors = (qr_finder_line **)malloc(nhlines * sizeof(*hneighbors)); + /*We require more than one line per cluster, so there are at most nhlines/2.*/ + hclusters = + (qr_finder_cluster *)malloc((nhlines >> 1) * sizeof(*hclusters)); + nhclusters = + qr_finder_cluster_lines(hclusters, hneighbors, hlines, nhlines, 0); + /*We need vertical lines to be sorted by X coordinate, with ties broken by Y + coordinate, for clustering purposes. + We scan the image in the opposite order for cache efficiency, so sort the + lines we found here.*/ + qsort(vlines, nvlines, sizeof(*vlines), qr_finder_vline_cmp); + vneighbors = (qr_finder_line **)malloc(nvlines * sizeof(*vneighbors)); + /*We require more than one line per cluster, so there are at most nvlines/2.*/ + vclusters = + (qr_finder_cluster *)malloc((nvlines >> 1) * sizeof(*vclusters)); + nvclusters = + qr_finder_cluster_lines(vclusters, vneighbors, vlines, nvlines, 1); + /*Find line crossings among the clusters.*/ + if (nhclusters >= 3 && nvclusters >= 3) { + qr_finder_edge_pt *edge_pts; + qr_finder_center *centers; + int nedge_pts; + int i; + nedge_pts = 0; + for (i = 0; i < nhclusters; i++) + nedge_pts += hclusters[i].nlines; + for (i = 0; i < nvclusters; i++) + nedge_pts += vclusters[i].nlines; + nedge_pts <<= 1; + edge_pts = (qr_finder_edge_pt *)malloc(nedge_pts * sizeof(*edge_pts)); + centers = (qr_finder_center *)malloc(QR_MINI(nhclusters, nvclusters) * + sizeof(*centers)); + ncenters = qr_finder_find_crossings(centers, edge_pts, hclusters, + nhclusters, vclusters, nvclusters); + *_centers = centers; + *_edge_pts = edge_pts; + } else + ncenters = 0; + free(vclusters); + free(vneighbors); + free(hclusters); + free(hneighbors); + return ncenters; +} + +static void qr_point_translate(qr_point _point, int _dx, int _dy) +{ + _point[0] += _dx; + _point[1] += _dy; +} + +static unsigned qr_point_distance2(const qr_point _p1, const qr_point _p2) +{ + return (_p1[0] - _p2[0]) * (_p1[0] - _p2[0]) + + (_p1[1] - _p2[1]) * (_p1[1] - _p2[1]); +} + +/*Returns the cross product of the three points, which is positive if they are + in CCW order (in a right-handed coordinate system), and 0 if they're + colinear.*/ +static int qr_point_ccw(const qr_point _p0, const qr_point _p1, + const qr_point _p2) +{ + return (_p1[0] - _p0[0]) * (_p2[1] - _p0[1]) - + (_p1[1] - _p0[1]) * (_p2[0] - _p0[0]); +} + +/*Evaluates a line equation at a point. + _line: The line to evaluate. + _x: The X coordinate of the point. + _y: The y coordinate of the point. + Return: The value of the line equation _line[0]*_x+_line[1]*_y+_line[2].*/ +static int qr_line_eval(qr_line _line, int _x, int _y) +{ + return _line[0] * _x + _line[1] * _y + _line[2]; +} + +/*Computes a line passing through the given point using the specified second + order statistics. + Given a line defined by the equation + A*x+B*y+C = 0 , + the least squares fit to n points (x_i,y_i) must satisfy the two equations + A^2 + (Syy - Sxx)/Sxy*A*B - B^2 = 0 , + C = -(xbar*A+ybar*B) , + where + xbar = sum(x_i)/n , + ybar = sum(y_i)/n , + Sxx = sum((x_i-xbar)**2) , + Sxy = sum((x_i-xbar)*(y_i-ybar)) , + Syy = sum((y_i-ybar)**2) . + The quadratic can be solved for the ratio (A/B) or (B/A): + A/B = (Syy + sqrt((Sxx-Syy)**2 + 4*Sxy**2) - Sxx)/(-2*Sxy) , + B/A = (Sxx + sqrt((Sxx-Syy)**2 + 4*Sxy**2) - Syy)/(-2*Sxy) . + We pick the one that leads to the larger ratio to avoid destructive + cancellation (and e.g., 0/0 for horizontal or vertical lines). + The above solutions correspond to the actual minimum. + The other solution of the quadratic corresponds to a saddle point of the + least squares objective function. + _l: Returns the fitted line values A, B, and C. + _x0: The X coordinate of the point the line is supposed to pass through. + _y0: The Y coordinate of the point the line is supposed to pass through. + _sxx: The sum Sxx. + _sxy: The sum Sxy. + _syy: The sum Syy. + _res: The maximum number of bits occupied by the product of any two of + _l[0] or _l[1]. + Smaller numbers give less angular resolution, but allow more overhead + room for computations.*/ +static void qr_line_fit(qr_line _l, int _x0, int _y0, int _sxx, int _sxy, + int _syy, int _res) +{ + int dshift; + int dround; + int u; + int v; + int w; + u = abs(_sxx - _syy); + v = -_sxy << 1; + w = qr_ihypot(u, v); + /*Computations in later stages can easily overflow with moderate sizes, so we + compute a shift factor to scale things down into a manageable range. + We ensure that the product of any two of _l[0] and _l[1] fits within _res + bits, which allows computation of line intersections without overflow.*/ + dshift = + QR_MAXI(0, QR_MAXI(qr_ilog(u), qr_ilog(abs(v))) + 1 - (_res + 1 >> 1)); + dround = (1 << dshift) >> 1; + if (_sxx > _syy) { + _l[0] = v + dround >> dshift; + _l[1] = u + w + dround >> dshift; + } else { + _l[0] = u + w + dround >> dshift; + _l[1] = v + dround >> dshift; + } + _l[2] = -(_x0 * _l[0] + _y0 * _l[1]); +} + +/*Perform a least-squares line fit to a list of points. + At least two points are required.*/ +static void qr_line_fit_points(qr_line _l, qr_point *_p, int _np, int _res) +{ + int sx; + int sy; + int xmin; + int xmax; + int ymin; + int ymax; + int xbar; + int ybar; + int dx; + int dy; + int sxx; + int sxy; + int syy; + int sshift; + int sround; + int i; + sx = sy = 0; + ymax = xmax = INT_MIN; + ymin = xmin = INT_MAX; + for (i = 0; i < _np; i++) { + sx += _p[i][0]; + xmin = QR_MINI(xmin, _p[i][0]); + xmax = QR_MAXI(xmax, _p[i][0]); + sy += _p[i][1]; + ymin = QR_MINI(ymin, _p[i][1]); + ymax = QR_MAXI(ymax, _p[i][1]); + } + xbar = (sx + (_np >> 1)) / _np; + ybar = (sy + (_np >> 1)) / _np; + sshift = + QR_MAXI(0, qr_ilog(_np * QR_MAXI(QR_MAXI(xmax - xbar, xbar - xmin), + QR_MAXI(ymax - ybar, ybar - ymin))) - + (QR_INT_BITS - 1 >> 1)); + sround = (1 << sshift) >> 1; + sxx = sxy = syy = 0; + for (i = 0; i < _np; i++) { + dx = _p[i][0] - xbar + sround >> sshift; + dy = _p[i][1] - ybar + sround >> sshift; + sxx += dx * dx; + sxy += dx * dy; + syy += dy * dy; + } + qr_line_fit(_l, xbar, ybar, sxx, sxy, syy, _res); +} + +static void qr_line_orient(qr_line _l, int _x, int _y) +{ + if (qr_line_eval(_l, _x, _y) < 0) { + _l[0] = -_l[0]; + _l[1] = -_l[1]; + _l[2] = -_l[2]; + } +} + +static int qr_line_isect(qr_point _p, const qr_line _l0, const qr_line _l1) +{ + int d; + int x; + int y; + d = _l0[0] * _l1[1] - _l0[1] * _l1[0]; + if (d == 0) + return -1; + x = _l0[1] * _l1[2] - _l1[1] * _l0[2]; + y = _l1[0] * _l0[2] - _l0[0] * _l1[2]; + if (d < 0) { + x = -x; + y = -y; + d = -d; + } + _p[0] = QR_DIVROUND(x, d); + _p[1] = QR_DIVROUND(y, d); + return 0; +} + +/*An affine homography. + This maps from the image (at subpel resolution) to a square domain with + power-of-two sides (of res bits) and back.*/ +struct qr_aff { + int fwd[2][2]; + int inv[2][2]; + int x0; + int y0; + int res; + int ires; +}; + +static void qr_aff_init(qr_aff *_aff, const qr_point _p0, const qr_point _p1, + const qr_point _p2, int _res) +{ + int det; + int ires; + int dx1; + int dy1; + int dx2; + int dy2; + /*det is ensured to be positive by our caller.*/ + dx1 = _p1[0] - _p0[0]; + dx2 = _p2[0] - _p0[0]; + dy1 = _p1[1] - _p0[1]; + dy2 = _p2[1] - _p0[1]; + det = dx1 * dy2 - dy1 * dx2; + ires = QR_MAXI((qr_ilog(abs(det)) >> 1) - 2, 0); + _aff->fwd[0][0] = dx1; + _aff->fwd[0][1] = dx2; + _aff->fwd[1][0] = dy1; + _aff->fwd[1][1] = dy2; + _aff->inv[0][0] = QR_DIVROUND(dy2 << _res, det >> ires); + _aff->inv[0][1] = QR_DIVROUND(-dx2 << _res, det >> ires); + _aff->inv[1][0] = QR_DIVROUND(-dy1 << _res, det >> ires); + _aff->inv[1][1] = QR_DIVROUND(dx1 << _res, det >> ires); + _aff->x0 = _p0[0]; + _aff->y0 = _p0[1]; + _aff->res = _res; + _aff->ires = ires; +} + +/*Map from the image (at subpel resolution) into the square domain.*/ +static void qr_aff_unproject(qr_point _q, const qr_aff *_aff, int _x, int _y) +{ + _q[0] = _aff->inv[0][0] * (_x - _aff->x0) + + _aff->inv[0][1] * (_y - _aff->y0) + (1 << _aff->ires >> 1) >> + _aff->ires; + _q[1] = _aff->inv[1][0] * (_x - _aff->x0) + + _aff->inv[1][1] * (_y - _aff->y0) + (1 << _aff->ires >> 1) >> + _aff->ires; +} + +/*Map from the square domain into the image (at subpel resolution).*/ +static void qr_aff_project(qr_point _p, const qr_aff *_aff, int _u, int _v) +{ + _p[0] = + (_aff->fwd[0][0] * _u + _aff->fwd[0][1] * _v + (1 << _aff->res - 1) >> + _aff->res) + + _aff->x0; + _p[1] = + (_aff->fwd[1][0] * _u + _aff->fwd[1][1] * _v + (1 << _aff->res - 1) >> + _aff->res) + + _aff->y0; +} + +/*A full homography. + Like the affine homography, this maps from the image (at subpel resolution) + to a square domain with power-of-two sides (of res bits) and back.*/ +struct qr_hom { + int fwd[3][2]; + int inv[3][2]; + int fwd22; + int inv22; + int x0; + int y0; + int res; +}; + +static void qr_hom_init(qr_hom *_hom, int _x0, int _y0, int _x1, int _y1, + int _x2, int _y2, int _x3, int _y3, int _res) +{ + int dx10; + int dx20; + int dx30; + int dx31; + int dx32; + int dy10; + int dy20; + int dy30; + int dy31; + int dy32; + int a20; + int a21; + int a22; + int b0; + int b1; + int b2; + int s1; + int s2; + int r1; + int r2; + dx10 = _x1 - _x0; + dx20 = _x2 - _x0; + dx30 = _x3 - _x0; + dx31 = _x3 - _x1; + dx32 = _x3 - _x2; + dy10 = _y1 - _y0; + dy20 = _y2 - _y0; + dy30 = _y3 - _y0; + dy31 = _y3 - _y1; + dy32 = _y3 - _y2; + a20 = dx32 * dy10 - dx10 * dy32; + a21 = dx20 * dy31 - dx31 * dy20; + a22 = dx32 * dy31 - dx31 * dy32; + /*Figure out if we need to downscale anything.*/ + b0 = qr_ilog(QR_MAXI(abs(dx10), abs(dy10))) + qr_ilog(abs(a20 + a22)); + b1 = qr_ilog(QR_MAXI(abs(dx20), abs(dy20))) + qr_ilog(abs(a21 + a22)); + b2 = qr_ilog(QR_MAXI(QR_MAXI(abs(a20), abs(a21)), abs(a22))); + s1 = QR_MAXI(0, _res + QR_MAXI(QR_MAXI(b0, b1), b2) - (QR_INT_BITS - 2)); + r1 = (1 << s1) >> 1; + /*Compute the final coefficients of the forward transform. + The 32x32->64 bit multiplies are really needed for accuracy with large + versions.*/ + _hom->fwd[0][0] = QR_FIXMUL(dx10, a20 + a22, r1, s1); + _hom->fwd[0][1] = QR_FIXMUL(dx20, a21 + a22, r1, s1); + _hom->x0 = _x0; + _hom->fwd[1][0] = QR_FIXMUL(dy10, a20 + a22, r1, s1); + _hom->fwd[1][1] = QR_FIXMUL(dy20, a21 + a22, r1, s1); + _hom->y0 = _y0; + _hom->fwd[2][0] = a20 + r1 >> s1; + _hom->fwd[2][1] = a21 + r1 >> s1; + _hom->fwd22 = s1 > _res ? a22 + (r1 >> _res) >> s1 - _res : + a22 << _res - s1; + /*Now compute the inverse transform.*/ + b0 = qr_ilog(QR_MAXI(QR_MAXI(abs(dx10), abs(dx20)), abs(dx30))) + + qr_ilog(QR_MAXI(abs(_hom->fwd[0][0]), abs(_hom->fwd[1][0]))); + b1 = qr_ilog(QR_MAXI(QR_MAXI(abs(dy10), abs(dy20)), abs(dy30))) + + qr_ilog(QR_MAXI(abs(_hom->fwd[0][1]), abs(_hom->fwd[1][1]))); + b2 = qr_ilog(abs(a22)) - s1; + s2 = QR_MAXI(0, QR_MAXI(b0, b1) + b2 - (QR_INT_BITS - 3)); + r2 = (1 << s2) >> 1; + s1 += s2; + r1 <<= s2; + /*The 32x32->64 bit multiplies are really needed for accuracy with large + versions.*/ + _hom->inv[0][0] = QR_FIXMUL(_hom->fwd[1][1], a22, r1, s1); + _hom->inv[0][1] = QR_FIXMUL(-_hom->fwd[0][1], a22, r1, s1); + _hom->inv[1][0] = QR_FIXMUL(-_hom->fwd[1][0], a22, r1, s1); + _hom->inv[1][1] = QR_FIXMUL(_hom->fwd[0][0], a22, r1, s1); + _hom->inv[2][0] = + QR_FIXMUL(_hom->fwd[1][0], _hom->fwd[2][1], + -QR_EXTMUL(_hom->fwd[1][1], _hom->fwd[2][0], r2), s2); + _hom->inv[2][1] = + QR_FIXMUL(_hom->fwd[0][1], _hom->fwd[2][0], + -QR_EXTMUL(_hom->fwd[0][0], _hom->fwd[2][1], r2), s2); + _hom->inv22 = QR_FIXMUL(_hom->fwd[0][0], _hom->fwd[1][1], + -QR_EXTMUL(_hom->fwd[0][1], _hom->fwd[1][0], r2), + s2); + _hom->res = _res; +} + +/*Map from the image (at subpel resolution) into the square domain. + Returns a negative value if the point went to infinity.*/ +static int qr_hom_unproject(qr_point _q, const qr_hom *_hom, int _x, int _y) +{ + int x; + int y; + int w; + _x -= _hom->x0; + _y -= _hom->y0; + x = _hom->inv[0][0] * _x + _hom->inv[0][1] * _y; + y = _hom->inv[1][0] * _x + _hom->inv[1][1] * _y; + w = _hom->inv[2][0] * _x + _hom->inv[2][1] * _y + _hom->inv22 + + (1 << _hom->res - 1) >> + _hom->res; + if (w == 0) { + _q[0] = x < 0 ? INT_MIN : INT_MAX; + _q[1] = y < 0 ? INT_MIN : INT_MAX; + return -1; + } else { + if (w < 0) { + x = -x; + y = -y; + w = -w; + } + _q[0] = QR_DIVROUND(x, w); + _q[1] = QR_DIVROUND(y, w); + } + return 0; +} + +/*Finish a partial projection, converting from homogeneous coordinates to the + normal 2-D representation. + In loops, we can avoid many multiplies by computing the homogeneous _x, _y, + and _w incrementally, but we cannot avoid the divisions, done here.*/ +static void qr_hom_fproject(qr_point _p, const qr_hom *_hom, int _x, int _y, + int _w) +{ + if (_w == 0) { + _p[0] = _x < 0 ? INT_MIN : INT_MAX; + _p[1] = _y < 0 ? INT_MIN : INT_MAX; + } else { + if (_w < 0) { + _x = -_x; + _y = -_y; + _w = -_w; + } + _p[0] = QR_DIVROUND(_x, _w) + _hom->x0; + _p[1] = QR_DIVROUND(_y, _w) + _hom->y0; + } +} + +#if defined(QR_DEBUG) +/*Map from the square domain into the image (at subpel resolution). + Currently only used directly by debug code.*/ +static void qr_hom_project(qr_point _p, const qr_hom *_hom, int _u, int _v) +{ + qr_hom_fproject(_p, _hom, _hom->fwd[0][0] * _u + _hom->fwd[0][1] * _v, + _hom->fwd[1][0] * _u + _hom->fwd[1][1] * _v, + _hom->fwd[2][0] * _u + _hom->fwd[2][1] * _v + _hom->fwd22); +} +#endif + +/*All the information we've collected about a finder pattern in the current + configuration.*/ +struct qr_finder { + /*The module size along each axis (in the square domain).*/ + int size[2]; + /*The version estimated from the module size along each axis.*/ + int eversion[2]; + /*The list of classified edge points for each edge.*/ + qr_finder_edge_pt *edge_pts[4]; + /*The number of edge points classified as belonging to each edge.*/ + int nedge_pts[4]; + /*The number of inliers found after running RANSAC on each edge.*/ + int ninliers[4]; + /*The center of the finder pattern (in the square domain).*/ + qr_point o; + /*The finder center information from the original image.*/ + qr_finder_center *c; +}; + +static int qr_cmp_edge_pt(const void *_a, const void *_b) +{ + const qr_finder_edge_pt *a; + const qr_finder_edge_pt *b; + a = (const qr_finder_edge_pt *)_a; + b = (const qr_finder_edge_pt *)_b; + return ((a->edge > b->edge) - (a->edge < b->edge) << 1) + + (a->extent > b->extent) - (a->extent < b->extent); +} + +/*Computes the index of the edge each edge point belongs to, and its (signed) + distance along the corresponding axis from the center of the finder pattern + (in the square domain). + The resulting list of edge points is sorted by edge index, with ties broken + by extent.*/ +static void qr_finder_edge_pts_aff_classify(qr_finder *_f, const qr_aff *_aff) +{ + qr_finder_center *c; + int i; + int e; + c = _f->c; + for (e = 0; e < 4; e++) + _f->nedge_pts[e] = 0; + for (i = 0; i < c->nedge_pts; i++) { + qr_point q; + int d; + qr_aff_unproject(q, _aff, c->edge_pts[i].pos[0], c->edge_pts[i].pos[1]); + qr_point_translate(q, -_f->o[0], -_f->o[1]); + d = abs(q[1]) > abs(q[0]); + e = d << 1 | (q[d] >= 0); + _f->nedge_pts[e]++; + c->edge_pts[i].edge = e; + c->edge_pts[i].extent = q[d]; + } + qsort(c->edge_pts, c->nedge_pts, sizeof(*c->edge_pts), qr_cmp_edge_pt); + _f->edge_pts[0] = c->edge_pts; + for (e = 1; e < 4; e++) + _f->edge_pts[e] = _f->edge_pts[e - 1] + _f->nedge_pts[e - 1]; +} + +/*Computes the index of the edge each edge point belongs to, and its (signed) + distance along the corresponding axis from the center of the finder pattern + (in the square domain). + The resulting list of edge points is sorted by edge index, with ties broken + by extent.*/ +static void qr_finder_edge_pts_hom_classify(qr_finder *_f, const qr_hom *_hom) +{ + qr_finder_center *c; + int i; + int e; + c = _f->c; + for (e = 0; e < 4; e++) + _f->nedge_pts[e] = 0; + for (i = 0; i < c->nedge_pts; i++) { + qr_point q; + int d; + if (qr_hom_unproject(q, _hom, c->edge_pts[i].pos[0], + c->edge_pts[i].pos[1]) >= 0) { + qr_point_translate(q, -_f->o[0], -_f->o[1]); + d = abs(q[1]) > abs(q[0]); + e = d << 1 | (q[d] >= 0); + _f->nedge_pts[e]++; + c->edge_pts[i].edge = e; + c->edge_pts[i].extent = q[d]; + } else { + c->edge_pts[i].edge = 4; + c->edge_pts[i].extent = q[0]; + } + } + qsort(c->edge_pts, c->nedge_pts, sizeof(*c->edge_pts), qr_cmp_edge_pt); + _f->edge_pts[0] = c->edge_pts; + for (e = 1; e < 4; e++) + _f->edge_pts[e] = _f->edge_pts[e - 1] + _f->nedge_pts[e - 1]; +} + +/*TODO: Perhaps these thresholds should be on the module size instead? + Unfortunately, I'd need real-world images of codes with larger versions to + see if these thresholds are still effective, but such versions aren't used + often.*/ + +/*The amount that the estimated version numbers are allowed to differ from the + real version number and still be considered valid.*/ +#define QR_SMALL_VERSION_SLACK (1) +/*Since cell phone cameras can have severe radial distortion, the estimated + version for larger versions can be off by larger amounts.*/ +#define QR_LARGE_VERSION_SLACK (3) + +/*Estimates the size of a module after classifying the edge points. + _width: The distance between UL and UR in the square domain. + _height: The distance between UL and DL in the square domain.*/ +static int qr_finder_estimate_module_size_and_version(qr_finder *_f, int _width, + int _height) +{ + qr_point offs; + int sums[4]; + int nsums[4]; + int usize; + int nusize; + int vsize; + int nvsize; + int uversion; + int vversion; + int e; + offs[0] = offs[1] = 0; + for (e = 0; e < 4; e++) + if (_f->nedge_pts[e] > 0) { + qr_finder_edge_pt *edge_pts; + int sum; + int mean; + int n; + int i; + /*Average the samples for this edge, dropping the top and bottom 25%.*/ + edge_pts = _f->edge_pts[e]; + n = _f->nedge_pts[e]; + sum = 0; + for (i = (n >> 2); i < n - (n >> 2); i++) + sum += edge_pts[i].extent; + n = n - ((n >> 2) << 1); + mean = QR_DIVROUND(sum, n); + offs[e >> 1] += mean; + sums[e] = sum; + nsums[e] = n; + } else + nsums[e] = sums[e] = 0; + /*If we have samples on both sides of an axis, refine our idea of where the + unprojected finder center is located.*/ + if (_f->nedge_pts[0] > 0 && _f->nedge_pts[1] > 0) { + _f->o[0] -= offs[0] >> 1; + sums[0] -= offs[0] * nsums[0] >> 1; + sums[1] -= offs[0] * nsums[1] >> 1; + } + if (_f->nedge_pts[2] > 0 && _f->nedge_pts[3] > 0) { + _f->o[1] -= offs[1] >> 1; + sums[2] -= offs[1] * nsums[2] >> 1; + sums[3] -= offs[1] * nsums[3] >> 1; + } + /*We must have _some_ samples along each axis... if we don't, our transform + must be pretty severely distorting the original square (e.g., with + coordinates so large as to cause overflow).*/ + nusize = nsums[0] + nsums[1]; + if (nusize <= 0) + return -1; + /*The module size is 1/3 the average edge extent.*/ + nusize *= 3; + usize = sums[1] - sums[0]; + usize = ((usize << 1) + nusize) / (nusize << 1); + if (usize <= 0) + return -1; + /*Now estimate the version directly from the module size and the distance + between the finder patterns. + This is done independently using the extents along each axis. + If either falls significantly outside the valid range (1 to 40), reject the + configuration.*/ + uversion = (_width - 8 * usize) / (usize << 2); + if (uversion < 1 || uversion > 40 + QR_LARGE_VERSION_SLACK) + return -1; + /*Now do the same for the other axis.*/ + nvsize = nsums[2] + nsums[3]; + if (nvsize <= 0) + return -1; + nvsize *= 3; + vsize = sums[3] - sums[2]; + vsize = ((vsize << 1) + nvsize) / (nvsize << 1); + if (vsize <= 0) + return -1; + vversion = (_height - 8 * vsize) / (vsize << 2); + if (vversion < 1 || vversion > 40 + QR_LARGE_VERSION_SLACK) + return -1; + /*If the estimated version using extents along one axis is significantly + different than the estimated version along the other axis, then the axes + have significantly different scalings (relative to the grid). + This can happen, e.g., when we have multiple adjacent QR codes, and we've + picked two finder patterns from one and the third finder pattern from + another, e.g.: + X---DL UL---X + |.... |.... + X.... UR.... + Such a configuration might even pass any other geometric checks if we + didn't reject it here.*/ + if (abs(uversion - vversion) > QR_LARGE_VERSION_SLACK) + return -1; + _f->size[0] = usize; + _f->size[1] = vsize; + /*We intentionally do not compute an average version from the sizes along + both axes. + In the presence of projective distortion, one of them will be much more + accurate than the other.*/ + _f->eversion[0] = uversion; + _f->eversion[1] = vversion; + return 0; +} + +/*Eliminate outliers from the classified edge points with RANSAC.*/ +static void qr_finder_ransac(qr_finder *_f, const qr_aff *_hom, + isaac_ctx *_isaac, int _e) +{ + qr_finder_edge_pt *edge_pts; + int best_ninliers; + int n; + edge_pts = _f->edge_pts[_e]; + n = _f->nedge_pts[_e]; + best_ninliers = 0; + if (n > 1) { + int max_iters; + int i; + int j; + /*17 iterations is enough to guarantee an outlier-free sample with more + than 99% probability given as many as 50% outliers.*/ + max_iters = 17; + for (i = 0; i < max_iters; i++) { + qr_point q0; + qr_point q1; + int ninliers; + int thresh; + int p0i; + int p1i; + int *p0; + int *p1; + int j; + /*Pick two random points on this edge.*/ + p0i = isaac_next_uint(_isaac, n); + p1i = isaac_next_uint(_isaac, n - 1); + if (p1i >= p0i) + p1i++; + p0 = edge_pts[p0i].pos; + p1 = edge_pts[p1i].pos; + /*If the corresponding line is not within 45 degrees of the proper + orientation in the square domain, reject it outright. + This can happen, e.g., when highly skewed orientations cause points to + be misclassified into the wrong edge. + The irony is that using such points might produce a line which _does_ + pass the corresponding validity checks.*/ + qr_aff_unproject(q0, _hom, p0[0], p0[1]); + qr_aff_unproject(q1, _hom, p1[0], p1[1]); + qr_point_translate(q0, -_f->o[0], -_f->o[1]); + qr_point_translate(q1, -_f->o[0], -_f->o[1]); + if (abs(q0[_e >> 1] - q1[_e >> 1]) > + abs(q0[1 - (_e >> 1)] - q1[1 - (_e >> 1)])) + continue; + /*Identify the other edge points which are inliers. + The squared distance should be distributed as a \Chi^2 distribution + with one degree of freedom, which means for a 95% confidence the + point should lie within a factor 3.8414588 ~= 4 times the expected + variance of the point locations. + We grossly approximate the standard deviation as 1 pixel in one + direction, and 0.5 pixels in the other (because we average two + coordinates).*/ + thresh = qr_isqrt(qr_point_distance2(p0, p1) + << 2 * QR_FINDER_SUBPREC + 1); + ninliers = 0; + for (j = 0; j < n; j++) { + if (abs(qr_point_ccw(p0, p1, edge_pts[j].pos)) <= thresh) { + edge_pts[j].extent |= 1; + ninliers++; + } else + edge_pts[j].extent &= ~1; + } + if (ninliers > best_ninliers) { + for (j = 0; j < n; j++) + edge_pts[j].extent <<= 1; + best_ninliers = ninliers; + /*The actual number of iterations required is + log(1-\alpha)/log(1-r*r), + where \alpha is the required probability of taking a sample with + no outliers (e.g., 0.99) and r is the estimated ratio of inliers + (e.g. ninliers/n). + This is just a rough (but conservative) approximation, but it + should be good enough to stop the iteration early when we find + a good set of inliers.*/ + if (ninliers > n >> 1) + max_iters = (67 * n - 63 * ninliers - 1) / (n << 1); + } + } + /*Now collect all the inliers at the beginning of the list.*/ + for (i = j = 0; j < best_ninliers; i++) + if (edge_pts[i].extent & 2) { + if (j < i) { + qr_finder_edge_pt tmp; + *&tmp = *(edge_pts + i); + *(edge_pts + j) = *(edge_pts + i); + *(edge_pts + i) = *&tmp; + } + j++; + } + } + _f->ninliers[_e] = best_ninliers; +} + +/*Perform a least-squares line fit to an edge of a finder pattern using the + inliers found by RANSAC.*/ +static int qr_line_fit_finder_edge(qr_line _l, const qr_finder *_f, int _e, + int _res) +{ + qr_finder_edge_pt *edge_pts; + qr_point *pts; + int npts; + int i; + npts = _f->ninliers[_e]; + if (npts < 2) + return -1; + /*We could write a custom version of qr_line_fit_points that accesses + edge_pts directly, but this saves on code size and doesn't measurably slow + things down.*/ + pts = (qr_point *)malloc(npts * sizeof(*pts)); + edge_pts = _f->edge_pts[_e]; + for (i = 0; i < npts; i++) { + pts[i][0] = edge_pts[i].pos[0]; + pts[i][1] = edge_pts[i].pos[1]; + } + qr_line_fit_points(_l, pts, npts, _res); + /*Make sure the center of the finder pattern lies in the positive halfspace + of the line.*/ + qr_line_orient(_l, _f->c->pos[0], _f->c->pos[1]); + free(pts); + return 0; +} + +/*Perform a least-squares line fit to a pair of common finder edges using the + inliers found by RANSAC. + Unlike a normal edge fit, we guarantee that this one succeeds by creating at + least one point on each edge using the estimated module size if it has no + inliers.*/ +static void qr_line_fit_finder_pair(qr_line _l, const qr_aff *_aff, + const qr_finder *_f0, const qr_finder *_f1, + int _e) +{ + qr_point *pts; + int npts; + qr_finder_edge_pt *edge_pts; + qr_point q; + int n0; + int n1; + int i; + n0 = _f0->ninliers[_e]; + n1 = _f1->ninliers[_e]; + /*We could write a custom version of qr_line_fit_points that accesses + edge_pts directly, but this saves on code size and doesn't measurably slow + things down.*/ + npts = QR_MAXI(n0, 1) + QR_MAXI(n1, 1); + pts = (qr_point *)malloc(npts * sizeof(*pts)); + if (n0 > 0) { + edge_pts = _f0->edge_pts[_e]; + for (i = 0; i < n0; i++) { + pts[i][0] = edge_pts[i].pos[0]; + pts[i][1] = edge_pts[i].pos[1]; + } + } else { + q[0] = _f0->o[0]; + q[1] = _f0->o[1]; + q[_e >> 1] += _f0->size[_e >> 1] * (2 * (_e & 1) - 1); + qr_aff_project(pts[0], _aff, q[0], q[1]); + n0++; + } + if (n1 > 0) { + edge_pts = _f1->edge_pts[_e]; + for (i = 0; i < n1; i++) { + pts[n0 + i][0] = edge_pts[i].pos[0]; + pts[n0 + i][1] = edge_pts[i].pos[1]; + } + } else { + q[0] = _f1->o[0]; + q[1] = _f1->o[1]; + q[_e >> 1] += _f1->size[_e >> 1] * (2 * (_e & 1) - 1); + qr_aff_project(pts[n0], _aff, q[0], q[1]); + n1++; + } + qr_line_fit_points(_l, pts, npts, _aff->res); + /*Make sure at least one finder center lies in the positive halfspace.*/ + qr_line_orient(_l, _f0->c->pos[0], _f0->c->pos[1]); + free(pts); +} + +static int qr_finder_quick_crossing_check(const unsigned char *_img, int _width, + int _height, int _x0, int _y0, + int _x1, int _y1, int _v) +{ + /*The points must be inside the image, and have a !_v:_v:!_v pattern. + We don't scan the whole line initially, but quickly reject if the endpoints + aren't !_v, or the midpoint isn't _v. + If either end point is out of the image, or we don't encounter a _v pixel, + we return a negative value, indicating the region should be considered + empty. + Otherwise, we return a positive value to indicate it is non-empty.*/ + if (_x0 < 0 || _x0 >= _width || _y0 < 0 || _y0 >= _height || _x1 < 0 || + _x1 >= _width || _y1 < 0 || _y1 >= _height) { + return -1; + } + if ((!_img[_y0 * _width + _x0]) != _v || (!_img[_y1 * _width + _x1]) != _v) + return 1; + if ((!_img[(_y0 + _y1 >> 1) * _width + (_x0 + _x1 >> 1)]) == _v) + return -1; + return 0; +} + +/*Locate the midpoint of a _v segment along a !_v:_v:!_v line from (_x0,_y0) to + (_x1,_y1). + All coordinates, which are NOT in subpel resolution, must lie inside the + image, and the endpoints are already assumed to have the value !_v. + The returned value is in subpel resolution.*/ +static int qr_finder_locate_crossing(const unsigned char *_img, int _width, + int _height, int _x0, int _y0, int _x1, + int _y1, int _v, qr_point _p) +{ + qr_point x0; + qr_point x1; + qr_point dx; + int step[2]; + int steep; + int err; + int derr; + /*Use Bresenham's algorithm to trace along the line and find the exact + transitions from !_v to _v and back.*/ + x0[0] = _x0; + x0[1] = _y0; + x1[0] = _x1; + x1[1] = _y1; + dx[0] = abs(_x1 - _x0); + dx[1] = abs(_y1 - _y0); + steep = dx[1] > dx[0]; + err = 0; + derr = dx[1 - steep]; + step[0] = ((_x0 < _x1) << 1) - 1; + step[1] = ((_y0 < _y1) << 1) - 1; + /*Find the first crossing from !_v to _v.*/ + for (;;) { + /*If we make it all the way to the other side, there's no crossing.*/ + if (x0[steep] == x1[steep]) + return -1; + x0[steep] += step[steep]; + err += derr; + if (err << 1 > dx[steep]) { + x0[1 - steep] += step[1 - steep]; + err -= dx[steep]; + } + if ((!_img[x0[1] * _width + x0[0]]) != _v) + break; + } + /*Find the last crossing from _v to !_v.*/ + err = 0; + for (;;) { + if (x0[steep] == x1[steep]) + break; + x1[steep] -= step[steep]; + err += derr; + if (err << 1 > dx[steep]) { + x1[1 - steep] -= step[1 - steep]; + err -= dx[steep]; + } + if ((!_img[x1[1] * _width + x1[0]]) != _v) + break; + } + /*Return the midpoint of the _v segment.*/ + _p[0] = (x0[0] + x1[0] + 1 << QR_FINDER_SUBPREC) >> 1; + _p[1] = (x0[1] + x1[1] + 1 << QR_FINDER_SUBPREC) >> 1; + return 0; +} + +static int qr_aff_line_step(const qr_aff *_aff, qr_line _l, int _v, int _du, + int *_dv) +{ + int shift; + int round; + int dv; + int n; + int d; + n = _aff->fwd[0][_v] * _l[0] + _aff->fwd[1][_v] * _l[1]; + d = _aff->fwd[0][1 - _v] * _l[0] + _aff->fwd[1][1 - _v] * _l[1]; + if (d < 0) { + n = -n; + d = -d; + } + shift = QR_MAXI(0, qr_ilog(_du) + qr_ilog(abs(n)) + 3 - QR_INT_BITS); + round = (1 << shift) >> 1; + n = n + round >> shift; + d = d + round >> shift; + /*The line should not be outside 45 degrees of horizontal/vertical. + TODO: We impose this restriction to help ensure the loop below terminates, + but it should not technically be required. + It also, however, ensures we avoid division by zero.*/ + if (abs(n) >= d) + return -1; + n = -_du * n; + dv = QR_DIVROUND(n, d); + if (abs(dv) >= _du) + return -1; + *_dv = dv; + return 0; +} + +/*Computes the Hamming distance between two bit patterns (the number of bits + that differ). + May stop counting after _maxdiff differences.*/ +static int qr_hamming_dist(unsigned _y1, unsigned _y2, int _maxdiff) +{ + unsigned y; + int ret; + y = _y1 ^ _y2; + for (ret = 0; ret < _maxdiff && y; ret++) + y &= y - 1; + return ret; +} + +/*Retrieve a bit (guaranteed to be 0 or 1) from the image, given coordinates in + subpel resolution which have not been bounds checked.*/ +static int qr_img_get_bit(const unsigned char *_img, int _width, int _height, + int _x, int _y) +{ + _x >>= QR_FINDER_SUBPREC; + _y >>= QR_FINDER_SUBPREC; + return _img[QR_CLAMPI(0, _y, _height - 1) * _width + + QR_CLAMPI(0, _x, _width - 1)] != 0; +} + +#if defined(QR_DEBUG) +#include "image.h" + +static void qr_finder_dump_aff_undistorted(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_aff *_aff, + const unsigned char *_img, + int _width, int _height) +{ + unsigned char *gimg; + FILE *fout; + int lpsz; + int pixel_size; + int dim; + int min; + int max; + int u; + int y; + int i; + int j; + lpsz = + qr_ilog(_ur->size[0] + _ur->size[1] + _dl->size[0] + _dl->size[1]) - 6; + pixel_size = 1 << lpsz; + dim = (1 << _aff->res - lpsz) + 128; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_point p; + qr_aff_project(p, _aff, (j - 64) << lpsz, (i - 64) << lpsz); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + { + min = (_ur->o[0] - 7 * _ur->size[0] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_ur->o[0] + 7 * _ur->size[0] >> lpsz) + 64; + if (max > dim) + max = dim; + for (y = -7; y <= 7; y++) { + i = (_ur->o[1] + y * _ur->size[1] >> lpsz) + 64; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_ur->o[1] - 7 * _ur->size[1] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_ur->o[1] + 7 * _ur->size[1] >> lpsz) + 64; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_ur->o[0] + u * _ur->size[0] >> lpsz) + 64; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + { + min = (_dl->o[0] - 7 * _dl->size[0] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_dl->o[0] + 7 * _dl->size[0] >> lpsz) + 64; + if (max > dim) + max = dim; + for (y = -7; y <= 7; y++) { + i = (_dl->o[1] + y * _dl->size[1] >> lpsz) + 64; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_dl->o[1] - 7 * _dl->size[1] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_dl->o[1] + 7 * _dl->size[1] >> lpsz) + 64; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_dl->o[0] + u * _dl->size[0] >> lpsz) + 64; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + fout = fopen("undistorted_aff.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} + +static void qr_finder_dump_hom_undistorted(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_hom *_hom, + const unsigned char *_img, + int _width, int _height) +{ + unsigned char *gimg; + FILE *fout; + int lpsz; + int pixel_size; + int dim; + int min; + int max; + int u; + int v; + int i; + int j; + lpsz = + qr_ilog(_ur->size[0] + _ur->size[1] + _dl->size[0] + _dl->size[1]) - 6; + pixel_size = 1 << lpsz; + dim = (1 << _hom->res - lpsz) + 256; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_point p; + qr_hom_project(p, _hom, (j - 128) << lpsz, (i - 128) << lpsz); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + { + min = (_ur->o[0] - 7 * _ur->size[0] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_ur->o[0] + 7 * _ur->size[0] >> lpsz) + 128; + if (max > dim) + max = dim; + for (v = -7; v <= 7; v++) { + i = (_ur->o[1] + v * _ur->size[1] >> lpsz) + 128; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_ur->o[1] - 7 * _ur->size[1] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_ur->o[1] + 7 * _ur->size[1] >> lpsz) + 128; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_ur->o[0] + u * _ur->size[0] >> lpsz) + 128; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + { + min = (_dl->o[0] - 7 * _dl->size[0] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_dl->o[0] + 7 * _dl->size[0] >> lpsz) + 128; + if (max > dim) + max = dim; + for (v = -7; v <= 7; v++) { + i = (_dl->o[1] + v * _dl->size[1] >> lpsz) + 128; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_dl->o[1] - 7 * _dl->size[1] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_dl->o[1] + 7 * _dl->size[1] >> lpsz) + 128; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_dl->o[0] + u * _dl->size[0] >> lpsz) + 128; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + fout = fopen("undistorted_hom.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} +#endif + +/*A homography from one region of the grid back to the image. + Unlike a qr_hom, this does not include an inverse transform and maps directly + from the grid points, not a square with power-of-two sides.*/ +struct qr_hom_cell { + int fwd[3][3]; + int x0; + int y0; + int u0; + int v0; +}; + +static void qr_hom_cell_init(qr_hom_cell *_cell, int _u0, int _v0, int _u1, + int _v1, int _u2, int _v2, int _u3, int _v3, + int _x0, int _y0, int _x1, int _y1, int _x2, + int _y2, int _x3, int _y3) +{ + int du10; + int du20; + int du30; + int du31; + int du32; + int dv10; + int dv20; + int dv30; + int dv31; + int dv32; + int dx10; + int dx20; + int dx30; + int dx31; + int dx32; + int dy10; + int dy20; + int dy30; + int dy31; + int dy32; + int a00; + int a01; + int a02; + int a10; + int a11; + int a12; + int a20; + int a21; + int a22; + int i00; + int i01; + int i10; + int i11; + int i20; + int i21; + int i22; + int b0; + int b1; + int b2; + int shift; + int round; + int x; + int y; + int w; + /*First, correct for the arrangement of the source points. + We take advantage of the fact that we know the source points have a very + limited dynamic range (so there will never be overflow) and a small amount + of projective distortion.*/ + du10 = _u1 - _u0; + du20 = _u2 - _u0; + du30 = _u3 - _u0; + du31 = _u3 - _u1; + du32 = _u3 - _u2; + dv10 = _v1 - _v0; + dv20 = _v2 - _v0; + dv30 = _v3 - _v0; + dv31 = _v3 - _v1; + dv32 = _v3 - _v2; + /*Compute the coefficients of the forward transform from the unit square to + the source point configuration.*/ + a20 = du32 * dv10 - du10 * dv32; + a21 = du20 * dv31 - du31 * dv20; + if (a20 || a21) + a22 = du32 * dv31 - du31 * dv32; + /*If the source grid points aren't in a non-affine arrangement, there's no + reason to scale everything by du32*dv31-du31*dv32. + Not doing so allows a much larger dynamic range, and is the only way we can + initialize a base cell that covers the whole grid.*/ + else + a22 = 1; + a00 = du10 * (a20 + a22); + a01 = du20 * (a21 + a22); + a10 = dv10 * (a20 + a22); + a11 = dv20 * (a21 + a22); + /*Now compute the inverse transform.*/ + i00 = a11 * a22; + i01 = -a01 * a22; + i10 = -a10 * a22; + i11 = a00 * a22; + i20 = a10 * a21 - a11 * a20; + i21 = a01 * a20 - a00 * a21; + i22 = a00 * a11 - a01 * a10; + /*Invert the coefficients. + Since i22 is the largest, we divide it by all the others. + The quotient is often exact (e.g., when the source points contain no + projective distortion), and is never zero. + Hence we can use zero to signal "infinity" when the divisor is zero.*/ + if (i00) + i00 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i00)), i00); + if (i01) + i01 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i01)), i01); + if (i10) + i10 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i10)), i10); + if (i11) + i11 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i11)), i11); + if (i20) + i20 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i20)), i20); + if (i21) + i21 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i21)), i21); + /*Now compute the map from the unit square into the image.*/ + dx10 = _x1 - _x0; + dx20 = _x2 - _x0; + dx30 = _x3 - _x0; + dx31 = _x3 - _x1; + dx32 = _x3 - _x2; + dy10 = _y1 - _y0; + dy20 = _y2 - _y0; + dy30 = _y3 - _y0; + dy31 = _y3 - _y1; + dy32 = _y3 - _y2; + a20 = dx32 * dy10 - dx10 * dy32; + a21 = dx20 * dy31 - dx31 * dy20; + a22 = dx32 * dy31 - dx31 * dy32; + /*Figure out if we need to downscale anything.*/ + b0 = qr_ilog(QR_MAXI(abs(dx10), abs(dy10))) + qr_ilog(abs(a20 + a22)); + b1 = qr_ilog(QR_MAXI(abs(dx20), abs(dy20))) + qr_ilog(abs(a21 + a22)); + b2 = qr_ilog(QR_MAXI(QR_MAXI(abs(a20), abs(a21)), abs(a22))); + shift = QR_MAXI(0, QR_MAXI(QR_MAXI(b0, b1), b2) - + (QR_INT_BITS - 3 - QR_ALIGN_SUBPREC)); + round = (1 << shift) >> 1; + /*Compute the final coefficients of the forward transform.*/ + a00 = QR_FIXMUL(dx10, a20 + a22, round, shift); + a01 = QR_FIXMUL(dx20, a21 + a22, round, shift); + a10 = QR_FIXMUL(dy10, a20 + a22, round, shift); + a11 = QR_FIXMUL(dy20, a21 + a22, round, shift); + /*And compose the two transforms. + Since we inverted the coefficients above, we divide by them here instead + of multiplying. + This lets us take advantage of the full dynamic range. + Note a zero divisor is really "infinity", and thus the quotient should also + be zero.*/ + _cell->fwd[0][0] = + (i00 ? QR_DIVROUND(a00, i00) : 0) + (i10 ? QR_DIVROUND(a01, i10) : 0); + _cell->fwd[0][1] = + (i01 ? QR_DIVROUND(a00, i01) : 0) + (i11 ? QR_DIVROUND(a01, i11) : 0); + _cell->fwd[1][0] = + (i00 ? QR_DIVROUND(a10, i00) : 0) + (i10 ? QR_DIVROUND(a11, i10) : 0); + _cell->fwd[1][1] = + (i01 ? QR_DIVROUND(a10, i01) : 0) + (i11 ? QR_DIVROUND(a11, i11) : 0); + _cell->fwd[2][0] = (i00 ? QR_DIVROUND(a20, i00) : 0) + + (i10 ? QR_DIVROUND(a21, i10) : 0) + + (i20 ? QR_DIVROUND(a22, i20) : 0) + round >> + shift; + _cell->fwd[2][1] = (i01 ? QR_DIVROUND(a20, i01) : 0) + + (i11 ? QR_DIVROUND(a21, i11) : 0) + + (i21 ? QR_DIVROUND(a22, i21) : 0) + round >> + shift; + _cell->fwd[2][2] = a22 + round >> shift; + /*Mathematically, a02 and a12 are exactly zero. + However, that concentrates all of the rounding error in the (_u3,_v3) + corner; we compute offsets which distribute it over the whole range.*/ + x = _cell->fwd[0][0] * du10 + _cell->fwd[0][1] * dv10; + y = _cell->fwd[1][0] * du10 + _cell->fwd[1][1] * dv10; + w = _cell->fwd[2][0] * du10 + _cell->fwd[2][1] * dv10 + _cell->fwd[2][2]; + a02 = dx10 * w - x; + a12 = dy10 * w - y; + x = _cell->fwd[0][0] * du20 + _cell->fwd[0][1] * dv20; + y = _cell->fwd[1][0] * du20 + _cell->fwd[1][1] * dv20; + w = _cell->fwd[2][0] * du20 + _cell->fwd[2][1] * dv20 + _cell->fwd[2][2]; + a02 += dx20 * w - x; + a12 += dy20 * w - y; + x = _cell->fwd[0][0] * du30 + _cell->fwd[0][1] * dv30; + y = _cell->fwd[1][0] * du30 + _cell->fwd[1][1] * dv30; + w = _cell->fwd[2][0] * du30 + _cell->fwd[2][1] * dv30 + _cell->fwd[2][2]; + a02 += dx30 * w - x; + a12 += dy30 * w - y; + _cell->fwd[0][2] = a02 + 2 >> 2; + _cell->fwd[1][2] = a12 + 2 >> 2; + _cell->x0 = _x0; + _cell->y0 = _y0; + _cell->u0 = _u0; + _cell->v0 = _v0; +} + +/*Finish a partial projection, converting from homogeneous coordinates to the + normal 2-D representation. + In loops, we can avoid many multiplies by computing the homogeneous _x, _y, + and _w incrementally, but we cannot avoid the divisions, done here.*/ +static void qr_hom_cell_fproject(qr_point _p, const qr_hom_cell *_cell, int _x, + int _y, int _w) +{ + if (_w == 0) { + _p[0] = _x < 0 ? INT_MIN : INT_MAX; + _p[1] = _y < 0 ? INT_MIN : INT_MAX; + } else { + if (_w < 0) { + _x = -_x; + _y = -_y; + _w = -_w; + } + _p[0] = QR_DIVROUND(_x, _w) + _cell->x0; + _p[1] = QR_DIVROUND(_y, _w) + _cell->y0; + } +} + +static void qr_hom_cell_project(qr_point _p, const qr_hom_cell *_cell, int _u, + int _v, int _res) +{ + _u -= _cell->u0 << _res; + _v -= _cell->v0 << _res; + qr_hom_cell_fproject(_p, _cell, + _cell->fwd[0][0] * _u + _cell->fwd[0][1] * _v + + (_cell->fwd[0][2] << _res), + _cell->fwd[1][0] * _u + _cell->fwd[1][1] * _v + + (_cell->fwd[1][2] << _res), + _cell->fwd[2][0] * _u + _cell->fwd[2][1] * _v + + (_cell->fwd[2][2] << _res)); +} + +/*Retrieves the bits corresponding to the alignment pattern template centered + at the given location in the original image (at subpel precision).*/ +static unsigned qr_alignment_pattern_fetch(qr_point _p[5][5], int _x0, int _y0, + const unsigned char *_img, + int _width, int _height) +{ + unsigned v; + int i; + int j; + int k; + int dx; + int dy; + dx = _x0 - _p[2][2][0]; + dy = _y0 - _p[2][2][1]; + v = 0; + for (k = i = 0; i < 5; i++) + for (j = 0; j < 5; j++, k++) { + v |= qr_img_get_bit(_img, _width, _height, _p[i][j][0] + dx, + _p[i][j][1] + dy) + << k; + } + return v; +} + +/*Searches for an alignment pattern near the given location.*/ +static int qr_alignment_pattern_search(qr_point _p, const qr_hom_cell *_cell, + int _u, int _v, int _r, + const unsigned char *_img, int _width, + int _height) +{ + qr_point c[4]; + int nc[4]; + qr_point p[5][5]; + qr_point pc; + unsigned best_match; + int best_dist; + int bestx; + int besty; + unsigned match; + int dist; + int u; + int v; + int x0; + int y0; + int w0; + int x; + int y; + int w; + int dxdu; + int dydu; + int dwdu; + int dxdv; + int dydv; + int dwdv; + int dx; + int dy; + int i; + int j; + /*Build up a basic template using _cell to control shape and scale. + We project the points in the template back to the image just once, since if + the alignment pattern has moved, we don't really know why. + If it's because of radial distortion, or the code wasn't flat, or something + else, there's no reason to expect that a re-projection around each + subsequent search point would be any closer to the actual shape than our + first projection. + Therefore we simply slide this template around, as is.*/ + u = (_u - 2) - _cell->u0; + v = (_v - 2) - _cell->v0; + x0 = _cell->fwd[0][0] * u + _cell->fwd[0][1] * v + _cell->fwd[0][2]; + y0 = _cell->fwd[1][0] * u + _cell->fwd[1][1] * v + _cell->fwd[1][2]; + w0 = _cell->fwd[2][0] * u + _cell->fwd[2][1] * v + _cell->fwd[2][2]; + dxdu = _cell->fwd[0][0]; + dydu = _cell->fwd[1][0]; + dwdu = _cell->fwd[2][0]; + dxdv = _cell->fwd[0][1]; + dydv = _cell->fwd[1][1]; + dwdv = _cell->fwd[2][1]; + for (i = 0; i < 5; i++) { + x = x0; + y = y0; + w = w0; + for (j = 0; j < 5; j++) { + qr_hom_cell_fproject(p[i][j], _cell, x, y, w); + x += dxdu; + y += dydu; + w += dwdu; + } + x0 += dxdv; + y0 += dydv; + w0 += dwdv; + } + bestx = p[2][2][0]; + besty = p[2][2][1]; + best_match = + qr_alignment_pattern_fetch(p, bestx, besty, _img, _width, _height); + best_dist = qr_hamming_dist(best_match, 0x1F8D63F, 25); + if (best_dist > 0) { + u = _u - _cell->u0; + v = _v - _cell->v0; + x = _cell->fwd[0][0] * u + _cell->fwd[0][1] * v + _cell->fwd[0][2] + << QR_ALIGN_SUBPREC; + y = _cell->fwd[1][0] * u + _cell->fwd[1][1] * v + _cell->fwd[1][2] + << QR_ALIGN_SUBPREC; + w = _cell->fwd[2][0] * u + _cell->fwd[2][1] * v + _cell->fwd[2][2] + << QR_ALIGN_SUBPREC; + /*Search an area at most _r modules around the target location, in + concentric squares..*/ + for (i = 1; i < _r << QR_ALIGN_SUBPREC; i++) { + int side_len; + side_len = (i << 1) - 1; + x -= dxdu + dxdv; + y -= dydu + dydv; + w -= dwdu + dwdv; + for (j = 0; j < 4 * side_len; j++) { + int dir; + qr_hom_cell_fproject(pc, _cell, x, y, w); + match = qr_alignment_pattern_fetch(p, pc[0], pc[1], _img, + _width, _height); + dist = qr_hamming_dist(match, 0x1F8D63F, best_dist + 1); + if (dist < best_dist) { + best_match = match; + best_dist = dist; + bestx = pc[0]; + besty = pc[1]; + } + if (j < 2 * side_len) { + dir = j >= side_len; + x += _cell->fwd[0][dir]; + y += _cell->fwd[1][dir]; + w += _cell->fwd[2][dir]; + } else { + dir = j >= 3 * side_len; + x -= _cell->fwd[0][dir]; + y -= _cell->fwd[1][dir]; + w -= _cell->fwd[2][dir]; + } + if (!best_dist) + break; + } + if (!best_dist) + break; + } + } + /*If the best result we got was sufficiently bad, reject the match. + If we're wrong and we include it, we can grossly distort the nearby + region, whereas using the initial starting point should at least be + consistent with the geometry we already have.*/ + if (best_dist > 6) { + _p[0] = p[2][2][0]; + _p[1] = p[2][2][1]; + return -1; + } + /*Now try to get a more accurate location of the pattern center.*/ + dx = bestx - p[2][2][0]; + dy = besty - p[2][2][1]; + memset(nc, 0, sizeof(nc)); + memset(c, 0, sizeof(c)); + /*We consider 8 lines across the finder pattern in turn. + If we actually found a symmetric pattern along that line, search for its + exact center in the image. + There are plenty more lines we could use if these don't work, but if we've + found anything remotely close to an alignment pattern, we should be able + to use most of these.*/ + for (i = 0; i < 8; i++) { + static const unsigned MASK_TESTS[8][2] = { + { 0x1040041, 0x1000001 }, { 0x0041040, 0x0001000 }, + { 0x0110110, 0x0100010 }, { 0x0011100, 0x0001000 }, + { 0x0420084, 0x0400004 }, { 0x0021080, 0x0001000 }, + { 0x0006C00, 0x0004400 }, { 0x0003800, 0x0001000 }, + }; + static const unsigned char MASK_COORDS[8][2] = { { 0, 0 }, { 1, 1 }, + { 4, 0 }, { 3, 1 }, + { 2, 0 }, { 2, 1 }, + { 0, 2 }, { 1, 2 } }; + if ((best_match & MASK_TESTS[i][0]) == MASK_TESTS[i][1]) { + int x0; + int y0; + int x1; + int y1; + x0 = p[MASK_COORDS[i][1]][MASK_COORDS[i][0]][0] + dx >> + QR_FINDER_SUBPREC; + if (x0 < 0 || x0 >= _width) + continue; + y0 = p[MASK_COORDS[i][1]][MASK_COORDS[i][0]][1] + dy >> + QR_FINDER_SUBPREC; + if (y0 < 0 || y0 >= _height) + continue; + x1 = p[4 - MASK_COORDS[i][1]][4 - MASK_COORDS[i][0]][0] + dx >> + QR_FINDER_SUBPREC; + if (x1 < 0 || x1 >= _width) + continue; + y1 = p[4 - MASK_COORDS[i][1]][4 - MASK_COORDS[i][0]][1] + dy >> + QR_FINDER_SUBPREC; + if (y1 < 0 || y1 >= _height) + continue; + if (!qr_finder_locate_crossing(_img, _width, _height, x0, y0, x1, + y1, i & 1, pc)) { + int w; + int cx; + int cy; + cx = pc[0] - bestx; + cy = pc[1] - besty; + if (i & 1) { + /*Weight crossings around the center dot more highly, as they are + generally more reliable.*/ + w = 3; + cx += cx << 1; + cy += cy << 1; + } else + w = 1; + nc[i >> 1] += w; + c[i >> 1][0] += cx; + c[i >> 1][1] += cy; + } + } + } + /*Sum offsets from lines in orthogonal directions.*/ + for (i = 0; i < 2; i++) { + int a; + int b; + a = nc[i << 1]; + b = nc[i << 1 | 1]; + if (a && b) { + int w; + w = QR_MAXI(a, b); + c[i << 1][0] = + QR_DIVROUND(w * (b * c[i << 1][0] + a * c[i << 1 | 1][0]), + a * b); + c[i << 1][1] = + QR_DIVROUND(w * (b * c[i << 1][1] + a * c[i << 1 | 1][1]), + a * b); + nc[i << 1] = w << 1; + } else { + c[i << 1][0] += c[i << 1 | 1][0]; + c[i << 1][1] += c[i << 1 | 1][1]; + nc[i << 1] += b; + } + } + /*Average offsets from pairs of orthogonal lines.*/ + c[0][0] += c[2][0]; + c[0][1] += c[2][1]; + nc[0] += nc[2]; + /*If we actually found any such lines, apply the adjustment.*/ + if (nc[0]) { + dx = QR_DIVROUND(c[0][0], nc[0]); + dy = QR_DIVROUND(c[0][1], nc[0]); + /*But only if it doesn't make things too much worse.*/ + match = qr_alignment_pattern_fetch(p, bestx + dx, besty + dy, _img, + _width, _height); + dist = qr_hamming_dist(match, 0x1F8D63F, best_dist + 1); + if (dist <= best_dist + 1) { + bestx += dx; + besty += dy; + } + } + _p[0] = bestx; + _p[1] = besty; + return 0; +} + +static int qr_hom_fit(qr_hom *_hom, qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_point _p[4], const qr_aff *_aff, + isaac_ctx *_isaac, const unsigned char *_img, int _width, + int _height) +{ + qr_point *b; + int nb; + int cb; + qr_point *r; + int nr; + int cr; + qr_line l[4]; + qr_point q; + qr_point p; + int ox; + int oy; + int ru; + int rv; + int dru; + int drv; + int bu; + int bv; + int dbu; + int dbv; + int rx; + int ry; + int drxi; + int dryi; + int drxj; + int dryj; + int rdone; + int nrempty; + int rlastfit; + int bx; + int by; + int dbxi; + int dbyi; + int dbxj; + int dbyj; + int bdone; + int nbempty; + int blastfit; + int shift; + int round; + int version4; + int brx; + int bry; + int i; + /*We attempt to correct large-scale perspective distortion by fitting lines + to the edge of the code area. + We could also look for an alignment pattern now, but that wouldn't work for + version 1 codes, which have no alignment pattern. + Even if the code is supposed to have one, there's go guarantee we'd find it + intact.*/ + /*Fitting lines is easy for the edges on which we have two finder patterns. + After the fit, UL is guaranteed to be on the proper side, but if either of + the other two finder patterns aren't, something is wrong.*/ + qr_finder_ransac(_ul, _aff, _isaac, 0); + qr_finder_ransac(_dl, _aff, _isaac, 0); + qr_line_fit_finder_pair(l[0], _aff, _ul, _dl, 0); + if (qr_line_eval(l[0], _dl->c->pos[0], _dl->c->pos[1]) < 0 || + qr_line_eval(l[0], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + qr_finder_ransac(_ul, _aff, _isaac, 2); + qr_finder_ransac(_ur, _aff, _isaac, 2); + qr_line_fit_finder_pair(l[2], _aff, _ul, _ur, 2); + if (qr_line_eval(l[2], _dl->c->pos[0], _dl->c->pos[1]) < 0 || + qr_line_eval(l[2], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + /*The edges which only have one finder pattern are more difficult. + We start by fitting a line to the edge of the one finder pattern we do + have. + This can fail due to an insufficient number of sample points, and even if + it succeeds can be fairly inaccurate, because all of the points are + clustered in one corner of the QR code. + If it fails, we just use an axis-aligned line in the affine coordinate + system. + Then we walk along the edge of the entire code, looking for + light:dark:light patterns perpendicular to the edge. + Wherever we find one, we take the center of the dark portion as an + additional sample point. + At the end, we re-fit the line using all such sample points found.*/ + drv = _ur->size[1] >> 1; + qr_finder_ransac(_ur, _aff, _isaac, 1); + if (qr_line_fit_finder_edge(l[1], _ur, 1, _aff->res) >= 0) { + if (qr_line_eval(l[1], _ul->c->pos[0], _ul->c->pos[1]) < 0 || + qr_line_eval(l[1], _dl->c->pos[0], _dl->c->pos[1]) < 0) { + return -1; + } + /*Figure out the change in ru for a given change in rv when stepping along + the fitted line.*/ + if (qr_aff_line_step(_aff, l[1], 1, drv, &dru) < 0) + return -1; + } else + dru = 0; + ru = _ur->o[0] + 3 * _ur->size[0] - 2 * dru; + rv = _ur->o[1] - 2 * drv; + dbu = _dl->size[0] >> 1; + qr_finder_ransac(_dl, _aff, _isaac, 3); + if (qr_line_fit_finder_edge(l[3], _dl, 3, _aff->res) >= 0) { + if (qr_line_eval(l[3], _ul->c->pos[0], _ul->c->pos[1]) < 0 || + qr_line_eval(l[3], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + /*Figure out the change in bv for a given change in bu when stepping along + the fitted line.*/ + if (qr_aff_line_step(_aff, l[3], 0, dbu, &dbv) < 0) + return -1; + } else + dbv = 0; + bu = _dl->o[0] - 2 * dbu; + bv = _dl->o[1] + 3 * _dl->size[1] - 2 * dbv; + /*Set up the initial point lists.*/ + nr = rlastfit = _ur->ninliers[1]; + cr = nr + (_dl->o[1] - rv + drv - 1) / drv; + r = (qr_point *)malloc(cr * sizeof(*r)); + for (i = 0; i < _ur->ninliers[1]; i++) { + memcpy(r[i], _ur->edge_pts[1][i].pos, sizeof(r[i])); + } + nb = blastfit = _dl->ninliers[3]; + cb = nb + (_ur->o[0] - bu + dbu - 1) / dbu; + b = (qr_point *)malloc(cb * sizeof(*b)); + for (i = 0; i < _dl->ninliers[3]; i++) { + memcpy(b[i], _dl->edge_pts[3][i].pos, sizeof(b[i])); + } + /*Set up the step parameters for the affine projection.*/ + ox = (_aff->x0 << _aff->res) + (1 << _aff->res - 1); + oy = (_aff->y0 << _aff->res) + (1 << _aff->res - 1); + rx = _aff->fwd[0][0] * ru + _aff->fwd[0][1] * rv + ox; + ry = _aff->fwd[1][0] * ru + _aff->fwd[1][1] * rv + oy; + drxi = _aff->fwd[0][0] * dru + _aff->fwd[0][1] * drv; + dryi = _aff->fwd[1][0] * dru + _aff->fwd[1][1] * drv; + drxj = _aff->fwd[0][0] * _ur->size[0]; + dryj = _aff->fwd[1][0] * _ur->size[0]; + bx = _aff->fwd[0][0] * bu + _aff->fwd[0][1] * bv + ox; + by = _aff->fwd[1][0] * bu + _aff->fwd[1][1] * bv + oy; + dbxi = _aff->fwd[0][0] * dbu + _aff->fwd[0][1] * dbv; + dbyi = _aff->fwd[1][0] * dbu + _aff->fwd[1][1] * dbv; + dbxj = _aff->fwd[0][1] * _dl->size[1]; + dbyj = _aff->fwd[1][1] * _dl->size[1]; + /*Now step along the lines, looking for new sample points.*/ + nrempty = nbempty = 0; + for (;;) { + int ret; + int x0; + int y0; + int x1; + int y1; + /*If we take too many steps without encountering a non-zero pixel, assume + we have wandered off the edge and stop looking before we hit the other + side of the quiet region. + Otherwise, stop when the lines cross (if they do so inside the affine + region) or come close to crossing (outside the affine region). + TODO: We don't have any way of detecting when we've wandered into the + code interior; we could stop if the outside sample ever shows up dark, + but this could happen because of noise in the quiet region, too.*/ + rdone = rv >= QR_MINI(bv, _dl->o[1] + bv >> 1) || nrempty > 14; + bdone = bu >= QR_MINI(ru, _ur->o[0] + ru >> 1) || nbempty > 14; + if (!rdone && (bdone || rv < bu)) { + x0 = rx + drxj >> _aff->res + QR_FINDER_SUBPREC; + y0 = ry + dryj >> _aff->res + QR_FINDER_SUBPREC; + x1 = rx - drxj >> _aff->res + QR_FINDER_SUBPREC; + y1 = ry - dryj >> _aff->res + QR_FINDER_SUBPREC; + if (nr >= cr) { + cr = cr << 1 | 1; + r = (qr_point *)realloc(r, cr * sizeof(*r)); + } + ret = qr_finder_quick_crossing_check(_img, _width, _height, x0, y0, + x1, y1, 1); + if (!ret) { + ret = qr_finder_locate_crossing(_img, _width, _height, x0, y0, + x1, y1, 1, r[nr]); + } + if (ret >= 0) { + if (!ret) { + qr_aff_unproject(q, _aff, r[nr][0], r[nr][1]); + /*Move the current point halfway towards the crossing. + We don't move the whole way to give us some robustness to noise.*/ + ru = ru + q[0] >> 1; + /*But ensure that rv monotonically increases.*/ + if (q[1] + drv > rv) + rv = rv + q[1] >> 1; + rx = _aff->fwd[0][0] * ru + _aff->fwd[0][1] * rv + ox; + ry = _aff->fwd[1][0] * ru + _aff->fwd[1][1] * rv + oy; + nr++; + /*Re-fit the line to update the step direction periodically.*/ + if (nr > QR_MAXI(1, rlastfit + (rlastfit >> 2))) { + qr_line_fit_points(l[1], r, nr, _aff->res); + if (qr_aff_line_step(_aff, l[1], 1, drv, &dru) >= 0) { + drxi = + _aff->fwd[0][0] * dru + _aff->fwd[0][1] * drv; + dryi = + _aff->fwd[1][0] * dru + _aff->fwd[1][1] * drv; + } + rlastfit = nr; + } + } + nrempty = 0; + } else + nrempty++; + ru += dru; + /*Our final defense: if we overflow, stop.*/ + if (rv + drv > rv) + rv += drv; + else + nrempty = INT_MAX; + rx += drxi; + ry += dryi; + } else if (!bdone) { + x0 = bx + dbxj >> _aff->res + QR_FINDER_SUBPREC; + y0 = by + dbyj >> _aff->res + QR_FINDER_SUBPREC; + x1 = bx - dbxj >> _aff->res + QR_FINDER_SUBPREC; + y1 = by - dbyj >> _aff->res + QR_FINDER_SUBPREC; + if (nb >= cb) { + cb = cb << 1 | 1; + b = (qr_point *)realloc(b, cb * sizeof(*b)); + } + ret = qr_finder_quick_crossing_check(_img, _width, _height, x0, y0, + x1, y1, 1); + if (!ret) { + ret = qr_finder_locate_crossing(_img, _width, _height, x0, y0, + x1, y1, 1, b[nb]); + } + if (ret >= 0) { + if (!ret) { + qr_aff_unproject(q, _aff, b[nb][0], b[nb][1]); + /*Move the current point halfway towards the crossing. + We don't move the whole way to give us some robustness to noise.*/ + /*But ensure that bu monotonically increases.*/ + if (q[0] + dbu > bu) + bu = bu + q[0] >> 1; + bv = bv + q[1] >> 1; + bx = _aff->fwd[0][0] * bu + _aff->fwd[0][1] * bv + ox; + by = _aff->fwd[1][0] * bu + _aff->fwd[1][1] * bv + oy; + nb++; + /*Re-fit the line to update the step direction periodically.*/ + if (nb > QR_MAXI(1, blastfit + (blastfit >> 2))) { + qr_line_fit_points(l[3], b, nb, _aff->res); + if (qr_aff_line_step(_aff, l[3], 0, dbu, &dbv) >= 0) { + dbxi = + _aff->fwd[0][0] * dbu + _aff->fwd[0][1] * dbv; + dbyi = + _aff->fwd[1][0] * dbu + _aff->fwd[1][1] * dbv; + } + blastfit = nb; + } + } + nbempty = 0; + } else + nbempty++; + /*Our final defense: if we overflow, stop.*/ + if (bu + dbu > bu) + bu += dbu; + else + nbempty = INT_MAX; + bv += dbv; + bx += dbxi; + by += dbyi; + } else + break; + } + /*Fit the new lines. + If we _still_ don't have enough sample points, then just use an + axis-aligned line from the affine coordinate system (e.g., one parallel + to the opposite edge in the image).*/ + if (nr > 1) + qr_line_fit_points(l[1], r, nr, _aff->res); + else { + qr_aff_project(p, _aff, _ur->o[0] + 3 * _ur->size[0], _ur->o[1]); + shift = QR_MAXI(0, qr_ilog(QR_MAXI(abs(_aff->fwd[0][1]), + abs(_aff->fwd[1][1]))) - + (_aff->res + 1 >> 1)); + round = (1 << shift) >> 1; + l[1][0] = _aff->fwd[1][1] + round >> shift; + l[1][1] = -_aff->fwd[0][1] + round >> shift; + l[1][2] = -(l[1][0] * p[0] + l[1][1] * p[1]); + } + free(r); + if (nb > 1) + qr_line_fit_points(l[3], b, nb, _aff->res); + else { + qr_aff_project(p, _aff, _dl->o[0], _dl->o[1] + 3 * _dl->size[1]); + shift = QR_MAXI(0, qr_ilog(QR_MAXI(abs(_aff->fwd[0][1]), + abs(_aff->fwd[1][1]))) - + (_aff->res + 1 >> 1)); + round = (1 << shift) >> 1; + l[3][0] = _aff->fwd[1][0] + round >> shift; + l[3][1] = -_aff->fwd[0][0] + round >> shift; + l[3][2] = -(l[1][0] * p[0] + l[1][1] * p[1]); + } + free(b); + for (i = 0; i < 4; i++) { + if (qr_line_isect(_p[i], l[i & 1], l[2 + (i >> 1)]) < 0) + return -1; + /*It's plausible for points to be somewhat outside the image, but too far + and too much of the pattern will be gone for it to be decodable.*/ + if (_p[i][0] < -_width << QR_FINDER_SUBPREC || + _p[i][0] >= _width << QR_FINDER_SUBPREC + 1 || + _p[i][1] < -_height << QR_FINDER_SUBPREC || + _p[i][1] >= _height << QR_FINDER_SUBPREC + 1) { + return -1; + } + } + /*By default, use the edge intersection point for the bottom-right corner.*/ + brx = _p[3][0]; + bry = _p[3][1]; + /*However, if our average version estimate is greater than 1, NOW we try to + search for an alignment pattern. + We get a much better success rate by doing this after our initial attempt + to promote the transform to a homography than before. + You might also think it would be more reliable to use the interior finder + pattern edges, since the outer ones may be obscured or damaged, and it + would save us a reprojection below, since they would form a nice square + with the location of the alignment pattern, but this turns out to be a bad + idea. + Non-linear distortion is usually maximal on the outside edge, and thus + estimating the grid position from points on the interior means we might + get mis-aligned by the time we reach the edge.*/ + version4 = _ul->eversion[0] + _ul->eversion[1] + _ur->eversion[0] + + _dl->eversion[1]; + if (version4 > 4) { + qr_hom_cell cell; + qr_point p3; + int dim; + dim = 17 + version4; + qr_hom_cell_init(&cell, 0, 0, dim - 1, 0, 0, dim - 1, dim - 1, dim - 1, + _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], + _p[2][1], _p[3][0], _p[3][1]); + if (qr_alignment_pattern_search(p3, &cell, dim - 7, dim - 7, 4, _img, + _width, _height) >= 0) { + long long w; + long long mask; + int c21; + int dx21; + int dy21; + /*There's no real need to update the bounding box corner, and in fact we + actively perform worse if we do. + Clearly it was good enough for us to find this alignment pattern, so + it should be good enough to use for grid initialization. + The point of doing the search was to get more accurate version + estimates and a better chance of decoding the version and format info. + This is particularly important for small versions that have no encoded + version info, since any mismatch in version renders the code + undecodable.*/ + /*We do, however, need four points in a square to initialize our + homography, so project the point from the alignment center to the + corner of the code area.*/ + c21 = _p[2][0] * _p[1][1] - _p[2][1] * _p[1][0]; + dx21 = _p[2][0] - _p[1][0]; + dy21 = _p[2][1] - _p[1][1]; + w = QR_EXTMUL(dim - 7, c21, + QR_EXTMUL(dim - 13, _p[0][0] * dy21 - _p[0][1] * dx21, + QR_EXTMUL(6, p3[0] * dy21 - p3[1] * dx21, + 0))); + /*The projection failed: invalid geometry.*/ + if (w == 0) + return -1; + mask = QR_SIGNMASK(w); + w = w + mask ^ mask; + brx = (int)QR_DIVROUND( + QR_EXTMUL((dim - 7) * _p[0][0], p3[0] * dy21, + QR_EXTMUL((dim - 13) * p3[0], c21 - _p[0][1] * dx21, + QR_EXTMUL(6 * _p[0][0], c21 - p3[1] * dx21, + 0))) + + mask ^ + mask, + w); + bry = (int)QR_DIVROUND( + QR_EXTMUL((dim - 7) * _p[0][1], -p3[1] * dx21, + QR_EXTMUL((dim - 13) * p3[1], c21 + _p[0][0] * dy21, + QR_EXTMUL(6 * _p[0][1], c21 + p3[0] * dy21, + 0))) + + mask ^ + mask, + w); + } + } + /*Now we have four points that map to a square: initialize the projection.*/ + qr_hom_init(_hom, _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], + _p[2][1], brx, bry, QR_HOM_BITS); + return 0; +} + +/*The BCH(18,6,3) codes are only used for version information, which must lie + between 7 and 40 (inclusive).*/ +static const unsigned BCH18_6_CODES[34] = { + 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, + 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, + 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, + 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, + 0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69 +}; + +/*Corrects a BCH(18,6,3) code word. + _y: Contains the code word to be checked on input, and the corrected value on + output. + Return: The number of errors. + If more than 3 errors are detected, returns a negative value and + performs no correction.*/ +static int bch18_6_correct(unsigned *_y) +{ + unsigned x; + unsigned y; + int nerrs; + y = *_y; + /*Check the easy case first: see if the data bits were uncorrupted.*/ + x = y >> 12; + if (x >= 7 && x <= 40) { + nerrs = qr_hamming_dist(y, BCH18_6_CODES[x - 7], 4); + if (nerrs < 4) { + *_y = BCH18_6_CODES[x - 7]; + return nerrs; + } + } + /*Exhaustive search is faster than field operations in GF(19).*/ + for (x = 0; x < 34; x++) + if (x + 7 != y >> 12) { + nerrs = qr_hamming_dist(y, BCH18_6_CODES[x], 4); + if (nerrs < 4) { + *_y = BCH18_6_CODES[x]; + return nerrs; + } + } + return -1; +} + +#if 0 +static unsigned bch18_6_encode(unsigned _x){ + return (-(_x&1)&0x01F25)^(-(_x>>1&1)&0x0216F)^(-(_x>>2&1)&0x042DE)^ + (-(_x>>3&1)&0x085BC)^(-(_x>>4&1)&0x10B78)^(-(_x>>5&1)&0x209D5); +} +#endif + +/*Reads the version bits near a finder module and decodes the version number.*/ +static int qr_finder_version_decode(qr_finder *_f, const qr_hom *_hom, + const unsigned char *_img, int _width, + int _height, int _dir) +{ + qr_point q; + unsigned v; + int x0; + int y0; + int w0; + int dxi; + int dyi; + int dwi; + int dxj; + int dyj; + int dwj; + int ret; + int i; + int j; + int k; + v = 0; + q[_dir] = _f->o[_dir] - 7 * _f->size[_dir]; + q[1 - _dir] = _f->o[1 - _dir] - 3 * _f->size[1 - _dir]; + x0 = _hom->fwd[0][0] * q[0] + _hom->fwd[0][1] * q[1]; + y0 = _hom->fwd[1][0] * q[0] + _hom->fwd[1][1] * q[1]; + w0 = _hom->fwd[2][0] * q[0] + _hom->fwd[2][1] * q[1] + _hom->fwd22; + dxi = _hom->fwd[0][1 - _dir] * _f->size[1 - _dir]; + dyi = _hom->fwd[1][1 - _dir] * _f->size[1 - _dir]; + dwi = _hom->fwd[2][1 - _dir] * _f->size[1 - _dir]; + dxj = _hom->fwd[0][_dir] * _f->size[_dir]; + dyj = _hom->fwd[1][_dir] * _f->size[_dir]; + dwj = _hom->fwd[2][_dir] * _f->size[_dir]; + for (k = i = 0; i < 6; i++) { + int x; + int y; + int w; + x = x0; + y = y0; + w = w0; + for (j = 0; j < 3; j++, k++) { + qr_point p; + qr_hom_fproject(p, _hom, x, y, w); + v |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dxj; + y += dyj; + w += dwj; + } + x0 += dxi; + y0 += dyi; + w0 += dwi; + } + ret = bch18_6_correct(&v); + /*TODO: I seem to have an image with the version bits in a different order + (the transpose of the standard order). + Even if I change the order here so I can parse the version on this image, + I can't decode the rest of the code. + If this is really needed, we should just re-order the bits.*/ +#if 0 + if(ret<0){ + /*17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + 0 3 6 9 12 15 1 4 7 10 13 16 2 5 8 11 14 17 + 17 13 9 5 1 -3 10 6 2 -2 -6-10 3 -1 -5 -9-13-17*/ + v=0; + for(k=i=0;i<3;i++){ + p[_dir]=_f->o[_dir]+_f->size[_dir]*(-5-i); + for(j=0;j<6;j++,k++){ + qr_point q; + p[1-_dir]=_f->o[1-_dir]+_f->size[1-_dir]*(2-j); + qr_hom_project(q,_hom,p[0],p[1]); + v|=qr_img_get_bit(_img,_width,_height,q[0],q[1])<<k; + } + } + ret=bch18_6_correct(&v); + } +#endif + return ret >= 0 ? (int)(v >> 12) : ret; +} + +/*Reads the format info bits near the finder modules and decodes them.*/ +static int qr_finder_fmt_info_decode(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, const qr_hom *_hom, + const unsigned char *_img, int _width, + int _height) +{ + qr_point p; + unsigned lo[2]; + unsigned hi[2]; + int u; + int v; + int x; + int y; + int w; + int dx; + int dy; + int dw; + int fmt_info[4]; + int count[4]; + int nerrs[4]; + int nfmt_info; + int besti; + int imax; + int di; + int i; + int k; + /*Read the bits around the UL corner.*/ + lo[0] = 0; + u = _ul->o[0] + 5 * _ul->size[0]; + v = _ul->o[1] - 3 * _ul->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = _hom->fwd[0][1] * _ul->size[1]; + dy = _hom->fwd[1][1] * _ul->size[1]; + dw = _hom->fwd[2][1] * _ul->size[1]; + for (k = i = 0;; i++) { + /*Skip the timing pattern row.*/ + if (i != 6) { + qr_hom_fproject(p, _hom, x, y, w); + lo[0] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k++; + /*Don't advance q in the last iteration... we'll start the next loop from + the current position.*/ + if (i >= 8) + break; + } + x += dx; + y += dy; + w += dw; + } + hi[0] = 0; + dx = -_hom->fwd[0][0] * _ul->size[0]; + dy = -_hom->fwd[1][0] * _ul->size[0]; + dw = -_hom->fwd[2][0] * _ul->size[0]; + while (i-- > 0) { + x += dx; + y += dy; + w += dw; + /*Skip the timing pattern column.*/ + if (i != 6) { + qr_hom_fproject(p, _hom, x, y, w); + hi[0] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k++; + } + } + /*Read the bits next to the UR corner.*/ + lo[1] = 0; + u = _ur->o[0] + 3 * _ur->size[0]; + v = _ur->o[1] + 5 * _ur->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = -_hom->fwd[0][0] * _ur->size[0]; + dy = -_hom->fwd[1][0] * _ur->size[0]; + dw = -_hom->fwd[2][0] * _ur->size[0]; + for (k = 0; k < 8; k++) { + qr_hom_fproject(p, _hom, x, y, w); + lo[1] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dx; + y += dy; + w += dw; + } + /*Read the bits next to the DL corner.*/ + hi[1] = 0; + u = _dl->o[0] + 5 * _dl->size[0]; + v = _dl->o[1] - 3 * _dl->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = _hom->fwd[0][1] * _dl->size[1]; + dy = _hom->fwd[1][1] * _dl->size[1]; + dw = _hom->fwd[2][1] * _dl->size[1]; + for (k = 8; k < 15; k++) { + qr_hom_fproject(p, _hom, x, y, w); + hi[1] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dx; + y += dy; + w += dw; + } + /*For each group of bits we have two samples... try them in all combinations + and pick the most popular valid code, breaking ties using the number of + bit errors.*/ + imax = 2 << (hi[0] != hi[1]); + di = 1 + (lo[0] == lo[1]); + nfmt_info = 0; + for (i = 0; i < imax; i += di) { + unsigned v; + int ret; + int j; + v = (lo[i & 1] | hi[i >> 1]) ^ 0x5412; + ret = bch15_5_correct(&v); + v >>= 10; + if (ret < 0) + ret = 4; + for (j = 0;; j++) { + if (j >= nfmt_info) { + fmt_info[j] = v; + count[j] = 1; + nerrs[j] = ret; + nfmt_info++; + break; + } + if (fmt_info[j] == (int)v) { + count[j]++; + if (ret < nerrs[j]) + nerrs[j] = ret; + break; + } + } + } + besti = 0; + for (i = 1; i < nfmt_info; i++) { + if (nerrs[besti] > 3 && nerrs[i] <= 3 || count[i] > count[besti] || + count[i] == count[besti] && nerrs[i] < nerrs[besti]) { + besti = i; + } + } + return nerrs[besti] < 4 ? fmt_info[besti] : -1; +} + +/*The grid used to sample the image bits. + The grid is divided into separate cells bounded by finder patterns and/or + alignment patterns, and a separate map back to the original image is + constructed for each cell. + All of these structural elements, as well as the timing patterns, version + info, and format info, are marked in fpmask so they can easily be skipped + during decode.*/ +struct qr_sampling_grid { + qr_hom_cell *cells[6]; + unsigned *fpmask; + int cell_limits[6]; + int ncells; +}; + +/*Mark a given region as belonging to the function pattern.*/ +static void qr_sampling_grid_fp_mask_rect(qr_sampling_grid *_grid, int _dim, + int _u, int _v, int _w, int _h) +{ + int i; + int j; + int stride; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*Note that we store bits column-wise, since that's how they're read out of + the grid.*/ + for (j = _u; j < _u + _w; j++) + for (i = _v; i < _v + _h; i++) { + _grid->fpmask[j * stride + (i >> QR_INT_LOGBITS)] |= + 1 << (i & QR_INT_BITS - 1); + } +} + +/*Determine if a given grid location is inside the function pattern.*/ +static int qr_sampling_grid_is_in_fp(const qr_sampling_grid *_grid, int _dim, + int _u, int _v) +{ + return _grid->fpmask[_u * (_dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) + + (_v >> QR_INT_LOGBITS)] >> + (_v & QR_INT_BITS - 1) & + 1; +} + +/*The spacing between alignment patterns after the second for versions >= 7. + We could compact this more, but the code to access it would eliminate the + gains.*/ +static const unsigned char QR_ALIGNMENT_SPACING[34] = { + 16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24, + 26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28 +}; + +static inline void qr_svg_points(const char *cls, qr_point *p, int n) +{ + int i; + svg_path_start(cls, 1, 0, 0); + for (i = 0; i < n; i++, p++) + svg_path_moveto(SVG_ABS, p[0][0], p[0][1]); + svg_path_end(); +} + +/*Initialize the sampling grid for each region of the code. + _version: The (decoded) version number. + _ul_pos: The location of the UL finder pattern. + _ur_pos: The location of the UR finder pattern. + _dl_pos: The location of the DL finder pattern. + _p: On input, contains estimated positions of the four corner modules. + On output, contains a bounding quadrilateral for the code. + _img: The binary input image. + _width: The width of the input image. + _height: The height of the input image. + Return: 0 on success, or a negative value on error.*/ +static void qr_sampling_grid_init(qr_sampling_grid *_grid, int _version, + const qr_point _ul_pos, + const qr_point _ur_pos, + const qr_point _dl_pos, qr_point _p[4], + const unsigned char *_img, int _width, + int _height) +{ + qr_hom_cell base_cell; + int align_pos[7]; + int dim; + int nalign; + int i; + dim = 17 + (_version << 2); + nalign = (_version / 7) + 2; + /*Create a base cell to bootstrap the alignment pattern search.*/ + qr_hom_cell_init(&base_cell, 0, 0, dim - 1, 0, 0, dim - 1, dim - 1, dim - 1, + _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], _p[2][1], + _p[3][0], _p[3][1]); + /*Allocate the array of cells.*/ + _grid->ncells = nalign - 1; + _grid->cells[0] = (qr_hom_cell *)malloc((nalign - 1) * (nalign - 1) * + sizeof(*_grid->cells[0])); + for (i = 1; i < _grid->ncells; i++) + _grid->cells[i] = _grid->cells[i - 1] + _grid->ncells; + /*Initialize the function pattern mask.*/ + _grid->fpmask = + (unsigned *)calloc(dim, (dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) * + sizeof(*_grid->fpmask)); + /*Mask out the finder patterns (and separators and format info bits).*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, 0, 9, 9); + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, dim - 8, 9, 8); + qr_sampling_grid_fp_mask_rect(_grid, dim, dim - 8, 0, 8, 9); + /*Mask out the version number bits.*/ + if (_version > 6) { + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, dim - 11, 6, 3); + qr_sampling_grid_fp_mask_rect(_grid, dim, dim - 11, 0, 3, 6); + } + /*Mask out the timing patterns.*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, 9, 6, dim - 17, 1); + qr_sampling_grid_fp_mask_rect(_grid, dim, 6, 9, 1, dim - 17); + /*If we have no alignment patterns (e.g., this is a version 1 code), just use + the base cell and hope it's good enough.*/ + if (_version < 2) + memcpy(_grid->cells[0], &base_cell, sizeof(base_cell)); + else { + qr_point *q; + qr_point *p; + int j; + int k; + q = (qr_point *)malloc(nalign * nalign * sizeof(*q)); + p = (qr_point *)malloc(nalign * nalign * sizeof(*p)); + /*Initialize the alignment pattern position list.*/ + align_pos[0] = 6; + align_pos[nalign - 1] = dim - 7; + if (_version > 6) { + int d; + d = QR_ALIGNMENT_SPACING[_version - 7]; + for (i = nalign - 1; i-- > 1;) + align_pos[i] = align_pos[i + 1] - d; + } + /*Three of the corners use a finder pattern instead of a separate + alignment pattern.*/ + q[0][0] = 3; + q[0][1] = 3; + p[0][0] = _ul_pos[0]; + p[0][1] = _ul_pos[1]; + q[nalign - 1][0] = dim - 4; + q[nalign - 1][1] = 3; + p[nalign - 1][0] = _ur_pos[0]; + p[nalign - 1][1] = _ur_pos[1]; + q[(nalign - 1) * nalign][0] = 3; + q[(nalign - 1) * nalign][1] = dim - 4; + p[(nalign - 1) * nalign][0] = _dl_pos[0]; + p[(nalign - 1) * nalign][1] = _dl_pos[1]; + /*Scan for alignment patterns using a diagonal sweep.*/ + for (k = 1; k < 2 * nalign - 1; k++) { + int jmin; + int jmax; + jmax = QR_MINI(k, nalign - 1) - (k == nalign - 1); + jmin = QR_MAXI(0, k - (nalign - 1)) + (k == nalign - 1); + for (j = jmin; j <= jmax; j++) { + qr_hom_cell *cell; + int u; + int v; + int k; + i = jmax - (j - jmin); + k = i * nalign + j; + u = align_pos[j]; + v = align_pos[i]; + q[k][0] = u; + q[k][1] = v; + /*Mask out the alignment pattern.*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, u - 2, v - 2, 5, 5); + /*Pick a cell to use to govern the alignment pattern search.*/ + if (i > 1 && j > 1) { + qr_point p0; + qr_point p1; + qr_point p2; + /*Each predictor is basically a straight-line extrapolation from two + neighboring alignment patterns (except possibly near the opposing + finder patterns).*/ + qr_hom_cell_project(p0, _grid->cells[i - 2] + j - 1, u, v, + 0); + qr_hom_cell_project(p1, _grid->cells[i - 2] + j - 2, u, v, + 0); + qr_hom_cell_project(p2, _grid->cells[i - 1] + j - 2, u, v, + 0); + /*Take the median of the predictions as the search center.*/ + QR_SORT2I(p0[0], p1[0]); + QR_SORT2I(p0[1], p1[1]); + QR_SORT2I(p1[0], p2[0]); + QR_SORT2I(p1[1], p2[1]); + QR_SORT2I(p0[0], p1[0]); + QR_SORT2I(p0[1], p1[1]); + /*We need a cell that has the target point at a known (u,v) location. + Since our cells don't have inverses, just construct one from our + neighboring points.*/ + cell = _grid->cells[i - 1] + j - 1; + qr_hom_cell_init(cell, q[k - nalign - 1][0], + q[k - nalign - 1][1], q[k - nalign][0], + q[k - nalign][1], q[k - 1][0], q[k - 1][1], + q[k][0], q[k][1], p[k - nalign - 1][0], + p[k - nalign - 1][1], p[k - nalign][0], + p[k - nalign][1], p[k - 1][0], p[k - 1][1], + p1[0], p1[1]); + } else if (i > 1 && j > 0) + cell = _grid->cells[i - 2] + j - 1; + else if (i > 0 && j > 1) + cell = _grid->cells[i - 1] + j - 2; + else + cell = &base_cell; + /*Use a very small search radius. + A large displacement here usually means a false positive (e.g., when + the real alignment pattern is damaged or missing), which can + severely distort the projection.*/ + qr_alignment_pattern_search(p[k], cell, u, v, 2, _img, _width, + _height); + if (i > 0 && j > 0) { + qr_hom_cell_init(_grid->cells[i - 1] + j - 1, + q[k - nalign - 1][0], q[k - nalign - 1][1], + q[k - nalign][0], q[k - nalign][1], + q[k - 1][0], q[k - 1][1], q[k][0], q[k][1], + p[k - nalign - 1][0], p[k - nalign - 1][1], + p[k - nalign][0], p[k - nalign][1], + p[k - 1][0], p[k - 1][1], p[k][0], + p[k][1]); + } + } + } + qr_svg_points("align", p, nalign * nalign); + free(q); + free(p); + } + /*Set the limits over which each cell is used.*/ + memcpy(_grid->cell_limits, align_pos + 1, + (_grid->ncells - 1) * sizeof(*_grid->cell_limits)); + _grid->cell_limits[_grid->ncells - 1] = dim; + /*Produce a bounding square for the code (to mark finder centers with). + Because of non-linear distortion, this might not actually bound the code, + but it should be good enough. + I don't think it's worth computing a convex hull or anything silly like + that.*/ + qr_hom_cell_project(_p[0], _grid->cells[0] + 0, -1, -1, 1); + qr_hom_cell_project(_p[1], _grid->cells[0] + _grid->ncells - 1, + (dim << 1) - 1, -1, 1); + qr_hom_cell_project(_p[2], _grid->cells[_grid->ncells - 1] + 0, -1, + (dim << 1) - 1, 1); + qr_hom_cell_project(_p[3], + _grid->cells[_grid->ncells - 1] + _grid->ncells - 1, + (dim << 1) - 1, (dim << 1) - 1, 1); + /*Clamp the points somewhere near the image (this is really just in case a + corner is near the plane at infinity).*/ + for (i = 0; i < 4; i++) { + _p[i][0] = QR_CLAMPI(-_width << QR_FINDER_SUBPREC, _p[i][0], + _width << QR_FINDER_SUBPREC + 1); + _p[i][1] = QR_CLAMPI(-_height << QR_FINDER_SUBPREC, _p[i][1], + _height << QR_FINDER_SUBPREC + 1); + } + /*TODO: Make fine adjustments using the timing patterns. + Possible strategy: scan the timing pattern at QR_ALIGN_SUBPREC (or finer) + resolution, use dynamic programming to match midpoints between + transitions to the ideal grid locations.*/ +} + +static void qr_sampling_grid_clear(qr_sampling_grid *_grid) +{ + free(_grid->fpmask); + free(_grid->cells[0]); +} + +#if defined(QR_DEBUG) +static void qr_sampling_grid_dump(qr_sampling_grid *_grid, int _version, + const unsigned char *_img, int _width, + int _height) +{ + unsigned char *gimg; + FILE *fout; + int dim; + int u; + int v; + int x; + int y; + int w; + int i; + int j; + int r; + int s; + dim = 17 + (_version << 2) + 8 << QR_ALIGN_SUBPREC; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_hom_cell *cell; + if (i >= (4 << QR_ALIGN_SUBPREC) && + i <= dim - (5 << QR_ALIGN_SUBPREC) && + j >= (4 << QR_ALIGN_SUBPREC) && + j <= dim - (5 << QR_ALIGN_SUBPREC) && + ((!(i & (1 << QR_ALIGN_SUBPREC) - 1)) ^ + (!(j & (1 << QR_ALIGN_SUBPREC) - 1)))) { + gimg[i * dim + j] = 0x7F; + } else { + qr_point p; + u = (j >> QR_ALIGN_SUBPREC) - 4; + v = (i >> QR_ALIGN_SUBPREC) - 4; + for (r = 0; r < _grid->ncells - 1; r++) + if (u < _grid->cell_limits[r]) + break; + for (s = 0; s < _grid->ncells - 1; s++) + if (v < _grid->cell_limits[s]) + break; + cell = _grid->cells[s] + r; + u = j - (cell->u0 + 4 << QR_ALIGN_SUBPREC); + v = i - (cell->v0 + 4 << QR_ALIGN_SUBPREC); + x = cell->fwd[0][0] * u + cell->fwd[0][1] * v + + (cell->fwd[0][2] << QR_ALIGN_SUBPREC); + y = cell->fwd[1][0] * u + cell->fwd[1][1] * v + + (cell->fwd[1][2] << QR_ALIGN_SUBPREC); + w = cell->fwd[2][0] * u + cell->fwd[2][1] * v + + (cell->fwd[2][2] << QR_ALIGN_SUBPREC); + qr_hom_cell_fproject(p, cell, x, y, w); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + } + for (v = 0; v < 17 + (_version << 2); v++) + for (u = 0; u < 17 + (_version << 2); u++) { + if (qr_sampling_grid_is_in_fp(_grid, 17 + (_version << 2), u, v)) { + j = u + 4 << QR_ALIGN_SUBPREC; + i = v + 4 << QR_ALIGN_SUBPREC; + gimg[(i - 1) * dim + j - 1] = 0x7F; + gimg[(i - 1) * dim + j] = 0x7F; + gimg[(i - 1) * dim + j + 1] = 0x7F; + gimg[i * dim + j - 1] = 0x7F; + gimg[i * dim + j + 1] = 0x7F; + gimg[(i + 1) * dim + j - 1] = 0x7F; + gimg[(i + 1) * dim + j] = 0x7F; + gimg[(i + 1) * dim + j + 1] = 0x7F; + } + } + fout = fopen("grid.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} +#endif + +/*Generate the data mask corresponding to the given mask pattern.*/ +static void qr_data_mask_fill(unsigned *_mask, int _dim, int _pattern) +{ + int stride; + int i; + int j; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*Note that we store bits column-wise, since that's how they're read out of + the grid.*/ + switch (_pattern) { + /*10101010 i+j+1&1 + 01010101 + 10101010 + 01010101*/ + case 0: { + int m; + m = 0x55; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, m, stride * sizeof(*_mask)); + m ^= 0xFF; + } + } break; + /*11111111 i+1&1 + 00000000 + 11111111 + 00000000*/ + case 1: + memset(_mask, 0x55, _dim * stride * sizeof(*_mask)); + break; + /*10010010 (j+1)%3&1 + 10010010 + 10010010 + 10010010*/ + case 2: { + unsigned m; + m = 0xFF; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, m & 0xFF, stride * sizeof(*_mask)); + m = m << 8 | m >> 16; + } + } break; + /*10010010 (i+j+1)%3&1 + 00100100 + 01001001 + 10010010*/ + case 3: { + unsigned mi; + unsigned mj; + mj = 0; + for (i = 0; i < (QR_INT_BITS + 2) / 3; i++) + mj |= 1 << 3 * i; + for (j = 0; j < _dim; j++) { + mi = mj; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = mi; + mi = mi >> QR_INT_BITS % 3 | mi << 3 - QR_INT_BITS % 3; + } + mj = mj >> 1 | mj << 2; + } + } break; + /*11100011 (i>>1)+(j/3)+1&1 + 11100011 + 00011100 + 00011100*/ + case 4: { + unsigned m; + m = 7; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, (0xCC ^ -(m & 1)) & 0xFF, + stride * sizeof(*_mask)); + m = m >> 1 | m << 5; + } + } break; + /*11111111 !((i*j)%6) + 10000010 + 10010010 + 10101010*/ + case 5: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= !((i * j) % 6) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + /*11111111 (i*j)%3+i*j+1&1 + 11100011 + 11011011 + 10101010*/ + case 6: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= ((i * j) % 3 + i * j + 1 & 1) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + /*10101010 (i*j)%3+i+j+1&1 + 00011100 + 10001110 + 01010101*/ + default: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= ((i * j) % 3 + i + j + 1 & 1) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + } +} + +static void qr_sampling_grid_sample(const qr_sampling_grid *_grid, + unsigned *_data_bits, int _dim, + int _fmt_info, const unsigned char *_img, + int _width, int _height) +{ + int stride; + int u0; + int u1; + int j; + /*We initialize the buffer with the data mask and XOR bits into it as we read + them out of the image instead of unmasking in a separate step.*/ + qr_data_mask_fill(_data_bits, _dim, _fmt_info & 7); + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + u0 = 0; + svg_path_start("sampling-grid", 1, 0, 0); + /*We read data cell-by-cell to avoid having to constantly change which + projection we're using as we read each bit. + This (and the position-dependent data mask) is the reason we buffer the + bits we read instead of converting them directly to codewords here. + Note that bits are stored column-wise, since that's how we'll scan them.*/ + for (j = 0; j < _grid->ncells; j++) { + int i; + int v0; + int v1; + u1 = _grid->cell_limits[j]; + v0 = 0; + for (i = 0; i < _grid->ncells; i++) { + qr_hom_cell *cell; + int x0; + int y0; + int w0; + int u; + int du; + int dv; + v1 = _grid->cell_limits[i]; + cell = _grid->cells[i] + j; + du = u0 - cell->u0; + dv = v0 - cell->v0; + x0 = cell->fwd[0][0] * du + cell->fwd[0][1] * dv + cell->fwd[0][2]; + y0 = cell->fwd[1][0] * du + cell->fwd[1][1] * dv + cell->fwd[1][2]; + w0 = cell->fwd[2][0] * du + cell->fwd[2][1] * dv + cell->fwd[2][2]; + for (u = u0; u < u1; u++) { + int x; + int y; + int w; + int v; + x = x0; + y = y0; + w = w0; + for (v = v0; v < v1; v++) { + /*Skip doing all the divisions and bounds checks if the bit is in the + function pattern.*/ + if (!qr_sampling_grid_is_in_fp(_grid, _dim, u, v)) { + qr_point p; + qr_hom_cell_fproject(p, cell, x, y, w); + _data_bits[u * stride + (v >> QR_INT_LOGBITS)] ^= + qr_img_get_bit(_img, _width, _height, p[0], p[1]) + << (v & QR_INT_BITS - 1); + svg_path_moveto(SVG_ABS, p[0], p[1]); + } + x += cell->fwd[0][1]; + y += cell->fwd[1][1]; + w += cell->fwd[2][1]; + } + x0 += cell->fwd[0][0]; + y0 += cell->fwd[1][0]; + w0 += cell->fwd[2][0]; + } + v0 = v1; + } + u0 = u1; + } + svg_path_end(); +} + +/*Arranges the sample bits read by qr_sampling_grid_sample() into bytes and + groups those bytes into Reed-Solomon blocks. + The individual block pointers are destroyed by this routine.*/ +static void qr_samples_unpack(unsigned char **_blocks, int _nblocks, + int _nshort_data, int _nshort_blocks, + const unsigned *_data_bits, + const unsigned *_fp_mask, int _dim) +{ + unsigned bits; + int biti; + int stride; + int blocki; + int blockj; + int i; + int j; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*If _all_ the blocks are short, don't skip anything (see below).*/ + if (_nshort_blocks >= _nblocks) + _nshort_blocks = 0; + /*Scan columns in pairs from right to left.*/ + bits = 0; + for (blocki = blockj = biti = 0, j = _dim - 1; j > 0; j -= 2) { + unsigned data1; + unsigned data2; + unsigned fp_mask1; + unsigned fp_mask2; + int nbits; + int l; + /*Scan up a pair of columns.*/ + nbits = (_dim - 1 & QR_INT_BITS - 1) + 1; + l = j * stride; + for (i = stride; i-- > 0;) { + data1 = _data_bits[l + i]; + fp_mask1 = _fp_mask[l + i]; + data2 = _data_bits[l + i - stride]; + fp_mask2 = _fp_mask[l + i - stride]; + while (nbits-- > 0) { + /*Pull a bit from the right column.*/ + if (!(fp_mask1 >> nbits & 1)) { + bits = bits << 1 | data1 >> nbits & 1; + biti++; + } + /*Pull a bit from the left column.*/ + if (!(fp_mask2 >> nbits & 1)) { + bits = bits << 1 | data2 >> nbits & 1; + biti++; + } + /*If we finished a byte, drop it in a block.*/ + if (biti >= 8) { + biti -= 8; + *_blocks[blocki++]++ = (unsigned char)(bits >> biti); + /*For whatever reason, the long blocks are at the _end_ of the list, + instead of the beginning. + Even worse, the extra bytes they get come at the end of the data + bytes, before the parity bytes. + Hence the logic here: when we've filled up the data portion of the + short blocks, skip directly to the long blocks for the next byte. + It's also the reason we increment _blocks[blocki] on each store, + instead of just indexing with blockj (after this iteration the + number of bytes in each block differs).*/ + if (blocki >= _nblocks) + blocki = ++blockj == _nshort_data ? _nshort_blocks : 0; + } + } + nbits = QR_INT_BITS; + } + j -= 2; + /*Skip the column with the vertical timing pattern.*/ + if (j == 6) + j--; + /*Scan down a pair of columns.*/ + l = j * stride; + for (i = 0; i < stride; i++) { + data1 = _data_bits[l + i]; + fp_mask1 = _fp_mask[l + i]; + data2 = _data_bits[l + i - stride]; + fp_mask2 = _fp_mask[l + i - stride]; + nbits = QR_MINI(_dim - (i << QR_INT_LOGBITS), QR_INT_BITS); + while (nbits-- > 0) { + /*Pull a bit from the right column.*/ + if (!(fp_mask1 & 1)) { + bits = bits << 1 | data1 & 1; + biti++; + } + data1 >>= 1; + fp_mask1 >>= 1; + /*Pull a bit from the left column.*/ + if (!(fp_mask2 & 1)) { + bits = bits << 1 | data2 & 1; + biti++; + } + data2 >>= 1; + fp_mask2 >>= 1; + /*If we finished a byte, drop it in a block.*/ + if (biti >= 8) { + biti -= 8; + *_blocks[blocki++]++ = (unsigned char)(bits >> biti); + /*See comments on the "up" loop for the reason behind this mess.*/ + if (blocki >= _nblocks) + blocki = ++blockj == _nshort_data ? _nshort_blocks : 0; + } + } + } + } +} + +/*Bit reading code blatantly stolen^W^Wadapted from libogg/libtheora (because + I've already debugged it and I know it works). + Portions (C) Xiph.Org Foundation 1994-2008, BSD-style license.*/ +struct qr_pack_buf { + const unsigned char *buf; + int endbyte; + int endbit; + int storage; +}; + +static void qr_pack_buf_init(qr_pack_buf *_b, const unsigned char *_data, + int _ndata) +{ + _b->buf = _data; + _b->storage = _ndata; + _b->endbyte = _b->endbit = 0; +} + +/*Assumes 0<=_bits<=16.*/ +static int qr_pack_buf_read(qr_pack_buf *_b, int _bits) +{ + const unsigned char *p; + unsigned ret; + int m; + int d; + m = 16 - _bits; + _bits += _b->endbit; + d = _b->storage - _b->endbyte; + if (d <= 2) { + /*Not the main path.*/ + if (d * 8 < _bits) { + _b->endbyte += _bits >> 3; + _b->endbit = _bits & 7; + return -1; + } + /*Special case to avoid reading p[0] below, which might be past the end of + the buffer; also skips some useless accounting.*/ + else if (!_bits) + return 0; + } + p = _b->buf + _b->endbyte; + ret = p[0] << 8 + _b->endbit; + if (_bits > 8) { + ret |= p[1] << _b->endbit; + if (_bits > 16) + ret |= p[2] >> 8 - _b->endbit; + } + _b->endbyte += _bits >> 3; + _b->endbit = _bits & 7; + return (ret & 0xFFFF) >> m; +} + +static int qr_pack_buf_avail(const qr_pack_buf *_b) +{ + return (_b->storage - _b->endbyte << 3) - _b->endbit; +} + +/*The characters available in QR_MODE_ALNUM.*/ +static const unsigned char QR_ALNUM_TABLE[45] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' +}; + +static int qr_code_data_parse(qr_code_data *_qrdata, int _version, + const unsigned char *_data, int _ndata) +{ + qr_pack_buf qpb; + unsigned self_parity; + int centries; + int len_bits_idx; + /*Entries are stored directly in the struct during parsing. + Caller cleans up any allocated data on failure.*/ + _qrdata->entries = NULL; + _qrdata->nentries = 0; + _qrdata->sa_size = 0; + self_parity = 0; + centries = 0; + /*The versions are divided into 3 ranges that each use a different number of + bits for length fields.*/ + len_bits_idx = (_version > 9) + (_version > 26); + qr_pack_buf_init(&qpb, _data, _ndata); + /*While we have enough bits to read a mode...*/ + while (qr_pack_buf_avail(&qpb) >= 4) { + qr_code_data_entry *entry; + int mode; + mode = qr_pack_buf_read(&qpb, 4); + /*Mode 0 is a terminator.*/ + if (!mode) + break; + if (_qrdata->nentries >= centries) { + centries = centries << 1 | 1; + _qrdata->entries = (qr_code_data_entry *)realloc( + _qrdata->entries, centries * sizeof(*_qrdata->entries)); + } + entry = _qrdata->entries + _qrdata->nentries++; + entry->mode = mode; + /*Set the buffer to NULL, because if parsing fails, we might try to free it + on clean-up.*/ + entry->payload.data.buf = NULL; + switch (mode) { + /*The number of bits used to encode the character count for each version + range and each data mode.*/ + static const unsigned char LEN_BITS[3][4] = { { 10, 9, 8, 8 }, + { 12, 11, 16, 10 }, + { 14, 13, 16, 12 } }; + case QR_MODE_NUM: { + unsigned char *buf; + unsigned bits; + unsigned c; + int len; + int count; + int rem; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][0]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + count = len / 3; + rem = len % 3; + if (qr_pack_buf_avail(&qpb) < + 10 * count + 7 * (rem >> 1 & 1) + 4 * (rem & 1)) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + /*Read groups of 3 digits encoded in 10 bits.*/ + while (count-- > 0) { + bits = qr_pack_buf_read(&qpb, 10); + if (bits >= 1000) + return -1; + c = '0' + bits / 100; + self_parity ^= c; + *buf++ = (unsigned char)c; + bits %= 100; + c = '0' + bits / 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = '0' + bits % 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + /*Read the last two digits encoded in 7 bits.*/ + if (rem > 1) { + bits = qr_pack_buf_read(&qpb, 7); + if (bits >= 100) + return -1; + c = '0' + bits / 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = '0' + bits % 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + /*Or the last one digit encoded in 4 bits.*/ + else if (rem) { + bits = qr_pack_buf_read(&qpb, 4); + if (bits >= 10) + return -1; + c = '0' + bits; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + case QR_MODE_ALNUM: { + unsigned char *buf; + unsigned bits; + unsigned c; + int len; + int count; + int rem; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][1]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + count = len >> 1; + rem = len & 1; + if (qr_pack_buf_avail(&qpb) < 11 * count + 6 * rem) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + /*Read groups of two characters encoded in 11 bits.*/ + while (count-- > 0) { + bits = qr_pack_buf_read(&qpb, 11); + if (bits >= 2025) + return -1; + c = QR_ALNUM_TABLE[bits / 45]; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = QR_ALNUM_TABLE[bits % 45]; + self_parity ^= c; + *buf++ = (unsigned char)c; + len -= 2; + } + /*Read the last character encoded in 6 bits.*/ + if (rem) { + bits = qr_pack_buf_read(&qpb, 6); + if (bits >= 45) + return -1; + c = QR_ALNUM_TABLE[bits]; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + /*Structured-append header.*/ + case QR_MODE_STRUCT: { + int bits; + bits = qr_pack_buf_read(&qpb, 16); + if (bits < 0) + return -1; + /*We save a copy of the data in _qrdata for easy reference when + grouping structured-append codes. + If for some reason the code has multiple S-A headers, first one wins, + since it is supposed to come before everything else (TODO: should we + return an error instead?).*/ + if (_qrdata->sa_size == 0) { + _qrdata->sa_index = entry->payload.sa.sa_index = + (unsigned char)(bits >> 12 & 0xF); + _qrdata->sa_size = entry->payload.sa.sa_size = + (unsigned char)((bits >> 8 & 0xF) + 1); + _qrdata->sa_parity = entry->payload.sa.sa_parity = + (unsigned char)(bits & 0xFF); + } + } break; + case QR_MODE_BYTE: { + unsigned char *buf; + unsigned c; + int len; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][2]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + if (qr_pack_buf_avail(&qpb) < len << 3) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + while (len-- > 0) { + c = qr_pack_buf_read(&qpb, 8); + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + /*FNC1 first position marker.*/ + case QR_MODE_FNC1_1ST: + break; + /*Extended Channel Interpretation data.*/ + case QR_MODE_ECI: { + unsigned val; + int bits; + /*ECI uses a variable-width encoding similar to UTF-8*/ + bits = qr_pack_buf_read(&qpb, 8); + if (bits < 0) + return -1; + /*One byte:*/ + if (!(bits & 0x80)) + val = bits; + /*Two bytes:*/ + else if (!(bits & 0x40)) { + val = bits & 0x3F << 8; + bits = qr_pack_buf_read(&qpb, 8); + if (bits < 0) + return -1; + val |= bits; + } + /*Three bytes:*/ + else if (!(bits & 0x20)) { + val = bits & 0x1F << 16; + bits = qr_pack_buf_read(&qpb, 16); + if (bits < 0) + return -1; + val |= bits; + /*Valid ECI values are 0...999999.*/ + if (val >= 1000000) + return -1; + } + /*Invalid lead byte.*/ + else + return -1; + entry->payload.eci = val; + } break; + case QR_MODE_KANJI: { + unsigned char *buf; + unsigned bits; + int len; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][3]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + if (qr_pack_buf_avail(&qpb) < 13 * len) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(2 * len * sizeof(*buf)); + entry->payload.data.len = 2 * len; + /*Decode 2-byte SJIS characters encoded in 13 bits.*/ + while (len-- > 0) { + bits = qr_pack_buf_read(&qpb, 13); + bits = (bits / 0xC0 << 8 | bits % 0xC0) + 0x8140; + if (bits >= 0xA000) + bits += 0x4000; + /*TODO: Are values 0xXX7F, 0xXXFD...0xXXFF always invalid? + Should we reject them here?*/ + self_parity ^= bits; + *buf++ = (unsigned char)(bits >> 8); + *buf++ = (unsigned char)(bits & 0xFF); + } + } break; + /*FNC1 second position marker.*/ + case QR_MODE_FNC1_2ND: { + int bits; + /*FNC1 in the 2nd position encodes an Application Indicator in one + byte, which is either a letter (A...Z or a...z) or a 2-digit number. + The letters are encoded with their ASCII value plus 100, the numbers + are encoded directly with their numeric value. + Values 100...164, 191...196, and 223...255 are invalid, so we reject + them here.*/ + bits = qr_pack_buf_read(&qpb, 8); + if (!(bits >= 0 && bits < 100 || bits >= 165 && bits < 191 || + bits >= 197 && bits < 223)) { + return -1; + } + entry->payload.ai = bits; + } break; + /*Unknown mode number:*/ + default: { + /*Unfortunately, because we have to understand the format of a mode to + know how many bits it occupies, we can't skip unknown modes. + Therefore we have to fail.*/ + return -1; + } break; + } + } + /*Store the parity of the data from this code, for S-A. + The final parity is the 8-bit XOR of all the decoded bytes of literal data. + We don't combine the 2-byte kanji codes into one byte in the loops above, + because we can just do it here instead.*/ + _qrdata->self_parity = ((self_parity >> 8) ^ self_parity) & 0xFF; + /*Success.*/ + _qrdata->entries = (qr_code_data_entry *)realloc( + _qrdata->entries, _qrdata->nentries * sizeof(*_qrdata->entries)); + return 0; +} + +static void qr_code_data_clear(qr_code_data *_qrdata) +{ + int i; + for (i = 0; i < _qrdata->nentries; i++) { + if (QR_MODE_HAS_DATA(_qrdata->entries[i].mode)) { + free(_qrdata->entries[i].payload.data.buf); + } + } + free(_qrdata->entries); +} + +void qr_code_data_list_init(qr_code_data_list *_qrlist) +{ + _qrlist->qrdata = NULL; + _qrlist->nqrdata = _qrlist->cqrdata = 0; +} + +void qr_code_data_list_clear(qr_code_data_list *_qrlist) +{ + int i; + for (i = 0; i < _qrlist->nqrdata; i++) + qr_code_data_clear(_qrlist->qrdata + i); + free(_qrlist->qrdata); + qr_code_data_list_init(_qrlist); +} + +static void qr_code_data_list_add(qr_code_data_list *_qrlist, + qr_code_data *_qrdata) +{ + if (_qrlist->nqrdata >= _qrlist->cqrdata) { + _qrlist->cqrdata = _qrlist->cqrdata << 1 | 1; + _qrlist->qrdata = (qr_code_data *)realloc( + _qrlist->qrdata, _qrlist->cqrdata * sizeof(*_qrlist->qrdata)); + } + memcpy(_qrlist->qrdata + _qrlist->nqrdata++, _qrdata, sizeof(*_qrdata)); +} + +#if 0 +static const unsigned short QR_NCODEWORDS[40]={ + 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, + 404, 466, 532, 581, 655, 733, 815, 901, 991,1085, + 1156,1258,1364,1474,1588,1706,1828,1921,2051,2185, + 2323,2465,2611,2761,2876,3034,3196,3362,3532,3706 +}; +#endif + +/*The total number of codewords in a QR code.*/ +static int qr_code_ncodewords(unsigned _version) +{ + unsigned nalign; + /*This is 24-27 instructions on ARM in thumb mode, or a 26-32 byte savings + over just using a table (not counting the instructions that would be + needed to do the table lookup).*/ + if (_version == 1) + return 26; + nalign = (_version / 7) + 2; + return (_version << 4) * (_version + 8) - (5 * nalign) * (5 * nalign - 2) + + 36 * (_version < 7) + 83 >> + 3; +} + +#if 0 +/*The number of parity bytes per Reed-Solomon block for each version and error + correction level.*/ +static const unsigned char QR_RS_NPAR[40][4]={ + { 7,10,13,17},{10,16,22,28},{15,26,18,22},{20,18,26,16}, + {26,24,18,22},{18,16,24,28},{20,18,18,26},{24,22,22,26}, + {30,22,20,24},{18,26,24,28},{20,30,28,24},{24,22,26,28}, + {26,22,24,22},{30,24,20,24},{22,24,30,24},{24,28,24,30}, + {28,28,28,28},{30,26,28,28},{28,26,26,26},{28,26,30,28}, + {28,26,28,30},{28,28,30,24},{30,28,30,30},{30,28,30,30}, + {26,28,30,30},{28,28,28,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30} +}; +#endif + +/*Bulk data for the number of parity bytes per Reed-Solomon block.*/ +static const unsigned char QR_RS_NPAR_VALS[71] = { + /*[ 0]*/ 7, 10, 13, 17, + /*[ 4]*/ 10, 16, 22, 28, 26, 26, 26, 22, 24, 22, 22, 26, + 24, 18, 22, + /*[19]*/ 15, 26, 18, 22, 24, 30, 24, 20, 24, + /*[28]*/ 18, 16, 24, 28, 28, 28, 28, 30, 24, + /*[37]*/ 20, 18, 18, 26, 24, 28, 24, 30, 26, 28, 28, 26, + 28, 30, 30, 22, 20, 24, + /*[55]*/ 20, 18, 26, 16, + /*[59]*/ 20, 30, 28, 24, 22, 26, 28, 26, 30, 28, 30, 30 +}; + +/*An offset into QR_RS_NPAR_DATA for each version that gives the number of + parity bytes per Reed-Solomon block for each error correction level.*/ +static const unsigned char QR_RS_NPAR_OFFS[40] = { + 0, 4, 19, 55, 15, 28, 37, 12, 51, 39, 59, 62, 10, 24, + 22, 41, 31, 44, 7, 65, 47, 33, 67, 67, 48, 32, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67 +}; + +/*The number of Reed-Solomon blocks for each version and error correction + level.*/ +static const unsigned char QR_RS_NBLOCKS[40][4] = { + { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 2, 2 }, + { 1, 2, 2, 4 }, { 1, 2, 4, 4 }, { 2, 4, 4, 4 }, + { 2, 4, 6, 5 }, { 2, 4, 6, 6 }, { 2, 5, 8, 8 }, + { 4, 5, 8, 8 }, { 4, 5, 8, 11 }, { 4, 8, 10, 11 }, + { 4, 9, 12, 16 }, { 4, 9, 16, 16 }, { 6, 10, 12, 18 }, + { 6, 10, 17, 16 }, { 6, 11, 16, 19 }, { 6, 13, 18, 21 }, + { 7, 14, 21, 25 }, { 8, 16, 20, 25 }, { 8, 17, 23, 25 }, + { 9, 17, 23, 34 }, { 9, 18, 25, 30 }, { 10, 20, 27, 32 }, + { 12, 21, 29, 35 }, { 12, 23, 34, 37 }, { 12, 25, 34, 40 }, + { 13, 26, 35, 42 }, { 14, 28, 38, 45 }, { 15, 29, 40, 48 }, + { 16, 31, 43, 51 }, { 17, 33, 45, 54 }, { 18, 35, 48, 57 }, + { 19, 37, 51, 60 }, { 19, 38, 53, 63 }, { 20, 40, 56, 66 }, + { 21, 43, 59, 70 }, { 22, 45, 62, 74 }, { 24, 47, 65, 77 }, + { 25, 49, 68, 81 } +}; + +/*Attempts to fully decode a QR code. + _qrdata: Returns the parsed code data. + _gf: Used for Reed-Solomon error correction. + _ul_pos: The location of the UL finder pattern. + _ur_pos: The location of the UR finder pattern. + _dl_pos: The location of the DL finder pattern. + _version: The (decoded) version number. + _fmt_info: The decoded format info. + _img: The binary input image. + _width: The width of the input image. + _height: The height of the input image. + Return: 0 on success, or a negative value on error.*/ +static int qr_code_decode(qr_code_data *_qrdata, const rs_gf256 *_gf, + const qr_point _ul_pos, const qr_point _ur_pos, + const qr_point _dl_pos, int _version, int _fmt_info, + const unsigned char *_img, int _width, int _height) +{ + qr_sampling_grid grid; + unsigned *data_bits; + unsigned char **blocks; + unsigned char *block_data; + int nblocks; + int nshort_blocks; + int ncodewords; + int block_sz; + int ecc_level; + int ndata; + int npar; + int dim; + int ret; + int i; + /*Read the bits out of the image.*/ + qr_sampling_grid_init(&grid, _version, _ul_pos, _ur_pos, _dl_pos, + _qrdata->bbox, _img, _width, _height); +#if defined(QR_DEBUG) + qr_sampling_grid_dump(&grid, _version, _img, _width, _height); +#endif + dim = 17 + (_version << 2); + data_bits = (unsigned *)malloc( + dim * (dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) * sizeof(*data_bits)); + qr_sampling_grid_sample(&grid, data_bits, dim, _fmt_info, _img, _width, + _height); + /*Group those bits into Reed-Solomon codewords.*/ + ecc_level = (_fmt_info >> 3) ^ 1; + nblocks = QR_RS_NBLOCKS[_version - 1][ecc_level]; + npar = *(QR_RS_NPAR_VALS + QR_RS_NPAR_OFFS[_version - 1] + ecc_level); + ncodewords = qr_code_ncodewords(_version); + block_sz = ncodewords / nblocks; + nshort_blocks = nblocks - (ncodewords % nblocks); + blocks = (unsigned char **)malloc(nblocks * sizeof(*blocks)); + block_data = (unsigned char *)malloc(ncodewords * sizeof(*block_data)); + blocks[0] = block_data; + for (i = 1; i < nblocks; i++) + blocks[i] = blocks[i - 1] + block_sz + (i > nshort_blocks); + qr_samples_unpack(blocks, nblocks, block_sz - npar, nshort_blocks, + data_bits, grid.fpmask, dim); + qr_sampling_grid_clear(&grid); + free(blocks); + free(data_bits); + /*Perform the error correction.*/ + ndata = 0; + ncodewords = 0; + ret = 0; + for (i = 0; i < nblocks; i++) { + int block_szi; + int ndatai; + block_szi = block_sz + (i >= nshort_blocks); + ret = rs_correct(_gf, QR_M0, block_data + ncodewords, block_szi, npar, + NULL, 0); + zprintf(1, "Number of errors corrected: %i%s\n", ret, + ret < 0 ? " (data irrecoverable)" : ""); + /*For version 1 symbols and version 2-L and 3-L symbols, we aren't allowed + to use all the parity bytes for correction. + They are instead used to improve detection. + Version 1-L reserves 3 parity bytes for detection. + Versions 1-M and 2-L reserve 2 parity bytes for detection. + Versions 1-Q, 1-H, and 3-L reserve 1 parity byte for detection. + We can ignore the version 3-L restriction because it has an odd number of + parity bytes, and we don't support erasure detection.*/ + if (ret < 0 || _version == 1 && ret > ecc_level + 1 << 1 || + _version == 2 && ecc_level == 0 && ret > 4) { + ret = -1; + break; + } + ndatai = block_szi - npar; + memmove(block_data + ndata, block_data + ncodewords, + ndatai * sizeof(*block_data)); + ncodewords += block_szi; + ndata += ndatai; + } + /*Parse the corrected bitstream.*/ + if (ret >= 0) { + ret = qr_code_data_parse(_qrdata, _version, block_data, ndata); + /*We could return any partially decoded data, but then we'd have to have + API support for that; a mode ignoring ECC errors might also be useful.*/ + if (ret < 0) + qr_code_data_clear(_qrdata); + _qrdata->version = _version; + _qrdata->ecc_level = ecc_level; + } + free(block_data); + return ret; +} + +/*Searches for an arrangement of these three finder centers that yields a valid + configuration. + _c: On input, the three finder centers to consider in any order. + Return: The detected version number, or a negative value on error.*/ +static int qr_reader_try_configuration(qr_reader *_reader, + qr_code_data *_qrdata, + const unsigned char *_img, int _width, + int _height, qr_finder_center *_c[3]) +{ + int ci[7]; + unsigned maxd; + int ccw; + int i0; + int i; + /*Sort the points in counter-clockwise order.*/ + ccw = qr_point_ccw(_c[0]->pos, _c[1]->pos, _c[2]->pos); + /*Colinear points can't be the corners of a quadrilateral.*/ + if (!ccw) + return -1; + /*Include a few extra copies of the cyclical list to avoid mods.*/ + ci[6] = ci[3] = ci[0] = 0; + ci[4] = ci[1] = 1 + (ccw < 0); + ci[5] = ci[2] = 2 - (ccw < 0); + /*Assume the points farthest from each other are the opposite corners, and + find the top-left point.*/ + maxd = qr_point_distance2(_c[1]->pos, _c[2]->pos); + i0 = 0; + for (i = 1; i < 3; i++) { + unsigned d; + d = qr_point_distance2(_c[ci[i + 1]]->pos, _c[ci[i + 2]]->pos); + if (d > maxd) { + i0 = i; + maxd = d; + } + } + /*However, try all three possible orderings, just to be sure (a severely + skewed projection could move opposite corners closer than adjacent).*/ + for (i = i0; i < i0 + 3; i++) { + qr_aff aff; + qr_hom hom; + qr_finder ul; + qr_finder ur; + qr_finder dl; + qr_point bbox[4]; + int res; + int ur_version; + int dl_version; + int fmt_info; + ul.c = _c[ci[i]]; + ur.c = _c[ci[i + 1]]; + dl.c = _c[ci[i + 2]]; + /*Estimate the module size and version number from the two opposite corners. + The module size is not constant in the image, so we compute an affine + projection from the three points we have to a square domain, and + estimate it there. + Although it should be the same along both axes, we keep separate + estimates to account for any remaining projective distortion.*/ + res = QR_INT_BITS - 2 - QR_FINDER_SUBPREC - + qr_ilog(QR_MAXI(_width, _height) - 1); + qr_aff_init(&aff, ul.c->pos, ur.c->pos, dl.c->pos, res); + qr_aff_unproject(ur.o, &aff, ur.c->pos[0], ur.c->pos[1]); + qr_finder_edge_pts_aff_classify(&ur, &aff); + if (qr_finder_estimate_module_size_and_version(&ur, 1 << res, + 1 << res) < 0) + continue; + qr_aff_unproject(dl.o, &aff, dl.c->pos[0], dl.c->pos[1]); + qr_finder_edge_pts_aff_classify(&dl, &aff); + if (qr_finder_estimate_module_size_and_version(&dl, 1 << res, + 1 << res) < 0) + continue; + /*If the estimated versions are significantly different, reject the + configuration.*/ + if (abs(ur.eversion[1] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + continue; + qr_aff_unproject(ul.o, &aff, ul.c->pos[0], ul.c->pos[1]); + qr_finder_edge_pts_aff_classify(&ul, &aff); + if (qr_finder_estimate_module_size_and_version(&ul, 1 << res, + 1 << res) < 0 || + abs(ul.eversion[1] - ur.eversion[1]) > QR_LARGE_VERSION_SLACK || + abs(ul.eversion[0] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) { + continue; + } +#if defined(QR_DEBUG) + qr_finder_dump_aff_undistorted(&ul, &ur, &dl, &aff, _img, _width, + _height); +#endif + /*If we made it this far, upgrade the affine homography to a full + homography.*/ + if (qr_hom_fit(&hom, &ul, &ur, &dl, bbox, &aff, &_reader->isaac, _img, + _width, _height) < 0) { + continue; + } + memcpy(_qrdata->bbox, bbox, sizeof(bbox)); + qr_hom_unproject(ul.o, &hom, ul.c->pos[0], ul.c->pos[1]); + qr_hom_unproject(ur.o, &hom, ur.c->pos[0], ur.c->pos[1]); + qr_hom_unproject(dl.o, &hom, dl.c->pos[0], dl.c->pos[1]); + qr_finder_edge_pts_hom_classify(&ur, &hom); + if (qr_finder_estimate_module_size_and_version(&ur, ur.o[0] - ul.o[0], + ur.o[0] - ul.o[0]) < 0) { + continue; + } + qr_finder_edge_pts_hom_classify(&dl, &hom); + if (qr_finder_estimate_module_size_and_version(&dl, dl.o[1] - ul.o[1], + dl.o[1] - ul.o[1]) < 0) { + continue; + } +#if defined(QR_DEBUG) + qr_finder_dump_hom_undistorted(&ul, &ur, &dl, &hom, _img, _width, + _height); +#endif + /*If we have a small version (less than 7), there's no encoded version + information. + If the estimated version on the two corners matches and is sufficiently + small, we assume this is the case.*/ + if (ur.eversion[1] == dl.eversion[0] && ur.eversion[1] < 7) { + /*We used to do a whole bunch of extra geometric checks for small + versions, because with just an affine correction, it was fairly easy + to estimate two consistent module sizes given a random configuration. + However, now that we're estimating a full homography, these appear to + be unnecessary.*/ +#if 0 + static const signed char LINE_TESTS[12][6]={ + /*DL left, UL > 0, UR > 0*/ + {2,0,0, 1,1, 1}, + /*DL right, UL > 0, UR < 0*/ + {2,1,0, 1,1,-1}, + /*UR top, UL > 0, DL > 0*/ + {1,2,0, 1,2, 1}, + /*UR bottom, UL > 0, DL < 0*/ + {1,3,0, 1,2,-1}, + /*UR left, DL < 0, UL < 0*/ + {1,0,2,-1,0,-1}, + /*UR right, DL > 0, UL > 0*/ + {1,1,2, 1,0, 1}, + /*DL top, UR < 0, UL < 0*/ + {2,2,1,-1,0,-1}, + /*DL bottom, UR > 0, UL > 0*/ + {2,3,1, 1,0, 1}, + /*UL left, DL > 0, UR > 0*/ + {0,0,2, 1,1, 1}, + /*UL right, DL > 0, UR < 0*/ + {0,1,2, 1,1,-1}, + /*UL top, UR > 0, DL > 0*/ + {0,2,1, 1,2, 1}, + /*UL bottom, UR > 0, DL < 0*/ + {0,3,1, 1,2,-1} + }; + qr_finder *f[3]; + int j; + /*Start by decoding the format information. + This is cheap, but unlikely to reject invalid configurations. + 56.25% of all bitstrings are valid, and we mix and match several pieces + until we find a valid combination, so our real chances of finding a + valid codeword in random bits are even higher.*/ + fmt_info=qr_finder_fmt_info_decode(&ul,&ur,&dl,&aff,_img,_width,_height); + if(fmt_info<0)continue; + /*Now we fit lines to the edges of each finder pattern and check to make + sure the centers of the other finder patterns lie on the proper side.*/ + f[0]=&ul; + f[1]=&ur; + f[2]=&dl; + for(j=0;j<12;j++){ + const signed char *t; + qr_line l0; + int *p; + t=LINE_TESTS[j]; + qr_finder_ransac(f[t[0]],&aff,&_reader->isaac,t[1]); + /*We may not have enough points to fit a line accurately here. + If not, we just skip the test.*/ + if(qr_line_fit_finder_edge(l0,f[t[0]],t[1],res)<0)continue; + p=f[t[2]]->c->pos; + if(qr_line_eval(l0,p[0],p[1])*t[3]<0)break; + p=f[t[4]]->c->pos; + if(qr_line_eval(l0,p[0],p[1])*t[5]<0)break; + } + if(j<12)continue; + /*All tests passed.*/ +#endif + ur_version = ur.eversion[1]; + } else { + /*If the estimated versions are significantly different, reject the + configuration.*/ + if (abs(ur.eversion[1] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + continue; + /*Otherwise we try to read the actual version data from the image. + If the real version is not sufficiently close to our estimated version, + then we assume there was an unrecoverable decoding error (so many bit + errors we were within 3 errors of another valid code), and throw that + value away. + If no decoded version could be sufficiently close, we don't even try.*/ + if (ur.eversion[1] >= 7 - QR_LARGE_VERSION_SLACK) { + ur_version = qr_finder_version_decode(&ur, &hom, _img, _width, + _height, 0); + if (abs(ur_version - ur.eversion[1]) > QR_LARGE_VERSION_SLACK) + ur_version = -1; + } else + ur_version = -1; + if (dl.eversion[0] >= 7 - QR_LARGE_VERSION_SLACK) { + dl_version = qr_finder_version_decode(&dl, &hom, _img, _width, + _height, 1); + if (abs(dl_version - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + dl_version = -1; + } else + dl_version = -1; + /*If we got at least one valid version, or we got two and they match, + then we found a valid configuration.*/ + if (ur_version >= 0) { + if (dl_version >= 0 && dl_version != ur_version) + continue; + } else if (dl_version < 0) + continue; + else + ur_version = dl_version; + } + qr_finder_edge_pts_hom_classify(&ul, &hom); + if (qr_finder_estimate_module_size_and_version(&ul, ur.o[0] - dl.o[0], + dl.o[1] - ul.o[1]) < 0 || + abs(ul.eversion[1] - ur.eversion[1]) > QR_SMALL_VERSION_SLACK || + abs(ul.eversion[0] - dl.eversion[0]) > QR_SMALL_VERSION_SLACK) { + continue; + } + fmt_info = qr_finder_fmt_info_decode(&ul, &ur, &dl, &hom, _img, _width, + _height); + if (fmt_info < 0 || + qr_code_decode(_qrdata, &_reader->gf, ul.c->pos, ur.c->pos, + dl.c->pos, ur_version, fmt_info, _img, _width, + _height) < 0) { + /*The code may be flipped. + Try again, swapping the UR and DL centers. + We should get a valid version either way, so it's relatively cheap to + check this, as we've already filtered out a lot of invalid + configurations.*/ + QR_SWAP2I(hom.inv[0][0], hom.inv[1][0]); + QR_SWAP2I(hom.inv[0][1], hom.inv[1][1]); + QR_SWAP2I(hom.fwd[0][0], hom.fwd[0][1]); + QR_SWAP2I(hom.fwd[1][0], hom.fwd[1][1]); + QR_SWAP2I(hom.fwd[2][0], hom.fwd[2][1]); + QR_SWAP2I(ul.o[0], ul.o[1]); + QR_SWAP2I(ul.size[0], ul.size[1]); + QR_SWAP2I(ur.o[0], ur.o[1]); + QR_SWAP2I(ur.size[0], ur.size[1]); + QR_SWAP2I(dl.o[0], dl.o[1]); + QR_SWAP2I(dl.size[0], dl.size[1]); +#if defined(QR_DEBUG) + qr_finder_dump_hom_undistorted(&ul, &dl, &ur, &hom, _img, _width, + _height); +#endif + fmt_info = qr_finder_fmt_info_decode(&ul, &dl, &ur, &hom, _img, + _width, _height); + if (fmt_info < 0) + continue; + QR_SWAP2I(bbox[1][0], bbox[2][0]); + QR_SWAP2I(bbox[1][1], bbox[2][1]); + memcpy(_qrdata->bbox, bbox, sizeof(bbox)); + if (qr_code_decode(_qrdata, &_reader->gf, ul.c->pos, dl.c->pos, + ur.c->pos, ur_version, fmt_info, _img, _width, + _height) < 0) { + continue; + } + } + return ur_version; + } + return -1; +} + +void qr_reader_match_centers(qr_reader *_reader, qr_code_data_list *_qrlist, + qr_finder_center *_centers, int _ncenters, + const unsigned char *_img, int _width, int _height) +{ + /*The number of centers should be small, so an O(n^3) exhaustive search of + which ones go together should be reasonable.*/ + unsigned char *mark; + int nfailures_max; + int nfailures; + int i; + int j; + int k; + mark = (unsigned char *)calloc(_ncenters, sizeof(*mark)); + nfailures_max = QR_MAXI(8192, _width * _height >> 9); + nfailures = 0; + for (i = 0; i < _ncenters; i++) { + /*TODO: We might be able to accelerate this step significantly by + considering the remaining finder centers in a more intelligent order, + based on the first finder center we just chose.*/ + for (j = i + 1; i <_ncenters && !mark[i] && j < _ncenters; j++) { + for (k = j + 1; j<_ncenters && !mark[j] && k < _ncenters; k++) + if (!mark[k]) { + qr_finder_center *c[3]; + qr_code_data qrdata; + int version; + c[0] = _centers + i; + c[1] = _centers + j; + c[2] = _centers + k; + version = qr_reader_try_configuration(_reader, &qrdata, + _img, _width, _height, + c); + if (version >= 0) { + int ninside; + int l; + /*Add the data to the list.*/ + qr_code_data_list_add(_qrlist, &qrdata); + /*Convert the bounding box we're returning to the user to normal + image coordinates.*/ + for (l = 0; l < 4; l++) { + _qrlist->qrdata[_qrlist->nqrdata - 1].bbox[l][0] >>= + QR_FINDER_SUBPREC; + _qrlist->qrdata[_qrlist->nqrdata - 1].bbox[l][1] >>= + QR_FINDER_SUBPREC; + } + /*Mark these centers as used.*/ + mark[i] = mark[j] = mark[k] = 1; + /*Find any other finder centers located inside this code.*/ + for (l = ninside = 0; l < _ncenters; l++) + if (!mark[l]) { + if (qr_point_ccw(qrdata.bbox[0], qrdata.bbox[1], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[1], qrdata.bbox[3], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[3], qrdata.bbox[2], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[2], qrdata.bbox[0], + _centers[l].pos) >= 0) { + mark[l] = 2; + ninside++; + } + } + if (ninside >= 3) { + /*We might have a "Double QR": a code inside a code. + Copy the relevant centers to a new array and do a search confined + to that subset.*/ + qr_finder_center *inside; + inside = (qr_finder_center *)malloc( + ninside * sizeof(*inside)); + for (l = ninside = 0; l < _ncenters; l++) { + if (mark[l] == 2) + *&inside[ninside++] = *&_centers[l]; + } + qr_reader_match_centers(_reader, _qrlist, inside, + ninside, _img, _width, + _height); + free(inside); + } + /*Mark _all_ such centers used: codes cannot partially overlap.*/ + for (l = 0; l < _ncenters; l++) + if (mark[l] == 2) + mark[l] = 1; + nfailures = 0; + } else if (++nfailures > nfailures_max) { + /*Give up. + We're unlikely to find a valid code in all this clutter, and we + could spent quite a lot of time trying.*/ + i = j = k = _ncenters; + } + } + } + } + free(mark); +} + +int _zbar_qr_found_line(qr_reader *reader, int dir, const qr_finder_line *line) +{ + /* minimally intrusive brute force version */ + qr_finder_lines *lines = &reader->finder_lines[dir]; + + if (lines->nlines >= lines->clines) { + lines->clines *= 2; + lines->lines = + realloc(lines->lines, ++lines->clines * sizeof(*lines->lines)); + } + + memcpy(lines->lines + lines->nlines++, line, sizeof(*line)); + + return (0); +} + +static inline void qr_svg_centers(const qr_finder_center *centers, int ncenters) +{ + int i, j; + svg_path_start("centers", 1, 0, 0); + for (i = 0; i < ncenters; i++) + svg_path_moveto(SVG_ABS, centers[i].pos[0], centers[i].pos[1]); + svg_path_end(); + + svg_path_start("edge-pts", 1, 0, 0); + for (i = 0; i < ncenters; i++) { + const qr_finder_center *cen = centers + i; + for (j = 0; j < cen->nedge_pts; j++) + svg_path_moveto(SVG_ABS, cen->edge_pts[j].pos[0], + cen->edge_pts[j].pos[1]); + } + svg_path_end(); +} + +int _zbar_qr_decode(qr_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + int nqrdata = 0, ncenters; + qr_finder_edge_pt *edge_pts = NULL; + qr_finder_center *centers = NULL; + + if (reader->finder_lines[0].nlines < 9 || + reader->finder_lines[1].nlines < 9) + return (0); + + svg_group_start("finder", 0, 1. / (1 << QR_FINDER_SUBPREC), 0, 0, 0); + + ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0); + + zprintf(14, "%dx%d finders, %d centers:\n", reader->finder_lines[0].nlines, + reader->finder_lines[1].nlines, ncenters); + qr_svg_centers(centers, ncenters); + + if (ncenters >= 3) { + void *bin = qr_binarize(img->data, img->width, img->height); + + qr_code_data_list qrlist; + qr_code_data_list_init(&qrlist); + + qr_reader_match_centers(reader, &qrlist, centers, ncenters, bin, + img->width, img->height); + + if (qrlist.nqrdata > 0) + nqrdata = qr_code_data_list_extract_text(&qrlist, iscn, img); + + qr_code_data_list_clear(&qrlist); + free(bin); + } + svg_group_end(); + + if (centers) + free(centers); + if (edge_pts) + free(edge_pts); + return (nqrdata); +} diff --git a/zbar/qrcode/qrdec.h b/zbar/qrcode/qrdec.h new file mode 100644 index 0000000..40cb204 --- /dev/null +++ b/zbar/qrcode/qrdec.h @@ -0,0 +1,171 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrdec_H) +#define _qrdec_H (1) + +#include <zbar.h> + +typedef struct qr_code_data_entry qr_code_data_entry; +typedef struct qr_code_data qr_code_data; +typedef struct qr_code_data_list qr_code_data_list; + +typedef enum qr_mode +{ + /*Numeric digits ('0'...'9').*/ + QR_MODE_NUM = 1, + /*Alphanumeric characters ('0'...'9', 'A'...'Z', plus the punctuation + ' ', '$', '%', '*', '+', '-', '.', '/', ':').*/ + QR_MODE_ALNUM, + /*Structured-append header.*/ + QR_MODE_STRUCT, + /*Raw 8-bit bytes.*/ + QR_MODE_BYTE, + /*FNC1 marker (for more info, see http://www.mecsw.com/specs/uccean128.html). + In the "first position" data is formatted in accordance with GS1 General + Specifications.*/ + QR_MODE_FNC1_1ST, + /*Mode 6 reserved?*/ + /*Extended Channel Interpretation code.*/ + QR_MODE_ECI = 7, + /*SJIS kanji characters.*/ + QR_MODE_KANJI, + /*FNC1 marker (for more info, see http://www.mecsw.com/specs/uccean128.html). + In the "second position" data is formatted in accordance with an industry + application as specified by AIM Inc.*/ + QR_MODE_FNC1_2ND +} qr_mode; + +/*Check if a mode has a data buffer associated with it. + Currently this is only modes with exactly one bit set.*/ +#define QR_MODE_HAS_DATA(_mode) (!((_mode) & (_mode)-1)) + +/*ECI may be used to signal a character encoding for the data.*/ +typedef enum qr_eci_encoding +{ + /*GLI0 is like CP437, but the encoding is reset at the beginning of each + structured append symbol.*/ + QR_ECI_GLI0, + /*GLI1 is like ISO8859_1, but the encoding is reset at the beginning of each + structured append symbol.*/ + QR_ECI_GLI1, + /*The remaining encodings do not reset at the start of the next structured + append symbol.*/ + QR_ECI_CP437, + /*Western European.*/ + QR_ECI_ISO8859_1, + /*Central European.*/ + QR_ECI_ISO8859_2, + /*South European.*/ + QR_ECI_ISO8859_3, + /*North European.*/ + QR_ECI_ISO8859_4, + /*Cyrillic.*/ + QR_ECI_ISO8859_5, + /*Arabic.*/ + QR_ECI_ISO8859_6, + /*Greek.*/ + QR_ECI_ISO8859_7, + /*Hebrew.*/ + QR_ECI_ISO8859_8, + /*Turkish.*/ + QR_ECI_ISO8859_9, + /*Nordic.*/ + QR_ECI_ISO8859_10, + /*Thai.*/ + QR_ECI_ISO8859_11, + /*There is no ISO/IEC 8859-12.*/ + /*Baltic rim.*/ + QR_ECI_ISO8859_13 = QR_ECI_ISO8859_11 + 2, + /*Celtic.*/ + QR_ECI_ISO8859_14, + /*Western European with euro.*/ + QR_ECI_ISO8859_15, + /*South-Eastern European (with euro).*/ + QR_ECI_ISO8859_16, + /*ECI 000019 is reserved?*/ + /*Shift-JIS.*/ + QR_ECI_SJIS = 20, + /*UTF-8.*/ + QR_ECI_UTF8 = 26 +} qr_eci_encoding; + +/*A single unit of parsed QR code data.*/ +struct qr_code_data_entry { + /*The mode of this data block.*/ + qr_mode mode; + union { + /*Data buffer for modes that have one.*/ + struct { + unsigned char *buf; + int len; + } data; + /*Decoded "Extended Channel Interpretation" data.*/ + unsigned eci; + /*Decoded "Application Indicator" for FNC1 in 2nd position.*/ + int ai; + /*Structured-append header data.*/ + struct { + unsigned char sa_index; + unsigned char sa_size; + unsigned char sa_parity; + } sa; + } payload; +}; + +/*Low-level QR code data.*/ +struct qr_code_data { + /*The decoded data entries.*/ + qr_code_data_entry *entries; + int nentries; + /*The code version (1...40).*/ + unsigned char version; + /*The ECC level (0...3, corresponding to 'L', 'M', 'Q', and 'H').*/ + unsigned char ecc_level; + /*Structured-append information.*/ + /*The index of this code in the structured-append group. + If sa_size is zero, this is undefined.*/ + unsigned char sa_index; + /*The size of the structured-append group, or 0 if there was no S-A header.*/ + unsigned char sa_size; + /*The parity of the entire structured-append group. + If sa_size is zero, this is undefined.*/ + unsigned char sa_parity; + /*The parity of this code. + If sa_size is zero, this is undefined.*/ + unsigned char self_parity; + /*An approximate bounding box for the code. + Points appear in the order up-left, up-right, down-left, down-right, + relative to the orientation of the QR code.*/ + qr_point bbox[4]; +}; + +struct qr_code_data_list { + qr_code_data *qrdata; + int nqrdata; + int cqrdata; +}; + +/*Extract symbol data from a list of QR codes and attach to the image. + All text is converted to UTF-8. + For binary/byte mode QR codes: if configured with ZBAR_CFG_BINARY, + the bytes will be returned as is. Otherwise, the encoding will be + automatically determined and the data will be converted to that character set. + Any structured-append group that does not have all of its members is decoded + as ZBAR_PARTIAL with ZBAR_PARTIAL components for the discontinuities. + Note that isolated members of a structured-append group may be decoded with + the wrong character set, since the correct setting cannot be propagated + between codes. + Return: The number of symbols which were successfully extracted from the + codes; this will be at most the number of codes.*/ +int qr_code_data_list_extract_text(const qr_code_data_list *_qrlist, + zbar_image_scanner_t *iscn, + zbar_image_t *img); + +/*TODO: Parse DoCoMo standard barcode data formats. + See http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/function/application/ + for details.*/ + +#endif diff --git a/zbar/qrcode/qrdectxt.c b/zbar/qrcode/qrdectxt.c new file mode 100644 index 0000000..5676e33 --- /dev/null +++ b/zbar/qrcode/qrdectxt.c @@ -0,0 +1,573 @@ +/*Copyright (C) 2008-2010 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <iconv.h> + +#include "decoder.h" +#include "error.h" +#include "image.h" +#include "img_scanner.h" +#include "qrcode.h" +#include "qrdec.h" +#include "util.h" + +#define ENC_LIST_SIZE 4 + +static int text_is_ascii(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) + if (_text[i] >= 0x80) + return 0; + return 1; +} + +static int text_is_latin1(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) { + /*The following line fails to compile correctly with gcc 3.4.4 on ARM with + any optimizations enabled.*/ + if (_text[i] >= 0x80 && _text[i] < 0xA0) + return 0; + } + return 1; +} + +static int text_is_big5(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) { + if (_text[i] == 0xFF) + return 0; + else if (_text[i] >= 0x80) { // first byte is big5 + i++; + if (i >= _len) // second byte not exists + return 0; + if (_text[i] < 0x40 || (_text[i] > 0x7E && _text[i] < 0xA1) || + _text[i] > 0xFE) { // second byte not in range + return 0; + } + } else { // normal ascii encoding, it's okay + } + } + return 1; +} + +static void enc_list_mtf(iconv_t _enc_list[ENC_LIST_SIZE], iconv_t _enc) +{ + int i; + for (i = 0; i < ENC_LIST_SIZE; i++) + if (_enc_list[i] == _enc) { + int j; + for (j = i; j-- > 0;) + _enc_list[j + 1] = _enc_list[j]; + _enc_list[0] = _enc; + break; + } +} + +int qr_code_data_list_extract_text(const qr_code_data_list *_qrlist, + zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + iconv_t sjis_cd; + iconv_t utf8_cd; + iconv_t latin1_cd; + iconv_t big5_cd; + const qr_code_data *qrdata; + int nqrdata; + unsigned char *mark; + int ntext; + int i; + int raw_binary = 0; + zbar_image_scanner_get_config(iscn, ZBAR_QRCODE, ZBAR_CFG_BINARY, + &raw_binary); + qrdata = _qrlist->qrdata; + nqrdata = _qrlist->nqrdata; + mark = (unsigned char *)calloc(nqrdata, sizeof(*mark)); + ntext = 0; + /*This is the encoding the standard says is the default.*/ + latin1_cd = iconv_open("UTF-8", "ISO8859-1"); + /*But this one is often used, as well.*/ + sjis_cd = iconv_open("UTF-8", "SJIS"); + /*This is a trivial conversion just to check validity without extra code.*/ + utf8_cd = iconv_open("UTF-8", "UTF-8"); + /* add support for big5 encoding. */ + big5_cd = iconv_open("UTF-8", "BIG-5"); + for (i = 0; i < nqrdata; i++) + if (!mark[i]) { + const qr_code_data *qrdataj; + const qr_code_data_entry *entry; + iconv_t enc_list[ENC_LIST_SIZE]; + iconv_t eci_cd; + int sa[16]; + int sa_size; + char *sa_text; + size_t sa_ntext; + size_t sa_ctext; + int fnc1; + int fnc1_2ai; + int has_kanji; + int eci; + int err; + int j; + int k; + zbar_symbol_t *syms = NULL, **sym = &syms; + qr_point dir; + int horiz; + char *bytebuf_text; + size_t bytebuf_ntext; + + /*Step 0: Collect the other QR codes belonging to this S-A group.*/ + if (qrdata[i].sa_size) { + unsigned sa_parity; + sa_size = qrdata[i].sa_size; + sa_parity = qrdata[i].sa_parity; + for (j = 0; j < sa_size; j++) + sa[j] = -1; + for (j = i; j < nqrdata; j++) + if (!mark[j]) { + /*TODO: We could also match version, ECC level, etc. if size and + parity alone are too ambiguous.*/ + if (qrdata[j].sa_size == sa_size && + qrdata[j].sa_parity == sa_parity && + sa[qrdata[j].sa_index] < 0) { + sa[qrdata[j].sa_index] = j; + mark[j] = 1; + } + } + /*TODO: If the S-A group is complete, check the parity.*/ + } else { + sa[0] = i; + sa_size = 1; + } + + sa_ctext = 0; + fnc1 = 0; + fnc1_2ai = 0; + has_kanji = 0; + /*Step 1: Detect FNC1 markers and estimate the required buffer size.*/ + for (j = 0; j < sa_size; j++) + if (sa[j] >= 0) { + qrdataj = qrdata + sa[j]; + for (k = 0; k < qrdataj->nentries; k++) { + int shift; + entry = qrdataj->entries + k; + shift = 0; + switch (entry->mode) { + /*FNC1 applies to the entire code and ignores subsequent markers.*/ + case QR_MODE_FNC1_1ST: { + if (!fnc1) + fnc1 = MOD(ZBAR_MOD_GS1); + } break; + case QR_MODE_FNC1_2ND: { + if (!fnc1) { + fnc1 = MOD(ZBAR_MOD_AIM); + fnc1_2ai = entry->payload.ai; + sa_ctext += 2; + } + } break; + /*We assume at most 4 UTF-8 bytes per input byte. + I believe this is true for all the encodings we actually use.*/ + case QR_MODE_KANJI: + has_kanji = 1; + case QR_MODE_BYTE: + shift = 2; + default: { + /*The remaining two modes are already valid UTF-8.*/ + if (QR_MODE_HAS_DATA(entry->mode)) { + sa_ctext += entry->payload.data.len << shift; + } + } break; + } + } + } + + /*Step 2: Convert the entries.*/ + sa_text = (char *)malloc((sa_ctext + 1) * sizeof(*sa_text)); + sa_ntext = 0; + /*Add the encoded Application Indicator for FNC1 in the second position.*/ + if (fnc1 == MOD(ZBAR_MOD_AIM)) { + if (fnc1_2ai < 100) { + /*The Application Indicator is a 2-digit number.*/ + sa_text[sa_ntext++] = '0' + fnc1_2ai / 10; + sa_text[sa_ntext++] = '0' + fnc1_2ai % 10; + } + /*The Application Indicator is a single letter. + We already checked that it lies in one of the ranges A...Z, a...z + when we decoded it.*/ + else + sa_text[sa_ntext++] = (char)(fnc1_2ai - 100); + } + eci = -1; + enc_list[0] = sjis_cd; + enc_list[1] = latin1_cd; + enc_list[2] = big5_cd; + enc_list[3] = utf8_cd; + eci_cd = (iconv_t)-1; + err = 0; + + bytebuf_text = (char *)malloc((sa_ctext + 1) * sizeof(*sa_text)); + bytebuf_ntext = 0; + + for (j = 0; j < sa_size && !err; j++, sym = &(*sym)->next) { + *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + (*sym)->datalen = sa_ntext; + if (sa[j] < 0) { + /* generic placeholder for unfinished results */ + (*sym)->type = ZBAR_PARTIAL; + + /*Skip all contiguous missing segments.*/ + for (j++; j < sa_size && sa[j] < 0; j++) + ; + /*If there aren't any more, stop.*/ + if (j >= sa_size) + break; + + /* mark break in data */ + sa_text[sa_ntext++] = '\0'; + (*sym)->datalen = sa_ntext; + + /* advance to next symbol */ + sym = &(*sym)->next; + *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + } + + qrdataj = qrdata + sa[j]; + /* expose bounding box */ + sym_add_point(*sym, qrdataj->bbox[0][0], qrdataj->bbox[0][1]); + sym_add_point(*sym, qrdataj->bbox[2][0], qrdataj->bbox[2][1]); + sym_add_point(*sym, qrdataj->bbox[3][0], qrdataj->bbox[3][1]); + sym_add_point(*sym, qrdataj->bbox[1][0], qrdataj->bbox[1][1]); + + /* approx symbol "up" direction */ + dir[0] = (qrdataj->bbox[0][0] - qrdataj->bbox[2][0] + + qrdataj->bbox[1][0] - qrdataj->bbox[3][0]); + dir[1] = (qrdataj->bbox[2][1] - qrdataj->bbox[0][1] + + qrdataj->bbox[3][1] - qrdataj->bbox[1][1]); + horiz = abs(dir[0]) > abs(dir[1]); + (*sym)->orient = horiz + 2 * (dir[1 - horiz] < 0); + + for (k = 0; k <= qrdataj->nentries && !err; k++) { + size_t inleft; + size_t outleft; + char *in; + char *out; + + // Check if bytebuf_text is empty INSIDE for loop. + if (bytebuf_ntext > 0) { + entry = (k == qrdataj->nentries) ? NULL : + qrdataj->entries + k; + // next entry is not byte mode, convert bytes to text. + if (entry == NULL || (entry->mode != QR_MODE_BYTE && + entry->mode != QR_MODE_KANJI)) { + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + /*If we have no specified encoding, attempt to auto-detect it + unless configured with ZBAR_CFG_BINARY.*/ + if (eci < 0) { + if (raw_binary) { + /* copy all remaining bytes to output buffer. */ + memcpy(out, in, inleft); + sa_ntext += inleft; + bytebuf_ntext = 0; + } else { + int ei; + /*If there was data encoded in kanji mode, assume it's SJIS.*/ + if (has_kanji) + enc_list_mtf(enc_list, sjis_cd); + /*Otherwise check for the UTF-8 BOM. + UTF-8 is rarely specified with ECI, and few decoders + currently support doing so, so this is the best way for + encoders to reliably indicate it.*/ + else if (inleft >= 3 && + in[0] == (char)0xEF && + in[1] == (char)0xBB && + in[2] == (char)0xBF) { + in += 3; + inleft -= 3; + /*Actually try converting (to check validity).*/ + err = utf8_cd == (iconv_t)-1 || + iconv(utf8_cd, &in, &inleft, &out, + &outleft) == (size_t)-1; + if (!err) { + sa_ntext = out - sa_text; + enc_list_mtf(enc_list, utf8_cd); + bytebuf_ntext = 0; + } + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + } + /*If the text is 8-bit clean, prefer UTF-8 over SJIS, since + SJIS will corrupt the backslashes used for DoCoMo formats.*/ + else if (text_is_ascii((unsigned char *)in, + inleft)) { + enc_list_mtf(enc_list, utf8_cd); + } + /* Check if it's big5 encoding. */ + else if (text_is_big5((unsigned char *)in, + inleft)) { + enc_list_mtf(enc_list, big5_cd); + } + + /*Try our list of encodings.*/ + for (ei = 0; ei < ENC_LIST_SIZE; ei++) + if (enc_list[ei] != (iconv_t)-1) { + /*According to the 2005 version of the standard, + ISO/IEC 8859-1 (one hyphen) is supposed to be used, but + reality is not always so (and in the 2000 version of the + standard, it was JIS8/SJIS that was the default). + It's got an invalid range that is used often with SJIS + and UTF-8, though, which makes detection easier. + However, iconv() does not properly reject characters in + those ranges, since ISO-8859-1 (two hyphens) defines a + number of seldom-used control code characters there. + So if we see any of those characters, move this + conversion to the end of the list.*/ + if (ei < 3 && + enc_list[ei] == latin1_cd && + !text_is_latin1( + (unsigned char *)in, + inleft)) { + int ej; + for (ej = ei + 1; + ej < ENC_LIST_SIZE; ej++) + enc_list[ej - 1] = + enc_list[ej]; + enc_list[3] = latin1_cd; + } + err = iconv(enc_list[ei], &in, + &inleft, &out, + &outleft) == (size_t)-1; + if (!err) { + sa_ntext = out - sa_text; + enc_list_mtf(enc_list, + enc_list[ei]); + break; + } + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + } + } + } + /*We were actually given a character set; use it. + The spec says that in this case, data should be treated as if it + came from the given character set even when encoded in kanji + mode.*/ + else { + err = eci_cd == (iconv_t)-1 || + iconv(eci_cd, &in, &inleft, &out, + &outleft) == (size_t)-1; + if (!err) + sa_ntext = out - sa_text; + } + bytebuf_ntext = 0; + } + } + if (k == qrdataj->nentries) + break; + + entry = qrdataj->entries + k; + switch (entry->mode) { + case QR_MODE_NUM: { + if (sa_ctext - sa_ntext >= + (size_t)entry->payload.data.len) { + memcpy(sa_text + sa_ntext, entry->payload.data.buf, + entry->payload.data.len * sizeof(*sa_text)); + sa_ntext += entry->payload.data.len; + } else + err = 1; + } break; + case QR_MODE_ALNUM: { + char *p; + in = (char *)entry->payload.data.buf; + inleft = entry->payload.data.len; + /*FNC1 uses '%' as an escape character.*/ + if (fnc1) + for (;;) { + size_t plen; + char c; + p = memchr(in, '%', inleft * sizeof(*in)); + if (p == NULL) + break; + plen = p - in; + if (sa_ctext - sa_ntext < plen + 1) + break; + memcpy(sa_text + sa_ntext, in, + plen * sizeof(*in)); + sa_ntext += plen; + /*Two '%'s is a literal '%'*/ + if (plen + 1 < inleft && p[1] == '%') { + c = '%'; + plen++; + p++; + } + /*One '%' is the ASCII group separator.*/ + else + c = 0x1D; + sa_text[sa_ntext++] = c; + inleft -= plen + 1; + in = p + 1; + } + else + p = NULL; + if (p != NULL || sa_ctext - sa_ntext < inleft) + err = 1; + else { + memcpy(sa_text + sa_ntext, in, + inleft * sizeof(*sa_text)); + sa_ntext += inleft; + } + } break; + /* DONE: This handles a multi-byte sequence split between + multiple data blocks. */ + case QR_MODE_BYTE: + case QR_MODE_KANJI: { + // copy byte to bytebuf + in = (char *)entry->payload.data.buf; + inleft = entry->payload.data.len; + memcpy(bytebuf_text + bytebuf_ntext, in, + inleft * sizeof(*bytebuf_text)); + bytebuf_ntext += inleft; + } break; + /*Check to see if a character set was specified.*/ + case QR_MODE_ECI: { + const char *enc; + char buf[16]; + unsigned cur_eci; + cur_eci = entry->payload.eci; + if (cur_eci <= QR_ECI_ISO8859_16 && cur_eci != 14) { + if (cur_eci != QR_ECI_GLI0 && + cur_eci != QR_ECI_CP437) { + sprintf(buf, "ISO8859-%i", + QR_MAXI(cur_eci, 3) - 2); + enc = buf; + } + /*Note that CP437 requires an iconv compiled with + --enable-extra-encodings, and thus may not be available.*/ + else + enc = "CP437"; + } else if (cur_eci == QR_ECI_SJIS) + enc = "SJIS"; + else if (cur_eci == QR_ECI_UTF8) + enc = "UTF-8"; + /*Don't know what this ECI code specifies, but not an encoding that + we recognize.*/ + else + continue; + eci = cur_eci; + eci_cd = iconv_open("UTF-8", enc); + } break; + /*Silence stupid compiler warnings.*/ + default: + break; + } + } + /*If eci should be reset between codes, do so.*/ + if (eci <= QR_ECI_GLI1) { + eci = -1; + if (eci_cd != (iconv_t)-1) { + iconv_close(eci_cd); + eci_cd = (iconv_t)-1; + } + } + } + + free(bytebuf_text); + + if (eci_cd != (iconv_t)-1) + iconv_close(eci_cd); + if (!err) { + zbar_symbol_t *sa_sym; + sa_text[sa_ntext++] = '\0'; + if (sa_ctext + 1 > sa_ntext) { + sa_text = + (char *)realloc(sa_text, sa_ntext * sizeof(*sa_text)); + } + + if (sa_size == 1) + sa_sym = syms; + else { + /* cheap out w/axis aligned bbox for now */ + int xmin = img->width, xmax = -2; + int ymin = img->height, ymax = -2; + + /* create "virtual" container symbol for composite result */ + sa_sym = + _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + sa_sym->syms = _zbar_symbol_set_create(); + sa_sym->syms->head = syms; + + /* fixup data references */ + for (; syms; syms = syms->next) { + int next; + _zbar_symbol_refcnt(syms, 1); + if (syms->type == ZBAR_PARTIAL) + sa_sym->type = ZBAR_PARTIAL; + else + for (j = 0; j < syms->npts; j++) { + int u = syms->pts[j].x; + if (xmin >= u) + xmin = u - 1; + if (xmax <= u) + xmax = u + 1; + u = syms->pts[j].y; + if (ymin >= u) + ymin = u - 1; + if (ymax <= u) + ymax = u + 1; + } + syms->data = sa_text + syms->datalen; + next = (syms->next) ? syms->next->datalen : sa_ntext; + if (next > syms->datalen) + syms->datalen = next - syms->datalen - 1; + else { + zprintf( + 1, "Assertion `next > syms->datalen' failed\n"); + syms->datalen = 0; + } + } + if (xmax >= -1) { + sym_add_point(sa_sym, xmin, ymin); + sym_add_point(sa_sym, xmin, ymax); + sym_add_point(sa_sym, xmax, ymax); + sym_add_point(sa_sym, xmax, ymin); + } + } + sa_sym->data = sa_text; + sa_sym->data_alloc = sa_ntext; + sa_sym->datalen = sa_ntext - 1; + sa_sym->modifiers = fnc1; + + _zbar_image_scanner_add_sym(iscn, sa_sym); + } else { + _zbar_image_scanner_recycle_syms(iscn, syms); + free(sa_text); + } + } + if (utf8_cd != (iconv_t)-1) + iconv_close(utf8_cd); + if (sjis_cd != (iconv_t)-1) + iconv_close(sjis_cd); + if (latin1_cd != (iconv_t)-1) + iconv_close(latin1_cd); + if (big5_cd != (iconv_t)-1) + iconv_close(big5_cd); + free(mark); + return ntext; +} diff --git a/zbar/qrcode/rs.c b/zbar/qrcode/rs.c new file mode 100644 index 0000000..bb195e9 --- /dev/null +++ b/zbar/qrcode/rs.c @@ -0,0 +1,907 @@ +/*Copyright (C) 1991-1995 Henry Minsky (hqm@ua.com, hqm@ai.mit.edu) + Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "rs.h" +#include <stdlib.h> +#include <string.h> + +/*Reed-Solomon encoder and decoder. + Original implementation (C) Henry Minsky (hqm@ua.com, hqm@ai.mit.edu), + Universal Access 1991-1995. + Updates by Timothy B. Terriberry (C) 2008-2009: + - Properly reject codes when error-locator polynomial has repeated roots or + non-trivial irreducible factors. + - Removed the hard-coded parity size and performed general API cleanup. + - Allow multiple representations of GF(2**8), since different standards use + different irreducible polynomials. + - Allow different starting indices for the generator polynomial, since + different standards use different values. + - Greatly reduced the computation by eliminating unnecessary operations. + - Explicitly solve for the roots of low-degree polynomials instead of using + an exhaustive search. + This is another major speed boost when there are few errors.*/ + +/*Galois Field arithmetic in GF(2**8).*/ + +void rs_gf256_init(rs_gf256 *_gf, unsigned _ppoly) +{ + unsigned p; + int i; + /*Initialize the table of powers of a primtive root, alpha=0x02.*/ + p = 1; + for (i = 0; i < 256; i++) { + _gf->exp[i] = _gf->exp[i + 255] = p; + p = ((p << 1) ^ (-(p >> 7) & _ppoly)) & 0xFF; + } + /*Invert the table to recover the logs.*/ + for (i = 0; i < 255; i++) + _gf->log[_gf->exp[i]] = i; + /*Note that we rely on the fact that _gf->log[0]=0 below.*/ + _gf->log[0] = 0; +} + +/*Multiplication in GF(2**8) using logarithms.*/ +static unsigned rs_gmul(const rs_gf256 *_gf, unsigned _a, unsigned _b) +{ + return _a == 0 || _b == 0 ? 0 : _gf->exp[_gf->log[_a] + _gf->log[_b]]; +} + +/*Division in GF(2**8) using logarithms. + The result of division by zero is undefined.*/ +static unsigned rs_gdiv(const rs_gf256 *_gf, unsigned _a, unsigned _b) +{ + return _a == 0 ? 0 : _gf->exp[_gf->log[_a] + 255 - _gf->log[_b]]; +} + +/*Multiplication in GF(2**8) when one of the numbers is known to be non-zero + (proven by representing it by its logarithm).*/ +static unsigned rs_hgmul(const rs_gf256 *_gf, unsigned _a, unsigned _logb) +{ + return _a == 0 ? 0 : _gf->exp[_gf->log[_a] + _logb]; +} + +/*Square root in GF(2**8) using logarithms.*/ +static unsigned rs_gsqrt(const rs_gf256 *_gf, unsigned _a) +{ + unsigned loga; + if (!_a) + return 0; + loga = _gf->log[_a]; + return _gf->exp[loga + (255 & -(loga & 1)) >> 1]; +} + +/*Polynomial root finding in GF(2**8). + Each routine returns a list of the distinct roots (i.e., with duplicates + removed).*/ + +/*Solve a quadratic equation x**2 + _b*x + _c in GF(2**8) using the method + of~\cite{Wal99}. + Returns the number of distinct roots. + ARTICLE{Wal99, + author="C. Wayne Walker", + title="New Formulas for Solving Quadratic Equations over Certain Finite + Fields", + journal="{IEEE} Transactions on Information Theory", + volume=45, + number=1, + pages="283--284", + month=Jan, + year=1999 + }*/ +static int rs_quadratic_solve(const rs_gf256 *_gf, unsigned _b, unsigned _c, + unsigned char _x[2]) +{ + unsigned b; + unsigned logb; + unsigned logb2; + unsigned logb4; + unsigned logb8; + unsigned logb12; + unsigned logb14; + unsigned logc; + unsigned logc2; + unsigned logc4; + unsigned c8; + unsigned g3; + unsigned z3; + unsigned l3; + unsigned c0; + unsigned g2; + unsigned l2; + unsigned z2; + int inc; + /*If _b is zero, all we need is a square root.*/ + if (!_b) { + _x[0] = rs_gsqrt(_gf, _c); + return 1; + } + /*If _c is zero, 0 and _b are the roots.*/ + if (!_c) { + _x[0] = 0; + _x[1] = _b; + return 2; + } + logb = _gf->log[_b]; + logc = _gf->log[_c]; + /*If _b lies in GF(2**4), scale x to move it out.*/ + inc = logb % (255 / 15) == 0; + if (inc) { + b = _gf->exp[logb + 254]; + logb = _gf->log[b]; + _c = _gf->exp[logc + 253]; + logc = _gf->log[_c]; + } else + b = _b; + logb2 = _gf->log[_gf->exp[logb << 1]]; + logb4 = _gf->log[_gf->exp[logb2 << 1]]; + logb8 = _gf->log[_gf->exp[logb4 << 1]]; + logb12 = _gf->log[_gf->exp[logb4 + logb8]]; + logb14 = _gf->log[_gf->exp[logb2 + logb12]]; + logc2 = _gf->log[_gf->exp[logc << 1]]; + logc4 = _gf->log[_gf->exp[logc2 << 1]]; + c8 = _gf->exp[logc4 << 1]; + g3 = rs_hgmul(_gf, + _gf->exp[logb14 + logc] ^ _gf->exp[logb12 + logc2] ^ + _gf->exp[logb8 + logc4] ^ c8, + logb); + /*If g3 doesn't lie in GF(2**4), then our roots lie in an extension field. + Note that we rely on the fact that _gf->log[0]==0 here.*/ + if (_gf->log[g3] % (255 / 15) != 0) + return 0; + /*Construct the corresponding quadratic in GF(2**4): + x**2 + x/alpha**(255/15) + l3/alpha**(2*(255/15))*/ + z3 = rs_gdiv(_gf, g3, _gf->exp[logb8 << 1] ^ b); + l3 = rs_hgmul(_gf, rs_gmul(_gf, z3, z3) ^ rs_hgmul(_gf, z3, logb) ^ _c, + 255 - logb2); + c0 = rs_hgmul(_gf, l3, 255 - 2 * (255 / 15)); + /*Construct the corresponding quadratic in GF(2**2): + x**2 + x/alpha**(255/3) + l2/alpha**(2*(255/3))*/ + g2 = + rs_hgmul(_gf, + rs_hgmul(_gf, c0, 255 - 2 * (255 / 15)) ^ rs_gmul(_gf, c0, c0), + 255 - 255 / 15); + z2 = rs_gdiv(_gf, g2, + _gf->exp[255 - (255 / 15) * 4] ^ _gf->exp[255 - (255 / 15)]); + l2 = rs_hgmul(_gf, + rs_gmul(_gf, z2, z2) ^ rs_hgmul(_gf, z2, 255 - (255 / 15)) ^ + c0, + 2 * (255 / 15)); + /*Back substitute to the solution in the original field.*/ + _x[0] = _gf->exp[_gf->log[z3 ^ rs_hgmul(_gf, + rs_hgmul(_gf, l2, 255 / 3) ^ + rs_hgmul(_gf, z2, 255 / 15), + logb)] + + inc]; + _x[1] = _x[0] ^ _b; + return 2; +} + +/*Solve a cubic equation x**3 + _a*x**2 + _b*x + _c in GF(2**8). + Returns the number of distinct roots.*/ +static int rs_cubic_solve(const rs_gf256 *_gf, unsigned _a, unsigned _b, + unsigned _c, unsigned char _x[3]) +{ + unsigned k; + unsigned logd; + unsigned d2; + unsigned logd2; + unsigned logw; + int nroots; + /*If _c is zero, factor out the 0 root.*/ + if (!_c) { + nroots = rs_quadratic_solve(_gf, _a, _b, _x); + if (_b) + _x[nroots++] = 0; + return nroots; + } + /*Substitute x=_a+y*sqrt(_a**2+_b) to get y**3 + y + k == 0, + k = (_a*_b+c)/(_a**2+b)**(3/2).*/ + k = rs_gmul(_gf, _a, _b) ^ _c; + d2 = rs_gmul(_gf, _a, _a) ^ _b; + if (!d2) { + int logx; + if (!k) { + /*We have a triple root.*/ + _x[0] = _a; + return 1; + } + logx = _gf->log[k]; + if (logx % 3 != 0) + return 0; + logx /= 3; + _x[0] = _a ^ _gf->exp[logx]; + _x[1] = _a ^ _gf->exp[logx + 255 / 3]; + _x[2] = _a ^ _x[0] ^ _x[1]; + return 3; + } + logd2 = _gf->log[d2]; + logd = logd2 + (255 & -(logd2 & 1)) >> 1; + k = rs_gdiv(_gf, k, _gf->exp[logd + logd2]); + /*Substitute y=w+1/w and z=w**3 to get z**2 + k*z + 1 == 0.*/ + nroots = rs_quadratic_solve(_gf, k, 1, _x); + if (nroots < 1) { + /*The Reed-Solomon code is only valid if we can find 3 distinct roots in + GF(2**8), so if we know there's only one, we don't actually need to find + it. + Note that we're also called by the quartic solver, but if we contain a + non-trivial irreducible factor, than so does the original + quartic~\cite{LW72}, and failing to return a root here actually saves us + some work there, also.*/ + return 0; + } + /*Recover w from z.*/ + logw = _gf->log[_x[0]]; + if (logw) { + if (logw % 3 != 0) + return 0; + logw /= 3; + /*Recover x from w.*/ + _x[0] = + _gf->exp[_gf->log[_gf->exp[logw] ^ _gf->exp[255 - logw]] + logd] ^ + _a; + logw += 255 / 3; + _x[1] = + _gf->exp[_gf->log[_gf->exp[logw] ^ _gf->exp[255 - logw]] + logd] ^ + _a; + _x[2] = _x[0] ^ _x[1] ^ _a; + return 3; + } else { + _x[0] = _a; + /*In this case _x[1] is a double root, so we know the Reed-Solomon code is + invalid. + Note that we still have to return at least one root, because if we're + being called by the quartic solver, the quartic might still have 4 + distinct roots. + But we don't need more than one root, so we can avoid computing the + expensive one.*/ + /*_x[1]=_gf->exp[_gf->log[_gf->exp[255/3]^_gf->exp[2*(255/3)]]+logd]^_a;*/ + return 1; + } +} + +/*Solve a quartic equation x**4 + _a*x**3 + _b*x**2 + _c*x + _d in GF(2**8) by + decomposing it into the cases given by~\cite{LW72}. + Returns the number of distinct roots. + @ARTICLE{LW72, + author="Philip A. Leonard and Kenneth S. Williams", + title="Quartics over $GF(2^n)$", + journal="Proceedings of the American Mathematical Society", + volume=36, + number=2, + pages="347--450", + month=Dec, + year=1972 + }*/ +static int rs_quartic_solve(const rs_gf256 *_gf, unsigned _a, unsigned _b, + unsigned _c, unsigned _d, unsigned char _x[3]) +{ + unsigned r; + unsigned s; + unsigned t; + unsigned b; + int nroots; + int i; + /*If _d is zero, factor out the 0 root.*/ + if (!_d) { + nroots = rs_cubic_solve(_gf, _a, _b, _c, _x); + if (_c) + _x[nroots++] = 0; + return nroots; + } + if (_a) { + unsigned loga; + /*Substitute x=(1/y) + sqrt(_c/_a) to eliminate the cubic term.*/ + loga = _gf->log[_a]; + r = rs_hgmul(_gf, _c, 255 - loga); + s = rs_gsqrt(_gf, r); + t = _d ^ rs_gmul(_gf, _b, r) ^ rs_gmul(_gf, r, r); + if (t) { + unsigned logti; + logti = 255 - _gf->log[t]; + /*The result is still quartic, but with no cubic term.*/ + nroots = rs_quartic_solve( + _gf, 0, rs_hgmul(_gf, _b ^ rs_hgmul(_gf, s, loga), logti), + _gf->exp[loga + logti], _gf->exp[logti], _x); + for (i = 0; i < nroots; i++) + _x[i] = _gf->exp[255 - _gf->log[_x[i]]] ^ s; + } else { + /*s must be a root~\cite{LW72}, and is in fact a double-root~\cite{CCO69}. + Thus we're left with only a quadratic to solve. + @ARTICLE{CCO69, + author="Robert T. Chien and B. D. Cunningham and I. B. Oldham", + title="Hybrid Methods for Finding Roots of a Polynomial---With + Applications to {BCH} Decoding", + journal="{IEEE} Transactions on Information Theory", + volume=15, + number=2, + pages="329--335", + month=Mar, + year=1969 + }*/ + nroots = rs_quadratic_solve(_gf, _a, _b ^ r, _x); + /*s may be a triple root if s=_b/_a, but not quadruple, since _a!=0.*/ + if (nroots != 2 || _x[0] != s && _x[1] != s) + _x[nroots++] = s; + } + return nroots; + } + /*If there are no odd powers, it's really just a quadratic in disguise.*/ + if (!_c) + return rs_quadratic_solve(_gf, rs_gsqrt(_gf, _b), rs_gsqrt(_gf, _d), + _x); + /*Factor into (x**2 + r*x + s)*(x**2 + r*x + t) by solving for r, which can + be shown to satisfy r**3 + _b*r + _c == 0.*/ + nroots = rs_cubic_solve(_gf, 0, _b, _c, _x); + if (nroots < 1) { + /*The Reed-Solomon code is only valid if we can find 4 distinct roots in + GF(2**8). + If the cubic does not factor into 3 (possibly duplicate) roots, then we + know that the quartic must have a non-trivial irreducible factor.*/ + return 0; + } + r = _x[0]; + /*Now solve for s and t.*/ + b = rs_gdiv(_gf, _c, r); + nroots = rs_quadratic_solve(_gf, b, _d, _x); + if (nroots < 2) + return 0; + s = _x[0]; + t = _x[1]; + /*_c=r*(s^t) was non-zero, so s and t must be distinct. + But if z is a root of z**2 ^ r*z ^ s, then so is (z^r), and s = z*(z^r). + Hence if z is also a root of z**2 ^ r*z ^ t, then t = s, a contradiction. + Thus all four roots are distinct, if they exist.*/ + nroots = rs_quadratic_solve(_gf, r, s, _x); + return nroots + rs_quadratic_solve(_gf, r, t, _x + nroots); +} + +/*Polynomial arithmetic with coefficients in GF(2**8).*/ + +static void rs_poly_zero(unsigned char *_p, int _dp1) +{ + memset(_p, 0, _dp1 * sizeof(*_p)); +} + +static void rs_poly_copy(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memcpy(_p, _q, _dp1 * sizeof(*_p)); +} + +/*Multiply the polynomial by the free variable, x (shift the coefficients). + The number of coefficients, _dp1, must be non-zero.*/ +static void rs_poly_mul_x(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memmove(_p + 1, _q, (_dp1 - 1) * sizeof(*_p)); + _p[0] = 0; +} + +/*Divide the polynomial by the free variable, x (shift the coefficients). + The number of coefficients, _dp1, must be non-zero.*/ +static void rs_poly_div_x(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memmove(_p, _q + 1, (_dp1 - 1) * sizeof(*_p)); + _p[_dp1 - 1] = 0; +} + +/*Compute the first (d+1) coefficients of the product of a degree e and a + degree f polynomial.*/ +static void rs_poly_mult(const rs_gf256 *_gf, unsigned char *_p, int _dp1, + const unsigned char *_q, int _ep1, + const unsigned char *_r, int _fp1) +{ + int m; + int i; + rs_poly_zero(_p, _dp1); + m = _ep1 < _dp1 ? _ep1 : _dp1; + for (i = 0; i < m; i++) + if (_q[i] != 0) { + unsigned logqi; + int n; + int j; + n = _dp1 - i < _fp1 ? _dp1 - i : _fp1; + logqi = _gf->log[_q[i]]; + for (j = 0; j < n; j++) + _p[i + j] ^= rs_hgmul(_gf, _r[j], logqi); + } +} + +/*Decoding.*/ + +/*Computes the syndrome of a codeword.*/ +static void rs_calc_syndrome(const rs_gf256 *_gf, int _m0, unsigned char *_s, + int _npar, const unsigned char *_data, int _ndata) +{ + int i; + int j; + for (j = 0; j < _npar; j++) { + unsigned alphaj; + unsigned sj; + sj = 0; + alphaj = _gf->log[_gf->exp[j + _m0]]; + for (i = 0; i < _ndata; i++) + sj = _data[i] ^ rs_hgmul(_gf, sj, alphaj); + _s[j] = sj; + } +} + +/*Berlekamp-Peterson and Berlekamp-Massey Algorithms for error-location, + modified to handle known erasures, from \cite{CC81}, p. 205. + This finds the coefficients of the error locator polynomial. + The roots are then found by looking for the values of alpha**n where + evaluating the polynomial yields zero. + Error correction is done using the error-evaluator equation on p. 207. + @BOOK{CC81, + author="George C. Clark, Jr and J. Bibb Cain", + title="Error-Correction Coding for Digital Communications", + series="Applications of Communications Theory", + publisher="Springer", + address="New York, NY", + month=Jun, + year=1981 + }*/ + +/*Initialize lambda to the product of (1-x*alpha**e[i]) for erasure locations + e[i]. + Note that the user passes in array indices counting from the beginning of the + data, while our polynomial indexes starting from the end, so + e[i]=(_ndata-1)-_erasures[i].*/ +static void rs_init_lambda(const rs_gf256 *_gf, unsigned char *_lambda, + int _npar, const unsigned char *_erasures, + int _nerasures, int _ndata) +{ + int i; + int j; + rs_poly_zero(_lambda, (_npar < 4 ? 4 : _npar) + 1); + _lambda[0] = 1; + for (i = 0; i < _nerasures; i++) + for (j = i + 1; j > 0; j--) { + _lambda[j] ^= + rs_hgmul(_gf, _lambda[j - 1], _ndata - 1 - _erasures[i]); + } +} + +/*From \cite{CC81}, p. 216. + Returns the number of errors detected (degree of _lambda).*/ +static int rs_modified_berlekamp_massey(const rs_gf256 *_gf, + unsigned char *_lambda, + const unsigned char *_s, + unsigned char *_omega, int _npar, + const unsigned char *_erasures, + int _nerasures, int _ndata) +{ + unsigned char tt[256]; + int n; + int l; + int k; + int i; + /*Initialize _lambda, the error locator-polynomial, with the location of + known erasures.*/ + rs_init_lambda(_gf, _lambda, _npar, _erasures, _nerasures, _ndata); + rs_poly_copy(tt, _lambda, _npar + 1); + l = _nerasures; + k = 0; + for (n = _nerasures + 1; n <= _npar; n++) { + unsigned d; + rs_poly_mul_x(tt, tt, n - k + 1); + d = 0; + for (i = 0; i <= l; i++) + d ^= rs_gmul(_gf, _lambda[i], _s[n - 1 - i]); + if (d != 0) { + unsigned logd; + logd = _gf->log[d]; + if (l < n - k) { + int t; + for (i = 0; i <= n - k; i++) { + unsigned tti; + tti = tt[i]; + tt[i] = rs_hgmul(_gf, _lambda[i], 255 - logd); + _lambda[i] = _lambda[i] ^ rs_hgmul(_gf, tti, logd); + } + t = n - k; + k = n - l; + l = t; + } else + for (i = 0; i <= l; i++) + _lambda[i] = _lambda[i] ^ rs_hgmul(_gf, tt[i], logd); + } + } + rs_poly_mult(_gf, _omega, _npar, _lambda, l + 1, _s, _npar); + return l; +} + +/*Finds all the roots of an error-locator polynomial _lambda by evaluating it + at successive values of alpha, and returns the positions of the associated + errors in _epos. + Returns the number of valid roots identified.*/ +static int rs_find_roots(const rs_gf256 *_gf, unsigned char *_epos, + const unsigned char *_lambda, int _nerrors, int _ndata) +{ + unsigned alpha; + int nroots; + int i; + nroots = 0; + if (_nerrors <= 4) { + /*Explicit solutions for higher degrees are possible. + Chien uses large lookup tables to solve quintics, and Truong et al. give + special algorithms for degree up through 11, though they use exhaustive + search (with reduced complexity) for some portions. + Quartics are good enough for reading CDs, and represent a reasonable code + complexity trade-off without requiring any extra tables. + Note that _lambda[0] is always 1.*/ + _nerrors = rs_quartic_solve(_gf, _lambda[1], _lambda[2], _lambda[3], + _lambda[4], _epos); + for (i = 0; i < _nerrors; i++) + if (_epos[i]) { + alpha = _gf->log[_epos[i]]; + if ((int)alpha < _ndata) + _epos[nroots++] = alpha; + } + return nroots; + } else + for (alpha = 0; (int)alpha < _ndata; alpha++) { + unsigned alphai; + unsigned sum; + sum = 0; + alphai = 0; + for (i = 0; i <= _nerrors; i++) { + sum ^= rs_hgmul(_gf, _lambda[_nerrors - i], alphai); + alphai = _gf->log[_gf->exp[alphai + alpha]]; + } + if (!sum) + _epos[nroots++] = alpha; + } + return nroots; +} + +/*Corrects a codeword with _ndata<256 bytes, of which the last _npar are parity + bytes. + Known locations of errors can be passed in the _erasures array. + Twice as many (up to _npar) errors with a known location can be corrected + compared to errors with an unknown location. + Returns the number of errors corrected if successful, or a negative number if + the message could not be corrected because too many errors were detected.*/ +int rs_correct(const rs_gf256 *_gf, int _m0, unsigned char *_data, int _ndata, + int _npar, const unsigned char *_erasures, int _nerasures) +{ + /*lambda must have storage for at least five entries to avoid special cases + in the low-degree polynomial solver.*/ + unsigned char lambda[256]; + unsigned char omega[256]; + unsigned char epos[256]; + unsigned char s[256]; + int i; + /*If we already have too many erasures, we can't possibly succeed.*/ + if (_nerasures > _npar) + return -1; + /*Compute the syndrome values.*/ + rs_calc_syndrome(_gf, _m0, s, _npar, _data, _ndata); + /*Check for a non-zero value.*/ + for (i = 0; i < _npar; i++) + if (s[i]) { + int nerrors; + int j; + /*Construct the error locator polynomial.*/ + nerrors = rs_modified_berlekamp_massey(_gf, lambda, s, omega, _npar, + _erasures, _nerasures, + _ndata); + /*If we can't locate any errors, we can't force the syndrome values to + zero, and must have a decoding error. + Conversely, if we have too many errors, there's no reason to even attempt + the root search.*/ + if (nerrors <= 0 || nerrors - _nerasures > _npar - _nerasures >> 1) + return -1; + /*Compute the locations of the errors. + If they are not all distinct, or some of them were outside the valid + range for our block size, we have a decoding error.*/ + if (rs_find_roots(_gf, epos, lambda, nerrors, _ndata) < nerrors) + return -1; + /*Now compute the error magnitudes.*/ + for (i = 0; i < nerrors; i++) { + unsigned a; + unsigned b; + unsigned alpha; + unsigned alphan1; + unsigned alphan2; + unsigned alphanj; + alpha = epos[i]; + /*Evaluate omega at alpha**-1.*/ + a = 0; + alphan1 = 255 - alpha; + alphanj = 0; + for (j = 0; j < _npar; j++) { + a ^= rs_hgmul(_gf, omega[j], alphanj); + alphanj = _gf->log[_gf->exp[alphanj + alphan1]]; + } + /*Evaluate the derivative of lambda at alpha**-1 + All the odd powers vanish.*/ + b = 0; + alphan2 = _gf->log[_gf->exp[alphan1 << 1]]; + alphanj = alphan1 + _m0 * alpha % 255; + for (j = 1; j <= _npar; j += 2) { + b ^= rs_hgmul(_gf, lambda[j], alphanj); + alphanj = _gf->log[_gf->exp[alphanj + alphan2]]; + } + /*Apply the correction.*/ + _data[_ndata - 1 - alpha] ^= rs_gdiv(_gf, a, b); + } + return nerrors; + } + return 0; +} + +/*Encoding.*/ + +/*Create an _npar-coefficient generator polynomial for a Reed-Solomon code + with _npar<256 parity bytes.*/ +void rs_compute_genpoly(const rs_gf256 *_gf, int _m0, unsigned char *_genpoly, + int _npar) +{ + int i; + if (_npar <= 0) + return; + rs_poly_zero(_genpoly, _npar); + _genpoly[0] = 1; + /*Multiply by (x+alpha^i) for i = 1 ... _ndata.*/ + for (i = 0; i < _npar; i++) { + unsigned alphai; + int n; + int j; + n = i + 1 < _npar - 1 ? i + 1 : _npar - 1; + alphai = _gf->log[_gf->exp[_m0 + i]]; + for (j = n; j > 0; j--) + _genpoly[j] = _genpoly[j - 1] ^ rs_hgmul(_gf, _genpoly[j], alphai); + _genpoly[0] = rs_hgmul(_gf, _genpoly[0], alphai); + } +} + +/*Adds _npar<=_ndata parity bytes to an _ndata-_npar byte message. + _data must contain room for _ndata<256 bytes.*/ +void rs_encode(const rs_gf256 *_gf, unsigned char *_data, int _ndata, + const unsigned char *_genpoly, int _npar) +{ + unsigned char *lfsr; + unsigned d; + int i; + int j; + if (_npar <= 0) + return; + lfsr = _data + _ndata - _npar; + rs_poly_zero(lfsr, _npar); + for (i = 0; i < _ndata - _npar; i++) { + d = _data[i] ^ lfsr[0]; + if (d) { + unsigned logd; + logd = _gf->log[d]; + for (j = 0; j < _npar - 1; j++) { + lfsr[j] = lfsr[j + 1] ^ + rs_hgmul(_gf, _genpoly[_npar - 1 - j], logd); + } + lfsr[_npar - 1] = rs_hgmul(_gf, _genpoly[0], logd); + } else + rs_poly_div_x(lfsr, lfsr, _npar); + } +} + +#if defined(RS_TEST_ENC) +#include <stdio.h> +#include <stdlib.h> + +int main(void) +{ + rs_gf256 gf; + int k; + rs_gf256_init(&gf, QR_PPOLY); + srand(0); + for (k = 0; k < 64 * 1024; k++) { + unsigned char genpoly[256]; + unsigned char data[256]; + unsigned char epos[256]; + int ndata; + int npar; + int nerrors; + int i; + ndata = rand() & 0xFF; + npar = ndata > 0 ? rand() % ndata : 0; + for (i = 0; i < ndata - npar; i++) + data[i] = rand() & 0xFF; + rs_compute_genpoly(&gf, QR_M0, genpoly, npar); + rs_encode(&gf, QR_M0, data, ndata, genpoly, npar); + /*Write a clean version of the codeword.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" 0\n"); + /*Write the correct output to compare the decoder against.*/ + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) + fprintf(stderr, "%i%s", data[i], i + 1 < ndata ? " " : "\n"); + if (npar > 0) { + /*Corrupt it.*/ + nerrors = rand() % (npar + 1); + if (nerrors > 0) { + /*This test is not quite correct: there could be so many errors it + comes within (npar>>1) errors of another valid codeword. + I don't know a simple way to test for that without trying to decode + the corrupt codeword, though, which is the very code we're trying to + test.*/ + if (nerrors <= npar >> 1) { + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) { + fprintf(stderr, "%i%s", data[i], + i + 1 < ndata ? " " : "\n"); + } + } else + fprintf(stderr, "Failure.\n"); + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) + fprintf(stderr, "%i%s", data[i], + i + 1 < ndata ? " " : "\n"); + for (i = 0; i < ndata; i++) + epos[i] = i; + for (i = 0; i < nerrors; i++) { + unsigned char e; + int ei; + ei = rand() % (ndata - i) + i; + e = epos[ei]; + epos[ei] = epos[i]; + epos[i] = e; + data[e] ^= rand() % 255 + 1; + } + /*First with no erasure locations.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" 0\n"); + /*Now with erasure locations.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" %i", nerrors); + for (i = 0; i < nerrors; i++) + printf(" %i", epos[i]); + printf("\n"); + } + } + } + return 0; +} +#endif + +#if defined(RS_TEST_DEC) +#include <stdio.h> + +int main(void) +{ + rs_gf256 gf; + rs_gf256_init(&gf, QR_PPOLY); + for (;;) { + unsigned char data[255]; + unsigned char erasures[255]; + int idata[255]; + int ierasures[255]; + int ndata; + int npar; + int nerasures; + int nerrors; + int i; + if (scanf("%i", &ndata) < 1 || ndata < 0 || ndata > 255 || + scanf("%i", &npar) < 1 || npar < 0 || npar > ndata) + break; + for (i = 0; i < ndata; i++) { + if (scanf("%i", idata + i) < 1 || idata[i] < 0 || idata[i] > 255) + break; + data[i] = idata[i]; + } + if (i < ndata) + break; + if (scanf("%i", &nerasures) < 1 || nerasures < 0 || nerasures > ndata) + break; + for (i = 0; i < nerasures; i++) { + if (scanf("%i", ierasures + i) < 1 || ierasures[i] < 0 || + ierasures[i] >= ndata) + break; + erasures[i] = ierasures[i]; + } + nerrors = + rs_correct(&gf, QR_M0, data, ndata, npar, erasures, nerasures); + if (nerrors >= 0) { + unsigned char data2[255]; + unsigned char genpoly[255]; + for (i = 0; i < ndata - npar; i++) + data2[i] = data[i]; + rs_compute_genpoly(&gf, QR_M0, genpoly, npar); + rs_encode(&gf, QR_M0, data2, ndata, genpoly, npar); + for (i = ndata - npar; i < ndata; i++) + if (data[i] != data2[i]) { + printf("Abject failure! %i!=%i\n", data[i], data2[i]); + } + printf("Success!\n", nerrors); + for (i = 0; i < ndata; i++) + printf("%i%s", data[i], i + 1 < ndata ? " " : "\n"); + } else + printf("Failure.\n"); + } + return 0; +} +#endif + +#if defined(RS_TEST_ROOTS) +#include <stdio.h> + +/*Exhaustively test the root finder.*/ +int main(void) +{ + rs_gf256 gf; + int a; + int b; + int c; + int d; + rs_gf256_init(&gf, QR_PPOLY); + for (a = 0; a < 256; a++) + for (b = 0; b < 256; b++) + for (c = 0; c < 256; c++) + for (d = 0; d < 256; d++) { + unsigned char x[4]; + unsigned char r[4]; + unsigned x2; + unsigned e[5]; + int nroots; + int mroots; + int i; + int j; + nroots = rs_quartic_solve(&gf, a, b, c, d, x); + for (i = 0; i < nroots; i++) { + x2 = rs_gmul(&gf, x[i], x[i]); + e[0] = rs_gmul(&gf, x2, x2) ^ + rs_gmul(&gf, a, rs_gmul(&gf, x[i], x2)) ^ + rs_gmul(&gf, b, x2) ^ rs_gmul(&gf, c, x[i]) ^ d; + if (e[0]) { + printf( + "Invalid root: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + x[i], a, x[i], b, x[i], c, x[i], d, e[0]); + } + for (j = 0; j < i; j++) + if (x[i] == x[j]) { + printf( + "Repeated root %i=%i: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + i, j, x[i], a, x[i], b, x[i], c, x[i], d, + e[0]); + } + } + mroots = 0; + for (j = 1; j < 256; j++) { + int logx; + int logx2; + logx = gf.log[j]; + logx2 = gf.log[gf.exp[logx << 1]]; + e[mroots] = + d ^ rs_hgmul(&gf, c, logx) ^ + rs_hgmul(&gf, b, logx2) ^ + rs_hgmul(&gf, a, gf.log[gf.exp[logx + logx2]]) ^ + gf.exp[logx2 << 1]; + if (!e[mroots]) + r[mroots++] = j; + } + /*We only care about missing roots if the quartic had 4 distinct, non-zero + roots.*/ + if (mroots == 4) + for (j = 0; j < mroots; j++) { + for (i = 0; i < nroots; i++) + if (x[i] == r[j]) + break; + if (i >= nroots) { + printf( + "Missing root: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + r[j], a, r[j], b, r[j], c, r[j], d, e[j]); + } + } + } + return 0; +} +#endif diff --git a/zbar/qrcode/rs.h b/zbar/qrcode/rs.h new file mode 100644 index 0000000..21b47dc --- /dev/null +++ b/zbar/qrcode/rs.h @@ -0,0 +1,66 @@ +/*Copyright (C) 1991-1995 Henry Minsky (hqm@ua.com, hqm@ai.mit.edu) + Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_rs_H) +#define _qrcode_rs_H (1) + +/*This is one of 16 irreducible primitive polynomials of degree 8: + x**8+x**4+x**3+x**2+1. + Under such a polynomial, x (i.e., 0x02) is a generator of GF(2**8). + The high order 1 bit is implicit. + From~\cite{MD88}, Ch. 5, p. 275 by Patel. + @BOOK{MD88, + author="C. Dennis Mee and Eric D. Daniel", + title="Video, Audio, and Instrumentation Recording", + series="Magnetic Recording", + volume=3, + publisher="McGraw-Hill Education", + address="Columbus, OH", + month=Jun, + year=1988 + }*/ +#define QR_PPOLY (0x1D) + +/*The index to start the generator polynomial from (0...254).*/ +#define QR_M0 (0) + +typedef struct rs_gf256 rs_gf256; + +struct rs_gf256 { + /*A logarithm table in GF(2**8).*/ + unsigned char log[256]; + /*An exponential table in GF(2**8): exp[i] contains x^i reduced modulo the + irreducible primitive polynomial used to define the field. + The extra 256 entries are used to do arithmetic mod 255, since some extra + table lookups are generally faster than doing the modulus.*/ + unsigned char exp[511]; +}; + +/*Initialize discrete logarithm tables for GF(2**8) using a given primitive + irreducible polynomial.*/ +void rs_gf256_init(rs_gf256 *_gf, unsigned _ppoly); + +/*Corrects a codeword with _ndata<256 bytes, of which the last _npar are parity + bytes. + Known locations of errors can be passed in the _erasures array. + Twice as many (up to _npar) errors with a known location can be corrected + compared to errors with an unknown location. + Returns the number of errors corrected if successful, or a negative number if + the message could not be corrected because too many errors were detected.*/ +int rs_correct(const rs_gf256 *_gf, int _m0, unsigned char *_data, int _ndata, + int _npar, const unsigned char *_erasures, int _nerasures); + +/*Create an _npar-coefficient generator polynomial for a Reed-Solomon code with + _npar<256 parity bytes.*/ +void rs_compute_genpoly(const rs_gf256 *_gf, int _m0, unsigned char *_genpoly, + int _npar); + +/*Adds _npar<=_ndata parity bytes to an _ndata-_npar byte message. + _data must contain room for _ndata<256 bytes.*/ +void rs_encode(const rs_gf256 *_gf, unsigned char *_data, int _ndata, + const unsigned char *_genpoly, int _npar); + +#endif diff --git a/zbar/qrcode/util.c b/zbar/qrcode/util.c new file mode 100644 index 0000000..a0a5eab --- /dev/null +++ b/zbar/qrcode/util.c @@ -0,0 +1,144 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "util.h" +#include <stdlib.h> + +/*Computes floor(sqrt(_val)) exactly.*/ +unsigned qr_isqrt(unsigned _val) +{ + unsigned g; + unsigned b; + int bshift; + /*Uses the second method from + http://www.azillionmonkeys.com/qed/sqroot.html + The main idea is to search for the largest binary digit b such that + (g+b)*(g+b) <= _val, and add it to the solution g.*/ + g = 0; + b = 0x8000; + for (bshift = 16; bshift-- > 0;) { + unsigned t; + t = (g << 1) + b << bshift; + if (t <= _val) { + g += b; + _val -= t; + } + b >>= 1; + } + return g; +} + +/*Computes sqrt(_x*_x+_y*_y) using CORDIC. + This implementation is valid for all 32-bit inputs and returns a result + accurate to about 27 bits of precision. + It has been tested for all positive 16-bit inputs, where it returns correctly + rounded results in 99.998% of cases and the maximum error is + 0.500137134862598032 (for _x=48140, _y=63018). + Very nearly all results less than (1<<16) are correctly rounded. + All Pythagorean triples with a hypotenuse of less than ((1<<27)-1) evaluate + correctly, and the total bias over all Pythagorean triples is -0.04579, with + a relative RMS error of 7.2864E-10 and a relative peak error of 7.4387E-9.*/ +unsigned qr_ihypot(int _x, int _y) +{ + unsigned x; + unsigned y; + int mask; + int shift; + int u; + int v; + int i; + x = _x = abs(_x); + y = _y = abs(_y); + mask = -(x > y) & (_x ^ _y); + x ^= mask; + y ^= mask; + _y ^= mask; + shift = 31 - qr_ilog(y); + shift = QR_MAXI(shift, 0); + x = (unsigned)((x << shift) * 0x9B74EDAAULL >> 32); + _y = (int)((_y << shift) * 0x9B74EDA9LL >> 32); + u = x; + mask = -(_y < 0); + x += _y + mask ^ mask; + _y -= u + mask ^ mask; + u = x + 1 >> 1; + v = _y + 1 >> 1; + mask = -(_y < 0); + x += v + mask ^ mask; + _y -= u + mask ^ mask; + for (i = 1; i < 16; i++) { + int r; + u = x + 1 >> 2; + r = (1 << 2 * i) >> 1; + v = _y + r >> 2 * i; + mask = -(_y < 0); + x += v + mask ^ mask; + _y = _y - (u + mask ^ mask) << 1; + } + return x + ((1U << shift) >> 1) >> shift; +} + +#if defined(__GNUC__) && defined(HAVE_FEATURES_H) +#include <features.h> +#if __GNUC_PREREQ(3, 4) +#include <limits.h> +#if INT_MAX >= 2147483647 +#define QR_CLZ0 sizeof(unsigned) * CHAR_BIT +#define QR_CLZ(_x) (__builtin_clz(_x)) +#elif LONG_MAX >= 2147483647L +#define QR_CLZ0 sizeof(unsigned long) * CHAR_BIT +#define QR_CLZ(_x) (__builtin_clzl(_x)) +#endif +#endif +#endif + +int qr_ilog(unsigned _v) +{ +#if defined(QR_CLZ) + /*Note that __builtin_clz is not defined when _x==0, according to the gcc + documentation (and that of the x86 BSR instruction that implements it), so + we have to special-case it.*/ + return QR_CLZ0 - QR_CLZ(_v) & -!!_v; +#else + int ret; + int m; + m = !!(_v & 0xFFFF0000) << 4; + _v >>= m; + ret = m; + m = !!(_v & 0xFF00) << 3; + _v >>= m; + ret |= m; + m = !!(_v & 0xF0) << 2; + _v >>= m; + ret |= m; + m = !!(_v & 0xC) << 1; + _v >>= m; + ret |= m; + ret |= !!(_v & 0x2); + return ret + !!_v; +#endif +} + +#if defined(QR_TEST_SQRT) +#include <math.h> +#include <stdio.h> + +/*Exhaustively test the integer square root function.*/ +int main(void) +{ + unsigned u; + u = 0; + do { + unsigned r; + unsigned s; + r = qr_isqrt(u); + s = (int)sqrt(u); + if (r != s) + printf("%u: %u!=%u\n", u, r, s); + u++; + } while (u); + return 0; +} +#endif diff --git a/zbar/qrcode/util.h b/zbar/qrcode/util.h new file mode 100644 index 0000000..6b605c2 --- /dev/null +++ b/zbar/qrcode/util.h @@ -0,0 +1,56 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_util_H) +#define _qrcode_util_H (1) + +#define QR_MAXI(_a, _b) ((_a) - ((_a) - (_b) & -((_b) > (_a)))) +#define QR_MINI(_a, _b) ((_a) + ((_b) - (_a) & -((_b) < (_a)))) +#define QR_SIGNI(_x) (((_x) > 0) - ((_x) < 0)) +#define QR_SIGNMASK(_x) (-((_x) < 0)) +/*Unlike copysign(), simply inverts the sign of _a if _b is negative.*/ +#define QR_FLIPSIGNI(_a, _b) ((_a) + QR_SIGNMASK(_b) ^ QR_SIGNMASK(_b)) +#define QR_COPYSIGNI(_a, _b) QR_FLIPSIGNI(abs(_a), _b) +/*Divides a signed integer by a positive value with exact rounding.*/ +#define QR_DIVROUND(_x, _y) (((_x) + QR_FLIPSIGNI(_y >> 1, _x)) / (_y)) +#define QR_CLAMPI(_a, _b, _c) (QR_MAXI(_a, QR_MINI(_b, _c))) +#define QR_CLAMP255(_x) \ + ((unsigned char)((((_x) < 0) - 1) & ((_x) | -((_x) > 255)))) +#define QR_SWAP2I(_a, _b) \ + do { \ + int t__; \ + t__ = (_a); \ + (_a) = (_b); \ + (_b) = t__; \ + } while (0) +/*Swaps two integers _a and _b if _a>_b.*/ +#define QR_SORT2I(_a, _b) \ + do { \ + int t__; \ + t__ = QR_MINI(_a, _b) ^ (_a); \ + (_a) ^= t__; \ + (_b) ^= t__; \ + } while (0) +#define QR_ILOG0(_v) (!!((_v)&0x2)) +#define QR_ILOG1(_v) (((_v)&0xC) ? 2 + QR_ILOG0((_v) >> 2) : QR_ILOG0(_v)) +#define QR_ILOG2(_v) (((_v)&0xF0) ? 4 + QR_ILOG1((_v) >> 4) : QR_ILOG1(_v)) +#define QR_ILOG3(_v) (((_v)&0xFF00) ? 8 + QR_ILOG2((_v) >> 8) : QR_ILOG2(_v)) +#define QR_ILOG4(_v) \ + (((_v)&0xFFFF0000) ? 16 + QR_ILOG3((_v) >> 16) : QR_ILOG3(_v)) +/*Computes the integer logarithm of a (positive, 32-bit) constant.*/ +#define QR_ILOG(_v) ((int)QR_ILOG4((unsigned)(_v))) + +/*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and + takes bits [_s,_s+31] of the result.*/ +#define QR_FIXMUL(_a, _b, _r, _s) ((int)((_a) * (long long)(_b) + (_r) >> (_s))) +/*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and + gives all 64 bits of the result.*/ +#define QR_EXTMUL(_a, _b, _r) ((_a) * (long long)(_b) + (_r)) + +unsigned qr_isqrt(unsigned _val); +unsigned qr_ihypot(int _x, int _y); +int qr_ilog(unsigned _val); + +#endif diff --git a/zbar/refcnt.c b/zbar/refcnt.c new file mode 100644 index 0000000..e3a5cd9 --- /dev/null +++ b/zbar/refcnt.c @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "refcnt.h" + +#if !defined(_WIN32) && !defined(TARGET_OS_MAC) && defined(HAVE_LIBPTHREAD) + +pthread_once_t initialized = PTHREAD_ONCE_INIT; +pthread_mutex_t _zbar_reflock; + +static void initialize(void) +{ + pthread_mutex_init(&_zbar_reflock, NULL); +} + +void _zbar_refcnt_init() +{ + pthread_once(&initialized, initialize); +} + +#else + +void _zbar_refcnt_init() +{ +} + +#endif diff --git a/zbar/refcnt.h b/zbar/refcnt.h new file mode 100644 index 0000000..259fab3 --- /dev/null +++ b/zbar/refcnt.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _REFCNT_H_ +#define _REFCNT_H_ + +#include "config.h" +#include <assert.h> + +#if defined(_WIN32) +#include <windows.h> + +typedef LONG refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = -1; + if (delta > 0) + while (delta--) + rc = InterlockedIncrement(cnt); + else if (delta < 0) + while (delta++) + rc = InterlockedDecrement(cnt); + assert(rc >= 0); + return (rc); +} + +#elif defined(TARGET_OS_MAC) +#include <libkern/OSAtomic.h> + +typedef int32_t refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = OSAtomicAdd32Barrier(delta, cnt); + assert(rc >= 0); + return (rc); +} + +#elif defined(HAVE_LIBPTHREAD) +#include <pthread.h> + +typedef int refcnt_t; + +extern pthread_mutex_t _zbar_reflock; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + pthread_mutex_lock(&_zbar_reflock); + int rc = (*cnt += delta); + pthread_mutex_unlock(&_zbar_reflock); + assert(rc >= 0); + return (rc); +} + +#else + +typedef int refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = (*cnt += delta); + assert(rc >= 0); + return (rc); +} + +#endif + +void _zbar_refcnt_init(void); + +#endif diff --git a/zbar/scanner.c b/zbar/scanner.c new file mode 100644 index 0000000..1db14f5 --- /dev/null +++ b/zbar/scanner.c @@ -0,0 +1,311 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stddef.h> +#include <stdlib.h> /* malloc, free, abs */ +#include <string.h> /* memset */ + +#include <zbar.h> +#include "svg.h" + +#ifdef DEBUG_SCANNER +#define DEBUG_LEVEL (DEBUG_SCANNER) +#endif +#include "debug.h" + +#ifndef ZBAR_FIXED +#define ZBAR_FIXED 5 +#endif +#define ROUND (1 << (ZBAR_FIXED - 1)) + +/* FIXME add runtime config API for these */ +#ifndef ZBAR_SCANNER_THRESH_MIN +#define ZBAR_SCANNER_THRESH_MIN 4 +#endif + +#ifndef ZBAR_SCANNER_THRESH_INIT_WEIGHT +#define ZBAR_SCANNER_THRESH_INIT_WEIGHT .44 +#endif +#define THRESH_INIT \ + ((unsigned)((ZBAR_SCANNER_THRESH_INIT_WEIGHT * (1 << (ZBAR_FIXED + 1)) + \ + 1) / \ + 2)) + +#ifndef ZBAR_SCANNER_THRESH_FADE +#define ZBAR_SCANNER_THRESH_FADE 8 +#endif + +#ifndef ZBAR_SCANNER_EWMA_WEIGHT +#define ZBAR_SCANNER_EWMA_WEIGHT .78 +#endif +#define EWMA_WEIGHT \ + ((unsigned)((ZBAR_SCANNER_EWMA_WEIGHT * (1 << (ZBAR_FIXED + 1)) + 1) / 2)) + +/* scanner state */ +struct zbar_scanner_s { + zbar_decoder_t *decoder; /* associated bar width decoder */ + unsigned y1_min_thresh; /* minimum threshold */ + + unsigned x; /* relative scan position of next sample */ + int y0[4]; /* short circular buffer of average intensities */ + + int y1_sign; /* slope at last crossing */ + unsigned y1_thresh; /* current slope threshold */ + + unsigned cur_edge; /* interpolated position of tracking edge */ + unsigned last_edge; /* interpolated position of last located edge */ + unsigned width; /* last element width */ +}; + +zbar_scanner_t *zbar_scanner_create(zbar_decoder_t *dcode) +{ + zbar_scanner_t *scn = malloc(sizeof(zbar_scanner_t)); + scn->decoder = dcode; + scn->y1_min_thresh = ZBAR_SCANNER_THRESH_MIN; + zbar_scanner_reset(scn); + return (scn); +} + +void zbar_scanner_destroy(zbar_scanner_t *scn) +{ + free(scn); +} + +zbar_symbol_type_t zbar_scanner_reset(zbar_scanner_t *scn) +{ + memset(&scn->x, 0, sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x)); + scn->y1_thresh = scn->y1_min_thresh; + if (scn->decoder) + zbar_decoder_reset(scn->decoder); + return (ZBAR_NONE); +} + +unsigned zbar_scanner_get_width(const zbar_scanner_t *scn) +{ + return (scn->width); +} + +unsigned zbar_scanner_get_edge(const zbar_scanner_t *scn, unsigned offset, + int prec) +{ + unsigned edge = scn->last_edge - offset - (1 << ZBAR_FIXED) - ROUND; + prec = ZBAR_FIXED - prec; + if (prec > 0) + return (edge >> prec); + else if (!prec) + return (edge); + else + return (edge << -prec); +} + +zbar_color_t zbar_scanner_get_color(const zbar_scanner_t *scn) +{ + return ((scn->y1_sign <= 0) ? ZBAR_SPACE : ZBAR_BAR); +} + +static inline unsigned calc_thresh(zbar_scanner_t *scn) +{ + /* threshold 1st to improve noise rejection */ + unsigned dx, thresh = scn->y1_thresh; + unsigned long t; + if ((thresh <= scn->y1_min_thresh) || !scn->width) { + dbprintf(1, " tmin=%d", scn->y1_min_thresh); + return (scn->y1_min_thresh); + } + /* slowly return threshold to min */ + dx = (scn->x << ZBAR_FIXED) - scn->last_edge; + t = thresh * dx; + t /= scn->width; + t /= ZBAR_SCANNER_THRESH_FADE; + dbprintf(1, " thr=%d t=%ld x=%d last=%d.%d (%d)", thresh, t, scn->x, + scn->last_edge >> ZBAR_FIXED, + scn->last_edge & ((1 << ZBAR_FIXED) - 1), dx); + if (thresh > t) { + thresh -= t; + if (thresh > scn->y1_min_thresh) + return (thresh); + } + scn->y1_thresh = scn->y1_min_thresh; + return (scn->y1_min_thresh); +} + +static inline zbar_symbol_type_t process_edge(zbar_scanner_t *scn, int y1) +{ + if (!scn->y1_sign) + scn->last_edge = scn->cur_edge = (1 << ZBAR_FIXED) + ROUND; + else if (!scn->last_edge) + scn->last_edge = scn->cur_edge; + + scn->width = scn->cur_edge - scn->last_edge; + dbprintf(1, " sgn=%d cur=%d.%d w=%d (%s)\n", scn->y1_sign, + scn->cur_edge >> ZBAR_FIXED, + scn->cur_edge & ((1 << ZBAR_FIXED) - 1), scn->width, + ((y1 > 0) ? "SPACE" : "BAR")); + scn->last_edge = scn->cur_edge; + +#if DEBUG_SVG > 1 + svg_path_moveto(SVG_ABS, scn->last_edge - (1 << ZBAR_FIXED) - ROUND, 0); +#endif + + /* pass to decoder */ + if (scn->decoder) + return (zbar_decode_width(scn->decoder, scn->width)); + return (ZBAR_PARTIAL); +} + +inline zbar_symbol_type_t zbar_scanner_flush(zbar_scanner_t *scn) +{ + unsigned x; + if (!scn->y1_sign) + return (ZBAR_NONE); + + x = (scn->x << ZBAR_FIXED) + ROUND; + + if (scn->cur_edge != x || scn->y1_sign > 0) { + zbar_symbol_type_t edge = process_edge(scn, -scn->y1_sign); + dbprintf(1, "flush0:"); + scn->cur_edge = x; + scn->y1_sign = -scn->y1_sign; + return (edge); + } + + scn->y1_sign = scn->width = 0; + if (scn->decoder) + return (zbar_decode_width(scn->decoder, 0)); + return (ZBAR_PARTIAL); +} + +zbar_symbol_type_t zbar_scanner_new_scan(zbar_scanner_t *scn) +{ + zbar_symbol_type_t edge = ZBAR_NONE; + while (scn->y1_sign) { + zbar_symbol_type_t tmp = zbar_scanner_flush(scn); + if (tmp < 0 || tmp > edge) + edge = tmp; + } + + /* reset scanner and associated decoder */ + memset(&scn->x, 0, sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x)); + scn->y1_thresh = scn->y1_min_thresh; + if (scn->decoder) + zbar_decoder_new_scan(scn->decoder); + return (edge); +} + +zbar_symbol_type_t zbar_scan_y(zbar_scanner_t *scn, int y) +{ + /* FIXME calc and clip to max y range... */ + /* retrieve short value history */ + register int x = scn->x; + register int y0_1 = scn->y0[(x - 1) & 3]; + register int y0_0 = y0_1; + register int y0_2, y0_3, y1_1, y2_1, y2_2; + zbar_symbol_type_t edge; + if (x) { + /* update weighted moving average */ + y0_0 += ((int)((y - y0_1) * EWMA_WEIGHT)) >> ZBAR_FIXED; + scn->y0[x & 3] = y0_0; + } else + y0_0 = y0_1 = scn->y0[0] = scn->y0[1] = scn->y0[2] = scn->y0[3] = y; + y0_2 = scn->y0[(x - 2) & 3]; + y0_3 = scn->y0[(x - 3) & 3]; + /* 1st differential @ x-1 */ + y1_1 = y0_1 - y0_2; + { + register int y1_2 = y0_2 - y0_3; + if ((abs(y1_1) < abs(y1_2)) && ((y1_1 >= 0) == (y1_2 >= 0))) + y1_1 = y1_2; + } + + /* 2nd differentials @ x-1 & x-2 */ + y2_1 = y0_0 - (y0_1 * 2) + y0_2; + y2_2 = y0_1 - (y0_2 * 2) + y0_3; + + dbprintf(1, "scan: x=%d y=%d y0=%d y1=%d y2=%d", x, y, y0_1, y1_1, y2_1); + + edge = ZBAR_NONE; + /* 2nd zero-crossing is 1st local min/max - could be edge */ + if ((!y2_1 || ((y2_1 > 0) ? y2_2 < 0 : y2_2 > 0)) && + (calc_thresh(scn) <= abs(y1_1))) { + /* check for 1st sign change */ + char y1_rev = (scn->y1_sign > 0) ? y1_1 < 0 : y1_1 > 0; + if (y1_rev) + /* intensity change reversal - finalize previous edge */ + edge = process_edge(scn, y1_1); + + if (y1_rev || (abs(scn->y1_sign) < abs(y1_1))) { + int d; + scn->y1_sign = y1_1; + + /* adaptive thresholding */ + /* start at multiple of new min/max */ + scn->y1_thresh = (abs(y1_1) * THRESH_INIT + ROUND) >> ZBAR_FIXED; + dbprintf(1, "\tthr=%d", scn->y1_thresh); + if (scn->y1_thresh < scn->y1_min_thresh) + scn->y1_thresh = scn->y1_min_thresh; + + /* update current edge */ + d = y2_1 - y2_2; + scn->cur_edge = 1 << ZBAR_FIXED; + if (!d) + scn->cur_edge >>= 1; + else if (y2_1) + /* interpolate zero crossing */ + scn->cur_edge -= ((y2_1 << ZBAR_FIXED) + 1) / d; + scn->cur_edge += x << ZBAR_FIXED; + dbprintf(1, "\n"); + } + } else + dbprintf(1, "\n"); + /* FIXME add fall-thru pass to decoder after heuristic "idle" period + (eg, 6-8 * last width) */ + scn->x = x + 1; + return (edge); +} + +/* undocumented API for drawing cutesy debug graphics */ +void zbar_scanner_get_state(const zbar_scanner_t *scn, unsigned *x, + unsigned *cur_edge, unsigned *last_edge, int *y0, + int *y1, int *y2, int *y1_thresh) +{ + register int y0_0 = scn->y0[(scn->x - 1) & 3]; + register int y0_1 = scn->y0[(scn->x - 2) & 3]; + register int y0_2 = scn->y0[(scn->x - 3) & 3]; + zbar_scanner_t *mut_scn; + if (x) + *x = scn->x - 1; + if (last_edge) + *last_edge = scn->last_edge; + if (y0) + *y0 = y0_1; + if (y1) + *y1 = y0_1 - y0_2; + if (y2) + *y2 = y0_0 - (y0_1 * 2) + y0_2; + /* NB not quite accurate (uses updated x) */ + mut_scn = (zbar_scanner_t *)scn; + if (y1_thresh) + *y1_thresh = calc_thresh(mut_scn); + dbprintf(1, "\n"); +} diff --git a/zbar/sqcode.c b/zbar/sqcode.c new file mode 100644 index 0000000..422c803 --- /dev/null +++ b/zbar/sqcode.c @@ -0,0 +1,578 @@ +/*Copyright (C) 2018 Javier Serrano Polo <javier@jasp.net> + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "config.h" + +#include "sqcode.h" + +#include <stdbool.h> +#include <stdlib.h> + +#include "image.h" +#include "img_scanner.h" + +typedef enum +{ + SHAPE_DOT, + SHAPE_CORNER, + SHAPE_OTHER, + SHAPE_VOID +} shape_t; + +typedef struct { + float x; + float y; +} sq_point; + +typedef struct { + shape_t type; + unsigned x0; + unsigned y0; + unsigned width; + unsigned height; + sq_point center; +} sq_dot; + +struct sq_reader { + bool enabled; +}; + +/*Initializes a client reader handle.*/ +static void sq_reader_init(sq_reader *reader) +{ + reader->enabled = true; +} + +/*Allocates a client reader handle.*/ +sq_reader *_zbar_sq_create(void) +{ + sq_reader *reader = malloc(sizeof(sq_reader)); + if (reader) + sq_reader_init(reader); + return reader; +} + +/*Frees a client reader handle.*/ +void _zbar_sq_destroy(sq_reader *reader) +{ + free(reader); +} + +/* reset finder state between scans */ +void _zbar_sq_reset(sq_reader *reader) +{ + reader->enabled = true; +} + +int _zbar_sq_new_config(sq_reader *reader, unsigned config) +{ + reader->enabled = config; + return 0; +} + +static const char base64_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static char *base64_encode_buffer(const char *s, size_t size) +{ + char *e; + size_t encoded_size = (size + 2) / 3 * 4 + 1; + char *encoded = malloc(encoded_size); + if (!encoded) + return NULL; + e = encoded; + for (;;) { + unsigned char c = (*s >> 2) & 0x3f; + *e++ = base64_table[c]; + c = (*s++ << 4) & 0x30; + if (!--size) { + *e++ = base64_table[c]; + *e++ = '='; + *e++ = '='; + break; + } + c |= (*s >> 4) & 0x0f; + *e++ = base64_table[c]; + c = (*s++ << 2) & 0x3c; + if (!--size) { + *e++ = base64_table[c]; + *e++ = '='; + break; + } + c |= (*s >> 6) & 0x03; + *e++ = base64_table[c]; + c = *s++ & 0x3f; + *e++ = base64_table[c]; + if (!--size) + break; + } + *e = '\0'; + return encoded; +} + +static bool sq_extract_text(zbar_image_scanner_t *iscn, const char *buf, + size_t len) +{ + size_t b64_len; + zbar_symbol_t *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_SQCODE, 0); + sym->data = base64_encode_buffer(buf, len); + if (!sym->data) { + _zbar_image_scanner_recycle_syms(iscn, sym); + return true; + } + b64_len = (len + 2) / 3 * 4; + sym->data_alloc = b64_len + 1; + sym->datalen = b64_len; + _zbar_image_scanner_add_sym(iscn, sym); + return false; +} + +static bool is_black_color(const unsigned char c) +{ + return c <= 0x7f; +} + +static bool is_black(zbar_image_t *img, int x, int y) +{ + const unsigned char *data; + if (x < 0 || (unsigned)x >= img->width || y < 0 || + (unsigned)y >= img->height) + return false; + data = img->data; + return is_black_color(data[y * img->width + x]); +} + +static void set_dot_center(sq_dot *dot, float x, float y) +{ + dot->center.x = x; + dot->center.y = y; +} + +static void sq_scan_shape(zbar_image_t *img, sq_dot *dot, int start_x, + int start_y) +{ + int x, y; + unsigned x0, y0, width, height, x_sum, y_sum, total_weight; + const unsigned char *data; + if (!is_black(img, start_x, start_y)) { + dot->type = SHAPE_VOID; + dot->x0 = start_x; + dot->y0 = start_y; + dot->width = 0; + dot->height = 0; + set_dot_center(dot, start_x, start_y); + return; + } + + x0 = start_x; + y0 = start_y; + width = 1; + height = 1; + +new_point: + for (x = x0 - 1; x < (int)(x0 + width + 1); x++) { + if (is_black(img, x, y0 - 1)) { + y0 = y0 - 1; + height++; + goto new_point; + } + if (is_black(img, x, y0 + height)) { + height++; + goto new_point; + } + } + for (y = y0; y < (int)(y0 + height); y++) { + if (is_black(img, x0 - 1, y)) { + x0 = x0 - 1; + width++; + goto new_point; + } + if (is_black(img, x0 + width, y)) { + width++; + goto new_point; + } + } + + dot->x0 = x0; + dot->y0 = y0; + dot->width = width; + dot->height = height; + + /* Is it a corner? */ + if (is_black(img, x0 + 0.25 * width, y0 + 0.25 * height) && + !is_black(img, x0 + 0.75 * width, y0 + 0.25 * height) && + !is_black(img, x0 + 0.25 * width, y0 + 0.75 * height) && + is_black(img, x0 + 0.75 * width, y0 + 0.75 * height)) { + dot->type = SHAPE_CORNER; + set_dot_center(dot, x0 + 0.5 * width, y0 + 0.5 * height); + return; + } + + /* Set dot center */ + data = img->data; + x_sum = 0; + y_sum = 0; + total_weight = 0; + for (y = y0; y < (int)(y0 + height); y++) { + int x; + for (x = x0; x < (int)(x0 + width); x++) { + unsigned char weight; + if (!is_black(img, x, y)) + continue; + weight = 0xff - data[y * img->width + x]; + x_sum += weight * x; + y_sum += weight * y; + total_weight += weight; + } + } + dot->type = SHAPE_DOT; + set_dot_center(dot, x_sum / (float)total_weight + 0.5, + y_sum / (float)total_weight + 0.5); + + /* TODO: Is it other shape? White hole? Really a dot? */ +} + +static void set_middle_point(sq_point *middle, const sq_point *start, + const sq_point *end) +{ + middle->x = (start->x + end->x) / 2; + middle->y = (start->y + end->y) / 2; +} + +bool find_left_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int y, x; + for (y = dot->y0; y < (int)(dot->y0 + dot->height); y++) { + for (x = dot->x0 - 1; x >= (int)(dot->x0 - 2 * dot->width); x--) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +bool find_right_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int y, x; + for (y = dot->y0; y < (int)(dot->y0 + dot->height); y++) { + for (x = dot->x0 + dot->width; x < (int)(dot->x0 + 3 * dot->width); + x++) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +bool find_bottom_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int x, y; + + for (x = dot->x0 + dot->width - 1; x >= (int)dot->x0; x--) { + for (y = dot->y0 + dot->height; y < (int)(dot->y0 + 3 * dot->height); + y++) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +int _zbar_sq_decode(sq_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + unsigned scan_y, scan_x, y; + sq_dot start_dot, top_left_dot, top_right_dot, bottom_left_dot, + bottom_right_dot, bottom_left2_dot; + bool start_corner, error; + sq_point *top_border, *left_border, *right_border, *bottom_border; + size_t border_len, cur_len, offset, bit_side_len, bit_len, byte_len, idx; + float inc_x, inc_y; + void *ptr; + char *buf; + + if (!reader->enabled) + return 0; + + if (img->format != fourcc('Y', '8', '0', '0')) { + fputs("Unexpected image format\n", stderr); + return 1; + } + + /* Starting pixel */ + for (scan_y = 0; scan_y < img->height; scan_y++) { + for (scan_x = 0; scan_x < img->width; scan_x++) { + if (is_black(img, scan_x, scan_y)) + goto found_start; + } + } + return 1; + +found_start:; + /* Starting dot */ + sq_scan_shape(img, &start_dot, scan_x, scan_y); + + start_corner = start_dot.type == SHAPE_CORNER; + + error = true; + + top_border = NULL; + left_border = NULL; + right_border = NULL; + bottom_border = NULL; + + if (start_corner) { + border_len = 0; + } else { + border_len = 1; + top_border = malloc(sizeof(sq_point)); + if (!top_border) + return 1; + top_border[0] = start_dot.center; + } + + top_left_dot = start_dot; + while (find_left_dot(img, &top_left_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &top_left_dot, scan_x, scan_y); + if (top_left_dot.type != SHAPE_DOT) + goto free_borders; + if (border_len) { + void *ptr; + size_t i; + border_len += 2; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + for (i = border_len - 1; i >= 2; i--) + top_border[i] = top_border[i - 2]; + top_border[0] = top_left_dot.center; + set_middle_point(&top_border[1], &top_border[0], &top_border[2]); + } else { + border_len = 1; + top_border = malloc(sizeof(sq_point)); + if (!top_border) + return 1; + top_border[0] = top_left_dot.center; + } + } + if (top_left_dot.type != SHAPE_DOT) + goto free_borders; + + top_right_dot = start_dot; + if (!start_corner) { + while (find_right_dot(img, &top_right_dot, &scan_x, &scan_y)) { + void *ptr; + sq_scan_shape(img, &top_right_dot, scan_x, scan_y); + if (top_right_dot.type == SHAPE_CORNER) + break; + if (top_right_dot.type != SHAPE_DOT) + goto free_borders; + border_len += 2; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + top_border[border_len - 1] = top_right_dot.center; + set_middle_point(&top_border[border_len - 2], + &top_border[border_len - 3], + &top_border[border_len - 1]); + } + } + if (border_len < 3) + goto free_borders; + inc_x = top_border[border_len - 1].x - top_border[border_len - 3].x; + inc_y = top_border[border_len - 1].y - top_border[border_len - 3].y; + border_len += 3; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + top_border[border_len - 3].x = top_border[border_len - 4].x + 0.5 * inc_x; + top_border[border_len - 3].y = top_border[border_len - 4].y + 0.5 * inc_y; + top_border[border_len - 2].x = top_border[border_len - 4].x + inc_x; + top_border[border_len - 2].y = top_border[border_len - 4].y + inc_y; + top_border[border_len - 1].x = top_border[border_len - 4].x + 1.5 * inc_x; + top_border[border_len - 1].y = top_border[border_len - 4].y + 1.5 * inc_y; + + left_border = malloc(border_len * sizeof(sq_point)); + if (!left_border) + goto free_borders; + left_border[0] = top_border[0]; + + bottom_left_dot = top_left_dot; + cur_len = 1; + while (find_bottom_dot(img, &bottom_left_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_left_dot, scan_x, scan_y); + if (bottom_left_dot.type == SHAPE_CORNER) + break; + if (bottom_left_dot.type != SHAPE_DOT) + goto free_borders; + cur_len += 2; + if (cur_len > border_len) + goto free_borders; + left_border[cur_len - 1] = bottom_left_dot.center; + set_middle_point(&left_border[cur_len - 2], &left_border[cur_len - 3], + &left_border[cur_len - 1]); + } + if (cur_len != border_len - 3 || bottom_left_dot.type != SHAPE_CORNER) + goto free_borders; + inc_x = left_border[cur_len - 1].x - left_border[cur_len - 3].x; + inc_y = left_border[cur_len - 1].y - left_border[cur_len - 3].y; + left_border[border_len - 3].x = left_border[border_len - 4].x + 0.5 * inc_x; + left_border[border_len - 3].y = left_border[border_len - 4].y + 0.5 * inc_y; + left_border[border_len - 2].x = left_border[border_len - 4].x + inc_x; + left_border[border_len - 2].y = left_border[border_len - 4].y + inc_y; + left_border[border_len - 1].x = left_border[border_len - 4].x + 1.5 * inc_x; + left_border[border_len - 1].y = left_border[border_len - 4].y + 1.5 * inc_y; + + right_border = malloc(border_len * sizeof(sq_point)); + if (!right_border) + goto free_borders; + + bottom_right_dot = top_right_dot; + cur_len = 3; + while (find_bottom_dot(img, &bottom_right_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_right_dot, scan_x, scan_y); + if (bottom_right_dot.type != SHAPE_DOT) + goto free_borders; + if (cur_len == 3) { + cur_len++; + if (cur_len > border_len) + goto free_borders; + right_border[cur_len - 1] = bottom_right_dot.center; + } else { + cur_len += 2; + if (cur_len > border_len) + goto free_borders; + right_border[cur_len - 1] = bottom_right_dot.center; + set_middle_point(&right_border[cur_len - 2], + &right_border[cur_len - 3], + &right_border[cur_len - 1]); + } + } + if (cur_len != border_len || border_len < 6) + return 1; + inc_x = right_border[5].x - right_border[3].x; + inc_y = right_border[5].y - right_border[3].y; + right_border[2].x = right_border[3].x - 0.5 * inc_x; + right_border[2].y = right_border[3].y - 0.5 * inc_y; + right_border[1].x = right_border[3].x - inc_x; + right_border[1].y = right_border[3].y - inc_y; + right_border[0].x = right_border[3].x - 1.5 * inc_x; + right_border[0].y = right_border[3].y - 1.5 * inc_y; + + bottom_border = malloc(border_len * sizeof(sq_point)); + if (!bottom_border) + goto free_borders; + bottom_border[border_len - 1] = right_border[border_len - 1]; + + bottom_left2_dot = bottom_right_dot; + offset = border_len - 1; + while (find_left_dot(img, &bottom_left2_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_left2_dot, scan_x, scan_y); + if (bottom_left2_dot.type == SHAPE_CORNER) + break; + if (bottom_left2_dot.type != SHAPE_DOT) + goto free_borders; + if (offset < 2) + goto free_borders; + offset -= 2; + bottom_border[offset] = bottom_left2_dot.center; + set_middle_point(&bottom_border[offset + 1], &bottom_border[offset], + &bottom_border[offset + 2]); + } + if (offset != 3 || bottom_left2_dot.type != SHAPE_CORNER) + goto free_borders; + inc_x = bottom_border[5].x - bottom_border[3].x; + inc_y = bottom_border[5].y - bottom_border[3].y; + bottom_border[2].x = bottom_border[3].x - 0.5 * inc_x; + bottom_border[2].y = bottom_border[3].y - 0.5 * inc_y; + bottom_border[1].x = bottom_border[3].x - inc_x; + bottom_border[1].y = bottom_border[3].y - inc_y; + bottom_border[0].x = bottom_border[3].x - 1.5 * inc_x; + bottom_border[0].y = bottom_border[3].y - 1.5 * inc_y; + + /* Size check */ + if (border_len < 8 + 2 * (1 + 2) || border_len > 65535) + goto free_borders; + bit_side_len = border_len - 2 * (1 + 2); + + bit_len = bit_side_len * bit_side_len; + if (bit_len % 8) + goto free_borders; + byte_len = bit_len / 8; + buf = calloc(byte_len, sizeof(char)); + if (!buf) + goto free_borders; + + idx = 0; + for (y = 3; y <= border_len - 4; y++) { + unsigned x; + for (x = 3; x <= border_len - 4; x++) { + unsigned char bottom_right_color, mixed_color; + float bottom_weight = y / (float)(border_len - 1); + float top_weight = 1 - bottom_weight; + float right_weight = x / (float)(border_len - 1); + float left_weight = 1 - right_weight; + + sq_point top_left_source = { + top_border[x].x + left_border[y].x - left_border[0].x, + top_border[x].y + left_border[y].y - left_border[0].y + }; + + sq_point bottom_right_source = { + bottom_border[x].x + right_border[y].x - + right_border[border_len - 1].x, + bottom_border[x].y + right_border[y].y - + right_border[border_len - 1].y + }; + + const unsigned char *data = img->data; + unsigned sample_x = top_left_source.x; + unsigned sample_y = top_left_source.y; + unsigned char top_left_color = + data[sample_y * img->width + sample_x]; + sample_x = bottom_right_source.x; + sample_y = bottom_right_source.y; + bottom_right_color = data[sample_y * img->width + sample_x]; + + mixed_color = + ((top_weight + left_weight) * top_left_color + + (bottom_weight + right_weight) * bottom_right_color) / + 2; + + if (is_black_color(mixed_color)) + buf[idx / 8] |= 1 << 7 - idx % 8; + idx++; + } + } + error = sq_extract_text(iscn, buf, byte_len); + free(buf); + +free_borders: + free(top_border); + free(left_border); + free(right_border); + free(bottom_border); + return error ? 1 : 0; +} diff --git a/zbar/sqcode.h b/zbar/sqcode.h new file mode 100644 index 0000000..b3f5430 --- /dev/null +++ b/zbar/sqcode.h @@ -0,0 +1,21 @@ +/*Copyright (C) 2018 Javier Serrano Polo <javier@jasp.net> + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#ifndef _SQCODE_H_ +#define _SQCODE_H_ + +#include <zbar.h> + +typedef struct sq_reader sq_reader; + +sq_reader *_zbar_sq_create(void); +void _zbar_sq_destroy(sq_reader *reader); +void _zbar_sq_reset(sq_reader *reader); + +int _zbar_sq_new_config(sq_reader *reader, unsigned config); +int _zbar_sq_decode(sq_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img); + +#endif diff --git a/zbar/svg.c b/zbar/svg.c new file mode 100644 index 0000000..f4788dd --- /dev/null +++ b/zbar/svg.c @@ -0,0 +1,187 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "svg.h" + +static const char svg_head[] = + "<?xml version='1.0'?>\n" + "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" + " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n" + "<svg version='1.1' id='top' width='8in' height='8in'" + " preserveAspectRatio='xMidYMid' overflow='visible'" + " viewBox='%g,%g %g,%g' xmlns:xlink='http://www.w3.org/1999/xlink'" + " xmlns='http://www.w3.org/2000/svg'>\n" + "<defs><style type='text/css'><![CDATA[\n" + "* { image-rendering: optimizeSpeed }\n" + "image { opacity: .9 }\n" + "path, line, circle { fill: none; stroke-width: .5;" + " stroke-linejoin: round; stroke-linecap: butt;" + " stroke-opacity: .666; fill-opacity: .666 }\n" + /*"#hedge line, #vedge line { stroke: #34f }\n"*/ + "#hedge, #vedge { stroke: yellow }\n" + "#target * { fill: none; stroke: #f24 }\n" + "#mdot * { fill: #e2f; stroke: none }\n" + "#ydot * { fill: yellow; stroke: none }\n" + "#cross * { stroke: #44f }\n" + ".hedge { marker: url(#hedge) }\n" + ".vedge { marker: url(#vedge) }\n" + ".scanner .hedge, .scanner .vedge { stroke-width: 8 }\n" + ".finder .hedge, .finder .vedge { /*stroke: #a0c;*/ stroke-width: .9 }\n" + ".cluster { stroke: #a0c; stroke-width: 1; stroke-opacity: .45 }\n" + ".cluster.valid { stroke: #c0a; stroke-width: 1; stroke-opacity: .666 }\n" + ".h.cluster { marker: url(#vedge) }\n" + ".v.cluster { marker: url(#hedge) }\n" + ".centers { marker: url(#target); stroke-width: 3 }\n" + ".edge-pts { marker: url(#ydot); stroke-width: .5 }\n" + ".align { marker: url(#mdot); stroke-width: 1.5 }\n" + ".sampling-grid { stroke-width: .75; marker: url(#cross) }\n" + "]]></style>\n" + "<marker id='hedge' overflow='visible'><line x1='-2' x2='2'/></marker>\n" + "<marker id='vedge' overflow='visible'><line y1='-2' y2='2'/></marker>\n" + "<marker id='ydot' overflow='visible'><circle r='2'/></marker>\n" + "<marker id='mdot' overflow='visible'><circle r='2'/></marker>\n" + "<marker id='cross' overflow='visible'><path d='M-2,0h4 M0,-2v4'/></marker>\n" + "<marker id='target' overflow='visible'><path d='M-4,0h8 M0,-4v8'/><circle r='2'/></marker>\n" + "</defs>\n"; + +static FILE *svg = NULL; + +void svg_open(const char *name, double x, double y, double w, double h) +{ + svg = fopen(name, "w"); + if (!svg) + return; + + /* FIXME calc scaled size */ + fprintf(svg, svg_head, x, y, w, h); +} + +void svg_close() +{ + if (!svg) + return; + fprintf(svg, "</svg>\n"); + fclose(svg); + svg = NULL; +} + +void svg_commentf(const char *format, ...) +{ + if (!svg) + return; + fprintf(svg, "<!-- "); + va_list args; + va_start(args, format); + vfprintf(svg, format, args); + va_end(args); + fprintf(svg, " -->\n"); +} + +void svg_image(const char *name, double width, double height) +{ + if (!svg) + return; + fprintf(svg, "<image width='%g' height='%g' xlink:href='%s'/>\n", width, + height, name); +} + +void svg_group_start(const char *cls, double deg, double sx, double sy, + double x, double y) +{ + if (!svg) + return; + fprintf(svg, "<g class='%s'", cls); + if (sx != 1. || sy != 1 || deg || x || y) { + fprintf(svg, " transform='"); + if (deg) + fprintf(svg, "rotate(%g)", deg); + if (x || y) + fprintf(svg, "translate(%g,%g)", x, y); + if (sx != 1. || sy != 1.) { + if (!sy) + fprintf(svg, "scale(%g)", sx); + else + fprintf(svg, "scale(%g,%g)", sx, sy); + } + fprintf(svg, "'"); + } + fprintf(svg, ">\n"); +} + +void svg_group_end() +{ + fprintf(svg, "</g>\n"); +} + +void svg_path_start(const char *cls, double scale, double x, double y) +{ + if (!svg) + return; + fprintf(svg, "<path class='%s'", cls); + if (scale != 1. || x || y) { + fprintf(svg, " transform='"); + if (x || y) + fprintf(svg, "translate(%g,%g)", x, y); + if (scale != 1.) + fprintf(svg, "scale(%g)", scale); + fprintf(svg, "'"); + } + fprintf(svg, " d='"); +} + +void svg_path_end() +{ + if (!svg) + return; + fprintf(svg, "'/>\n"); +} + +void svg_path_close() +{ + if (!svg) + return; + fprintf(svg, "z"); +} + +void svg_path_moveto(svg_absrel_t abs, double x, double y) +{ + if (!svg) + return; + fprintf(svg, " %c%g,%g", (abs) ? 'M' : 'm', x, y); +} + +void svg_path_lineto(svg_absrel_t abs, double x, double y) +{ + if (!svg) + return; + if (x && y) + fprintf(svg, "%c%g,%g", (abs) ? 'L' : 'l', x, y); + else if (x) + fprintf(svg, "%c%g", (abs) ? 'H' : 'h', x); + else if (y) + fprintf(svg, "%c%g", (abs) ? 'V' : 'v', y); +} diff --git a/zbar/svg.h b/zbar/svg.h new file mode 100644 index 0000000..0908deb --- /dev/null +++ b/zbar/svg.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _SVG_H_ +#define _SVG_H_ + +#ifdef DEBUG_SVG + +typedef enum +{ + SVG_REL, + SVG_ABS +} svg_absrel_t; + +void svg_open(const char *name, double x, double y, double w, double h); +void svg_close(void); + +void svg_commentf(const char *format, ...); +void svg_image(const char *name, double width, double height); + +void svg_group_start(const char *cls, double rotate, double scalex, + double scaley, double x, double y); +void svg_group_end(void); + +void svg_path_start(const char *cls, double scale, double x, double y); +void svg_path_end(void); +void svg_path_close(void); +void svg_path_moveto(svg_absrel_t abs, double x, double y); +void svg_path_lineto(svg_absrel_t abs, double x, double y); + +#else + +#define svg_open(...) +#define svg_close(...) + +#define svg_image(...) + +#define svg_group_start(...) +#define svg_group_end(...) + +#define svg_path_start(...) +#define svg_path_end(...) +#define svg_path_moveto(...) +#define svg_path_lineto(...) +#define svg_path_close(...) + +#endif + +#endif diff --git a/zbar/symbol.c b/zbar/symbol.c new file mode 100644 index 0000000..a518435 --- /dev/null +++ b/zbar/symbol.c @@ -0,0 +1,520 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <zbar.h> +#include "symbol.h" + +const char *zbar_get_symbol_name(zbar_symbol_type_t sym) +{ + switch (sym & ZBAR_SYMBOL) { + case ZBAR_EAN2: + return ("EAN-2"); + case ZBAR_EAN5: + return ("EAN-5"); + case ZBAR_EAN8: + return ("EAN-8"); + case ZBAR_UPCE: + return ("UPC-E"); + case ZBAR_ISBN10: + return ("ISBN-10"); + case ZBAR_UPCA: + return ("UPC-A"); + case ZBAR_EAN13: + return ("EAN-13"); + case ZBAR_ISBN13: + return ("ISBN-13"); + case ZBAR_COMPOSITE: + return ("COMPOSITE"); + case ZBAR_I25: + return ("I2/5"); + case ZBAR_DATABAR: + return ("DataBar"); + case ZBAR_DATABAR_EXP: + return ("DataBar-Exp"); + case ZBAR_CODABAR: + return ("Codabar"); + case ZBAR_CODE39: + return ("CODE-39"); + case ZBAR_CODE93: + return ("CODE-93"); + case ZBAR_CODE128: + return ("CODE-128"); + case ZBAR_PDF417: + return ("PDF417"); + case ZBAR_QRCODE: + return ("QR-Code"); + case ZBAR_SQCODE: + return ("SQ-Code"); + default: + return ("UNKNOWN"); + } +} + +const char *zbar_get_addon_name(zbar_symbol_type_t sym) +{ + return (""); +} + +const char *zbar_get_config_name(zbar_config_t cfg) +{ + switch (cfg) { + case ZBAR_CFG_ENABLE: + return ("ENABLE"); + case ZBAR_CFG_ADD_CHECK: + return ("ADD_CHECK"); + case ZBAR_CFG_EMIT_CHECK: + return ("EMIT_CHECK"); + case ZBAR_CFG_ASCII: + return ("ASCII"); + case ZBAR_CFG_BINARY: + return ("BINARY"); + case ZBAR_CFG_MIN_LEN: + return ("MIN_LEN"); + case ZBAR_CFG_MAX_LEN: + return ("MAX_LEN"); + case ZBAR_CFG_UNCERTAINTY: + return ("UNCERTAINTY"); + case ZBAR_CFG_POSITION: + return ("POSITION"); + case ZBAR_CFG_X_DENSITY: + return ("X_DENSITY"); + case ZBAR_CFG_Y_DENSITY: + return ("Y_DENSITY"); + default: + return (""); + } +} + +const char *zbar_get_modifier_name(zbar_modifier_t mod) +{ + switch (mod) { + case ZBAR_MOD_GS1: + return ("GS1"); + case ZBAR_MOD_AIM: + return ("AIM"); + default: + return (""); + } +} + +const char *zbar_get_orientation_name(zbar_orientation_t orient) +{ + switch (orient) { + case ZBAR_ORIENT_UP: + return ("UP"); + case ZBAR_ORIENT_RIGHT: + return ("RIGHT"); + case ZBAR_ORIENT_DOWN: + return ("DOWN"); + case ZBAR_ORIENT_LEFT: + return ("LEFT"); + default: + return ("UNKNOWN"); + } +} + +#ifndef _MSC_VER +static const signed char _zbar_symbol_hash[ZBAR_CODE128 + 1] = { + [0 ... ZBAR_CODE128] = -1, + + /* [ZBAR_FOO] = 0, is empty */ + [ZBAR_SQCODE] = 1, + [ZBAR_CODE128] = 2, + [ZBAR_EAN13] = 3, + [ZBAR_UPCA] = 4, + [ZBAR_EAN8] = 5, + [ZBAR_UPCE] = 6, + [ZBAR_ISBN13] = 7, + [ZBAR_ISBN10] = 8, + [ZBAR_CODE39] = 9, + [ZBAR_I25] = 10, + [ZBAR_PDF417] = 11, + [ZBAR_QRCODE] = 12, + [ZBAR_DATABAR] = 13, + [ZBAR_DATABAR_EXP] = 14, + [ZBAR_CODE93] = 15, + [ZBAR_EAN2] = 16, + [ZBAR_EAN5] = 17, + [ZBAR_COMPOSITE] = 18, + [ZBAR_CODABAR] = 19, + + /* Please update NUM_SYMS accordingly */ +}; + +static const signed char *_init_hash() +{ + return _zbar_symbol_hash; +}; +#else +/* + * Needed By Microsoft C. Even on Visual Studio 2019, C99 designated + * identifiers aren't supported! So, we need this hack. + */ +static const signed char *_init_hash() +{ + static signed char hash[ZBAR_CODE128 + 1] = { -1 }; + static int was_initialized = 0; + + if (was_initialized) + return (const signed char *)hash; + + memset(hash, -1, sizeof(hash)); + + /* Keep in sync with the C99 implementation */ + hash[ZBAR_SQCODE] = 1, hash[ZBAR_CODE128] = 2, hash[ZBAR_EAN13] = 3, + hash[ZBAR_UPCA] = 4, hash[ZBAR_EAN8] = 5, hash[ZBAR_UPCE] = 6, + hash[ZBAR_ISBN13] = 7, hash[ZBAR_ISBN10] = 8, hash[ZBAR_CODE39] = 9, + hash[ZBAR_I25] = 10, hash[ZBAR_PDF417] = 11, hash[ZBAR_QRCODE] = 12, + hash[ZBAR_DATABAR] = 13, hash[ZBAR_DATABAR_EXP] = 14, + hash[ZBAR_CODE93] = 15, hash[ZBAR_EAN2] = 16, hash[ZBAR_EAN5] = 17, + hash[ZBAR_COMPOSITE] = 18, hash[ZBAR_CODABAR] = 19; + + was_initialized = 1; + + return (const signed char *)hash; +}; +#endif + +int _zbar_get_symbol_hash(zbar_symbol_type_t sym) +{ + int h; + const signed char *hash = _init_hash(); + + assert(sym >= ZBAR_PARTIAL && sym <= ZBAR_CODE128); + + h = hash[sym]; + assert(h >= 0 && h < NUM_SYMS); + + return h; +} + +void _zbar_symbol_free(zbar_symbol_t *sym) +{ + if (sym->syms) { + zbar_symbol_set_ref(sym->syms, -1); + sym->syms = NULL; + } + if (sym->pts) + free(sym->pts); + if (sym->data_alloc && sym->data) + free(sym->data); + free(sym); +} + +void zbar_symbol_ref(const zbar_symbol_t *sym, int refs) +{ + zbar_symbol_t *ncsym = (zbar_symbol_t *)sym; + _zbar_symbol_refcnt(ncsym, refs); +} + +zbar_symbol_type_t zbar_symbol_get_type(const zbar_symbol_t *sym) +{ + return (sym->type); +} + +unsigned int zbar_symbol_get_configs(const zbar_symbol_t *sym) +{ + return (sym->configs); +} + +unsigned int zbar_symbol_get_modifiers(const zbar_symbol_t *sym) +{ + return (sym->modifiers); +} + +const char *zbar_symbol_get_data(const zbar_symbol_t *sym) +{ + return (sym->data); +} + +unsigned int zbar_symbol_get_data_length(const zbar_symbol_t *sym) +{ + return (sym->datalen); +} + +int zbar_symbol_get_count(const zbar_symbol_t *sym) +{ + return (sym->cache_count); +} + +int zbar_symbol_get_quality(const zbar_symbol_t *sym) +{ + return (sym->quality); +} + +unsigned zbar_symbol_get_loc_size(const zbar_symbol_t *sym) +{ + return (sym->npts); +} + +int zbar_symbol_get_loc_x(const zbar_symbol_t *sym, unsigned idx) +{ + if (idx < sym->npts) + return (sym->pts[idx].x); + else + return (-1); +} + +int zbar_symbol_get_loc_y(const zbar_symbol_t *sym, unsigned idx) +{ + if (idx < sym->npts) + return (sym->pts[idx].y); + else + return (-1); +} + +zbar_orientation_t zbar_symbol_get_orientation(const zbar_symbol_t *sym) +{ + return (sym->orient); +} + +const zbar_symbol_t *zbar_symbol_next(const zbar_symbol_t *sym) +{ + return ((sym) ? sym->next : NULL); +} + +const zbar_symbol_set_t *zbar_symbol_get_components(const zbar_symbol_t *sym) +{ + return (sym->syms); +} + +const zbar_symbol_t *zbar_symbol_first_component(const zbar_symbol_t *sym) +{ + return ((sym && sym->syms) ? sym->syms->head : NULL); +} + +unsigned base64_encode(char *dst, const char *src, unsigned srclen) +{ + static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *start = dst; + int nline = 19; + for (; srclen; srclen -= 3) { + unsigned int buf = *(src++) << 16; + if (srclen > 1) + buf |= *(src++) << 8; + if (srclen > 2) + buf |= *(src++); + *(dst++) = alphabet[(buf >> 18) & 0x3f]; + *(dst++) = alphabet[(buf >> 12) & 0x3f]; + *(dst++) = (srclen > 1) ? alphabet[(buf >> 6) & 0x3f] : '='; + *(dst++) = (srclen > 2) ? alphabet[buf & 0x3f] : '='; + if (srclen < 3) + break; + if (!--nline) { + *(dst++) = '\n'; + nline = 19; + } + } + *(dst++) = '\n'; + *(dst++) = '\0'; + return (dst - start - 1); +} + +enum +{ + TMPL_START, + TMPL_MOD_START, + TMPL_MOD_ITEM, + TMPL_MOD_END, + TMPL_COUNT, + TMPL_DATA_START, + TMPL_FORMAT, + TMPL_CDATA, + TMPL_NL, + TMPL_END, +}; + +/* FIXME should be big enough to store XML extra data */ +#define MAX_STATIC (1 << 16) + +#define MAX_MOD (5 * ZBAR_MOD_NUM) +#define MAX_CFG (10 * ZBAR_CFG_NUM) +#define MAX_INT_DIGITS 10 + +#define TMPL_COPY(t) \ + do { \ + static const char *_st = (t); \ + i = strlen(_st); \ + memcpy(*buf + n, _st, i + 1); \ + n += i; \ + assert(n <= maxlen); \ + } while (0) + +#define TMPL_FMT(t, ...) \ + do { \ + static const char *_st = (t); \ + i = snprintf(*buf + n, maxlen - n, _st, __VA_ARGS__); \ + assert(i > 0); \ + n += i; \ + assert(n <= maxlen); \ + } while (0) + +char *zbar_symbol_xml(const zbar_symbol_t *sym, char **buf, unsigned *len) +{ + unsigned int mods, cfgs; + unsigned int datalen, maxlen; + int i, n = 0, p; + + const char *type = zbar_get_symbol_name(sym->type); + const char *orient = zbar_get_orientation_name(sym->orient); + + /* check for binary data */ + unsigned char *data = (unsigned char *)sym->data; + char binary = ((data[0] == 0xff && data[1] == 0xfe) || + (data[0] == 0xfe && data[1] == 0xff) || + !strncmp(sym->data, "<?xml", 5)); + for (i = 0; !binary && i < sym->datalen; i++) { + unsigned char c = sym->data[i]; + binary = ((c < 0x20 && ((~0x00002600 >> c) & 1)) || + (c >= 0x7f && c < 0xa0) || + (c == ']' && i + 2 < sym->datalen && + sym->data[i + 1] == ']' && sym->data[i + 2] == '>')); + } + + datalen = strlen(sym->data); + if (binary) + datalen = (sym->datalen + 2) / 3 * 4 + sym->datalen / 57 + 3; + + maxlen = (MAX_STATIC + strlen(type) + strlen(orient) + datalen + + MAX_INT_DIGITS + 1); + mods = sym->modifiers; + if (mods) + maxlen += MAX_MOD; + cfgs = sym->configs & ~(1 << ZBAR_CFG_ENABLE); + if (cfgs) + maxlen += MAX_CFG; + if (binary) + maxlen += MAX_INT_DIGITS; + + if (!*buf || (*len < maxlen)) { + if (*buf) + free(*buf); + *buf = malloc(maxlen); + /* FIXME check OOM */ + *len = maxlen; + } + + TMPL_FMT("<symbol type='%s' quality='%d' orientation='%s'", type, + sym->quality, orient); + + if (mods) { + int j; + TMPL_COPY(" modifiers='"); + for (j = 0; mods && j < ZBAR_MOD_NUM; j++, mods >>= 1) + if (mods & 1) + TMPL_FMT("%s ", zbar_get_modifier_name(j)); + /* cleanup trailing space */ + n--; + TMPL_COPY("'"); + } + + if (cfgs) { + int j; + TMPL_COPY(" configs='"); + for (j = 0; cfgs && j < ZBAR_CFG_NUM; j++, cfgs >>= 1) + if (cfgs & 1) + TMPL_FMT("%s ", zbar_get_config_name(j)); + /* cleanup trailing space */ + n--; + TMPL_COPY("'"); + } + + if (sym->cache_count) + TMPL_FMT(" count='%d'", sym->cache_count); + + TMPL_COPY("><polygon points='"); + if (sym->npts > 0 ) + TMPL_FMT("%+d,%+d", sym->pts[0].x, sym->pts[0].y); + for(p = 1; p < sym->npts; p++) + TMPL_FMT(" %+d,%+d", sym->pts[p].x, sym->pts[p].y); + + TMPL_COPY("'/><data"); + if (binary) + TMPL_FMT(" format='base64' length='%d'", sym->datalen); + TMPL_COPY("><![CDATA["); + + if (!binary) { + memcpy(*buf + n, sym->data, sym->datalen + 1); + n += sym->datalen; + } else { + TMPL_COPY("\n"); + n += base64_encode(*buf + n, sym->data, sym->datalen); + } + assert(n <= maxlen); + + TMPL_COPY("]]></data></symbol>"); + + *len = n; + return (*buf); +} + +zbar_symbol_set_t *_zbar_symbol_set_create() +{ + zbar_symbol_set_t *syms = calloc(1, sizeof(*syms)); + _zbar_refcnt(&syms->refcnt, 1); + return (syms); +} + +inline void _zbar_symbol_set_free(zbar_symbol_set_t *syms) +{ + zbar_symbol_t *sym, *next; + for (sym = syms->head; sym; sym = next) { + next = sym->next; + sym->next = NULL; + _zbar_symbol_refcnt(sym, -1); + } + syms->head = NULL; + free(syms); +} + +void zbar_symbol_set_ref(const zbar_symbol_set_t *syms, int delta) +{ + zbar_symbol_set_t *ncsyms = (zbar_symbol_set_t *)syms; + if (!_zbar_refcnt(&ncsyms->refcnt, delta) && delta <= 0) + _zbar_symbol_set_free(ncsyms); +} + +int zbar_symbol_set_get_size(const zbar_symbol_set_t *syms) +{ + return (syms->nsyms); +} + +const zbar_symbol_t *zbar_symbol_set_first_symbol(const zbar_symbol_set_t *syms) +{ + zbar_symbol_t *sym = syms->tail; + if (sym) + return (sym->next); + return (syms->head); +} + +const zbar_symbol_t * +zbar_symbol_set_first_unfiltered(const zbar_symbol_set_t *syms) +{ + return (syms->head); +} diff --git a/zbar/symbol.h b/zbar/symbol.h new file mode 100644 index 0000000..5ea97a3 --- /dev/null +++ b/zbar/symbol.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _SYMBOL_H_ +#define _SYMBOL_H_ + +#include <stdlib.h> +#include <zbar.h> +#include "refcnt.h" + +#define NUM_SYMS 20 + +typedef struct point_s { + int x, y; +} point_t; + +struct zbar_symbol_set_s { + refcnt_t refcnt; + int nsyms; /* number of filtered symbols */ + zbar_symbol_t *head; /* first of decoded symbol results */ + zbar_symbol_t *tail; /* last of unfiltered symbol results */ +}; + +struct zbar_symbol_s { + zbar_symbol_type_t type; /* symbol type */ + unsigned int configs; /* symbology boolean config bitmask */ + unsigned int modifiers; /* symbology modifier bitmask */ + unsigned int data_alloc; /* allocation size of data */ + unsigned int datalen; /* length of binary symbol data */ + char *data; /* symbol data */ + + unsigned pts_alloc; /* allocation size of pts */ + unsigned npts; /* number of points in location polygon */ + point_t *pts; /* list of points in location polygon */ + zbar_orientation_t orient; /* coarse orientation */ + + refcnt_t refcnt; /* reference count */ + zbar_symbol_t *next; /* linked list of results (or siblings) */ + zbar_symbol_set_t *syms; /* components of composite result */ + unsigned long time; /* relative symbol capture time */ + int cache_count; /* cache state */ + int quality; /* relative symbol reliability metric */ +}; + +extern int _zbar_get_symbol_hash(zbar_symbol_type_t); + +extern void _zbar_symbol_free(zbar_symbol_t *); + +extern zbar_symbol_set_t *_zbar_symbol_set_create(void); +extern void _zbar_symbol_set_free(zbar_symbol_set_t *); + +static inline void sym_add_point(zbar_symbol_t *sym, int x, int y) +{ + int i = sym->npts; + if (++sym->npts >= sym->pts_alloc) + sym->pts = realloc(sym->pts, ++sym->pts_alloc * sizeof(point_t)); + sym->pts[i].x = x; + sym->pts[i].y = y; +} + +static inline void _zbar_symbol_refcnt(zbar_symbol_t *sym, int delta) +{ + if (!_zbar_refcnt(&sym->refcnt, delta) && delta <= 0) + _zbar_symbol_free(sym); +} + +static inline void _zbar_symbol_set_add(zbar_symbol_set_t *syms, + zbar_symbol_t *sym) +{ + sym->next = syms->head; + syms->head = sym; + syms->nsyms++; + + _zbar_symbol_refcnt(sym, 1); +} + +#endif diff --git a/zbar/thread.h b/zbar/thread.h new file mode 100644 index 0000000..56a9828 --- /dev/null +++ b/zbar/thread.h @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_THREAD_H_ +#define _ZBAR_THREAD_H_ + +/* simple platform thread abstraction + */ + +#include "config.h" +#include "event.h" + +#if defined(_WIN32) + +#include <windows.h> +#define HAVE_THREADS +#define ZTHREAD DWORD WINAPI + +typedef DWORD(WINAPI zbar_thread_proc_t)(void *); + +typedef DWORD zbar_thread_id_t; + +#elif defined(HAVE_LIBPTHREAD) + +#include <pthread.h> +#include <signal.h> +#define HAVE_THREADS +#define ZTHREAD void * + +typedef ZTHREAD(zbar_thread_proc_t)(void *); + +typedef pthread_t zbar_thread_id_t; + +#else + +#undef HAVE_THREADS +#undef ZTHREAD + +typedef void zbar_thread_proc_t; +typedef int zbar_thread_id_t; + +#endif + +typedef struct zbar_thread_s { + zbar_thread_id_t tid; + int started, running; + zbar_event_t notify, activity; +} zbar_thread_t; + +#if defined(_WIN32) + +static inline void _zbar_thread_init(zbar_thread_t *thr) +{ + thr->running = 1; + _zbar_event_trigger(&thr->activity); +} + +static inline zbar_thread_id_t _zbar_thread_self() +{ + return (GetCurrentThreadId()); +} + +static inline int _zbar_thread_is_self(zbar_thread_id_t tid) +{ + return (tid == GetCurrentThreadId()); +} + +#elif defined(HAVE_LIBPTHREAD) + +static inline void _zbar_thread_init(zbar_thread_t *thr) +{ + sigset_t sigs; + sigfillset(&sigs); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + thr->running = 1; + _zbar_event_trigger(&thr->activity); +} + +static inline zbar_thread_id_t _zbar_thread_self(void) +{ + return (pthread_self()); +} + +static inline int _zbar_thread_is_self(zbar_thread_id_t tid) +{ + return (pthread_equal(tid, pthread_self())); +} + +#else + +#define _zbar_thread_start(...) -1 +#define _zbar_thread_stop(...) 0 +#define _zbar_thread_self(...) 0 +#define _zbar_thread_is_self(...) 1 + +#endif + +#ifdef HAVE_THREADS +extern int _zbar_thread_start(zbar_thread_t *, zbar_thread_proc_t *, void *, + zbar_mutex_t *); +extern int _zbar_thread_stop(zbar_thread_t *, zbar_mutex_t *); +#endif + +#endif diff --git a/zbar/timer.h b/zbar/timer.h new file mode 100644 index 0000000..95f3195 --- /dev/null +++ b/zbar/timer.h @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_TIMER_H_ +#define _ZBAR_TIMER_H_ + +#include <time.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> /* gettimeofday */ +#endif + +/* platform timer abstraction + * + * zbar_timer_t stores the absolute expiration of a delay from + * when the timer was initialized. + * + * _zbar_timer_init() initialized timer with specified ms delay. + * returns timer or NULL if timeout < 0 (no/infinite timeout) + * _zbar_timer_check() returns ms remaining until expiration. + * will be <= 0 if timer has expired + */ + +#if _POSIX_TIMERS > 0 + +typedef struct timespec zbar_timer_t; + +static inline int _zbar_timer_now() +{ + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return (now.tv_sec * 1000 + now.tv_nsec / 1000000); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + clock_gettime(CLOCK_REALTIME, timer); + timer->tv_nsec += (delay % 1000) * 1000000; + timer->tv_sec += (delay / 1000) + (timer->tv_nsec / 1000000000); + timer->tv_nsec %= 1000000000; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + struct timespec now; + int delay; + if (!timer) + return (-1); + + clock_gettime(CLOCK_REALTIME, &now); + delay = ((timer->tv_sec - now.tv_sec) * 1000 + + (timer->tv_nsec - now.tv_nsec) / 1000000); + return ((delay >= 0) ? delay : 0); +} + +#elif defined(_WIN32) + +#include <windows.h> + +typedef DWORD zbar_timer_t; + +static inline int _zbar_timer_now() +{ + return (timeGetTime()); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + *timer = timeGetTime() + delay; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + int delay; + if (!timer) + return (INFINITE); + + delay = *timer - timeGetTime(); + return ((delay >= 0) ? delay : 0); +} + +#elif defined(HAVE_SYS_TIME_H) + +typedef struct timeval zbar_timer_t; + +static inline int _zbar_timer_now() +{ + struct timeval now; + gettimeofday(&now, NULL); + return (now.tv_sec * 1000 + now.tv_usec / 1000); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + gettimeofday(timer, NULL); + timer->tv_usec += (delay % 1000) * 1000; + timer->tv_sec += (delay / 1000) + (timer->tv_usec / 1000000); + timer->tv_usec %= 1000000; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + struct timeval now; + if (!timer) + return (-1); + + gettimeofday(&now, NULL); + return ((timer->tv_sec - now.tv_sec) * 1000 + + (timer->tv_usec - now.tv_usec) / 1000); +} + +#else +#error "unable to find a timer interface" +#endif + +#endif diff --git a/zbar/video.c b/zbar/video.c new file mode 100644 index 0000000..664b037 --- /dev/null +++ b/zbar/video.c @@ -0,0 +1,452 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "video.h" +#include "image.h" + +#ifdef HAVE_LIBJPEG +extern struct jpeg_decompress_struct *_zbar_jpeg_decomp_create(void); +extern void _zbar_jpeg_decomp_destroy(struct jpeg_decompress_struct *cinfo); +#endif + +static void _zbar_video_recycle_image(zbar_image_t *img) +{ + zbar_video_t *vdo = img->src; + assert(vdo); + assert(img->srcidx >= 0); + video_lock(vdo); + if (vdo->images[img->srcidx] != img) + vdo->images[img->srcidx] = img; + if (vdo->active) + vdo->nq(vdo, img); + else + video_unlock(vdo); +} + +static void _zbar_video_recycle_shadow(zbar_image_t *img) +{ + zbar_video_t *vdo = img->src; + assert(vdo); + assert(img->srcidx == -1); + video_lock(vdo); + img->next = vdo->shadow_image; + vdo->shadow_image = img; + video_unlock(vdo); +} + +zbar_video_t *zbar_video_create() +{ + zbar_video_t *vdo = calloc(1, sizeof(zbar_video_t)); + int i; + if (!vdo) + return (NULL); + err_init(&vdo->err, ZBAR_MOD_VIDEO); + vdo->fd = -1; + + (void)_zbar_mutex_init(&vdo->qlock); + + /* pre-allocate images */ + vdo->num_images = ZBAR_VIDEO_IMAGES_MAX; + vdo->images = calloc(ZBAR_VIDEO_IMAGES_MAX, sizeof(zbar_image_t *)); + if (!vdo->images) { + zbar_video_destroy(vdo); + return (NULL); + } + + for (i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) { + zbar_image_t *img = vdo->images[i] = zbar_image_create(); + if (!img) { + zbar_video_destroy(vdo); + return (NULL); + } + img->refcnt = 0; + img->cleanup = _zbar_video_recycle_image; + img->srcidx = i; + img->src = vdo; + } + + return (vdo); +} + +void zbar_video_destroy(zbar_video_t *vdo) +{ + if (vdo->intf != VIDEO_INVALID) + zbar_video_open(vdo, NULL); + if (vdo->images) { + int i; + for (i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) + if (vdo->images[i]) + _zbar_image_free(vdo->images[i]); + free(vdo->images); + } + while (vdo->shadow_image) { + zbar_image_t *img = vdo->shadow_image; + vdo->shadow_image = img->next; + free((void *)img->data); + img->data = NULL; + free(img); + } + if (vdo->buf) + free(vdo->buf); + if (vdo->formats) + free(vdo->formats); + if (vdo->emu_formats) + free(vdo->emu_formats); + + if (vdo->free) + vdo->free(vdo); + + err_cleanup(&vdo->err); + _zbar_mutex_destroy(&vdo->qlock); + +#ifdef HAVE_LIBJPEG + if (vdo->jpeg_img) { + zbar_image_destroy(vdo->jpeg_img); + vdo->jpeg_img = NULL; + } + if (vdo->jpeg) { + _zbar_jpeg_decomp_destroy(vdo->jpeg); + vdo->jpeg = NULL; + } +#endif + free(vdo); +} + +int zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + char *ldev = NULL; + int rc; + zbar_video_enable(vdo, 0); + video_lock(vdo); + if (vdo->intf != VIDEO_INVALID) { + if (vdo->cleanup) { + vdo->cleanup(vdo); + vdo->cleanup = NULL; + } + zprintf(1, "closed camera (fd=%d)\n", vdo->fd); + vdo->intf = VIDEO_INVALID; + } + video_unlock(vdo); + + if (!dev) + return (0); + + if ((unsigned char)dev[0] < 0x10) { + /* default linux device, overloaded for other platforms */ + int id = dev[0]; + dev = ldev = strdup("/dev/video0"); + ldev[10] = '0' + id; + } + + rc = _zbar_video_open(vdo, dev); + + if (ldev) + free(ldev); + return (rc); +} + +int zbar_video_get_fd(const zbar_video_t *vdo) +{ + if (vdo->intf == VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not opened")); + if (vdo->intf != VIDEO_V4L2) + return (err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "video driver does not support polling")); + return (vdo->fd); +} + +int zbar_video_request_size(zbar_video_t *vdo, unsigned width, unsigned height) +{ + if (vdo->initialized) + /* FIXME re-init different format? */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "already initialized, unable to resize")); + + vdo->width = width; + vdo->height = height; + zprintf(1, "request size: %d x %d\n", width, height); + return (0); +} + +int zbar_video_request_interface(zbar_video_t *vdo, int ver) +{ + if (vdo->intf != VIDEO_INVALID) + return ( + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "device already opened, unable to change interface")); + vdo->intf = (video_interface_t)ver; + zprintf(1, "request interface version %d\n", vdo->intf); + return (0); +} + +int zbar_video_request_iomode(zbar_video_t *vdo, int iomode) +{ + if (vdo->intf != VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "device already opened, unable to change iomode")); + if (iomode < 0 || iomode > VIDEO_USERPTR) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "invalid iomode requested")); + vdo->iomode = iomode; + return (0); +} + +int zbar_video_get_width(const zbar_video_t *vdo) +{ + return (vdo->width); +} + +int zbar_video_get_height(const zbar_video_t *vdo) +{ + return (vdo->height); +} + +uint32_t zbar_video_get_format(const zbar_video_t *vdo) +{ + return (vdo->format); +} + +static inline int video_init_images(zbar_video_t *vdo) +{ + int i; + assert(vdo->datalen); + if (vdo->iomode != VIDEO_MMAP) { + assert(!vdo->buf); + vdo->buflen = vdo->num_images * vdo->datalen; + vdo->buf = calloc(1, vdo->buflen); + if (!vdo->buf) + return (err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "unable to allocate image buffers")); + zprintf(1, "pre-allocated %d %s buffers size=0x%lx\n", vdo->num_images, + (vdo->iomode == VIDEO_READWRITE) ? "READ" : "USERPTR", + vdo->buflen); + } + for (i = 0; i < vdo->num_images; i++) { + zbar_image_t *img = vdo->images[i]; + img->format = vdo->format; + zbar_image_set_size(img, vdo->width, vdo->height); + if (vdo->iomode != VIDEO_MMAP) { + unsigned long offset = i * vdo->datalen; + img->datalen = vdo->datalen; + img->data = (uint8_t *)vdo->buf + offset; + zprintf(2, " [%02d] @%08lx\n", i, offset); + } + } + return (0); +} + +int zbar_video_init(zbar_video_t *vdo, unsigned long fmt) +{ +#ifdef HAVE_LIBJPEG + const zbar_format_def_t *vidfmt; +#endif + if (vdo->initialized) + /* FIXME re-init different format? */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "already initialized, re-init unimplemented")); + + if (vdo->init(vdo, fmt)) + return (-1); + vdo->format = fmt; + if (video_init_images(vdo)) + return (-1); +#ifdef HAVE_LIBJPEG + vidfmt = _zbar_format_lookup(fmt); + if (vidfmt && vidfmt->group == ZBAR_FMT_JPEG) { + zbar_image_t *img; + /* prepare for decoding */ + if (!vdo->jpeg) + vdo->jpeg = _zbar_jpeg_decomp_create(); + if (vdo->jpeg_img) + zbar_image_destroy(vdo->jpeg_img); + + /* create intermediate image for decoder to use*/ + img = vdo->jpeg_img = zbar_image_create(); + img->format = fourcc('Y', '8', '0', '0'); + zbar_image_set_size(img, vdo->width, vdo->height); + img->datalen = vdo->width * vdo->height; + } +#endif + vdo->initialized = 1; + return (0); +} + +int zbar_video_enable(zbar_video_t *vdo, int enable) +{ + if (vdo->active == enable) + return (0); + + if (enable) { + if (vdo->intf == VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not opened")); + + if (!vdo->initialized && zbar_negotiate_format(vdo, NULL)) + return (-1); + } + + if (video_lock(vdo)) + return (-1); + vdo->active = enable; + if (enable) { + /* enqueue all buffers */ + int i; + for (i = 0; i < vdo->num_images; i++) + if (vdo->nq(vdo, vdo->images[i]) || + ((i + 1 < vdo->num_images) && video_lock(vdo))) + return (-1); + + return (vdo->start(vdo)); + } else { + int i; + for (i = 0; i < vdo->num_images; i++) + vdo->images[i]->next = NULL; + vdo->nq_image = vdo->dq_image = NULL; + if (video_unlock(vdo)) + return (-1); + + return (vdo->stop(vdo)); + } +} + +zbar_image_t *zbar_video_next_image(zbar_video_t *vdo) +{ + unsigned frame; + zbar_image_t *img; + + if (video_lock(vdo)) + return (NULL); + if (!vdo->active) { + video_unlock(vdo); + return (NULL); + } + + frame = vdo->frame++; + img = vdo->dq(vdo); + if (img) { + img->seq = frame; + if (vdo->num_images < 2) { + /* return a *copy* of the video image and immediately recycle + * the driver's buffer to avoid deadlocking the resources + */ + zbar_image_t *tmp = img; + video_lock(vdo); + img = vdo->shadow_image; + vdo->shadow_image = (img) ? img->next : NULL; + video_unlock(vdo); + + if (!img) { + img = zbar_image_create(); + assert(img); + img->refcnt = 0; + img->src = vdo; + /* recycle the shadow images */ + + img->format = vdo->format; + zbar_image_set_size(img, vdo->width, vdo->height); + img->datalen = vdo->datalen; + img->data = malloc(vdo->datalen); + } + img->cleanup = _zbar_video_recycle_shadow; + img->seq = frame; + memcpy((void *)img->data, tmp->data, img->datalen); + _zbar_video_recycle_image(tmp); + } else + img->cleanup = _zbar_video_recycle_image; + _zbar_image_refcnt(img, 1); + } + return (img); +} + +/** @brief return if fun unsupported, otherwise continue */ +#define return_if_not_supported(fun, name) \ + { \ + if (!(fun)) { \ + zprintf(1, "video driver does not implement %s\n", name); \ + return ZBAR_ERR_UNSUPPORTED; \ + } \ + } +#define return_if_non_zero(a) \ + { \ + int rv = a; \ + if (rv != 0) \ + return (rv); \ + } + +int zbar_video_set_control(zbar_video_t *vdo, const char *control_name, + int value) +{ + int loc_value, rv; + return_if_not_supported(vdo->set_control, "set_control"); + loc_value = value; + + rv = vdo->set_control(vdo, control_name, &loc_value); + + if (rv == 0) + zprintf(1, "value of %s set to: %d\n", control_name, loc_value); + return (rv); +} + +int zbar_video_get_control(zbar_video_t *vdo, const char *control_name, + int *value) +{ + return_if_not_supported(vdo->get_control, "get_control"); + return (vdo->get_control(vdo, control_name, value)); +} + +struct video_controls_s *zbar_video_get_controls(const zbar_video_t *vdo, + int index) +{ + int i = 0; + struct video_controls_s *p = vdo->controls; + + while (p && i != index) { + i++; + p = p->next; + } + + if (!p) + return NULL; + + return p; +} + +struct video_resolution_s *zbar_video_get_resolutions(const zbar_video_t *vdo, + int index) +{ + int i = 0; + struct video_resolution_s *p = vdo->res; + + while (i != index) { + if (!p->width || !p->height) + return NULL; + i++; + p++; + } + + if (!p->width || !p->height) + return NULL; + + return p; +} diff --git a/zbar/video.h b/zbar/video.h new file mode 100644 index 0000000..fdb7394 --- /dev/null +++ b/zbar/video.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#include "config.h" + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <zbar.h> + +#include "error.h" +#include "image.h" +#include "mutex.h" + +/* number of images to preallocate */ +#define ZBAR_VIDEO_IMAGES_MAX 4 + +typedef enum video_interface_e +{ + VIDEO_INVALID = 0, /* uninitialized */ + VIDEO_V4L1, /* v4l protocol version 1 */ + VIDEO_V4L2, /* v4l protocol version 2 */ + VIDEO_VFW, /* video for windows */ + VIDEO_DSHOW /* direct show */ +} video_interface_t; + +typedef enum video_iomode_e +{ + VIDEO_READWRITE = 1, /* standard system calls */ + VIDEO_MMAP, /* mmap interface */ + VIDEO_USERPTR, /* userspace buffers */ +} video_iomode_t; + +typedef struct video_state_s video_state_t; + +struct zbar_video_s { + errinfo_t err; /* error reporting */ + int fd; /* open camera device */ + unsigned width, height; /* video frame size */ + + video_interface_t intf; /* input interface type */ + video_iomode_t iomode; /* video data transfer mode */ + unsigned initialized : 1; /* format selected and images mapped */ + unsigned active : 1; /* current streaming state */ + + uint32_t format; /* selected fourcc */ + unsigned palette; /* v4l1 format index corresponding to format */ + uint32_t *formats; /* 0 terminated list of supported formats */ + uint32_t *emu_formats; /* 0 terminated list of emulated formats */ + + struct video_resolution_s *res; /* 0 terminated list of resolutions */ + + struct video_controls_s *controls; /* linked list of controls */ + + unsigned long datalen; /* size of image data for selected format */ + unsigned long buflen; /* total size of image data buffer */ + void *buf; /* image data buffer */ + + unsigned frame; /* frame count */ + + zbar_mutex_t qlock; /* lock image queue */ + int num_images; /* number of allocated images */ + zbar_image_t **images; /* indexed list of images */ + zbar_image_t *nq_image; /* last image enqueued */ + zbar_image_t *dq_image; /* first image to dequeue (when ordered) */ + zbar_image_t *shadow_image; /* special case internal double buffering */ + + video_state_t *state; /* platform/interface specific state */ + +#ifdef HAVE_LIBJPEG + struct jpeg_decompress_struct *jpeg; /* JPEG decompressor */ + zbar_image_t *jpeg_img; /* temporary image */ +#endif + + /* interface dependent methods */ + int (*init)(zbar_video_t *, uint32_t); + int (*cleanup)(zbar_video_t *); + int (*start)(zbar_video_t *); + int (*stop)(zbar_video_t *); + int (*nq)(zbar_video_t *, zbar_image_t *); + /** set value of video control + * implemented by v4l2_set_control() + * @param name name of a control, acceptable names are listed + * in processor.h + * @param value pointer to value of a type specified by flags + */ + int (*set_control)(zbar_video_t *, const char *name, void *value); + /** get value of video control + * implemented by v4l2_get_control() + * @param name name of a control, acceptable names are listed + * in processor.h + * @param value pointer to a receiver of a value of a type + * specified by flags + */ + int (*get_control)(zbar_video_t *, const char *name, void *value); + + void (*free)(zbar_video_t *); + + zbar_image_t *(*dq)(zbar_video_t *); +}; + +/* video.next_image and video.recycle_image have to be thread safe + * wrt/other apis + */ +static inline int video_lock(zbar_video_t *vdo) +{ + int rc = 0; + if ((rc = _zbar_mutex_lock(&vdo->qlock))) { + err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to acquire lock"); + vdo->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int video_unlock(zbar_video_t *vdo) +{ + int rc = 0; + if ((rc = _zbar_mutex_unlock(&vdo->qlock))) { + err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to release lock"); + vdo->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int video_nq_image(zbar_video_t *vdo, zbar_image_t *img) +{ + /* maintains queued buffers in order */ + img->next = NULL; + if (vdo->nq_image) + vdo->nq_image->next = img; + vdo->nq_image = img; + if (!vdo->dq_image) + vdo->dq_image = img; + return (video_unlock(vdo)); +} + +static inline zbar_image_t *video_dq_image(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->dq_image; + if (img) { + vdo->dq_image = img->next; + img->next = NULL; + } + if (video_unlock(vdo)) + /* FIXME reclaim image */ + return (NULL); + return (img); +} + +/* PAL interface */ +extern int _zbar_video_open(zbar_video_t *, const char *); + +#endif diff --git a/zbar/video/dshow.c b/zbar/video/dshow.c new file mode 100644 index 0000000..d304dfe --- /dev/null +++ b/zbar/video/dshow.c @@ -0,0 +1,1334 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Klaus Triendl <klaus@triendl.eu> + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbarw + *------------------------------------------------------------------------*/ + +#include "config.h" + +#include <assert.h> +#include <objbase.h> +#include <strmif.h> +#include <control.h> + +#include <qedit.h> + +#include <amvideo.h> // include after ddraw.h has been included from any dshow header + +#include <initguid.h> + +#include "misc.h" +#include "thread.h" +#include "video.h" + +#define ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, \ + b8) \ + static const GUID name; \ + static const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; + +// define a special guid that can be used for fourcc formats +// 00000000-0000-0010-8000-00AA00389B71 == MEDIASUBTYPE_FOURCC_PLACEHOLDER +ZBAR_DEFINE_STATIC_GUID(MEDIASUBTYPE_FOURCC_PLACEHOLDER, 0x00000000, 0x0000, + 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + +#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8); + +#include <uuids.h> + +DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46); +DEFINE_GUID(IID_ISampleGrabber, 0x6b652fff, 0x11fe, 0x4fce, 0x92, 0xad, 0x02, + 0x66, 0xb5, 0xd7, 0xc7, 0x8f); +DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154a, 0x2b53, 0x4994, 0xb0, 0xd0, 0xe7, + 0x73, 0x14, 0x8e, 0xff, 0x85); +DEFINE_GUID(IID_IBaseFilter, 0x56a86895, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b, 0x00, + 0xa0, 0xc9, 0x11, 0xce, 0x86); +DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00, + 0xaa, 0x00, 0x4b, 0xb8, 0x51); +DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00, + 0xa0, 0xc9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab, 0xfa, + 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d); +DEFINE_GUID(CLSID_NullRenderer, 0xc1f400a4, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, + 0x60, 0x08, 0x03, 0x9e, 0x37); +DEFINE_GUID(CLSID_SampleGrabber, 0xc1f400a0, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, + 0x60, 0x08, 0x03, 0x9e, 0x37); + +#define BIH_FMT \ + "%ldx%ld @%dbpp (%lx) cmp=%.4s(%08lx) res=%ldx%ld clr=%ld/%ld (%lx)" +#define BIH_FIELDS(bih) \ + (bih)->biWidth, (bih)->biHeight, (bih)->biBitCount, (bih)->biSizeImage, \ + (char *)&(bih)->biCompression, (bih)->biCompression, \ + (bih)->biXPelsPerMeter, (bih)->biYPelsPerMeter, (bih)->biClrImportant, \ + (bih)->biClrUsed, (bih)->biSize + +// taken from Capturing an Image winapi sample +#define BMP_SIZE(cx, cy, bitsPerPix) \ + ((((cx) * (bitsPerPix) + 31) / 32) * 4 * (cy)) + +#define COM_SAFE_RELEASE(ppIface) \ + if (*ppIface) \ + (*ppIface)->lpVtbl->Release(*ppIface) + +#define CHECK_COM_ERROR(hr, msg, stmt) \ + if (FAILED(hr)) { \ + LPSTR sysmsg = NULL; \ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM | \ + FORMAT_MESSAGE_IGNORE_INSERTS, \ + NULL, hr, \ + 0 /* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) */, \ + (LPSTR)&sysmsg, 0, NULL); \ + zprintf(6, "%s, hresult: 0x%lx %s", msg, hr, sysmsg); \ + LocalFree(sysmsg); \ + stmt; \ + } + +static const struct uuid_desc_s { + const GUID *guid; + const char *name; +} known_uuids[] = { +#define OUR_GUID_ENTRY(m_name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + { &m_name, #m_name }, +#include <ksuuids.h> + { NULL, NULL } +}; + +static const REFERENCE_TIME _100ns_unit = 1 * 1000 * 1000 * 1000 / 100; + +// format to which we convert MJPG streams +static const REFGUID mjpg_conversion_mediatype = &MEDIASUBTYPE_RGB32; +static const int mjpg_conversion_fmt = fourcc('B', 'G', 'R', '4'); +static const int mjpg_conversion_fmt_bpp = 32; + +static long grabbed_count = 0; + +// Destroy (Release) the format block for a media type. +static void DestroyMediaType(AM_MEDIA_TYPE *mt) +{ + if (mt->cbFormat != 0) + CoTaskMemFree(mt->pbFormat); + + if (mt->pUnk) + IUnknown_Release(mt->pUnk); +} + +// Destroy the format block for a media type and free the media type +static void DeleteMediaType(AM_MEDIA_TYPE *mt) +{ + if (!mt) + return; + DestroyMediaType(mt); + CoTaskMemFree(mt); +} + +static void make_fourcc_subtype(GUID *subtype, uint32_t fmt) +{ + *subtype = MEDIASUBTYPE_FOURCC_PLACEHOLDER; + subtype->Data1 = fmt; +} + +static int dshow_is_fourcc_guid(REFGUID subtype) +{ + // make up a fourcc guid in spe + GUID clsid; + make_fourcc_subtype(&clsid, subtype->Data1); + + return IsEqualGUID(subtype, &clsid); +} + +/// Checks whether the given AM_MEDIA_TYPE contains a +/// VIDEOINFOHEADER block. +static inline int dshow_has_vih(AM_MEDIA_TYPE *mt) +{ + // documentation for AM_MEDIA_TYPE emphasizes to do a thorough check + int isvih = (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) && + mt->cbFormat >= sizeof(VIDEOINFOHEADER) && mt->pbFormat); + return isvih; +} + +/// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE, +/// access is non-const +static inline BITMAPINFOHEADER *dshow_access_bih(AM_MEDIA_TYPE *mt) +{ + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat; + return &vih->bmiHeader; +} + +/// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE, +/// access is const +static inline const BITMAPINFOHEADER *dshow_caccess_bih(const AM_MEDIA_TYPE *mt) +{ + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat; + return &vih->bmiHeader; +} + +/// Flips the image vertically copying it from srcBuf to img. +/** @param bpp Bits Per Pixel */ +static void flip_vert(zbar_image_t *const img, void *const srcBuf, int bpp) +{ + BYTE *dst, *src; + int i, n; + + // The formula below works only if bpp%8==0 + long bytesPerLine = 1L * img->width * bpp / 8; + assert(img->datalen >= img->height * bytesPerLine); + dst = (BYTE *)img->data; + src = ((BYTE *)srcBuf) + (img->height - 1) * bytesPerLine; + for (i = 0, n = img->height; i < n; i++) { + memcpy(dst, src, bytesPerLine); + dst += bytesPerLine; + src -= bytesPerLine; + } + assert(src + bytesPerLine == srcBuf); +} + +/// Internal format information +struct int_format_s { + uint32_t fourcc; + /// index for IAMStreamConfig::GetStreamCaps + /// @note compression in bih structure may differ from one used by zbar + int idx_caps; + resolution_list_t resolutions; +}; +typedef struct int_format_s int_format_t; + +struct video_state_s { + zbar_thread_t thread; /* capture message pump */ + HANDLE captured; + HANDLE notify; /* capture thread status change */ + int bi_size; /* size of bih */ + BITMAPINFOHEADER *bih; /* video format details of grabbed samples; + format might be among fourcc or BI_RGB */ + int do_flip_bitmap; /* whether uncompressed bitmap images are + bottom-up and need to be vertically + flipped */ + zbar_image_t *image; /* current capturing frame */ + + IGraphBuilder *graph; /* dshow graph manager */ + IMediaControl *mediacontrol; /* dshow graph control */ + IBaseFilter *camera; /* dshow source filter */ + ISampleGrabber *samplegrabber; /* dshow intermediate filter */ + IBaseFilter *grabberbase; /* samplegrabber's IBaseFilter interface */ + IBaseFilter *nullrenderer; + ICaptureGraphBuilder2 *builder; + IAMStreamConfig *camstreamconfig; /* dshow stream configuration interface */ + int caps_size; /* length of stream config caps */ + /// 0 terminated list of supported internal formats. + /** The size of this + * array matches the size of {@link zbar_video_s#formats} array and + * the consecutive entries correspond to each other, making a mapping + * between internal (camera) formats and zbar formats + * (presented to zbar processor). */ + int_format_t *int_formats; + resolution_t def_resolution; /* initial resolution read + from the camera */ +}; + +static void dshow_destroy_video_state_t(video_state_t *state) +{ + COM_SAFE_RELEASE(&state->camstreamconfig); + COM_SAFE_RELEASE(&state->builder); + COM_SAFE_RELEASE(&state->nullrenderer); + COM_SAFE_RELEASE(&state->grabberbase); + COM_SAFE_RELEASE(&state->samplegrabber); + COM_SAFE_RELEASE(&state->camera); + COM_SAFE_RELEASE(&state->mediacontrol); + COM_SAFE_RELEASE(&state->graph); + + if (state->captured) + CloseHandle(state->captured); + + free(state->bih); + + if (state->int_formats) { + int_format_t *fmt; + for (fmt = state->int_formats; !is_struct_null(fmt); fmt++) { + resolution_list_cleanup(&fmt->resolutions); + } + } + free(state->int_formats); +} + +/// Returns the index of the given format in formats array. +/** If not found, returns -1. The array is constructed like + * <code>vdo->formats</code>, with the terminating null. */ +static int get_format_index(uint32_t *fmts, uint32_t fmt0) +{ + uint32_t *fmt; + int i = 0; + for (fmt = fmts; *fmt; fmt++) { + if (*fmt == fmt0) + break; + i++; + } + if (*fmt) + return i; + else + return -1; +} + +/// Returns the index of the given format in internal formats array. +/** If not found, returns -1. The array is constructed like + * <code>vdo->state->int_formats</code>, with the terminating zeroed + * element. */ +/** @param fmt0 A fourcc format code */ +static int get_int_format_index(int_format_t *fmts, uint32_t fmt0) +{ + int_format_t *fmt; + int i = 0; + for (fmt = fmts; !is_struct_null(fmt); fmt++) { + if (fmt->fourcc == fmt0) + break; + i++; + } + if (!is_struct_null(fmt)) + return i; + else + return -1; +} + +/// Gets the fourcc code for the given media type. +/** This is necessary because of non-standard coding of video + * formats by Windows, for example BGR3/4 = BI_RGB . */ +static uint32_t get_fourcc_for_mt(const AM_MEDIA_TYPE *mt) +{ + if IsEqualGUID (&mt->subtype, &MEDIASUBTYPE_RGB24) + return fourcc('B', 'G', 'R', '3'); + else if IsEqualGUID (&mt->subtype, &MEDIASUBTYPE_RGB32) + return fourcc('B', 'G', 'R', '4'); + else if (dshow_is_fourcc_guid(&mt->subtype)) + return mt->subtype.Data1; + else + return 0; +} + +/// Dumps the mapping of internal and external formats, if the debug level +/// is sufficient. +static void dump_formats(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + uint32_t *fmt; + int_format_t *int_fmt; + zprintf(8, "Detected formats: (internal) / (translated for zbar)\n"); + fmt = vdo->formats; + int_fmt = state->int_formats; + while (*fmt) { + zprintf(8, " %.4s / %.4s, resolutions: %lu\n", + (char *)&int_fmt->fourcc, (char *)fmt, + int_fmt->resolutions.cnt); + fmt++; + int_fmt++; + } +} + +/// Allocates a string containing given guid. +/** If it's among known CLSID's, its name is returned. + * Returned string should be freed with `free`. */ +static char *get_clsid_string(REFGUID guid) +{ + char *msg = NULL; + + // first try to determine guid name from the list + int i; + for (i = 0; known_uuids[i].name; i++) { + if (IsEqualGUID(known_uuids[i].guid, guid)) { + msg = malloc(strlen(known_uuids[i].name) + 1); + strcpy(msg, known_uuids[i].name); + break; + } + } + + // if that failed, give the ugly string + if (msg == NULL) { + LPOLESTR str = L"ERROR"; + HRESULT hr = StringFromCLSID(guid, &str); + int c = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + msg = malloc(c); + WideCharToMultiByte(CP_UTF8, 0, str, -1, msg, c, NULL, NULL); + if (!FAILED(hr)) + CoTaskMemFree(str); + } + + return msg; +} + +/// Maps internal format MJPG (if found) to a format known to +/// zbar. +/** The conversion will be done by dshow mjpeg decompressor + * before passing the image to zbar. */ +static void prepare_mjpg_format_mapping(zbar_video_t *vdo) +{ + int iMjpgConv; + video_state_t *state = vdo->state; + /// The format we will convert MJPG to + uint32_t fmtConv = mjpg_conversion_fmt; + int iMjpg = + get_int_format_index(state->int_formats, fourcc('M', 'J', 'P', 'G')); + if (iMjpg < 0) + return; + assert(vdo->formats[iMjpg] == fourcc('M', 'J', 'P', 'G')); + + // If we already have fmtConv, it will lead to duplicating it in + // external formats. + // We can't leave it this way, because when zbar wants to use fmtConv + // we must have only one internal format for that. + // It's better to drop MJPG then, as it's a compressed format and + // we prefer better quality images. + + // The index of fmtConv before mapping mjpg to fmtConv + iMjpgConv = get_int_format_index(state->int_formats, fmtConv); + + if (iMjpgConv >= 0) { + // remove the iMjpgConv entry by moving the following entries by 1 + int i; + for (i = iMjpgConv; vdo->formats[i]; i++) { + vdo->formats[i] = vdo->formats[i + 1]; + state->int_formats[i] = state->int_formats[i + 1]; + } + // The number of formats is reduced by 1 now, but to realloc just + // to save 2 times 4 bytes? Too much fuss. + } else { + vdo->formats[iMjpg] = fmtConv; + } +} + +/// sample grabber callback implementation (derived from ISampleGrabberCB) +typedef struct zbar_samplegrabber_cb { + // baseclass + const struct ISampleGrabberCB _; + // COM refcount + ULONG refcount; + + zbar_video_t *vdo; + +} zbar_samplegrabber_cb; + +// ISampleGrabber methods (implementation below) + +HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB *_This, + REFIID riid, + void **ppvObject); +ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB *_This); +ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB *_This); +HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB *_This, + double sampletime, + IMediaSample *sample); +// note: original MS version expects a long for the buffer length, wine version a LONG +//HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen); +HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB *_This, + double sampletime, + BYTE *buffer, LONG bufferlen); + +static struct ISampleGrabberCBVtbl SampleGrabberCBVtbl = { + &zbar_samplegrabber_cb_QueryInterface, &zbar_samplegrabber_cb_AddRef, + &zbar_samplegrabber_cb_Release, &zbar_samplegrabber_cb_SampleCB, + &zbar_samplegrabber_cb_BufferCB +}; + +static zbar_samplegrabber_cb *new_zbar_samplegrabber_cb(zbar_video_t *vdo) +{ + // allocate memory + zbar_samplegrabber_cb *o = calloc(1, sizeof(zbar_samplegrabber_cb)); + + // construct parent + ISampleGrabberCB *base = (ISampleGrabberCB *)o; + base->lpVtbl = &SampleGrabberCBVtbl; + + // construct object + o->refcount = 1; + o->vdo = vdo; + + return o; +} + +static void delete_zbar_samplegrabber_cb(zbar_samplegrabber_cb *o) +{ + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + // no destruction necessary + + free(o); +} + +HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB *_This, + REFIID riid, + void **ppvObject) +{ + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISampleGrabberCB)) { + *ppvObject = _This; + return NOERROR; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB *_This) +{ + zbar_samplegrabber_cb *This = (zbar_samplegrabber_cb *)_This; + + return ++This->refcount; +} + +ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB *_This) +{ + zbar_samplegrabber_cb *This = (zbar_samplegrabber_cb *)_This; + + ULONG refcnt = --This->refcount; + if (!refcnt) + delete_zbar_samplegrabber_cb(This); + + return refcnt; +} + +HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB *_This, + double sampletime, + IMediaSample *sample) +{ + return E_NOTIMPL; +} + +//HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen) +HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB *_This, + double sampletime, + BYTE *buffer, LONG bufferlen) +{ + zbar_samplegrabber_cb *This; + zbar_video_t *vdo; + zbar_image_t *img; + if (!buffer || !bufferlen) + return S_OK; + + grabbed_count++; + This = (zbar_samplegrabber_cb *)_This; + vdo = This->vdo; + + _zbar_mutex_lock(&vdo->qlock); + + zprintf(16, "got sample no %ld: %p (%ld), thr=%04lx\n", grabbed_count, + buffer, bufferlen, _zbar_thread_self()); + + img = vdo->state->image; + if (!img) { + _zbar_mutex_lock(&vdo->qlock); + img = video_dq_image(vdo); + // note: video_dq_image() has unlocked the mutex + } + if (img) { + zprintf(16, "copying into img: %p (srcidx: %d, data: %p, len: %ld)\n", + img, img->srcidx, img->data, img->datalen); + + assert(img->datalen == bufferlen); + + // The image needs to be copied now. Usually memcpy is ok, + // but in case of MJPG the picture is upside-down. + if (vdo->state->do_flip_bitmap) + flip_vert(img, buffer, vdo->state->bih->biBitCount); + else + memcpy((void *)img->data, buffer, img->datalen); + + vdo->state->image = img; + SetEvent(vdo->state->captured); + } + + _zbar_mutex_unlock(&vdo->qlock); + + return S_OK; +} + +static ZTHREAD dshow_capture_thread(void *arg) +{ + MSG msg; + int rc = 0; + + zbar_video_t *vdo = arg; + video_state_t *state = vdo->state; + zbar_thread_t *thr = &state->thread; + + _zbar_mutex_lock(&vdo->qlock); + + _zbar_thread_init(thr); + zprintf(4, "spawned dshow capture thread (thr=%04lx)\n", + _zbar_thread_self()); + + while (thr->started && rc >= 0 && rc <= 1) { + _zbar_mutex_unlock(&vdo->qlock); + + rc = MsgWaitForMultipleObjects(1, &thr->notify, 0, INFINITE, + QS_ALLINPUT); + if (rc == 1) + while (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE)) + if (rc > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + _zbar_mutex_lock(&vdo->qlock); + } + + //done: + thr->running = 0; + _zbar_event_trigger(&thr->activity); + _zbar_mutex_unlock(&vdo->qlock); + return 0; +} + +static int dshow_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, + img->srcidx, img->data, img->datalen, _zbar_thread_self()); + return video_nq_image(vdo, img); +} + +/// Platform dependent part of #zbar_video_next_image, which blocks +/// until an image is available. +/** Must be called with video lock held and returns + * with the lock released. + * <p>Waits for the image from `vdo->state->image`. If available, + * this field is nulled. Releases the lock temporarily when waiting for + * the `vdo->state->captured` signal. */ +static zbar_image_t *dshow_dq(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->state->image; + if (!img) { + DWORD rc; + _zbar_mutex_unlock(&vdo->qlock); + rc = WaitForSingleObject(vdo->state->captured, INFINITE); + // note: until we get the lock again the grabber thread might + // already provide the next sample (which is fine) + _zbar_mutex_lock(&vdo->qlock); + + switch (rc) { + case WAIT_OBJECT_0: + img = vdo->state->image; + break; + case WAIT_ABANDONED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "event handle abandoned"); + break; + case WAIT_FAILED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "Waiting for image failed"); + break; + } + } + + zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, + img ? img->srcidx : 0, img ? img->data : NULL, + img ? img->datalen : 0, _zbar_thread_self()); + + vdo->state->image = NULL; + ResetEvent(vdo->state->captured); + video_unlock(vdo); + + return img; +} + +static int dshow_start(zbar_video_t *vdo) +{ + HRESULT hr; + video_state_t *state = vdo->state; + ResetEvent(state->captured); + + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + hr = IMediaControl_Run(state->mediacontrol); + CHECK_COM_ERROR(hr, "couldn't start video stream", (void)0); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "starting video stream")); + return 0; +} + +static int dshow_stop(zbar_video_t *vdo) +{ + HRESULT hr; + video_state_t *state = vdo->state; + + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + hr = IMediaControl_Stop(state->mediacontrol); + CHECK_COM_ERROR(hr, "couldn't stop video stream", (void)0); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "stopping video stream")); + + _zbar_mutex_lock(&vdo->qlock); + if (state->image) + state->image = NULL; + SetEvent(state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return 0; +} + +static int dshow_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + int rc = 0; // return code + video_state_t *state; + int_format_t *int_fmt; + BYTE *caps; + AM_MEDIA_TYPE *mt = NULL, *currentmt = NULL; + HRESULT hr; + BITMAPINFOHEADER *bih; + + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + int fmt_ind = get_format_index(vdo->formats, fmt); + if (!fmtdef->format || fmt_ind < 0) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported vfw format: %x", fmt)); + + state = vdo->state; + int_fmt = &state->int_formats[fmt_ind]; + + // prepare media type structure as read from GetStreamCaps + caps = malloc(state->caps_size); + if (!caps) + err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, ""); + + hr = IAMStreamConfig_GetStreamCaps(state->camstreamconfig, + int_fmt->idx_caps, &mt, caps); + free(caps); + CHECK_COM_ERROR(hr, "querying chosen stream caps failed", goto cleanup) + bih = dshow_access_bih(mt); + + // then, adjust the format + if (!vdo->width || !vdo->height) { + // video size not requested, take camera default + resolution_t resolution = vdo->state->def_resolution; + // the initial resolution may be not suitable for the current format + get_closest_resolution(&resolution, &int_fmt->resolutions); + vdo->width = resolution.cx; + vdo->height = resolution.cy; + } + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + + zprintf(4, "setting camera format: %.4s(%08x) " BIH_FMT "\n", + (char *)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih)); + + hr = IAMStreamConfig_SetFormat(state->camstreamconfig, mt); + CHECK_COM_ERROR(hr, "setting camera format failed", goto cleanup) + + // re-read format, image data size might have changed + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "queried currentmt", goto cleanup); + + bih = dshow_access_bih(currentmt); + + if (get_fourcc_for_mt(currentmt) != int_fmt->fourcc) { + rc = err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video format set ignored"); + goto cleanup; + } + + vdo->format = fmt; + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->datalen = bih->biSizeImage; + + // datalen was set based on the internal format, but sometimes it's + // different from the format reported to zbar processor + if (vdo->formats[fmt_ind] != state->int_formats[fmt_ind].fourcc) { + // See prepare_mjpg_format_mapping for possible differences + // between internal and zbar format. + vdo->datalen = + BMP_SIZE(vdo->width, vdo->height, mjpg_conversion_fmt_bpp); + } + + zprintf(4, "set camera format: %.4s(%08x) " BIH_FMT "\n", + (char *)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih)); + +cleanup: + DeleteMediaType(mt); + DeleteMediaType(currentmt); + + if (FAILED(hr)) + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting dshow format failed"); + else + // note: if an error happened it was already captured and reported + return rc; +} + +static int dshow_init(zbar_video_t *vdo, uint32_t fmt) +{ + HRESULT hr; + video_state_t *state; + int fmt_ind; + ISampleGrabberCB *grabbercb; + REFERENCE_TIME avgtime_perframe; + + if (dshow_set_format(vdo, fmt)) + return -1; + + state = vdo->state; + fmt_ind = get_format_index(vdo->formats, fmt); + + // install sample grabber callback + grabbercb = (ISampleGrabberCB *)new_zbar_samplegrabber_cb(vdo); + hr = ISampleGrabber_SetCallback(vdo->state->samplegrabber, grabbercb, 1); + ISampleGrabberCB_Release(grabbercb); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_BUSY, __func__, + "setting capture callbacks")); + + // set up directshow graph + + // special handling for MJPG streams: + // we use the stock mjpeg decompressor filter + if (state->int_formats[fmt_ind].fourcc == fourcc('M', 'J', 'P', 'G')) { + IBaseFilter *mjpgdecompressor = NULL; + AM_MEDIA_TYPE conv_mt = { 0 }; + + hr = CoCreateInstance(&CLSID_MjpegDec, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&mjpgdecompressor); + CHECK_COM_ERROR(hr, "failed to create mjpeg decompressor filter", + (void)0) + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to create mjpeg decompressor filter"); + } + + // add mjpeg decompressor to graph + hr = IGraphBuilder_AddFilter(state->graph, mjpgdecompressor, + L"MJPEG decompressor"); + CHECK_COM_ERROR(hr, "adding MJPEG decompressor", goto mjpg_cleanup) + + // explicitly convert MJPG to RGB32 + // (sample grabber will only accept this format) + // + // note: the mjpeg decompressor's output seems to be RGB32 (BGR4) + // by default. + // This fact has been a decisive factor to choosing the mapping + // (BGR4 [zbar input] -> MJPG [camera output]). + // Because zbar requests BGR4 we have to ensure that we really do + // provide it. + conv_mt.majortype = MEDIATYPE_Video; + conv_mt.subtype = *mjpg_conversion_mediatype; + conv_mt.formattype = FORMAT_VideoInfo; + hr = ISampleGrabber_SetMediaType(state->samplegrabber, &conv_mt); + CHECK_COM_ERROR(hr, "setting mjpg conversion media type", + goto mjpg_cleanup) + // no need to destroy conv_mt + + hr = ICaptureGraphBuilder2_RenderStream( + state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, + (IUnknown *)state->camera, NULL, mjpgdecompressor); + CHECK_COM_ERROR(hr, "rendering filter graph 1", goto mjpg_cleanup) + + hr = ICaptureGraphBuilder2_RenderStream(state->builder, NULL, + &MEDIATYPE_Video, + (IUnknown *)mjpgdecompressor, + state->grabberbase, + state->nullrenderer); + CHECK_COM_ERROR(hr, "rendering filter graph 2", goto mjpg_cleanup) + + mjpg_cleanup: + IBaseFilter_Release(mjpgdecompressor); + + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "rendering filter graph failed"); + } + } else { + // ensure (again) that the sample grabber gets only + // video media types with VIDEOINFOHEADER + AM_MEDIA_TYPE grab_mt = { 0 }; + grab_mt.majortype = MEDIATYPE_Video; + grab_mt.formattype = FORMAT_VideoInfo; + hr = ISampleGrabber_SetMediaType(state->samplegrabber, &grab_mt); + CHECK_COM_ERROR(hr, "setting sample grabber media type", + goto render_cleanup) + // no need to destroy grab_mt + + hr = ICaptureGraphBuilder2_RenderStream( + state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, + (IUnknown *)state->camera, state->grabberbase, state->nullrenderer); + CHECK_COM_ERROR(hr, "rendering filter graph", goto render_cleanup) + + render_cleanup: + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "rendering filter graph failed"); + } + } + + // scope: after the graph is built (and the pins connected) we query the + // final media type from sample grabber's input pin; + { + AM_MEDIA_TYPE input_mt = { 0 }; + const BITMAPINFOHEADER *bih; + + hr = ISampleGrabber_GetConnectedMediaType(state->samplegrabber, + &input_mt); + CHECK_COM_ERROR(hr, + "couldn't query input media type from sample grabber", + goto cleanup1) + + assert(dshow_has_vih(&input_mt)); + bih = dshow_caccess_bih(&input_mt); + + // adjust state->bih + state->bi_size = bih->biSize; + state->bih = realloc(state->bih, state->bi_size); + memcpy(state->bih, bih, state->bi_size); + + if (bih->biCompression == BI_RGB) { + // check bitmap orientation for native BI_RGB: + // positive biHeight: bottom-up bitmap -> flip + // negative biHeight: top-down bitmap + state->do_flip_bitmap = (bih->biHeight > 0); + } + + cleanup1: + DestroyMediaType(&input_mt); + + if (FAILED(hr)) + return -1; + } + + // query camera stream parameters; + avgtime_perframe = 0; + + // scope: query avgtime_perframe from camera + { + AM_MEDIA_TYPE *currentmt = NULL; + VIDEOINFOHEADER *vih; + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "querying camera format failed", goto cleanup2) + + assert(dshow_has_vih(currentmt)); + vih = (VIDEOINFOHEADER *)currentmt->pbFormat; + avgtime_perframe = vih->AvgTimePerFrame; + + cleanup2: + DeleteMediaType(currentmt); + } + + // directshow keeps ownership of the image passed to the callback, + // hence, keeping a pointer to the original data (iomode VIDEO_MMAP) is a bad idea + // in a multi-threaded environment. + // real case szenario on a windows 7 tablet with intel atom: + // with vdo->iomode=VIDEO_MMAP and vdo->num_images=1: + // 1) directshow graph provides a sample, sets the data pointer of vdo->state->img + // 2) dshow_dq (called from proc_video_thread()/zbar_video_next_image()) fetches the image and makes a copy of it + // 3) directshow graph provides the next sample, sets the data pointer of vdo->state->img + // 4) dshow_nq (called from ) resets the data pointer of vdo->state->img (nullptr) + // 5) dshow_dq returns vdo->state->img without data (nullptr) + // + // now, we could deal with this special case, but zbar_video_next_image() makes a copy of the sample anyway when + // vdo->num_images==1 (thus VIDEO_MMAP won't save us anything); therefore rather use image buffers provided + // by zbar (see video_init_images()) + vdo->iomode = VIDEO_USERPTR; + // keep zbar's default + //vdo->num_images = ZBAR_VIDEO_IMAGES_MAX; + + // note: vdo->format and vdo->datalen have been set accordingly in dshow_set_format() + + zprintf(3, "initialized video capture: %.4s(%08x), %" PRId64 " frames/s\n", + (char *)&fmt, fmt, _100ns_unit / avgtime_perframe); + + return 0; +} + +static int dshow_cleanup(zbar_video_t *vdo) +{ + video_state_t *state; + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + /* close open device */ + state = vdo->state; + + _zbar_thread_stop(&state->thread, &vdo->qlock); + + dshow_destroy_video_state_t(state); + free(state); + vdo->state = NULL; + + CoUninitialize(); + + return 0; +} + +static int dshow_determine_formats(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + BYTE *caps; + int n = 0, i; + + // collect formats + int resolutions; + HRESULT hr = IAMStreamConfig_GetNumberOfCapabilities(state->camstreamconfig, + &resolutions, + &state->caps_size); + CHECK_COM_ERROR(hr, "couldn't query camera capabilities", return -1) + + zprintf( + 6, + "number of formats/resolutions supported by the camera as reported by directshow: %d\n", + resolutions); + zprintf(6, "list of those with a VIDEOINFOHEADER:\n"); + + vdo->formats = calloc(resolutions + 1, sizeof(uint32_t)); + state->int_formats = calloc(resolutions + 1, sizeof(int_format_t)); + + // this is actually a VIDEO_STREAM_CONFIG_CAPS structure, which is mostly deprecated anyway, + // so we just reserve enough buffer but treat it as opaque otherwise + caps = malloc(state->caps_size); + for (i = 0; i < resolutions; ++i) { + AM_MEDIA_TYPE *mt; + int is_supported; + + HRESULT hr = + IAMStreamConfig_GetStreamCaps(state->camstreamconfig, i, &mt, caps); + CHECK_COM_ERROR(hr, "querying stream capability failed", continue) + is_supported = 0; + + if (dshow_has_vih(mt)) { + uint32_t fmt; + const BITMAPINFOHEADER *bih = dshow_caccess_bih(mt); + + zprintf(6, BIH_FMT "\n", BIH_FIELDS(bih)); + fmt = get_fourcc_for_mt(mt); + // This is actually a check if the format is recognized. + // TODO: Check if the format is really supported by zbar. + is_supported = (fmt != 0); + + if (is_supported) { + resolution_t resolution; + int j; + + // first search for existing fourcc format + for (j = 0; j < n; ++j) { + if (state->int_formats[i].fourcc == fmt) + break; + } + // push back if not found + if (j == n) { + state->int_formats[n].fourcc = fmt; + state->int_formats[n].idx_caps = i; + resolution_list_init(&state->int_formats[n].resolutions); + vdo->formats[n] = fmt; + ++n; + } + + resolution.cx = bih->biWidth; + resolution.cy = bih->biHeight; + resolution_list_add(&state->int_formats[j].resolutions, + &resolution); + } + } + // note: other format types could be possible, e.g. VIDEOINFOHEADER2 ... + + if (!is_supported) { + char *formattype = get_clsid_string(&mt->formattype); + char *subtype = get_clsid_string(&mt->subtype); + zprintf(6, "unsupported format: %s / %s\n", formattype, subtype); + free(formattype); + free(subtype); + } + + DeleteMediaType(mt); + } + free(caps); + + zprintf(6, "number of supported fourcc formats: %d\n", n); + + vdo->formats = realloc(vdo->formats, (n + 1) * sizeof(uint32_t)); + state->int_formats = + realloc(state->int_formats, (n + 1) * sizeof(int_format_t)); + prepare_mjpg_format_mapping(vdo); + dump_formats(vdo); + + if (n == 0) + return -1; + + return 0; +} + +static int dshow_probe(zbar_video_t *vdo) +{ + video_state_t *state; + AM_MEDIA_TYPE *currentmt; + HRESULT hr; + if (dshow_determine_formats(vdo)) + return -1; + + state = vdo->state; + + // query current format + + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "couldn't query current camera format", return -1) + + if (!dshow_has_vih(currentmt)) { + char *formattype = get_clsid_string(¤tmt->formattype); + char *subtype = get_clsid_string(¤tmt->subtype); + zprintf(1, + "encountered unsupported initial format, " + "no VIDEOINFOHEADER video format type: %s / %s\n", + formattype, subtype); + free(formattype); + free(subtype); + + // if we cannot read the current resolution, we need sane defaults + vdo->state->def_resolution.cx = 640; + vdo->state->def_resolution.cy = 480; + } else { + const BITMAPINFOHEADER *current_bih = dshow_caccess_bih(currentmt); + assert(current_bih); + zprintf(3, "initial format: %.4s(%08lx) " BIH_FMT "\n", + (char *)¤t_bih->biCompression, current_bih->biCompression, + BIH_FIELDS(current_bih)); + + // store initial size + vdo->state->def_resolution.cx = current_bih->biWidth; + vdo->state->def_resolution.cy = current_bih->biHeight; + } + + DeleteMediaType(currentmt); + + vdo->intf = VIDEO_DSHOW; + vdo->init = dshow_init; + vdo->start = dshow_start; + vdo->stop = dshow_stop; + vdo->cleanup = dshow_cleanup; + vdo->nq = dshow_nq; + vdo->dq = dshow_dq; + + return 0; +} + +// search camera by index or by device path +static IBaseFilter *dshow_search_camera(const char *dev) +{ + IBaseFilter *camera = NULL; + ICreateDevEnum *devenumerator = NULL; + IEnumMoniker *enummoniker = NULL; + HRESULT hr; + BSTR wdev; + int docontinue; + int devid; + + int reqid = -1; + if ((!strncmp(dev, "/dev/video", 10) || + !strncmp(dev, "\\dev\\video", 10)) && + dev[10] >= '0' && dev[10] <= '9' && !dev[11]) + reqid = dev[10] - '0'; + else if (strlen(dev) == 1 && dev[0] >= '0' && dev[0] <= '9') + reqid = dev[0] - '0'; + + zprintf(6, "searching for camera (#%d): %s\n", reqid, dev); + + hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ICreateDevEnum, (void **)&devenumerator); + CHECK_COM_ERROR(hr, "failed to create system device enumerator", goto done) + + hr = ICreateDevEnum_CreateClassEnumerator(devenumerator, + &CLSID_VideoInputDeviceCategory, + &enummoniker, 0); + CHECK_COM_ERROR(hr, "failed to create enumerator moniker", goto done) + + if (hr != S_OK) { + zprintf(6, "no video devices available"); + goto done; + } + + // turn device name (the GUID) from char to wide char + wdev = SysAllocStringLen(NULL, strlen(dev)); + if (!wdev) + goto done; + MultiByteToWideChar(CP_UTF8, 0, dev, -1, wdev, strlen(dev) + 1); + + // Go through and find capture device + for (devid = 0, docontinue = 1; docontinue; ++devid) { + IMoniker *moniker = NULL; + IPropertyBag *propbag = NULL; + VARIANT devpath_variant; + VARIANT friendlyname_variant; + + hr = IEnumMoniker_Next(enummoniker, 1, &moniker, NULL); + // end of monikers + if (hr != S_OK) + break; + + VariantInit(&devpath_variant); + VariantInit(&friendlyname_variant); + + hr = IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, + (void **)&propbag); + CHECK_COM_ERROR(hr, "failed to get property bag from moniker", + goto breakout) + + hr = IPropertyBag_Read(propbag, L"DevicePath", &devpath_variant, NULL); + CHECK_COM_ERROR(hr, "failed to read DevicePath from camera device", + goto breakout) + hr = IPropertyBag_Read(propbag, L"FriendlyName", &friendlyname_variant, + NULL); + CHECK_COM_ERROR(hr, "failed to read FriendlyName from camera device", + goto breakout) + + if ((reqid >= 0) ? devid == reqid : + !wcscmp(wdev, V_BSTR(&devpath_variant))) { + // create camera from moniker + hr = IMoniker_BindToObject(moniker, NULL, NULL, &IID_IBaseFilter, + (void **)&camera); + CHECK_COM_ERROR(hr, "failed to get camera device", goto breakout) + } + + if (camera) { + zwprintf(1, L"using camera #%d: %s (%s)\n", devid, + V_BSTR(&friendlyname_variant), V_BSTR(&devpath_variant)); + goto breakout; + } else + goto cleanup; + + breakout: + docontinue = 0; + cleanup: + VariantClear(&friendlyname_variant); + VariantClear(&devpath_variant); + COM_SAFE_RELEASE(&propbag); + COM_SAFE_RELEASE(&moniker); + } + SysFreeString(wdev); + +done: + COM_SAFE_RELEASE(&enummoniker); + COM_SAFE_RELEASE(&devenumerator); + + if (!camera) + zprintf(6, "no camera found\n"); + + return camera; +} + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + HRESULT hr; + // assume failure + int ret = -1; + + video_state_t *state; + state = vdo->state = calloc(1, sizeof(video_state_t)); + + state->camera = dshow_search_camera(dev); + if (!state->camera) + goto done; + + if (!state->captured) + // create manual reset event + state->captured = CreateEvent(NULL, TRUE, FALSE, NULL); + else + ResetEvent(state->captured); + + if (_zbar_thread_start(&state->thread, dshow_capture_thread, vdo, NULL)) + return -1; + + // create filter graph instance + hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IGraphBuilder, (void **)&state->graph); + CHECK_COM_ERROR(hr, "graph builder creation", goto done) + + // query media control from filter graph + hr = IGraphBuilder_QueryInterface(state->graph, &IID_IMediaControl, + (void **)&state->mediacontrol); + CHECK_COM_ERROR(hr, "querying media control", goto done) + + // create sample grabber instance + hr = CoCreateInstance(&CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, + &IID_ISampleGrabber, (void **)&state->samplegrabber); + CHECK_COM_ERROR(hr, "samplegrabber creation", goto done) + + // query base filter interface from sample grabber + hr = ISampleGrabber_QueryInterface(state->samplegrabber, &IID_IBaseFilter, + (void **)&state->grabberbase); + CHECK_COM_ERROR(hr, "grabberbase query", goto done) + + // capture graph without preview window + hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&state->nullrenderer); + CHECK_COM_ERROR(hr, "null renderer creation", goto done) + + // add camera to graph + hr = IGraphBuilder_AddFilter(state->graph, state->camera, L"Camera"); + CHECK_COM_ERROR(hr, "adding camera", goto done) + + // add sample grabber to graph + hr = IGraphBuilder_AddFilter(state->graph, state->grabberbase, + L"Sample Grabber"); + CHECK_COM_ERROR(hr, "adding samplegrabber", goto done) + + // add nullrenderer to graph + hr = IGraphBuilder_AddFilter(state->graph, state->nullrenderer, + L"Null Renderer"); + CHECK_COM_ERROR(hr, "adding null renderer", goto done) + + // Create the Capture Graph Builder. + hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, + CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, + (void **)&state->builder); + CHECK_COM_ERROR(hr, "capturegraph builder creation", goto done) + + // tell graph builder about the filter graph + hr = ICaptureGraphBuilder2_SetFiltergraph(state->builder, state->graph); + CHECK_COM_ERROR(hr, "setting filtergraph", goto done) + + // TODO: + // finding the streamconfig interface on the camera's preview output pin (specifying PIN_CATEGORY_PREVIEW, MEDIATYPE_Video) + // should work according to the documentation but it doesn't. + // Because devices may have separate pins for capture and preview or have a video port pin (PIN_CATEGORY_VIDEOPORT) + // instead of a preview pin, I do hope that we get the streamconfig interface for the correct pin + hr = ICaptureGraphBuilder2_FindInterface( + state->builder, &LOOK_DOWNSTREAM_ONLY, NULL, state->camera, + &IID_IAMStreamConfig, (void **)&state->camstreamconfig); + CHECK_COM_ERROR(hr, "querying camera's streamconfig interface", goto done) + + if (dshow_probe(vdo)) + goto done; + + // success + ret = 0; + +done: + if (ret) { + if (state->camera) + _zbar_thread_stop(&state->thread, NULL); + dshow_destroy_video_state_t(state); + free(state); + vdo->state = NULL; + CoUninitialize(); + + return err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to connect to camera '%s'", dev); + } + + return ret; +} diff --git a/zbar/video/null.c b/zbar/video/null.c new file mode 100644 index 0000000..93b828d --- /dev/null +++ b/zbar/video/null.c @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "video.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with video input support")); +} + +int _zbar_video_open(zbar_video_t *vdo, const char *device) +{ + return (null_error(vdo, __func__)); +} diff --git a/zbar/video/v4l.c b/zbar/video/v4l.c new file mode 100644 index 0000000..2039c72 --- /dev/null +++ b/zbar/video/v4l.c @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2011 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_LIBV4L2_H +#include <fcntl.h> +#include <libv4l2.h> +#else +#define v4l2_open open +#define v4l2_close close +#endif + +#include "video.h" + +extern int _zbar_v4l1_probe(zbar_video_t *); +extern int _zbar_v4l2_probe(zbar_video_t *); + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + vdo->fd = v4l2_open(dev, O_RDWR); + if (vdo->fd < 0) + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "opening video device '%s'", dev)); + zprintf(1, "opened camera device %s (fd=%d)\n", dev, vdo->fd); + + int rc = -1; +#ifdef HAVE_LINUX_VIDEODEV2_H + if (vdo->intf != VIDEO_V4L1) + rc = _zbar_v4l2_probe(vdo); +#endif +#ifdef HAVE_LINUX_VIDEODEV_H + if (rc && vdo->intf != VIDEO_V4L2) + rc = _zbar_v4l1_probe(vdo); +#endif + + if (rc && vdo->fd >= 0) { + v4l2_close(vdo->fd); + vdo->fd = -1; + } + return (rc); +} diff --git a/zbar/video/v4l1.c b/zbar/video/v4l1.c new file mode 100644 index 0000000..cb0b9c1 --- /dev/null +++ b/zbar/video/v4l1.c @@ -0,0 +1,404 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2011 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#include <linux/videodev.h> + +#include "image.h" +#include "video.h" + +typedef struct v4l1_format_s { + uint32_t format; + uint8_t bpp; +} v4l1_format_t; + +/* static v4l1 "palette" mappings + * documentation for v4l1 formats is terrible... + */ +static const v4l1_format_t v4l1_formats[17] = { + /* format bpp */ + { 0, 0 }, + { fourcc('G', 'R', 'E', 'Y'), 8 }, /* GREY */ + { fourcc('H', 'I', '2', '4'), 8 }, /* HI240 (BT848) */ + + /* component ordering for RGB palettes is unspecified, + * convention appears to place red in the most significant bits + * FIXME is this true for other drivers? big endian machines? + */ + { fourcc('R', 'G', 'B', 'P'), 16 }, /* RGB565 */ + { fourcc('B', 'G', 'R', '3'), 24 }, /* RGB24 */ + { fourcc('B', 'G', 'R', '4'), 32 }, /* RGB32 */ + { fourcc('R', 'G', 'B', 'O'), 16 }, /* RGB555 */ + { fourcc('Y', 'U', 'Y', '2'), 16 }, /* YUV422 (8 bpp?!) */ + { fourcc('Y', 'U', 'Y', 'V'), 16 }, /* YUYV */ + { fourcc('U', 'Y', 'V', 'Y'), 16 }, /* UYVY */ + { 0, 12 }, /* YUV420 (24 bpp?) FIXME?! */ + { fourcc('Y', '4', '1', 'P'), 12 }, /* YUV411 */ + { 0, 0 }, /* Bt848 raw */ + { fourcc('4', '2', '2', 'P'), 16 }, /* YUV422P (24 bpp?) */ + { fourcc('4', '1', '1', 'P'), 12 }, /* YUV411P */ + { fourcc('Y', 'U', '1', '2'), 12 }, /* YUV420P */ + { fourcc('Y', 'U', 'V', '9'), 9 }, /* YUV410P */ +}; + +static int v4l1_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + if (video_nq_image(vdo, img)) + return (-1); + + if (vdo->iomode != VIDEO_MMAP) + return (0); + + struct video_mmap vmap; + vmap.frame = img->srcidx; + vmap.width = vdo->width; + vmap.height = vdo->height; + vmap.format = vdo->palette; + if (ioctl(vdo->fd, VIDIOCMCAPTURE, &vmap) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "initiating video capture (VIDIOCMCAPTURE)")); + + return (0); +} + +static zbar_image_t *v4l1_dq(zbar_video_t *vdo) +{ + video_iomode_t iomode = vdo->iomode; + int fd = vdo->fd; + zbar_image_t *img = video_dq_image(vdo); + if (!img) + return (NULL); + + if (iomode == VIDEO_MMAP) { + int frame = img->srcidx; + if (ioctl(fd, VIDIOCSYNC, &frame) < 0) + return (NULL); + } else if (read(fd, (void *)img->data, img->datalen) != img->datalen) + return (NULL); + + return (img); +} + +static int v4l1_mmap_buffers(zbar_video_t *vdo) +{ +#ifdef HAVE_SYS_MMAN_H + /* map camera image to memory */ + struct video_mbuf vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + if (ioctl(vdo->fd, VIDIOCGMBUF, &vbuf) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video frame buffers (VIDIOCGMBUF)")); + assert(vbuf.frames && vbuf.size); + + zprintf(1, "mapping %d buffers size=0x%x\n", vbuf.frames, vbuf.size); + vdo->buflen = vbuf.size; + vdo->buf = + mmap(0, vbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vdo->fd, 0); + if (vdo->buf == MAP_FAILED) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "mapping video frame buffers")); + + int i; + for (i = 0; i < vbuf.frames; i++) { + zbar_image_t *img = vdo->images[i]; + zprintf(2, " [%02d] @%08x\n", img->srcidx, vbuf.offsets[i]); + img->data = vdo->buf + vbuf.offsets[i]; + img->datalen = vdo->datalen; + int next_offset = + ((i + 1 < vdo->num_images) ? vbuf.offsets[i + 1] : vbuf.size); + if (next_offset < vbuf.offsets[i] + vdo->datalen) + fprintf(stderr, + "WARNING: insufficient v4l1 video buffer size:\n" + "\tvbuf[%d]=%x vbuf[%d]=%x datalen=%lx\n" + "\timage=%d x %d %.4s(%08x) palette=%d\n", + i, vbuf.offsets[i], i + 1, next_offset, vdo->datalen, + vdo->width, vdo->height, (char *)&vdo->format, vdo->format, + vdo->palette); + } + return (0); +#else + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "memory mapping not supported")); +#endif +} + +static int v4l1_start(zbar_video_t *vdo) +{ + return (0); +} + +static int v4l1_stop(zbar_video_t *vdo) +{ + return (0); +} + +static inline int v4l1_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + struct video_picture vpic; + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video format (VIDIOCGPICT)")); + + vdo->palette = 0; + int ifmt; + for (ifmt = 1; ifmt <= VIDEO_PALETTE_YUV410P; ifmt++) + if (v4l1_formats[ifmt].format == fmt) + break; + if (!fmt || ifmt >= VIDEO_PALETTE_YUV410P) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "invalid v4l1 format: %x", fmt)); + + vpic.palette = ifmt; + vpic.depth = v4l1_formats[ifmt].bpp; + if (ioctl(vdo->fd, VIDIOCSPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting format (VIDIOCSPICT)")); + + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video format (VIDIOCGPICT)")); + + if (vpic.palette != ifmt || vpic.depth != v4l1_formats[ifmt].bpp) { + fprintf(stderr, + "WARNING: set v4l1 palette %d which should have depth %d bpp\n" + " but probed palette %d with depth %d bpp?" + " ...continuing anyway\n", + ifmt, v4l1_formats[ifmt].bpp, vpic.palette, vpic.depth); + err_capture_int(vdo, SEV_WARNING, ZBAR_ERR_INVALID, __func__, + "driver format (%x) inconsistency", fmt); + } + vdo->format = fmt; + vdo->palette = ifmt; + vdo->datalen = (vdo->width * vdo->height * v4l1_formats[ifmt].bpp + 7) >> 3; + + zprintf(1, "set new format: %.4s(%08x) depth=%d palette=%d size=0x%lx\n", + (char *)&vdo->format, vdo->format, vpic.depth, vdo->palette, + vdo->datalen); + return (0); +} + +static int v4l1_init(zbar_video_t *vdo, uint32_t fmt) +{ + if (v4l1_set_format(vdo, fmt)) + return (-1); + if (vdo->iomode == VIDEO_MMAP && v4l1_mmap_buffers(vdo)) + return (-1); + return (0); +} + +static int v4l1_cleanup(zbar_video_t *vdo) +{ +#ifdef HAVE_SYS_MMAN_H + /* FIXME should avoid holding onto mmap'd buffers so long? */ + if (vdo->iomode == VIDEO_MMAP && vdo->buf) { + if (munmap(vdo->buf, vdo->buflen)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "unmapping video frame buffers")); + vdo->buf = NULL; + /* FIXME reset image */ + } +#endif + + /* close open device */ + if (vdo->fd >= 0) { + close(vdo->fd); + vdo->fd = -1; + } + return (0); +} + +static int v4l1_probe_iomode(zbar_video_t *vdo) +{ + vdo->iomode = VIDEO_READWRITE; +#ifdef HAVE_SYS_MMAN_H + struct video_mbuf vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + if (ioctl(vdo->fd, VIDIOCGMBUF, &vbuf) < 0) { + if (errno != EINVAL) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video frame buffers (VIDIOCGMBUF)")); + /* not supported */ + return (0); + } + if (!vbuf.frames || !vbuf.size) + return (0); + vdo->iomode = VIDEO_MMAP; + if (vdo->num_images > vbuf.frames) + vdo->num_images = vbuf.frames; +#endif + zprintf(1, "using %d images in %s mode\n", vdo->num_images, + (vdo->iomode == VIDEO_READWRITE) ? "READ" : "MMAP"); + return (0); +} + +static inline int v4l1_probe_formats(zbar_video_t *vdo) +{ + struct video_picture vpic; + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying format (VIDIOCGPICT)")); + + vdo->format = 0; + if (vpic.palette <= VIDEO_PALETTE_YUV410P) + vdo->format = v4l1_formats[vpic.palette].format; + + zprintf(1, "current format: %.4s(%08x) depth=%d palette=%d\n", + (char *)&vdo->format, vdo->format, vpic.depth, vpic.palette); + + vdo->formats = calloc(16, sizeof(uint32_t)); + if (!vdo->formats) + return (err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating format list")); + + int num_formats = 0; + zprintf(2, "probing supported formats:\n"); + int i; + for (i = 1; i <= VIDEO_PALETTE_YUV410P; i++) { + if (!v4l1_formats[i].format) + continue; + vpic.depth = v4l1_formats[i].bpp; + vpic.palette = i; + if (ioctl(vdo->fd, VIDIOCSPICT, &vpic) < 0) { + zprintf(2, " [%02d] %.4s...no (set fails)\n", i, + (char *)&v4l1_formats[i].format); + continue; + } + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0 || vpic.palette != i) { + zprintf(2, " [%02d] %.4s...no (set ignored)\n", i, + (char *)&v4l1_formats[i].format); + continue; + } + zprintf(2, " [%02d] %.4s...yes\n", i, + (char *)&v4l1_formats[i].format); + vdo->formats[num_formats++] = v4l1_formats[i].format; + } + vdo->formats = realloc(vdo->formats, (num_formats + 1) * sizeof(uint32_t)); + assert(vdo->formats); + + return (v4l1_set_format(vdo, vdo->format)); +} + +static inline int v4l1_init_window(zbar_video_t *vdo) +{ + struct video_window vwin; + memset(&vwin, 0, sizeof(vwin)); + if (ioctl(vdo->fd, VIDIOCGWIN, &vwin) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video window settings (VIDIOCGWIN)")); + + zprintf(1, "current window: %d x %d @(%d, %d)%s\n", vwin.width, vwin.height, + vwin.x, vwin.y, (vwin.flags & 1) ? " INTERLACE" : ""); + + if (vwin.width == vdo->width && vwin.height == vdo->height) + /* max window already set */ + return (0); + + struct video_window maxwin; + memcpy(&maxwin, &vwin, sizeof(maxwin)); + maxwin.width = vdo->width; + maxwin.height = vdo->height; + + zprintf(1, "setting max win: %d x %d @(%d, %d)%s\n", maxwin.width, + maxwin.height, maxwin.x, maxwin.y, + (maxwin.flags & 1) ? " INTERLACE" : ""); + if (ioctl(vdo->fd, VIDIOCSWIN, &maxwin) < 0) { + zprintf(1, "set FAILED...trying to recover original window\n"); + /* ignore errors (driver broken anyway) */ + ioctl(vdo->fd, VIDIOCSWIN, &vwin); + } + + /* re-query resulting parameters */ + memset(&vwin, 0, sizeof(vwin)); + if (ioctl(vdo->fd, VIDIOCGWIN, &vwin) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video window settings (VIDIOCGWIN)")); + + zprintf(1, " final window: %d x %d @(%d, %d)%s\n", vwin.width, + vwin.height, vwin.x, vwin.y, (vwin.flags & 1) ? " INTERLACE" : ""); + vdo->width = vwin.width; + vdo->height = vwin.height; + return (0); +} + +int _zbar_v4l1_probe(zbar_video_t *vdo) +{ + /* check capabilities */ + struct video_capability vcap; + memset(&vcap, 0, sizeof(vcap)); + if (ioctl(vdo->fd, VIDIOCGCAP, &vcap) < 0) + return ( + err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "video4linux version 1 not supported (VIDIOCGCAP)")); + + zprintf(1, "%s (%sCAPTURE) (%d x %d) - (%d x %d)\n", vcap.name, + (vcap.type & VID_TYPE_CAPTURE) ? "" : "*NO* ", vcap.minwidth, + vcap.minheight, vcap.maxwidth, vcap.maxheight); + + if (!(vcap.type & VID_TYPE_CAPTURE)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "v4l1 device does not support CAPTURE")); + + if (!vdo->width || !vdo->height) { + vdo->width = vcap.maxwidth; + vdo->height = vcap.maxheight; + } + + if (v4l1_init_window(vdo) || v4l1_probe_formats(vdo) || + v4l1_probe_iomode(vdo)) + return (-1); + + vdo->intf = VIDEO_V4L1; + vdo->init = v4l1_init; + vdo->cleanup = v4l1_cleanup; + vdo->start = v4l1_start; + vdo->stop = v4l1_stop; + vdo->nq = v4l1_nq; + vdo->dq = v4l1_dq; + return (0); +} diff --git a/zbar/video/v4l2.c b/zbar/video/v4l2.c new file mode 100644 index 0000000..338a4d1 --- /dev/null +++ b/zbar/video/v4l2.c @@ -0,0 +1,1237 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#ifdef HAVE_LIBV4L2_H +#include <fcntl.h> +#include <libv4l2.h> +#else +#define v4l2_close close +#define v4l2_ioctl ioctl +#define v4l2_mmap mmap +#define v4l2_munmap munmap +#endif +#include <linux/videodev2.h> + +#include "image.h" +#include "video.h" + +#define V4L2_FORMATS_MAX 64 +#define V4L2_FORMATS_SIZE_MAX 256 + +typedef struct video_controls_priv_s { + struct video_controls_s s; + + // Private fields + __u32 id; +} video_controls_priv_t; + +static int v4l2_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (video_nq_image(vdo, img)); + + if (video_unlock(vdo)) + return (-1); + + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) { + vbuf.memory = V4L2_MEMORY_MMAP; + vbuf.index = img->srcidx; + } else { + vbuf.memory = V4L2_MEMORY_USERPTR; + vbuf.m.userptr = (unsigned long)img->data; + vbuf.length = img->datalen; + vbuf.index = img->srcidx; /* FIXME workaround broken drivers */ + } + if (v4l2_ioctl(vdo->fd, VIDIOC_QBUF, &vbuf) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "queuing video buffer (VIDIOC_QBUF)")); + return (0); +} + +static zbar_image_t *v4l2_dq(zbar_video_t *vdo) +{ + zbar_image_t *img; + int fd = vdo->fd; + + if (vdo->iomode != VIDEO_READWRITE) { + video_iomode_t iomode = vdo->iomode; + if (video_unlock(vdo)) + return (NULL); + + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (iomode == VIDEO_MMAP) + vbuf.memory = V4L2_MEMORY_MMAP; + else + vbuf.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(fd, VIDIOC_DQBUF, &vbuf) < 0) + return (NULL); + + if (iomode == VIDEO_MMAP) { + assert(vbuf.index >= 0); + assert(vbuf.index < vdo->num_images); + img = vdo->images[vbuf.index]; + } else { + /* reverse map pointer back to image (FIXME) */ + assert(vbuf.m.userptr >= (unsigned long)vdo->buf); + assert(vbuf.m.userptr < (unsigned long)(vdo->buf + vdo->buflen)); + int i = (vbuf.m.userptr - (unsigned long)vdo->buf) / vdo->datalen; + assert(i >= 0); + assert(i < vdo->num_images); + img = vdo->images[i]; + assert(vbuf.m.userptr == (unsigned long)img->data); + } + } else { + img = video_dq_image(vdo); + if (!img) + return (NULL); + + /* FIXME should read entire image */ + ssize_t datalen = read(fd, (void *)img->data, img->datalen); + if (datalen < 0) { + perror("v4l2_dq read"); + return (NULL); + } else if (datalen != img->datalen) + zprintf(0, "WARNING: read() size mismatch: 0x%lx != 0x%lx\n", + datalen, img->datalen); + } + return (img); +} + +static int v4l2_start(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_STREAMON, &type) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "starting video stream (VIDIOC_STREAMON)")); + return (0); +} + +static int v4l2_stop(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_STREAMOFF, &type) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "stopping video stream (VIDIOC_STREAMOFF)")); + return (0); +} + +static int v4l2_cleanup(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) { + rb.memory = V4L2_MEMORY_MMAP; + int i; + for (i = 0; i < vdo->num_images; i++) { + zbar_image_t *img = vdo->images[i]; + if (img->data && v4l2_munmap((void *)img->data, img->datalen)) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "unmapping video frame buffers"); + img->data = NULL; + img->datalen = 0; + } + } else + rb.memory = V4L2_MEMORY_USERPTR; + + /* requesting 0 buffers + * should implicitly disable streaming + */ + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "releasing video frame buffers (VIDIOC_REQBUFS)"); + + /* v4l2_close v4l2_open device */ + if (vdo->fd >= 0) { + v4l2_close(vdo->fd); + vdo->fd = -1; + } + return (0); +} + +static int v4l2_mmap_buffers(zbar_video_t *vdo) +{ + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vbuf.memory = V4L2_MEMORY_MMAP; + + int i; + for (i = 0; i < vdo->num_images; i++) { + vbuf.index = i; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYBUF, &vbuf) < 0) + /* FIXME cleanup */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video buffer (VIDIOC_QUERYBUF)")); + + if (vbuf.length < vdo->datalen) + fprintf( + stderr, + "WARNING: insufficient v4l2 video buffer size:\n" + "\tvbuf[%d].length=%x datalen=%lx image=%d x %d %.4s(%08x)\n", + i, vbuf.length, vdo->datalen, vdo->width, vdo->height, + (char *)&vdo->format, vdo->format); + + zbar_image_t *img = vdo->images[i]; + img->datalen = vbuf.length; + img->data = v4l2_mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE, + MAP_SHARED, vdo->fd, vbuf.m.offset); + if (img->data == MAP_FAILED) + /* FIXME cleanup */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "mapping video frame buffers")); + zprintf(2, " buf[%d] 0x%lx bytes @%p\n", i, img->datalen, img->data); + } + return (0); +} + +static int v4l2_request_buffers(zbar_video_t *vdo, uint32_t num_images) +{ + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.count = num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "requesting video frame buffers (VIDIOC_REQBUFS)")); + if (num_images && rb.count) + vdo->num_images = rb.count; + return (0); +} + +static int v4l2_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + struct v4l2_format vfmt; + struct v4l2_pix_format *vpix = &vfmt.fmt.pix; + memset(&vfmt, 0, sizeof(vfmt)); + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpix->width = vdo->width; + vpix->height = vdo->height; + vpix->pixelformat = fmt; + vpix->field = V4L2_FIELD_NONE; + int rc = 0; + if ((rc = v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt)) < 0) { + /* several broken drivers return an error if we request + * no interlacing (NB v4l2 spec violation) + * ...try again with an interlaced request + */ + zprintf(1, "VIDIOC_S_FMT returned %d(%d), trying interlaced...\n", rc, + errno); + + /* FIXME this might be _ANY once we can de-interlace */ + vpix->field = V4L2_FIELD_INTERLACED; + + if (v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt) < 0) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting format %x (VIDIOC_S_FMT)", fmt)); + + zprintf(0, "WARNING: broken driver returned error when non-interlaced" + " format requested\n"); + } + + struct v4l2_format newfmt; + struct v4l2_pix_format *newpix = &newfmt.fmt.pix; + memset(&newfmt, 0, sizeof(newfmt)); + newfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &newfmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying format (VIDIOC_G_FMT)")); + + if (newpix->field != V4L2_FIELD_NONE) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_INVALID, __func__, + "video driver only supports interlaced format," + " vertical scanning may not work"); + + if (newpix->pixelformat != fmt + /* FIXME bpl/bpp checks? */) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video driver can't provide compatible format")); + + vdo->format = fmt; + vdo->width = newpix->width; + vdo->height = newpix->height; + vdo->datalen = newpix->sizeimage; + + zprintf(1, "set new format: %.4s(%08x) %u x %u (0x%lx)\n", + (char *)&vdo->format, vdo->format, vdo->width, vdo->height, + vdo->datalen); + return (0); +} + +static int v4l2_init(zbar_video_t *vdo, uint32_t fmt) +{ + struct v4l2_requestbuffers rb; + if (v4l2_set_format(vdo, fmt)) + return (-1); + + memset(&rb, 0, sizeof(rb)); + rb.count = vdo->num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "requesting video frame buffers (VIDIOC_REQBUFS)")); + + if (!rb.count) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "driver returned 0 buffers")); + + if (vdo->num_images > rb.count) + vdo->num_images = rb.count; + + zprintf(1, "using %u buffers (of %d requested)\n", rb.count, + vdo->num_images); + + if (vdo->iomode == VIDEO_MMAP) + return (v4l2_mmap_buffers(vdo)); + if (vdo->iomode == VIDEO_USERPTR) + return (v4l2_request_buffers(vdo, vdo->num_images)); + return (0); +} + +static int v4l2_probe_iomode(zbar_video_t *vdo) +{ + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.count = vdo->num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) { + if (vdo->iomode) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported iomode requested (%d)", + vdo->iomode)); + else if (errno != EINVAL) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying streaming mode (VIDIOC_REQBUFS)")); +#ifdef HAVE_SYS_MMAN_H + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "USERPTR failed. Falling back to mmap"); + vdo->iomode = VIDEO_MMAP; +#else + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "Userptr not supported, and zbar was compiled without mmap support")); +#endif + } else { + if (!vdo->iomode) + rb.memory = V4L2_MEMORY_USERPTR; + /* Update the num_images with the max supported by the driver */ + if (rb.count) + vdo->num_images = rb.count; + else + err_capture( + vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "Something is wrong: number of buffers returned by REQBUF is zero!"); + + /* requesting 0 buffers + * This cleans up the buffers allocated previously on probe + */ + rb.count = 0; + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "releasing video frame buffers (VIDIOC_REQBUFS)"); + } + return (0); +} + +static inline void v4l2_max_size(zbar_video_t *vdo, uint32_t pixfmt, + uint32_t *max_width, uint32_t *max_height) +{ + int mwidth = 0, mheight = 0, i; + struct v4l2_frmsizeenum frm; + + for (i = 0; i < V4L2_FORMATS_SIZE_MAX; i++) { + memset(&frm, 0, sizeof(frm)); + frm.index = i; + frm.pixel_format = pixfmt; + + if (v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frm)) + break; + + switch (frm.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + mwidth = frm.discrete.width; + mheight = frm.discrete.height; + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + mwidth = frm.stepwise.max_width; + mheight = frm.stepwise.max_height; + break; + default: + continue; + } + if (mwidth > *max_width) + *max_width = mwidth; + if (mheight > *max_height) + *max_height = mheight; + } +} + +static inline int v4l2_probe_formats(zbar_video_t *vdo) +{ + int n_formats = 0, n_emu_formats = 0; + uint32_t max_width = 0, max_height = 0; + + if (vdo->width && vdo->height) + zprintf(1, "Caller requested an specific size: %d x %d\n", vdo->width, + vdo->height); + + zprintf(2, "enumerating supported formats:\n"); + struct v4l2_fmtdesc desc; + memset(&desc, 0, sizeof(desc)); + desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + for (desc.index = 0; desc.index < V4L2_FORMATS_MAX; desc.index++) { + if (v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &desc) < 0) + break; + zprintf(2, " [%d] %.4s : %s%s%s\n", desc.index, + (char *)&desc.pixelformat, desc.description, + (desc.flags & V4L2_FMT_FLAG_COMPRESSED) ? " COMPRESSED" : "", + (desc.flags & V4L2_FMT_FLAG_EMULATED) ? " EMULATED" : ""); + if (desc.flags & V4L2_FMT_FLAG_EMULATED) { + vdo->emu_formats = realloc(vdo->emu_formats, + (n_emu_formats + 2) * sizeof(uint32_t)); + vdo->emu_formats[n_emu_formats++] = desc.pixelformat; + } else { + vdo->formats = + realloc(vdo->formats, (n_formats + 2) * sizeof(uint32_t)); + vdo->formats[n_formats++] = desc.pixelformat; + } + + if (!vdo->width || !vdo->height) + v4l2_max_size(vdo, desc.pixelformat, &max_width, &max_height); + } + + if (!vdo->width || !vdo->height) { + zprintf(1, "Max supported size: %d x %d\n", max_width, max_height); + if (max_width && max_height) { + vdo->width = max_width; + vdo->height = max_height; + } else { + /* fallback to large size, driver reduces to max available */ + vdo->width = 640 * 64; + vdo->height = 480 * 64; + } + } + + if (!desc.index) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "enumerating video formats (VIDIOC_ENUM_FMT)")); + if (vdo->formats) + vdo->formats[n_formats] = 0; + if (vdo->emu_formats) + vdo->emu_formats[n_emu_formats] = 0; + if (!vdo->formats && vdo->emu_formats) { + /* + * If only emu formats are available, just move them to vdo->formats. + * This happens when libv4l detects that the only available fourcc + * formats are webcam proprietary formats or bayer formats. + */ + vdo->formats = vdo->emu_formats; + vdo->emu_formats = NULL; + } + + zprintf(2, "Found %d formats and %d emulated formats.\n", n_formats, + n_emu_formats); + + struct v4l2_format fmt; + struct v4l2_pix_format *pix = &fmt.fmt.pix; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying current video format (VIDIO_G_FMT)")); + + zprintf(1, "current format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n", + (char *)&pix->pixelformat, pix->pixelformat, pix->width, + pix->height, (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "", + pix->bytesperline, pix->sizeimage); + + vdo->format = pix->pixelformat; + vdo->datalen = pix->sizeimage; + if (pix->width == vdo->width && pix->height == vdo->height) + return (0); + + struct v4l2_format maxfmt; + struct v4l2_pix_format *maxpix = &maxfmt.fmt.pix; + memcpy(&maxfmt, &fmt, sizeof(maxfmt)); + maxpix->width = vdo->width; + maxpix->height = vdo->height; + + zprintf(1, "setting requested size: %d x %d\n", vdo->width, vdo->height); + if (v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &maxfmt) < 0) { + zprintf(1, "set FAILED...trying to recover original format\n"); + /* ignore errors (driver broken anyway) */ + v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &fmt); + } + + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying current video format (VIDIOC_G_FMT)")); + + zprintf(1, "final format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n", + (char *)&pix->pixelformat, pix->pixelformat, pix->width, + pix->height, (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "", + pix->bytesperline, pix->sizeimage); + + vdo->width = pix->width; + vdo->height = pix->height; + vdo->datalen = pix->sizeimage; + return (0); +} + +static inline int v4l2_reset_crop(zbar_video_t *vdo) +{ + /* check cropping */ + struct v4l2_cropcap ccap; + memset(&ccap, 0, sizeof(ccap)); + ccap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_CROPCAP, &ccap) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying crop support (VIDIOC_CROPCAP)")); + + zprintf(1, "crop bounds: %d x %d @ (%d, %d)\n", ccap.bounds.width, + ccap.bounds.height, ccap.bounds.left, ccap.bounds.top); + zprintf(1, "current crop win: %d x %d @ (%d, %d) aspect %d / %d\n", + ccap.defrect.width, ccap.defrect.height, ccap.defrect.left, + ccap.defrect.top, ccap.pixelaspect.numerator, + ccap.pixelaspect.denominator); + +#if 0 + // This logic causes the device to fallback to the current resolution + if(!vdo->width || !vdo->height) { + vdo->width = ccap.defrect.width; + vdo->height = ccap.defrect.height; + } +#endif + + /* reset crop parameters */ + struct v4l2_crop crop; + memset(&crop, 0, sizeof(crop)); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = ccap.defrect; + if (v4l2_ioctl(vdo->fd, VIDIOC_S_CROP, &crop) < 0 && errno != EINVAL && + errno != ENOTTY) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting default crop window (VIDIOC_S_CROP)")); + return (0); +} + +/** locate a control entry + */ +static struct video_controls_priv_s *v4l2_g_control_def(zbar_video_t *vdo, + const char *name) +{ + struct video_controls_priv_s *p = (void *)vdo->controls; + + while (p) { + if (!strcasecmp(p->s.name, name)) + break; + p = p->s.next; + } + + if (!p->s.name) { + zprintf(1, "Control not found: %s", name); + return NULL; + } + + return p; +} + +void v4l2_free_controls(zbar_video_t *vdo) +{ + int i; + + if (vdo->controls) { + struct video_controls_s *p = vdo->controls; + while (p) { + free(p->name); + free(p->group); + if (p->menu) { + for (i = 0; i < p->menu_size; i++) + free(p->menu[i].name); + free(p->menu); + } + p = p->next; + } + free(vdo->controls); + } + vdo->controls = NULL; +} + +#ifdef VIDIOC_QUERY_EXT_CTRL +static const char *v4l2_ctrl_type(uint32_t type) +{ + switch (type) { + // All controls below are available since, at least, Kernel 2.6.31 + case V4L2_CTRL_TYPE_INTEGER: + return "int"; + case V4L2_CTRL_TYPE_BOOLEAN: + return "bool"; + case V4L2_CTRL_TYPE_MENU: + return "menu"; + case V4L2_CTRL_TYPE_BUTTON: + return "button"; + case V4L2_CTRL_TYPE_INTEGER64: + return "int64"; + case V4L2_CTRL_TYPE_CTRL_CLASS: + return "ctrl class"; + case V4L2_CTRL_TYPE_STRING: + return "string"; +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + case V4L2_CTRL_TYPE_INTEGER_MENU: + return "int menu"; +#endif +#ifdef V4L2_CTRL_TYPE_U32 + // Newer controls. All of them should be there since Kernel 3.16 + case V4L2_CTRL_TYPE_BITMASK: + return "bitmask"; + case V4L2_CTRL_TYPE_U8: + return "compound u8"; + case V4L2_CTRL_TYPE_U16: + return "compound u16"; + case V4L2_CTRL_TYPE_U32: + return "compound 32"; +#endif + default: + return "unknown"; + } +} + +static const char *v4l2_ctrl_class(uint32_t class) +{ + switch (class) { + // All classes below are available since, at least, Kernel 2.6.31 + case V4L2_CTRL_CLASS_USER: + return "User"; + case V4L2_CTRL_CLASS_MPEG: + return "MPEG-compression"; + case V4L2_CTRL_CLASS_CAMERA: + return "Camera"; + case V4L2_CTRL_CLASS_FM_TX: + return "FM Modulator"; +#ifdef V4L2_CTRL_CLASS_DETECT + // Newer classes added up to Kernel 3.16 + case V4L2_CTRL_CLASS_FLASH: + return "Camera flash"; + case V4L2_CTRL_CLASS_JPEG: + return "JPEG-compression"; + case V4L2_CTRL_CLASS_IMAGE_SOURCE: + return "Image source"; + case V4L2_CTRL_CLASS_IMAGE_PROC: + return "Image processing"; + case V4L2_CTRL_CLASS_DV: + return "Digital Video"; + case V4L2_CTRL_CLASS_FM_RX: + return "FM Receiver"; + case V4L2_CTRL_CLASS_RF_TUNER: + return "RF tuner"; + case V4L2_CTRL_CLASS_DETECT: + return "Detection"; +#endif + default: + return "Unknown"; + } +} + +// return values: 1: ignore, 0: added, -1: silently ignore +static int v4l2_add_control(zbar_video_t *vdo, + struct v4l2_query_ext_ctrl *query, + struct video_controls_priv_s **ptr) +{ + // Control is disabled, ignore it. Please notice that disabled controls + // can be re-enabled. The right thing here would be to get those too, + // and add a logic to + if (query->flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + + /* Silently ignore control classes */ + if (query->type == V4L2_CTRL_TYPE_CTRL_CLASS) + return -1; + + // There's not much sense on displaying permanent read-only controls + if (query->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 1; + + // Allocate a new element on the linked list + if (!vdo->controls) { + *ptr = calloc(1, sizeof(**ptr)); + vdo->controls = (void *)*ptr; + } else { + (*ptr)->s.next = calloc(1, sizeof(**ptr)); + *ptr = (*ptr)->s.next; + } + + // Fill control data + (*ptr)->id = query->id; + (*ptr)->s.name = strdup((const char *)query->name); + (*ptr)->s.group = strdup(v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query->id))); + switch (query->type) { + case V4L2_CTRL_TYPE_INTEGER: + (*ptr)->s.type = VIDEO_CNTL_INTEGER; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + return (0); + case V4L2_CTRL_TYPE_INTEGER64: + (*ptr)->s.type = VIDEO_CNTL_INTEGER64; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + return (0); + case V4L2_CTRL_TYPE_BOOLEAN: + (*ptr)->s.type = VIDEO_CNTL_BOOLEAN; + return (0); + case V4L2_CTRL_TYPE_BUTTON: + (*ptr)->s.type = VIDEO_CNTL_BUTTON; + return (0); + case V4L2_CTRL_TYPE_STRING: + (*ptr)->s.type = VIDEO_CNTL_STRING; + return (0); +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + case V4L2_CTRL_TYPE_INTEGER_MENU: +#endif + case V4L2_CTRL_TYPE_MENU: { + struct v4l2_querymenu menu; + struct video_control_menu_s *first = NULL, *p; + int n_menu = 0; + + memset(&menu, 0, sizeof(menu)); + menu.id = query->id; + + for (menu.index = query->minimum; menu.index <= query->maximum; + menu.index++) { + if (!ioctl(vdo->fd, VIDIOC_QUERYMENU, &menu)) { + first = realloc(first, (n_menu + 1) * sizeof(*(*ptr)->s.menu)); + + p = &first[n_menu]; + p->value = menu.index; + +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + if (query->type == V4L2_CTRL_TYPE_INTEGER_MENU) + asprintf(p->name, "%i", menu.value); + else +#endif /* V4L2_CTRL_TYPE_INTEGER_MENU */ + p->name = strdup((const char *)menu.name); + + n_menu++; + } + } + (*ptr)->s.menu = first; + (*ptr)->s.menu_size = n_menu; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.type = VIDEO_CNTL_MENU; + return (0); + } + default: + return (1); + } +} + +static int v4l2_query_controls(zbar_video_t *vdo) +{ + struct video_controls_priv_s *ptr = NULL; + struct v4l2_query_ext_ctrl query; + int ignore; + const char *old_class = NULL; + + // Free controls list if not NULL + v4l2_free_controls(vdo); + + memset(&query, 0, sizeof(query)); + query.id = V4L2_CTRL_FLAG_NEXT_CTRL; + while (!v4l2_ioctl(vdo->fd, VIDIOC_QUERY_EXT_CTRL, &query)) { + ignore = v4l2_add_control(vdo, &query, &ptr); + + if (ignore >= 0 && _zbar_verbosity) { + int i; + const char *class = v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query.id)); + if (class != old_class) + zprintf(1, "Control class %s:\n", class); + + zprintf(1, "%-10s %-32s - 0x%x%s\n", v4l2_ctrl_type(query.type), + query.name, query.id, ignore ? " - Ignored" : ""); + + for (i = 0; i < ptr->s.menu_size; i++) + zprintf(1, " %" PRId64 ": %s\n", ptr->s.menu[i].value, + ptr->s.menu[i].name); + + old_class = class; + } + + query.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + + return (0); +} + +static int v4l2_s_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control c; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = 1; +#ifdef V4L2_CTRL_ID2WHICH + ctrls.which = V4L2_CTRL_ID2WHICH(p->id); +#else + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); +#endif + ctrls.controls = &c; + + memset(&c, 0, sizeof(c)); + c.id = p->id; + + switch (p->s.type) { + case VIDEO_CNTL_INTEGER: + case VIDEO_CNTL_BOOLEAN: + case VIDEO_CNTL_BUTTON: + case VIDEO_CNTL_MENU: + c.value = *(int *)value; + break; +#if 0 + //FIXME: Need to check callers with respect bufffer size + case VIDEO_CNTL_INTEGER64: + c.value64 = *(int64_t *)value; + break; +#endif + default: + return ZBAR_ERR_UNSUPPORTED; + } + + int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_EXT_CTRLS, &ctrls); + if (rv) { + zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_INVALID; + } + zprintf(1, "%-32s id: 0x%x set to value %d\n", name, p->id, *(int *)value); + + return 0; +} + +static int v4l2_g_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control c; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = 1; +#ifdef V4L2_CTRL_ID2WHICH + ctrls.which = V4L2_CTRL_ID2WHICH(p->id); +#else + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); +#endif + ctrls.controls = &c; + + memset(&c, 0, sizeof(c)); + c.id = p->id; + + int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_EXT_CTRLS, &ctrls); + if (rv) { + zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv); + return ZBAR_ERR_UNSUPPORTED; + } + + switch (p->s.type) { + case VIDEO_CNTL_INTEGER: + case VIDEO_CNTL_BOOLEAN: + case VIDEO_CNTL_BUTTON: + case VIDEO_CNTL_MENU: + *(int *)value = c.value; + zprintf(1, "v4l2 get user control \"%s\" = %d\n", p->s.name, c.value); + return (0); +#if 0 + //FIXME: Need to check callers with respect bufffer size + case VIDEO_CNTL_INTEGER64: + *(int64_t *)value = c.value64; + return(0); +#endif + default: + return ZBAR_ERR_UNSUPPORTED; + } +} + +#else /* For very old Kernels < 3.16 (2014) */ +static void v4l2_add_control(zbar_video_t *vdo, char *group_name, + struct v4l2_queryctrl *query, + struct video_controls_priv_s **ptr) +{ + // Control is disabled, ignore it. Please notice that disabled controls + // can be re-enabled. The right thing here would be to get those too, + // and add a logic to + if (query->flags & V4L2_CTRL_FLAG_DISABLED) + return; + + // FIXME: add support also for other control types + if (!((query->type == V4L2_CTRL_TYPE_INTEGER) || + (query->type == V4L2_CTRL_TYPE_BOOLEAN))) + return; + + zprintf(1, "%s %s ctrl %-32s id: 0x%x\n", group_name, + (query->type == V4L2_CTRL_TYPE_INTEGER) ? "int " : "bool", + query->name, query->id); + + // Allocate a new element on the linked list + if (!vdo->controls) { + *ptr = calloc(1, sizeof(**ptr)); + vdo->controls = (void *)*ptr; + } else { + (*ptr)->s.next = calloc(1, sizeof(**ptr)); + *ptr = (*ptr)->s.next; + } + + // Fill control data + (*ptr)->id = query->id; + (*ptr)->s.name = strdup((const char *)query->name); + if (query->type == V4L2_CTRL_TYPE_INTEGER) { + (*ptr)->s.type = VIDEO_CNTL_INTEGER; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + } else { + (*ptr)->s.type = VIDEO_CNTL_BOOLEAN; + } +} + +static int v4l2_query_controls(zbar_video_t *vdo) +{ + int id = 0; + struct video_controls_priv_s *ptr; + struct v4l2_queryctrl query; + + // Free controls list if not NULL + ptr = (void *)vdo->controls; + while (ptr) { + free(ptr->s.name); + ptr = ptr->s.next; + } + free(vdo->controls); + vdo->controls = NULL; + ptr = NULL; + + id = 0; + do { + query.id = id | V4L2_CTRL_FLAG_NEXT_CTRL; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query)) + break; + + v4l2_add_control(vdo, "extended", &query, &ptr); + id = query.id; + } while (1); + + id = V4L2_CID_PRIVATE_BASE; + do { + query.id = id; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query)) + break; + v4l2_add_control(vdo, "private", &query, &ptr); + id = query.id; + } while (1); + + return (0); +} + +static int v4l2_s_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_control cs; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + zprintf(1, "%-32s id: 0x%x set to value %d\n", name, p->id, *(int *)value); + + // FIXME: add support for VIDIOC_S_EXT_CTRL + memset(&cs, 0, sizeof(cs)); + cs.id = p->id; + cs.value = *(int *)value; + int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_CTRL, &cs); + if (rv != 0) { + zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_INVALID; + } + return rv; +} + +static int v4l2_g_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_control cs; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&cs, 0, sizeof(cs)); + + cs.id = p->id; + cs.value = *(int *)value; + int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_CTRL, &cs); + *(int *)value = cs.value; + if (rv != 0) { + zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_UNSUPPORTED; + } + return rv; +} +#endif /* VIDIOC_QUERY_EXT_CTRL */ + +static int v4l2_sort_resolutions(const void *__a, const void *__b) +{ + const struct video_resolution_s *a = __a; + const struct video_resolution_s *b = __b; + int r; + + r = (int)b->width - a->width; + if (!r) + r = (int)b->height - a->height; + + return r; +} + +static float v4l2_get_max_fps_discrete(zbar_video_t *vdo, + struct v4l2_frmsizeenum *frmsize) +{ + struct v4l2_frmivalenum frmival = { 0 }; + float fps, max_fps = -1; + + frmival.width = frmsize->discrete.width; + frmival.height = frmsize->discrete.height; + frmival.pixel_format = frmsize->pixel_format; + frmival.index = 0; + + for (frmival.index = 0; + !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); + frmival.index++) { + fps = + ((float)frmival.discrete.denominator) / frmival.discrete.numerator; + if (fps > max_fps) + max_fps = fps; + } + return max_fps; +} + +static void v4l2_insert_resolution(zbar_video_t *vdo, unsigned int *n_res, + unsigned int width, unsigned int height, + float max_fps) +{ + unsigned int i; + + for (i = 0; i < *n_res; i++) { + if (vdo->res[i].width == width && vdo->res[i].height == height) + return; + } + + vdo->res = + realloc(vdo->res, (*n_res + 1) * sizeof(struct video_resolution_s)); + + vdo->res[*n_res].width = width; + vdo->res[*n_res].height = height; + vdo->res[*n_res].max_fps = max_fps; + + (*n_res)++; +} + +static int v4l2_get_supported_resolutions(zbar_video_t *vdo) +{ + struct v4l2_fmtdesc fmt = { 0 }; + struct v4l2_frmsizeenum frmsize = { 0 }; + int i; + unsigned int width, height, n_res = 0; + + vdo->res = NULL; + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + for (fmt.index = 0; !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &fmt); + fmt.index++) { + if (vdo->format != fmt.pixelformat) + continue; + + frmsize.pixel_format = fmt.pixelformat; + frmsize.index = 0; + + while (!v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) { + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + v4l2_insert_resolution(vdo, &n_res, frmsize.discrete.width, + frmsize.discrete.height, + v4l2_get_max_fps_discrete(vdo, + &frmsize)); + } else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) { + for (i = 0; i <= 4; i++) { + width = frmsize.stepwise.min_width + + i * + (frmsize.stepwise.max_width - + frmsize.stepwise.min_width) / + 4; + height = frmsize.stepwise.min_height + + i * + (frmsize.stepwise.max_height - + frmsize.stepwise.min_height) / + 4; + v4l2_insert_resolution(vdo, &n_res, width, height, -1); + } + } + frmsize.index++; + } + } + qsort(vdo->res, n_res, sizeof(struct video_resolution_s), + v4l2_sort_resolutions); + + for (i = 0; i < n_res; i++) { + zprintf(1, "%dx%d (%0.2f fps)\n", vdo->res[i].width, vdo->res[i].height, + vdo->res[i].max_fps); + } + + /* Make the list zero-terminated */ + v4l2_insert_resolution(vdo, &n_res, 0, 0, 0); + + return 0; +} + +int _zbar_v4l2_probe(zbar_video_t *vdo) +{ + /* check capabilities */ + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(vcap)); + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCAP, &vcap) < 0) + return (err_capture( + vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "video4linux version 2 not supported (VIDIOC_QUERYCAP)")); + + zprintf(1, "%.32s on %.32s driver %.16s (version %u.%u.%u)\n", vcap.card, + (vcap.bus_info[0]) ? (char *)vcap.bus_info : "<unknown>", + vcap.driver, (vcap.version >> 16) & 0xff, + (vcap.version >> 8) & 0xff, vcap.version & 0xff); + zprintf(1, " capabilities:%s%s%s%s\n", + (vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) ? " CAPTURE" : "", + (vcap.device_caps & V4L2_CAP_VIDEO_OVERLAY) ? " OVERLAY" : "", + (vcap.device_caps & V4L2_CAP_READWRITE) ? " READWRITE" : "", + (vcap.device_caps & V4L2_CAP_STREAMING) ? " STREAMING" : ""); + + if (!(vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) || + !(vcap.device_caps & (V4L2_CAP_READWRITE | V4L2_CAP_STREAMING))) + return (err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "v4l2 device does not support usable CAPTURE")); + + if (v4l2_reset_crop(vdo)) + /* ignoring errors (driver cropping support questionable) */; + + if (v4l2_probe_formats(vdo)) + return (-1); + + if (v4l2_query_controls(vdo)) + return (-1); + + if (v4l2_get_supported_resolutions(vdo)) + return (-1); + + /* FIXME report error and fallback to readwrite? (if supported...) */ + if (vdo->iomode != VIDEO_READWRITE && + (vcap.device_caps & V4L2_CAP_STREAMING) && v4l2_probe_iomode(vdo)) + return (-1); + if (!vdo->iomode) + vdo->iomode = VIDEO_READWRITE; + + zprintf(1, "using I/O mode: %s\n", + (vdo->iomode == VIDEO_READWRITE) ? "READWRITE" : + (vdo->iomode == VIDEO_MMAP) ? "MMAP" : + (vdo->iomode == VIDEO_USERPTR) ? "USERPTR" : + "<UNKNOWN>"); + + vdo->intf = VIDEO_V4L2; + vdo->init = v4l2_init; + vdo->cleanup = v4l2_cleanup; + vdo->start = v4l2_start; + vdo->stop = v4l2_stop; + vdo->nq = v4l2_nq; + vdo->dq = v4l2_dq; + vdo->set_control = v4l2_s_control; + vdo->get_control = v4l2_g_control; + vdo->free = v4l2_free_controls; + return (0); +} diff --git a/zbar/video/vfw.c b/zbar/video/vfw.c new file mode 100644 index 0000000..a9e050d --- /dev/null +++ b/zbar/video/vfw.c @@ -0,0 +1,511 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "thread.h" +#include "video.h" + +#include <vfw.h> + +#include <assert.h> + +#define MAX_DRIVERS 10 +#define MAX_NAME 128 + +#define BIH_FMT \ + "%ldx%ld @%dbpp (%lx) cmp=%.4s(%08lx) res=%ldx%ld clr=%ld/%ld (%lx)" +#define BIH_FIELDS(bih) \ + (bih)->biWidth, (bih)->biHeight, (bih)->biBitCount, (bih)->biSizeImage, \ + (char *)&(bih)->biCompression, (bih)->biCompression, \ + (bih)->biXPelsPerMeter, (bih)->biYPelsPerMeter, (bih)->biClrImportant, \ + (bih)->biClrUsed, (bih)->biSize + +struct video_state_s { + zbar_thread_t thread; /* capture message pump */ + HANDLE captured; + HWND hwnd; /* vfw interface */ + HANDLE notify; /* capture thread status change */ + int bi_size; /* size of bih */ + BITMAPINFOHEADER *bih; /* video format details */ + zbar_image_t *image; /* currently capturing frame */ +}; + +static const uint32_t vfw_formats[] = { + /* planar YUV formats */ + fourcc('I', '4', '2', '0'), + /* FIXME YU12 is IYUV in windows */ + fourcc('Y', 'V', '1', '2'), + /* FIXME IMC[1-4]? */ + + /* planar Y + packed UV plane */ + fourcc('N', 'V', '1', '2'), + + /* packed YUV formats */ + fourcc('U', 'Y', 'V', 'Y'), fourcc('Y', 'U', 'Y', '2'), /* FIXME add YVYU */ + /* FIXME AYUV? Y411? Y41P? */ + + /* packed rgb formats */ + fourcc('B', 'G', 'R', '3'), fourcc('B', 'G', 'R', '4'), + + fourcc('Y', 'V', 'U', '9'), + + /* basic grayscale format */ + fourcc('G', 'R', 'E', 'Y'), fourcc('Y', '8', '0', '0'), + + /* compressed formats */ + fourcc('J', 'P', 'E', 'G'), + + /* terminator */ + 0 +}; + +#define VFW_NUM_FORMATS (sizeof(vfw_formats) / sizeof(uint32_t)) + +static ZTHREAD vfw_capture_thread(void *arg) +{ + MSG msg; + int rc = 0; + + zbar_video_t *vdo = arg; + video_state_t *state = vdo->state; + zbar_thread_t *thr = &state->thread; + + state->hwnd = capCreateCaptureWindow(NULL, WS_POPUP, 0, 0, 1, 1, NULL, 0); + if (!state->hwnd) + goto done; + + _zbar_mutex_lock(&vdo->qlock); + _zbar_thread_init(thr); + zprintf(4, "spawned vfw capture thread (thr=%04lx)\n", _zbar_thread_self()); + + while (thr->started && rc >= 0 && rc <= 1) { + _zbar_mutex_unlock(&vdo->qlock); + + rc = MsgWaitForMultipleObjects(1, &thr->notify, 0, INFINITE, + QS_ALLINPUT); + if (rc == 1) + while (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE)) + if (rc > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + _zbar_mutex_lock(&vdo->qlock); + } + +done: + thr->running = 0; + _zbar_event_trigger(&thr->activity); + _zbar_mutex_unlock(&vdo->qlock); + return (0); +} + +static LRESULT CALLBACK vfw_stream_cb(HWND hwnd, VIDEOHDR *hdr) +{ + zbar_video_t *vdo; + zbar_image_t *img; + + if (!hwnd || !hdr) + return (0); + vdo = (void *)capGetUserData(hwnd); + + _zbar_mutex_lock(&vdo->qlock); + img = vdo->state->image; + if (!img) { + _zbar_mutex_lock(&vdo->qlock); + img = video_dq_image(vdo); + } + if (img) { + img->data = hdr->lpData; + img->datalen = hdr->dwBufferLength; + vdo->state->image = img; + SetEvent(vdo->state->captured); + } + _zbar_mutex_unlock(&vdo->qlock); + + return (1); +} + +static LRESULT CALLBACK vfw_error_cb(HWND hwnd, int errid, const char *errmsg) +{ + zbar_video_t *vdo; + if (!hwnd) + return (0); + vdo = (void *)capGetUserData(hwnd); + zprintf(2, "id=%d msg=%s\n", errid, errmsg); + _zbar_mutex_lock(&vdo->qlock); + vdo->state->image = NULL; + SetEvent(vdo->state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return (1); +} + +static int vfw_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + img->data = NULL; + img->datalen = 0; + return (video_nq_image(vdo, img)); +} + +/// Platform dependent part of #zbar_video_next_image, which blocks +/// until an image is available. +/** Must be called with video lock held and returns + * with the lock released. + * <p>Waits for the image from `vdo->state->image`. If available, + * this field is nulled. Releases the lock temporarily when waiting for + * the `vdo->state->captured` signal. */ +static zbar_image_t *vfw_dq(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->state->image; + if (!img) { + int rc; + _zbar_mutex_unlock(&vdo->qlock); + rc = WaitForSingleObject(vdo->state->captured, INFINITE); + // note: until we get the lock again the grabber thread might + // already provide the next sample (which is fine) + _zbar_mutex_lock(&vdo->qlock); + + switch (rc) { + case WAIT_OBJECT_0: + img = vdo->state->image; + break; + case WAIT_ABANDONED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "event handle abandoned"); + break; + case WAIT_FAILED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "Waiting for image failed"); + break; + } + } + + vdo->state->image = NULL; + ResetEvent(vdo->state->captured); + + video_unlock(vdo); + return (img); +} + +static int vfw_start(zbar_video_t *vdo) +{ + ResetEvent(vdo->state->captured); + + if (!capCaptureSequenceNoFile(vdo->state->hwnd)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "starting video stream")); + return (0); +} + +static int vfw_stop(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + if (!capCaptureAbort(state->hwnd)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "stopping video stream")); + + _zbar_mutex_lock(&vdo->qlock); + if (state->image) + state->image = NULL; + SetEvent(state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return (0); +} + +static int vfw_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + BITMAPINFOHEADER *bih; + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + if (!fmtdef->format) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported vfw format: %x", fmt)); + + bih = vdo->state->bih; + assert(bih); + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + switch (fmtdef->group) { + case ZBAR_FMT_GRAY: + bih->biBitCount = 8; + break; + case ZBAR_FMT_YUV_PLANAR: + case ZBAR_FMT_YUV_PACKED: + case ZBAR_FMT_YUV_NV: + bih->biBitCount = + 8 + (16 >> (fmtdef->p.yuv.xsub2 + fmtdef->p.yuv.ysub2)); + break; + case ZBAR_FMT_RGB_PACKED: + bih->biBitCount = fmtdef->p.rgb.bpp * 8; + break; + default: + bih->biBitCount = 0; + } + bih->biClrUsed = bih->biClrImportant = 0; + bih->biCompression = fmt; + + zprintf(8, "setting format: %.4s(%08x) " BIH_FMT "\n", (char *)&fmt, fmt, + BIH_FIELDS(bih)); + + if (!capSetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting video format")); + + if (!capGetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "getting video format")); + + if (bih->biCompression != fmt) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video format set ignored")); + + vdo->format = fmt; + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->datalen = bih->biSizeImage; + + zprintf(4, "set new format: %.4s(%08x) " BIH_FMT "\n", (char *)&fmt, fmt, + BIH_FIELDS(bih)); + return (0); +} + +static int vfw_init(zbar_video_t *vdo, uint32_t fmt) +{ + HWND hwnd; + CAPTUREPARMS cp; + + if (vfw_set_format(vdo, fmt)) + return (-1); + + hwnd = vdo->state->hwnd; + if (!capCaptureGetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "retrieving capture parameters")); + + cp.dwRequestMicroSecPerFrame = 33333; + cp.fMakeUserHitOKToCapture = 0; + cp.wPercentDropForError = 90; + cp.fYield = 1; + cp.wNumVideoRequested = vdo->num_images; + cp.fCaptureAudio = 0; + cp.vKeyAbort = 0; + cp.fAbortLeftMouse = 0; + cp.fAbortRightMouse = 0; + cp.fLimitEnabled = 0; + + if (!capCaptureSetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "setting capture parameters")); + + if (!capCaptureGetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "checking capture parameters")); + + /* ignore errors since we skipped checking fHasOverlay */ + capOverlay(hwnd, 0); + + if (!capPreview(hwnd, 0) || !capPreviewScale(hwnd, 0)) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_WINAPI, __func__, + "disabling preview"); + + if (!capSetCallbackOnVideoStream(hwnd, vfw_stream_cb) || + !capSetCallbackOnError(hwnd, vfw_error_cb)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_BUSY, __func__, + "setting capture callbacks")); + + vdo->num_images = cp.wNumVideoRequested; + vdo->iomode = VIDEO_MMAP; /* driver provides "locked" buffers */ + + zprintf(3, "initialized video capture: %d buffers %ldms/frame\n", + vdo->num_images, cp.dwRequestMicroSecPerFrame); + + return (0); +} + +static int vfw_cleanup(zbar_video_t *vdo) +{ + /* close open device */ + video_state_t *state = vdo->state; + /* NB this has to go here so the thread can pump messages during cleanup */ + capDriverDisconnect(state->hwnd); + DestroyWindow(state->hwnd); + state->hwnd = NULL; + _zbar_thread_stop(&state->thread, &vdo->qlock); + + if (state->captured) { + CloseHandle(state->captured); + state->captured = NULL; + } + return (0); +} + +static int vfw_probe_format(zbar_video_t *vdo, uint32_t fmt) +{ + BITMAPINFOHEADER *bih; + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + if (!fmtdef) + return (0); + + zprintf(4, " trying %.4s(%08x)...\n", (char *)&fmt, fmt); + bih = vdo->state->bih; + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + switch (fmtdef->group) { + case ZBAR_FMT_GRAY: + bih->biBitCount = 8; + break; + case ZBAR_FMT_YUV_PLANAR: + case ZBAR_FMT_YUV_PACKED: + case ZBAR_FMT_YUV_NV: + bih->biBitCount = + 8 + (16 >> (fmtdef->p.yuv.xsub2 + fmtdef->p.yuv.ysub2)); + break; + case ZBAR_FMT_RGB_PACKED: + bih->biBitCount = fmtdef->p.rgb.bpp * 8; + break; + default: + bih->biBitCount = 0; + } + bih->biCompression = fmt; + + if (!capSetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) { + zprintf(4, "\tno (set fails)\n"); + return (0); + } + + if (!capGetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (0 /*FIXME error...*/); + + zprintf(6, "\tactual: " BIH_FMT "\n", BIH_FIELDS(bih)); + + if (bih->biCompression != fmt) { + zprintf(4, "\tno (set ignored)\n"); + return (0); + } + + zprintf(4, "\tyes\n"); + return (1); +} + +static int vfw_probe(zbar_video_t *vdo) +{ + BITMAPINFOHEADER *bih; + int n = 0; + const uint32_t *fmt; + + video_state_t *state = vdo->state; + state->bi_size = capGetVideoFormatSize(state->hwnd); + bih = state->bih = realloc(state->bih, state->bi_size); + /* FIXME check OOM */ + + if (!capSetUserData(state->hwnd, (LONG)vdo) || !state->bi_size || !bih || + !capGetVideoFormat(state->hwnd, bih, state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting up video capture")); + + zprintf(3, "initial format: " BIH_FMT " (bisz=%x)\n", BIH_FIELDS(bih), + state->bi_size); + + if (!vdo->width || !vdo->height) { + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + } + vdo->datalen = bih->biSizeImage; + + zprintf(2, "probing supported formats:\n"); + vdo->formats = calloc(VFW_NUM_FORMATS, sizeof(uint32_t)); + + for (fmt = vfw_formats; *fmt; fmt++) + if (vfw_probe_format(vdo, *fmt)) + vdo->formats[n++] = *fmt; + + vdo->formats = realloc(vdo->formats, (n + 1) * sizeof(uint32_t)); + + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->intf = VIDEO_VFW; + vdo->init = vfw_init; + vdo->start = vfw_start; + vdo->stop = vfw_stop; + vdo->cleanup = vfw_cleanup; + vdo->nq = vfw_nq; + vdo->dq = vfw_dq; + return (0); +} + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + int reqid = -1; + char name[MAX_NAME], desc[MAX_NAME]; + int devid; + + video_state_t *state = vdo->state; + if (!state) + state = vdo->state = calloc(1, sizeof(video_state_t)); + + if ((!strncmp(dev, "/dev/video", 10) || + !strncmp(dev, "\\dev\\video", 10)) && + dev[10] >= '0' && dev[10] <= '9' && !dev[11]) + reqid = dev[10] - '0'; + else if (strlen(dev) == 1 && dev[0] >= '0' && dev[0] <= '9') + reqid = dev[0] - '0'; + + zprintf(6, "searching for camera: %s (%d)\n", dev, reqid); + for (devid = 0; devid < MAX_DRIVERS; devid++) { + if (!capGetDriverDescription(devid, name, MAX_NAME, desc, MAX_NAME)) { + /* FIXME TBD error */ + zprintf(6, " [%d] not found...\n", devid); + continue; + } + zprintf(6, " [%d] %.100s - %.100s\n", devid, name, desc); + if ((reqid >= 0) ? devid == reqid : !strncmp(dev, name, MAX_NAME)) + break; + } + if (devid >= MAX_DRIVERS) + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not found '%s'", dev)); + + if (!state->captured) + state->captured = CreateEvent(NULL, TRUE, FALSE, NULL); + else + ResetEvent(state->captured); + + if (_zbar_thread_start(&state->thread, vfw_capture_thread, vdo, NULL)) + return (-1); + + /* FIXME error */ + assert(state->hwnd); + + if (!capDriverConnect(state->hwnd, devid)) { + _zbar_thread_stop(&state->thread, NULL); + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to connect to camera '%s'", dev)); + } + + zprintf(1, "opened camera: %.60s (%d) (thr=%04lx)\n", name, devid, + _zbar_thread_self()); + + if (vfw_probe(vdo)) { + _zbar_thread_stop(&state->thread, NULL); + return (-1); + } + return (0); +} diff --git a/zbar/window.c b/zbar/window.c new file mode 100644 index 0000000..80a952e --- /dev/null +++ b/zbar/window.c @@ -0,0 +1,305 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "window.h" +#include <time.h> /* clock_gettime */ +#include "image.h" +#include "timer.h" +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> /* gettimeofday */ +#endif + +zbar_window_t *zbar_window_create() +{ + zbar_window_t *w = calloc(1, sizeof(zbar_window_t)); + if (!w) + return (NULL); + err_init(&w->err, ZBAR_MOD_WINDOW); + w->overlay = 1; + (void)_zbar_mutex_init(&w->imglock); + return (w); +} + +void zbar_window_destroy(zbar_window_t *w) +{ + /* detach */ + zbar_window_attach(w, NULL, 0); + err_cleanup(&w->err); + _zbar_mutex_destroy(&w->imglock); + free(w); +} + +int zbar_window_attach(zbar_window_t *w, void *display, unsigned long drawable) +{ + /* release image */ + zbar_window_draw(w, NULL); + if (w->cleanup) { + w->cleanup(w); + w->cleanup = NULL; + w->draw_image = NULL; + } + if (w->formats) { + free(w->formats); + w->formats = NULL; + } + w->src_format = 0; + w->src_width = w->src_height = 0; + w->scaled_size.x = w->scaled_size.y = 0; + w->dst_width = w->dst_height = 0; + w->max_width = w->max_height = 1 << 15; + w->scale_num = w->scale_den = 1; + return (_zbar_window_attach(w, display, drawable)); +} + +static void window_outline_symbol(zbar_window_t *w, uint32_t color, + const zbar_symbol_t *sym) +{ + if (sym->syms) { + const zbar_symbol_t *s; + for (s = sym->syms->head; s; s = s->next) + window_outline_symbol(w, 1, s); + } + _zbar_window_draw_polygon(w, color, sym->pts, sym->npts); +} + +static inline int window_draw_overlay(zbar_window_t *w) +{ + if (!w->overlay) + return (0); + if (w->overlay >= 1 && w->image && w->image->syms) { + /* FIXME outline each symbol */ + const zbar_symbol_t *sym = w->image->syms->head; + for (; sym; sym = sym->next) { + uint32_t color = ((sym->cache_count < 0) ? 4 : 2); + if (sym->type == ZBAR_QRCODE || sym->type == ZBAR_SQCODE) + window_outline_symbol(w, color, sym); + else { + /* FIXME linear bbox broken */ + point_t org = w->scaled_offset; + int i; + for (i = 0; i < sym->npts; i++) { + point_t p = window_scale_pt(w, sym->pts[i]); + p.x += org.x; + p.y += org.y; + if (p.x < 3) + p.x = 3; + else if (p.x > w->width - 4) + p.x = w->width - 4; + if (p.y < 3) + p.y = 3; + else if (p.y > w->height - 4) + p.y = w->height - 4; + _zbar_window_draw_marker(w, color, p); + } + } + } + } + + if (w->overlay >= 2) { + /* calculate/display frame rate */ + unsigned long time = _zbar_timer_now(); + if (w->time) { + int avg = w->time_avg = (w->time_avg + time - w->time) / 2; + point_t p = { -8, -1 }; + char text[32]; + sprintf(text, "%d.%01d fps", 1000 / avg, (10000 / avg) % 10); + _zbar_window_draw_text(w, 3, p, text); + } + w->time = time; + } + return (0); +} + +inline int zbar_window_redraw(zbar_window_t *w) +{ + int rc = 0; + zbar_image_t *img; + if (window_lock(w)) + return (-1); + if (!w->display || _zbar_window_begin(w)) { + (void)window_unlock(w); + return (-1); + } + + img = w->image; + if (w->init && w->draw_image && img) { + int format_change = + (w->src_format != img->format && w->format != img->format); + if (format_change) { + _zbar_best_format(img->format, &w->format, w->formats); + if (!w->format) + rc = err_capture_int( + w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no conversion from %x to supported formats", img->format); + w->src_format = img->format; + } + + if (!rc && (format_change || !w->scaled_size.x || !w->dst_width)) { + point_t size = { w->width, w->height }; + zprintf(24, "init: src=%.4s(%08x) %dx%d dst=%.4s(%08x) %dx%d\n", + (char *)&w->src_format, w->src_format, w->src_width, + w->src_height, (char *)&w->format, w->format, w->dst_width, + w->dst_height); + if (!w->dst_width) { + w->src_width = img->width; + w->src_height = img->height; + } + + if (size.x > w->max_width) + size.x = w->max_width; + if (size.y > w->max_height) + size.y = w->max_height; + + if (size.x * w->src_height < size.y * w->src_width) { + w->scale_num = size.x; + w->scale_den = w->src_width; + } else { + w->scale_num = size.y; + w->scale_den = w->src_height; + } + + rc = w->init(w, img, format_change); + + if (!rc) { + size.x = w->src_width; + size.y = w->src_height; + w->scaled_size = size = window_scale_pt(w, size); + w->scaled_offset.x = ((int)w->width - size.x) / 2; + w->scaled_offset.y = ((int)w->height - size.y) / 2; + zprintf(24, + "scale: src=%dx%d win=%dx%d by %d/%d => %dx%d @%d,%d\n", + w->src_width, w->src_height, w->width, w->height, + w->scale_num, w->scale_den, size.x, size.y, + w->scaled_offset.x, w->scaled_offset.y); + } else { + /* unable to display this image */ + _zbar_image_refcnt(img, -1); + w->image = img = NULL; + } + } + + if (!rc && (img->format != w->format || img->width != w->dst_width || + img->height != w->dst_height)) { + /* save *converted* image for redraw */ + zprintf(48, "convert: %.4s(%08x) %dx%d => %.4s(%08x) %dx%d\n", + (char *)&img->format, img->format, img->width, img->height, + (char *)&w->format, w->format, w->dst_width, w->dst_height); + w->image = zbar_image_convert_resize(img, w->format, w->dst_width, + w->dst_height); + w->image->syms = img->syms; + if (img->syms) + zbar_symbol_set_ref(img->syms, 1); + zbar_image_destroy(img); + img = w->image; + } + + if (!rc) { + point_t org; + rc = w->draw_image(w, img); + + org = w->scaled_offset; + if (org.x > 0) { + point_t p = { 0, org.y }; + point_t s = { org.x, w->scaled_size.y }; + _zbar_window_fill_rect(w, 0, p, s); + s.x = w->width - w->scaled_size.x - s.x; + if (s.x > 0) { + p.x = w->width - s.x; + _zbar_window_fill_rect(w, 0, p, s); + } + } + if (org.y > 0) { + point_t p = { 0, 0 }; + point_t s = { w->width, org.y }; + _zbar_window_fill_rect(w, 0, p, s); + s.y = w->height - w->scaled_size.y - s.y; + if (s.y > 0) { + p.y = w->height - s.y; + _zbar_window_fill_rect(w, 0, p, s); + } + } + } + if (!rc) + rc = window_draw_overlay(w); + } else + rc = 1; + + if (rc) + rc = _zbar_window_draw_logo(w); + + _zbar_window_end(w); + (void)window_unlock(w); + return (rc); +} + +int zbar_window_draw(zbar_window_t *w, zbar_image_t *img) +{ + if (window_lock(w)) + return (-1); + if (!w->draw_image) + img = NULL; + if (img) { + _zbar_image_refcnt(img, 1); + if (img->width != w->src_width || img->height != w->src_height) + w->dst_width = 0; + } + if (w->image) + _zbar_image_refcnt(w->image, -1); + w->image = img; + return (window_unlock(w)); +} + +void zbar_window_set_overlay(zbar_window_t *w, int lvl) +{ + if (lvl < 0) + lvl = 0; + if (lvl > 2) + lvl = 2; + if (window_lock(w)) + return; + if (w->overlay != lvl) + w->overlay = lvl; + (void)window_unlock(w); +} + +int zbar_window_get_overlay(const zbar_window_t *w) +{ + zbar_window_t *ncw = (zbar_window_t *)w; + int lvl; + if (window_lock(ncw)) + return (-1); + lvl = w->overlay; + (void)window_unlock(ncw); + return (lvl); +} + +int zbar_window_resize(zbar_window_t *w, unsigned width, unsigned height) +{ + if (window_lock(w)) + return (-1); + w->width = width; + w->height = height; + w->scaled_size.x = 0; + _zbar_window_resize(w); + return (window_unlock(w)); +} diff --git a/zbar/window.h b/zbar/window.h new file mode 100644 index 0000000..d0ecc44 --- /dev/null +++ b/zbar/window.h @@ -0,0 +1,142 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdlib.h> + +#include <zbar.h> +#include "error.h" +#include "mutex.h" +#include "symbol.h" + +typedef struct window_state_s window_state_t; + +struct zbar_window_s { + errinfo_t err; /* error reporting */ + zbar_image_t *image; /* last displayed image + * NB image access must be locked! + */ + unsigned overlay; /* user set overlay level */ + + uint32_t format; /* output format */ + unsigned width, height; /* current output size */ + unsigned max_width, max_height; + + uint32_t src_format; /* current input format */ + unsigned src_width; /* last displayed image size */ + unsigned src_height; + + unsigned dst_width; /* conversion target */ + unsigned dst_height; + + unsigned scale_num; /* output scaling */ + unsigned scale_den; + + point_t scaled_offset; /* output position and size */ + point_t scaled_size; + + uint32_t *formats; /* supported formats (zero terminated) */ + + zbar_mutex_t imglock; /* lock displayed image */ + + void *display; + unsigned long xwin; + unsigned long time; /* last image display in milliseconds */ + unsigned long time_avg; /* average of inter-frame times */ + + window_state_t *state; /* platform/interface specific state */ + + /* interface dependent methods */ + int (*init)(zbar_window_t *, zbar_image_t *, int); + int (*draw_image)(zbar_window_t *, zbar_image_t *); + int (*cleanup)(zbar_window_t *); +}; + +/* window.draw has to be thread safe wrt/other apis + * FIXME should be a semaphore + */ +static inline int window_lock(zbar_window_t *w) +{ + int rc = 0; + if ((rc = _zbar_mutex_lock(&w->imglock))) { + err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to acquire lock"); + w->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int window_unlock(zbar_window_t *w) +{ + int rc = 0; + if ((rc = _zbar_mutex_unlock(&w->imglock))) { + err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to release lock"); + w->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int _zbar_window_add_format(zbar_window_t *w, uint32_t fmt) +{ + int i; + for (i = 0; w->formats && w->formats[i]; i++) + if (w->formats[i] == fmt) + return (i); + + w->formats = realloc(w->formats, (i + 2) * sizeof(uint32_t)); + w->formats[i] = fmt; + w->formats[i + 1] = 0; + return (i); +} + +static inline point_t window_scale_pt(zbar_window_t *w, point_t p) +{ + p.x = ((long)p.x * w->scale_num + w->scale_den - 1) / w->scale_den; + p.y = ((long)p.y * w->scale_num + w->scale_den - 1) / w->scale_den; + return (p); +} + +/* PAL interface */ +extern int _zbar_window_attach(zbar_window_t *, void *, unsigned long); +extern int _zbar_window_expose(zbar_window_t *, int, int, int, int); +extern int _zbar_window_resize(zbar_window_t *); +extern int _zbar_window_clear(zbar_window_t *); +extern int _zbar_window_begin(zbar_window_t *); +extern int _zbar_window_end(zbar_window_t *); +extern int _zbar_window_draw_marker(zbar_window_t *, uint32_t, point_t); +extern int _zbar_window_draw_polygon(zbar_window_t *, uint32_t, const point_t *, + int); +extern int _zbar_window_draw_text(zbar_window_t *, uint32_t, point_t, + const char *); +extern int _zbar_window_fill_rect(zbar_window_t *, uint32_t, point_t, point_t); +extern int _zbar_window_draw_logo(zbar_window_t *); + +#endif diff --git a/zbar/window/dib.c b/zbar/window/dib.c new file mode 100644 index 0000000..7ae3eb2 --- /dev/null +++ b/zbar/window/dib.c @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "image.h" +#include "win.h" +#include "window.h" + +static int dib_cleanup(zbar_window_t *w) +{ + return (0); +} + +static int dib_init(zbar_window_t *w, zbar_image_t *img, int new_format) +{ + window_state_t *win; + if (new_format) + _zbar_window_bih_init(w, img); + + win = w->state; + w->dst_width = win->bih.biWidth = (img->width + 3) & ~3; + w->dst_height = win->bih.biHeight = img->height; + return (0); +} + +static int dib_draw(zbar_window_t *w, zbar_image_t *img) +{ + StretchDIBits(w->state->hdc, w->scaled_offset.x, + w->scaled_offset.y + w->scaled_size.y - 1, w->scaled_size.x, + -w->scaled_size.y, 0, 0, w->src_width, w->src_height, + (void *)img->data, (BITMAPINFO *)&w->state->bih, + DIB_RGB_COLORS, SRCCOPY); + return (0); +} + +static uint32_t dib_formats[] = { fourcc('B', 'G', 'R', '3'), + fourcc('B', 'G', 'R', '4'), + fourcc('J', 'P', 'E', 'G'), 0 }; + +int _zbar_window_dib_init(zbar_window_t *w) +{ + uint32_t *fmt; + for (fmt = dib_formats; *fmt; fmt++) + _zbar_window_add_format(w, *fmt); + + w->init = dib_init; + w->draw_image = dib_draw; + w->cleanup = dib_cleanup; + return (0); +} diff --git a/zbar/window/null.c b/zbar/window/null.c new file mode 100644 index 0000000..7d30e53 --- /dev/null +++ b/zbar/window/null.c @@ -0,0 +1,88 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "window.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with output window support")); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long win) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_expose(zbar_window_t *w, int x, int y, int width, int height) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_resize(zbar_window_t *w) +{ + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_end(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} diff --git a/zbar/window/vfw.c b/zbar/window/vfw.c new file mode 100644 index 0000000..8c485cb --- /dev/null +++ b/zbar/window/vfw.c @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "window.h" + +#include <vfw.h> + +extern int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img); + +static int vfw_cleanup(zbar_window_t *w) +{ + if (w->hdd) { + DrawDibClose(w->hdd); + w->hdd = NULL; + } + return (0); +} + +static int vfw_init(zbar_window_t *w, zbar_image_t *img, int new_format) +{ + if (new_format) + _zbar_window_bih_init(w, img); + + w->dst_width = w->bih.biWidth = (img->width + 3) & ~3; + w->dst_height = w->bih.biHeight = img->height; + + HDC hdc = GetDC(w->hwnd); + if (!hdc) + return (-1 /*FIXME*/); + + if (!DrawDibBegin(w->hdd, hdc, w->width, w->height, &w->bih, img->width, + img->height, 0)) + return (-1 /*FIXME*/); + + ReleaseDC(w->hwnd, hdc); + return (0); +} + +static int vfw_draw(zbar_window_t *w, zbar_image_t *img) +{ + HDC hdc = GetDC(w->hwnd); + if (!hdc) + return (-1 /*FIXME*/); + + zprintf(24, "DrawDibDraw(%dx%d -> %dx%d)\n", img->width, img->height, + w->width, w->height); + + DrawDibDraw(w->hdd, hdc, 0, 0, w->width, w->height, &w->bih, + (void *)img->data, 0, 0, w->src_width, w->src_height, + DDF_SAME_DRAW); + + ValidateRect(w->hwnd, NULL); + ReleaseDC(w->hwnd, hdc); + return (0); +} + +static uint32_t vfw_formats[] = { fourcc('B', 'G', 'R', '3'), + fourcc('B', 'G', 'R', '4'), + fourcc('J', 'P', 'E', 'G'), 0 }; + +int _zbar_window_vfw_init(zbar_window_t *w) +{ + w->hdd = DrawDibOpen(); + if (!w->hdd) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "unable to initialize DrawDib")); + + uint32_t *fmt; + for (fmt = vfw_formats; *fmt; fmt++) + _zbar_window_add_format(w, *fmt); + + w->init = vfw_init; + w->draw_image = vfw_draw; + w->cleanup = vfw_cleanup; + return (0); +} diff --git a/zbar/window/win.c b/zbar/window/win.c new file mode 100644 index 0000000..458e1d9 --- /dev/null +++ b/zbar/window/win.c @@ -0,0 +1,321 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <ctype.h> + +#include "window.h" +#include "image.h" + +#include "win.h" + +int _zbar_window_vfw_init(zbar_window_t *w); +int _zbar_window_dib_init(zbar_window_t *w); + +int _zbar_window_resize(zbar_window_t *w) +{ + LOGBRUSH lb = { + 0, + }; + int x0, y0, by0, bh, i; + window_state_t *win = w->state; + int lbw; + + static const int bx[5] = { -6, -3, -1, 2, 5 }; + static const int bw[5] = { 1, 1, 2, 2, 1 }; + static const int zx[4] = { -7, 7, -7, 7 }; + static const int zy[4] = { -8, -8, 8, 8 }; + + if (w->height * 8 / 10 <= w->width) + lbw = w->height / 36; + else + lbw = w->width * 5 / 144; + if (lbw < 1) + lbw = 1; + win->logo_scale = lbw; + zprintf(7, "%dx%d scale=%d\n", w->width, w->height, lbw); + if (win->logo_zbars) { + DeleteObject(win->logo_zbars); + win->logo_zbars = NULL; + } + if (win->logo_zpen) + DeleteObject(win->logo_zpen); + if (win->logo_zbpen) + DeleteObject(win->logo_zbpen); + + lb.lbStyle = BS_SOLID; + lb.lbColor = RGB(0xd7, 0x33, 0x33); + win->logo_zpen = + ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, + lbw * 2, &lb, 0, NULL); + + lb.lbColor = RGB(0xa4, 0x00, 0x00); + win->logo_zbpen = + ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, + lbw * 2, &lb, 0, NULL); + + x0 = w->width / 2; + y0 = w->height / 2; + by0 = y0 - 54 * lbw / 5; + bh = 108 * lbw / 5; + + for (i = 0; i < 5; i++) { + int x = x0 + lbw * bx[i]; + HRGN bar = CreateRectRgn(x, by0, x + lbw * bw[i], by0 + bh); + if (win->logo_zbars) { + CombineRgn(win->logo_zbars, win->logo_zbars, bar, RGN_OR); + DeleteObject(bar); + } else + win->logo_zbars = bar; + } + + for (i = 0; i < 4; i++) { + win->logo_z[i].x = x0 + lbw * zx[i]; + win->logo_z[i].y = y0 + lbw * zy[i]; + } + return (0); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long unused) +{ + HDC hdc; + int height; + HFONT font; + TEXTMETRIC tm; + window_state_t *win = w->state; + if (w->display) { + /* FIXME cleanup existing resources */ + w->display = NULL; + } + + if (!display) { + if (win) { + free(win); + w->state = NULL; + } + return (0); + } + + if (!win) + win = w->state = calloc(1, sizeof(window_state_t)); + + w->display = display; + + win->bih.biSize = sizeof(win->bih); + win->bih.biPlanes = 1; + + hdc = GetDC(w->display); + if (!hdc) + return (-1 /*FIXME*/); + win->bih.biXPelsPerMeter = + 1000L * GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE); + win->bih.biYPelsPerMeter = + 1000L * GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE); + + height = -MulDiv(11, GetDeviceCaps(hdc, LOGPIXELSY), 96); + font = CreateFontW(height, 0, 0, 0, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, + FF_MODERN | FIXED_PITCH, NULL); + + SelectObject(hdc, font); + DeleteObject(font); + + GetTextMetrics(hdc, &tm); + win->font_height = tm.tmHeight; + + ReleaseDC(w->display, hdc); + + return (_zbar_window_dib_init(w)); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + HDC hdc = w->state->hdc = GetDC(w->display); + if (!hdc || !SaveDC(hdc)) + return (-1 /*FIXME*/); + return (0); +} + +int _zbar_window_end(zbar_window_t *w) +{ + HDC hdc = w->state->hdc; + w->state->hdc = NULL; + RestoreDC(hdc, -1); + ReleaseDC(w->display, hdc); + ValidateRect(w->display, NULL); + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + RECT r = { 0, 0, w->width, w->height }; + HDC hdc = GetDC(w->display); + if (!hdc) + return (-1 /*FIXME*/); + + FillRect(hdc, &r, GetStockObject(BLACK_BRUSH)); + + ReleaseDC(w->display, hdc); + ValidateRect(w->display, NULL); + return (0); +} + +static inline void win_set_rgb(HDC hdc, uint32_t rgb) +{ + SelectObject(hdc, GetStockObject(DC_PEN)); + SetDCPenColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + point_t org; + POINT *gdipts; + int i; + HDC hdc = w->state->hdc; + win_set_rgb(hdc, rgb); + + org = w->scaled_offset; + gdipts = (POINT *)calloc(npts + 1, sizeof(*gdipts)); + for (i = 0; i < npts; i++) { + point_t p = window_scale_pt(w, pts[i]); + gdipts[i].x = p.x + org.x; + gdipts[i].y = p.y + org.y; + } + gdipts[npts] = gdipts[0]; + + Polyline(hdc, gdipts, npts + 1); + free(gdipts); + return (0); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + HDC hdc = w->state->hdc; + static const DWORD npolys[3] = { 5, 2, 2 }; + POINT polys[9] = { + { p.x - 2, p.y - 2 }, { p.x - 2, p.y + 2 }, { p.x + 2, p.y + 2 }, + { p.x + 2, p.y - 2 }, { p.x - 2, p.y - 2 }, + + { p.x - 3, p.y }, { p.x + 4, p.y }, + + { p.x, p.y - 3 }, { p.x, p.y + 4 }, + }; + + win_set_rgb(hdc, rgb); + + PolyPolyline(hdc, polys, npolys, 3); + return (0); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + int n = 0; + HDC hdc = w->state->hdc; + SetTextColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); + SetBkMode(hdc, TRANSPARENT); + + while (n < 32 && text[n] && isprint(text[n])) + n++; + + if (p.x >= 0) + SetTextAlign(hdc, TA_BASELINE | TA_CENTER); + else { + SetTextAlign(hdc, TA_BASELINE | TA_RIGHT); + p.x += w->width; + } + + if (p.y < 0) + p.y = w->height + p.y * w->state->font_height * 5 / 4; + + TextOut(hdc, p.x, p.y, text, n); + return (0); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + RECT r = { org.x, org.y, org.x + size.x, org.y + size.y }; + HDC hdc = w->state->hdc; + SetDCBrushColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); + + FillRect(hdc, &r, GetStockObject(DC_BRUSH)); + + return (0); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + HDC hdc = w->state->hdc; + + window_state_t *win = w->state; + + /* FIXME buffer offscreen */ + HRGN rgn = CreateRectRgn(0, 0, w->width, w->height); + CombineRgn(rgn, rgn, win->logo_zbars, RGN_DIFF); + FillRgn(hdc, rgn, GetStockObject(WHITE_BRUSH)); + DeleteObject(rgn); + + FillRgn(hdc, win->logo_zbars, GetStockObject(BLACK_BRUSH)); + + SelectObject(hdc, win->logo_zpen); + Polyline(hdc, win->logo_z, 4); + + ExtSelectClipRgn(hdc, win->logo_zbars, RGN_AND); + SelectObject(hdc, win->logo_zbpen); + Polyline(hdc, win->logo_z, 4); + return (0); +} + +int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *win = w->state; + switch (w->format) { + case fourcc('J', 'P', 'E', 'G'): { + win->bih.biBitCount = 0; + win->bih.biCompression = BI_JPEG; + break; + } + case fourcc('B', 'G', 'R', '3'): { + win->bih.biBitCount = 24; + win->bih.biCompression = BI_RGB; + break; + } + case fourcc('B', 'G', 'R', '4'): { + win->bih.biBitCount = 32; + win->bih.biCompression = BI_RGB; + break; + } + default: + assert(0); + /* FIXME PNG? */ + } + win->bih.biSizeImage = img->datalen; + + zprintf(20, "biCompression=%d biBitCount=%d\n", (int)win->bih.biCompression, + win->bih.biBitCount); + + return (0); +} diff --git a/zbar/window/win.h b/zbar/window/win.h new file mode 100644 index 0000000..1daf65f --- /dev/null +++ b/zbar/window/win.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_WIN_H_ +#define _WINDOW_WIN_H_ + +#include <windows.h> + +struct window_state_s { + HDC hdc; + void *hdd; + + BITMAPINFOHEADER bih; + + /* pre-calculated logo geometries */ + int logo_scale; + HRGN logo_zbars; + HPEN logo_zpen, logo_zbpen; + POINT logo_z[4]; + + int font_height; +}; + +extern int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img); + +#endif diff --git a/zbar/window/x.c b/zbar/window/x.c new file mode 100644 index 0000000..f4284be --- /dev/null +++ b/zbar/window/x.c @@ -0,0 +1,338 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "x.h" +#include <ctype.h> +#include "image.h" +#include "window.h" + +#ifndef ZBAR_OVERLAY_FONT +#define ZBAR_OVERLAY_FONT "-*-fixed-medium-r-*-*-*-120-75-75-*-*-ISO8859-1" +#endif + +static inline unsigned long window_alloc_color(zbar_window_t *w, Colormap cmap, + unsigned short r, + unsigned short g, + unsigned short b) +{ + XColor color; + color.red = r; + color.green = g; + color.blue = b; + color.flags = 0; + XAllocColor(w->display, cmap, &color); + return (color.pixel); +} + +static inline int window_alloc_colors(zbar_window_t *w) +{ + window_state_t *x = w->state; + Colormap cmap = DefaultColormap(w->display, DefaultScreen(w->display)); + int i; + for (i = 0; i < 8; i++) + x->colors[i] = window_alloc_color(w, cmap, (i & 4) ? (0xcc * 0x101) : 0, + (i & 2) ? (0xcc * 0x101) : 0, + (i & 1) ? (0xcc * 0x101) : 0); + + x->logo_colors[0] = window_alloc_color(w, cmap, 0xd709, 0x3333, 0x3333); + x->logo_colors[1] = window_alloc_color(w, cmap, 0xa3d6, 0x0000, 0x0000); + return (0); +} + +static inline int window_hide_cursor(zbar_window_t *w) +{ + /* FIXME this seems lame...there must be a better way */ + Pixmap empty = XCreatePixmap(w->display, w->xwin, 1, 1, 1); + GC gc = XCreateGC(w->display, empty, 0, NULL); + XDrawPoint(w->display, empty, gc, 0, 0); + XColor black; + memset(&black, 0, sizeof(black)); + int screen = DefaultScreen(w->display); + black.pixel = BlackPixel(w->display, screen); + Cursor cursor = + XCreatePixmapCursor(w->display, empty, empty, &black, &black, 0, 0); + XDefineCursor(w->display, w->xwin, cursor); + XFreeCursor(w->display, cursor); + XFreeGC(w->display, gc); + XFreePixmap(w->display, empty); + return (0); +} + +int _zbar_window_resize(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (!x) + return (0); + + int lbw; + if (w->height * 8 / 10 <= w->width) + lbw = w->height / 36; + else + lbw = w->width * 5 / 144; + if (lbw < 1) + lbw = 1; + x->logo_scale = lbw; + if (x->logo_zbars) + XDestroyRegion(x->logo_zbars); + x->logo_zbars = XCreateRegion(); + + int x0 = w->width / 2; + int y0 = w->height / 2; + int by0 = y0 - 54 * lbw / 5; + int bh = 108 * lbw / 5; + + static const int bx[5] = { -6, -3, -1, 2, 5 }; + static const int bw[5] = { 1, 1, 2, 2, 1 }; + + int i; + for (i = 0; i < 5; i++) { + XRectangle *bar = &x->logo_bars[i]; + bar->x = x0 + lbw * bx[i]; + bar->y = by0; + bar->width = lbw * bw[i]; + bar->height = bh; + XUnionRectWithRegion(bar, x->logo_zbars, x->logo_zbars); + } + + static const int zx[4] = { -7, 7, -7, 7 }; + static const int zy[4] = { -8, -8, 8, 8 }; + + for (i = 0; i < 4; i++) { + x->logo_z[i].x = x0 + lbw * zx[i]; + x->logo_z[i].y = y0 + lbw * zy[i]; + } + return (0); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long win) +{ + window_state_t *x = w->state; + if (w->display) { + /* cleanup existing resources */ + if (x->gc) + XFreeGC(w->display, x->gc); + assert(!x->exposed); + if (x->font) { + XFreeFont(w->display, x->font); + x->font = NULL; + } + if (x->logo_zbars) { + XDestroyRegion(x->logo_zbars); + x->logo_zbars = NULL; + } + if (x->exposed) { + XDestroyRegion(x->exposed); + x->exposed = NULL; + } + w->display = NULL; + } + w->xwin = 0; + + if (!display || !win) { + if (x) { + free(x); + w->state = NULL; + } + return (0); + } + + if (!x) + x = w->state = calloc(1, sizeof(window_state_t)); + + w->display = display; + w->xwin = win; + x->gc = XCreateGC(display, win, 0, NULL); + + XWindowAttributes attr; + XGetWindowAttributes(w->display, w->xwin, &attr); + w->width = attr.width; + w->height = attr.height; + _zbar_window_resize(w); + + window_alloc_colors(w); + window_hide_cursor(w); + + /* load overlay font */ + x->font = XLoadQueryFont(w->display, ZBAR_OVERLAY_FONT); + if (x->font) + XSetFont(w->display, x->gc, x->font->fid); + + /* FIXME add interface preference override */ +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H + if (!_zbar_window_probe_xv(w)) + return (0); +#endif + + zprintf(1, "falling back to XImage\n"); + return (_zbar_window_probe_ximage(w)); +} + +int _zbar_window_expose(zbar_window_t *w, int x, int y, int width, int height) +{ + window_state_t *xs = w->state; + if (!xs->exposed) + xs->exposed = XCreateRegion(); + XRectangle r; + r.x = x; + r.y = y; + r.width = width; + r.height = height; + XUnionRectWithRegion(&r, xs->exposed, xs->exposed); + return (0); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + window_state_t *xs = w->state; + if (xs->exposed) + XSetRegion(w->display, xs->gc, xs->exposed); + + return (0); +} + +int _zbar_window_end(zbar_window_t *w) +{ + window_state_t *x = w->state; + XSetClipMask(w->display, x->gc, None); + if (x->exposed) { + XDestroyRegion(x->exposed); + x->exposed = NULL; + } + XFlush(w->display); + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + if (!w->display) + return (0); + window_state_t *x = w->state; + int screen = DefaultScreen(w->display); + XSetForeground(w->display, x->gc, WhitePixel(w->display, screen)); + XFillRectangle(w->display, w->xwin, x->gc, 0, 0, w->width, w->height); + return (0); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + + point_t org = w->scaled_offset; + XPoint xpts[npts + 1]; + int i; + for (i = 0; i < npts; i++) { + point_t p = window_scale_pt(w, pts[i]); + xpts[i].x = p.x + org.x; + xpts[i].y = p.y + org.y; + } + xpts[npts] = xpts[0]; + + XDrawLines(w->display, w->xwin, xs->gc, xpts, npts + 1, CoordModeOrigin); + + return (0); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + XDrawRectangle(w->display, w->xwin, xs->gc, p.x - 2, p.y - 2, 4, 4); + XDrawLine(w->display, w->xwin, xs->gc, p.x, p.y - 3, p.x, p.y + 3); + XDrawLine(w->display, w->xwin, xs->gc, p.x - 3, p.y, p.x + 3, p.y); + return (0); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + window_state_t *xs = w->state; + if (!xs->font) + return (-1); + + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + + int n = 0; + while (n < 32 && text[n] && isprint(text[n])) + n++; + + int width = XTextWidth(xs->font, text, n); + if (p.x >= 0) + p.x -= width / 2; + else + p.x += w->width - width; + + int dy = xs->font->ascent + xs->font->descent; + if (p.y >= 0) + p.y -= dy / 2; + else + p.y = w->height + p.y * dy * 5 / 4; + + XDrawString(w->display, w->xwin, xs->gc, p.x, p.y, text, n); + return (0); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + XFillRectangle(w->display, w->xwin, xs->gc, org.x, org.y, size.x, size.y); + return (0); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + window_state_t *x = w->state; + int screen = DefaultScreen(w->display); + + /* clear to white */ + XSetForeground(w->display, x->gc, WhitePixel(w->display, screen)); + XFillRectangle(w->display, w->xwin, x->gc, 0, 0, w->width, w->height); + + if (!x->logo_scale || !x->logo_zbars) + return (0); + + XSetForeground(w->display, x->gc, BlackPixel(w->display, screen)); + XFillRectangles(w->display, w->xwin, x->gc, x->logo_bars, 5); + + XSetLineAttributes(w->display, x->gc, 2 * x->logo_scale, LineSolid, + CapRound, JoinRound); + + XSetForeground(w->display, x->gc, x->logo_colors[0]); + XDrawLines(w->display, w->xwin, x->gc, x->logo_z, 4, CoordModeOrigin); + + if (x->exposed) { + XIntersectRegion(x->logo_zbars, x->exposed, x->exposed); + XSetRegion(w->display, x->gc, x->exposed); + } else + XSetRegion(w->display, x->gc, x->logo_zbars); + + XSetForeground(w->display, x->gc, x->logo_colors[1]); + XDrawLines(w->display, w->xwin, x->gc, x->logo_z, 4, CoordModeOrigin); + + /* reset GC */ + XSetLineAttributes(w->display, x->gc, 0, LineSolid, CapButt, JoinMiter); + return (0); +} diff --git a/zbar/window/x.h b/zbar/window/x.h new file mode 100644 index 0000000..dd7a2a7 --- /dev/null +++ b/zbar/window/x.h @@ -0,0 +1,74 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_X_H_ +#define _WINDOW_X_H_ + +#include "window.h" + +#ifdef HAVE_X +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#ifdef HAVE_X11_EXTENSIONS_XSHM_H +#include <X11/extensions/XShm.h> +#endif +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H +#include <X11/extensions/Xvlib.h> +#endif +#endif + +struct window_state_s { + unsigned long colors[8]; /* pre-allocated colors */ + + GC gc; /* graphics context */ + Region exposed; /* region to redraw */ + XFontStruct *font; /* overlay font */ + + /* pre-calculated logo geometries */ + int logo_scale; + unsigned long logo_colors[2]; + Region logo_zbars; + XPoint logo_z[4]; + XRectangle logo_bars[5]; + +#ifdef HAVE_X11_EXTENSIONS_XSHM_H + XShmSegmentInfo shm; /* shared memory segment */ +#endif + + union { + XImage *x; +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H + XvImage *xv; +#endif + } img; + + XID img_port; /* current format port */ + XID *xv_ports; /* best port for format */ + int num_xv_adaptors; /* number of adaptors */ + XID *xv_adaptors; /* port grabbed for each adaptor */ +}; + +extern int _zbar_window_probe_ximage(zbar_window_t *); +extern int _zbar_window_probe_xshm(zbar_window_t *); +extern int _zbar_window_probe_xv(zbar_window_t *); + +#endif diff --git a/zbar/window/ximage.c b/zbar/window/ximage.c new file mode 100644 index 0000000..17ee7e4 --- /dev/null +++ b/zbar/window/ximage.c @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "image.h" +#include "window.h" +#include "x.h" + +static int ximage_cleanup(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (x->img.x) + free(x->img.x); + x->img.x = NULL; + return (0); +} + +static inline int ximage_init(zbar_window_t *w, zbar_image_t *img, + int format_change) +{ + ximage_cleanup(w); + XImage *ximg = w->state->img.x = calloc(1, sizeof(XImage)); + ximg->width = img->width; + ximg->height = img->height; + ximg->format = ZPixmap; + ximg->byte_order = LSBFirst; + ximg->bitmap_unit = 8; + ximg->bitmap_bit_order = MSBFirst; + ximg->bitmap_pad = 8; + + const zbar_format_def_t *fmt = _zbar_format_lookup(w->format); + if (fmt->group == ZBAR_FMT_RGB_PACKED) { + ximg->depth = ximg->bits_per_pixel = fmt->p.rgb.bpp << 3; + ximg->red_mask = (0xff >> RGB_SIZE(fmt->p.rgb.red)) + << RGB_OFFSET(fmt->p.rgb.red); + ximg->green_mask = (0xff >> RGB_SIZE(fmt->p.rgb.green)) + << RGB_OFFSET(fmt->p.rgb.green); + ximg->blue_mask = (0xff >> RGB_SIZE(fmt->p.rgb.blue)) + << RGB_OFFSET(fmt->p.rgb.blue); + } else { + ximg->depth = ximg->bits_per_pixel = 8; + ximg->red_mask = ximg->green_mask = ximg->blue_mask = 0xff; + } + + if (!XInitImage(ximg)) + return (err_capture_int(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to init XImage for format %x", + w->format)); + + w->dst_width = img->width; + w->dst_height = img->height; + + /* FIXME implement some basic scaling */ + w->scale_num = w->scale_den = 1; + + zprintf(3, + "new XImage %.4s(%08" PRIx32 ") %dx%d" + " from %.4s(%08" PRIx32 ") %dx%d\n", + (char *)&w->format, w->format, ximg->width, ximg->height, + (char *)&img->format, img->format, img->width, img->height); + zprintf(4, " masks: %08lx %08lx %08lx\n", ximg->red_mask, + ximg->green_mask, ximg->blue_mask); + return (0); +} + +static int ximage_draw(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *x = w->state; + XImage *ximg = x->img.x; + assert(ximg); + ximg->data = (void *)img->data; + + point_t src = { 0, 0 }; + point_t dst = w->scaled_offset; + if (dst.x < 0) { + src.x = -dst.x; + dst.x = 0; + } + if (dst.y < 0) { + src.y = -dst.y; + dst.y = 0; + } + point_t size = w->scaled_size; + if (size.x > w->width) + size.x = w->width; + if (size.y > w->height) + size.y = w->height; + + XPutImage(w->display, w->xwin, x->gc, ximg, src.x, src.y, dst.x, dst.y, + size.x, size.y); + ximg->data = NULL; + return (0); +} + +static uint32_t ximage_formats[4][5] = { + { /* 8bpp */ + /* FIXME fourcc('Y','8','0','0'), */ + fourcc('R', 'G', 'B', '1'), fourcc('B', 'G', 'R', '1'), 0 }, + { /* 16bpp */ + fourcc('R', 'G', 'B', 'P'), fourcc('R', 'G', 'B', 'O'), + fourcc('R', 'G', 'B', 'R'), fourcc('R', 'G', 'B', 'Q'), 0 }, + { /* 24bpp */ + fourcc('R', 'G', 'B', '3'), fourcc('B', 'G', 'R', '3'), 0 }, + { /* 32bpp */ + fourcc('R', 'G', 'B', '4'), fourcc('B', 'G', 'R', '4'), 0 }, +}; + +static int ximage_probe_format(zbar_window_t *w, uint32_t format) +{ + const zbar_format_def_t *fmt = _zbar_format_lookup(format); + + XVisualInfo visreq, *visuals = NULL; + memset(&visreq, 0, sizeof(XVisualInfo)); + + visreq.depth = fmt->p.rgb.bpp << 3; + visreq.red_mask = (0xff >> RGB_SIZE(fmt->p.rgb.red)) + << RGB_OFFSET(fmt->p.rgb.red); + visreq.green_mask = (0xff >> RGB_SIZE(fmt->p.rgb.green)) + << RGB_OFFSET(fmt->p.rgb.green); + visreq.blue_mask = (0xff >> RGB_SIZE(fmt->p.rgb.blue)) + << RGB_OFFSET(fmt->p.rgb.blue); + + int n; + visuals = XGetVisualInfo(w->display, + VisualDepthMask | VisualRedMaskMask | + VisualGreenMaskMask | VisualBlueMaskMask, + &visreq, &n); + + zprintf(8, "bits=%d r=%08lx g=%08lx b=%08lx: n=%d visuals=%p\n", + visreq.depth, visreq.red_mask, visreq.green_mask, visreq.blue_mask, + n, visuals); + if (!visuals) + return (1); + XFree(visuals); + if (!n) + return (-1); + + return (0); +} + +int _zbar_window_probe_ximage(zbar_window_t *w) +{ + /* FIXME determine supported formats/depths */ + int n; + XPixmapFormatValues *formats = XListPixmapFormats(w->display, &n); + if (!formats) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to query XImage formats")); + + int i; + for (i = 0; i < n; i++) { + if (formats[i].depth & 0x7 || formats[i].depth > 0x20) { + zprintf(2, " [%d] depth=%d bpp=%d: not supported\n", i, + formats[i].depth, formats[i].bits_per_pixel); + continue; + } + int fmtidx = formats[i].depth / 8 - 1; + int j, n = 0; + for (j = 0; ximage_formats[fmtidx][j]; j++) + if (!ximage_probe_format(w, ximage_formats[fmtidx][j])) { + zprintf(2, " [%d] depth=%d bpp=%d: %.4s(%08" PRIx32 ")\n", i, + formats[i].depth, formats[i].bits_per_pixel, + (char *)&ximage_formats[fmtidx][j], + ximage_formats[fmtidx][j]); + _zbar_window_add_format(w, ximage_formats[fmtidx][j]); + n++; + } + if (!n) + zprintf(2, " [%d] depth=%d bpp=%d: no visuals\n", i, + formats[i].depth, formats[i].bits_per_pixel); + } + XFree(formats); + + if (!w->formats || !w->formats[0]) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no usable XImage formats found")); + + w->init = ximage_init; + w->draw_image = ximage_draw; + w->cleanup = ximage_cleanup; + return (0); +} diff --git a/zbar/window/xv.c b/zbar/window/xv.c new file mode 100644 index 0000000..f332800 --- /dev/null +++ b/zbar/window/xv.c @@ -0,0 +1,264 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <string.h> /* strcmp */ +#include "image.h" +#include "window.h" +#include "x.h" + +static int xv_cleanup(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (x->img.xv) { + XFree(x->img.xv); + x->img.xv = NULL; + } + int i; + for (i = 0; i < x->num_xv_adaptors; i++) + if (x->xv_adaptors[i]) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + free(x->xv_ports); + free(x->xv_adaptors); + x->xv_ports = NULL; + x->num_xv_adaptors = 0; + x->xv_adaptors = NULL; + return (0); +} + +static inline int xv_init(zbar_window_t *w, zbar_image_t *img, + int format_change) +{ + window_state_t *x = w->state; + if (x->img.xv) { + XFree(x->img.xv); + x->img.xv = NULL; + } + if (format_change) { + /* lookup port for format */ + x->img_port = 0; + int i; + for (i = 0; w->formats[i]; i++) + if (w->formats[i] == w->format) { + x->img_port = x->xv_ports[i]; + break; + } + assert(x->img_port > 0); + } + + XvImage *xvimg = XvCreateImage(w->display, x->img_port, w->format, NULL, + img->width, img->height); + zprintf(3, + "new XvImage %.4s(%08" PRIx32 ") %dx%d(%d)" + " from %.4s(%08" PRIx32 ") %dx%d\n", + (char *)&w->format, w->format, xvimg->width, xvimg->height, + xvimg->pitches[0], (char *)&img->format, img->format, img->width, + img->height); + + /* FIXME not sure this simple check is always correct + * should lookup format to decode/sanitize target width from pitch & bpp + */ + w->dst_width = + ((xvimg->num_planes <= 1) ? xvimg->width : xvimg->pitches[0]); + w->dst_height = xvimg->height; + + /* FIXME datalen check */ + if (w->dst_width < img->width || xvimg->height < img->height) { + XFree(xvimg); + /* FIXME fallback to XImage... */ + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "output image size mismatch (XvCreateImage)")); + } + x->img.xv = xvimg; + + return (0); +} + +static int xv_draw(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *x = w->state; + XvImage *xvimg = x->img.xv; + assert(xvimg); + xvimg->data = (void *)img->data; + zprintf(24, "XvPutImage(%dx%d -> %dx%d (%08lx))\n", w->src_width, + w->src_height, w->scaled_size.x, w->scaled_size.y, img->datalen); + XvPutImage(w->display, x->img_port, w->xwin, x->gc, xvimg, 0, 0, + w->src_width, w->src_height, w->scaled_offset.x, + w->scaled_offset.y, w->scaled_size.x, w->scaled_size.y); + xvimg->data = NULL; /* FIXME hold shm image until completion */ + return (0); +} + +static inline int xv_add_format(zbar_window_t *w, uint32_t fmt, XvPortID port) +{ + int i = _zbar_window_add_format(w, fmt); + + window_state_t *x = w->state; + if (!w->formats[i + 1]) + x->xv_ports = realloc(x->xv_ports, (i + 1) * sizeof(*x->xv_ports)); + + /* FIXME could prioritize by something (rate? size?) */ + x->xv_ports[i] = port; + return (i); +} + +static int xv_probe_port(zbar_window_t *w, XvPortID port) +{ + unsigned n; + XvEncodingInfo *encodings = NULL; + if (XvQueryEncodings(w->display, port, &n, &encodings)) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "querying XVideo encodings")); + + zprintf(1, "probing port %u with %d encodings:\n", (unsigned)port, n); + unsigned width = 0, height = 0; + int i; + for (i = 0; i < n; i++) { + XvEncodingInfo *enc = &encodings[i]; + zprintf(2, " [%d] %lu x %lu rate=%d/%d : %s\n", i, enc->width, + enc->height, enc->rate.numerator, enc->rate.denominator, + enc->name); + if (!strcmp(enc->name, "XV_IMAGE")) { + if (width < enc->width) + width = enc->width; + if (height < enc->height) + height = enc->height; + } + } + XvFreeEncodingInfo(encodings); + encodings = NULL; + + if (!width || !height) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "no XV_IMAGE encodings found")); + zprintf(1, "max XV_IMAGE size %dx%d\n", width, height); + if (w->max_width > width) + w->max_width = width; + if (w->max_height > height) + w->max_height = height; + + XvImageFormatValues *formats = + XvListImageFormats(w->display, port, (int *)&n); + if (!formats) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "querying XVideo image formats")); + + zprintf(1, "%d image formats\n", n); + for (i = 0; i < n; i++) { + XvImageFormatValues *fmt = &formats[i]; + zprintf(2, " [%d] %.4s(%08x) %s %s %s planes=%d bpp=%d : %.16s\n", i, + (char *)&fmt->id, fmt->id, (fmt->type == XvRGB) ? "RGB" : "YUV", + (fmt->byte_order == LSBFirst) ? "LSBFirst" : "MSBFirst", + (fmt->format == XvPacked) ? "packed" : "planar", + fmt->num_planes, fmt->bits_per_pixel, fmt->guid); + xv_add_format(w, fmt->id, port); + } + XFree(formats); + return (0); +} + +int _zbar_window_probe_xv(zbar_window_t *w) +{ + unsigned xv_major, xv_minor, xv_req, xv_ev, xv_err; + if (XvQueryExtension(w->display, &xv_major, &xv_minor, &xv_req, &xv_ev, + &xv_err)) { + zprintf(1, "XVideo extension not present\n"); + return (-1); + } + zprintf(1, "XVideo extension version %u.%u\n", xv_major, xv_minor); + + unsigned n; + XvAdaptorInfo *adaptors = NULL; + if (XvQueryAdaptors(w->display, w->xwin, &n, &adaptors)) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to query XVideo adaptors")); + + window_state_t *x = w->state; + x->num_xv_adaptors = 0; + x->xv_adaptors = calloc(n, sizeof(*x->xv_adaptors)); + int i; + for (i = 0; i < n; i++) { + XvAdaptorInfo *adapt = &adaptors[i]; + zprintf(2, " adaptor[%d] %lu ports %u-%u type=0x%x fmts=%lu : %s\n", + i, adapt->num_ports, (unsigned)adapt->base_id, + (unsigned)(adapt->base_id + adapt->num_ports - 1), adapt->type, + adapt->num_formats, adapt->name); + if (!(adapt->type & XvImageMask)) + continue; + + int j; + for (j = 0; j < adapt->num_ports; j++) + if (!XvGrabPort(w->display, adapt->base_id + j, CurrentTime)) { + zprintf(3, " grabbed port %u\n", + (unsigned)(adapt->base_id + j)); + x->xv_adaptors[x->num_xv_adaptors++] = adapt->base_id + j; + break; + } + + if (j == adapt->num_ports) + zprintf(3, " no available XVideo image port\n"); + } + XvFreeAdaptorInfo(adaptors); + adaptors = NULL; + + if (!x->num_xv_adaptors) { + zprintf(1, "WARNING: no XVideo adaptor supporting XvImages found\n"); + free(x->xv_adaptors); + x->xv_adaptors = NULL; + return (-1); + } + if (x->num_xv_adaptors < n) + x->xv_adaptors = + realloc(x->xv_adaptors, x->num_xv_adaptors * sizeof(int)); + + w->max_width = w->max_height = 65536; + w->formats = realloc(w->formats, sizeof(uint32_t)); + w->formats[0] = 0; + for (i = 0; i < x->num_xv_adaptors; i++) + if (xv_probe_port(w, x->xv_adaptors[i])) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + if (!w->formats[0] || w->max_width == 65536 || w->max_height == 65536) { + xv_cleanup(w); + return (-1); + } + + /* clean out any unused adaptors */ + for (i = 0; i < x->num_xv_adaptors; i++) { + int j; + for (j = 0; w->formats[j]; j++) + if (x->xv_ports[j] == x->xv_adaptors[i]) + break; + if (!w->formats[j]) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + } + + w->init = xv_init; + w->draw_image = xv_draw; + w->cleanup = xv_cleanup; + return (0); +} diff --git a/zbarcam/Makefile.am.inc b/zbarcam/Makefile.am.inc new file mode 100644 index 0000000..df6794d --- /dev/null +++ b/zbarcam/Makefile.am.inc @@ -0,0 +1,44 @@ +bin_PROGRAMS += zbarcam/zbarcam +zbarcam_zbarcam_SOURCES = zbarcam/zbarcam.c +zbarcam_zbarcam_LDADD = zbar/libzbar.la +zbarcam_zbarcam_CPPFLAGS = $(AM_CPPFLAGS) +# automake bug in "monolithic mode"? +CLEANFILES += zbarcam/.libs/zbarcam zbarcam/moc_zbarcam_qt.h + +if HAVE_GTK +if !WIN32 +bin_PROGRAMS += zbarcam/zbarcam-gtk +zbarcam_zbarcam_gtk_SOURCES = zbarcam/zbarcam-gtk.c zbarcam/scan_video.c +zbarcam_zbarcam_gtk_CPPFLAGS = $(GTK_CFLAGS) $(AM_CPPFLAGS) +zbarcam_zbarcam_gtk_LDADD = $(GTK_LIBS) gtk/libzbargtk.la zbar/libzbar.la \ + $(AM_LDADD) + +endif +endif + +if HAVE_QT +bin_PROGRAMS += zbarcam/zbarcam-qt + +zbarcam_zbarcam_qt_SOURCES = zbarcam/zbarcam-qt.cpp zbarcam/scan_video.c +nodist_zbarcam_zbarcam_qt_SOURCES = zbarcam/moc_zbarcam_qt.h +zbarcam_zbarcam_qt_CPPFLAGS = -Izbarcam $(QT_CFLAGS) $(AM_CPPFLAGS) +zbarcam_zbarcam_qt_LDADD = $(QT_LIBS) qt/libzbarqt.la $(AM_LDADD) +BUILT_SOURCES += $(nodist_zbarcam_zbarcam_qt_SOURCES) +DISTCLEANFILES += $(nodist_zbarcam_zbarcam_qt_SOURCES) zbarcam/moc_zbarcam_qt.h + + +zbarcam/moc_zbarcam_qt.h: zbarcam/zbarcam-qt.cpp + $(mkdir_p) zbarcam + $(MOC) -i $(zbarcam_zbarcam_qt_CPPFLAGS) $< -o $@ +endif + + +if WIN32 +zbarcam_zbarcam_SOURCES += zbarcam/zbarcam.rc +zbarcam_zbarcam_LDADD += zbarcam/zbarcam-rc.o @LTLIBINTL@ + +if WITH_DIRECTSHOW +zbarcam_zbarcam_CPPFLAGS += -DDIRECTSHOW +endif + +endif diff --git a/zbarcam/scan_video.c b/zbarcam/scan_video.c new file mode 100644 index 0000000..c83e53e --- /dev/null +++ b/zbarcam/scan_video.c @@ -0,0 +1,227 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" + +#include <fcntl.h> +#include <ftw.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/videodev2.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#ifdef MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#endif +typedef void(cb_t)(void *userdata, const char *device); + +struct devnodes { + char *fname; + int minor; + int is_valid; +}; + +static unsigned int n_devices = 0; +static struct devnodes *devices = NULL; + +/* + * Sort order: + * + * - Valid devices comes first + * - Lowest minors comes first + * + * For devnode names, it sorts on this order: + * - custom udev given names + * - /dev/v4l/by-id/ + * - /dev/v4l/by-path/ + * - /dev/video + * - /dev/char/ + * + * - Device name is sorted alphabetically if follows same pattern + */ +static int sort_devices(const void *__a, const void *__b) +{ + const struct devnodes *a = __a; + const struct devnodes *b = __b; + int val_a, val_b; + + if (a->is_valid != b->is_valid) + return !a->is_valid - !b->is_valid; + + if (a->minor != b->minor) + return a->minor - b->minor; + + /* Ensure that /dev/video* devices will stay at the top */ + + if (strstr(a->fname, "by-id")) + val_a = 1; + if (strstr(a->fname, "by-path")) + val_a = 2; + else if (strstr(a->fname, "/dev/video")) + val_a = 3; + else if (strstr(a->fname, "char")) + val_a = 4; + else /* Customized names comes first */ + val_a = 0; + + if (strstr(b->fname, "by-id")) + val_b = 1; + if (strstr(b->fname, "by-path")) + val_b = 2; + else if (strstr(b->fname, "/dev/video")) + val_b = 3; + else if (strstr(b->fname, "char")) + val_b = 4; + else /* Customized names comes first */ + val_b = 0; + + if (val_a != val_b) + return val_a - val_b; + + /* Finally, just use alphabetic order */ + return strcmp(a->fname, b->fname); +} + +static int handle_video_devs(const char *file, const struct stat *st, int flag) +{ + int dev_minor, first_device = -1, fd; + unsigned int i; + struct v4l2_capability vid_cap = { 0 }; + + /* Discard devices that can't be a videodev */ + if (!S_ISCHR(st->st_mode) || major(st->st_rdev) != 81) + return 0; + + dev_minor = minor(st->st_rdev); + + /* check if it is an already existing device */ + if (devices) { + for (i = 0; i < n_devices; i++) { + if (dev_minor == devices[i].minor) { + first_device = i; + break; + } + } + } + + devices = realloc(devices, (n_devices + 1) * sizeof(struct devnodes)); + if (!devices) { + perror("Can't allocate memory to store devices"); + exit(1); + } + memset(&devices[n_devices], 0, sizeof(struct devnodes)); + + if (first_device < 0) { + fd = open(file, O_RDWR); + if (fd < 0) { + devices[n_devices].is_valid = 0; + } else { + if (ioctl(fd, VIDIOC_QUERYCAP, &vid_cap) == -1) { + devices[n_devices].is_valid = 0; + } else { +#ifdef V4L2_CID_ALPHA_COMPONENT + /* + * device_caps was added on Kernel 3.3. The preferred + * way to handle such compat stuff would be to include + * a recent videodev2.h at ZBar's source and check the + * V4L2 API returned by VIDIOC_QUERYCAP. + * However, doing that require some care, as other + * compat code should be checked to see if they would work. + * Also, it is painful to keep updating the Kernel headers. + * Thankfully, V4L2_CID_ALPHA_COMPONENT was also added on + * Kernel 3.3, so just checking if this is defined should + * be enough to do the right thing. + */ + if (!(vid_cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) + devices[n_devices].is_valid = 0; + else + devices[n_devices].is_valid = 1; +#else + if (!(vid_cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) + devices[n_devices].is_valid = 0; + else + devices[n_devices].is_valid = 1; +#endif + } + } + + close(fd); + } else { + devices[n_devices].is_valid = devices[first_device].is_valid; + } + + devices[n_devices].fname = strdup(file); + devices[n_devices].minor = dev_minor; + + n_devices++; + + return (0); +} + +/* scan /dev for v4l video devices and call add_device for each. + * also looks for a specified "default" device (if not NULL) + * if not found, the default will be appended to the list. + * returns the index+1 of the default_device, or 0 if the default + * was not specified. NB *not* reentrant + */ +int scan_video(cb_t add_dev, void *userdata, const char *default_dev) +{ + unsigned int i, idx = 0; + int default_idx = -1, last_minor = -1; + + if (ftw("/dev", handle_video_devs, 4)) { + perror("search for video devices failed"); + return -1; + } + qsort(devices, n_devices, sizeof(struct devnodes), sort_devices); + + for (i = 0; i < n_devices; i++) { + if (!devices[i].is_valid) + continue; + + if (devices[i].minor == last_minor) + continue; + + add_dev(userdata, devices[i].fname); + last_minor = devices[i].minor; + idx++; + + if (default_dev && !strcmp(default_dev, devices[i].fname)) + default_idx = idx; + else if (!default_dev && default_idx < 0) + default_idx = idx; + } + + for (i = 0; i < n_devices; i++) + free(devices[i].fname); + free(devices); + + n_devices = 0; + devices = NULL; + + return (default_idx); +} diff --git a/zbarcam/zbarcam-gtk.c b/zbarcam/zbarcam-gtk.c new file mode 100644 index 0000000..1c7930a --- /dev/null +++ b/zbarcam/zbarcam-gtk.c @@ -0,0 +1,231 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <gtk/gtk.h> +#include <zbar/zbargtk.h> + +static GtkWidget *window = NULL; +static GtkWidget *status_image = NULL; +static GtkTextView *results = NULL; +static gchar *open_file = NULL; + +int scan_video(void *add_device, void *userdata, const char *default_device); + +/* decode signal callback + * puts the decoded result in the textbox + */ +static void decoded(GtkWidget *widget, zbar_symbol_type_t symbol, + const char *result, gpointer data) +{ + GtkTextBuffer *resultbuf = gtk_text_view_get_buffer(results); + GtkTextIter end; + gtk_text_buffer_get_end_iter(resultbuf, &end); + gtk_text_buffer_insert(resultbuf, &end, zbar_get_symbol_name(symbol), -1); + gtk_text_buffer_insert(resultbuf, &end, ":", -1); + gtk_text_buffer_insert(resultbuf, &end, result, -1); + gtk_text_buffer_insert(resultbuf, &end, "\n", -1); + gtk_text_view_scroll_to_iter(results, &end, 0, FALSE, 0, 0); +} + +/* update button state when video state changes + */ +static void video_enabled(GObject *object, GParamSpec *param, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(object); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + gboolean opened = zbar_gtk_get_video_opened(zbar); + + GtkToggleButton *button = GTK_TOGGLE_BUTTON(data); + gboolean active = gtk_toggle_button_get_active(button); + if (active != (opened && enabled)) + gtk_toggle_button_set_active(button, enabled); +} + +static void video_opened(GObject *object, GParamSpec *param, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(object); + gboolean opened = zbar_gtk_get_video_opened(zbar); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), opened && enabled); + gtk_widget_set_sensitive(GTK_WIDGET(data), opened); +} + +/* (re)open the video when a new device is selected + */ +static void video_changed(GtkWidget *widget, gpointer data) +{ + const char *video_device = + gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget)); + zbar_gtk_set_video_device( + ZBAR_GTK(data), + ((video_device && video_device[0] != '<') ? video_device : NULL)); +} + +static void status_button_toggled(GtkToggleButton *button, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(data); + gboolean opened = zbar_gtk_get_video_opened(zbar); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + gboolean active = gtk_toggle_button_get_active(button); + if (opened && (active != enabled)) + zbar_gtk_set_video_enabled(ZBAR_GTK(data), active); + gtk_image_set_from_icon_name(GTK_IMAGE(status_image), + (opened && active) ? "gtk-yes" : "gtk-no", + GTK_ICON_SIZE_BUTTON); + gtk_button_set_label(GTK_BUTTON(button), (!opened) ? "closed" : + (active) ? "enabled" : + "disabled"); +} + +static void open_button_clicked(GtkButton *button, gpointer data) +{ + GtkWidget *dialog = + gtk_file_chooser_dialog_new("Open Image File", GTK_WINDOW(window), + GTK_FILE_CHOOSER_ACTION_OPEN, "gtk-cancel", + GTK_RESPONSE_CANCEL, "gtk-open", + GTK_RESPONSE_ACCEPT, NULL); + GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog); + if (open_file) + gtk_file_chooser_set_filename(chooser, open_file); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *file = gtk_file_chooser_get_filename(chooser); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(file, NULL); + if (pixbuf) + zbar_gtk_scan_image(ZBAR_GTK(data), pixbuf); + else + fprintf(stderr, "ERROR: unable to open image file: %s\n", file); + + if (open_file && file) + g_free(open_file); + open_file = file; + } + gtk_widget_destroy(dialog); +} + +/* build a simple gui w/: + * - a combo box to select the desired video device + * - the zbar widget to display video + * - a non-editable text box to display any results + */ +int main(int argc, char *argv[]) +{ + const char *video_arg = NULL; + + gdk_set_allowed_backends("x11,*"); + gtk_init(&argc, &argv); + + if (argc > 1) + video_arg = argv[1]; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(window), "test_gtk"); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), + NULL); + + GtkWidget *zbar = zbar_gtk_new(); + + g_signal_connect(G_OBJECT(zbar), "decoded", G_CALLBACK(decoded), NULL); + + /* video device list combo box */ + GtkWidget *video_list = gtk_combo_box_text_new(); + + g_signal_connect(G_OBJECT(video_list), "changed", G_CALLBACK(video_changed), + zbar); + + /* enable/disable status button */ + GtkWidget *status_button = gtk_toggle_button_new(); + status_image = gtk_image_new_from_icon_name("gtk-no", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image(GTK_BUTTON(status_button), status_image); + gtk_button_set_label(GTK_BUTTON(status_button), "closed"); + gtk_widget_set_sensitive(status_button, FALSE); + + /* bind status button state and video state */ + g_signal_connect(G_OBJECT(status_button), "toggled", + G_CALLBACK(status_button_toggled), zbar); + g_signal_connect(G_OBJECT(zbar), "notify::video-enabled", + G_CALLBACK(video_enabled), status_button); + g_signal_connect(G_OBJECT(zbar), "notify::video-opened", + G_CALLBACK(video_opened), status_button); + + /* open image file button */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *open_button = + gtk_button_new_from_icon_name("gtk-open", GTK_ICON_SIZE_BUTTON); +#else + GtkWidget *open_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); +#endif + + g_signal_connect(G_OBJECT(open_button), "clicked", + G_CALLBACK(open_button_clicked), zbar); + + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(video_list), "<none>"); + int active = + scan_video(gtk_combo_box_text_append_text, video_list, video_arg); + if (active >= 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(video_list), active); + + /* hbox for combo box and buttons */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); +#else + GtkWidget *hbox = gtk_hbox_new(FALSE, 8); +#endif + + gtk_box_pack_start(GTK_BOX(hbox), video_list, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), status_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), open_button, FALSE, FALSE, 0); + + /* text box for holding results */ + results = GTK_TEXT_VIEW(gtk_text_view_new()); + gtk_widget_set_size_request(GTK_WIDGET(results), 320, 64); + gtk_text_view_set_editable(results, FALSE); + gtk_text_view_set_cursor_visible(results, FALSE); + gtk_text_view_set_left_margin(results, 4); + + /* vbox for hbox, zbar test widget and result text box */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); +#else + GtkWidget *vbox = gtk_vbox_new(FALSE, 8); +#endif + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), zbar, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(results), FALSE, FALSE, 0); + + GdkGeometry hints; + hints.min_width = 320; + hints.min_height = 240; + gtk_window_set_geometry_hints(GTK_WINDOW(window), zbar, &hints, + GDK_HINT_MIN_SIZE); + + gtk_widget_show_all(window); + gtk_main(); + return (0); +} diff --git a/zbarcam/zbarcam-qt.cpp b/zbarcam/zbarcam-qt.cpp new file mode 100644 index 0000000..b98a32a --- /dev/null +++ b/zbarcam/zbarcam-qt.cpp @@ -0,0 +1,1050 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include <QApplication> +#include <QCheckBox> +#include <QComboBox> +#include <QCommandLineParser> +#include <QFileDialog> +#include <QImage> +#include <QLayout> +#include <QPushButton> +#include <QTextEdit> +#include <QWidget> +#include <QtGlobal> +#include <config.h> +#include <zbar.h> +#include <zbar/QZBar.h> + +#define TEST_IMAGE_FORMATS \ + "Image Files (*.png *.jpg *.jpeg *.bmp *.gif *.ppm *.pgm *.pbm *.tiff " \ + "*.xpm *.xbm)" + +#define SYM_GROUP "Symbology" +#define CAM_GROUP "Camera" +#define DBUS_NAME "D-Bus" +#define OPTION_BAR "option_bar.enable" +#define CONTROL_BAR "control_bar.enable" + +extern "C" { +int scan_video(void *add_device, void *userdata, const char *default_device); +} + +struct configs_s { + QString name; + zbar::zbar_symbol_type_t sym; +}; + +static const struct configs_s configs[] = { + { "Composite codes", zbar::ZBAR_COMPOSITE }, + { "Image Scanner", zbar::ZBAR_PARTIAL }, +#if ENABLE_CODABAR == 1 + { "Codabar", zbar::ZBAR_CODABAR }, +#endif +#if ENABLE_CODE128 == 1 + { "Code-128", zbar::ZBAR_CODE128 }, +#endif +#if ENABLE_I25 == 1 + { "Code 2 of 5 interlaced", zbar::ZBAR_I25 }, +#endif +#if ENABLE_CODE39 == 1 + { "Code-39", zbar::ZBAR_CODE39 }, +#endif +#if ENABLE_CODE93 == 1 + { "Code-93", zbar::ZBAR_CODE93 }, +#endif +#if ENABLE_DATABAR == 1 + { "DataBar", zbar::ZBAR_DATABAR }, + { "DataBar expanded", zbar::ZBAR_DATABAR_EXP }, +#endif +#if ENABLE_EAN == 1 + { "EAN-2", zbar::ZBAR_EAN2 }, + { "EAN-5", zbar::ZBAR_EAN5 }, + { "EAN-8", zbar::ZBAR_EAN8 }, + { "EAN-13", zbar::ZBAR_EAN13 }, + { "ISBN-10", zbar::ZBAR_ISBN10 }, + { "ISBN-13", zbar::ZBAR_ISBN13 }, + { "UPC-A", zbar::ZBAR_UPCA }, + { "UPC-E", zbar::ZBAR_UPCE }, +#endif +#if ENABLE_PDF417 == 1 + { "PDF417", zbar::ZBAR_PDF417 }, +#endif +#if ENABLE_QRCODE == 1 + { "QR code", zbar::ZBAR_QRCODE }, +#endif +#if ENABLE_SQCODE == 1 + { "SQ code", zbar::ZBAR_SQCODE }, +#endif +}; + +#define CONFIGS_SIZE (sizeof(configs) / sizeof(*configs)) + +struct settings_s { + QString name; + zbar::zbar_config_t ctrl; + bool is_bool; +}; + +static const struct settings_s settings[] = { + { "x-density", zbar::ZBAR_CFG_Y_DENSITY, false }, + { "y-density", zbar::ZBAR_CFG_Y_DENSITY, false }, + { "min-length", zbar::ZBAR_CFG_MIN_LEN, false }, + { "max-length", zbar::ZBAR_CFG_MAX_LEN, false }, + { "uncertainty", zbar::ZBAR_CFG_UNCERTAINTY, false }, + { "ascii", zbar::ZBAR_CFG_ASCII, true }, + { "binary", zbar::ZBAR_CFG_BINARY, true }, + { "add-check", zbar::ZBAR_CFG_ADD_CHECK, true }, + { "emit-check", zbar::ZBAR_CFG_EMIT_CHECK, true }, + { "position", zbar::ZBAR_CFG_POSITION, true }, + { "test-inverted", zbar::ZBAR_CFG_TEST_INVERTED, true }, +}; +#define SETTINGS_SIZE (sizeof(settings) / sizeof(*settings)) + +// Represents an integer control + +class IntegerControl : public QSpinBox +{ + Q_OBJECT + +private: + char *name; + zbar::QZBar *zbar; + +private slots: + void updateControl(int value); + +public: + IntegerControl(QGroupBox *parent, zbar::QZBar *_zbar, char *_name, int min, + int max, int def, int step) + : QSpinBox(parent) + { + int val; + + zbar = _zbar; + name = _name; + + setRange(min, max); + setSingleStep(step); + if (!zbar->get_control(name, &val)) + setValue(val); + else + setValue(def); + + connect(this, SIGNAL(valueChanged(int)), this, + SLOT(updateControl(int))); + } +}; + +void IntegerControl::updateControl(int value) +{ + zbar->set_control(name, value); +} + +// Represents a menu control +class MenuControl : public QComboBox +{ + Q_OBJECT + +private: + char *name; + zbar::QZBar *zbar; + QVector<QPair<int, QString> > vector; + +private slots: + void updateControl(int value); + +public: + MenuControl(QGroupBox *parent, zbar::QZBar *_zbar, char *_name, + QVector<QPair<int, QString> > _vector) + : QComboBox(parent) + { + int val; + + zbar = _zbar; + name = _name; + vector = _vector; + + if (zbar->get_control(name, &val)) + val = 0; + for (int i = 0; i < vector.size(); ++i) { + QPair<int, QString> pair = vector.at(i); + addItem(pair.second, pair.first); + + if (val == pair.first) + setCurrentIndex(i); + } + connect(this, SIGNAL(currentIndexChanged(int)), this, + SLOT(updateControl(int))); + } +}; + +void MenuControl::updateControl(int index) +{ + zbar->set_control(name, vector.at(index).first); +} + +class IntegerSetting : public QSpinBox +{ + Q_OBJECT + +public: + QString name; + + IntegerSetting(QString _name, int val = 0) : name(_name) + { + setValue(val); + } +}; + +class SettingsDialog : public QDialog +{ + Q_OBJECT + +private: + QVector<int> val; + zbar::QZBar *zbar; + zbar::zbar_symbol_type_t sym; + +private slots: + + void accept() + { + for (unsigned i = 0; i < SETTINGS_SIZE; i++) + zbar->set_config(sym, settings[i].ctrl, val[i]); + QDialog::accept(); + }; + void reject() + { + QDialog::reject(); + }; + void clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + if (settings[i].name == name) { + val[i] = button->isChecked(); + return; + } + } + // ERROR! + }; + void update(int value) + { + IntegerSetting *setting = qobject_cast<IntegerSetting *>(sender()); + if (!setting) + return; + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + if (settings[i].name == setting->name) { + val[i] = value; + return; + } + } + // ERROR! + }; + +public: + SettingsDialog(zbar::QZBar *_zbar, QString &name, + zbar::zbar_symbol_type_t _sym) + : zbar(_zbar), sym(_sym) + { + val = QVector<int>(SETTINGS_SIZE); + + QGridLayout *layout = new QGridLayout(this); + + this->setWindowTitle(name); + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + int value = 0; + + if (zbar->get_config(sym, settings[i].ctrl, value)) + continue; + val[i] = value; + + if (settings[i].is_bool) { + QCheckBox *button = new QCheckBox(settings[i].name, this); + + button->setChecked(value); + + layout->addWidget(button, i, 0, 1, 2, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(clicked())); + } else { + QLabel *label = new QLabel(settings[i].name); + + layout->addWidget(label, i, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + IntegerSetting *spin = + new IntegerSetting(settings[i].name, value); + layout->addWidget(spin, i, 1, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(spin, SIGNAL(valueChanged(int)), this, + SLOT(update(int))); + } + } + QDialogButtonBox *buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + layout->addWidget(buttonBox); + } +}; + +class SettingsButton : public QPushButton +{ + Q_OBJECT + +private: + QString name; + zbar::QZBar *zbar; + zbar::zbar_symbol_type_t sym; + +public: + SettingsButton(zbar::QZBar *_zbar, const QIcon &_icon, QString _name, + zbar::zbar_symbol_type_t _sym) + : QPushButton(_icon, ""), name(_name), zbar(_zbar), sym(_sym) + { + int size = font().pointSize(); + + if (size < 0) + size = font().pixelSize(); + + if (size > 0) + setIconSize(QSize(size, size)); + }; + +public Q_SLOTS: + void button_clicked() + { + SettingsButton *button = qobject_cast<SettingsButton *>(sender()); + if (!button) + return; + + QString name = button->name; + + SettingsDialog *dialog = new SettingsDialog(zbar, name, sym); + dialog->setModal(true); + dialog->show(); + } +}; + +struct CamRes { + unsigned width; + unsigned height; + float max_fps; +}; + +class ZbarcamQZBar : public QWidget +{ + Q_OBJECT + +protected: + static void add_device(QComboBox *list, const char *device) + { + list->addItem(QString(device)); + } + +public Q_SLOTS: + void turn_show_options() + { + QPushButton *button = qobject_cast<QPushButton *>(sender()); + if (!button) + return; + + show_options = !show_options; + if (show_options) { + button->setText("Hide Options"); + optionsGroup->show(); + } else { + button->setText("Show Options"); + optionsGroup->hide(); + } + } + + void turn_show_controls() + { + QPushButton *button = qobject_cast<QPushButton *>(sender()); + if (!button) + return; + + show_controls = !show_controls; + if (show_controls) { + button->setText("Hide Controls"); + controlGroup->show(); + } else { + button->setText("Show Controls"); + controlGroup->hide(); + } + } + +public: + ~ZbarcamQZBar() + { + saveSettings(); + } + ZbarcamQZBar(const QStringList *names, int verbose = 0) : resolutions(NULL) + { + // drop-down list of video devices + QComboBox *videoList = new QComboBox; + + // toggle button to disable/enable video + statusButton = new QPushButton; + + QStyle *style = QApplication::style(); + QIcon statusIcon = style->standardIcon(QStyle::SP_DialogNoButton); + QIcon yesIcon = style->standardIcon(QStyle::SP_DialogYesButton); + statusIcon.addPixmap(yesIcon.pixmap(QSize(128, 128), QIcon::Normal, + QIcon::On), + QIcon::Normal, QIcon::On); + + statusButton->setIcon(statusIcon); + statusButton->setText("&Enable"); + statusButton->setCheckable(true); + statusButton->setEnabled(false); + statusButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + // command button to open image files for scanning + QPushButton *openButton = new QPushButton("&Open"); + QIcon openIcon = style->standardIcon(QStyle::SP_DialogOpenButton); + openButton->setIcon(openIcon); + openButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + // collect video list and buttons horizontally + ZBarMenu = new QHBoxLayout; + ZBarMenu->setAlignment(Qt::AlignLeft); + ZBarMenu->addWidget(videoList, 5); + ZBarMenu->addWidget(statusButton, 1); + ZBarMenu->addWidget(openButton, 1); + + // video barcode scanner + zbar = new zbar::QZBar(NULL, verbose); + zbar->setAcceptDrops(true); + + // text box for results + QTextEdit *results = new QTextEdit; + results->setReadOnly(true); + + QGridLayout *grid = new QGridLayout; + grid->addLayout(ZBarMenu, 0, 0, 1, -1); + grid->addWidget(zbar, 1, 0, 1, 1); + grid->addWidget(results, 2, 0, 1, 1); + + // Group box where controls will be added + optionsGroup = new QGroupBox(tr("Options"), this); + QGridLayout *optionsBoxLayout = new QGridLayout(optionsGroup); + optionsGroup->setAlignment(Qt::AlignHCenter); + optionsBoxLayout->setContentsMargins(0, 0, 16, 0); + grid->addWidget(optionsGroup, 1, 1, -1, 1, Qt::AlignTop); + + controlGroup = new QGroupBox(this); + controlBoxLayout = new QGridLayout(controlGroup); + controlBoxLayout->setContentsMargins(0, 0, 0, 0); + grid->addWidget(controlGroup, 1, 2, -1, 1, Qt::AlignTop); + + loadSettings(); + zbar->request_size(curWidth, curHeight, false); + + int pos = 0; + +#ifdef HAVE_DBUS + QCheckBox *button = new QCheckBox(DBUS_NAME, this); + button->setChecked(dbus_enabled); + optionsBoxLayout->addWidget(button, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(code_clicked())); + zbar->request_dbus(0); +#endif + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + + if (configs[i].sym == zbar::ZBAR_PARTIAL) { + QLabel *label = new QLabel(configs[i].name, this); + optionsBoxLayout->addWidget(label, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + } else { + QCheckBox *button = new QCheckBox(configs[i].name, this); + + zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val); + + button->setChecked(val); + optionsBoxLayout->addWidget(button, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(code_clicked())); + } + + /* Composite doesn't have configuration */ + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + QIcon icon = QIcon::fromTheme(QLatin1String("configure-toolbars")); + SettingsButton *settings = + new SettingsButton(zbar, icon, configs[i].name, configs[i].sym); + optionsBoxLayout->addWidget(settings, pos, 1, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + + connect(settings, &SettingsButton::clicked, settings, + &SettingsButton::button_clicked); + } + + // Allow showing/hiding options/controls menus + QPushButton *showOptionsButton, *showControlsButton; + + if (show_options) { + showOptionsButton = new QPushButton("Hide Options"); + optionsGroup->show(); + } else { + showOptionsButton = new QPushButton("Show Options"); + optionsGroup->hide(); + } + showOptionsButton->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + ZBarMenu->addWidget(showOptionsButton); + connect(showOptionsButton, SIGNAL(clicked()), this, + SLOT(turn_show_options())); + + if (show_controls) { + showControlsButton = new QPushButton("Hide Controls"); + controlGroup->show(); + } else { + showControlsButton = new QPushButton("Show Controls"); + controlGroup->hide(); + } + showControlsButton->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + ZBarMenu->addWidget(showControlsButton); + connect(showControlsButton, SIGNAL(clicked()), this, + SLOT(turn_show_controls())); + + if (!geometry.isEmpty()) + restoreGeometry(geometry); + + setLayout(grid); + + videoList->addItem(""); + + int active = 0; + for (int i = 0; i < names->size(); i++) + active += scan_video((void *)add_device, videoList, + names->at(i).toUtf8()); + + if (names->isEmpty()) + active += scan_video((void *)add_device, videoList, NULL); + + // directly connect combo box change signal to scanner video open + connect(videoList, SIGNAL(currentIndexChanged(const QString &)), zbar, + SLOT(setVideoDevice(const QString &))); + + // directly connect status button state to video enabled state + connect(statusButton, SIGNAL(toggled(bool)), zbar, + SLOT(setVideoEnabled(bool))); + + // also update status button state when video is opened/closed + connect(zbar, SIGNAL(videoOpened(bool)), this, SLOT(setEnabled(bool))); + + // prompt for image file to scan when openButton is clicked + connect(openButton, SIGNAL(clicked()), SLOT(openImage())); + + // directly connect video scanner decode result to display in text box + connect(zbar, SIGNAL(decodedText(const QString &)), results, + SLOT(append(const QString &))); + + if (active >= 0) + videoList->setCurrentIndex(active); + } + +public Q_SLOTS: + void openImage() + { + file = QFileDialog::getOpenFileName(this, "Open Image", file, + TEST_IMAGE_FORMATS); + if (!file.isEmpty()) + zbar->scanImage(QImage(file)); + } + + void control_clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + bool val = button->isChecked(); + + zbar->set_control(name.toUtf8().data(), val); + } + + void code_clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + bool val = button->isChecked(); + + if (name == DBUS_NAME) { + zbar->request_dbus(val); + dbus_enabled = val; + return; + } + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + if (configs[i].name == name) { + zbar->set_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val); + return; + } + } + } + + void clearLayout(QLayout *layout) + { + QLayoutItem *item; + while ((item = layout->takeAt(0))) { + if (item->layout()) { + clearLayout(item->layout()); + delete item->layout(); + } + if (item->widget()) { + delete item->widget(); + } + delete item; + } + } + + void setVideoResolution(int index) + { + struct CamRes *cur_res; + + if (index < 0 || res.isEmpty()) + return; + + cur_res = &res[index]; + + unsigned width = zbar->videoWidth(); + unsigned height = zbar->videoHeight(); + + if (width == cur_res->width && height == cur_res->height) + return; + + zbar->request_size(cur_res->width, cur_res->height); + + curWidth = cur_res->width; + curHeight = cur_res->height; + } + + void setEnabled(bool videoEnabled) + { + zbar->setVideoEnabled(videoEnabled); + + // Update the status button + statusButton->setEnabled(videoEnabled); + statusButton->setChecked(videoEnabled); + + // Delete items before creating a new set of controls + clearLayout(controlBoxLayout); + + if (!videoEnabled) + return; + + // get_controls + loadSettings(false); + + // FIXME: clear a previous resolutions box + + bool isNewResolutions = false; + + if (!resolutions) { + resolutions = new QComboBox; + resolutions->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + isNewResolutions = true; + } + + resolutions->blockSignals(true); + res.clear(); + resolutions->clear(); + + for (int i = 0;; i++) { + QString new_res, fps; + struct CamRes cur_res; + + if (!zbar->get_resolution(i, cur_res.width, cur_res.height, + cur_res.max_fps)) + break; + + fps.setNum(cur_res.max_fps, 'f', 2); + new_res = QString("%1x%2 - %3 fps (max)") + .arg(cur_res.width) + .arg(cur_res.height) + .arg(fps); + + resolutions->addItem(new_res); + res.append(cur_res); + + if (curWidth == cur_res.width && curHeight == cur_res.height) + resolutions->setCurrentIndex(i); + } + + if (isNewResolutions) { + ZBarMenu->addWidget(resolutions); + connect(resolutions, SIGNAL(currentIndexChanged(int)), this, + SLOT(setVideoResolution(int))); + } + resolutions->blockSignals(false); + + // Restore saved resolution + unsigned width = zbar->videoWidth(); + unsigned height = zbar->videoHeight(); + + if (width != curWidth || height != curHeight) { + for (int i = 0; i < res.size(); i++) { + if (res[i].width == curWidth && res[i].height == curHeight) { + resolutions->setCurrentIndex(i); + break; + } + } + } + + int pos = 0; + QString oldGroup = ""; + for (int i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + QString newGroup = + "<strong>" + QString::fromUtf8(group) + " Controls</strong>"; + + if (newGroup != oldGroup) { + if (oldGroup != "") + controlBoxLayout->addItem(new QSpacerItem(0, 12), pos++, 2, + 1, 2, Qt::AlignLeft); + QLabel *label = new QLabel(newGroup); + controlBoxLayout->addWidget(label, pos++, 0, 1, 2, + Qt::AlignTop | Qt::AlignHCenter); + pos++; + oldGroup = newGroup; + } + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: { + bool val; + + QCheckBox *button = new QCheckBox(name, controlGroup); + controlBoxLayout->addWidget(button, pos++, 0, 1, 2, + Qt::AlignLeft); + + if (!zbar->get_control(name, &val)) + button->setChecked(val); + else + button->setChecked(def); + connect(button, SIGNAL(clicked()), this, + SLOT(control_clicked())); + break; + } + case zbar::QZBar::Integer: { + IntegerControl *ctrl; + + QLabel *label = new QLabel(QString::fromUtf8(name)); + ctrl = new IntegerControl(controlGroup, zbar, name, min, max, + def, step); + + controlBoxLayout->addWidget(label, pos, 0, Qt::AlignLeft); + controlBoxLayout->addWidget(ctrl, pos++, 1, Qt::AlignLeft); + break; + } + case zbar::QZBar::Menu: { + MenuControl *ctrl; + + QLabel *label = new QLabel(QString::fromUtf8(name)); + + QVector<QPair<int, QString> > vector; + vector = zbar->get_menu(i); + ctrl = new MenuControl(controlGroup, zbar, name, vector); + + controlBoxLayout->addWidget(label, pos, 0, Qt::AlignLeft); + controlBoxLayout->addWidget(ctrl, pos++, 1, Qt::AlignLeft); + break; + } + default: + // Just ignore other types + break; + } + } + } + +private: + QString file; + zbar::QZBar *zbar; + QHBoxLayout *ZBarMenu; + QPushButton *statusButton; + QGroupBox *controlGroup, *optionsGroup; + QComboBox *resolutions; + QGridLayout *controlBoxLayout; + QSignalMapper *signalMapper; + bool dbus_enabled, show_options, show_controls; + QByteArray geometry; + QVector<struct CamRes> res; + unsigned curWidth, curHeight; + + void loadSettings(bool getRes = true) + { + QSettings qSettings(QCoreApplication::organizationName(), + QCoreApplication::applicationName()); + QString key; + QVariant qVal; + + geometry = qSettings.value("geometry").toByteArray(); + + key = OPTION_BAR; + qVal = qSettings.value(key, true); + show_options = qVal.toBool(); + + key = CONTROL_BAR; + qVal = qSettings.value(key, true); + show_controls = qVal.toBool(); + + if (getRes) { + qVal = qSettings.value("width"); + curWidth = qVal.toUInt(); + qVal = qSettings.value("height"); + curHeight = qVal.toUInt(); + } + +#ifdef HAVE_DBUS + key = DBUS_NAME ".enable"; + qVal = qSettings.value(key, false); + dbus_enabled = qVal.toBool(); +#endif + + qSettings.beginGroup(SYM_GROUP); + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + if (zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val)) + continue; + key = QString(configs[i].name) + QString(".enable"); + key.replace(" ", "_"); + qVal = qSettings.value(key, val); + zbar->set_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, + qVal.toInt()); + + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + for (unsigned j = 0; j < SETTINGS_SIZE; j++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, settings[j].ctrl, val)) + continue; + key = QString(configs[i].name) + QString(".") + + QString(settings[j].name); + key.replace(" ", "_"); + + qVal = qSettings.value(key, val); + zbar->set_config(configs[i].sym, settings[j].ctrl, + qVal.toInt()); + } + } + qSettings.endGroup(); + + qSettings.beginGroup(CAM_GROUP); + for (unsigned i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step, val; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: + case zbar::QZBar::Menu: + case zbar::QZBar::Integer: { + key = QString::fromUtf8(name); + + if (zbar->get_control(name, &val)) + continue; + + key.replace(QRegularExpression("[^\\w\\d]+"), "_"); + key.replace(QRegularExpression("_$"), ""); + + qVal = qSettings.value(key, val); + zbar->set_control(name, qVal.toInt()); + break; + } + default: + // Just ignore other types + break; + } + } + qSettings.endGroup(); + } + + void saveSettings() + { + QSettings qSettings(QCoreApplication::organizationName(), + QCoreApplication::applicationName()); + QString key; + unsigned int i; + + qSettings.setValue("geometry", saveGeometry()); + + key = OPTION_BAR; + qSettings.setValue(key, show_options); + + key = CONTROL_BAR; + qSettings.setValue(key, show_controls); + + curWidth = zbar->videoWidth(); + curHeight = zbar->videoHeight(); + qSettings.setValue("width", curWidth); + qSettings.setValue("height", curHeight); + +#ifdef HAVE_DBUS + // FIXME: track dbus enable-disable and store last state + key = DBUS_NAME ".enable"; + qSettings.setValue(key, dbus_enabled); +#endif + + qSettings.beginGroup(SYM_GROUP); + for (i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val)) + continue; + key = QString(configs[i].name) + QString(".enable"); + key.replace(" ", "_"); + qSettings.setValue(key, val); + + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + for (unsigned j = 0; j < SETTINGS_SIZE; j++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, settings[j].ctrl, val)) + continue; + key = QString(configs[i].name) + QString(".") + + QString(settings[j].name); + key.replace(" ", "_"); + qSettings.setValue(key, val); + } + } + qSettings.endGroup(); + + for (i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step, val; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + if (i == 0) + qSettings.beginGroup(CAM_GROUP); + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: + case zbar::QZBar::Menu: + case zbar::QZBar::Integer: { + key = QString::fromUtf8(name); + + if (zbar->get_control(name, &val)) + continue; + + key.replace(QRegularExpression("[^\\w\\d]+"), "_"); + key.replace(QRegularExpression("_$"), ""); + qSettings.setValue(key, val); + break; + } + default: + // Just ignore other types + break; + } + } + if (i > 0) + qSettings.endGroup(); + } +}; + +#include "moc_zbarcam_qt.h" + +int main(int argc, char *argv[]) +{ + int verbose = 0; + QApplication app(argc, argv); + app.setApplicationName("zbarcam_qt"); + app.setOrganizationName("LinuxTV"); + app.setOrganizationDomain("linuxtv.org"); + app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + + QCommandLineParser parser; + parser.setApplicationDescription("ZBar bar code reader Qt application"); + parser.addHelpOption(); + + parser.addPositionalArgument("name", QObject::tr("device or file name")); + + QCommandLineOption debugOption(QStringList() << "d" + << "debug", + QObject::tr("Enable debug mode.")); + parser.addOption(debugOption); + + QCommandLineOption verboseOption(QStringList() << "v" + << "verbosity", + QObject::tr("Verbosity level."), + QObject::tr("value")); + parser.addOption(verboseOption); + + parser.process(app); + + if (parser.isSet(verboseOption)) + verbose = parser.value(verboseOption).toInt(); + + if (parser.isSet(debugOption)) + verbose = 127; + + const QStringList args = parser.positionalArguments(); + + ZbarcamQZBar window(&args, verbose); + window.show(); + return (app.exec()); +} diff --git a/zbarcam/zbarcam.c b/zbarcam/zbarcam.c new file mode 100644 index 0000000..040c030 --- /dev/null +++ b/zbarcam/zbarcam.c @@ -0,0 +1,375 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <fcntl.h> +#include <io.h> +#include <objbase.h> +#endif +#include <assert.h> + +#include <zbar.h> + +#ifdef ENABLE_NLS +#include <libintl.h> +#include <locale.h> +#define _(string) gettext(string) +#else +#define _(string) string +#endif + +#define N_(string) string + +#define BELL "\a" + +static const char *note_usage = N_( + "usage: zbarcam [options] [/dev/video?]\n" + "\n" + "scan and decode bar codes from a video stream\n" + "\n" + "options:\n" + " -h, --help display this help text\n" + " --version display version information and exit\n" + " -q, --quiet disable beep when symbol is decoded\n" + " -v, --verbose increase debug output level\n" + " --verbose=N set specific debug output level\n" + " --xml use XML output format\n" + " --raw output decoded symbol data without converting charsets\n" + " -1, --oneshot exit after scanning one bar code\n" + " --nodisplay disable video display window\n" + " --prescale=<W>x<H>\n" + " request alternate video image size from driver\n" + " -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" + " set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" + /* FIXME overlay level */ + "\n"); + +#ifdef HAVE_DBUS +static const char *note_usage2 = + N_(" --nodbus disable dbus message\n"); +#endif + +static const char *xml_head = + "<barcodes xmlns='http://zbar.sourceforge.net/2008/barcode'>" + "<source device='%s'>\n"; +static const char *xml_foot = "</source></barcodes>\n"; + +static zbar_processor_t *proc; +static int quiet = 0, oneshot = 0; +static enum +{ + DEFAULT, + RAW, + XML +} format = DEFAULT; + +static char *xml_buf = NULL; +static unsigned xml_len = 0; + +static int usage(int rc) +{ + FILE *out = (rc) ? stderr : stdout; + fprintf(out, "%s", _(note_usage)); +#ifdef HAVE_DBUS + fprintf(out, "%s", _(note_usage2)); +#endif + return (rc); +} + +static inline int parse_config(const char *cfgstr, int i, int n, char *arg) +{ + if (i >= n || !*cfgstr) { + fprintf(stderr, "ERROR: need argument for option: %s\n", arg); + return (1); + } + + if (zbar_processor_parse_config(proc, cfgstr)) { + fprintf(stderr, "ERROR: invalid configuration setting: %s\n", cfgstr); + return (1); + } + return (0); +} + +static void data_handler(zbar_image_t *img, const void *userdata) +{ + int n = 0; + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + assert(sym); + for (; sym; sym = zbar_symbol_next(sym)) { + zbar_symbol_type_t type; + if (zbar_symbol_get_count(sym)) + continue; + + type = zbar_symbol_get_type(sym); + if (type == ZBAR_PARTIAL) + continue; + + 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("<index num='%u'>\n", zbar_image_get_sequence(img)); + zbar_symbol_xml(sym, &xml_buf, &xml_len); + if (fwrite(xml_buf, xml_len, 1, stdout) != 1) + continue; + } + n++; + + if (oneshot) { + if (format != RAW) + printf("\n"); + break; + } else + printf("\n"); + } + + if (format == XML && n) + printf("</index>\n"); + fflush(stdout); + + if (!quiet && n) + fprintf(stderr, BELL); +} + +int main(int argc, const char *argv[]) +{ + const char *video_device; + int display; + unsigned long infmt, outfmt; + int i, active; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + +#ifdef DIRECTSHOW + HRESULT res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(res)) { + fprintf(stderr, "ERROR: failed to initialize COM library\n"); + return (1); + } +#endif + + /* setup zbar library standalone processor, + * threads will be used if available + */ + proc = zbar_processor_create(1); + if (!proc) { + fprintf(stderr, "ERROR: unable to allocate memory?\n"); + return (1); + } + zbar_processor_set_data_handler(proc, data_handler, NULL); + + video_device = ""; +#ifdef HAVE_DBUS + int dbus = 1; +#endif + display = 1; + infmt = 0, outfmt = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') + video_device = argv[i]; + else if (argv[i][1] != '-') { + int j; + for (j = 1; argv[i][j]; j++) { + if (argv[i][j] == 'S') { + if (!argv[i][++j]) { + i++; + j = 0; + } + if (parse_config(&argv[i][j], i, argc, "-S")) + return (usage(1)); + break; + } + switch (argv[i][j]) { + case 'h': + return (usage(0)); + case 'v': + zbar_increase_verbosity(); + break; + case 'q': + quiet = 1; + break; + case '1': + oneshot = 1; + break; + default: + fprintf(stderr, "ERROR: unknown bundled config: -%c\n\n", + argv[i][j]); + return (usage(1)); + } + } + } else if (!argv[i][2]) { + if (i < argc - 1) + video_device = argv[argc - 1]; + break; + } else if (!strcmp(argv[i], "--help")) + return (usage(0)); + else if (!strcmp(argv[i], "--version")) + return (printf(PACKAGE_VERSION "\n") <= 0); + else if (!strcmp(argv[i], "--set")) { + i++; + if (parse_config(argv[i], i, argc, "--set")) + return (usage(1)); + } else if (!strncmp(argv[i], "--set=", 6)) { + if (parse_config(&argv[i][6], i, argc, "--set=")) + return (usage(1)); + } else if (!strcmp(argv[i], "--quiet")) + quiet = 1; + else if (!strcmp(argv[i], "--oneshot")) + oneshot = 1; + else if (!strcmp(argv[i], "--xml")) + format = XML; + else if (!strcmp(argv[i], "--raw")) + format = RAW; + else if (!strcmp(argv[i], "--nodbus")) +#ifdef HAVE_DBUS + dbus = 0; +#else + ; /* silently ignore the option */ +#endif + else if (!strcmp(argv[i], "--nodisplay")) + display = 0; + else if (!strcmp(argv[i], "--verbose")) + zbar_increase_verbosity(); + else if (!strncmp(argv[i], "--verbose=", 10)) + zbar_set_verbosity(strtol(argv[i] + 10, NULL, 0)); + else if (!strncmp(argv[i], "--prescale=", 11)) { + char *x = NULL; + long int w = strtol(argv[i] + 11, &x, 10); + long int h = 0; + if (x && *x == 'x') + h = strtol(x + 1, NULL, 10); + if (!w || !h || !x || *x != 'x') { + fprintf(stderr, "ERROR: invalid prescale: %s\n\n", argv[i]); + return (usage(1)); + } + zbar_processor_request_size(proc, w, h); + } else if (!strncmp(argv[i], "--v4l=", 6)) { + long int v = strtol(argv[i] + 6, NULL, 0); + zbar_processor_request_interface(proc, v); + } else if (!strncmp(argv[i], "--iomode=", 9)) { + long int v = strtol(argv[i] + 9, NULL, 0); + zbar_processor_request_iomode(proc, v); + } else if (!strncmp(argv[i], "--infmt=", 8) && strlen(argv[i]) == 12) + infmt = (argv[i][8] | (argv[i][9] << 8) | (argv[i][10] << 16) | + (argv[i][11] << 24)); + else if (!strncmp(argv[i], "--outfmt=", 9) && strlen(argv[i]) == 13) + outfmt = (argv[i][9] | (argv[i][10] << 8) | (argv[i][11] << 16) | + (argv[i][12] << 24)); + else { + fprintf(stderr, "ERROR: unknown option argument: %s\n\n", argv[i]); + return (usage(1)); + } + } + + if (infmt || outfmt) + zbar_processor_force_format(proc, infmt, outfmt); + +#ifdef HAVE_DBUS + zbar_processor_request_dbus(proc, dbus); +#endif + + /* open video device, open window */ + if (zbar_processor_init(proc, video_device, display) || + /* show window */ + (display && zbar_processor_set_visible(proc, 1))) + return (zbar_processor_error_spew(proc, 0)); + +#ifdef _WIN32 + if (format == XML || format == RAW) { + fflush(stdout); + if (_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "ERROR: failed to set stdout mode: %i\n", errno); + return (1); + } + } +#endif + + if (format == XML) { + printf(xml_head, video_device); + fflush(stdout); + } + + /* start video */ + active = 1; + if (zbar_processor_set_active(proc, active)) + return (zbar_processor_error_spew(proc, 0)); + + if (oneshot) { + if (zbar_process_one(proc, -1) < 0) + if (zbar_processor_get_error_code(proc) != ZBAR_ERR_CLOSED) + return zbar_processor_error_spew(proc, 0); + } else { + /* let the callback handle data */ + int rc; + while ((rc = zbar_processor_user_wait(proc, -1)) >= 0) { + if (rc == 'q' || rc == 'Q') + break; + // HACK: controls are known on V4L2 by ID, not by name. This is also + // not compatible with other platforms + if (rc == 'b' || rc == 'B') { + int value; + zbar_processor_get_control(proc, "Brightness", &value); + zbar_processor_set_control(proc, "Brightness", ++value); + } + if (rc == 'n' || rc == 'N') { + int value; + zbar_processor_get_control(proc, "Brightness", &value); + zbar_processor_set_control(proc, "Brightness", --value); + } + if (rc == ' ') { + active = !active; + if (zbar_processor_set_active(proc, active)) + return (zbar_processor_error_spew(proc, 0)); + } + } + + /* report any errors that aren't "window closed" */ + if (rc && rc != 'q' && rc != 'Q' && + zbar_processor_get_error_code(proc) != ZBAR_ERR_CLOSED) + return (zbar_processor_error_spew(proc, 0)); + } + + /* free resources (leak check) */ + zbar_processor_destroy(proc); + + if (format == XML) { + printf("%s", xml_foot); + fflush(stdout); + } + return (0); +} diff --git a/zbarcam/zbarcam.rc b/zbarcam/zbarcam.rc new file mode 100644 index 0000000..f5e0800 --- /dev/null +++ b/zbarcam/zbarcam.rc @@ -0,0 +1,30 @@ +#include <config.h> +#include <winver.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + PRODUCTVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ZBar Bar Code Reader" + VALUE "Company Name", "ZBar Bar Code Reader" + VALUE "InternalName", "zbarcam" + VALUE "OriginalFilename", "zbarcam.exe" + + VALUE "FileVersion", PACKAGE_VERSION + VALUE "ProductVersion", PACKAGE_VERSION + + VALUE "FileDescription", "Scan bar codes from video devices" + + VALUE "LegalCopyright", "Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>" + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 0x04e4 + } +} + +APP_ICON ICON "zbar.ico" diff --git a/zbarimg/Makefile.am.inc b/zbarimg/Makefile.am.inc new file mode 100644 index 0000000..0f201a6 --- /dev/null +++ b/zbarimg/Makefile.am.inc @@ -0,0 +1,13 @@ +bin_PROGRAMS += zbarimg/zbarimg +zbarimg_zbarimg_SOURCES = zbarimg/zbarimg.c +zbarimg_zbarimg_CPPFLAGS = $(MAGICK_CFLAGS) $(AM_CPPFLAGS) +zbarimg_zbarimg_LDADD = zbar/libzbar.la $(MAGICK_LIBS) +# automake bug in "monolithic mode"? +CLEANFILES += zbarimg/.libs/zbarimg + +if WIN32 +zbarimg_zbarimg_SOURCES += zbarimg/zbarimg.rc +zbarimg_zbarimg_LDADD += zbarimg/zbarimg-rc.o @LTLIBINTL@ +endif + +EXTRA_DIST += test/barcodetest.py diff --git a/zbarimg/zbarimg.c b/zbarimg/zbarimg.c new file mode 100644 index 0000000..e82aca9 --- /dev/null +++ b/zbarimg/zbarimg.c @@ -0,0 +1,555 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif +#ifdef _WIN32 +#include <fcntl.h> +#include <io.h> +#endif +#include <assert.h> + +#include <zbar.h> + +#ifdef ENABLE_NLS +#include <libintl.h> +#include <locale.h> +#define _(string) gettext(string) +#else +#define _(string) string +#endif + +#define N_(string) string + +#ifdef HAVE_GRAPHICSMAGICK +#include <wand/wand_api.h> +#endif + +#ifdef HAVE_IMAGEMAGICK +#ifdef HAVE_IMAGEMAGICK7 +#include <MagickWand/MagickWand.h> +#else +#include <wand/MagickWand.h> +#endif + +/* ImageMagick frequently changes API names - just use the original + * (more stable?) names to match GraphicsMagick + */ +#define InitializeMagick(f) MagickWandGenesis() +#define DestroyMagick MagickWandTerminus +#define MagickSetImageIndex MagickSetIteratorIndex + +/* in 6.4.5.4 MagickGetImagePixels changed to MagickExportImagePixels. + * (still not sure this check is quite right... + * how does MagickGetAuthenticImagePixels fit in?) + * ref http://bugs.gentoo.org/247292 + */ +#if MagickLibVersion > 0x645 +#define MagickGetImagePixels MagickExportImagePixels +#endif +#endif + +static const char *note_usage = N_( + "usage: zbarimg [options] <image>...\n" + "\n" + "scan and decode bar codes from one or more image files\n" + "\n" + "options:\n" + " -h, --help display this help text\n" + " --version display version information and exit\n" + " --polygon output points delimiting code zone with decoded symbol data\n" + " -q, --quiet minimal output, only print decoded symbol data\n" + " -v, --verbose increase debug output level\n" + " --verbose=N set specific debug output level\n" + " -d, --display enable display of following images to the screen\n" + " -D, --nodisplay disable display of following images (default)\n" + " --xml, --noxml enable/disable XML output format\n" + " --raw output decoded symbol data without converting charsets\n" + " -1, --oneshot exit after scanning one bar code\n" + " -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" + " set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" + // FIXME overlay level + "\n"); + +#ifdef HAVE_DBUS +static const char *note_usage2 = + N_(" --nodbus disable dbus message\n"); +#endif + +static const char *warning_not_found_head = N_( + "\n" + "WARNING: barcode data was not detected in some image(s)\n" + "Things to check:\n" + " - is the barcode type supported? Currently supported symbologies are:\n"); + +static const char *warning_not_found_tail = N_( + " - is the barcode large enough in the image?\n" + " - is the barcode mostly in focus?\n" + " - is there sufficient contrast/illumination?\n" + " - If the symbol is split in several barcodes, are they combined in one " + "image?\n" + " - Did you enable the barcode type?\n" + " some EAN/UPC codes are disabled by default. To enable all, use:\n" + " $ zbarimg -S*.enable <files>\n" + " Please also notice that some variants take precedence over others.\n" + " Due to that, if you want, for example, ISBN-10, you should do:\n" + " $ zbarimg -Sisbn10.enable <files>\n" + "\n"); + +static const char *xml_head = + "<barcodes xmlns='http://zbar.sourceforge.net/2008/barcode'>\n"; +static const char *xml_foot = "</barcodes>\n"; + +static int notfound = 0, exit_code = 0; +static int num_images = 0, num_symbols = 0; +static int xmllvl = 0; +static int polygon = 0; +static int oneshot = 0; +static int binary = 0; + +char *xmlbuf = NULL; +unsigned xmlbuflen = 0; + +static zbar_processor_t *processor = NULL; + +static inline int dump_error(MagickWand *wand) +{ + char *desc; + ExceptionType severity; + desc = MagickGetException(wand, &severity); + + if (severity >= FatalErrorException) + exit_code = 2; + else if (severity >= ErrorException) + exit_code = 1; + else + exit_code = 0; + + static const char *sevdesc[] = { "WARNING", "ERROR", "FATAL" }; + fprintf(stderr, "%s: %s\n", sevdesc[exit_code], desc); + + MagickRelinquishMemory(desc); + return (exit_code); +} + +static int scan_image(const char *filename) +{ + if (exit_code == 3) + return (-1); + + int found = 0; + MagickWand *images = NewMagickWand(); + + // default is a measly 72dpi for pdf + MagickSetResolution(images, 900, 900); + + if (!MagickReadImage(images, filename) && dump_error(images)) + return (-1); + + unsigned seq, n = MagickGetNumberImages(images); + for (seq = 0; seq < n; seq++) { + if (exit_code == 3) + return (-1); + + if (!MagickSetImageIndex(images, seq) && dump_error(images)) + return (-1); + + zbar_image_t *zimage = zbar_image_create(); + assert(zimage); + zbar_image_set_format(zimage, zbar_fourcc('Y', '8', '0', '0')); + + int width = MagickGetImageWidth(images); + int height = MagickGetImageHeight(images); + zbar_image_set_size(zimage, width, height); + + // extract grayscale image pixels + // FIXME color!! ...preserve most color w/422P + // (but only if it's a color image) + size_t bloblen = width * height; + unsigned char *blob = malloc(bloblen); + zbar_image_set_data(zimage, blob, bloblen, zbar_image_free_data); + + if (!MagickGetImagePixels(images, 0, 0, width, height, "I", CharPixel, + blob)) + return (-1); + + if (xmllvl == 1) { + xmllvl++; + printf("<source href='%s'>\n", filename); + } + + zbar_process_image(processor, zimage); + + // output result data + 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 <= 0) { + if (!xmllvl) + printf("%s:", zbar_get_symbol_name(typ)); + if (polygon) { + int p; + if (zbar_symbol_get_loc_size(sym) > 0) + printf("%+d,%+d", zbar_symbol_get_loc_x(sym,0), zbar_symbol_get_loc_y(sym,0)); + for (p = 1; p < zbar_symbol_get_loc_size(sym); p++) + printf(" %+d,%+d", zbar_symbol_get_loc_x(sym,p), zbar_symbol_get_loc_y(sym,p)); + printf(":"); + } + if (len && + fwrite(zbar_symbol_get_data(sym), len, 1, stdout) != 1) { + exit_code = 1; + return (-1); + } + } else { + if (xmllvl < 3) { + xmllvl++; + printf("<index num='%u'>\n", seq); + } + zbar_symbol_xml(sym, &xmlbuf, &xmlbuflen); + if (fwrite(xmlbuf, xmlbuflen, 1, stdout) != 1) { + exit_code = 1; + return (-1); + } + } + found++; + num_symbols++; + + if (!binary) { + if (oneshot) { + if (xmllvl >= 0) + printf("\n"); + break; + } else + printf("\n"); + } + } + if (xmllvl > 2) { + xmllvl--; + printf("</index>\n"); + } + fflush(stdout); + + zbar_image_destroy(zimage); + + num_images++; + if (zbar_processor_is_visible(processor)) { + int rc = zbar_processor_user_wait(processor, -1); + if (rc < 0 || rc == 'q' || rc == 'Q') + exit_code = 3; + } + } + + if (xmllvl > 1) { + xmllvl--; + printf("</source>\n"); + } + + if (!found) + notfound++; + + DestroyMagickWand(images); + return (0); +} + +int usage(int rc, const char *msg, const char *arg) +{ + FILE *out = (rc) ? stderr : stdout; + if (msg) { + fprintf(out, "%s", msg); + if (arg) + fprintf(out, "%s", arg); + fprintf(out, "\n\n"); + } + fprintf(out, "%s", _(note_usage)); +#ifdef HAVE_DBUS + fprintf(out, "%s", _(note_usage2)); +#endif + return (rc); +} + +static inline int parse_config(const char *cfgstr, const char *arg) +{ + if (!cfgstr || !cfgstr[0]) + return (usage(1, "ERROR: need argument for option: ", arg)); + + if (zbar_processor_parse_config(processor, cfgstr)) + return (usage(1, "ERROR: invalid configuration setting: ", cfgstr)); + + if (!strcmp(cfgstr, "binary")) + binary = 1; + + return (0); +} + +int main(int argc, const char *argv[]) +{ + // option pre-scan + int quiet = 0; +#ifdef HAVE_DBUS + int dbus = 1; +#endif + int display = 0; + int i, j; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-' || !arg[1]) + // first pass, skip images + num_images++; + else if (arg[1] != '-') + for (j = 1; arg[j]; j++) { + if (arg[j] == 'S') { + if (!arg[++j] && ++i >= argc) + /* FIXME parse check */ + return (parse_config("", "-S")); + break; + } + switch (arg[j]) { + case 'h': + return (usage(0, NULL, NULL)); + case 'q': + quiet = 1; + break; + case '1': + oneshot = 1; + break; + case 'v': + zbar_increase_verbosity(); + break; + case 'd': + display = 1; + break; + case 'D': + break; + default: + return ( + usage(1, "ERROR: unknown bundled option: -", arg + j)); + } + } + else if (!strcmp(arg, "--help")) + return (usage(0, NULL, NULL)); + else if (!strcmp(arg, "--version")) { + printf("%s\n", PACKAGE_VERSION); + return (0); + } else if (!strcmp(arg, "--quiet")) { + quiet = 1; + argv[i] = NULL; + } else if (!strcmp(arg, "--oneshot")) + oneshot = 1; + else if (!strcmp(arg, "--verbose")) + zbar_increase_verbosity(); + else if (!strncmp(arg, "--verbose=", 10)) + zbar_set_verbosity(strtol(argv[i] + 10, NULL, 0)); + else if (!strcmp(arg, "--nodbus")) +#ifdef HAVE_DBUS + dbus = 0; +#else + ; /* silently ignore the option */ +#endif + else if (!strcmp(arg, "--display")) + display++; + else if (!strcmp(arg, "--xml")) { + if (xmllvl >= 0) + xmllvl = 1; + } else if (!strcmp(arg, "--noxml")) { + if (xmllvl > 0) + xmllvl = 0; + } else if (!strcmp(arg, "--raw")) { + // RAW mode takes precedence + xmllvl = -1; + } else if (!strcmp(arg, "--polygon")) { + polygon = 1; + } else if (!strcmp(arg, "--nodisplay") || !strcmp(arg, "--set") || + !strncmp(arg, "--set=", 6)) + continue; + else if (!strcmp(arg, "--")) { + num_images += argc - i - 1; + break; + } else + return (usage(1, "ERROR: unknown option: ", arg)); + } + + if (!num_images) + return (usage(1, "ERROR: specify image file(s) to scan", NULL)); + num_images = 0; + + InitializeMagick("zbarimg"); + + processor = zbar_processor_create(0); + assert(processor); + +#ifdef HAVE_DBUS + zbar_processor_request_dbus(processor, dbus); +#endif + + if (zbar_processor_init(processor, NULL, display)) { + zbar_processor_error_spew(processor, 0); + return (1); + } + + if (xmllvl > 0) { + printf("%s", xml_head); + } + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!arg) + continue; + + if (binary) + xmllvl = -1; + +#ifdef _WIN32 + if (xmllvl == -1) { + _setmode(_fileno(stdout), _O_BINARY); + } else { + _setmode(_fileno(stdout), _O_TEXT); + } +#endif + + if (arg[0] != '-' || !arg[1]) { + if (scan_image(arg)) + return (exit_code); + } else if (arg[1] != '-') + for (j = 1; arg[j]; j++) { + if (arg[j] == 'S') { + if ((arg[++j]) ? parse_config(arg + j, "-S") : + parse_config(argv[++i], "-S")) + return (1); + break; + } + switch (arg[j]) { + case 'd': + zbar_processor_set_visible(processor, 1); + break; + case 'D': + zbar_processor_set_visible(processor, 0); + break; + } + } + else if (!strcmp(arg, "--display")) + zbar_processor_set_visible(processor, 1); + else if (!strcmp(arg, "--nodisplay")) + zbar_processor_set_visible(processor, 0); + + else if (!strcmp(arg, "--set")) { + if (parse_config(argv[++i], "--set")) + return (1); + } else if (!strncmp(arg, "--set=", 6)) { + if (parse_config(arg + 6, "--set=")) + return (1); + } else if (!strcmp(arg, "--")) + break; + } + for (i++; i < argc; i++) + if (scan_image(argv[i])) + return (exit_code); + + /* ignore quit during last image */ + if (exit_code == 3) + exit_code = 0; + + if (xmllvl > 0) { + printf("%s", xml_foot); + fflush(stdout); + } + + if (xmlbuf) + free(xmlbuf); + + if (num_images && !quiet && xmllvl <= 0) { + fprintf(stderr, "scanned %d barcode symbols from %d images", + num_symbols, num_images); +#ifdef HAVE_SYS_TIMES_H +#ifdef HAVE_UNISTD_H + long clk_tck = sysconf(_SC_CLK_TCK); + struct tms tms; + if (clk_tck > 0 && times(&tms) >= 0) { + double secs = tms.tms_utime + tms.tms_stime; + secs /= clk_tck; + fprintf(stderr, " in %.2g seconds\n", secs); + } +#endif +#endif + fprintf(stderr, "\n"); + if (notfound) { + fprintf(stderr, "%s", _(warning_not_found_head)); +#if ENABLE_EAN == 1 + fprintf( + stderr, + _("\t. EAN/UPC (EAN-13, EAN-8, EAN-2, EAN-5, UPC-A, UPC-E, ISBN-10, ISBN-13)\n")); +#endif +#if ENABLE_DATABAR == 1 + fprintf(stderr, _("\t. DataBar, DataBar Expanded\n")); +#endif +#if ENABLE_CODE128 == 1 + fprintf(stderr, _("\t. Code 128\n")); +#endif +#if ENABLE_CODE93 == 1 + fprintf(stderr, _("\t. Code 93\n")); +#endif +#if ENABLE_CODE39 == 1 + fprintf(stderr, _("\t. Code 39\n")); +#endif +#if ENABLE_CODABAR == 1 + fprintf(stderr, _("\t. Codabar\n")); +#endif +#if ENABLE_I25 == 1 + fprintf(stderr, _("\t. Interleaved 2 of 5\n")); +#endif +#if ENABLE_QRCODE == 1 + fprintf(stderr, _("\t. QR code\n")); +#endif +#if ENABLE_SQCODE == 1 + fprintf(stderr, _("\t. SQ code\n")); +#endif +#if ENABLE_PDF417 == 1 + fprintf(stderr, _("\t. PDF 417\n")); +#endif + fprintf(stderr, "%s", _(warning_not_found_tail)); + } + } + if (num_images && notfound && !exit_code) + exit_code = 4; + + zbar_processor_destroy(processor); + DestroyMagick(); + return (exit_code); +} diff --git a/zbarimg/zbarimg.rc b/zbarimg/zbarimg.rc new file mode 100644 index 0000000..a7e9366 --- /dev/null +++ b/zbarimg/zbarimg.rc @@ -0,0 +1,30 @@ +#include <config.h> +#include <winver.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + PRODUCTVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ZBar Bar Code Reader" + VALUE "Company Name", "ZBar Bar Code Reader" + VALUE "InternalName", "zbarimg" + VALUE "OriginalFilename", "zbarimg.exe" + + VALUE "FileVersion", PACKAGE_VERSION + VALUE "ProductVersion", PACKAGE_VERSION + + VALUE "FileDescription", "Scan bar codes from image files" + + VALUE "LegalCopyright", "Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>" + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 0x04e4 + } +} + +APP_ICON ICON "zbar.ico" |