pax_global_header00006660000000000000000000000064142315666070014524gustar00rootroot0000000000000052 comment=dafd3dd813fb448eab0272f139d45a6b894912df libdmtx-0.7.7/000077500000000000000000000000001423156660700132025ustar00rootroot00000000000000libdmtx-0.7.7/.gitignore000066400000000000000000000000061423156660700151660ustar00rootroot00000000000000build libdmtx-0.7.7/AUTHORS000066400000000000000000000000411423156660700142450ustar00rootroot00000000000000Mike Laughton Mackenzie Straight libdmtx-0.7.7/CMakeLists.txt000066400000000000000000000014131423156660700157410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(DMTX VERSION 0.7.5 LANGUAGES C) # DMTX library option(DMTX_SHARED "Build DMTX as shared library" ${BUILD_SHARED_LIBS}) if(DMTX_SHARED) add_library(dmtx SHARED "dmtx.c") else() add_library(dmtx STATIC "dmtx.c") endif() # Compiler specific settings if (MSVC) set_target_properties(dmtx PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) else() set_target_properties(dmtx PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) target_link_libraries(dmtx PUBLIC -lm) endif() # Add tests if DMTX is the main project if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) if(BUILD_TESTING) add_subdirectory("test") endif() endif() libdmtx-0.7.7/ChangeLog000066400000000000000000000206761423156660700147670ustar00rootroot00000000000000Changes for libdmtx ----------------------------------------------------------------- version 0.7.5 [March 2018] https://github.com/dmtx/libdmtx/compare/v0.7.4..v0.7.5 Please, use similar way to check changelog for the next versions. version 0.7.4 [June 2011] library: Relicensed to use Simplified BSD with waiver option library: Added new error codes and messages in dmtxencode.c library: Added DmtxByteList struct and supporting functions library: Changed file header with updated text library: Fixed ECC bug for 144x144 case (thanks Huver!) library: New Reed Solomon implementation library: New repository structure: libdmtx, dmtx-utils, and dmtx-wrappers testing: Added test in compare_generated.sh to create directory if needed testing: Fix compare_generated.sh to prevent false negatives encoder: Review CHK macro strategy encoder: New encoding implementation encoder: Added Base 256 "encode to end of symbol" single byte header encoder: Check ProcessEndOfSymbolTriplet() for same problem fixed in Edifact encoder: Clean up PushCTXValues() passFail handling encoder: Fixed all encoding bugs reported by compare_generated.sh encoder: Fixed encoding bug affecting certain end-of-symbol conditions encoder: Replaced "twothirdsbits" encoder concept (major source of headaches) encoder: Track intermediate states in "optimize best" to handle all possibilities decoder: Use new Edifact decode function that doesn't assume full triplet version 0.7.2 [September 2009] Added initial macro decoding support (thanks Marlon!) Fast quad fill for dmtxDecodeMatrixRegion() (thanks Mackenzie!) Fixed capacity bug with rectangle requests New Vala wrapper (thanks Evan!) Wrapper integration in build system (thanks Evan!) Add libdmtx-X.X.X.zip as source package option Add libdmtx-win32-X.X.X.zip as binary package option Add "project" directory to EXTRA_DIST in Makefile.am version 0.7.0 [March 2009] New Java wrapper (thanks Pete and Dikran!) New .NET wrapper (thanks Vali and Joe!) Added solution and project files for MS Visual Studio 9.0 Reader support for FNC1 and upper shift (thanks Robin!) Support for byte-padded rows through dmtxImageSetProp() New no-copy image handling with configurable pixel packing Moved image scaling factors to DmtxDecode Moved scan cache from DmtxImage to DmtxDecode Eliminate types DmtxRgb, DmtxColor3, and DmtxGradient API updates for consistent naming and conventions Added dmtxEncodeSetProp() and dmtxEncodeGetProp() Option to print extended ASCII as UTF-8 (thanks Robin!) Fixed diagnostic image output Fixed misrepresented capacity in verbose mode True vector SVG output bypassing ImageMagick (thanks Richard!) Use ImageMagick to write images in major raster formats Fixed several bugs and compiler warnings version 0.6.0 [November 2008] dmtxread now scans all major image formats [Olivier] New encoding/decoding Ruby wrapper [Romain] Reduced memory footprint Will scan multiple barcodes per image Various platform-specific improvements Initial work preparing for custom pixel packing in future Begin static analysis cleanup with splint New --disable-dmtxread and --disable-dmtxwrite [Romain] Ability to specify max/min expected barcode sizes New edge neighbor tracking (Hough + 2 way edge cache) Info cache to track scan progress and avoid rescanning pixels Major reduction in floating point operations New informative return codes (found, not found, error) Read input from STDIN Diagnostic images display trail left by scanning logic Option to write output to STDOUT [Olivier] PHP wrapper now compiles with recent libdmtx Dedicated README.xxx instructions for specific platforms version 0.5.2 [September 2008] Move SetRangeLimit and SetScanRegion into library Replace DMTXUTIL_SUCCESS/ERROR with DMTX_SUCCESS/FAILURE Add edge threshold filtering Add encoding support for 144x144 barcodes Fixed encoding case when message starts with two digits Fixed bug in range limit option Add dynamic image shrinking (pixel skipping) Add step-by-step diagnostic image dump (debug build) Fixed bug in minimum scan gap setting Removed y-flip from internal pixel storage Added strict border tests to eliminate false positives Added squareness deviation filter Implement simplified Hough transform for locating first edge Several behind-the-scenes performance enhancements Python wrapper update; 15x faster (thanks Jonathan!) New PHP wrapper code added Various improvements when building for OS X and FreeBSD version 0.5.1 [July 2008] Fixed Extended ASCII encoding bug Fixed error correction bug related to multiple interleaved blocks Added timeout condition for region detection Allow partial and complete disabling of error correction Replaced DmtxPixel struct with DmtxRgb for safe pixel copies Tighter integration with libfec Conditional build logic for libtiff Added placeholder for new utility, dmtxquery Added unit test program for testing libdmtx internals Include local copies of getopt1.c, getopt.c, and getopt.h Various things to help compiling in MS VC++ Lots of holes filled in comments (Doxygen) Fixed experimental Data Mosaic decoding New Cocoa/iPhone wrapper (thanks Stefan!) version 0.5 [April 2008] Error correction using libfec (thanks Florian!) Rework encoding and decoding API for consistency and intuitiveness Handle region detection and region decoding as separate tasks Pass found regions back to calling app before attempting decode Image mask approach (for performance and multi-barcode scans) Fix TestForEndOfSymbolEdifact() to handle special cases correctly Roll scan pattern into core library (inward breadth-first cross) Replace dmtxScanLine() with dmtxScanPixel() Implement 4-direction weighted module decisions (eliminates thresholds) Add ability to scan portion of image Add --diagnose option that dumps image with embedded scan infomation Added Z rotation angle (in degrees) to verbose output Move ASCII and codeword output to separate --preview option Added -R option for setting image print resolution in dpi (PNG only) Remove gltest and simpletest from default build target Update Subversion to be keyword friendly ($Id$) Updated documentation to reflect API and option changes version 0.4 [December 2007] Remove arbitrary sz scaling (100.0) since it doesn't matter anyway Fix 4 bottom-right modules in sizes where they are not used (thanks Matthias R.!) Replace callback references with preprocessor macros Implement remaining encodation schemes for encoding (X12, Base 256, etc...) Implement remaining encodation schemes for decoding (X12, Base 256, etc...) Implement --auto-best option for best possible encoding efficiency Implement multi-region symbols Read and write rectangle barcodes Use GNU autotools (autoconf, automake, libtool) New region detection overhaul Include initial version of Python bindings from pydmtx project (thanks Dan!) Add decoding functionality through Python Add marathon images to project (thanks john@sportcam.net!) Fix dmtxread crash when same barcode is found more than 16 times Verbose messages describing traits of detected barcodes --codewords option to print codewords instead of decoded message --newline option to insert newline character at end of output Additional output formats (PNG, ASCII, codewords) 'make test' executes regression tests for encodation version 0.3 [October 2006] Several high-level changes: build process, naming consistency, file layout Added new automatic style checks in script directory ("make style") Implemented remaining encodation schemes for decode (X12, Base 256, etc...) Fixed instability/regressions that were introduced in v0.2 release Color sampling now averaged from multiple pixel locations Size calibration accuracy improved with new approach dmtxread: increased default scanline count, added multi-page TIFF format dmtxwrite: bug fixes, implemented -o option Improved documentation: deps listed in INSTALL, new man page for dmtxwrite version 0.2 [June 2006] Cleaned up program structure surrounding decoding process Improved API for real-world use (no longer just dumping results to STDOUT) Added "dmtxread" command line utility Added "dmtxwrite" command line utility Implemented Reed-Solomon error detection Created "simpletest.c" for full circle processing test Now using libpng(3) in library to read Data Matrix images Improved documentation (somewhat) version 0.1 [April 2006] Initial release libdmtx-0.7.7/KNOWNBUG000066400000000000000000000026211423156660700143600ustar00rootroot00000000000000Bugs in libdmtx ----------------------------------------------------------------- 1. libdtmx - Core Library :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: While regular encoder works fine, the optimizer feature (--best) still occasionally generates codeword sequences that are not 100% legal according to the ISO specification. Everything still appears to decode properly, but until I have time to go through every corner case and validate the behavior this will be treated as an experimental feature. For now dmtxwrite will encode using a straight ASCII scheme by default. Data Mosaic encoding doesn't produce output for certain sizes: $ echo -n foo | dmtxwrite -M <-- works $ echo -n fooo | dmtxwrite -M <-- doesn't work $ echo -n foooo | dmtxwrite -M <-- works 2. Test Programs :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: multi_test: * No known issues (not included in general download) rotate_test: * No known issues (not included in general download) simple_test: * No known issues unit_test: * Missing files (not included in general download) 3. Scripts in the script directory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The check_headers.pl script verifies that every function has a correctly-formed header comment. But the test condition is currently pretty simple, and does not test the first function appearing in each file. libdmtx-0.7.7/LICENSE000066400000000000000000000035501423156660700142120ustar00rootroot00000000000000Copyright 2005-2016 Mike Laughton, Vadim A. Misbakh-Soloviov and others. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the libdmtx project. -------------------------------------------------------------------------------- ALTERNATE TERMS Redistributions in binary form, with or without modification, are permitted without including the above copyright notice, list of conditions, and disclaimer if express written permission has been obtained from Dragonfly Logic, Inc. libdmtx-0.7.7/Makefile.am000066400000000000000000000022721423156660700152410ustar00rootroot00000000000000# Packaging commands (all run from libdmtx root): # $ make distclean # $ make dist-bzip2 # $ make dist-gzip AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -Wshadow -Wall -pedantic -std=c99 lib_LTLIBRARIES = libdmtx.la libdmtx_la_SOURCES = dmtx.c libdmtx_la_CFLAGS = -Wall -pedantic EXTRA_libdmtx_la_SOURCES = dmtxencode.c dmtxencodestream.c dmtxencodescheme.c \ dmtxencodeoptimize.c dmtxencodeascii.c dmtxencodec40textx12.c \ dmtxencodeedifact.c dmtxencodebase256.c dmtxdecode.c dmtxdecodescheme.c \ dmtxmessage.c dmtxregion.c dmtxsymbol.c dmtxplacemod.c dmtxreedsol.c \ dmtxscangrid.c dmtximage.c dmtxbytelist.c dmtxtime.c dmtxvector2.c \ dmtxmatrix3.c dmtxstatic.h include_HEADERS = dmtx.h SUBDIRS = . test dist_man_MANS = man/libdmtx.3 EXTRA_DIST = KNOWNBUG \ LICENSE \ README.cygwin \ README.freebsd \ README.linux \ README.mingw \ README.osx \ README.unix \ m4 \ script/check_all.sh \ script/check_comments.sh \ script/check_copyright.sh \ script/check_headers.pl \ script/check_license.sh \ script/check_spacing.sh \ script/check_splint.sh \ script/check_todo.sh \ script/check_whitespace.sh pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libdmtx.pc libdmtx-0.7.7/NEWS000066400000000000000000000050751423156660700137100ustar00rootroot0000000000000026-May-2011: New official download page available 17-Mar-2011: Core library relicensed to Simplified BSD with waiver option 03-Mar-2011: libdmtx code now in SourceForge GIT repositories 29-Nov-2010: libdmtx binaries now available as official Fedora package 18-Nov-2009: Mikko Koppanen released improved PHP bindings for libdmtx 05-Nov-2009: "Onion" released a Gstreamer element using libdmtx 04-Sep-2009: New release: libdmtx-0.7.2 (Release Notes) 06-Aug-2009: Simon Wood released pyDataMatrixScanner at SourceForge 03-Aug-2009: Evan Nemerson added Vala support with a new wrapper 31-Jul-2009: libdmtx Documentation wiki moved to libdmtx.wikidot.com 19-May-2009: libdmtx project discussed in Linux Journal (March 2009) 12-Mar-2009: Python wrapper supports multi-barcode scans (thanks Simon!) 02-Mar-2009: New release: libdmtx-0.7.0 (Release Notes) 31-Jan-2009: Experimental libdmtx Windows binaries now available 23-Nov-2008: New release: libdmtx-0.6.0 (Release Notes) 08-Oct-2008: Olivier Guilyardi added support for all major image formats! (SVN) 04-Sep-2008: New release: libdmtx-0.5.2 (README) 20-Aug-2008: Significant performance gains available in SVN (latest tarball) 01-Jul-2008: New release: libdmtx-0.5.1 (README) 29-Jun-2008: libdmtx.org now hosted on a super efficient fit-PC 03-Jun-2008: New iPhone reader by Stefan Hafeneger (no jailbreak required) 31-May-2008: New libdmtx mailing lists replace forums 30-May-2008: GeoWeb does it again: CameraDMTX for Nokia N60 27-May-2008: Et tu, Cognex? 22-May-2008: Congratulations Cognex! Good guys 2, patent trolls 0 16-Apr-2008: New documentation wiki. Everyone can contribute! 13-Apr-2008: 5th release: libdmtx-0.5.0 (README) 22-Feb-2008: New phone video, this time on Symbian S60 15-Jan-2008: Unofficial .debs now available (thanks pcitizen!) 21-Dec-2007: Check out Martin's iDMTX video! 07-Dec-2007: 4th release: libdmtx-0.4.0 (README) 02-Dec-2007: Good news: Project is ACTIVE - new release soon 16-Aug-2007: Added new screenshots to show progress during freeze 30-Nov-2006: Bad news: Downloads have been halted (explanation) 15-Oct-2006: 3rd release: libdmtx-0.3.0 (README) 13-Sep-2006: Unstable version now available from CVS (instructions) 07-Sep-2006: Now taking feature requests for next release 11-Jun-2006: 2nd release: libdmtx-0.2.0 (view README file before using) 22-Apr-2006: 1st release: libdmtx-0.1.0 (view README file before using) 12-Mar-2006: Project home page changed hosting 24-Jan-2006: ISO/IEC 16022:2000 specification arrived in the mail 25-Nov-2005: Project home page libdmtx.sourceforge.net created 22-Nov-2005: SourceForge project created libdmtx-0.7.7/README000066400000000000000000000113731423156660700140670ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (all platforms) This summary of the libdmtx package applies generally to all platforms. For instructions regarding your specific platform, also see the README.xxx file in this directory that matches your system (e.g., README.linux, README.osx, etc...). 1. Introduction ----------------------------------------------------------------- libdmtx is a software library that enables programs to read and write Data Matrix barcodes of the modern ECC200 variety. The library runs natively on several platforms and can be accessed by multiple languages using the libdmtx language wrappers. The utility programs dmtxread and dmtxwrite also provide a command line interface for libdmtx, and serve as a good reference for developers writing their own libdmtx-enabled programs. This package (libdmtx) contains only the core library, and is distributed under a Simplified BSD license with an alternate waiver option. See the LICENSE file in the main project directory for full terms of use and distribution. The non-library components related to libdmtx are available as separate downloads, and are distributed under a different license (typically LGPLv2). Please contact support@dragonflylogic.com if you require clarification on licensing. It's not complicated, but it's important to us that all license terms are respected (not just ours). 2. Project Components ----------------------------------------------------------------- The libdmtx project serves a diverse audience and contains many components -- some of which may not be useful to you. Components fall into one of four categories: Description Package Audience ----------------- ------------- ---------------------- Core library libdmtx libdmtx programs Test programs libdmtx libdmtx developers Utility programs dmtx-utils Shell and command line Language Wrappers dmtx-wrappers Non-C/C++ developers 3. Installation ----------------------------------------------------------------- libdmtx uses GNU Autotools so installation should be familiar to free software veterans. If your platform cannot easily run the Autotools scripts, refer to the appropriate platform-specific README.xxx located in this directory for alternate instructions. In theory the following 3 steps would build and install libdmtx on your system: $ ./configure $ make $ sudo make install However, you may need to install additional software or make other changes for these steps to work properly. The details below will help to address errors and/or customize beyond the defaults. Problems with "configure" step ---------------------------------------- If you obtained libdmtx from Git you may have received an error like "./configure: No such file or directory". Run this command before trying again: $ ./autogen.sh The autogen.sh command requires autoconf, automake, libtool, and pkgconfig to be installed on your system. The configure script also offers many options for customizing the build process, described in detail by running: $ ./configure --help Problems with "make" step ---------------------------------------- Errors encountered during the "make" step are often a result of missing software dependencies. Install any missing software mentioned in the error message(s) and try again. Problems with "sudo make install" step ---------------------------------------- If the 'sudo' command is not configured on your system, you can alternatively yell "Yeeehaww!" as you log in as root and run it like this: # make install And finally... ---------------------------------------- If you want to verify that everything is working properly you can optionally build the test programs: $ make check This command will not perform any tests, but will build the programs that contain test logic: multi_test, rotate_test, simple_test, and unit_test. Note: multi_test and rotate_test contain extra dependencies due to their graphical nature, and are not terribly useful unless you need to test the library's internals. 5. Contact ----------------------------------------------------------------- Documentation wiki: libdmtx.wikidot.com GitHub page: github.com/dmtx/libdmtx OhLoh.net page: www.ohloh.net/projects/libdmtx Open mailing list: libdmtx-open_discussion@lists.sourceforge.net 6. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/general-instructions If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.cygwin000066400000000000000000000025071423156660700153650ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (Cygwin) This README.cygwin file contains information on installing and using libdmtx on Windows in a Cygwin environment. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on Windows using Cygwin ----------------------------------------------------------------- libdmtx can be installed on Cygwin using the instructions provided in the general README file. However, please see below for additional details that might benefit users on this platform. 2. Dependencies ----------------------------------------------------------------- The following packages must be installed to compile libdmtx on Cygwin: * gcc * make * automake * pkg-config Also, if libdmtx was obtained from Git: * autoconf * libtool 3. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-windows-using-cygwin If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.freebsd000066400000000000000000000025211423156660700154730ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (FreeBSD) This README.freebsd file contains information on installing and using libdmtx on FreeBSD. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on FreeBSD ----------------------------------------------------------------- libdmtx can be installed on FreeBSD using the instructions provided in the general README file. However, please read below for additional details that might benefit users on this platform. 2. Running configure ----------------------------------------------------------------- FreeBSD users may need to export the CPPFLAGS and LDFLAGS variables as follows before running configure: $ export CPPFLAGS=-I/usr/local/include $ export LDFLAGS=-L/usr/local/lib $ ./configure $ make $ sudo make install 3. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-freebsd If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.linux000066400000000000000000000024451423156660700152250ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (GNU/Linux) This README.linux file contains information on installing and using libdmtx on GNU/Linux. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on GNU/Linux ----------------------------------------------------------------- libdmtx can be installed on Linux using the instructions provided in the general README file. However, please see below for additional details that might benefit users on this platform. 2. Pre-Compiled Binaries ----------------------------------------------------------------- Many Linux distributions offer pre-compiled libdmtx binaries in their package repositories. This can be a real time saver if you aren't required to build from source for other reasons. 3. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-gnu-linux If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.mingw000066400000000000000000000037051423156660700152070ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (MinGW) This README.mingw file contains information on installing and using libdmtx using MinGW. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on Windows using MinGW and MSYS ----------------------------------------------------------------- libdmtx can be installed on MinGW using the instructions provided in the general README file. However, please see below for additional details that might benefit users on this platform. 2. Installing MinGW and MSYS ----------------------------------------------------------------- If you haven't done so already, first install MinGW, MSYS, and all recommended updates to your Windows system. Instructions for doing this are provided here: http://www.mingw.org/wiki/msys 3. Building and installing the core library ----------------------------------------------------------------- To install libdmtx, download and unpack the libdmtx source to your MSYS folder. If you accepted the installation defaults this will be C:\msys\1.0. Open the MSYS shell and run the following: $ ./configure $ make $ sudo make install Go to folder .libs: $ cd .libs $ ls Now you should see following output: libdmtx.a libdmtx.la libdmtx.lai libdmtx_la-dmtx.o Finally run: $ gcc -shared -o dmtx.dll libdmtx_la-dmtx.o -Wl,--out-implib,libdmtx.a Now you should have working dmtx.dll in the folder .libs. 4. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-windows-using-mingw If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.osx000066400000000000000000000033761423156660700147030ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (Mac OS X) This README.osx file contains information on installing and using libdmtx on Mac OS X. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on Mac OS X ----------------------------------------------------------------- libdmtx can be installed on OS X using the instructions provided in the general README file. However, please see below for additional details that might benefit users on this platform. 2. Universal Binaries ----------------------------------------------------------------- You can tweak configure's parameters to build an Universal Binary version of the library. Recommendations are provided at: http://developer.apple.com/technotes/tn2005/tn2137.html 3. Dependencies ----------------------------------------------------------------- Compiling from Git requires a working autoconf/pkg-config setup: * autoconf, automake, libtool, and pkgconfig are required to generate the configure script. These packages are available from MacPorts. * You may run into issues if you mix the autotools packages in MacPorts with the ones installed from Xcode Tools. Make sure /opt/local/bin appears before /usr/bin in your $PATH. 4. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-mac-os-x If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/README.unix000066400000000000000000000024471423156660700150530ustar00rootroot00000000000000================================================================= libdmtx - Open Source Data Matrix Software ================================================================= libdmtx README file (Unix) This README.unix file contains information on installing and using libdmtx on Unix. The general README file, also found in this directory, contains a high level summary of libdmtx and its components. 1. Installing libdmtx on Unix ----------------------------------------------------------------- libdmtx can be installed on Unix using the instructions provided in the general README file. However, please see below for additional details that might benefit users on this platform. 2. Known Issues ----------------------------------------------------------------- libdmtx is known to work on the following commercial Unix versions: * AIX * HP-UX * Solaris However, building libdmtx from source on these operating systems can be tricky due to their non-GNU conventions. 3. This Document ----------------------------------------------------------------- This document is derived from the wiki page located at: http://libdmtx.wikidot.com/libdmtx-on-unix If you find an error or have additional helpful information, please edit the wiki directly with your updates. libdmtx-0.7.7/TODO000066400000000000000000000276071423156660700137060ustar00rootroot00000000000000TODO ----------------------------------------------------------------- version 1.0.0: (planned TBD) FOCUS: remaining gaps, testing, documentation o decoder: Investigate using MMX to optimize inner loops o decoder: Investigate using threads to split up image processing o testing: Generate metrics in reproducible format to enable historical tracking o testing: Investigate option of embedding decoded text into PNG test image comments o testing: Tests should compare scanned results to embedded PNG comments o testing: 'make test' writes metrics file o testing: 'make test' confirms performance version 0.9.0: (planned TBD) FOCUS: multiple barcode scanning, structured append, FNC1, macros o Implement --auto-fast option using algorithm from spec (lighter & faster?) o Structured append reading and writing o (test suite) Implement exhaustive comparison between --auto-fast and --auto-best o Implement consistent and robust error handling (errno.h + custom) o Implement structured append symbols o Image quality metric o Clean up source file permissions (write script to detect outliers?) version 0.8.0: (planned TBD) FOCUS: region detection o Use calibration edge alignment to set precise locs for all edges o Replace DmtxDirection (e.g., DmtxDirUp) with index range 0-7 (?) o Rename outputIdx to outputLength? (Count pad codewords instead of pointer) o Rename math types to drop unnecessary numeral (DmtxVector2, DmtxRay2, etc...) o Inspect SDL image packing naming conventions (stride vs. pad, etc...) o Clean up API for use with external ROI finders o Is there a good way to know if dmtxRegionFindNext() timed out or finished file? o testing: Test error corrections with controled damage to images o library: Add .gitignore for generated files o library: Add explicit build targets for debug and release o library: Library should never call exit() or assert() x encoder: Fixed Data Mosaic encoding bug version 0.7.4: (02-Jun-2011) x library: Relicensed to use Simplified BSD with waiver option x library: Added new error codes and messages in dmtxencode.c x library: Added DmtxByteList struct and supporting functions x library: Changed file header with updated text x library: Fixed ECC bug for 144x144 case (thanks Huver!) x library: New Reed Solomon implementation x library: New repository structure: libdmtx, dmtx-utils, and dmtx-wrappers x testing: Added test in compare_generated.sh to create directory if needed x testing: Fix compare_generated.sh to prevent false negatives x encoder: Review CHK macro strategy x encoder: New encoding implementation x encoder: Added Base 256 "encode to end of symbol" single byte header x encoder: Check ProcessEndOfSymbolTriplet() for same problem fixed in Edifact x encoder: Clean up PushCTXValues() passFail handling x encoder: Fixed all encoding bugs reported by compare_generated.sh x encoder: Fixed encoding bug affecting certain end-of-symbol conditions x encoder: Replaced "twothirdsbits" encoder concept (major source of headaches) x encoder: Track intermediate states in "optimize best" to handle all possibilities x decoder: Use new Edifact decode function that doesn't assume full triplet version 0.7.2: (04-Sep-2009) x Added initial macro decoding support (thanks Marlon!) x Fast quad fill for dmtxDecodeMatrixRegion() (thanks Mackenzie!) x Fixed capacity bug with rectangle requests x Add libdmtx-X.X.X.zip as source package option x Add libdmtx-win32-X.X.X.zip as binary package option x Add "project" directory to EXTRA_DIST in Makefile.am version 0.7.0: (02-Mar-2009) x Fix 64b->32b int assignment warnings x FNC1 and correct upper shift (thanks Robin!) x Support byte-padded row sizes via dmtxImageSetProp() x Move image scaling factors to DmtxDecode x Add DmtxUndefined to replace "-1" for undefined fixes, offset, etc... x Update dmtxImageCreate() parameter options x Switch DmtxFlipNone represent top-down row order x Add dmtxEncodeSetProp() and dmtxEncodeGetProp() x Relocate scan cache from DmtxImage to DmtxDecode x Remove status from DmtxPixelLoc x Configurable pixel packing x Removed DmtxRgb, dmtxcolor.c, DmtxColor3, and DmtxGradient x DmtxTrue/DmtxFalse replaces DMTX_TRUE/DMTX_FALSE x DmtxPass/DmtxFail replaces DMTX_SUCCESS/DMTX_FAILURE x Change all major types to use Create() and Destroy() convention x Update documentation to reflect API changes x Add comment to wiki pages that points to source README x Figure out earliest usable Magick version for configure.ac x Add simple_test project to libdmtx.sln x Rename wiki page to "Windows (VisualC)" x Introduce "project" directory for non-autotools platforms x Rename "wrappers" directory to "wrapper" for consistency x Create a common tasks and release checklist document version 0.6.0: (23-Nov-2008) x Initial work preparing for custom pixel packing in future x Begin static analysis cleanup with splint x New --disable-dmtxread and --disable-dmtxwrite [Romain] x Ability to specify max/min expected barcode sizes x New edge neighbor tracking (Hough Transform + 2 way edge cache) x Info cache to track scan progress and avoid rescanning pixels x Scan multiple barcodes within an image x Significantly reduced memory footprint x Major reduction in floating point operations x Dedicated README.xxx instructions for specific platforms x Various improvements for cross platform builds version 0.5.2: (04-Sep-2008) x Move SetRangeLimit and SetScanRegion into library x Replace DMTXUTIL_SUCCESS/ERROR with DMTX_SUCCESS/FAILURE x Add edge threshold filtering x Add encoding support for 144x144 barcodes x Fixed encoding case when message starts with two digits x Fixed bug in range limit option x Add dynamic image shrinking (pixel skipping) x Add step-by-step diagnostic image dump (debug build) x Fixed bug in minimum scan gap setting x Removed y-flip from internal pixel storage x Added strict border tests to eliminate false positives x Added squareness deviation filter x Implement simplified Hough transform for locating first edge x Several behind-the-scenes performance enhancements x Various improvements when building for OS X and FreeBSD version 0.5.1: (01-Jul-2008) x Fixed Extended ASCII encoding bug x Fixed error correction bug related to multiple interleaved blocks x Added timeout condition for region detection x Allow partial and complete disabling of error correction x Replaced DmtxPixel struct with DmtxRgb for safe pixel copies x Tighter integration with libfec x (test suite) Started unit test executable for low level testing x Include local copies of getopt1.c getopt.c getopt.h x Various things to help compiling in MS VC++ x Added missing header comments version 0.5: (13-Apr-2008) x Rework encoding and decoding API for consistency and intuitiveness x Handle region detection and region decoding as separate tasks x Pass found regions back to calling app before attempting decode x Image mask approach (for performance and multi-barcode scans) x Remove "2" from functions named *MatrixRegion2*() (whoops) x Fix TestForEndOfSymbolEdifact() to handle special cases correctly x Roll scan pattern into core library (inward breadth-first cross) x Replace dmtxScanLine() with dmtxScanPixel() x Implement 4-direction weighted module decisions (eliminates thresholds) x Error correction using libfec (thanks Florian!) x Remove gltest and simpletest from default build target x Update Subversion to be keyword friendly ($Id$) x Updated documentation to reflect API and option changes x (test suite) Moved all public images to common directory with single copyright file version 0.4: (07-Dec-2008) x Remove arbitrary sz scaling (100.0) since it doesn't matter anyway x Fix 4 bottom-right modules in sizes where they are not used (thanks Matthias R.!) x Replace callback references with preprocessor macros x Implement remaining encodation schemes for encoding (X12, Base 256, etc...) x Implement remaining encodation schemes for decoding (X12, Base 256, etc...) x Implement --auto-best option for best possible encoding efficiency x Implement multi-region symbols x Read and write rectangle shaped barcodes x Use GNU autotools (autoconf, automake, libtool) x New region detection overhaul x Fix chcon error in Makefile (right answer might be to use autoconf) x (test suite) 'make test' executes regression tests for encodation x (test suite) Add marathon images to project (thanks John!) version 0.3: (15-Oct-2006) x Use preprocessor to pull code into one big file before compiling x Update Makefile to handle monolithic approach; add targets for test, util, tarball x Rename DmtxInfo struct and variables to DmtxDecode (for consistency with DmtxEncode) x Merge placement logic into single implementation for both encoding and decoding x Deploy codebase to SourceForge CVS x Add revision control keywords to source files x Implement remaining encodation schemes in dmtxdecode.c (X12, Base 256, etc...) x Create separate file for callback functions (allows them to be optional) x Move PNG (and other format) logic and dependencies to dmtxread, dmtxwrite, etc... x Fix the regressions (crash bugs) introduced during v0.2 structural rework x Add multi-page TIFF capabilities to dmtxread x Move pure decode calls from dmtxScanLine into a dmtxdecode.c function x Sample module color from more than one pixel location x Rename DmtxVector3 to DmtxColor3 and merge into dmtxcolor.c x Add package/build dependencies to INSTALL file x Build coding style test scripts x Replace current calibration size estimate with new approach x Size step size dynamically according to pixel size version 0.2: (11-Jun-2006) x Move dmtxCapturePixel routine to library code x Initial restructuring of code for architectural goodness x Improve API for real-world use (and not just dumping results to STDOUT) x Implement error detection x Create "simpletest.c" for full-circle processing x Use libpng(3) in library to read Data Matrix images x Slap together some basic documentation version 0.1: (22-Apr-2006) x Cycle texture images with right-click x Complete PlotPoint so it handles floating rows and columns x Implement right and left directions of FollowEdge x Call right and left edge following scans started from vertical step scans x Implement 2D vector and matrix functions x Trace lines with actual line object (2D ray) x Turn corners when encountering the end of a followed line x Build 2d transformation to wrap around region, assuming parallelogram x Display pane 4 with reverse-transformed image capture x Enhance dmtxCapturePixel to use "area averaging" instead of nearest neighbor x Figure out why squares are 3 pixels off (to start: draw white gl lines over follower paths) x Add callback function for PlotEventPoint(x, y, event_type) x Improve follower logic (weighted line fit) x dmtxGetPixel: do averaged interpolation followed by a tMin/tMid/tMax cutoff x Add in de-skew transformation x Refactor vector libraries to consistently list target parameter first x Calibrate based on calibration lines x Shrink-fit transformation around region Future Versions: ----------------------------------------------------------------- o Capture high-level design in documentation (data flow, module analogies) o Try bi-linear approximation (instead of linear) in follower edge detection o Implement fixed point math functions for use on mobile platforms o Add calibration functionality to remove spherical distortion Perhaps Never: ----------------------------------------------------------------- o Implement pre-ECC200 Data Matrix standards (big effort/low demand) Website: ----------------------------------------------------------------- o Explore using single background image instead of split o Add what we currently do, don't do, would like to do in the future o Add http://hosted-projects.com/trac/hudora/public/wiki/huBarcode to resources page libdmtx-0.7.7/autogen.sh000077500000000000000000000003161423156660700152030ustar00rootroot00000000000000#!/bin/sh # Create empty m4 directory if missing if [ ! -d "m4" ]; then echo "autogen.sh: creating empty m4 directory" mkdir m4 fi echo "autogen.sh: running autoreconf" autoreconf --force --install libdmtx-0.7.7/configure.ac000066400000000000000000000015671423156660700155010ustar00rootroot00000000000000AC_INIT([libdmtx], [0.7.7], [https://github.com/dmtx/libdmtx/issues], [libdmtx], [https://github.com/dmtx/libdmtx]) AM_INIT_AUTOMAKE([-Wall -Werror]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile libdmtx.pc test/Makefile test/simple_test/Makefile ]) AC_PROG_CC AC_PROG_LIBTOOL AM_PROG_CC_C_O AC_SEARCH_LIBS([sin], [m] ,[], AC_MSG_ERROR([libdmtx requires libm])) AC_SEARCH_LIBS([cos], [m] ,[], AC_MSG_ERROR([libdmtx requires libm])) AC_SEARCH_LIBS([atan2], [m] ,[], AC_MSG_ERROR([libdmtx requires libm])) AC_CHECK_HEADERS([sys/time.h]) AC_CHECK_FUNCS([gettimeofday]) case $target_os in cygwin*) ARCH=cygwin ;; darwin*) ARCH=macosx ;; freebsd*) ARCH=freebsd ;; linux-gnu*) ARCH=linux-gnu ;; mingw32*) ARCH=mingw32 ;; esac AM_CONDITIONAL([TARGET_MACOSX], [test x$ARCH = xmacosx]) AC_OUTPUT libdmtx-0.7.7/dmtx.c000066400000000000000000000040321423156660700143210ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtx.c * \brief Main libdmtx source file */ #include #include #include #include #include #include #include #include #include #include #include "dmtx.h" #include "dmtxstatic.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef CALLBACK_POINT_PLOT #define CALLBACK_POINT_PLOT(a,b,c,d) #endif #ifndef CALLBACK_POINT_XFRM #define CALLBACK_POINT_XFRM(a,b,c,d) #endif #ifndef CALLBACK_MODULE #define CALLBACK_MODULE(a,b,c,d,e) #endif #ifndef CALLBACK_MATRIX #define CALLBACK_MATRIX(a) #endif #ifndef CALLBACK_FINAL #define CALLBACK_FINAL(a,b) #endif /** * Use #include to merge the individual .c source files into a single combined * file during preprocessing. This allows the project to be organized in files * of like-functionality while still keeping a clean namespace. Specifically, * internal functions can be static without losing the ability to access them * "externally" from the other source files in this list. */ #include "dmtxencode.c" #include "dmtxencodestream.c" #include "dmtxencodescheme.c" #include "dmtxencodeoptimize.c" #include "dmtxencodeascii.c" #include "dmtxencodec40textx12.c" #include "dmtxencodeedifact.c" #include "dmtxencodebase256.c" #include "dmtxdecode.c" #include "dmtxdecodescheme.c" #include "dmtxmessage.c" #include "dmtxregion.c" #include "dmtxsymbol.c" #include "dmtxplacemod.c" #include "dmtxreedsol.c" #include "dmtxscangrid.c" #include "dmtximage.c" #include "dmtxbytelist.c" #include "dmtxtime.c" #include "dmtxvector2.c" #include "dmtxmatrix3.c" extern char * dmtxVersion(void) { return DmtxVersion; } libdmtx-0.7.7/dmtx.h000066400000000000000000000526761423156660700143470ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtx.h * \brief Main libdmtx header */ #ifndef __DMTX_H__ #define __DMTX_H__ #ifdef __cplusplus extern "C" { #endif /* Time headers required for DmtxTime struct below */ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 #endif #define DmtxVersion "0.7.5" #define DmtxUndefined -1 #define DmtxPassFail unsigned int #define DmtxPass 1 #define DmtxFail 0 #define DmtxBoolean unsigned int #define DmtxTrue 1 #define DmtxFalse 0 #define DmtxFormatMatrix 0 #define DmtxFormatMosaic 1 #define DmtxSymbolSquareCount 24 #define DmtxSymbolRectCount 6 #define DmtxModuleOff 0x00 #define DmtxModuleOnRed 0x01 #define DmtxModuleOnGreen 0x02 #define DmtxModuleOnBlue 0x04 #define DmtxModuleOnRGB 0x07 /* OnRed | OnGreen | OnBlue */ #define DmtxModuleOn 0x07 #define DmtxModuleUnsure 0x08 #define DmtxModuleAssigned 0x10 #define DmtxModuleVisited 0x20 #define DmtxModuleData 0x40 #define DMTX_CHECK_BOUNDS(l,i) (assert((i) >= 0 && (i) < (l)->length && (l)->length <= (l)->capacity)) typedef enum { DmtxStatusEncoding, /* Encoding is currently underway */ DmtxStatusComplete, /* Encoding is done and everything went well */ DmtxStatusInvalid, /* Something bad happened that sometimes happens */ DmtxStatusFatal /* Something happened that should never happen */ } DmtxStatus; typedef enum { DmtxSchemeAutoFast = -2, DmtxSchemeAutoBest = -1, DmtxSchemeAscii = 0, DmtxSchemeC40, DmtxSchemeText, DmtxSchemeX12, DmtxSchemeEdifact, DmtxSchemeBase256 } DmtxScheme; typedef enum { DmtxSymbolRectAuto = -3, DmtxSymbolSquareAuto = -2, DmtxSymbolShapeAuto = -1, DmtxSymbol10x10 = 0, DmtxSymbol12x12, DmtxSymbol14x14, DmtxSymbol16x16, DmtxSymbol18x18, DmtxSymbol20x20, DmtxSymbol22x22, DmtxSymbol24x24, DmtxSymbol26x26, DmtxSymbol32x32, DmtxSymbol36x36, DmtxSymbol40x40, DmtxSymbol44x44, DmtxSymbol48x48, DmtxSymbol52x52, DmtxSymbol64x64, DmtxSymbol72x72, DmtxSymbol80x80, DmtxSymbol88x88, DmtxSymbol96x96, DmtxSymbol104x104, DmtxSymbol120x120, DmtxSymbol132x132, DmtxSymbol144x144, DmtxSymbol8x18, DmtxSymbol8x32, DmtxSymbol12x26, DmtxSymbol12x36, DmtxSymbol16x36, DmtxSymbol16x48 } DmtxSymbolSize; typedef enum { DmtxDirNone = 0x00, DmtxDirUp = 0x01 << 0, DmtxDirLeft = 0x01 << 1, DmtxDirDown = 0x01 << 2, DmtxDirRight = 0x01 << 3, DmtxDirHorizontal = DmtxDirLeft | DmtxDirRight, DmtxDirVertical = DmtxDirUp | DmtxDirDown, DmtxDirRightUp = DmtxDirRight | DmtxDirUp, DmtxDirLeftDown = DmtxDirLeft | DmtxDirDown } DmtxDirection; typedef enum { DmtxSymAttribSymbolRows, DmtxSymAttribSymbolCols, DmtxSymAttribDataRegionRows, DmtxSymAttribDataRegionCols, DmtxSymAttribHorizDataRegions, DmtxSymAttribVertDataRegions, DmtxSymAttribMappingMatrixRows, DmtxSymAttribMappingMatrixCols, DmtxSymAttribInterleavedBlocks, DmtxSymAttribBlockErrorWords, DmtxSymAttribBlockMaxCorrectable, DmtxSymAttribSymbolDataWords, DmtxSymAttribSymbolErrorWords, DmtxSymAttribSymbolMaxCorrectable } DmtxSymAttribute; typedef enum { DmtxCorner00 = 0x01 << 0, DmtxCorner10 = 0x01 << 1, DmtxCorner11 = 0x01 << 2, DmtxCorner01 = 0x01 << 3 } DmtxCornerLoc; typedef enum { /* Encoding properties */ DmtxPropScheme = 100, DmtxPropSizeRequest, DmtxPropMarginSize, DmtxPropModuleSize, DmtxPropFnc1, /* Decoding properties */ DmtxPropEdgeMin = 200, DmtxPropEdgeMax, DmtxPropScanGap, DmtxPropSquareDevn, DmtxPropSymbolSize, DmtxPropEdgeThresh, /* Image properties */ DmtxPropWidth = 300, DmtxPropHeight, DmtxPropPixelPacking, DmtxPropBitsPerPixel, DmtxPropBytesPerPixel, DmtxPropRowPadBytes, DmtxPropRowSizeBytes, DmtxPropImageFlip, DmtxPropChannelCount, /* Image modifiers */ DmtxPropXmin = 400, DmtxPropXmax, DmtxPropYmin, DmtxPropYmax, DmtxPropScale } DmtxProperty; typedef enum { /* Custom format */ DmtxPackCustom = 100, /* 1 bpp */ DmtxPack1bppK = 200, /* 8 bpp grayscale */ DmtxPack8bppK = 300, /* 16 bpp formats */ DmtxPack16bppRGB = 400, DmtxPack16bppRGBX, DmtxPack16bppXRGB, DmtxPack16bppBGR, DmtxPack16bppBGRX, DmtxPack16bppXBGR, DmtxPack16bppYCbCr, /* 24 bpp formats */ DmtxPack24bppRGB = 500, DmtxPack24bppBGR, DmtxPack24bppYCbCr, /* 32 bpp formats */ DmtxPack32bppRGBX = 600, DmtxPack32bppXRGB, DmtxPack32bppBGRX, DmtxPack32bppXBGR, DmtxPack32bppCMYK } DmtxPackOrder; typedef enum { DmtxFlipNone = 0x00, DmtxFlipX = 0x01 << 0, DmtxFlipY = 0x01 << 1 } DmtxFlip; typedef double DmtxMatrix3[3][3]; /** * @struct DmtxPixelLoc * @brief DmtxPixelLoc */ typedef struct DmtxPixelLoc_struct { int X; int Y; } DmtxPixelLoc; /** * @struct DmtxVector2 * @brief DmtxVector2 */ typedef struct DmtxVector2_struct { double X; double Y; } DmtxVector2; /** * @struct DmtxRay2 * @brief DmtxRay2 */ typedef struct DmtxRay2_struct { double tMin; double tMax; DmtxVector2 p; DmtxVector2 v; } DmtxRay2; typedef unsigned char DmtxByte; /** * @struct DmtxByteList * @brief DmtxByteList * Use signed int for length fields instead of size_t to play nicely with RS * arithmetic */ typedef struct DmtxByteList_struct DmtxByteList; struct DmtxByteList_struct { int length; int capacity; DmtxByte *b; }; typedef struct DmtxEncodeStream_struct DmtxEncodeStream; struct DmtxEncodeStream_struct { int currentScheme; /* Current encodation scheme */ int inputNext; /* Index of next unprocessed input word in queue */ int outputChainValueCount; /* Count of output values pushed within current scheme chain */ int outputChainWordCount; /* Count of output words pushed within current scheme chain */ char *reason; /* Reason for status */ int sizeIdx; /* Symbol size of completed stream */ int fnc1; /* Character to represent FNC1, or DmtxUndefined */ DmtxStatus status; DmtxByteList *input; DmtxByteList *output; }; /** * @struct DmtxImage * @brief DmtxImage */ typedef struct DmtxImage_struct { int width; int height; int pixelPacking; int bitsPerPixel; int bytesPerPixel; int rowPadBytes; int rowSizeBytes; int imageFlip; int channelCount; int channelStart[4]; int bitsPerChannel[4]; unsigned char *pxl; } DmtxImage; /** * @struct DmtxPointFlow * @brief DmtxPointFlow */ typedef struct DmtxPointFlow_struct { int plane; int arrive; int depart; int mag; DmtxPixelLoc loc; } DmtxPointFlow; /** * @struct DmtxBestLine * @brief DmtxBestLine */ typedef struct DmtxBestLine_struct { int angle; int hOffset; int mag; int stepBeg; int stepPos; int stepNeg; int distSq; double devn; DmtxPixelLoc locBeg; DmtxPixelLoc locPos; DmtxPixelLoc locNeg; } DmtxBestLine; /** * @struct DmtxRegion * @brief DmtxRegion */ typedef struct DmtxRegion_struct { /* Trail blazing values */ int jumpToPos; /* */ int jumpToNeg; /* */ int stepsTotal; /* */ DmtxPixelLoc finalPos; /* */ DmtxPixelLoc finalNeg; /* */ DmtxPixelLoc boundMin; /* */ DmtxPixelLoc boundMax; /* */ DmtxPointFlow flowBegin; /* */ /* Orientation values */ int polarity; /* */ int stepR; int stepT; DmtxPixelLoc locR; /* remove if stepR works above */ DmtxPixelLoc locT; /* remove if stepT works above */ /* Region fitting values */ int leftKnown; /* known == 1; unknown == 0 */ int leftAngle; /* hough angle of left edge */ DmtxPixelLoc leftLoc; /* known (arbitrary) location on left edge */ DmtxBestLine leftLine; /* */ int bottomKnown; /* known == 1; unknown == 0 */ int bottomAngle; /* hough angle of bottom edge */ DmtxPixelLoc bottomLoc; /* known (arbitrary) location on bottom edge */ DmtxBestLine bottomLine; /* */ int topKnown; /* known == 1; unknown == 0 */ int topAngle; /* hough angle of top edge */ DmtxPixelLoc topLoc; /* known (arbitrary) location on top edge */ int rightKnown; /* known == 1; unknown == 0 */ int rightAngle; /* hough angle of right edge */ DmtxPixelLoc rightLoc; /* known (arbitrary) location on right edge */ /* Region calibration values */ int onColor; /* */ int offColor; /* */ int sizeIdx; /* Index of arrays that store Data Matrix constants */ int symbolRows; /* Number of total rows in symbol including alignment patterns */ int symbolCols; /* Number of total columns in symbol including alignment patterns */ int mappingRows; /* Number of data rows in symbol */ int mappingCols; /* Number of data columns in symbol */ /* Transform values */ DmtxMatrix3 raw2fit; /* 3x3 transformation from raw image to fitted barcode grid */ DmtxMatrix3 fit2raw; /* 3x3 transformation from fitted barcode grid to raw image */ } DmtxRegion; /** * @struct DmtxMessage * @brief DmtxMessage */ typedef struct DmtxMessage_struct { size_t arraySize; /* mappingRows * mappingCols */ size_t codeSize; /* Size of encoded data (data words + error words) */ size_t outputSize; /* Size of buffer used to hold decoded data */ int outputIdx; /* Internal index used to store output progress */ int padCount; int fnc1; /* Character to represent FNC1, or DmtxUndefined */ unsigned char *array; /* Pointer to internal representation of Data Matrix modules */ unsigned char *code; /* Pointer to internal storage of code words (data and error) */ unsigned char *output; /* Pointer to internal storage of decoded output */ } DmtxMessage; /** * @struct DmtxScanGrid * @brief DmtxScanGrid */ typedef struct DmtxScanGrid_struct { /* set once */ int minExtent; /* Smallest cross size used in scan */ int maxExtent; /* Size of bounding grid region (2^N - 1) */ int xOffset; /* Offset to obtain image X coordinate */ int yOffset; /* Offset to obtain image Y coordinate */ int xMin; /* Minimum X in image coordinate system */ int xMax; /* Maximum X in image coordinate system */ int yMin; /* Minimum Y in image coordinate system */ int yMax; /* Maximum Y in image coordinate system */ /* reset for each level */ int total; /* Total number of crosses at this size */ int extent; /* Length/width of cross in pixels */ int jumpSize; /* Distance in pixels between cross centers */ int pixelTotal; /* Total pixel count within an individual cross path */ int startPos; /* X and Y coordinate of first cross center in pattern */ /* reset for each cross */ int pixelCount; /* Progress (pixel count) within current cross pattern */ int xCenter; /* X center of current cross pattern */ int yCenter; /* Y center of current cross pattern */ } DmtxScanGrid; /** * @struct DmtxTime * @brief DmtxTime */ typedef struct DmtxTime_struct { time_t sec; unsigned long usec; } DmtxTime; /** * @struct DmtxDecode * @brief DmtxDecode */ typedef struct DmtxDecode_struct { /* Options */ int edgeMin; int edgeMax; int scanGap; int fnc1; double squareDevn; int sizeIdxExpected; int edgeThresh; /* Image modifiers */ int xMin; int xMax; int yMin; int yMax; int scale; /* Internals */ /* int cacheComplete; */ unsigned char *cache; DmtxImage *image; DmtxScanGrid grid; } DmtxDecode; /** * @struct DmtxEncode * @brief DmtxEncode */ typedef struct DmtxEncode_struct { int method; int scheme; int sizeIdxRequest; int marginSize; int moduleSize; int pixelPacking; int imageFlip; int rowPadBytes; int fnc1; DmtxMessage *message; DmtxImage *image; DmtxRegion region; DmtxMatrix3 xfrm; /* XXX still necessary? */ DmtxMatrix3 rxfrm; /* XXX still necessary? */ } DmtxEncode; /** * @struct DmtxChannel * @brief DmtxChannel */ typedef struct DmtxChannel_struct { int encScheme; /* current encodation scheme */ int invalid; /* channel status (invalid if non-zero) */ unsigned char *inputPtr; /* pointer to current input character */ unsigned char *inputStop; /* pointer to position after final input character */ int encodedLength; /* encoded length (units of 2/3 bits) */ int currentLength; /* current length (units of 2/3 bits) */ int firstCodeWord; /* */ unsigned char encodedWords[1558]; } DmtxChannel; /* Wrap in a struct for fast copies */ /** * @struct DmtxChannelGroup * @brief DmtxChannelGroup */ typedef struct DmtxChannelGroup_struct { DmtxChannel channel[6]; } DmtxChannelGroup; /** * @struct DmtxTriplet * @brief DmtxTriplet */ typedef struct DmtxTriplet_struct { unsigned char value[3]; } DmtxTriplet; /** * @struct DmtxQuadruplet * @brief DmtxQuadruplet */ typedef struct DmtxQuadruplet_struct { unsigned char value[4]; } DmtxQuadruplet; /* dmtxtime.c */ extern DmtxTime dmtxTimeNow(void); extern DmtxTime dmtxTimeAdd(DmtxTime t, long msec); extern int dmtxTimeExceeded(DmtxTime timeout); /* dmtxencode.c */ extern DmtxEncode *dmtxEncodeCreate(void); extern DmtxPassFail dmtxEncodeDestroy(DmtxEncode **enc); extern DmtxPassFail dmtxEncodeSetProp(DmtxEncode *enc, int prop, int value); extern int dmtxEncodeGetProp(DmtxEncode *enc, int prop); extern DmtxPassFail dmtxEncodeDataMatrix(DmtxEncode *enc, int n, unsigned char *s); extern DmtxPassFail dmtxEncodeDataMosaic(DmtxEncode *enc, int n, unsigned char *s); /* dmtxdecode.c */ extern DmtxDecode *dmtxDecodeCreate(DmtxImage *img, int scale); extern DmtxPassFail dmtxDecodeDestroy(DmtxDecode **dec); extern DmtxPassFail dmtxDecodeSetProp(DmtxDecode *dec, int prop, int value); extern int dmtxDecodeGetProp(DmtxDecode *dec, int prop); extern /*@exposed@*/ unsigned char *dmtxDecodeGetCache(DmtxDecode *dec, int x, int y); extern DmtxPassFail dmtxDecodeGetPixelValue(DmtxDecode *dec, int x, int y, int channel, /*@out@*/ int *value); extern DmtxMessage *dmtxDecodeMatrixRegion(DmtxDecode *dec, DmtxRegion *reg, int fix); extern DmtxMessage *dmtxDecodePopulatedArray(int sizeIdx, DmtxMessage *msg, int fix); extern DmtxMessage *dmtxDecodeMosaicRegion(DmtxDecode *dec, DmtxRegion *reg, int fix); extern unsigned char *dmtxDecodeCreateDiagnostic(DmtxDecode *dec, /*@out@*/ int *totalBytes, /*@out@*/ int *headerBytes, int style); /* dmtxregion.c */ extern DmtxRegion *dmtxRegionCreate(DmtxRegion *reg); extern DmtxPassFail dmtxRegionDestroy(DmtxRegion **reg); extern DmtxRegion *dmtxRegionFindNext(DmtxDecode *dec, DmtxTime *timeout); extern DmtxRegion *dmtxRegionScanPixel(DmtxDecode *dec, int x, int y); extern DmtxPassFail dmtxRegionUpdateCorners(DmtxDecode *dec, DmtxRegion *reg, DmtxVector2 p00, DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01); extern DmtxPassFail dmtxRegionUpdateXfrms(DmtxDecode *dec, DmtxRegion *reg); /* dmtxmessage.c */ extern DmtxMessage *dmtxMessageCreate(int sizeIdx, int symbolFormat); extern DmtxPassFail dmtxMessageDestroy(DmtxMessage **msg); /* dmtximage.c */ extern DmtxImage *dmtxImageCreate(unsigned char *pxl, int width, int height, int pack); extern DmtxPassFail dmtxImageDestroy(DmtxImage **img); extern DmtxPassFail dmtxImageSetChannel(DmtxImage *img, int channelStart, int bitsPerChannel); extern DmtxPassFail dmtxImageSetProp(DmtxImage *img, int prop, int value); extern int dmtxImageGetProp(DmtxImage *img, int prop); extern int dmtxImageGetByteOffset(DmtxImage *img, int x, int y); extern DmtxPassFail dmtxImageGetPixelValue(DmtxImage *img, int x, int y, int channel, /*@out@*/ int *value); extern DmtxPassFail dmtxImageSetPixelValue(DmtxImage *img, int x, int y, int channel, int value); extern DmtxBoolean dmtxImageContainsInt(DmtxImage *img, int margin, int x, int y); extern DmtxBoolean dmtxImageContainsFloat(DmtxImage *img, double x, double y); /* dmtxvector2.c */ extern DmtxVector2 *dmtxVector2AddTo(DmtxVector2 *v1, const DmtxVector2 *v2); extern DmtxVector2 *dmtxVector2Add(/*@out@*/ DmtxVector2 *vOut, const DmtxVector2 *v1, const DmtxVector2 *v2); extern DmtxVector2 *dmtxVector2SubFrom(DmtxVector2 *v1, const DmtxVector2 *v2); extern DmtxVector2 *dmtxVector2Sub(/*@out@*/ DmtxVector2 *vOut, const DmtxVector2 *v1, const DmtxVector2 *v2); extern DmtxVector2 *dmtxVector2ScaleBy(DmtxVector2 *v, double s); extern DmtxVector2 *dmtxVector2Scale(/*@out@*/ DmtxVector2 *vOut, const DmtxVector2 *v, double s); extern double dmtxVector2Cross(const DmtxVector2 *v1, const DmtxVector2 *v2); extern double dmtxVector2Norm(DmtxVector2 *v); extern double dmtxVector2Dot(const DmtxVector2 *v1, const DmtxVector2 *v2); extern double dmtxVector2Mag(const DmtxVector2 *v); extern double dmtxDistanceFromRay2(const DmtxRay2 *r, const DmtxVector2 *q); extern double dmtxDistanceAlongRay2(const DmtxRay2 *r, const DmtxVector2 *q); extern DmtxPassFail dmtxRay2Intersect(/*@out@*/ DmtxVector2 *point, const DmtxRay2 *p0, const DmtxRay2 *p1); extern DmtxPassFail dmtxPointAlongRay2(/*@out@*/ DmtxVector2 *point, const DmtxRay2 *r, double t); /* dmtxmatrix3.c */ extern void dmtxMatrix3Copy(/*@out@*/ DmtxMatrix3 m0, DmtxMatrix3 m1); extern void dmtxMatrix3Identity(/*@out@*/ DmtxMatrix3 m); extern void dmtxMatrix3Translate(/*@out@*/ DmtxMatrix3 m, double tx, double ty); extern void dmtxMatrix3Rotate(/*@out@*/ DmtxMatrix3 m, double angle); extern void dmtxMatrix3Scale(/*@out@*/ DmtxMatrix3 m, double sx, double sy); extern void dmtxMatrix3Shear(/*@out@*/ DmtxMatrix3 m, double shx, double shy); extern void dmtxMatrix3LineSkewTop(/*@out@*/ DmtxMatrix3 m, double b0, double b1, double sz); extern void dmtxMatrix3LineSkewTopInv(/*@out@*/ DmtxMatrix3 m, double b0, double b1, double sz); extern void dmtxMatrix3LineSkewSide(/*@out@*/ DmtxMatrix3 m, double b0, double b1, double sz); extern void dmtxMatrix3LineSkewSideInv(/*@out@*/ DmtxMatrix3 m, double b0, double b1, double sz); extern void dmtxMatrix3Multiply(/*@out@*/ DmtxMatrix3 mOut, DmtxMatrix3 m0, DmtxMatrix3 m1); extern void dmtxMatrix3MultiplyBy(DmtxMatrix3 m0, DmtxMatrix3 m1); extern int dmtxMatrix3VMultiply(/*@out@*/ DmtxVector2 *vOut, DmtxVector2 *vIn, DmtxMatrix3 m); extern int dmtxMatrix3VMultiplyBy(DmtxVector2 *v, DmtxMatrix3 m); extern void dmtxMatrix3Print(DmtxMatrix3 m); /* dmtxsymbol.c */ extern int dmtxSymbolModuleStatus(DmtxMessage *mapping, int sizeIdx, int row, int col); extern int dmtxGetSymbolAttribute(int attribute, int sizeIdx); extern int dmtxGetBlockDataSize(int sizeIdx, int blockIdx); extern int getSizeIdxFromSymbolDimension(int rows, int cols); /* dmtxbytelist.c */ extern DmtxByteList dmtxByteListBuild(DmtxByte *storage, int capacity); extern void dmtxByteListInit(DmtxByteList *list, int length, DmtxByte value, DmtxPassFail *passFail); extern void dmtxByteListClear(DmtxByteList *list); extern DmtxBoolean dmtxByteListHasCapacity(DmtxByteList *list); extern void dmtxByteListCopy(DmtxByteList *dst, const DmtxByteList *src, DmtxPassFail *passFail); extern void dmtxByteListPush(DmtxByteList *list, DmtxByte value, DmtxPassFail *passFail); extern DmtxByte dmtxByteListPop(DmtxByteList *list, DmtxPassFail *passFail); extern void dmtxByteListPrint(DmtxByteList *list, char *prefix); extern char *dmtxVersion(void); #ifdef __cplusplus } #endif #endif libdmtx-0.7.7/dmtxbytelist.c000066400000000000000000000047111423156660700161050ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file file.c */ /** * * */ extern DmtxByteList dmtxByteListBuild(DmtxByte *storage, int capacity) { DmtxByteList list; list.b = storage; list.capacity = capacity; list.length = 0; return list; } /** * * */ extern void dmtxByteListInit(DmtxByteList *list, int length, DmtxByte value, DmtxPassFail *passFail) { if(length > list->capacity) { *passFail = DmtxFail; } else { list->length = length; memset(list->b, value, sizeof(DmtxByte) * list->capacity); *passFail = DmtxPass; } } /** * * */ extern void dmtxByteListClear(DmtxByteList *list) { memset(list->b, 0x00, sizeof(DmtxByte) * list->capacity); list->length = 0; } /** * * */ extern DmtxBoolean dmtxByteListHasCapacity(DmtxByteList *list) { return (list->length < list->capacity) ? DmtxTrue : DmtxFalse; } /** * * */ extern void dmtxByteListCopy(DmtxByteList *dst, const DmtxByteList *src, DmtxPassFail *passFail) { int length; if(dst->capacity < src->length) { *passFail = DmtxFail; /* dst must be large enough to hold src data */ } else { /* Copy as many bytes as dst can hold or src can provide (smaller of two) */ length = (dst->capacity < src->capacity) ? dst->capacity : src->capacity; dst->length = src->length; memcpy(dst->b, src->b, sizeof(unsigned char) * length); *passFail = DmtxPass; } } /** * * */ extern void dmtxByteListPush(DmtxByteList *list, DmtxByte value, DmtxPassFail *passFail) { if(list->length >= list->capacity) { *passFail = DmtxFail; } else { list->b[list->length++] = value; *passFail = DmtxPass; } } /** * * */ extern DmtxByte dmtxByteListPop(DmtxByteList *list, DmtxPassFail *passFail) { *passFail = (list->length > 0) ? DmtxPass : DmtxFail; return list->b[--(list->length)]; } /** * * */ extern void dmtxByteListPrint(DmtxByteList *list, char *prefix) { int i; if(prefix != NULL) fprintf(stdout, "%s", prefix); for(i = 0; i < list->length; i++) fprintf(stdout, " %d", list->b[i]); fputc('\n', stdout); } libdmtx-0.7.7/dmtxdecode.c000066400000000000000000000557071423156660700155040ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2009 Mackenzie Straight. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxdecode.c * \brief Decode regions */ /** * \brief Initialize decode struct with default values * \param img * \return Initialized DmtxDecode struct */ #include // for snprintf extern DmtxDecode * dmtxDecodeCreate(DmtxImage *img, int scale) { DmtxDecode *dec; int width, height; dec = (DmtxDecode *)calloc(1, sizeof(DmtxDecode)); if(dec == NULL) return NULL; width = dmtxImageGetProp(img, DmtxPropWidth) / scale; height = dmtxImageGetProp(img, DmtxPropHeight) / scale; dec->fnc1 = DmtxUndefined; dec->edgeMin = DmtxUndefined; dec->edgeMax = DmtxUndefined; dec->scanGap = 1; dec->squareDevn = cos(50 * (M_PI/180)); dec->sizeIdxExpected = DmtxSymbolShapeAuto; dec->edgeThresh = 10; dec->xMin = 0; dec->xMax = width - 1; dec->yMin = 0; dec->yMax = height - 1; dec->scale = scale; dec->cache = (unsigned char *)calloc(width * height, sizeof(unsigned char)); if(dec->cache == NULL) { free(dec); return NULL; } dec->image = img; dec->grid = InitScanGrid(dec); return dec; } /** * \brief Deinitialize decode struct * \param dec * \return void */ extern DmtxPassFail dmtxDecodeDestroy(DmtxDecode **dec) { if(dec == NULL || *dec == NULL) return DmtxFail; if((*dec)->cache != NULL) free((*dec)->cache); free(*dec); *dec = NULL; return DmtxPass; } /** * \brief Set decoding behavior property * \param dec * \param prop * \param value * \return DmtxPass | DmtxFail */ extern DmtxPassFail dmtxDecodeSetProp(DmtxDecode *dec, int prop, int value) { switch(prop) { case DmtxPropEdgeMin: dec->edgeMin = value; break; case DmtxPropEdgeMax: dec->edgeMax = value; break; case DmtxPropScanGap: dec->scanGap = value; /* XXX Should this be scaled? */ break; case DmtxPropFnc1: dec->fnc1 = value; break; case DmtxPropSquareDevn: dec->squareDevn = cos(value * (M_PI/180.0)); break; case DmtxPropSymbolSize: dec->sizeIdxExpected = value; break; case DmtxPropEdgeThresh: dec->edgeThresh = value; break; /* Min and Max values arrive unscaled */ case DmtxPropXmin: dec->xMin = value / dec->scale; break; case DmtxPropXmax: dec->xMax = value / dec->scale; break; case DmtxPropYmin: dec->yMin = value / dec->scale; break; case DmtxPropYmax: dec->yMax = value / dec->scale; break; default: break; } if(dec->squareDevn <= 0.0 || dec->squareDevn >= 1.0) return DmtxFail; if(dec->scanGap < 1) return DmtxFail; if(dec->edgeThresh < 1 || dec->edgeThresh > 100) return DmtxFail; /* Reinitialize scangrid in case any inputs changed */ dec->grid = InitScanGrid(dec); return DmtxPass; } /** * \brief Get decoding behavior property * \param dec * \param prop * \return value */ extern int dmtxDecodeGetProp(DmtxDecode *dec, int prop) { switch(prop) { case DmtxPropEdgeMin: return dec->edgeMin; case DmtxPropEdgeMax: return dec->edgeMax; case DmtxPropScanGap: return dec->scanGap; case DmtxPropFnc1: return dec->fnc1; case DmtxPropSquareDevn: return (int)(acos(dec->squareDevn) * 180.0/M_PI); case DmtxPropSymbolSize: return dec->sizeIdxExpected; case DmtxPropEdgeThresh: return dec->edgeThresh; case DmtxPropXmin: return dec->xMin; case DmtxPropXmax: return dec->xMax; case DmtxPropYmin: return dec->yMin; case DmtxPropYmax: return dec->yMax; case DmtxPropScale: return dec->scale; case DmtxPropWidth: return dmtxImageGetProp(dec->image, DmtxPropWidth) / dec->scale; case DmtxPropHeight: return dmtxImageGetProp(dec->image, DmtxPropHeight) / dec->scale; default: break; } return DmtxUndefined; } /** * \brief Returns xxx * \param img * \param Scaled x coordinate * \param Scaled y coordinate * \return Scaled pixel offset */ extern unsigned char * dmtxDecodeGetCache(DmtxDecode *dec, int x, int y) { int width, height; assert(dec != NULL); /* if(dec.cacheComplete == DmtxFalse) CacheImage(); */ width = dmtxDecodeGetProp(dec, DmtxPropWidth); height = dmtxDecodeGetProp(dec, DmtxPropHeight); if(x < 0 || x >= width || y < 0 || y >= height) return NULL; return &(dec->cache[y * width + x]); } /** * * */ extern DmtxPassFail dmtxDecodeGetPixelValue(DmtxDecode *dec, int x, int y, int channel, int *value) { int xUnscaled, yUnscaled; DmtxPassFail err; xUnscaled = x * dec->scale; yUnscaled = y * dec->scale; /* Remove spherical lens distortion */ /* int width, height; double radiusPow2, radiusPow4; double factor; DmtxVector2 pointShifted; DmtxVector2 correctedPoint; width = dmtxImageGetProp(img, DmtxPropWidth); height = dmtxImageGetProp(img, DmtxPropHeight); pointShifted.X = point.X - width/2.0; pointShifted.Y = point.Y - height/2.0; radiusPow2 = pointShifted.X * pointShifted.X + pointShifted.Y * pointShifted.Y; radiusPow4 = radiusPow2 * radiusPow2; factor = 1 + (k1 * radiusPow2) + (k2 * radiusPow4); correctedPoint.X = pointShifted.X * factor + width/2.0; correctedPoint.Y = pointShifted.Y * factor + height/2.0; return correctedPoint; */ err = dmtxImageGetPixelValue(dec->image, xUnscaled, yUnscaled, channel, value); return err; } /** * \brief Fill the region covered by the quadrilateral given by (p0,p1,p2,p3) in the cache. */ static void CacheFillQuad(DmtxDecode *dec, DmtxPixelLoc p0, DmtxPixelLoc p1, DmtxPixelLoc p2, DmtxPixelLoc p3) { DmtxBresLine lines[4]; DmtxPixelLoc pEmpty = { 0, 0 }; unsigned char *cache; int *scanlineMin, *scanlineMax; int minY, maxY, sizeY, posY, posX; int i, idx; lines[0] = BresLineInit(p0, p1, pEmpty); lines[1] = BresLineInit(p1, p2, pEmpty); lines[2] = BresLineInit(p2, p3, pEmpty); lines[3] = BresLineInit(p3, p0, pEmpty); minY = dec->yMax; maxY = 0; minY = min(minY, p0.Y); maxY = max(maxY, p0.Y); minY = min(minY, p1.Y); maxY = max(maxY, p1.Y); minY = min(minY, p2.Y); maxY = max(maxY, p2.Y); minY = min(minY, p3.Y); maxY = max(maxY, p3.Y); sizeY = maxY - minY + 1; scanlineMin = (int *)malloc(sizeY * sizeof(int)); scanlineMax = (int *)calloc(sizeY, sizeof(int)); assert(scanlineMin); /* XXX handle this better */ assert(scanlineMax); /* XXX handle this better */ for(i = 0; i < sizeY; i++) scanlineMin[i] = dec->xMax; for(i = 0; i < 4; i++) { while(lines[i].loc.X != lines[i].loc1.X || lines[i].loc.Y != lines[i].loc1.Y) { idx = lines[i].loc.Y - minY; scanlineMin[idx] = min(scanlineMin[idx], lines[i].loc.X); scanlineMax[idx] = max(scanlineMax[idx], lines[i].loc.X); BresLineStep(lines + i, 1, 0); } } for(posY = minY; posY < maxY && posY < dec->yMax; posY++) { idx = posY - minY; for(posX = scanlineMin[idx]; posX < scanlineMax[idx] && posX < dec->xMax; posX++) { cache = dmtxDecodeGetCache(dec, posX, posY); if(cache != NULL) *cache |= 0x80; } } free(scanlineMin); free(scanlineMax); } /** * \brief Convert fitted Data Matrix region into a decoded message * \param dec * \param reg * \param fix * \return Decoded message */ extern DmtxMessage * dmtxDecodeMatrixRegion(DmtxDecode *dec, DmtxRegion *reg, int fix) { //fprintf(stdout, "libdmtx::dmtxDecodeMatrixRegion()\n"); DmtxMessage *msg; DmtxVector2 topLeft, topRight, bottomLeft, bottomRight; DmtxPixelLoc pxTopLeft, pxTopRight, pxBottomLeft, pxBottomRight; msg = dmtxMessageCreate(reg->sizeIdx, DmtxFormatMatrix); if(msg == NULL) return NULL; if(PopulateArrayFromMatrix(dec, reg, msg) != DmtxPass) { dmtxMessageDestroy(&msg); return NULL; } msg->fnc1 = dec->fnc1; topLeft.X = bottomLeft.X = topLeft.Y = topRight.Y = -0.1; topRight.X = bottomRight.X = bottomLeft.Y = bottomRight.Y = 1.1; dmtxMatrix3VMultiplyBy(&topLeft, reg->fit2raw); dmtxMatrix3VMultiplyBy(&topRight, reg->fit2raw); dmtxMatrix3VMultiplyBy(&bottomLeft, reg->fit2raw); dmtxMatrix3VMultiplyBy(&bottomRight, reg->fit2raw); pxTopLeft.X = (int)(0.5 + topLeft.X); pxTopLeft.Y = (int)(0.5 + topLeft.Y); pxBottomLeft.X = (int)(0.5 + bottomLeft.X); pxBottomLeft.Y = (int)(0.5 + bottomLeft.Y); pxTopRight.X = (int)(0.5 + topRight.X); pxTopRight.Y = (int)(0.5 + topRight.Y); pxBottomRight.X = (int)(0.5 + bottomRight.X); pxBottomRight.Y = (int)(0.5 + bottomRight.Y); CacheFillQuad(dec, pxTopLeft, pxTopRight, pxBottomRight, pxBottomLeft); return dmtxDecodePopulatedArray(reg->sizeIdx, msg, fix); } /** * \brief Ripped out a part of dmtxDecodeMatrixRegion function to this one to parse own array * \param sizeIdx * \param msg * \param fix * \return Decoded message (msg pointer) or NULL in case of failure. * \note You should reaffect msg with the result of this call * since a NULL result means msg gets freed and should not be used anymore. * ex: msg = dmtxDecodePopulatedArray(sizeidx, msg, fix); */ DmtxMessage * dmtxDecodePopulatedArray(int sizeIdx, DmtxMessage *msg, int fix) { /* * Example msg->array indices for a 12x12 datamatrix. * also, the 'L' color (usually black) is defined as 'DmtxModuleOnRGB' * * XX XX XX XX XX XX * XX 0 1 2 3 4 5 6 7 8 9 XX * XX 10 11 12 13 14 15 16 17 18 19 * XX 20 21 22 23 24 25 26 27 28 29 XX * XX 30 31 32 33 34 35 36 37 38 39 * XX 40 41 42 43 44 45 46 47 48 49 XX * XX 50 51 52 53 54 55 56 57 58 59 * XX 60 61 62 63 64 65 66 67 68 69 XX * XX 70 71 72 73 74 75 76 77 78 79 * XX 80 81 82 83 84 85 86 87 88 89 XX * XX 90 91 92 93 94 95 96 97 98 99 * XX XX XX XX XX XX XX XX XX XX XX XX * */ ModulePlacementEcc200(msg->array, msg->code, sizeIdx, DmtxModuleOnRed | DmtxModuleOnGreen | DmtxModuleOnBlue); if(RsDecode(msg->code, sizeIdx, fix) == DmtxFail){ dmtxMessageDestroy(&msg); msg = NULL; return NULL; } if(DecodeDataStream(msg, sizeIdx, NULL) == DmtxFail) { dmtxMessageDestroy(&msg); msg = NULL; return NULL; } return msg; } /** * \brief Convert fitted Data Mosaic region into a decoded message * \param dec * \param reg * \param fix * \return Decoded message */ extern DmtxMessage * dmtxDecodeMosaicRegion(DmtxDecode *dec, DmtxRegion *reg, int fix) { int offset; int colorPlane; DmtxMessage *oMsg, *rMsg, *gMsg, *bMsg; colorPlane = reg->flowBegin.plane; /** * Consider performing a color cube fit here to identify exact RGB of * all 6 "cube-like" corners based on pixels located within region. Then * force each sample pixel to the "cube-like" corner based o which one * is nearest "sqrt(dr^2+dg^2+db^2)" (except sqrt is unnecessary). * colorPlane = reg->flowBegin.plane; * * To find RGB values of primary colors, perform something like a * histogram except instead of going from black to color N, go from * (127,127,127) to color. Use color bins along with distance to * identify value. An additional method will be required to get actual * RGB instead of just a plane in 3D. */ reg->flowBegin.plane = 0; /* kind of a hack */ rMsg = dmtxDecodeMatrixRegion(dec, reg, fix); reg->flowBegin.plane = 1; /* kind of a hack */ gMsg = dmtxDecodeMatrixRegion(dec, reg, fix); reg->flowBegin.plane = 2; /* kind of a hack */ bMsg = dmtxDecodeMatrixRegion(dec, reg, fix); reg->flowBegin.plane = colorPlane; oMsg = dmtxMessageCreate(reg->sizeIdx, DmtxFormatMosaic); if(oMsg == NULL || rMsg == NULL || gMsg == NULL || bMsg == NULL) { dmtxMessageDestroy(&oMsg); dmtxMessageDestroy(&rMsg); dmtxMessageDestroy(&gMsg); dmtxMessageDestroy(&bMsg); return NULL; } offset = 0; memcpy(oMsg->output + offset, rMsg->output, rMsg->outputIdx); offset += rMsg->outputIdx; memcpy(oMsg->output + offset, gMsg->output, gMsg->outputIdx); offset += gMsg->outputIdx; memcpy(oMsg->output + offset, bMsg->output, bMsg->outputIdx); offset += bMsg->outputIdx; oMsg->outputIdx = offset; dmtxMessageDestroy(&rMsg); dmtxMessageDestroy(&gMsg); dmtxMessageDestroy(&bMsg); return oMsg; } /** * * */ extern unsigned char * dmtxDecodeCreateDiagnostic(DmtxDecode *dec, int *totalBytes, int *headerBytes, int style) { int i, row, col; int width, height; int widthDigits, heightDigits; int count, channelCount; int rgb[3]; double shade; unsigned char *pnm, *output, *cache; width = dmtxDecodeGetProp(dec, DmtxPropWidth); height = dmtxDecodeGetProp(dec, DmtxPropHeight); channelCount = dmtxImageGetProp(dec->image, DmtxPropChannelCount); style = 1; /* this doesn't mean anything yet */ /* Count width digits */ for(widthDigits = 0, i = width; i > 0; i /= 10) widthDigits++; /* Count height digits */ for(heightDigits = 0, i = height; i > 0; i /= 10) heightDigits++; *headerBytes = widthDigits + heightDigits + 9; *totalBytes = *headerBytes + width * height * 3; pnm = (unsigned char *)malloc(*totalBytes); if(pnm == NULL) return NULL; #if defined(_MSC_VER) && (_MSC_VER < 1700) count = sprintf_s((char *)pnm, *headerBytes + 1, "P6\n%d %d\n255\n", width, height); #else count = snprintf((char *)pnm, *headerBytes + 1, "P6\n%d %d\n255\n", width, height); #endif if(count != *headerBytes) { free(pnm); return NULL; } output = pnm + (*headerBytes); for(row = height - 1; row >= 0; row--) { for(col = 0; col < width; col++) { cache = dmtxDecodeGetCache(dec, col, row); if(cache == NULL) { rgb[0] = 0; rgb[1] = 0; rgb[2] = 128; } else if(*cache & 0x40) { rgb[0] = 255; rgb[1] = 0; rgb[2] = 0; } else { shade = (*cache & 0x80) ? 0.0 : 0.7; for(i = 0; i < 3; i++) { if(i < channelCount) dmtxDecodeGetPixelValue(dec, col, row, i, &rgb[i]); else dmtxDecodeGetPixelValue(dec, col, row, 0, &rgb[i]); rgb[i] += (int)(shade * (double)(255 - rgb[i]) + 0.5); if(rgb[i] > 255) rgb[i] = 255; } } *(output++) = (unsigned char)rgb[0]; *(output++) = (unsigned char)rgb[1]; *(output++) = (unsigned char)rgb[2]; } } assert(output == pnm + *totalBytes); return pnm; } /** * \brief Increment counters used to determine module values * \param img * \param reg * \param tally * \param xOrigin * \param yOrigin * \param mapWidth * \param mapHeight * \param dir * \return void */ static void TallyModuleJumps(DmtxDecode *dec, DmtxRegion *reg, int tally[][24], int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir) { int extent, weight; int travelStep; int symbolRow, symbolCol; int mapRow, mapCol; int lineStart, lineStop; int travelStart, travelStop; int *line, *travel; int jumpThreshold; int darkOnLight; int color; int statusPrev, statusModule; int tPrev, tModule; assert(dir == DmtxDirUp || dir == DmtxDirLeft || dir == DmtxDirDown || dir == DmtxDirRight); travelStep = (dir == DmtxDirUp || dir == DmtxDirRight) ? 1 : -1; /* Abstract row and column progress using pointers to allow grid traversal in all 4 directions using same logic */ if((dir & DmtxDirHorizontal) != 0x00) { line = &symbolRow; travel = &symbolCol; extent = mapWidth; lineStart = yOrigin; lineStop = yOrigin + mapHeight; travelStart = (travelStep == 1) ? xOrigin - 1 : xOrigin + mapWidth; travelStop = (travelStep == 1) ? xOrigin + mapWidth : xOrigin - 1; } else { assert(dir & DmtxDirVertical); line = &symbolCol; travel = &symbolRow; extent = mapHeight; lineStart = xOrigin; lineStop = xOrigin + mapWidth; travelStart = (travelStep == 1) ? yOrigin - 1: yOrigin + mapHeight; travelStop = (travelStep == 1) ? yOrigin + mapHeight : yOrigin - 1; } darkOnLight = (int)(reg->offColor > reg->onColor); jumpThreshold = abs((int)(0.4 * (reg->offColor - reg->onColor) + 0.5)); assert(jumpThreshold >= 0); for(*line = lineStart; *line < lineStop; (*line)++) { /* Capture tModule for each leading border module as normal but decide status based on predictable barcode border pattern */ *travel = travelStart; color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; statusModule = (travelStep == 1 || (*line & 0x01) == 0) ? DmtxModuleOnRGB : DmtxModuleOff; weight = extent; while((*travel += travelStep) != travelStop) { tPrev = tModule; statusPrev = statusModule; /* For normal data-bearing modules capture color and decide module status based on comparison to previous "known" module */ color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; if(statusPrev == DmtxModuleOnRGB) { if(tModule < tPrev - jumpThreshold){ statusModule = DmtxModuleOff; } else { statusModule = DmtxModuleOnRGB; } } else if(statusPrev == DmtxModuleOff) { if(tModule > tPrev + jumpThreshold) { statusModule = DmtxModuleOnRGB; } else { statusModule = DmtxModuleOff; } } mapRow = symbolRow - yOrigin; mapCol = symbolCol - xOrigin; assert(mapRow < 24 && mapCol < 24); if(statusModule == DmtxModuleOnRGB){ tally[mapRow][mapCol] += (2 * weight); } weight--; } assert(weight == 0); } } /** * \brief Populate array with codeword values based on module colors * \param msg * \param img * \param reg * \return DmtxPass | DmtxFail */ static DmtxPassFail PopulateArrayFromMatrix(DmtxDecode *dec, DmtxRegion *reg, DmtxMessage *msg) { //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix()\n"); int weightFactor; int mapWidth, mapHeight; int xRegionTotal, yRegionTotal; int xRegionCount, yRegionCount; int xOrigin, yOrigin; int mapCol, mapRow; int colTmp, rowTmp, idx; int tally[24][24]; /* Large enough to map largest single region */ /* memset(msg->array, 0x00, msg->arraySize); */ /* Capture number of regions present in barcode */ xRegionTotal = dmtxGetSymbolAttribute(DmtxSymAttribHorizDataRegions, reg->sizeIdx); yRegionTotal = dmtxGetSymbolAttribute(DmtxSymAttribVertDataRegions, reg->sizeIdx); /* Capture region dimensions (not including border modules) */ mapWidth = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionCols, reg->sizeIdx); mapHeight = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionRows, reg->sizeIdx); weightFactor = 2 * (mapHeight + mapWidth + 2); assert(weightFactor > 0); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->sizeIdx: %d\n", reg->sizeIdx); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->flowBegin.plane: %d\n", reg->flowBegin.plane); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->onColor: %d\n", reg->onColor); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->offColor: %d\n", reg->offColor); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::xRegionTotal: %d\n", xRegionTotal); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::yRegionTotal: %d\n", yRegionTotal); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::mapWidth: %d\n", mapWidth); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::mapHeight: %d\n", mapHeight); //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::weightFactor: %d\n", weightFactor); //reg->fit2raw[1][0]=0; //reg->fit2raw[0][1]=0; //reg->fit2raw[0][2]=0; //reg->fit2raw[2][2]=1; //reg->fit2raw[1][2]=0; //reg->fit2raw[2][0]=10; //translation //reg->fit2raw[2][1]=10; //translation //reg->fit2raw[0][0]=60; //scale //reg->fit2raw[1][1]=60; //scale //dmtxMatrix3Print(reg->fit2raw); /* Tally module changes for each region in each direction */ for(yRegionCount = 0; yRegionCount < yRegionTotal; yRegionCount++) { /* Y location of mapping region origin in symbol coordinates */ yOrigin = yRegionCount * (mapHeight + 2) + 1; for(xRegionCount = 0; xRegionCount < xRegionTotal; xRegionCount++) { /* X location of mapping region origin in symbol coordinates */ xOrigin = xRegionCount * (mapWidth + 2) + 1; //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::xOrigin: %d\n", xOrigin); memset(tally, 0x00, 24 * 24 * sizeof(int)); TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirUp); TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirLeft); TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirDown); TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirRight); /* Decide module status based on final tallies */ for(mapRow = 0; mapRow < mapHeight; mapRow++) { //for(mapRow = mapHeight-1; mapRow >= 0; mapRow--) { for(mapCol = 0; mapCol < mapWidth; mapCol++) { rowTmp = (yRegionCount * mapHeight) + mapRow; rowTmp = yRegionTotal * mapHeight - rowTmp - 1; colTmp = (xRegionCount * mapWidth) + mapCol; idx = (rowTmp * xRegionTotal * mapWidth) + colTmp; //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::idx: %d @ %d,%d\n", idx, mapCol, mapRow); //fprintf(stdout, "%c ",tally[mapRow][mapCol]==DmtxModuleOff ? 'X' : ' '); if(tally[mapRow][mapCol]/(double)weightFactor >= 0.5){ msg->array[idx] = DmtxModuleOnRGB; //fprintf(stdout, "X "); } else { msg->array[idx] = DmtxModuleOff; //fprintf(stdout, " "); } msg->array[idx] |= DmtxModuleAssigned; } //fprintf(stdout, "\n"); } } } return DmtxPass; } libdmtx-0.7.7/dmtxdecodescheme.c000066400000000000000000000343751423156660700166670ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxdecodescheme.c */ /** * \brief Translate encoded data stream into final output * \param msg * \param sizeIdx * \param outputStart * \return void */ extern DmtxPassFail DecodeDataStream(DmtxMessage *msg, int sizeIdx, unsigned char *outputStart) { //fprintf(stdout, "libdmtx::DecodeDataStream()\n"); //int oned = sqrt(msg->arraySize); //for (int i=0; iarraySize; i++){ // fprintf(stdout, " %c.", msg->array[i]); // if (i%oned==oned-1){ // fprintf(stdout, "\n"); // } //} DmtxBoolean macro = DmtxFalse; DmtxScheme encScheme; unsigned char *ptr, *dataEnd; msg->output = (outputStart == NULL) ? msg->output : outputStart; msg->outputIdx = 0; ptr = msg->code; dataEnd = ptr + dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx); /* Print macro header if first codeword triggers it */ if(*ptr == DmtxValue05Macro || *ptr == DmtxValue06Macro) { PushOutputMacroHeader(msg, *ptr); macro = DmtxTrue; } while(ptr < dataEnd) { encScheme = GetEncodationScheme(*ptr); if(encScheme != DmtxSchemeAscii) ptr++; switch(encScheme) { case DmtxSchemeAscii: ptr = DecodeSchemeAscii(msg, ptr, dataEnd); break; case DmtxSchemeC40: case DmtxSchemeText: ptr = DecodeSchemeC40Text(msg, ptr, dataEnd, encScheme); break; case DmtxSchemeX12: ptr = DecodeSchemeX12(msg, ptr, dataEnd); break; case DmtxSchemeEdifact: ptr = DecodeSchemeEdifact(msg, ptr, dataEnd); break; case DmtxSchemeBase256: ptr = DecodeSchemeBase256(msg, ptr, dataEnd); break; default: /* error */ break; } if(ptr == NULL) return DmtxFail; } /* Print macro trailer if required */ if(macro == DmtxTrue) PushOutputMacroTrailer(msg); return DmtxPass; } /** * \brief Determine next encodation scheme * \param encScheme * \param cw * \return Pointer to next undecoded codeword */ static int GetEncodationScheme(unsigned char cw) { DmtxScheme encScheme; switch(cw) { case DmtxValueC40Latch: encScheme = DmtxSchemeC40; break; case DmtxValueTextLatch: encScheme = DmtxSchemeText; break; case DmtxValueX12Latch: encScheme = DmtxSchemeX12; break; case DmtxValueEdifactLatch: encScheme = DmtxSchemeEdifact; break; case DmtxValueBase256Latch: encScheme = DmtxSchemeBase256; break; default: encScheme = DmtxSchemeAscii; break; } return encScheme; } /** * * */ static void PushOutputWord(DmtxMessage *msg, int value) { assert(value >= 0 && value < 256); msg->output[msg->outputIdx++] = (unsigned char)value; } /** * * */ static DmtxBoolean ValidOutputWord(int value) { return (value >= 0 && value < 256) ? DmtxTrue : DmtxFalse; } /** * * */ static void PushOutputC40TextWord(DmtxMessage *msg, C40TextState *state, int value) { assert(value >= 0 && value < 256); msg->output[msg->outputIdx] = (unsigned char)value; if(state->upperShift == DmtxTrue) { assert(value < 128); msg->output[msg->outputIdx] += 128; } msg->outputIdx++; state->shift = DmtxC40TextBasicSet; state->upperShift = DmtxFalse; } /** * * */ static void PushOutputMacroHeader(DmtxMessage *msg, int macroType) { PushOutputWord(msg, '['); PushOutputWord(msg, ')'); PushOutputWord(msg, '>'); PushOutputWord(msg, 30); /* ASCII RS */ PushOutputWord(msg, '0'); assert(macroType == DmtxValue05Macro || macroType == DmtxValue06Macro); if(macroType == DmtxValue05Macro) PushOutputWord(msg, '5'); else PushOutputWord(msg, '6'); PushOutputWord(msg, 29); /* ASCII GS */ } /** * * */ static void PushOutputMacroTrailer(DmtxMessage *msg) { PushOutputWord(msg, 30); /* ASCII RS */ PushOutputWord(msg, 4); /* ASCII EOT */ } /** * \brief Decode stream assuming standard ASCII encodation * \param msg * \param ptr * \param dataEnd * \return Pointer to next undecoded codeword * NULL if an error was detected in the stream */ static unsigned char * DecodeSchemeAscii(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd) { int upperShift = DmtxFalse; while(ptr < dataEnd) { int codeword = (int)(*ptr); if(GetEncodationScheme(*ptr) != DmtxSchemeAscii) return ptr; else ptr++; if(upperShift == DmtxTrue) { int pushword = codeword + 127; if (ValidOutputWord(pushword) != DmtxTrue) return NULL; PushOutputWord(msg, pushword); upperShift = DmtxFalse; } else if(codeword == DmtxValueAsciiUpperShift) { upperShift = DmtxTrue; } else if(codeword == DmtxValueAsciiPad) { assert(dataEnd >= ptr); assert(dataEnd - ptr <= INT_MAX); msg->padCount = (int)(dataEnd - ptr); return dataEnd; } else if(codeword == 0 || codeword >= 242) { return ptr; } else if(codeword <= 128) { PushOutputWord(msg, codeword - 1); } else if(codeword <= 229) { int digits = codeword - 130; PushOutputWord(msg, digits/10 + '0'); PushOutputWord(msg, digits - (digits/10)*10 + '0'); } else if(codeword == DmtxValueFNC1) { if(msg->fnc1 != DmtxUndefined) { int pushword = msg->fnc1; if (ValidOutputWord(pushword) != DmtxTrue) return NULL; PushOutputWord(msg, pushword); } } } return ptr; } /** * \brief Decode stream assuming C40 or Text encodation * \param msg * \param ptr * \param dataEnd * \param encScheme * \return Pointer to next undecoded codeword */ static unsigned char * DecodeSchemeC40Text(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd, DmtxScheme encScheme) { int i; int packed; int c40Values[3]; C40TextState state; state.shift = DmtxC40TextBasicSet; state.upperShift = DmtxFalse; assert(encScheme == DmtxSchemeC40 || encScheme == DmtxSchemeText); /* Unlatch is implied if only one codeword remains */ if(dataEnd - ptr < 2) return ptr; while(ptr < dataEnd) { /* FIXME Also check that ptr+1 is safe to access */ packed = (*ptr << 8) | *(ptr+1); c40Values[0] = ((packed - 1)/1600); c40Values[1] = ((packed - 1)/40) % 40; c40Values[2] = (packed - 1) % 40; ptr += 2; for(i = 0; i < 3; i++) { if(state.shift == DmtxC40TextBasicSet) { /* Basic set */ if(c40Values[i] <= 2) { state.shift = c40Values[i] + 1; } else if(c40Values[i] == 3) { PushOutputC40TextWord(msg, &state, ' '); } else if(c40Values[i] <= 13) { PushOutputC40TextWord(msg, &state, c40Values[i] - 13 + '9'); /* 0-9 */ } else if(c40Values[i] <= 39) { if(encScheme == DmtxSchemeC40) { PushOutputC40TextWord(msg, &state, c40Values[i] - 39 + 'Z'); /* A-Z */ } else if(encScheme == DmtxSchemeText) { PushOutputC40TextWord(msg, &state, c40Values[i] - 39 + 'z'); /* a-z */ } } } else if(state.shift == DmtxC40TextShift1) { /* Shift 1 set */ PushOutputC40TextWord(msg, &state, c40Values[i]); /* ASCII 0 - 31 */ } else if(state.shift == DmtxC40TextShift2) { /* Shift 2 set */ if(c40Values[i] <= 14) { PushOutputC40TextWord(msg, &state, c40Values[i] + 33); /* ASCII 33 - 47 */ } else if(c40Values[i] <= 21) { PushOutputC40TextWord(msg, &state, c40Values[i] + 43); /* ASCII 58 - 64 */ } else if(c40Values[i] <= 26) { PushOutputC40TextWord(msg, &state, c40Values[i] + 69); /* ASCII 91 - 95 */ } else if(c40Values[i] == 27) { if(msg->fnc1 != DmtxUndefined) { PushOutputC40TextWord(msg, &state, msg->fnc1); } } else if(c40Values[i] == 30) { state.upperShift = DmtxTrue; state.shift = DmtxC40TextBasicSet; } } else if(state.shift == DmtxC40TextShift3) { /* Shift 3 set */ if(encScheme == DmtxSchemeC40) { PushOutputC40TextWord(msg, &state, c40Values[i] + 96); } else if(encScheme == DmtxSchemeText) { if(c40Values[i] == 0) PushOutputC40TextWord(msg, &state, c40Values[i] + 96); else if(c40Values[i] <= 26) PushOutputC40TextWord(msg, &state, c40Values[i] - 26 + 'Z'); /* A-Z */ else PushOutputC40TextWord(msg, &state, c40Values[i] - 31 + 127); /* { | } ~ DEL */ } } } /* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */ if(*ptr == DmtxValueCTXUnlatch) return ptr + 1; /* Unlatch is implied if only one codeword remains */ if(dataEnd - ptr < 2) return ptr; } return ptr; } /** * \brief Decode stream assuming X12 encodation * \param msg * \param ptr * \param dataEnd * \return Pointer to next undecoded codeword */ static unsigned char * DecodeSchemeX12(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd) { int i; int packed; int x12Values[3]; /* Unlatch is implied if only one codeword remains */ if(dataEnd - ptr < 2) return ptr; while(ptr < dataEnd) { /* FIXME Also check that ptr+1 is safe to access */ packed = (*ptr << 8) | *(ptr+1); x12Values[0] = ((packed - 1)/1600); x12Values[1] = ((packed - 1)/40) % 40; x12Values[2] = (packed - 1) % 40; ptr += 2; for(i = 0; i < 3; i++) { if(x12Values[i] == 0) PushOutputWord(msg, 13); else if(x12Values[i] == 1) PushOutputWord(msg, 42); else if(x12Values[i] == 2) PushOutputWord(msg, 62); else if(x12Values[i] == 3) PushOutputWord(msg, 32); else if(x12Values[i] <= 13) PushOutputWord(msg, x12Values[i] + 44); else if(x12Values[i] <= 90) PushOutputWord(msg, x12Values[i] + 51); } /* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */ if(*ptr == DmtxValueCTXUnlatch) return ptr + 1; /* Unlatch is implied if only one codeword remains */ if(dataEnd - ptr < 2) return ptr; } return ptr; } /** * \brief Decode stream assuming EDIFACT encodation * \param msg * \param ptr * \param dataEnd * \return Pointer to next undecoded codeword */ static unsigned char * DecodeSchemeEdifact(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd) { int i; unsigned char unpacked[4]; /* Unlatch is implied if fewer than 3 codewords remain */ if(dataEnd - ptr < 3) return ptr; while(ptr < dataEnd) { /* FIXME Also check that ptr+2 is safe to access -- shouldn't be a problem because I'm guessing you can guarantee there will always be at least 3 error codewords */ unpacked[0] = (*ptr & 0xfc) >> 2; unpacked[1] = (*ptr & 0x03) << 4 | (*(ptr+1) & 0xf0) >> 4; unpacked[2] = (*(ptr+1) & 0x0f) << 2 | (*(ptr+2) & 0xc0) >> 6; unpacked[3] = *(ptr+2) & 0x3f; for(i = 0; i < 4; i++) { /* Advance input ptr (4th value comes from already-read 3rd byte) */ if(i < 3) ptr++; /* Test for unlatch condition */ if(unpacked[i] == DmtxValueEdifactUnlatch) { assert(msg->output[msg->outputIdx] == 0); /* XXX dirty why? */ return ptr; } PushOutputWord(msg, unpacked[i] ^ (((unpacked[i] & 0x20) ^ 0x20) << 1)); } /* Unlatch is implied if fewer than 3 codewords remain */ if(dataEnd - ptr < 3) return ptr; } return ptr; /* XXX the following version should be safer, but requires testing before replacing the old version int bits = 0; int bitCount = 0; int value; while(ptr < dataEnd) { if(bitCount < 6) { bits = (bits << 8) | *(ptr++); bitCount += 8; } value = bits >> (bitCount - 6); bits -= (value << (bitCount - 6)); bitCount -= 6; if(value == 0x1f) { assert(bits == 0); // should be padded with zero-value bits return ptr; } PushOutputWord(msg, value ^ (((value & 0x20) ^ 0x20) << 1)); // Unlatch implied if just completed triplet and 1 or 2 words are left if(bitCount == 0 && dataEnd - ptr - 1 > 0 && dataEnd - ptr - 1 < 3) return ptr; } assert(bits == 0); // should be padded with zero-value bits assert(bitCount == 0); // should be padded with zero-value bits return ptr; */ } /** * \brief Decode stream assuming Base 256 encodation * \param msg * \param ptr * \param dataEnd * \return Pointer to next undecoded codeword, * NULL if an error was detected in the stream */ static unsigned char * DecodeSchemeBase256(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd) { int d0, d1; int idx; unsigned char *ptrEnd; /* Find positional index used for unrandomizing */ assert(ptr + 1 >= msg->code); assert(ptr + 1 - msg->code <= INT_MAX); idx = (int)(ptr + 1 - msg->code); d0 = UnRandomize255State(*(ptr++), idx++); if(d0 == 0) { ptrEnd = dataEnd; } else if(d0 <= 249) { ptrEnd = ptr + d0; } else { d1 = UnRandomize255State(*(ptr++), idx++); ptrEnd = ptr + (d0 - 249) * 250 + d1; } if(ptrEnd > dataEnd) return NULL; while(ptr < ptrEnd) PushOutputWord(msg, UnRandomize255State(*(ptr++), idx++)); return ptr; } libdmtx-0.7.7/dmtxencode.c000066400000000000000000000345451423156660700155130ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencode.c * \brief Base encoding logic */ #undef ISDIGIT #define ISDIGIT(n) (n > 47 && n < 58) /** * \brief Initialize encode struct with default values * \return Initialized DmtxEncode struct */ extern DmtxEncode * dmtxEncodeCreate(void) { DmtxEncode *enc; enc = (DmtxEncode *)calloc(1, sizeof(DmtxEncode)); if(enc == NULL) return NULL; enc->scheme = DmtxSchemeAscii; enc->sizeIdxRequest = DmtxSymbolSquareAuto; enc->marginSize = 10; enc->moduleSize = 5; enc->pixelPacking = DmtxPack24bppRGB; enc->imageFlip = DmtxFlipNone; enc->rowPadBytes = 0; enc->fnc1 = DmtxUndefined; /* Initialize background color to white */ /* enc.region.gradient.ray.p.R = 255.0; enc.region.gradient.ray.p.G = 255.0; enc.region.gradient.ray.p.B = 255.0; */ /* Initialize foreground color to black */ /* enc.region.gradient.tMin = 0.0; enc.region.gradient.tMax = xyz; */ dmtxMatrix3Identity(enc->xfrm); return enc; } /** * \brief Deinitialize encode struct * \param enc * \return void */ extern DmtxPassFail dmtxEncodeDestroy(DmtxEncode **enc) { if(enc == NULL || *enc == NULL) return DmtxFail; /* Free pixel array allocated in dmtxEncodeDataMatrix() */ if((*enc)->image != NULL && (*enc)->image->pxl != NULL) { free((*enc)->image->pxl); (*enc)->image->pxl = NULL; } dmtxImageDestroy(&((*enc)->image)); dmtxMessageDestroy(&((*enc)->message)); free(*enc); *enc = NULL; return DmtxPass; } /** * \brief Set encoding behavior property * \param enc * \param prop * \param value * \return DmtxPass | DmtxFail */ extern DmtxPassFail dmtxEncodeSetProp(DmtxEncode *enc, int prop, int value) { switch(prop) { /* Encoding details */ case DmtxPropScheme: enc->scheme = value; break; case DmtxPropSizeRequest: if(value == DmtxSymbolShapeAuto) return DmtxFail; enc->sizeIdxRequest = value; break; case DmtxPropFnc1: enc->fnc1 = value; break; /* Presentation details */ case DmtxPropMarginSize: enc->marginSize = value; break; case DmtxPropModuleSize: enc->moduleSize = value; break; /* Image properties */ case DmtxPropPixelPacking: enc->pixelPacking = value; break; case DmtxPropImageFlip: enc->imageFlip = value; break; case DmtxPropRowPadBytes: enc->rowPadBytes = value; default: break; } return DmtxPass; } /** * \brief Get encoding behavior property * \param enc * \param prop * \return value */ extern int dmtxEncodeGetProp(DmtxEncode *enc, int prop) { switch(prop) { case DmtxPropMarginSize: return enc->marginSize; case DmtxPropModuleSize: return enc->moduleSize; case DmtxPropScheme: return enc->scheme; case DmtxPropFnc1: return enc->fnc1; default: break; } return DmtxUndefined; } /** * \brief Convert message into Data Matrix image * \param enc * \param inputSize * \param inputString * \param sizeIdxRequest * \return DmtxPass | DmtxFail */ extern DmtxPassFail dmtxEncodeDataMatrix(DmtxEncode *enc, int inputSize, unsigned char *inputString) { int sizeIdx; int width, height, bitsPerPixel; unsigned char *pxl; DmtxByte outputStorage[4096]; DmtxByteList output = dmtxByteListBuild(outputStorage, sizeof(outputStorage)); DmtxByteList input = dmtxByteListBuild(inputString, inputSize); input.length = inputSize; /* Future: stream = StreamInit() ... */ /* Future: EncodeDataCodewords(&stream) ... */ /* Encode input string into data codewords */ sizeIdx = EncodeDataCodewords(&input, &output, enc->sizeIdxRequest, enc->scheme, enc->fnc1); if(sizeIdx == DmtxUndefined || output.length <= 0) return DmtxFail; /* EncodeDataCodewords() should have updated any auto sizeIdx to a real one */ assert(sizeIdx != DmtxSymbolSquareAuto && sizeIdx != DmtxSymbolRectAuto); /* XXX we can remove a lot of this redundant data */ enc->region.sizeIdx = sizeIdx; enc->region.symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); enc->region.symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx); enc->region.mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdx); enc->region.mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdx); /* Allocate memory for message and array */ enc->message = dmtxMessageCreate(sizeIdx, DmtxFormatMatrix); enc->message->padCount = 0; /* XXX this needs to be added back */ memcpy(enc->message->code, output.b, output.length); /* Generate error correction codewords */ RsEncode(enc->message, enc->region.sizeIdx); /* Module placement in region */ ModulePlacementEcc200(enc->message->array, enc->message->code, enc->region.sizeIdx, DmtxModuleOnRGB); width = 2 * enc->marginSize + (enc->region.symbolCols * enc->moduleSize); height = 2 * enc->marginSize + (enc->region.symbolRows * enc->moduleSize); bitsPerPixel = GetBitsPerPixel(enc->pixelPacking); if(bitsPerPixel == DmtxUndefined) return DmtxFail; assert(bitsPerPixel % 8 == 0); /* Allocate memory for the image to be generated */ pxl = (unsigned char *)malloc((width * bitsPerPixel / 8 + enc->rowPadBytes) * height); if(pxl == NULL) { perror("pixel malloc error"); return DmtxFail; } enc->image = dmtxImageCreate(pxl, width, height, enc->pixelPacking); if(enc->image == NULL) { perror("image malloc error"); return DmtxFail; } dmtxImageSetProp(enc->image, DmtxPropImageFlip, enc->imageFlip); dmtxImageSetProp(enc->image, DmtxPropRowPadBytes, enc->rowPadBytes); /* Insert finder and aligment pattern modules */ PrintPattern(enc); return DmtxPass; } /** * \brief Convert message into Data Mosaic image * * 1) count how many codewords it would take to encode the whole thing * 2) take ceiling N of codeword count divided by 3 * 3) using minimum symbol size that can accomodate N codewords: * 4) create several barcodes over iterations of increasing numbers of * input codewords until you go one too far * 5) if codewords remain after filling R, G, and B barcodes then go back * to 3 and try with next larger size * 6) take the 3 different images you created and write out a new barcode * * \param enc * \param inputSize * \param inputString * \param sizeIdxRequest * \return DmtxPass | DmtxFail */ extern DmtxPassFail dmtxEncodeDataMosaic(DmtxEncode *enc, int inputSize, unsigned char *inputString) { unsigned char *inputStringR, *inputStringG, *inputStringB; int tmpInputSize; int inputSizeR, inputSizeG, inputSizeB; int sizeIdxAttempt, sizeIdxFirst, sizeIdxLast; int row, col, mappingRows, mappingCols; DmtxEncode *encR, *encG, *encB; /* Use 1/3 (ceiling) of inputSize establish input size target */ tmpInputSize = (inputSize + 2) / 3; inputSizeR = tmpInputSize; inputSizeG = tmpInputSize; inputSizeB = inputSize - (inputSizeR + inputSizeG); inputStringR = inputString; inputStringG = inputStringR + inputSizeR; inputStringB = inputStringG + inputSizeG; /* Use 1/3 (floor) of dataWordCount establish first symbol size attempt */ sizeIdxFirst = FindSymbolSize(tmpInputSize, enc->sizeIdxRequest); if(sizeIdxFirst == DmtxUndefined) return DmtxFail; /* Set the last possible symbol size for this symbol shape or specific size request */ if(enc->sizeIdxRequest == DmtxSymbolSquareAuto) sizeIdxLast = DmtxSymbolSquareCount - 1; else if(enc->sizeIdxRequest == DmtxSymbolRectAuto) sizeIdxLast = DmtxSymbolSquareCount + DmtxSymbolRectCount - 1; else sizeIdxLast = sizeIdxFirst; encR = encG = encB = NULL; /* Try increasing symbol sizes until 3 of them can hold all input values */ for(sizeIdxAttempt = sizeIdxFirst; sizeIdxAttempt <= sizeIdxLast; sizeIdxAttempt++) { dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); encR = dmtxEncodeCreate(); encG = dmtxEncodeCreate(); encB = dmtxEncodeCreate(); /* Copy all settings from master DmtxEncode, including pointer to image and message, which is initially null */ *encR = *encG = *encB = *enc; dmtxEncodeSetProp(encR, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeSetProp(encG, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeSetProp(encB, DmtxPropSizeRequest, sizeIdxAttempt); /* RED LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encR, inputSizeR, inputStringR); if(encR->region.sizeIdx != sizeIdxAttempt) continue; /* GREEN LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encG, inputSizeG, inputStringG); if(encG->region.sizeIdx != sizeIdxAttempt) continue; /* BLUE LAYER - Holds temporary copy */ dmtxEncodeDataMatrix(encB, inputSizeB, inputStringB); if(encB->region.sizeIdx != sizeIdxAttempt) continue; /* If we get this far we found a fit */ break; } if(encR == NULL || encG == NULL || encB == NULL) { dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); return DmtxFail; } /* Now we have the correct sizeIdxAttempt, and they all fit into the desired size */ /* Perform the red portion of the final encode to set internals correctly */ dmtxEncodeSetProp(enc, DmtxPropSizeRequest, sizeIdxAttempt); dmtxEncodeDataMatrix(enc, inputSizeR, inputStringR); /* Zero out the array and overwrite the bits in 3 passes */ mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdxAttempt); mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdxAttempt); memset(enc->message->array, 0x00, sizeof(unsigned char) * enc->region.mappingRows * enc->region.mappingCols); ModulePlacementEcc200(enc->message->array, encR->message->code, sizeIdxAttempt, DmtxModuleOnRed); /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */ for(row = 0; row < mappingRows; row++) { for(col = 0; col < mappingCols; col++) { enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited)); } } ModulePlacementEcc200(enc->message->array, encG->message->code, sizeIdxAttempt, DmtxModuleOnGreen); /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */ for(row = 0; row < mappingRows; row++) { for(col = 0; col < mappingCols; col++) { enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited)); } } ModulePlacementEcc200(enc->message->array, encB->message->code, sizeIdxAttempt, DmtxModuleOnBlue); /* Destroy encR, encG, and encB */ dmtxEncodeDestroy(&encR); dmtxEncodeDestroy(&encG); dmtxEncodeDestroy(&encB); PrintPattern(enc); return DmtxPass; } /** * \brief Convert input into message using specific encodation scheme * \param buf * \param inputString * \param inputSize * \param scheme * \param sizeIdx * \return Count of encoded data words * * Future: pass DmtxEncode to this function with an error reason field, which * goes to EncodeSingle... too */ static int EncodeDataCodewords(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1) { int sizeIdx; /* Encode input string into data codewords */ switch(scheme) { case DmtxSchemeAutoBest: sizeIdx = EncodeOptimizeBest(input, output, sizeIdxRequest, fnc1); break; case DmtxSchemeAutoFast: sizeIdx = DmtxUndefined; /* EncodeAutoFast(input, output, sizeIdxRequest, passFail); */ break; default: sizeIdx = EncodeSingleScheme(input, output, sizeIdxRequest, scheme, fnc1); break; } return sizeIdx; } /** * \brief Write encoded message to image * \param enc * \return void */ static void PrintPattern(DmtxEncode *enc) { int i, j; int symbolRow, symbolCol; int pixelRow, pixelCol; int moduleStatus; size_t rowSize, height; int rgb[3]; double sxy, txy; DmtxMatrix3 m1, m2; DmtxVector2 vIn, vOut; txy = enc->marginSize; sxy = 1.0/enc->moduleSize; dmtxMatrix3Translate(m1, -txy, -txy); dmtxMatrix3Scale(m2, sxy, -sxy); dmtxMatrix3Multiply(enc->xfrm, m1, m2); dmtxMatrix3Translate(m1, txy, txy); dmtxMatrix3Scale(m2, enc->moduleSize, enc->moduleSize); dmtxMatrix3Multiply(enc->rxfrm, m2, m1); rowSize = dmtxImageGetProp(enc->image, DmtxPropRowSizeBytes); height = dmtxImageGetProp(enc->image, DmtxPropHeight); memset(enc->image->pxl, 0xff, rowSize * height); for(symbolRow = 0; symbolRow < enc->region.symbolRows; symbolRow++) { for(symbolCol = 0; symbolCol < enc->region.symbolCols; symbolCol++) { vIn.X = symbolCol; vIn.Y = symbolRow; dmtxMatrix3VMultiply(&vOut, &vIn, enc->rxfrm); pixelCol = (int)(vOut.X); pixelRow = (int)(vOut.Y); moduleStatus = dmtxSymbolModuleStatus(enc->message, enc->region.sizeIdx, symbolRow, symbolCol); if (enc->image->bytesPerPixel == 1) { for(i = pixelRow; i < pixelRow + enc->moduleSize; i++) { for(j = pixelCol; j < pixelCol + enc->moduleSize; j++) { rgb[0] = ((moduleStatus & DmtxModuleOnRed) != 0x00) ? 0 : 255; dmtxImageSetPixelValue(enc->image, j, i, 0, rgb[0]); } } } else { for(i = pixelRow; i < pixelRow + enc->moduleSize; i++) { for(j = pixelCol; j < pixelCol + enc->moduleSize; j++) { rgb[0] = ((moduleStatus & DmtxModuleOnRed) != 0x00) ? 0 : 255; rgb[1] = ((moduleStatus & DmtxModuleOnGreen) != 0x00) ? 0 : 255; rgb[2] = ((moduleStatus & DmtxModuleOnBlue) != 0x00) ? 0 : 255; /* dmtxImageSetRgb(enc->image, j, i, rgb); */ dmtxImageSetPixelValue(enc->image, j, i, 0, rgb[0]); dmtxImageSetPixelValue(enc->image, j, i, 1, rgb[1]); dmtxImageSetPixelValue(enc->image, j, i, 2, rgb[2]); } } } } } } libdmtx-0.7.7/dmtxencodeascii.c000066400000000000000000000136401423156660700165150ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodeascii.c * \brief ASCII encoding rules */ /** * Simple single scheme encoding uses "Normal" * The optimizer needs to track "Expanded" and "Compact" streams separately, so they * are called explicitly. * * Normal: Automatically collapses 2 consecutive digits into one codeword * Expanded: Uses a whole codeword to represent a digit (never collapses) * Compact: Collapses 2 digits into a single codeword or marks the stream * invalid if either values are not digits * * \param stream * \param option [Expanded|Compact|Normal] */ static void EncodeNextChunkAscii(DmtxEncodeStream *stream, int option) { DmtxByte v0, v1; DmtxBoolean compactDigits; if(StreamInputHasNext(stream)) { v0 = StreamInputAdvanceNext(stream); CHKERR; if((option == DmtxEncodeCompact || option == DmtxEncodeNormal) && StreamInputHasNext(stream)) { v1 = StreamInputPeekNext(stream); CHKERR; /* Check for FNC1 character */ if(stream->fnc1 != DmtxUndefined && (int)v1 == stream->fnc1) { v1 = 0; compactDigits = DmtxFalse; } else compactDigits = (ISDIGIT(v0) && ISDIGIT(v1)) ? DmtxTrue : DmtxFalse; } else /* option == DmtxEncodeFull */ { v1 = 0; compactDigits = DmtxFalse; } if(compactDigits == DmtxTrue) { /* Two adjacent digit chars: Make peek progress official and encode */ StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, 10 * (v0-'0') + (v1-'0') + 130); CHKERR; } else if(option == DmtxEncodeCompact) { /* Can't compact non-digits */ StreamMarkInvalid(stream, DmtxErrorCantCompactNonDigits); } else { /* Encode single ASCII value */ if(stream->fnc1 != DmtxUndefined && (int)v0 == stream->fnc1) { /* FNC1 */ AppendValueAscii(stream, DmtxValueFNC1); CHKERR; } else if(v0 < 128) { /* Regular ASCII */ AppendValueAscii(stream, v0 + 1); CHKERR; } else { /* Extended ASCII */ AppendValueAscii(stream, DmtxValueAsciiUpperShift); CHKERR; AppendValueAscii(stream, v0 - 127); CHKERR; } } } } /** * this code is separated from EncodeNextChunkAscii() because it needs to be * called directly elsewhere */ static void AppendValueAscii(DmtxEncodeStream *stream, DmtxByte value) { CHKSCHEME(DmtxSchemeAscii); StreamOutputChainAppend(stream, value); CHKERR; stream->outputChainValueCount++; } /** * * */ static void CompleteIfDoneAscii(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; PadRemainingInAscii(stream, sizeIdx); CHKERR; StreamMarkComplete(stream, sizeIdx); } } /** * Can we just receive a length to pad here? I don't like receiving * sizeIdxRequest (or sizeIdx) this late in the game */ static void PadRemainingInAscii(DmtxEncodeStream *stream, int sizeIdx) { int symbolRemaining; DmtxByte padValue; CHKSCHEME(DmtxSchemeAscii); CHKSIZE; symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); /* First pad character is not randomized */ if(symbolRemaining > 0) { padValue = DmtxValueAsciiPad; StreamOutputChainAppend(stream, padValue); CHKERR; symbolRemaining--; } /* All remaining pad characters are randomized based on character position */ while(symbolRemaining > 0) { padValue = Randomize253State(DmtxValueAsciiPad, stream->output->length + 1); StreamOutputChainAppend(stream, padValue); CHKERR; symbolRemaining--; } } /** * consider receiving instantiated DmtxByteList instead of the output components */ static DmtxByteList EncodeTmpRemainingInAscii(DmtxEncodeStream *stream, DmtxByte *storage, int capacity, DmtxPassFail *passFail) { DmtxEncodeStream streamAscii; DmtxByteList output = dmtxByteListBuild(storage, capacity); /* Create temporary copy of stream that writes to storage */ streamAscii = *stream; streamAscii.currentScheme = DmtxSchemeAscii; streamAscii.outputChainValueCount = 0; streamAscii.outputChainWordCount = 0; streamAscii.reason = NULL; streamAscii.sizeIdx = DmtxUndefined; streamAscii.status = DmtxStatusEncoding; streamAscii.output = &output; while(dmtxByteListHasCapacity(streamAscii.output)) { if(StreamInputHasNext(&streamAscii)) EncodeNextChunkAscii(&streamAscii, DmtxEncodeNormal); /* No CHKERR */ else break; } /* * We stopped encoding before attempting to write beyond output boundary so * any stream errors are truly unexpected. The passFail status indicates * whether output.length can be trusted by the calling function. */ if(streamAscii.status == DmtxStatusInvalid || streamAscii.status == DmtxStatusFatal) *passFail = DmtxFail; else *passFail = DmtxPass; return output; } /** * \brief Randomize 253 state * \param codewordValue * \param codewordPosition * \return Randomized value */ static DmtxByte Randomize253State(DmtxByte cwValue, int cwPosition) { int pseudoRandom, tmp; pseudoRandom = ((149 * cwPosition) % 253) + 1; tmp = cwValue + pseudoRandom; if(tmp > 254) tmp -= 254; assert(tmp >= 0 && tmp < 256); return (DmtxByte)tmp; } libdmtx-0.7.7/dmtxencodebase256.c000066400000000000000000000200151423156660700165660ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodebase256.c * \brief Base 256 encoding rules */ /** * * */ static void EncodeNextChunkBase256(DmtxEncodeStream *stream) { DmtxByte value; if(StreamInputHasNext(stream)) { /* Check for FNC1 character, which needs to be sent in ASCII */ value = StreamInputPeekNext(stream); CHKERR; if(stream->fnc1 != DmtxUndefined && (int)value == stream->fnc1) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, DmtxValueFNC1); CHKERR; return; } value = StreamInputAdvanceNext(stream); CHKERR; AppendValueBase256(stream, value); CHKERR; } } /** * * */ static void AppendValueBase256(DmtxEncodeStream *stream, DmtxByte value) { CHKSCHEME(DmtxSchemeBase256); StreamOutputChainAppend(stream, Randomize255State(value, stream->output->length + 1)); CHKERR; stream->outputChainValueCount++; UpdateBase256ChainHeader(stream, DmtxUndefined); CHKERR; } /** * check remaining symbol capacity and remaining codewords * if the chain can finish perfectly at the end of symbol data words there is a * special one-byte length header value that can be used (i think ... read the * spec again before commiting to anything) */ static void CompleteIfDoneBase256(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; int headerByteCount, outputLength, symbolRemaining; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount; assert(headerByteCount == 1 || headerByteCount == 2); /* Check for special case where every last symbol word is used */ if(headerByteCount == 2) { /* Find symbol size as if headerByteCount was only 1 */ outputLength = stream->output->length - 1; sizeIdx = FindSymbolSize(outputLength, sizeIdxRequest); /* No CHKSIZE */ if(sizeIdx != DmtxUndefined) { symbolRemaining = GetRemainingSymbolCapacity(outputLength, sizeIdx); if(symbolRemaining == 0) { /* Perfect fit -- complete encoding */ UpdateBase256ChainHeader(stream, sizeIdx); CHKERR; StreamMarkComplete(stream, sizeIdx); return; } } } /* Normal case */ sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); PadRemainingInAscii(stream, sizeIdx); StreamMarkComplete(stream, sizeIdx); } } /** * * */ static void UpdateBase256ChainHeader(DmtxEncodeStream *stream, int perfectSizeIdx) { int headerIndex; int outputLength; int headerByteCount; int symbolDataWords; DmtxBoolean perfectFit; DmtxByte headerValue0; DmtxByte headerValue1; outputLength = stream->outputChainValueCount; headerIndex = stream->output->length - stream->outputChainWordCount; headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount; perfectFit = (perfectSizeIdx == DmtxUndefined) ? DmtxFalse : DmtxTrue; /* * If requested perfect fit verify symbol capacity against final length */ if(perfectFit) { symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, perfectSizeIdx); if(symbolDataWords != stream->output->length - 1) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } } /* * Adjust header to hold correct number of bytes, not worrying about the * values held there until below. Note: Header bytes are not considered * scheme "values" so we can insert or remove them without updating the * outputChainValueCount. */ if(headerByteCount == 0 && stream->outputChainWordCount == 0) { /* No output words written yet -- insert single header byte */ StreamOutputChainAppend(stream, 0); CHKERR; headerByteCount++; } else if(!perfectFit && headerByteCount == 1 && outputLength > 249) { /* Beyond 249 bytes requires a second header byte */ Base256OutputChainInsertFirst(stream); CHKERR; headerByteCount++; } else if(perfectFit && headerByteCount == 2) { /* Encoding to exact end of symbol only requires single byte */ Base256OutputChainRemoveFirst(stream); CHKERR; headerByteCount--; } /* * Encode header byte(s) with current length */ if(!perfectFit && headerByteCount == 1 && outputLength <= 249) { /* Normal condition for chain length < 250 bytes */ headerValue0 = Randomize255State(outputLength, headerIndex + 1); StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; } else if(!perfectFit && headerByteCount == 2 && outputLength > 249) { /* Normal condition for chain length >= 250 bytes */ headerValue0 = Randomize255State(outputLength/250 + 249, headerIndex + 1); StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; headerValue1 = Randomize255State(outputLength%250, headerIndex + 2); StreamOutputSet(stream, headerIndex + 1, headerValue1); CHKERR; } else if(perfectFit && headerByteCount == 1) { /* Special condition when Base 256 stays in effect to end of symbol */ headerValue0 = Randomize255State(0, headerIndex + 1); /* XXX replace magic value 0? */ StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; } else { StreamMarkFatal(stream, DmtxErrorUnknown); return; } } /** * insert element at beginning of chain, shifting all following elements forward by one * used for binary length changes */ static void Base256OutputChainInsertFirst(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; int i, chainStart; chainStart = stream->output->length - stream->outputChainWordCount; dmtxByteListPush(stream->output, 0, &passFail); if(passFail == DmtxPass) { for(i = stream->output->length - 1; i > chainStart; i--) { value = UnRandomize255State(stream->output->b[i-1], i); stream->output->b[i] = Randomize255State(value, i + 1); } stream->outputChainWordCount++; } else { StreamMarkFatal(stream, DmtxErrorUnknown); } } /** * remove first element from chain, shifting all following elements back by one * used for binary length changes end condition */ static void Base256OutputChainRemoveFirst(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; int i, chainStart; chainStart = stream->output->length - stream->outputChainWordCount; for(i = chainStart; i < stream->output->length - 1; i++) { value = UnRandomize255State(stream->output->b[i+1], i+2); stream->output->b[i] = Randomize255State(value, i + 1); } dmtxByteListPop(stream->output, &passFail); if(passFail == DmtxPass) stream->outputChainWordCount--; else StreamMarkFatal(stream, DmtxErrorUnknown); } /** * \brief Randomize 255 state * \param value * \param position * \return Randomized value */ static DmtxByte Randomize255State(DmtxByte value, int position) { int pseudoRandom, tmp; pseudoRandom = ((149 * position) % 255) + 1; tmp = value + pseudoRandom; return (tmp <= 255) ? tmp : tmp - 256; } /** * \brief Unrandomize 255 state * \param value * \param idx * \return Unrandomized value */ static unsigned char UnRandomize255State(unsigned char value, int idx) { int pseudoRandom; int tmp; pseudoRandom = ((149 * idx) % 255) + 1; tmp = value - pseudoRandom; if(tmp < 0) tmp += 256; assert(tmp >= 0 && tmp < 256); return (unsigned char)tmp; } libdmtx-0.7.7/dmtxencodec40textx12.c000066400000000000000000000434621423156660700172600ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodec40textx12.c * \brief C40/Text/X12 encoding rules */ #undef CHKERR #define CHKERR { if(stream->status != DmtxStatusEncoding) { return; } } #undef CHKSIZE #define CHKSIZE { if(sizeIdx == DmtxUndefined) { StreamMarkInvalid(stream, DmtxErrorUnknown); return; } } #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } } #undef RETURN_IF_FAIL #define RETURN_IF_FAIL { if(*passFail == DmtxFail) return; } /** * * */ static void EncodeNextChunkCTX(DmtxEncodeStream *stream, int sizeIdxRequest) { int i; DmtxPassFail passFail; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); while(StreamInputHasNext(stream)) { if(stream->currentScheme == DmtxSchemeX12) { /* Check for FNC1 character */ inputValue = StreamInputPeekNext(stream); CHKERR; if(stream->fnc1 != DmtxUndefined && (int)inputValue == stream->fnc1) { /* X12 does not allow partial blocks, resend last 1 or 2 as ASCII */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < valueList.length % 3; i++) StreamInputAdvancePrev(stream); CHKERR; while(i) { inputValue = StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, inputValue + 1); CHKERR; i--; } StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, DmtxValueFNC1); CHKERR; return; } } inputValue = StreamInputAdvanceNext(stream); CHKERR; /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, stream->currentScheme, &passFail, stream->fnc1); if(passFail == DmtxFail) { /* XXX Perhaps PushCTXValues should return this error code */ StreamMarkInvalid(stream, DmtxErrorUnsupportedCharacter); return; } /* If there at least 3 CTX values available encode them to output */ while(valueList.length >= 3) { AppendValuesCTX(stream, &valueList); CHKERR; ShiftValueListBy3(&valueList, &passFail); CHKPASS; } /* Finished on byte boundary -- done with current chunk */ if(valueList.length == 0) break; } /* * Special case: If all input values have been consumed and 1 or 2 unwritten * C40/Text/X12 values remain, finish encoding the symbol according to the * established end-of-symbol conditions. */ if(!StreamInputHasNext(stream) && valueList.length > 0) { if(stream->currentScheme == DmtxSchemeX12) { CompletePartialX12(stream, &valueList, sizeIdxRequest); CHKERR; } else { CompletePartialC40Text(stream, &valueList, sizeIdxRequest); CHKERR; } } } /** * * */ static void AppendValuesCTX(DmtxEncodeStream *stream, DmtxByteList *valueList) { int pairValue; DmtxByte cw0, cw1; if(!IsCTX(stream->currentScheme)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } if(valueList->length < 3) { StreamMarkFatal(stream, DmtxErrorIncompleteValueList); return; } /* Build codewords from computed value */ pairValue = (1600 * valueList->b[0]) + (40 * valueList->b[1]) + valueList->b[2] + 1; cw0 = pairValue / 256; cw1 = pairValue % 256; /* Append 2 codewords */ StreamOutputChainAppend(stream, cw0); CHKERR; StreamOutputChainAppend(stream, cw1); CHKERR; /* Update count for 3 encoded values */ stream->outputChainValueCount += 3; } /** * * */ static void AppendUnlatchCTX(DmtxEncodeStream *stream) { if(!IsCTX(stream->currentScheme)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Verify we are on byte boundary */ if(stream->outputChainValueCount % 3 != 0) { StreamMarkInvalid(stream, DmtxErrorNotOnByteBoundary); return; } StreamOutputChainAppend(stream, DmtxValueCTXUnlatch); CHKERR; stream->outputChainValueCount++; } /** * Complete C40/Text/X12 encoding if it matches a known end-of-symbol condition. * * Term Trip Symbol Codeword * Cond Size Remain Sequence * ---- ---- ------ ----------------------- * (a) 3 2 Special case * - - UNLATCH [PAD] */ static void CompleteIfDoneCTX(DmtxEncodeStream *stream, int sizeIdxRequest) { int sizeIdx; int symbolRemaining; if(stream->status == DmtxStatusComplete) return; if(!StreamInputHasNext(stream)) { sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); if(symbolRemaining > 0) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; PadRemainingInAscii(stream, sizeIdx); } StreamMarkComplete(stream, sizeIdx); } } /** * The remaining values can exist in 3 possible cases: * * a) 1 C40/Text/X12 remaining == 1 data * b) 2 C40/Text/X12 remaining == 1 shift + 1 data * c) 2 C40/Text/X12 remaining == 1 data + 1 data * * To distinguish between cases (b) and (c), encode the final input value to * C40/Text/X12 in a temporary location and check the resulting length. If * it expands to multiple values it represents (b); otherwise it is (c). This * accounts for both shift and upper shift conditions. * * Note that in cases (a) and (c) the final C40/Text/X12 value encoded in the * previous chunk may have been a shift value, but this will be ignored by * the decoder due to the implicit shift to ASCII. <-- what if symbol is much * larger though? * * Term Value Symbol Codeword * Cond Count Remain Sequence * ---- ------- ------ ------------------------ * (b) C40 2 2 C40+C40+0 * (d) ASCII 1 1 ASCII (implicit unlatch) * (c) ASCII 1 2 UNLATCH ASCII * - - UNLATCH (finish ASCII) */ static void CompletePartialC40Text(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx1, sizeIdx2; int symbolRemaining1, symbolRemaining2; DmtxPassFail passFail; DmtxByte inputValue; DmtxByte outputTmpStorage[4]; DmtxByteList outputTmp = dmtxByteListBuild(outputTmpStorage, sizeof(outputTmpStorage)); if(stream->currentScheme != DmtxSchemeC40 && stream->currentScheme != DmtxSchemeText) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); sizeIdx1 = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); sizeIdx2 = FindSymbolSize(stream->output->length + 2, sizeIdxRequest); symbolRemaining1 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx1); symbolRemaining2 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx2); if(valueList->length == 2 && symbolRemaining2 == 2) { /* End of symbol condition (b) -- Use Shift1 to pad final list value */ dmtxByteListPush(valueList, DmtxValueCTXShift1, &passFail); CHKPASS; AppendValuesCTX(stream, valueList); CHKERR; StreamMarkComplete(stream, sizeIdx2); } else { /* * Rollback progress of previously consumed input value(s) since ASCII * encoder will be used to finish the symbol. 2 rollbacks are needed if * valueList holds 2 data words (i.e., not shifts or upper shifts). */ StreamInputAdvancePrev(stream); CHKERR; inputValue = StreamInputPeekNext(stream); CHKERR; /* Test-encode most recently consumed input value to C40/Text/X12 */ PushCTXValues(&outputTmp, inputValue, stream->currentScheme, &passFail, stream->fnc1); if(valueList->length == 2 && outputTmp.length == 1) StreamInputAdvancePrev(stream); CHKERR; /* Re-use outputTmp to hold ASCII representation of 1-2 input values */ /* XXX Refactor how the DmtxByteList is passed back here */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); if(passFail == DmtxFail) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } if(outputTmp.length == 1 && symbolRemaining1 == 1) { /* End of symbol condition (d) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } else { /* Finish in ASCII (c) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx1 = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx1); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } } } /** * Partial chunks are not valid in X12. Encode using ASCII instead, using * an implied unlatch if there is exactly one ascii codeword and one symbol * codeword remaining. Otherwise use explicit unlatch. */ static void CompletePartialX12(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx; int symbolRemaining; DmtxPassFail passFail; DmtxByte outputTmpStorage[2]; DmtxByteList outputTmp; if(stream->currentScheme != DmtxSchemeX12) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); /* Roll back input progress */ for(i = 0; i < valueList->length; i++) { StreamInputAdvancePrev(stream); CHKERR; } /* Encode up to 2 codewords to a temporary stream */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); sizeIdx = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); if(outputTmp.length == 1 && symbolRemaining == 1) { /* End of symbol condition (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } else { /* Finish in ASCII (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } } /** * Return DmtxTrue 1 or 2 X12 values remain, otherwise DmtxFalse */ static DmtxBoolean PartialX12ChunkRemains(DmtxEncodeStream *stream) { DmtxEncodeStream streamTmp; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); DmtxPassFail passFail; /* Create temporary copy of stream to track test input progress */ streamTmp = *stream; streamTmp.currentScheme = DmtxSchemeX12; streamTmp.outputChainValueCount = 0; streamTmp.outputChainWordCount = 0; streamTmp.reason = NULL; streamTmp.sizeIdx = DmtxUndefined; streamTmp.status = DmtxStatusEncoding; streamTmp.output = NULL; while(StreamInputHasNext(&streamTmp)) { inputValue = StreamInputAdvanceNext(&streamTmp); if(stream->status != DmtxStatusEncoding) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, streamTmp.currentScheme, &passFail, stream->fnc1); if(passFail == DmtxFail) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Not a final partial chunk */ if(valueList.length >= 3) return DmtxFalse; } return (valueList.length == 0) ? DmtxFalse : DmtxTrue; } /** * * */ static void PushCTXValues(DmtxByteList *valueList, DmtxByte inputValue, int targetScheme, DmtxPassFail *passFail, int fnc1) { assert(valueList->length <= 2); /* Handle extended ASCII with Upper Shift character */ if(inputValue > 127 && (fnc1 == DmtxUndefined || (int)inputValue != fnc1)) { if(targetScheme == DmtxSchemeX12) { *passFail = DmtxFail; return; } else { dmtxByteListPush(valueList, DmtxValueCTXShift2, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, 30, passFail); RETURN_IF_FAIL; inputValue -= 128; } } /* Handle all other characters according to encodation scheme */ if(targetScheme == DmtxSchemeX12) { if(inputValue == 13) { dmtxByteListPush(valueList, 0, passFail); RETURN_IF_FAIL; } else if(inputValue == 42) { dmtxByteListPush(valueList, 1, passFail); RETURN_IF_FAIL; } else if(inputValue == 62) { dmtxByteListPush(valueList, 2, passFail); RETURN_IF_FAIL; } else if(inputValue == 32) { dmtxByteListPush(valueList, 3, passFail); RETURN_IF_FAIL; } else if(inputValue >= 48 && inputValue <= 57) { dmtxByteListPush(valueList, inputValue - 44, passFail); RETURN_IF_FAIL; } else if(inputValue >= 65 && inputValue <= 90) { dmtxByteListPush(valueList, inputValue - 51, passFail); RETURN_IF_FAIL; } else { *passFail = DmtxFail; return; } } else { /* targetScheme is C40 or Text */ /* Check for FNC1 character */ if(fnc1 != DmtxUndefined && (int)inputValue == fnc1) { dmtxByteListPush(valueList, DmtxValueCTXShift2, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, 27, passFail); RETURN_IF_FAIL; /* C40 version of FNC1 */ } else if(inputValue <= 31) { dmtxByteListPush(valueList, DmtxValueCTXShift1, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue, passFail); RETURN_IF_FAIL; } else if(inputValue == 32) { dmtxByteListPush(valueList, 3, passFail); RETURN_IF_FAIL; } else if(inputValue <= 47) { dmtxByteListPush(valueList, DmtxValueCTXShift2, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue - 33, passFail); RETURN_IF_FAIL; } else if(inputValue <= 57) { dmtxByteListPush(valueList, inputValue - 44, passFail); RETURN_IF_FAIL; } else if(inputValue <= 64) { dmtxByteListPush(valueList, DmtxValueCTXShift2, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue - 43, passFail); RETURN_IF_FAIL; } else if(inputValue <= 90 && targetScheme == DmtxSchemeC40) { dmtxByteListPush(valueList, inputValue - 51, passFail); RETURN_IF_FAIL; } else if(inputValue <= 90 && targetScheme == DmtxSchemeText) { dmtxByteListPush(valueList, DmtxValueCTXShift3, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue - 64, passFail); RETURN_IF_FAIL; } else if(inputValue <= 95) { dmtxByteListPush(valueList, DmtxValueCTXShift2, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue - 69, passFail); RETURN_IF_FAIL; } else if(inputValue == 96 && targetScheme == DmtxSchemeText) { dmtxByteListPush(valueList, DmtxValueCTXShift3, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, 0, passFail); RETURN_IF_FAIL; } else if(inputValue <= 122 && targetScheme == DmtxSchemeText) { dmtxByteListPush(valueList, inputValue - 83, passFail); RETURN_IF_FAIL; } else if(inputValue <= 127) { dmtxByteListPush(valueList, DmtxValueCTXShift3, passFail); RETURN_IF_FAIL; dmtxByteListPush(valueList, inputValue - 96, passFail); RETURN_IF_FAIL; } else { *passFail = DmtxFail; return; } } *passFail = DmtxPass; } /** * * */ static DmtxBoolean IsCTX(int scheme) { DmtxBoolean isCTX; if(scheme == DmtxSchemeC40 || scheme == DmtxSchemeText || scheme == DmtxSchemeX12) isCTX = DmtxTrue; else isCTX = DmtxFalse; return isCTX; } /** * * */ static void ShiftValueListBy3(DmtxByteList *list, DmtxPassFail *passFail) { int i; /* Shift values */ for(i = 0; i < list->length - 3; i++) list->b[i] = list->b[i+3]; /* Shorten list by 3 (or less) */ for(i = 0; i < 3; i++) { dmtxByteListPop(list, passFail); if(*passFail == DmtxFail) return; if(list->length == 0) break; } *passFail = DmtxPass; } libdmtx-0.7.7/dmtxencodeedifact.c000066400000000000000000000132141423156660700170210ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodeedifact.c * \brief Edifact encoding rules */ /** * * */ static void EncodeNextChunkEdifact(DmtxEncodeStream *stream) { DmtxByte value; if(StreamInputHasNext(stream)) { /* Check for FNC1 character, which needs to be sent in ASCII */ value = StreamInputPeekNext(stream); CHKERR; if((value < 32 || value > 94)) { StreamMarkInvalid(stream, DmtxChannelUnsupportedChar); return; } if (stream->fnc1 != DmtxUndefined && (int)value == stream->fnc1) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; StreamInputAdvanceNext(stream); CHKERR; AppendValueAscii(stream, DmtxValueFNC1); CHKERR; return; } value = StreamInputAdvanceNext(stream); CHKERR; AppendValueEdifact(stream, value); CHKERR; } } /** * * */ static void AppendValueEdifact(DmtxEncodeStream *stream, DmtxByte value) { DmtxByte edifactValue, previousOutput; CHKSCHEME(DmtxSchemeEdifact); /* * TODO: KECA -> korean, circles * TODO: UNOX -> ISO-2022-JP * TODO: and so on */ if(value < 31 || value > 94) { StreamMarkInvalid(stream, DmtxChannelUnsupportedChar); return; } edifactValue = (value & 0x3f) << 2; switch(stream->outputChainValueCount % 4) { case 0: StreamOutputChainAppend(stream, edifactValue); CHKERR; break; case 1: previousOutput = StreamOutputChainRemoveLast(stream); CHKERR; StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 6)); CHKERR; StreamOutputChainAppend(stream, edifactValue << 2); CHKERR; break; case 2: previousOutput = StreamOutputChainRemoveLast(stream); CHKERR; StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 4)); CHKERR; StreamOutputChainAppend(stream, edifactValue << 4); CHKERR; break; case 3: previousOutput = StreamOutputChainRemoveLast(stream); CHKERR; StreamOutputChainAppend(stream, previousOutput | (edifactValue >> 2)); CHKERR; break; } stream->outputChainValueCount++; } /** * Complete EDIFACT encoding if it matches a known end-of-symbol condition. * * Term Clean Symbol ASCII Codeword * Cond Bound Remain Remain Sequence * ---- ----- ------ ------ ----------- * (a) Y 0 0 [none] * (b) Y 1 0 PAD * (c) Y 1 1 ASCII * (d) Y 2 0 PAD PAD * (e) Y 2 1 ASCII PAD * (f) Y 2 2 ASCII ASCII * - - 0 UNLATCH * * If not matching any of the above, continue without doing anything. */ static void CompleteIfDoneEdifact(DmtxEncodeStream *stream, int sizeIdxRequest) { int i; int sizeIdx; int symbolRemaining; DmtxBoolean cleanBoundary; DmtxPassFail passFail; DmtxByte outputTmpStorage[3]; DmtxByteList outputTmp; if(stream->status == DmtxStatusComplete) return; /* * If we just completed a triplet (cleanBoundary), 1 or 2 symbol codewords * remain, and our remaining inputs (if any) represented in ASCII would fit * in the remaining space, encode them in ASCII with an implicit unlatch. */ cleanBoundary = (stream->outputChainValueCount % 4 == 0) ? DmtxTrue : DmtxFalse; if(cleanBoundary == DmtxTrue) { /* Encode up to 3 codewords to a temporary stream */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); if(passFail == DmtxFail) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } if(outputTmp.length < 3) { /* Find minimum symbol size for projected length */ sizeIdx = FindSymbolSize(stream->output->length + outputTmp.length, sizeIdxRequest); CHKSIZE; /* Find remaining capacity over current length */ symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); CHKERR; if(symbolRemaining < 3 && outputTmp.length <= symbolRemaining) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) { AppendValueAscii(stream, outputTmp.b[i]); CHKERR; } /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; /* Pad remaining if necessary */ PadRemainingInAscii(stream, sizeIdx); CHKERR; StreamMarkComplete(stream, sizeIdx); return; } } } if(!StreamInputHasNext(stream)) { sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); CHKERR; /* Explicit unlatch required unless on clean boundary and full symbol */ if(cleanBoundary == DmtxFalse || symbolRemaining > 0) { EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE; PadRemainingInAscii(stream, sizeIdx); CHKERR; } StreamMarkComplete(stream, sizeIdx); } } libdmtx-0.7.7/dmtxencodeoptimize.c000066400000000000000000000323441423156660700172670ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodeoptimize.c * \brief Logic for optimized (multiple scheme) encoding */ #define DUMPSTREAMS 0 enum SchemeState { AsciiFull, AsciiCompactOffset0, /* 0 offset from first regular input value */ AsciiCompactOffset1, C40Offset0, /* 0 offset from first expanded C40 value */ C40Offset1, C40Offset2, TextOffset0, /* 0 offset from first expanded Text value */ TextOffset1, TextOffset2, X12Offset0, /* 0 offset from first expanded X12 value */ X12Offset1, X12Offset2, EdifactOffset0, /* 0 offset from first regular input value */ EdifactOffset1, EdifactOffset2, EdifactOffset3, Base256, SchemeStateCount }; #if DUMPSTREAMS static void DumpStreams(DmtxEncodeStream *streamBest) { enum SchemeState state; char prefix[32]; fprintf(stdout, "----------------------------------------\n"); for(state = 0; state < SchemeStateCount; state++) { if(streamBest[state].status == DmtxStatusEncoding || streamBest[state].status == DmtxStatusComplete) fprintf(stdout, "\"%c\" ", streamBest[state].input->b[streamBest[state].inputNext-1]); else fprintf(stdout, " "); switch(streamBest[state].status) { case DmtxStatusEncoding: snprintf(prefix, sizeof(prefix), "%2d (%s): ", state, " encode "); break; case DmtxStatusComplete: snprintf(prefix, sizeof(prefix), "%2d (%s): ", state, "complete"); break; case DmtxStatusInvalid: snprintf(prefix, sizeof(prefix), "%2d (%s): ", state, "invalid "); break; case DmtxStatusFatal: snprintf(prefix, sizeof(prefix), "%2d (%s): ", state, " fatal "); break; } dmtxByteListPrint(streamBest[state].output, prefix); } } #endif /** * * */ static int EncodeOptimizeBest(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, int fnc1) { enum SchemeState state; int inputNext, c40ValueCount, textValueCount, x12ValueCount; int sizeIdx; DmtxEncodeStream *winner; DmtxPassFail passFail; DmtxEncodeStream streamsBest[SchemeStateCount]; DmtxEncodeStream streamsTemp[SchemeStateCount]; DmtxByte outputsBestStorage[SchemeStateCount][4096]; DmtxByte outputsTempStorage[SchemeStateCount][4096]; DmtxByte ctxTempStorage[4]; DmtxByteList outputsBest[SchemeStateCount]; DmtxByteList outputsTemp[SchemeStateCount]; DmtxByteList ctxTemp = dmtxByteListBuild(ctxTempStorage, sizeof(ctxTempStorage)); /* Initialize all streams with their own output storage */ for(state = 0; state < SchemeStateCount; state++) { outputsBest[state] = dmtxByteListBuild(outputsBestStorage[state], sizeof(outputsBestStorage[state])); outputsTemp[state] = dmtxByteListBuild(outputsTempStorage[state], sizeof(outputsTempStorage[state])); streamsBest[state] = StreamInit(input, &(outputsBest[state])); streamsTemp[state] = StreamInit(input, &(outputsTemp[state])); streamsBest[state].fnc1 = fnc1; streamsTemp[state].fnc1 = fnc1; } c40ValueCount = textValueCount = x12ValueCount = 0; for(inputNext = 0; inputNext < input->length; inputNext++) { StreamAdvanceFromBest(streamsTemp, streamsBest, AsciiFull, sizeIdxRequest); AdvanceAsciiCompact(streamsTemp, streamsBest, AsciiCompactOffset0, inputNext, sizeIdxRequest); AdvanceAsciiCompact(streamsTemp, streamsBest, AsciiCompactOffset1, inputNext, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset0, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset1, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset2, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset0, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset1, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset2, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset0, inputNext, x12ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset1, inputNext, x12ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset2, inputNext, x12ValueCount, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset0, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset1, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset2, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset3, inputNext, sizeIdxRequest); StreamAdvanceFromBest(streamsTemp, streamsBest, Base256, sizeIdxRequest); /* Overwrite best streams with new results */ for(state = 0; state < SchemeStateCount; state++) { if(streamsBest[state].status != DmtxStatusComplete) StreamCopy(&(streamsBest[state]), &(streamsTemp[state])); } dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeC40, &passFail, fnc1); c40ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeText, &passFail, fnc1); textValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeX12, &passFail, fnc1); x12ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); #if DUMPSTREAMS DumpStreams(streamsBest); #endif } /* Choose the overall winner */ winner = NULL; for(state = 0; state < SchemeStateCount; state++) { if(streamsBest[state].status == DmtxStatusComplete) { if(winner == NULL || streamsBest[state].output->length < winner->output->length) winner = &(streamsBest[state]); } } /* Copy winner to output */ if(winner == NULL) { sizeIdx = DmtxUndefined; } else { dmtxByteListCopy(output, winner->output, &passFail); sizeIdx = (passFail == DmtxPass) ? winner->sizeIdx : DmtxUndefined; } return sizeIdx; } /** * It's safe to compare output length because all targetState combinations * start on same input and encodes same number of inputs. Only difference * is the number of latches/unlatches that are also encoded */ static void StreamAdvanceFromBest(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int sizeIdxRequest) { enum SchemeState fromState; DmtxScheme targetScheme; DmtxEncodeOption encodeOption; DmtxByte outputTempStorage[4096]; DmtxByteList outputTemp = dmtxByteListBuild(outputTempStorage, sizeof(outputTempStorage)); DmtxEncodeStream streamTemp; DmtxEncodeStream *targetStream = &(streamsNext[targetState]); streamTemp.output = &outputTemp; /* Set directly instead of calling StreamInit() */ targetScheme = GetScheme(targetState); if(targetState == AsciiFull) encodeOption = DmtxEncodeFull; else if(targetState == AsciiCompactOffset0 || targetState == AsciiCompactOffset1) encodeOption = DmtxEncodeCompact; else encodeOption = DmtxEncodeNormal; for(fromState = 0; fromState < SchemeStateCount; fromState++) { if(streamsBest[fromState].status != DmtxStatusEncoding || ValidStateSwitch(fromState, targetState) == DmtxFalse) { continue; } StreamCopy(&streamTemp, &(streamsBest[fromState])); EncodeNextChunk(&streamTemp, targetScheme, encodeOption, sizeIdxRequest); if(fromState == 0 || (streamTemp.status != DmtxStatusInvalid && streamTemp.output->length < targetStream->output->length)) { StreamCopy(targetStream, &streamTemp); } } } /** * */ static void AdvanceAsciiCompact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case AsciiCompactOffset0: isStartState = (inputNext % 2 == 0) ? DmtxTrue : DmtxFalse; break; case AsciiCompactOffset1: isStartState = (inputNext % 2 == 1) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } } /** * */ static void AdvanceCTX(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int ctxValueCount, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; /* we won't actually use inputNext here */ switch(targetState) { case C40Offset0: case TextOffset0: case X12Offset0: isStartState = (ctxValueCount % 3 == 0) ? DmtxTrue : DmtxFalse; break; case C40Offset1: case TextOffset1: case X12Offset1: isStartState = (ctxValueCount % 3 == 1) ? DmtxTrue : DmtxFalse; break; case C40Offset2: case TextOffset2: case X12Offset2: isStartState = (ctxValueCount % 3 == 2) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } } /** * */ static void AdvanceEdifact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case EdifactOffset0: isStartState = (inputNext % 4 == 0) ? DmtxTrue : DmtxFalse; break; case EdifactOffset1: isStartState = (inputNext % 4 == 1) ? DmtxTrue : DmtxFalse; break; case EdifactOffset2: isStartState = (inputNext % 4 == 2) ? DmtxTrue : DmtxFalse; break; case EdifactOffset3: isStartState = (inputNext % 4 == 3) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); if(currentStream->status == DmtxStatusEncoding && currentStream->currentScheme == DmtxSchemeEdifact) EncodeNextChunk(targetStream, DmtxSchemeEdifact, DmtxEncodeNormal, sizeIdxRequest); else StreamMarkInvalid(targetStream, DmtxErrorUnknown); } } /** * * */ static int GetScheme(int state) { DmtxScheme scheme; switch(state) { case AsciiFull: case AsciiCompactOffset0: case AsciiCompactOffset1: scheme = DmtxSchemeAscii; break; case C40Offset0: case C40Offset1: case C40Offset2: scheme = DmtxSchemeC40; break; case TextOffset0: case TextOffset1: case TextOffset2: scheme = DmtxSchemeText; break; case X12Offset0: case X12Offset1: case X12Offset2: scheme = DmtxSchemeX12; break; case EdifactOffset0: case EdifactOffset1: case EdifactOffset2: case EdifactOffset3: scheme = DmtxSchemeEdifact; break; case Base256: scheme = DmtxSchemeBase256; break; default: scheme = DmtxUndefined; break; } return scheme; } /** * * */ static DmtxBoolean ValidStateSwitch(int fromState, int targetState) { DmtxBoolean validStateSwitch; DmtxScheme fromScheme = GetScheme(fromState); DmtxScheme toScheme = GetScheme(targetState); if(fromScheme == toScheme && fromState != targetState && fromState != AsciiFull && targetState != AsciiFull) { validStateSwitch = DmtxFalse; } else { validStateSwitch = DmtxTrue; } return validStateSwitch; } libdmtx-0.7.7/dmtxencodescheme.c000066400000000000000000000175261423156660700167000ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodescheme.c * \brief Logic for encoding in single scheme */ /** * In this file: * * A "word" refers to a full codeword byte to be appended to the encoded output. * * A "value" refers to any scheme value being appended to the output stream, * regardless of how many bytes are used to represent it. Examples: * * ASCII: 1 value in 1 codeword * ASCII (digits): 2 values in 1 codeword * C40/Text/X12: 3 values in 2 codewords * C40/Text/X12 (unlatch): 1 values in 1 codeword * EDIFACT: 4 values in 3 codewords * Base 256: 1 value in 1 codeword * * * Shifts count as values, so outputChainValueCount will reflect these. * * * Latches and unlatches are also counted as values, but always in the * scheme being exited. * * * Base256 header bytes are not included as values. * * A "chunk" refers to the minimum grouping of values in a schema that must be * encoded together. * * ASCII: 1 value (1 codeword) in 1 chunk * ASCII (digits): 2 values (1 codeword) in 1 chunk (optional) * C40/Text/X12: 3 values (2 codewords) in 1 chunk * C40/Text/X12 (unlatch): 1 value (1 codeword) in 1 chunk * EDIFACT: 1 value (1 codeword*) in 1 chunk * Base 256: 1 value (1 codeword) in 1 chunk * * * EDIFACT writes 6 bits at a time, but progress is tracked to the next byte * boundary. If unlatch value finishes mid-byte, the remaining bits before * the next boundary are set to zero. * * Each scheme implements 3 equivalent functions: * * EncodeNextChunk[Scheme] * * AppendValue[Scheme] * * CompleteIfDone[Scheme] * * The function EncodeNextChunk() (no Scheme in the name) knows which scheme- * specific implementations to call based on the stream's current encodation * scheme. * * It's important that EncodeNextChunk[Scheme] not call CompleteIfDone[Scheme] * directly because some parts of the logic might want to encode a stream * without allowing the padding and other extra logic that can occur when an * end-of-symbol condition is triggered. */ /* Verify stream is using expected scheme */ #define CHKSCHEME(s) { \ if(stream->currentScheme != (s)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } \ } /* CHKERR should follow any call that might alter stream status */ #define CHKERR { \ if(stream->status != DmtxStatusEncoding) { return; } \ } /* CHKSIZE should follows typical calls to FindSymbolSize() */ #define CHKSIZE { \ if(sizeIdx == DmtxUndefined) { StreamMarkInvalid(stream, DmtxErrorUnknown); return; } \ } /** * * */ static int EncodeSingleScheme(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1) { DmtxEncodeStream stream; stream = StreamInit(input, output); stream.fnc1 = fnc1; /* 1st FNC1 special case, encode before scheme switch */ if (fnc1 != DmtxUndefined && (int)(input->b[0]) == fnc1) { StreamInputAdvanceNext(&stream); AppendValueAscii(&stream, DmtxValueFNC1); } /* Continue encoding until complete */ while(stream.status == DmtxStatusEncoding) EncodeNextChunk(&stream, scheme, DmtxEncodeNormal, sizeIdxRequest); /* Verify encoding completed and all inputs were consumed */ if(stream.status != DmtxStatusComplete || StreamInputHasNext(&stream)) return DmtxUndefined; return stream.sizeIdx; } /** * This function distributes work to the equivalent scheme-specific * implementation. * * Each of these functions will encode the next symbol input word, and in some * cases this requires additional input words to be encoded as well. */ static void EncodeNextChunk(DmtxEncodeStream *stream, int scheme, int option, int sizeIdxRequest) { /* Special case: Prevent X12 from entering state with no way to unlatch */ if(stream->currentScheme != DmtxSchemeX12 && scheme == DmtxSchemeX12) { if(PartialX12ChunkRemains(stream)) scheme = DmtxSchemeAscii; } /* Change to target scheme if necessary */ if(stream->currentScheme != scheme) { EncodeChangeScheme(stream, scheme, DmtxUnlatchExplicit); CHKERR; CHKSCHEME(scheme); } /* Special case: Edifact may be done before writing first word */ if(scheme == DmtxSchemeEdifact) CompleteIfDoneEdifact(stream, sizeIdxRequest); CHKERR; switch(stream->currentScheme) { case DmtxSchemeAscii: EncodeNextChunkAscii(stream, option); CHKERR; CompleteIfDoneAscii(stream, sizeIdxRequest); CHKERR; break; case DmtxSchemeC40: case DmtxSchemeText: case DmtxSchemeX12: EncodeNextChunkCTX(stream, sizeIdxRequest); CHKERR; CompleteIfDoneCTX(stream, sizeIdxRequest); CHKERR; break; case DmtxSchemeEdifact: EncodeNextChunkEdifact(stream); CHKERR; CompleteIfDoneEdifact(stream, sizeIdxRequest); CHKERR; break; case DmtxSchemeBase256: EncodeNextChunkBase256(stream); CHKERR; CompleteIfDoneBase256(stream, sizeIdxRequest); CHKERR; break; default: StreamMarkFatal(stream, DmtxErrorUnknown); break; } } /** * * */ static void EncodeChangeScheme(DmtxEncodeStream *stream, DmtxScheme targetScheme, int unlatchType) { /* Nothing to do */ if(stream->currentScheme == targetScheme) return; /* Every latch must go through ASCII */ switch(stream->currentScheme) { case DmtxSchemeC40: case DmtxSchemeText: case DmtxSchemeX12: if(unlatchType == DmtxUnlatchExplicit) { AppendUnlatchCTX(stream); CHKERR; } break; case DmtxSchemeEdifact: if(unlatchType == DmtxUnlatchExplicit) { AppendValueEdifact(stream, DmtxValueEdifactUnlatch); CHKERR; } break; default: /* Nothing to do for ASCII or Base 256 */ assert(stream->currentScheme == DmtxSchemeAscii || stream->currentScheme == DmtxSchemeBase256); break; } stream->currentScheme = DmtxSchemeAscii; /* Anything other than ASCII (the default) requires a latch */ switch(targetScheme) { case DmtxSchemeC40: AppendValueAscii(stream, DmtxValueC40Latch); CHKERR; break; case DmtxSchemeText: AppendValueAscii(stream, DmtxValueTextLatch); CHKERR; break; case DmtxSchemeX12: AppendValueAscii(stream, DmtxValueX12Latch); CHKERR; break; case DmtxSchemeEdifact: AppendValueAscii(stream, DmtxValueEdifactLatch); CHKERR; break; case DmtxSchemeBase256: AppendValueAscii(stream, DmtxValueBase256Latch); CHKERR; break; default: /* Nothing to do for ASCII */ CHKSCHEME(DmtxSchemeAscii); break; } stream->currentScheme = targetScheme; /* Reset new chain length to zero */ stream->outputChainWordCount = 0; stream->outputChainValueCount = 0; /* Insert header byte if just latched to Base256 */ if(targetScheme == DmtxSchemeBase256) { UpdateBase256ChainHeader(stream, DmtxUndefined); CHKERR; } } /** * * */ static int GetRemainingSymbolCapacity(int outputLength, int sizeIdx) { int capacity; int remaining; if(sizeIdx == DmtxUndefined) { remaining = DmtxUndefined; } else { capacity = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx); remaining = capacity - outputLength; } return remaining; } libdmtx-0.7.7/dmtxencodestream.c000066400000000000000000000103341423156660700167150ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxencodestream.c * \brief DmtxEncodeStream implementation */ /** * * */ static DmtxEncodeStream StreamInit(DmtxByteList *input, DmtxByteList *output) { DmtxEncodeStream stream; stream.input = input; stream.output = output; stream.currentScheme = DmtxSchemeAscii; stream.inputNext = 0; stream.outputChainValueCount = 0; stream.outputChainWordCount = 0; stream.reason = NULL; stream.sizeIdx = DmtxUndefined; stream.status = DmtxStatusEncoding; return stream; } /** * * */ static void StreamCopy(DmtxEncodeStream *dst, DmtxEncodeStream *src) { DmtxPassFail passFail; dst->currentScheme = src->currentScheme; dst->inputNext = src->inputNext; dst->outputChainValueCount = src->outputChainValueCount; dst->outputChainWordCount = src->outputChainWordCount; dst->reason = src->reason; dst->sizeIdx = src->sizeIdx; dst->status = src->status; dst->input = src->input; dst->fnc1 = src->fnc1; dmtxByteListCopy(dst->output, src->output, &passFail); } /** * * */ static void StreamMarkComplete(DmtxEncodeStream *stream, int sizeIdx) { if(stream->status == DmtxStatusEncoding) { stream->sizeIdx = sizeIdx; stream->status = DmtxStatusComplete; assert(stream->reason == NULL); } } /** * * */ static void StreamMarkInvalid(DmtxEncodeStream *stream, int reasonIdx) { stream->status = DmtxStatusInvalid; stream->reason = dmtxErrorMessage[reasonIdx]; } /** * * */ static void StreamMarkFatal(DmtxEncodeStream *stream, int reasonIdx) { stream->status = DmtxStatusFatal; stream->reason = dmtxErrorMessage[reasonIdx]; } /** * push on newest/last append * used for encoding each output cw */ static void StreamOutputChainAppend(DmtxEncodeStream *stream, DmtxByte value) { DmtxPassFail passFail; dmtxByteListPush(stream->output, value, &passFail); if(passFail == DmtxPass) stream->outputChainWordCount++; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); } /** * pop off newest/last * used for edifact */ static DmtxByte StreamOutputChainRemoveLast(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; if(stream->outputChainWordCount > 0) { value = dmtxByteListPop(stream->output, &passFail); stream->outputChainWordCount--; } else { value = 0; StreamMarkFatal(stream, DmtxErrorEmptyList); } return value; } /** * overwrite arbitrary element * used for binary length changes */ static void StreamOutputSet(DmtxEncodeStream *stream, int index, DmtxByte value) { if(index < 0 || index >= stream->output->length) StreamMarkFatal(stream, DmtxErrorOutOfBounds); else stream->output->b[index] = value; } /** * * */ static DmtxBoolean StreamInputHasNext(DmtxEncodeStream *stream) { return (stream->inputNext < stream->input->length) ? DmtxTrue : DmtxFalse; } /** * peek at first/oldest * used for ascii double digit */ static DmtxByte StreamInputPeekNext(DmtxEncodeStream *stream) { DmtxByte value = 0; if(StreamInputHasNext(stream)) value = stream->input->b[stream->inputNext]; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); return value; } /** * used as each input cw is processed * * \param value Value to populate, can be null (for blind dequeues) * \param stream */ static DmtxByte StreamInputAdvanceNext(DmtxEncodeStream *stream) { DmtxByte value; value = StreamInputPeekNext(stream); if(stream->status == DmtxStatusEncoding) stream->inputNext++; /* XXX is this what we really mean here? */ return value; } /** * used as each input cw is processed * * \param value Value to populate, can be null (for blind dequeues) * \param stream */ static void StreamInputAdvancePrev(DmtxEncodeStream *stream) { if(stream->inputNext > 0) stream->inputNext--; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); } libdmtx-0.7.7/dmtximage.c000066400000000000000000000266071423156660700153400ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtximage.c * \brief Image handling */ /** * libdmtx stores image data as a large one-dimensional array of packed pixels, * reading from the array when scanning barcodes and writing to it when creating * a barcode. Beyond this interaction the calling program is responsible for * populating and dispatching pixels between the image array and the outside * world, whether that means loading an image from a file, acquiring camera * input, displaying output to a screen, saving to disk, etc... * * By default, libdmtx treats the first pixel of an image array as the top-left * corner of the physical image, with the final pixel landing at the bottom- * right. However, if mapping a pixel buffer this way produces an inverted * image the calling program can specify DmtxFlipY at image creation time to * remove the inversion. This has a negligible effect on performance since it * only modifies the pixel mapping math, and does not alter any pixel data. * * Regardless of how an image is stored internally, all libdmtx functions * consider coordinate (0,0) to mathematically represent the bottom-left pixel * location of an image using a right-handed coordinate system. * * (0,HEIGHT-1) (WIDTH-1,HEIGHT-1) * * array pos = 0,1,2,3,...-----------+ * | | * | | * | libdmtx | * | image | * | coordinates | * | | * | | * +---------...,N-2,N-1,N = array pos * * (0,0) (WIDTH-1,0) * * Notes: * - OpenGL pixel arrays obtained with glReadPixels() are stored * bottom-to-top; use DmtxFlipY * - Many popular image formats (e.g., PNG, GIF) store rows * top-to-bottom; use DmtxFlipNone */ /** * \brief XXX * \param XXX * \return XXX */ extern DmtxImage * dmtxImageCreate(unsigned char *pxl, int width, int height, int pack) { // DmtxPassFail err; DmtxImage *img; if(pxl == NULL || width < 1 || height < 1) return NULL; img = (DmtxImage *)calloc(1, sizeof(DmtxImage)); if(img == NULL) return NULL; img->pxl = pxl; img->width = width; img->height = height; img->pixelPacking = pack; img->bitsPerPixel = GetBitsPerPixel(pack); img->bytesPerPixel = img->bitsPerPixel/8; img->rowPadBytes = 0; img->rowSizeBytes = img->width * img->bytesPerPixel + img->rowPadBytes; img->imageFlip = DmtxFlipNone; /* Leave channelStart[] and bitsPerChannel[] with zeros from calloc */ img->channelCount = 0; switch(pack) { case DmtxPackCustom: break; case DmtxPack1bppK: dmtxImageSetChannel(img, 0, 1); return NULL; /* unsupported packing order */ /* break; */ case DmtxPack8bppK: dmtxImageSetChannel(img, 0, 8); break; case DmtxPack16bppRGB: case DmtxPack16bppBGR: case DmtxPack16bppYCbCr: dmtxImageSetChannel(img, 0, 5); dmtxImageSetChannel(img, 5, 5); dmtxImageSetChannel(img, 10, 5); break; case DmtxPack24bppRGB: case DmtxPack24bppBGR: case DmtxPack24bppYCbCr: case DmtxPack32bppRGBX: case DmtxPack32bppBGRX: dmtxImageSetChannel(img, 0, 8); dmtxImageSetChannel(img, 8, 8); dmtxImageSetChannel(img, 16, 8); break; case DmtxPack16bppRGBX: case DmtxPack16bppBGRX: dmtxImageSetChannel(img, 0, 5); dmtxImageSetChannel(img, 5, 5); dmtxImageSetChannel(img, 10, 5); break; case DmtxPack16bppXRGB: case DmtxPack16bppXBGR: dmtxImageSetChannel(img, 1, 5); dmtxImageSetChannel(img, 6, 5); dmtxImageSetChannel(img, 11, 5); break; case DmtxPack32bppXRGB: case DmtxPack32bppXBGR: dmtxImageSetChannel(img, 8, 8); dmtxImageSetChannel(img, 16, 8); dmtxImageSetChannel(img, 24, 8); break; case DmtxPack32bppCMYK: dmtxImageSetChannel(img, 0, 8); dmtxImageSetChannel(img, 8, 8); dmtxImageSetChannel(img, 16, 8); dmtxImageSetChannel(img, 24, 8); break; default: return NULL; } return img; } /** * \brief Free libdmtx image memory * \param img pointer to img location * \return DmtxFail | DmtxPass */ extern DmtxPassFail dmtxImageDestroy(DmtxImage **img) { if(img == NULL || *img == NULL) return DmtxFail; free(*img); *img = NULL; return DmtxPass; } /** * * */ extern DmtxPassFail dmtxImageSetChannel(DmtxImage *img, int channelStart, int bitsPerChannel) { if(img->channelCount >= 4) /* IMAGE_MAX_CHANNEL */ return DmtxFail; /* New channel extends beyond pixel data */ /* if(channelStart + bitsPerChannel > img->bitsPerPixel) return DmtxFail; */ img->bitsPerChannel[img->channelCount] = bitsPerChannel; img->channelStart[img->channelCount] = channelStart; (img->channelCount)++; return DmtxPass; } /** * \brief Set image property * \param img pointer to image * \return image width */ extern DmtxPassFail dmtxImageSetProp(DmtxImage *img, int prop, int value) { if(img == NULL) return DmtxFail; switch(prop) { case DmtxPropRowPadBytes: img->rowPadBytes = value; img->rowSizeBytes = img->width * (img->bitsPerPixel/8) + img->rowPadBytes; break; case DmtxPropImageFlip: img->imageFlip = value; break; default: break; } return DmtxPass; } /** * \brief Get image width * \param img pointer to image * \return image width */ extern int dmtxImageGetProp(DmtxImage *img, int prop) { if(img == NULL) return DmtxUndefined; switch(prop) { case DmtxPropWidth: return img->width; case DmtxPropHeight: return img->height; case DmtxPropPixelPacking: return img->pixelPacking; case DmtxPropBitsPerPixel: return img->bitsPerPixel; case DmtxPropBytesPerPixel: return img->bytesPerPixel; case DmtxPropRowPadBytes: return img->rowPadBytes; case DmtxPropRowSizeBytes: return img->rowSizeBytes; case DmtxPropImageFlip: return img->imageFlip; case DmtxPropChannelCount: return img->channelCount; default: break; } return DmtxUndefined; } /** * \brief Returns pixel offset for image * \param img * \param x coordinate * \param y coordinate * \return pixel byte offset */ extern int dmtxImageGetByteOffset(DmtxImage *img, int x, int y) { assert(img != NULL); assert(!(img->imageFlip & DmtxFlipX)); /* DmtxFlipX is not an option */ if(dmtxImageContainsInt(img, 0, x, y) == DmtxFalse) return DmtxUndefined; if(img->imageFlip & DmtxFlipY) return (y * img->rowSizeBytes + x * img->bytesPerPixel); return ((img->height - y - 1) * img->rowSizeBytes + x * img->bytesPerPixel); } /** * * */ extern DmtxPassFail dmtxImageGetPixelValue(DmtxImage *img, int x, int y, int channel, int *value) { int offset; /* unsigned char *pixelPtr; int pixelValue; int mask; int bitShift; */ assert(img != NULL); assert(channel < img->channelCount); offset = dmtxImageGetByteOffset(img, x, y); if(offset == DmtxUndefined) return DmtxFail; switch(img->bitsPerChannel[channel]) { case 1: /* assert(img->bitsPerPixel == 1); mask = 0x01 << (7 - offset%8); *value = (img->pxl[offset/8] & mask) ? 255 : 0; */ break; case 5: /* XXX might be expensive if we want to scale perfect 0-255 range */ /* assert(img->bitsPerPixel == 16); pixelPtr = img->pxl + (offset * (img->bitsPerPixel/8)); pixelValue = (*pixelPtr << 8) | (*(pixelPtr+1)); bitShift = img->bitsPerPixel - 5 - img->channelStart[channel]; mask = 0x1f << bitShift; *value = (((pixelValue & mask) >> bitShift) << 3); */ break; case 8: assert(img->channelStart[channel] % 8 == 0); assert(img->bitsPerPixel % 8 == 0); *value = img->pxl[offset + channel]; break; } return DmtxPass; } /** * * */ extern DmtxPassFail dmtxImageSetPixelValue(DmtxImage *img, int x, int y, int channel, int value) { int offset; /* unsigned char *pixelPtr; */ /* int pixelValue; */ /* int mask; */ /* int bitShift; */ assert(img != NULL); assert(channel < img->channelCount); offset = dmtxImageGetByteOffset(img, x, y); if(offset == DmtxUndefined) return DmtxFail; switch(img->bitsPerChannel[channel]) { case 1: /* assert(img->bitsPerPixel == 1); mask = 0x01 << (7 - offset%8); *value = (img->pxl[offset/8] & mask) ? 255 : 0; */ break; case 5: /* XXX might be expensive if we want to scale perfect 0-255 range */ /* assert(img->bitsPerPixel == 16); pixelPtr = img->pxl + (offset * (img->bitsPerPixel/8)); pixelValue = (*pixelPtr << 8) | (*(pixelPtr+1)); bitShift = img->bitsPerPixel - 5 - img->channelStart[channel]; mask = 0x1f << bitShift; *value = (((pixelValue & mask) >> bitShift) << 3); */ break; case 8: assert(img->channelStart[channel] % 8 == 0); assert(img->bitsPerPixel % 8 == 0); img->pxl[offset + channel] = value; break; } return DmtxPass; } /** * \brief Test whether image contains a coordinate expressed in integers * \param img * \param margin width * \param x coordinate * \param y coordinate * \return DmtxTrue | DmtxFalse */ extern DmtxBoolean dmtxImageContainsInt(DmtxImage *img, int margin, int x, int y) { assert(img != NULL); if(x - margin >= 0 && x + margin < img->width && y - margin >= 0 && y + margin < img->height) return DmtxTrue; return DmtxFalse; } /** * \brief Test whether image contains a coordinate expressed in floating points * \param img * \param x coordinate * \param y coordinate * \return DmtxTrue | DmtxFalse */ extern DmtxBoolean dmtxImageContainsFloat(DmtxImage *img, double x, double y) { assert(img != NULL); if(x >= 0.0 && x < (double)img->width && y >= 0.0 && y < (double)img->height) return DmtxTrue; return DmtxFalse; } /** * * */ static int GetBitsPerPixel(int pack) { switch(pack) { case DmtxPack1bppK: return 1; case DmtxPack8bppK: return 8; case DmtxPack16bppRGB: case DmtxPack16bppRGBX: case DmtxPack16bppXRGB: case DmtxPack16bppBGR: case DmtxPack16bppBGRX: case DmtxPack16bppXBGR: case DmtxPack16bppYCbCr: return 16; case DmtxPack24bppRGB: case DmtxPack24bppBGR: case DmtxPack24bppYCbCr: return 24; case DmtxPack32bppRGBX: case DmtxPack32bppXRGB: case DmtxPack32bppBGRX: case DmtxPack32bppXBGR: case DmtxPack32bppCMYK: return 32; default: break; } return DmtxUndefined; } libdmtx-0.7.7/dmtxmatrix3.c000066400000000000000000000176141423156660700156430ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxmatrix3.c * \brief 2D Matrix (3x3) math */ /** * \brief Copy matrix contents * \param m0 Copy target * \param m1 Copy source * \return void */ extern void dmtxMatrix3Copy(DmtxMatrix3 m0, DmtxMatrix3 m1) { memcpy(m0, m1, sizeof(DmtxMatrix3)); } /** * \brief Generate identity transformation matrix * \param m Generated matrix * \return void * * | 1 0 0 | * m = | 0 1 0 | * | 0 0 1 | * * Transform "m" * (doesn't change anything) * |\ * (0,1) x----o +--+ \ (0,1) x----o * | | | \ | | * | | | / | | * +----* +--+ / +----* * (0,0) (1,0) |/ (0,0) (1,0) * */ extern void dmtxMatrix3Identity(DmtxMatrix3 m) { static DmtxMatrix3 tmp = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }; dmtxMatrix3Copy(m, tmp); } /** * \brief Generate translate transformation matrix * \param m Generated matrix * \param tx * \param ty * \return void * * | 1 0 0 | * m = | 0 1 0 | * | tx ty 1 | * * Transform "m" * _____ (tx,1+ty) x----o (1+tx,1+ty) * \ | | | * (0,1) x----o / | (0,1) +-|--+ | * | | / /\| | +----* (1+tx,ty) * | | \ / | | * +----* ` +----+ * (0,0) (1,0) (0,0) (1,0) * */ void dmtxMatrix3Translate(DmtxMatrix3 m, double tx, double ty) { dmtxMatrix3Identity(m); m[2][0] = tx; m[2][1] = ty; } /** * \brief Generate rotate transformation * \param m Generated matrix * \param angle * \return void * * | cos(a) sin(a) 0 | * m = | -sin(a) cos(a) 0 | * | 0 0 1 | * o * Transform "m" / ` * ___ / ` * (0,1) x----o |/ \ x * (cos(a),sin(a)) * | | '-- | ` / * | | ___/ ` / a * +----* `+ - - - - - - * (0,0) (1,0) (0,0) * */ extern void dmtxMatrix3Rotate(DmtxMatrix3 m, double angle) { double sinAngle, cosAngle; sinAngle = sin(angle); cosAngle = cos(angle); dmtxMatrix3Identity(m); m[0][0] = cosAngle; m[0][1] = sinAngle; m[1][0] = -sinAngle; m[1][1] = cosAngle; } /** * \brief Generate scale transformation matrix * \param m Generated matrix * \param sx * \param sy * \return void * * | sx 0 0 | * m = | 0 sy 0 | * | 0 0 1 | * * Transform "m" * _____ (0,sy) x-------o (sx,sy) * \ | | | * (0,1) x----o / | (0,1) +----+ | * | | / /\| | | | * | | \ / | | | * +----* ` +----+--* * (0,0) (1,0) (0,0) (sx,0) * */ extern void dmtxMatrix3Scale(DmtxMatrix3 m, double sx, double sy) { dmtxMatrix3Identity(m); m[0][0] = sx; m[1][1] = sy; } /** * \brief Generate shear transformation matrix * \param m Generated matrix * \param shx * \param shy * \return void * * | 0 shy 0 | * m = | shx 0 0 | * | 0 0 1 | */ extern void dmtxMatrix3Shear(DmtxMatrix3 m, double shx, double shy) { dmtxMatrix3Identity(m); m[1][0] = shx; m[0][1] = shy; } /** * \brief Generate top line skew transformation * \param m * \param b0 * \param b1 * \param sz * \return void * * | b1/b0 0 (b1-b0)/(sz*b0) | * m = | 0 sz/b0 0 | * | 0 0 1 | * * (sz,b1) o * /| Transform "m" * / | * / | +--+ * / | | | * (0,b0) x | | | * | | +-+ +-+ * (0,sz) +----+ \ / (0,sz) x----o * | | \ / | | * | | \/ | | * +----+ +----+ * (0,0) (sz,0) (0,0) (sz,0) * */ extern void dmtxMatrix3LineSkewTop(DmtxMatrix3 m, double b0, double b1, double sz) { assert(b0 >= DmtxAlmostZero); dmtxMatrix3Identity(m); m[0][0] = b1/b0; m[1][1] = sz/b0; m[0][2] = (b1 - b0)/(sz*b0); } /** * \brief Generate top line skew transformation (inverse) * \param m * \param b0 * \param b1 * \param sz * \return void */ extern void dmtxMatrix3LineSkewTopInv(DmtxMatrix3 m, double b0, double b1, double sz) { assert(b1 >= DmtxAlmostZero); dmtxMatrix3Identity(m); m[0][0] = b0/b1; m[1][1] = b0/sz; m[0][2] = (b0 - b1)/(sz*b1); } /** * \brief Generate side line skew transformation * \param m * \param b0 * \param b1 * \param sz * \return void */ extern void dmtxMatrix3LineSkewSide(DmtxMatrix3 m, double b0, double b1, double sz) { assert(b0 >= DmtxAlmostZero); dmtxMatrix3Identity(m); m[0][0] = sz/b0; m[1][1] = b1/b0; m[1][2] = (b1 - b0)/(sz*b0); } /** * \brief Generate side line skew transformation (inverse) * \param m * \param b0 * \param b1 * \param sz * \return void */ extern void dmtxMatrix3LineSkewSideInv(DmtxMatrix3 m, double b0, double b1, double sz) { assert(b1 >= DmtxAlmostZero); dmtxMatrix3Identity(m); m[0][0] = b0/sz; m[1][1] = b0/b1; m[1][2] = (b0 - b1)/(sz*b1); } /** * \brief Multiply two matrices to create a third * \param mOut * \param m0 * \param m1 * \return void */ extern void dmtxMatrix3Multiply(DmtxMatrix3 mOut, DmtxMatrix3 m0, DmtxMatrix3 m1) { int i, j, k; double val; for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) { val = 0.0; for(k = 0; k < 3; k++) { val += m0[i][k] * m1[k][j]; } mOut[i][j] = val; } } } /** * \brief Multiply two matrices in place * \param m0 * \param m1 * \return void */ extern void dmtxMatrix3MultiplyBy(DmtxMatrix3 m0, DmtxMatrix3 m1) { DmtxMatrix3 mTmp; dmtxMatrix3Copy(mTmp, m0); dmtxMatrix3Multiply(m0, mTmp, m1); } /** * \brief Multiply vector and matrix * \param vOut Vector (output) * \param vIn Vector (input) * \param m Matrix to be multiplied * \return DmtxPass | DmtxFail */ extern int dmtxMatrix3VMultiply(DmtxVector2 *vOut, DmtxVector2 *vIn, DmtxMatrix3 m) { double w; w = vIn->X*m[0][2] + vIn->Y*m[1][2] + m[2][2]; if(fabs(w) <= DmtxAlmostZero) { vOut->X = FLT_MAX; vOut->Y = FLT_MAX; return DmtxFail; } vOut->X = (vIn->X*m[0][0] + vIn->Y*m[1][0] + m[2][0])/w; vOut->Y = (vIn->X*m[0][1] + vIn->Y*m[1][1] + m[2][1])/w; return DmtxPass; } /** * \brief Multiply vector and matrix in place * \param v Vector (input and output) * \param m Matrix to be multiplied * \return DmtxPass | DmtxFail */ extern int dmtxMatrix3VMultiplyBy(DmtxVector2 *v, DmtxMatrix3 m) { int success; DmtxVector2 vOut; success = dmtxMatrix3VMultiply(&vOut, v, m); *v = vOut; return success; } /** * \brief Print matrix contents to STDOUT * \param m * \return void */ extern void dmtxMatrix3Print(DmtxMatrix3 m) { fprintf(stdout, "%8.8f\t%8.8f\t%8.8f\n", m[0][0], m[0][1], m[0][2]); fprintf(stdout, "%8.8f\t%8.8f\t%8.8f\n", m[1][0], m[1][1], m[1][2]); fprintf(stdout, "%8.8f\t%8.8f\t%8.8f\n", m[2][0], m[2][1], m[2][2]); fprintf(stdout, "\n"); } libdmtx-0.7.7/dmtxmessage.c000066400000000000000000000054011423156660700156670ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxmessage.c * \brief Data message handling */ /** * \brief Allocate memory for message * \param sizeIdx * \param symbolFormat DmtxFormatMatrix | DmtxFormatMosaic * \return Address of allocated memory */ extern DmtxMessage * dmtxMessageCreate(int sizeIdx, int symbolFormat) { DmtxMessage *message; int mappingRows, mappingCols; assert(symbolFormat == DmtxFormatMatrix || symbolFormat == DmtxFormatMosaic); mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdx); mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdx); message = (DmtxMessage *)calloc(1, sizeof(DmtxMessage)); if(message == NULL) return NULL; message->arraySize = sizeof(unsigned char) * mappingRows * mappingCols; message->array = (unsigned char *)calloc(1, message->arraySize); if(message->array == NULL) { perror("Calloc failed"); dmtxMessageDestroy(&message); return NULL; } message->codeSize = sizeof(unsigned char) * dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx) + dmtxGetSymbolAttribute(DmtxSymAttribSymbolErrorWords, sizeIdx); if(symbolFormat == DmtxFormatMosaic) message->codeSize *= 3; message->code = (unsigned char *)calloc(message->codeSize, sizeof(unsigned char)); if(message->code == NULL) { perror("Calloc failed"); dmtxMessageDestroy(&message); return NULL; } /* XXX not sure if this is the right place or even the right approach. Trying to allocate memory for the decoded data stream and will initially assume that decoded data will not be larger than 2x encoded data */ message->outputSize = sizeof(unsigned char) * message->codeSize * 10; message->output = (unsigned char *)calloc(message->outputSize, sizeof(unsigned char)); if(message->output == NULL) { perror("Calloc failed"); dmtxMessageDestroy(&message); return NULL; } return message; } /** * \brief Free memory previously allocated for message * \param message * \return void */ extern DmtxPassFail dmtxMessageDestroy(DmtxMessage **msg) { if(msg == NULL || *msg == NULL) return DmtxFail; if((*msg)->array != NULL) free((*msg)->array); if((*msg)->code != NULL) free((*msg)->code); if((*msg)->output != NULL) free((*msg)->output); free(*msg); *msg = NULL; return DmtxPass; } libdmtx-0.7.7/dmtxplacemod.c000066400000000000000000000273421423156660700160370ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxplacemod.c * \brief Data Matrix module placement */ /** * receives symbol row and col and returns status * DmtxModuleOn / !DmtxModuleOn (DmtxModuleOff) * DmtxModuleAssigned * DmtxModuleVisited * DmtxModuleData / !DmtxModuleData (DmtxModuleAlignment) * row and col are expressed in symbol coordinates, so (0,0) is the intersection of the "L" */ int dmtxSymbolModuleStatus(DmtxMessage *message, int sizeIdx, int symbolRow, int symbolCol) { int symbolRowReverse; int mappingRow, mappingCol; int dataRegionRows, dataRegionCols; int symbolRows, mappingCols; dataRegionRows = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionRows, sizeIdx); dataRegionCols = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionCols, sizeIdx); symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdx); symbolRowReverse = symbolRows - symbolRow - 1; mappingRow = symbolRowReverse - 1 - 2 * (symbolRowReverse / (dataRegionRows+2)); mappingCol = symbolCol - 1 - 2 * (symbolCol / (dataRegionCols+2)); /* Solid portion of alignment patterns */ if(symbolRow % (dataRegionRows+2) == 0 || symbolCol % (dataRegionCols+2) == 0) return (DmtxModuleOnRGB | (!DmtxModuleData)); /* Horinzontal calibration bars */ if((symbolRow+1) % (dataRegionRows+2) == 0) return (((symbolCol & 0x01) ? 0 : DmtxModuleOnRGB) | (!DmtxModuleData)); /* Vertical calibration bars */ if((symbolCol+1) % (dataRegionCols+2) == 0) return (((symbolRow & 0x01) ? 0 : DmtxModuleOnRGB) | (!DmtxModuleData)); /* Data modules */ return (message->array[mappingRow * mappingCols + mappingCol] | DmtxModuleData); } /** * \brief Logical relationship between bit and module locations * \param modules * \param codewords * \param sizeIdx * \param moduleOnColor * \return Number of codewords read */ static int ModulePlacementEcc200(unsigned char *modules, unsigned char *codewords, int sizeIdx, int moduleOnColor) { int row, col, chr; int mappingRows, mappingCols; assert(moduleOnColor & (DmtxModuleOnRed | DmtxModuleOnGreen | DmtxModuleOnBlue)); mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdx); mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdx); /* Start in the nominal location for the 8th bit of the first character */ chr = 0; row = 4; col = 0; do { /* Repeatedly first check for one of the special corner cases */ if((row == mappingRows) && (col == 0)) PatternShapeSpecial1(modules, mappingRows, mappingCols, &(codewords[chr++]), moduleOnColor); else if((row == mappingRows-2) && (col == 0) && (mappingCols%4 != 0)) PatternShapeSpecial2(modules, mappingRows, mappingCols, &(codewords[chr++]), moduleOnColor); else if((row == mappingRows-2) && (col == 0) && (mappingCols%8 == 4)) PatternShapeSpecial3(modules, mappingRows, mappingCols, &(codewords[chr++]), moduleOnColor); else if((row == mappingRows+4) && (col == 2) && (mappingCols%8 == 0)) PatternShapeSpecial4(modules, mappingRows, mappingCols, &(codewords[chr++]), moduleOnColor); /* Sweep upward diagonally, inserting successive characters */ do { if((row < mappingRows) && (col >= 0) && !(modules[row*mappingCols+col] & DmtxModuleVisited)) PatternShapeStandard(modules, mappingRows, mappingCols, row, col, &(codewords[chr++]), moduleOnColor); row -= 2; col += 2; } while ((row >= 0) && (col < mappingCols)); row += 1; col += 3; /* Sweep downward diagonally, inserting successive characters */ do { if((row >= 0) && (col < mappingCols) && !(modules[row*mappingCols+col] & DmtxModuleVisited)) PatternShapeStandard(modules, mappingRows, mappingCols, row, col, &(codewords[chr++]), moduleOnColor); row += 2; col -= 2; } while ((row < mappingRows) && (col >= 0)); row += 3; col += 1; /* ... until the entire modules array is scanned */ } while ((row < mappingRows) || (col < mappingCols)); /* If lower righthand corner is untouched then fill in the fixed pattern */ if(!(modules[mappingRows * mappingCols - 1] & DmtxModuleVisited)) { modules[mappingRows * mappingCols - 1] |= moduleOnColor; modules[(mappingRows * mappingCols) - mappingCols - 2] |= moduleOnColor; } /* XXX should this fixed pattern also be used in reading somehow? */ /* XXX compare that chr == region->dataSize here */ return chr; /* XXX number of codewords read off */ } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param row * \param col * \param codeword * \param moduleOnColor * \return void */ static void PatternShapeStandard(unsigned char *modules, int mappingRows, int mappingCols, int row, int col, unsigned char *codeword, int moduleOnColor) { PlaceModule(modules, mappingRows, mappingCols, row-2, col-2, codeword, DmtxMaskBit1, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row-2, col-1, codeword, DmtxMaskBit2, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row-1, col-2, codeword, DmtxMaskBit3, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row-1, col-1, codeword, DmtxMaskBit4, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row-1, col, codeword, DmtxMaskBit5, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row, col-2, codeword, DmtxMaskBit6, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row, col-1, codeword, DmtxMaskBit7, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, row, col, codeword, DmtxMaskBit8, moduleOnColor); } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param codeword * \param moduleOnColor * \return void */ static void PatternShapeSpecial1(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor) { PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 0, codeword, DmtxMaskBit1, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 1, codeword, DmtxMaskBit2, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 2, codeword, DmtxMaskBit3, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-2, codeword, DmtxMaskBit4, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-1, codeword, DmtxMaskBit5, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-1, codeword, DmtxMaskBit6, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols-1, codeword, DmtxMaskBit7, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols-1, codeword, DmtxMaskBit8, moduleOnColor); } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param codeword * \param moduleOnColor * \return void */ static void PatternShapeSpecial2(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor) { PlaceModule(modules, mappingRows, mappingCols, mappingRows-3, 0, codeword, DmtxMaskBit1, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-2, 0, codeword, DmtxMaskBit2, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 0, codeword, DmtxMaskBit3, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-4, codeword, DmtxMaskBit4, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-3, codeword, DmtxMaskBit5, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-2, codeword, DmtxMaskBit6, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-1, codeword, DmtxMaskBit7, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-1, codeword, DmtxMaskBit8, moduleOnColor); } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param codeword * \param moduleOnColor * \return void */ static void PatternShapeSpecial3(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor) { PlaceModule(modules, mappingRows, mappingCols, mappingRows-3, 0, codeword, DmtxMaskBit1, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-2, 0, codeword, DmtxMaskBit2, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 0, codeword, DmtxMaskBit3, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-2, codeword, DmtxMaskBit4, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-1, codeword, DmtxMaskBit5, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-1, codeword, DmtxMaskBit6, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols-1, codeword, DmtxMaskBit7, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols-1, codeword, DmtxMaskBit8, moduleOnColor); } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param codeword * \param moduleOnColor * \return void */ static void PatternShapeSpecial4(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor) { PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, 0, codeword, DmtxMaskBit1, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, mappingRows-1, mappingCols-1, codeword, DmtxMaskBit2, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-3, codeword, DmtxMaskBit3, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-2, codeword, DmtxMaskBit4, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols-1, codeword, DmtxMaskBit5, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-3, codeword, DmtxMaskBit6, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-2, codeword, DmtxMaskBit7, moduleOnColor); PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols-1, codeword, DmtxMaskBit8, moduleOnColor); } /** * \brief XXX * \param modules * \param mappingRows * \param mappingCols * \param row * \param col * \param codeword * \param mask * \param moduleOnColor * \return void */ static void PlaceModule(unsigned char *modules, int mappingRows, int mappingCols, int row, int col, unsigned char *codeword, int mask, int moduleOnColor) { if(row < 0) { row += mappingRows; col += 4 - ((mappingRows+4)%8); } if(col < 0) { col += mappingCols; row += 4 - ((mappingCols+4)%8); } /* If module has already been assigned then we are decoding the pattern into codewords */ if((modules[row*mappingCols+col] & DmtxModuleAssigned) != 0) { if((modules[row*mappingCols+col] & moduleOnColor) != 0) *codeword |= mask; else *codeword &= (0xff ^ mask); } /* Otherwise we are encoding the codewords into a pattern */ else { if((*codeword & mask) != 0x00) modules[row*mappingCols+col] |= moduleOnColor; modules[row*mappingCols+col] |= DmtxModuleAssigned; } modules[row*mappingCols+col] |= DmtxModuleVisited; } libdmtx-0.7.7/dmtxreedsol.c000066400000000000000000000431151423156660700157040ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * --------------------------------------------------------- * Portions of this file were derived from the Reed-Solomon * encoder/decoder released by Simon Rockliff in June 1991. * --------------------------------------------------------- * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxreedsol.c */ /** * TODO: * o try doxygen using using the JavaDoc style and JAVADOC_AUTOBRIEF = YES * o switch doxygen to simplified syntax, and using "\file" instead of "@file" */ #define NN 255 #define MAX_ERROR_WORD_COUNT 68 /* GF add (a + b) */ #define GfAdd(a,b) \ ((a) ^ (b)) /* GF multiply (a * b) */ #define GfMult(a,b) \ (((a) == 0 || (b) == 0) ? 0 : antilog301[(log301[(a)] + log301[(b)]) % NN]) /* GF multiply by antilog (a * alpha**b) */ #define GfMultAntilog(a,b) \ (((a) == 0) ? 0 : antilog301[(log301[(a)] + (b)) % NN]) /* GF(256) log values using primitive polynomial 301 */ static DmtxByte log301[] = { 255, 0, 1, 240, 2, 225, 241, 53, 3, 38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28, 243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115, 103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99, 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197, 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171, 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187, 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147, 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7, 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179, 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194, 113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127, 247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73, 164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82, 72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112, 232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149, 129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237, 130, 111, 20, 93, 122, 177, 150 }; /* GF(256) antilog values using primitive polynomial 301 */ static DmtxByte antilog301[] = { 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, 180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54, 108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36, 72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56, 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239, 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190, 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106, 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76, 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223, 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113, 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129, 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127, 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206, 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161, 111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132, 37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248, 221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 0 }; /** * Encode xyz. * More detailed description. * \param message * \param sizeIdx * \return Function success (DmtxPass|DmtxFail) */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFail; } static DmtxPassFail RsEncode(DmtxMessage *message, int sizeIdx) { int i, j; int blockStride, blockIdx; int blockErrorWords, symbolDataWords, symbolErrorWords, symbolTotalWords; DmtxPassFail passFail; DmtxByte val, *eccPtr; DmtxByte genStorage[MAX_ERROR_WORD_COUNT]; DmtxByte eccStorage[MAX_ERROR_WORD_COUNT]; DmtxByteList gen = dmtxByteListBuild(genStorage, sizeof(genStorage)); DmtxByteList ecc = dmtxByteListBuild(eccStorage, sizeof(eccStorage)); blockStride = dmtxGetSymbolAttribute(DmtxSymAttribInterleavedBlocks, sizeIdx); blockErrorWords = dmtxGetSymbolAttribute(DmtxSymAttribBlockErrorWords, sizeIdx); symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx); symbolErrorWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolErrorWords, sizeIdx); symbolTotalWords = symbolDataWords + symbolErrorWords; /* Populate generator polynomial */ RsGenPoly(&gen, blockErrorWords); /* For each interleaved block... */ for(blockIdx = 0; blockIdx < blockStride; blockIdx++) { /* Generate error codewords */ dmtxByteListInit(&ecc, blockErrorWords, 0, &passFail); CHKPASS; for(i = blockIdx; i < symbolDataWords; i += blockStride) { val = GfAdd(ecc.b[blockErrorWords-1], message->code[i]); for(j = blockErrorWords - 1; j > 0; j--) { DMTX_CHECK_BOUNDS(&ecc, j); DMTX_CHECK_BOUNDS(&ecc, j-1); DMTX_CHECK_BOUNDS(&gen, j); ecc.b[j] = GfAdd(ecc.b[j-1], GfMult(gen.b[j], val)); } ecc.b[0] = GfMult(gen.b[0], val); } /* Copy to output message */ eccPtr = ecc.b + blockErrorWords; for(i = symbolDataWords + blockIdx; i < symbolTotalWords; i += blockStride) message->code[i] = *(--eccPtr); assert(ecc.b == eccPtr); } return DmtxPass; } /** * Decode xyz. * More detailed description. * \param code * \param sizeIdx * \param fix * \return Function success (DmtxPass|DmtxFail) */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFail; } static DmtxPassFail RsDecode(unsigned char *code, int sizeIdx, int fix) { int i; int blockStride, blockIdx; int blockDataWords, blockErrorWords, blockMaxCorrectable; // int blockDataWords, blockErrorWords, blockTotalWords, blockMaxCorrectable; int symbolDataWords, symbolErrorWords, symbolTotalWords; DmtxBoolean error, repairable; DmtxPassFail passFail; unsigned char *word; DmtxByte elpStorage[MAX_ERROR_WORD_COUNT]; DmtxByte synStorage[MAX_ERROR_WORD_COUNT+1]; DmtxByte recStorage[NN]; DmtxByte locStorage[NN]; DmtxByteList elp = dmtxByteListBuild(elpStorage, sizeof(elpStorage)); DmtxByteList syn = dmtxByteListBuild(synStorage, sizeof(synStorage)); DmtxByteList rec = dmtxByteListBuild(recStorage, sizeof(recStorage)); DmtxByteList loc = dmtxByteListBuild(locStorage, sizeof(locStorage)); blockStride = dmtxGetSymbolAttribute(DmtxSymAttribInterleavedBlocks, sizeIdx); blockErrorWords = dmtxGetSymbolAttribute(DmtxSymAttribBlockErrorWords, sizeIdx); blockMaxCorrectable = dmtxGetSymbolAttribute(DmtxSymAttribBlockMaxCorrectable, sizeIdx); symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx); symbolErrorWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolErrorWords, sizeIdx); symbolTotalWords = symbolDataWords + symbolErrorWords; /* For each interleaved block */ for(blockIdx = 0; blockIdx < blockStride; blockIdx++) { /* Data word count depends on blockIdx due to special case at 144x144 */ blockDataWords = dmtxGetBlockDataSize(sizeIdx, blockIdx); // blockTotalWords = blockErrorWords + blockDataWords; /* Populate received list (rec) with data and error codewords */ dmtxByteListInit(&rec, 0, 0, &passFail); CHKPASS; /* Start with final error word and work backward */ word = code + symbolTotalWords + blockIdx - blockStride; for(i = 0; i < blockErrorWords; i++) { dmtxByteListPush(&rec, *word, &passFail); CHKPASS; word -= blockStride; } /* Start with final data word and work backward */ word = code + blockIdx + (blockStride * (blockDataWords - 1)); for(i = 0; i < blockDataWords; i++) { dmtxByteListPush(&rec, *word, &passFail); CHKPASS; word -= blockStride; } /* Compute syndromes (syn) */ error = RsComputeSyndromes(&syn, &rec, blockErrorWords); /* Error(s) detected: Attempt repair */ if(error) { /* Find error locator polynomial (elp) */ repairable = RsFindErrorLocatorPoly(&elp, &syn, blockErrorWords, blockMaxCorrectable); if(!repairable) return DmtxFail; /* Find error positions (loc) */ repairable = RsFindErrorLocations(&loc, &elp); if(!repairable) return DmtxFail; /* Find error values and repair */ RsRepairErrors(&rec, &loc, &elp, &syn); } /* * Overwrite output with correct/corrected values */ /* Start with first data word and work forward */ word = code + blockIdx; for(i = 0; i < blockDataWords; i++) { *word = dmtxByteListPop(&rec, &passFail); CHKPASS; word += blockStride; } /* Start with first error word and work forward */ word = code + symbolDataWords + blockIdx; for(i = 0; i < blockErrorWords; i++) { *word = dmtxByteListPop(&rec, &passFail); CHKPASS; word += blockStride; } } return DmtxPass; } /** * Populate generator polynomial. * More detailed description. * \param gen * \param errorWordCount * \return Function success (DmtxPass|DmtxFail) */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFail; } static DmtxPassFail RsGenPoly(DmtxByteList *gen, int errorWordCount) { int i, j; DmtxPassFail passFail; /* Initialize all coefficients to 1 */ dmtxByteListInit(gen, errorWordCount, 1, &passFail); CHKPASS; /* Generate polynomial */ for(i = 0; i < gen->length; i++) { for(j = i; j >= 0; j--) { gen->b[j] = GfMultAntilog(gen->b[j], i+1); if(j > 0) gen->b[j] = GfAdd(gen->b[j], gen->b[j-1]); } } return DmtxPass; } /** * Populate generator polynomial. * Assume we have received bits grouped into mm-bit symbols in rec[i], * i=0..(nn-1), and rec[i] is index form (ie as powers of alpha). We first * compute the 2*tt syndromes by substituting alpha**i into rec(X) and * evaluating, storing the syndromes in syn[i], i=1..2tt (leave syn[0] zero). * \param syn * \param rec * \param blockErrorWords * \return Are error(s) present? (DmtxPass|DmtxFail) */ /* XXX this CHKPASS isn't doing what we want ... really need a error reporting strategy */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxTrue; } static DmtxBoolean RsComputeSyndromes(DmtxByteList *syn, const DmtxByteList *rec, int blockErrorWords) { int i, j; DmtxPassFail passFail; DmtxBoolean error = DmtxFalse; /* Initialize all coefficients to 0 */ dmtxByteListInit(syn, blockErrorWords + 1, 0, &passFail); CHKPASS; for(i = 1; i < syn->length; i++) { /* Calculate syndrome at i */ for(j = 0; j < rec->length; j++) /* alternatively: j < blockTotalWords */ syn->b[i] = GfAdd(syn->b[i], GfMultAntilog(rec->b[j], i*j)); /* Non-zero syndrome indicates presence of error(s) */ if(syn->b[i] != 0) error = DmtxTrue; } return error; } /** * Find the error location polynomial using Berlekamp-Massey. * More detailed description. * \param elpOut * \param syn * \param errorWordCount * \param maxCorrectable * \return Is block repairable? (DmtxTrue|DmtxFalse) */ /* XXX this CHKPASS isn't doing what we want ... really need a error reporting strategy */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFalse; } static DmtxBoolean RsFindErrorLocatorPoly(DmtxByteList *elpOut, const DmtxByteList *syn, int errorWordCount, int maxCorrectable) { int i, iNext, j; int m, mCmp, lambda; DmtxByte disTmp, disStorage[MAX_ERROR_WORD_COUNT+1]; DmtxByte elpStorage[MAX_ERROR_WORD_COUNT+2][MAX_ERROR_WORD_COUNT]; DmtxByteList dis, elp[MAX_ERROR_WORD_COUNT+2]; DmtxPassFail passFail; dis = dmtxByteListBuild(disStorage, sizeof(disStorage)); dmtxByteListInit(&dis, 0, 0, &passFail); CHKPASS; for(i = 0; i < MAX_ERROR_WORD_COUNT + 2; i++) { elp[i] = dmtxByteListBuild(elpStorage[i], sizeof(elpStorage[i])); dmtxByteListInit(&elp[i], 0, 0, &passFail); CHKPASS; } /* iNext = 0 */ dmtxByteListPush(&elp[0], 1, &passFail); CHKPASS; dmtxByteListPush(&dis, 1, &passFail); CHKPASS; /* iNext = 1 */ dmtxByteListPush(&elp[1], 1, &passFail); CHKPASS; dmtxByteListPush(&dis, syn->b[1], &passFail); CHKPASS; for(iNext = 2, i = 1; /* explicit break */; i = iNext++) { if(dis.b[i] == 0) { /* Simple case: Copy directly from previous iteration */ dmtxByteListCopy(&elp[iNext], &elp[i], &passFail); CHKPASS; } else { /* Find earlier iteration (m) that provides maximal (m - lambda) */ for(m = 0, mCmp = 1; mCmp < i; mCmp++) if(dis.b[mCmp] != 0 && (mCmp - elp[mCmp].length) >= (m - elp[m].length)) m = mCmp; /* Calculate error location polynomial elp[i] (set 1st term) */ for(lambda = elp[m].length - 1, j = 0; j <= lambda; j++) elp[iNext].b[j+i-m] = (elp[i - 1].b[j] == 0) ? 0 : antilog301[(NN - log301[dis.b[m]] + log301[dis.b[i]] + log301[elp[m].b[j]]) % NN]; /* Calculate error location polynomial elp[i] (add 2nd term) */ for(lambda = elp[i].length - 1, j = 0; j <= lambda; j++) elp[iNext].b[j] = GfAdd(elp[iNext].b[j], elp[i].b[j]); elp[iNext].length = max(elp[i].length, elp[m].length + i - m); } lambda = elp[iNext].length - 1; if(i == errorWordCount || i >= lambda + maxCorrectable) break; /* Calculate discrepancy dis.b[i] */ for(disTmp = syn->b[iNext], j = 1; j <= lambda; j++) disTmp = GfAdd(disTmp, GfMult(syn->b[iNext-j], elp[iNext].b[j])); assert(dis.length == iNext); dmtxByteListPush(&dis, disTmp, &passFail); CHKPASS; } dmtxByteListCopy(elpOut, &elp[iNext], &passFail); CHKPASS; return (lambda <= maxCorrectable) ? DmtxTrue : DmtxFalse; } /** * Find roots of the error locator polynomial (Chien Search). * If the degree of elp is <= tt, we substitute alpha**i, i=1..n into the elp * to get the roots, hence the inverse roots, the error location numbers. * If the number of errors located does not equal the degree of the elp, we * have more than tt errors and cannot correct them. * \param loc * \param elp * \return Is block repairable? (DmtxTrue|DmtxFalse) */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFalse; } static DmtxBoolean RsFindErrorLocations(DmtxByteList *loc, const DmtxByteList *elp) { int i, j; int lambda = elp->length - 1; DmtxPassFail passFail; DmtxByte q, regStorage[MAX_ERROR_WORD_COUNT]; DmtxByteList reg = dmtxByteListBuild(regStorage, sizeof(regStorage)); dmtxByteListCopy(®, elp, &passFail); CHKPASS; dmtxByteListInit(loc, 0, 0, &passFail); CHKPASS; for(i = 1; i <= NN; i++) { for(q = 1, j = 1; j <= lambda; j++) { reg.b[j] = GfMultAntilog(reg.b[j], j); q = GfAdd(q, reg.b[j]); } if(q == 0) { dmtxByteListPush(loc, NN - i, &passFail); CHKPASS; } } return (loc->length == lambda) ? DmtxTrue : DmtxFalse; } /** * Find the error values and repair. * Solve for the error value at the error location and correct the error. The * procedure is that found in Lin and Costello. * For the cases where the number of errors is known to be too large to * correct, the information symbols as received are output (the advantage of * systematic encoding is that hopefully some of the information symbols will * be okay and that if we are in luck, the errors are in the parity part of * the transmitted codeword). * \param rec * \param loc * \param elp * \param syn */ #undef CHKPASS #define CHKPASS { if(passFail == DmtxFail) return DmtxFail; } static DmtxPassFail RsRepairErrors(DmtxByteList *rec, const DmtxByteList *loc, const DmtxByteList *elp, const DmtxByteList *syn) { int i, j, q; int lambda = elp->length - 1; DmtxPassFail passFail; DmtxByte zVal, root, err; DmtxByte zStorage[MAX_ERROR_WORD_COUNT+1]; DmtxByteList z = dmtxByteListBuild(zStorage, sizeof(zStorage)); /* Form polynomial z(x) */ dmtxByteListPush(&z, 1, &passFail); CHKPASS; for(i = 1; i <= lambda; i++) { for(zVal = GfAdd(syn->b[i], elp->b[i]), j = 1; j < i; j++) zVal= GfAdd(zVal, GfMult(elp->b[i-j], syn->b[j])); dmtxByteListPush(&z, zVal, &passFail); CHKPASS; } for(i = 0; i < lambda; i++) { /* Calculate numerator of error term */ root = NN - loc->b[i]; for(err = 1, j = 1; j <= lambda; j++) err = GfAdd(err, GfMultAntilog(z.b[j], j * root)); if(err == 0) continue; /* Calculate denominator of error term */ for(q = 0, j = 0; j < lambda; j++) { if(j != i) q += log301[1 ^ antilog301[(loc->b[j] + root) % NN]]; } q %= NN; err = GfMultAntilog(err, NN - q); rec->b[loc->b[i]] = GfAdd(rec->b[loc->b[i]], err); } return DmtxPass; } libdmtx-0.7.7/dmtxregion.c000066400000000000000000001500261423156660700155320ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxregion.c * \brief Detect barcode regions */ #define DMTX_HOUGH_RES 180 /** * \brief Create copy of existing region struct * \param None * \return Initialized DmtxRegion struct */ extern DmtxRegion * dmtxRegionCreate(DmtxRegion *reg) { DmtxRegion *regCopy; regCopy = (DmtxRegion *)malloc(sizeof(DmtxRegion)); if(regCopy == NULL) return NULL; memcpy(regCopy, reg, sizeof(DmtxRegion)); return regCopy; } /** * \brief Destroy region struct * \param reg * \return void */ extern DmtxPassFail dmtxRegionDestroy(DmtxRegion **reg) { if(reg == NULL || *reg == NULL) return DmtxFail; free(*reg); *reg = NULL; return DmtxPass; } /** * \brief Find next barcode region * \param dec Pointer to DmtxDecode information struct * \param timeout Pointer to timeout time (NULL if none) * \return Detected region (if found) */ extern DmtxRegion * dmtxRegionFindNext(DmtxDecode *dec, DmtxTime *timeout) { int locStatus; DmtxPixelLoc loc; DmtxRegion *reg; /* Continue until we find a region or run out of chances */ for(;;) { locStatus = PopGridLocation(&(dec->grid), &loc); if(locStatus == DmtxRangeEnd) break; /* Scan location for presence of valid barcode region */ reg = dmtxRegionScanPixel(dec, loc.X, loc.Y); if(reg != NULL) return reg; /* Ran out of time? */ if(timeout != NULL && dmtxTimeExceeded(*timeout)) break; } return NULL; } /** * \brief Scan individual pixel for presence of barcode edge * \param dec Pointer to DmtxDecode information struct * \param loc Pixel location * \return Detected region (if any) */ extern DmtxRegion * dmtxRegionScanPixel(DmtxDecode *dec, int x, int y) { unsigned char *cache; DmtxRegion reg; DmtxPointFlow flowBegin; DmtxPixelLoc loc; loc.X = x; loc.Y = y; cache = dmtxDecodeGetCache(dec, loc.X, loc.Y); if(cache == NULL) return NULL; if((int)(*cache & 0x80) != 0x00) return NULL; /* Test for presence of any reasonable edge at this location */ flowBegin = MatrixRegionSeekEdge(dec, loc); if(flowBegin.mag < (int)(dec->edgeThresh * 7.65 + 0.5)) return NULL; memset(®, 0x00, sizeof(DmtxRegion)); /* Determine barcode orientation */ if(MatrixRegionOrientation(dec, ®, flowBegin) == DmtxFail) return NULL; if(dmtxRegionUpdateXfrms(dec, ®) == DmtxFail) return NULL; /* Define top edge */ if(MatrixRegionAlignCalibEdge(dec, ®, DmtxEdgeTop) == DmtxFail) return NULL; if(dmtxRegionUpdateXfrms(dec, ®) == DmtxFail) return NULL; /* Define right edge */ if(MatrixRegionAlignCalibEdge(dec, ®, DmtxEdgeRight) == DmtxFail) return NULL; if(dmtxRegionUpdateXfrms(dec, ®) == DmtxFail) return NULL; CALLBACK_MATRIX(®); /* Calculate the best fitting symbol size */ if(MatrixRegionFindSize(dec, ®) == DmtxFail) return NULL; /* Found a valid matrix region */ return dmtxRegionCreate(®); } /** * * */ static DmtxPointFlow MatrixRegionSeekEdge(DmtxDecode *dec, DmtxPixelLoc loc) { int i; int strongIdx; int channelCount; DmtxPointFlow flow, flowPlane[3]; DmtxPointFlow flowPos, flowPosBack; DmtxPointFlow flowNeg, flowNegBack; channelCount = dec->image->channelCount; /* Find whether red, green, or blue shows the strongest edge */ strongIdx = 0; for(i = 0; i < channelCount; i++) { flowPlane[i] = GetPointFlow(dec, i, loc, dmtxNeighborNone); if(i > 0 && flowPlane[i].mag > flowPlane[strongIdx].mag) strongIdx = i; } if(flowPlane[strongIdx].mag < 10) return dmtxBlankEdge; flow = flowPlane[strongIdx]; flowPos = FindStrongestNeighbor(dec, flow, +1); flowNeg = FindStrongestNeighbor(dec, flow, -1); if(flowPos.mag != 0 && flowNeg.mag != 0) { flowPosBack = FindStrongestNeighbor(dec, flowPos, -1); flowNegBack = FindStrongestNeighbor(dec, flowNeg, +1); if(flowPos.arrive == (flowPosBack.arrive+4)%8 && flowNeg.arrive == (flowNegBack.arrive+4)%8) { flow.arrive = dmtxNeighborNone; CALLBACK_POINT_PLOT(flow.loc, 1, 1, 1); return flow; } } return dmtxBlankEdge; } /** * * */ static DmtxPassFail MatrixRegionOrientation(DmtxDecode *dec, DmtxRegion *reg, DmtxPointFlow begin) { int cross; int minArea; int scale; int symbolShape; int maxDiagonal; DmtxPassFail err; DmtxBestLine line1x, line2x; DmtxBestLine line2n, line2p; DmtxFollow fTmp; if(dec->sizeIdxExpected == DmtxSymbolSquareAuto || (dec->sizeIdxExpected >= DmtxSymbol10x10 && dec->sizeIdxExpected <= DmtxSymbol144x144)) symbolShape = DmtxSymbolSquareAuto; else if(dec->sizeIdxExpected == DmtxSymbolRectAuto || (dec->sizeIdxExpected >= DmtxSymbol8x18 && dec->sizeIdxExpected <= DmtxSymbol16x48)) symbolShape = DmtxSymbolRectAuto; else symbolShape = DmtxSymbolShapeAuto; if(dec->edgeMax != DmtxUndefined) { if(symbolShape == DmtxSymbolRectAuto) maxDiagonal = (int)(1.23 * dec->edgeMax + 0.5); /* sqrt(5/4) + 10% */ else maxDiagonal = (int)(1.56 * dec->edgeMax + 0.5); /* sqrt(2) + 10% */ } else { maxDiagonal = DmtxUndefined; } /* Follow to end in both directions */ err = TrailBlazeContinuous(dec, reg, begin, maxDiagonal); if(err == DmtxFail || reg->stepsTotal < 40) { TrailClear(dec, reg, 0x40); return DmtxFail; } /* Filter out region candidates that are smaller than expected */ if(dec->edgeMin != DmtxUndefined) { scale = dmtxDecodeGetProp(dec, DmtxPropScale); if(symbolShape == DmtxSymbolSquareAuto) minArea = (dec->edgeMin * dec->edgeMin)/(scale * scale); else minArea = (2 * dec->edgeMin * dec->edgeMin)/(scale * scale); if((reg->boundMax.X - reg->boundMin.X) * (reg->boundMax.Y - reg->boundMin.Y) < minArea) { TrailClear(dec, reg, 0x40); return DmtxFail; } } line1x = FindBestSolidLine(dec, reg, 0, 0, +1, DmtxUndefined); if(line1x.mag < 5) { TrailClear(dec, reg, 0x40); return DmtxFail; } err = FindTravelLimits(dec, reg, &line1x); if(line1x.distSq < 100 || line1x.devn * 10 >= sqrt((double)line1x.distSq)) { TrailClear(dec, reg, 0x40); return DmtxFail; } assert(line1x.stepPos >= line1x.stepNeg); fTmp = FollowSeek(dec, reg, line1x.stepPos + 5); line2p = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepNeg, +1, line1x.angle); fTmp = FollowSeek(dec, reg, line1x.stepNeg - 5); line2n = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepPos, -1, line1x.angle); if(max(line2p.mag, line2n.mag) < 5) return DmtxFail; if(line2p.mag > line2n.mag) { line2x = line2p; err = FindTravelLimits(dec, reg, &line2x); if(line2x.distSq < 100 || line2x.devn * 10 >= sqrt((double)line2x.distSq)) return DmtxFail; cross = ((line1x.locPos.X - line1x.locNeg.X) * (line2x.locPos.Y - line2x.locNeg.Y)) - ((line1x.locPos.Y - line1x.locNeg.Y) * (line2x.locPos.X - line2x.locNeg.X)); if(cross > 0) { /* Condition 2 */ reg->polarity = +1; reg->locR = line2x.locPos; reg->stepR = line2x.stepPos; reg->locT = line1x.locNeg; reg->stepT = line1x.stepNeg; reg->leftLoc = line1x.locBeg; reg->leftAngle = line1x.angle; reg->bottomLoc = line2x.locBeg; reg->bottomAngle = line2x.angle; reg->leftLine = line1x; reg->bottomLine = line2x; } else { /* Condition 3 */ reg->polarity = -1; reg->locR = line1x.locNeg; reg->stepR = line1x.stepNeg; reg->locT = line2x.locPos; reg->stepT = line2x.stepPos; reg->leftLoc = line2x.locBeg; reg->leftAngle = line2x.angle; reg->bottomLoc = line1x.locBeg; reg->bottomAngle = line1x.angle; reg->leftLine = line2x; reg->bottomLine = line1x; } } else { line2x = line2n; err = FindTravelLimits(dec, reg, &line2x); if(line2x.distSq < 100 || line2x.devn / sqrt((double)line2x.distSq) >= 0.1) return DmtxFail; cross = ((line1x.locNeg.X - line1x.locPos.X) * (line2x.locNeg.Y - line2x.locPos.Y)) - ((line1x.locNeg.Y - line1x.locPos.Y) * (line2x.locNeg.X - line2x.locPos.X)); if(cross > 0) { /* Condition 1 */ reg->polarity = -1; reg->locR = line2x.locNeg; reg->stepR = line2x.stepNeg; reg->locT = line1x.locPos; reg->stepT = line1x.stepPos; reg->leftLoc = line1x.locBeg; reg->leftAngle = line1x.angle; reg->bottomLoc = line2x.locBeg; reg->bottomAngle = line2x.angle; reg->leftLine = line1x; reg->bottomLine = line2x; } else { /* Condition 4 */ reg->polarity = +1; reg->locR = line1x.locPos; reg->stepR = line1x.stepPos; reg->locT = line2x.locNeg; reg->stepT = line2x.stepNeg; reg->leftLoc = line2x.locBeg; reg->leftAngle = line2x.angle; reg->bottomLoc = line1x.locBeg; reg->bottomAngle = line1x.angle; reg->leftLine = line2x; reg->bottomLine = line1x; } } /* CALLBACK_POINT_PLOT(reg->locR, 2, 1, 1); CALLBACK_POINT_PLOT(reg->locT, 2, 1, 1); */ reg->leftKnown = reg->bottomKnown = 1; return DmtxPass; } /** * * */ static long DistanceSquared(DmtxPixelLoc a, DmtxPixelLoc b) { long xDelta, yDelta; xDelta = a.X - b.X; yDelta = a.Y - b.Y; return (xDelta * xDelta) + (yDelta * yDelta); } /** * * */ extern DmtxPassFail dmtxRegionUpdateCorners(DmtxDecode *dec, DmtxRegion *reg, DmtxVector2 p00, DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01) { double xMax, yMax; double tx, ty, phi, shx, scx, scy, skx, sky; double dimOT, dimOR, dimTX, dimRX, ratio; DmtxVector2 vOT, vOR, vTX, vRX, vTmp; DmtxMatrix3 m, mtxy, mphi, mshx, mscx, mscy, mscxy, msky, mskx; xMax = (double)(dmtxDecodeGetProp(dec, DmtxPropWidth) - 1); yMax = (double)(dmtxDecodeGetProp(dec, DmtxPropHeight) - 1); if(p00.X < 0.0 || p00.Y < 0.0 || p00.X > xMax || p00.Y > yMax || p01.X < 0.0 || p01.Y < 0.0 || p01.X > xMax || p01.Y > yMax || p10.X < 0.0 || p10.Y < 0.0 || p10.X > xMax || p10.Y > yMax) return DmtxFail; dimOT = dmtxVector2Mag(dmtxVector2Sub(&vOT, &p01, &p00)); /* XXX could use MagSquared() */ dimOR = dmtxVector2Mag(dmtxVector2Sub(&vOR, &p10, &p00)); dimTX = dmtxVector2Mag(dmtxVector2Sub(&vTX, &p11, &p01)); dimRX = dmtxVector2Mag(dmtxVector2Sub(&vRX, &p11, &p10)); /* Verify that sides are reasonably long */ if(dimOT <= 8.0 || dimOR <= 8.0 || dimTX <= 8.0 || dimRX <= 8.0) return DmtxFail; /* Verify that the 4 corners define a reasonably fat quadrilateral */ ratio = dimOT / dimRX; if(ratio <= 0.5 || ratio >= 2.0) return DmtxFail; ratio = dimOR / dimTX; if(ratio <= 0.5 || ratio >= 2.0) return DmtxFail; /* Verify this is not a bowtie shape */ if(dmtxVector2Cross(&vOR, &vRX) <= 0.0 || dmtxVector2Cross(&vOT, &vTX) >= 0.0) return DmtxFail; if(RightAngleTrueness(p00, p10, p11, M_PI_2) <= dec->squareDevn) return DmtxFail; if(RightAngleTrueness(p10, p11, p01, M_PI_2) <= dec->squareDevn) return DmtxFail; /* Calculate values needed for transformations */ tx = -1 * p00.X; ty = -1 * p00.Y; dmtxMatrix3Translate(mtxy, tx, ty); phi = atan2(vOT.X, vOT.Y); dmtxMatrix3Rotate(mphi, phi); dmtxMatrix3Multiply(m, mtxy, mphi); dmtxMatrix3VMultiply(&vTmp, &p10, m); shx = -vTmp.Y / vTmp.X; dmtxMatrix3Shear(mshx, 0.0, shx); dmtxMatrix3MultiplyBy(m, mshx); scx = 1.0/vTmp.X; dmtxMatrix3Scale(mscx, scx, 1.0); dmtxMatrix3MultiplyBy(m, mscx); dmtxMatrix3VMultiply(&vTmp, &p11, m); scy = 1.0/vTmp.Y; dmtxMatrix3Scale(mscy, 1.0, scy); dmtxMatrix3MultiplyBy(m, mscy); dmtxMatrix3VMultiply(&vTmp, &p11, m); skx = vTmp.X; dmtxMatrix3LineSkewSide(mskx, 1.0, skx, 1.0); dmtxMatrix3MultiplyBy(m, mskx); dmtxMatrix3VMultiply(&vTmp, &p01, m); sky = vTmp.Y; dmtxMatrix3LineSkewTop(msky, sky, 1.0, 1.0); dmtxMatrix3Multiply(reg->raw2fit, m, msky); /* Create inverse matrix by reverse (avoid straight matrix inversion) */ dmtxMatrix3LineSkewTopInv(msky, sky, 1.0, 1.0); dmtxMatrix3LineSkewSideInv(mskx, 1.0, skx, 1.0); dmtxMatrix3Multiply(m, msky, mskx); dmtxMatrix3Scale(mscxy, 1.0/scx, 1.0/scy); dmtxMatrix3MultiplyBy(m, mscxy); dmtxMatrix3Shear(mshx, 0.0, -shx); dmtxMatrix3MultiplyBy(m, mshx); dmtxMatrix3Rotate(mphi, -phi); dmtxMatrix3MultiplyBy(m, mphi); dmtxMatrix3Translate(mtxy, -tx, -ty); dmtxMatrix3Multiply(reg->fit2raw, m, mtxy); return DmtxPass; } /** * * */ extern DmtxPassFail dmtxRegionUpdateXfrms(DmtxDecode *dec, DmtxRegion *reg) { double radians; DmtxRay2 rLeft, rBottom, rTop, rRight; DmtxVector2 p00, p10, p11, p01; assert(reg->leftKnown != 0 && reg->bottomKnown != 0); /* Build ray representing left edge */ rLeft.p.X = (double)reg->leftLoc.X; rLeft.p.Y = (double)reg->leftLoc.Y; radians = reg->leftAngle * (M_PI/DMTX_HOUGH_RES); rLeft.v.X = cos(radians); rLeft.v.Y = sin(radians); rLeft.tMin = 0.0; rLeft.tMax = dmtxVector2Norm(&rLeft.v); /* Build ray representing bottom edge */ rBottom.p.X = (double)reg->bottomLoc.X; rBottom.p.Y = (double)reg->bottomLoc.Y; radians = reg->bottomAngle * (M_PI/DMTX_HOUGH_RES); rBottom.v.X = cos(radians); rBottom.v.Y = sin(radians); rBottom.tMin = 0.0; rBottom.tMax = dmtxVector2Norm(&rBottom.v); /* Build ray representing top edge */ if(reg->topKnown != 0) { rTop.p.X = (double)reg->topLoc.X; rTop.p.Y = (double)reg->topLoc.Y; radians = reg->topAngle * (M_PI/DMTX_HOUGH_RES); rTop.v.X = cos(radians); rTop.v.Y = sin(radians); rTop.tMin = 0.0; rTop.tMax = dmtxVector2Norm(&rTop.v); } else { rTop.p.X = (double)reg->locT.X; rTop.p.Y = (double)reg->locT.Y; radians = reg->bottomAngle * (M_PI/DMTX_HOUGH_RES); rTop.v.X = cos(radians); rTop.v.Y = sin(radians); rTop.tMin = 0.0; rTop.tMax = rBottom.tMax; } /* Build ray representing right edge */ if(reg->rightKnown != 0) { rRight.p.X = (double)reg->rightLoc.X; rRight.p.Y = (double)reg->rightLoc.Y; radians = reg->rightAngle * (M_PI/DMTX_HOUGH_RES); rRight.v.X = cos(radians); rRight.v.Y = sin(radians); rRight.tMin = 0.0; rRight.tMax = dmtxVector2Norm(&rRight.v); } else { rRight.p.X = (double)reg->locR.X; rRight.p.Y = (double)reg->locR.Y; radians = reg->leftAngle * (M_PI/DMTX_HOUGH_RES); rRight.v.X = cos(radians); rRight.v.Y = sin(radians); rRight.tMin = 0.0; rRight.tMax = rLeft.tMax; } /* Calculate 4 corners, real or imagined */ if(dmtxRay2Intersect(&p00, &rLeft, &rBottom) == DmtxFail) return DmtxFail; if(dmtxRay2Intersect(&p10, &rBottom, &rRight) == DmtxFail) return DmtxFail; if(dmtxRay2Intersect(&p11, &rRight, &rTop) == DmtxFail) return DmtxFail; if(dmtxRay2Intersect(&p01, &rTop, &rLeft) == DmtxFail) return DmtxFail; if(dmtxRegionUpdateCorners(dec, reg, p00, p10, p11, p01) != DmtxPass) return DmtxFail; return DmtxPass; } /** * * */ static double RightAngleTrueness(DmtxVector2 c0, DmtxVector2 c1, DmtxVector2 c2, double angle) { DmtxVector2 vA, vB; DmtxMatrix3 m; dmtxVector2Norm(dmtxVector2Sub(&vA, &c0, &c1)); dmtxVector2Norm(dmtxVector2Sub(&vB, &c2, &c1)); dmtxMatrix3Rotate(m, angle); dmtxMatrix3VMultiplyBy(&vB, m); return dmtxVector2Dot(&vA, &vB); } /** * \brief Read color of Data Matrix module location * \param dec * \param reg * \param symbolRow * \param symbolCol * \param sizeIdx * \return Averaged module color */ static int ReadModuleColor(DmtxDecode *dec, DmtxRegion *reg, int symbolRow, int symbolCol, int sizeIdx, int colorPlane) { int i; int symbolRows, symbolCols; int color, colorTmp; double sampleX[] = { 0.5, 0.4, 0.5, 0.6, 0.5 }; double sampleY[] = { 0.5, 0.5, 0.4, 0.5, 0.6 }; DmtxVector2 p; symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx); color = 0; for(i = 0; i < 5; i++) { p.X = (1.0/symbolCols) * (symbolCol + sampleX[i]); p.Y = (1.0/symbolRows) * (symbolRow + sampleY[i]); dmtxMatrix3VMultiplyBy(&p, reg->fit2raw); //fprintf(stdout, "%dx%d\n", (int)(p.X + 0.5), (int)(p.Y + 0.5)); dmtxDecodeGetPixelValue(dec, (int)(p.X + 0.5), (int)(p.Y + 0.5), colorPlane, &colorTmp); color += colorTmp; } //fprintf(stdout, "\n"); return color/5; } /** * \brief Determine barcode size, expressed in modules * \param image * \param reg * \return DmtxPass | DmtxFail */ static DmtxPassFail MatrixRegionFindSize(DmtxDecode *dec, DmtxRegion *reg) { int row, col; int sizeIdxBeg, sizeIdxEnd; int sizeIdx, bestSizeIdx; int symbolRows, symbolCols; int jumpCount, errors; int color; int colorOnAvg, bestColorOnAvg; int colorOffAvg, bestColorOffAvg; int contrast, bestContrast; // DmtxImage *img; // img = dec->image; bestSizeIdx = DmtxUndefined; bestContrast = 0; bestColorOnAvg = bestColorOffAvg = 0; if(dec->sizeIdxExpected == DmtxSymbolShapeAuto) { sizeIdxBeg = 0; sizeIdxEnd = DmtxSymbolSquareCount + DmtxSymbolRectCount; } else if(dec->sizeIdxExpected == DmtxSymbolSquareAuto) { sizeIdxBeg = 0; sizeIdxEnd = DmtxSymbolSquareCount; } else if(dec->sizeIdxExpected == DmtxSymbolRectAuto) { sizeIdxBeg = DmtxSymbolSquareCount; sizeIdxEnd = DmtxSymbolSquareCount + DmtxSymbolRectCount; } else { sizeIdxBeg = dec->sizeIdxExpected; sizeIdxEnd = dec->sizeIdxExpected + 1; } /* Test each barcode size to find best contrast in calibration modules */ for(sizeIdx = sizeIdxBeg; sizeIdx < sizeIdxEnd; sizeIdx++) { symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx); colorOnAvg = colorOffAvg = 0; /* Sum module colors along horizontal calibration bar */ row = symbolRows - 1; for(col = 0; col < symbolCols; col++) { color = ReadModuleColor(dec, reg, row, col, sizeIdx, reg->flowBegin.plane); if((col & 0x01) != 0x00) colorOffAvg += color; else colorOnAvg += color; } /* Sum module colors along vertical calibration bar */ col = symbolCols - 1; for(row = 0; row < symbolRows; row++) { color = ReadModuleColor(dec, reg, row, col, sizeIdx, reg->flowBegin.plane); if((row & 0x01) != 0x00) colorOffAvg += color; else colorOnAvg += color; } colorOnAvg = (colorOnAvg * 2)/(symbolRows + symbolCols); colorOffAvg = (colorOffAvg * 2)/(symbolRows + symbolCols); contrast = abs(colorOnAvg - colorOffAvg); if(contrast < 20) continue; if(contrast > bestContrast) { bestContrast = contrast; bestSizeIdx = sizeIdx; bestColorOnAvg = colorOnAvg; bestColorOffAvg = colorOffAvg; } } /* If no sizes produced acceptable contrast then call it quits */ if(bestSizeIdx == DmtxUndefined || bestContrast < 20) return DmtxFail; reg->sizeIdx = bestSizeIdx; reg->onColor = bestColorOnAvg; reg->offColor = bestColorOffAvg; reg->symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, reg->sizeIdx); reg->symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, reg->sizeIdx); reg->mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, reg->sizeIdx); reg->mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, reg->sizeIdx); /* Tally jumps on horizontal calibration bar to verify sizeIdx */ jumpCount = CountJumpTally(dec, reg, 0, reg->symbolRows - 1, DmtxDirRight); errors = abs(1 + jumpCount - reg->symbolCols); if(jumpCount < 0 || errors > 2) return DmtxFail; /* Tally jumps on vertical calibration bar to verify sizeIdx */ jumpCount = CountJumpTally(dec, reg, reg->symbolCols - 1, 0, DmtxDirUp); errors = abs(1 + jumpCount - reg->symbolRows); if(jumpCount < 0 || errors > 2) return DmtxFail; /* Tally jumps on horizontal finder bar to verify sizeIdx */ errors = CountJumpTally(dec, reg, 0, 0, DmtxDirRight); if(jumpCount < 0 || errors > 2) return DmtxFail; /* Tally jumps on vertical finder bar to verify sizeIdx */ errors = CountJumpTally(dec, reg, 0, 0, DmtxDirUp); if(errors < 0 || errors > 2) return DmtxFail; /* Tally jumps on surrounding whitespace, else fail */ errors = CountJumpTally(dec, reg, 0, -1, DmtxDirRight); if(errors < 0 || errors > 2) return DmtxFail; errors = CountJumpTally(dec, reg, -1, 0, DmtxDirUp); if(errors < 0 || errors > 2) return DmtxFail; errors = CountJumpTally(dec, reg, 0, reg->symbolRows, DmtxDirRight); if(errors < 0 || errors > 2) return DmtxFail; errors = CountJumpTally(dec, reg, reg->symbolCols, 0, DmtxDirUp); if(errors < 0 || errors > 2) return DmtxFail; return DmtxPass; } /** * \brief Count the number of number of transitions between light and dark * \param img * \param reg * \param xStart * \param yStart * \param dir * \return Jump count */ static int CountJumpTally(DmtxDecode *dec, DmtxRegion *reg, int xStart, int yStart, DmtxDirection dir) { int x, xInc = 0; int y, yInc = 0; int state = DmtxModuleOn; int jumpCount = 0; int jumpThreshold; int tModule, tPrev; int darkOnLight; int color; assert(xStart == 0 || yStart == 0); assert(dir == DmtxDirRight || dir == DmtxDirUp); if(dir == DmtxDirRight) xInc = 1; else yInc = 1; if(xStart == -1 || xStart == reg->symbolCols || yStart == -1 || yStart == reg->symbolRows) state = DmtxModuleOff; darkOnLight = (int)(reg->offColor > reg->onColor); jumpThreshold = abs((int)(0.4 * (reg->onColor - reg->offColor) + 0.5)); color = ReadModuleColor(dec, reg, yStart, xStart, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; for(x = xStart + xInc, y = yStart + yInc; (dir == DmtxDirRight && x < reg->symbolCols) || (dir == DmtxDirUp && y < reg->symbolRows); x += xInc, y += yInc) { tPrev = tModule; color = ReadModuleColor(dec, reg, y, x, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; if(state == DmtxModuleOff) { if(tModule > tPrev + jumpThreshold) { jumpCount++; state = DmtxModuleOn; } } else { if(tModule < tPrev - jumpThreshold) { jumpCount++; state = DmtxModuleOff; } } } return jumpCount; } /** * * */ static DmtxPointFlow GetPointFlow(DmtxDecode *dec, int colorPlane, DmtxPixelLoc loc, int arrive) { static const int coefficient[] = { 0, 1, 2, 1, 0, -1, -2, -1 }; int err; int patternIdx, coefficientIdx; int compass, compassMax; int mag[4] = { 0 }; int xAdjust, yAdjust; int color, colorPattern[8]; DmtxPointFlow flow; for(patternIdx = 0; patternIdx < 8; patternIdx++) { xAdjust = loc.X + dmtxPatternX[patternIdx]; yAdjust = loc.Y + dmtxPatternY[patternIdx]; err = dmtxDecodeGetPixelValue(dec, xAdjust, yAdjust, colorPlane, &colorPattern[patternIdx]); if(err == DmtxFail) return dmtxBlankEdge; } /* Calculate this pixel's flow intensity for each direction (-45, 0, 45, 90) */ compassMax = 0; for(compass = 0; compass < 4; compass++) { /* Add portion from each position in the convolution matrix pattern */ for(patternIdx = 0; patternIdx < 8; patternIdx++) { coefficientIdx = (patternIdx - compass + 8) % 8; if(coefficient[coefficientIdx] == 0) continue; color = colorPattern[patternIdx]; switch(coefficient[coefficientIdx]) { case 2: mag[compass] += color; /* Fall through */ case 1: mag[compass] += color; break; case -2: mag[compass] -= color; /* Fall through */ case -1: mag[compass] -= color; break; } } /* Identify strongest compass flow */ if(compass != 0 && abs(mag[compass]) > abs(mag[compassMax])) compassMax = compass; } /* Convert signed compass direction into unique flow directions (0-7) */ flow.plane = colorPlane; flow.arrive = arrive; flow.depart = (mag[compassMax] > 0) ? compassMax + 4 : compassMax; flow.mag = abs(mag[compassMax]); flow.loc = loc; return flow; } /** * * */ static DmtxPointFlow FindStrongestNeighbor(DmtxDecode *dec, DmtxPointFlow center, int sign) { int i; int strongIdx; int attempt, attemptDiff; int occupied; unsigned char *cache; DmtxPixelLoc loc; DmtxPointFlow flow[8]; attempt = (sign < 0) ? center.depart : (center.depart+4)%8; occupied = 0; strongIdx = DmtxUndefined; for(i = 0; i < 8; i++) { loc.X = center.loc.X + dmtxPatternX[i]; loc.Y = center.loc.Y + dmtxPatternY[i]; cache = dmtxDecodeGetCache(dec, loc.X, loc.Y); if(cache == NULL) continue; if((int)(*cache & 0x80) != 0x00) { if(++occupied > 2) return dmtxBlankEdge; else continue; } attemptDiff = abs(attempt - i); if(attemptDiff > 4) attemptDiff = 8 - attemptDiff; if(attemptDiff > 1) continue; flow[i] = GetPointFlow(dec, center.plane, loc, i); if(strongIdx == DmtxUndefined || flow[i].mag > flow[strongIdx].mag || (flow[i].mag == flow[strongIdx].mag && ((i & 0x01) != 0))) { strongIdx = i; } } return (strongIdx == DmtxUndefined) ? dmtxBlankEdge : flow[strongIdx]; } /** * * */ static DmtxFollow FollowSeek(DmtxDecode *dec, DmtxRegion *reg, int seek) { int i; int sign; DmtxFollow follow; follow.loc = reg->flowBegin.loc; follow.step = 0; follow.ptr = dmtxDecodeGetCache(dec, follow.loc.X, follow.loc.Y); assert(follow.ptr != NULL); follow.neighbor = *follow.ptr; sign = (seek > 0) ? +1 : -1; for(i = 0; i != seek; i += sign) { follow = FollowStep(dec, reg, follow, sign); assert(follow.ptr != NULL); assert(abs(follow.step) <= reg->stepsTotal); } return follow; } /** * * */ static DmtxFollow FollowSeekLoc(DmtxDecode *dec, DmtxPixelLoc loc) { DmtxFollow follow; follow.loc = loc; follow.step = 0; follow.ptr = dmtxDecodeGetCache(dec, follow.loc.X, follow.loc.Y); assert(follow.ptr != NULL); follow.neighbor = *follow.ptr; return follow; } /** * * */ static DmtxFollow FollowStep(DmtxDecode *dec, DmtxRegion *reg, DmtxFollow followBeg, int sign) { int patternIdx; int stepMod; int factor; DmtxFollow follow; assert(abs(sign) == 1); assert((int)(followBeg.neighbor & 0x40) != 0x00); factor = reg->stepsTotal + 1; if(sign > 0) stepMod = (factor + (followBeg.step % factor)) % factor; else stepMod = (factor - (followBeg.step % factor)) % factor; /* End of positive trail -- magic jump */ if(sign > 0 && stepMod == reg->jumpToNeg) { follow.loc = reg->finalNeg; } /* End of negative trail -- magic jump */ else if(sign < 0 && stepMod == reg->jumpToPos) { follow.loc = reg->finalPos; } /* Trail in progress -- normal jump */ else { patternIdx = (sign < 0) ? followBeg.neighbor & 0x07 : ((followBeg.neighbor & 0x38) >> 3); follow.loc.X = followBeg.loc.X + dmtxPatternX[patternIdx]; follow.loc.Y = followBeg.loc.Y + dmtxPatternY[patternIdx]; } follow.step = followBeg.step + sign; follow.ptr = dmtxDecodeGetCache(dec, follow.loc.X, follow.loc.Y); assert(follow.ptr != NULL); follow.neighbor = *follow.ptr; return follow; } /** * * */ static DmtxFollow FollowStep2(DmtxDecode *dec, DmtxFollow followBeg, int sign) { int patternIdx; DmtxFollow follow; assert(abs(sign) == 1); assert((int)(followBeg.neighbor & 0x40) != 0x00); patternIdx = (sign < 0) ? followBeg.neighbor & 0x07 : ((followBeg.neighbor & 0x38) >> 3); follow.loc.X = followBeg.loc.X + dmtxPatternX[patternIdx]; follow.loc.Y = followBeg.loc.Y + dmtxPatternY[patternIdx]; follow.step = followBeg.step + sign; follow.ptr = dmtxDecodeGetCache(dec, follow.loc.X, follow.loc.Y); assert(follow.ptr != NULL); follow.neighbor = *follow.ptr; return follow; } /** * vaiiiooo * -------- * 0x80 v = visited bit * 0x40 a = assigned bit * 0x38 u = 3 bits points upstream 0-7 * 0x07 d = 3 bits points downstream 0-7 */ static DmtxPassFail TrailBlazeContinuous(DmtxDecode *dec, DmtxRegion *reg, DmtxPointFlow flowBegin, int maxDiagonal) { int posAssigns, negAssigns, clears; int sign; int steps; unsigned char *cache, *cacheNext, *cacheBeg; DmtxPointFlow flow, flowNext; DmtxPixelLoc boundMin, boundMax; boundMin = boundMax = flowBegin.loc; cacheBeg = dmtxDecodeGetCache(dec, flowBegin.loc.X, flowBegin.loc.Y); if(cacheBeg == NULL) return DmtxFail; *cacheBeg = (0x80 | 0x40); /* Mark location as visited and assigned */ reg->flowBegin = flowBegin; posAssigns = negAssigns = 0; for(sign = 1; sign >= -1; sign -= 2) { flow = flowBegin; cache = cacheBeg; for(steps = 0; ; steps++) { if(maxDiagonal != DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal || boundMax.Y - boundMin.Y > maxDiagonal)) break; /* Find the strongest eligible neighbor */ flowNext = FindStrongestNeighbor(dec, flow, sign); if(flowNext.mag < 50) break; /* Get the neighbor's cache location */ cacheNext = dmtxDecodeGetCache(dec, flowNext.loc.X, flowNext.loc.Y); if(cacheNext == NULL) break; assert(!(*cacheNext & 0x80)); /* Mark departure from current location. If flowing downstream * (sign < 0) then departure vector here is the arrival vector * of the next location. Upstream flow uses the opposite rule. */ *cache |= (sign < 0) ? flowNext.arrive : flowNext.arrive << 3; /* Mark known direction for next location */ /* If testing downstream (sign < 0) then next upstream is opposite of next arrival */ /* If testing upstream (sign > 0) then next downstream is opposite of next arrival */ *cacheNext = (sign < 0) ? (((flowNext.arrive + 4)%8) << 3) : ((flowNext.arrive + 4)%8); *cacheNext |= (0x80 | 0x40); /* Mark location as visited and assigned */ if(sign > 0) posAssigns++; else negAssigns++; cache = cacheNext; flow = flowNext; if(flow.loc.X > boundMax.X) boundMax.X = flow.loc.X; else if(flow.loc.X < boundMin.X) boundMin.X = flow.loc.X; if(flow.loc.Y > boundMax.Y) boundMax.Y = flow.loc.Y; else if(flow.loc.Y < boundMin.Y) boundMin.Y = flow.loc.Y; /* CALLBACK_POINT_PLOT(flow.loc, (sign > 0) ? 2 : 3, 1, 2); */ } if(sign > 0) { reg->finalPos = flow.loc; reg->jumpToNeg = steps; } else { reg->finalNeg = flow.loc; reg->jumpToPos = steps; } } reg->stepsTotal = reg->jumpToPos + reg->jumpToNeg; reg->boundMin = boundMin; reg->boundMax = boundMax; /* Clear "visited" bit from trail */ clears = TrailClear(dec, reg, 0x80); assert(posAssigns + negAssigns == clears - 1); /* XXX clean this up ... redundant test above */ if(maxDiagonal != DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal || boundMax.Y - boundMin.Y > maxDiagonal)) return DmtxFail; return DmtxPass; } /** * recives bresline, and follows strongest neighbor unless it involves * ratcheting bresline inward or backward (although back + outward is allowed). * */ static int TrailBlazeGapped(DmtxDecode *dec, DmtxRegion *reg, DmtxBresLine line, int streamDir) { unsigned char *beforeCache, *afterCache; DmtxBoolean onEdge; int distSq, distSqMax; int travel, outward; int xDiff, yDiff; int steps; int stepDir, dirMap[] = { 0, 1, 2, 7, 8, 3, 6, 5, 4 }; DmtxPassFail err; DmtxPixelLoc beforeStep, afterStep; DmtxPointFlow flow, flowNext; DmtxPixelLoc loc0; int xStep, yStep; loc0 = line.loc; flow = GetPointFlow(dec, reg->flowBegin.plane, loc0, dmtxNeighborNone); distSqMax = (line.xDelta * line.xDelta) + (line.yDelta * line.yDelta); steps = 0; onEdge = DmtxTrue; beforeStep = loc0; beforeCache = dmtxDecodeGetCache(dec, loc0.X, loc0.Y); if(beforeCache == NULL) return DmtxFail; else *beforeCache = 0x00; /* probably should just overwrite one direction */ do { if(onEdge == DmtxTrue) { flowNext = FindStrongestNeighbor(dec, flow, streamDir); if(flowNext.mag == DmtxUndefined) break; err = BresLineGetStep(line, flowNext.loc, &travel, &outward); if (err == DmtxFail) { return DmtxFail; } if(flowNext.mag < 50 || outward < 0 || (outward == 0 && travel < 0)) { onEdge = DmtxFalse; } else { BresLineStep(&line, travel, outward); flow = flowNext; } } if(onEdge == DmtxFalse) { BresLineStep(&line, 1, 0); flow = GetPointFlow(dec, reg->flowBegin.plane, line.loc, dmtxNeighborNone); if(flow.mag > 50) onEdge = DmtxTrue; } afterStep = line.loc; afterCache = dmtxDecodeGetCache(dec, afterStep.X, afterStep.Y); if(afterCache == NULL) break; /* Determine step direction using pure magic */ xStep = afterStep.X - beforeStep.X; yStep = afterStep.Y - beforeStep.Y; assert(abs(xStep) <= 1 && abs(yStep) <= 1); stepDir = dirMap[3 * yStep + xStep + 4]; assert(stepDir != 8); if(streamDir < 0) { *beforeCache |= (0x40 | stepDir); *afterCache = (((stepDir + 4)%8) << 3); } else { *beforeCache |= (0x40 | (stepDir << 3)); *afterCache = ((stepDir + 4)%8); } /* Guaranteed to have taken one step since top of loop */ xDiff = line.loc.X - loc0.X; yDiff = line.loc.Y - loc0.Y; distSq = (xDiff * xDiff) + (yDiff * yDiff); beforeStep = line.loc; beforeCache = afterCache; steps++; } while(distSq < distSqMax); return steps; } /** * * */ static int TrailClear(DmtxDecode *dec, DmtxRegion *reg, int clearMask) { int clears; DmtxFollow follow; assert((clearMask | 0xff) == 0xff); /* Clear "visited" bit from trail */ clears = 0; follow = FollowSeek(dec, reg, 0); while(abs(follow.step) <= reg->stepsTotal) { assert((int)(*follow.ptr & clearMask) != 0x00); *follow.ptr &= (clearMask ^ 0xff); follow = FollowStep(dec, reg, follow, +1); clears++; } return clears; } /** * * */ static DmtxBestLine FindBestSolidLine(DmtxDecode *dec, DmtxRegion *reg, int step0, int step1, int streamDir, int houghAvoid) { int hough[3][DMTX_HOUGH_RES] = { { 0 } }; int houghMin, houghMax; char houghTest[DMTX_HOUGH_RES]; int i; int step; int sign; int tripSteps; int angleBest; int hOffset, hOffsetBest; int xDiff, yDiff; int dH; DmtxRay2 rH; DmtxFollow follow; DmtxBestLine line; DmtxPixelLoc rHp; memset(&line, 0x00, sizeof(DmtxBestLine)); memset(&rH, 0x00, sizeof(DmtxRay2)); angleBest = 0; hOffset = hOffsetBest = 0; sign = 0; /* Always follow path flowing away from the trail start */ if(step0 != 0) { if(step0 > 0) { sign = +1; tripSteps = (step1 - step0 + reg->stepsTotal) % reg->stepsTotal; } else { sign = -1; tripSteps = (step0 - step1 + reg->stepsTotal) % reg->stepsTotal; } if(tripSteps == 0) tripSteps = reg->stepsTotal; } else if(step1 != 0) { sign = (step1 > 0) ? +1 : -1; tripSteps = abs(step1); } else if(step1 == 0) { sign = +1; tripSteps = reg->stepsTotal; } assert(sign == streamDir); follow = FollowSeek(dec, reg, step0); rHp = follow.loc; line.stepBeg = line.stepPos = line.stepNeg = step0; line.locBeg = follow.loc; line.locPos = follow.loc; line.locNeg = follow.loc; /* Predetermine which angles to test */ for(i = 0; i < DMTX_HOUGH_RES; i++) { if(houghAvoid == DmtxUndefined) { houghTest[i] = 1; } else { houghMin = (houghAvoid + DMTX_HOUGH_RES/6) % DMTX_HOUGH_RES; houghMax = (houghAvoid - DMTX_HOUGH_RES/6 + DMTX_HOUGH_RES) % DMTX_HOUGH_RES; if(houghMin > houghMax) houghTest[i] = (i > houghMin || i < houghMax) ? 1 : 0; else houghTest[i] = (i > houghMin && i < houghMax) ? 1 : 0; } } /* Test each angle for steps along path */ for(step = 0; step < tripSteps; step++) { xDiff = follow.loc.X - rHp.X; yDiff = follow.loc.Y - rHp.Y; /* Increment Hough accumulator */ for(i = 0; i < DMTX_HOUGH_RES; i++) { if((int)houghTest[i] == 0) continue; dH = (rHvX[i] * yDiff) - (rHvY[i] * xDiff); if(dH >= -384 && dH <= 384) { if(dH > 128) hOffset = 2; else if(dH >= -128) hOffset = 1; else hOffset = 0; hough[hOffset][i]++; /* New angle takes over lead */ if(hough[hOffset][i] > hough[hOffsetBest][angleBest]) { angleBest = i; hOffsetBest = hOffset; } } } /* CALLBACK_POINT_PLOT(follow.loc, (sign > 1) ? 4 : 3, 1, 2); */ follow = FollowStep(dec, reg, follow, sign); } line.angle = angleBest; line.hOffset = hOffsetBest; line.mag = hough[hOffsetBest][angleBest]; return line; } /** * * */ static DmtxBestLine FindBestSolidLine2(DmtxDecode *dec, DmtxPixelLoc loc0, int tripSteps, int sign, int houghAvoid) { int hough[3][DMTX_HOUGH_RES] = { { 0 } }; int houghMin, houghMax; char houghTest[DMTX_HOUGH_RES]; int i; int step; int angleBest; int hOffset, hOffsetBest; int xDiff, yDiff; int dH; DmtxRay2 rH; DmtxBestLine line; DmtxPixelLoc rHp; DmtxFollow follow; memset(&line, 0x00, sizeof(DmtxBestLine)); memset(&rH, 0x00, sizeof(DmtxRay2)); angleBest = 0; hOffset = hOffsetBest = 0; follow = FollowSeekLoc(dec, loc0); rHp = line.locBeg = line.locPos = line.locNeg = follow.loc; line.stepBeg = line.stepPos = line.stepNeg = 0; /* Predetermine which angles to test */ for(i = 0; i < DMTX_HOUGH_RES; i++) { if(houghAvoid == DmtxUndefined) { houghTest[i] = 1; } else { houghMin = (houghAvoid + DMTX_HOUGH_RES/6) % DMTX_HOUGH_RES; houghMax = (houghAvoid - DMTX_HOUGH_RES/6 + DMTX_HOUGH_RES) % DMTX_HOUGH_RES; if(houghMin > houghMax) houghTest[i] = (i > houghMin || i < houghMax) ? 1 : 0; else houghTest[i] = (i > houghMin && i < houghMax) ? 1 : 0; } } /* Test each angle for steps along path */ for(step = 0; step < tripSteps; step++) { xDiff = follow.loc.X - rHp.X; yDiff = follow.loc.Y - rHp.Y; /* Increment Hough accumulator */ for(i = 0; i < DMTX_HOUGH_RES; i++) { if((int)houghTest[i] == 0) continue; dH = (rHvX[i] * yDiff) - (rHvY[i] * xDiff); if(dH >= -384 && dH <= 384) { if(dH > 128) hOffset = 2; else if(dH >= -128) hOffset = 1; else hOffset = 0; hough[hOffset][i]++; /* New angle takes over lead */ if(hough[hOffset][i] > hough[hOffsetBest][angleBest]) { angleBest = i; hOffsetBest = hOffset; } } } /* CALLBACK_POINT_PLOT(follow.loc, (sign > 1) ? 4 : 3, 1, 2); */ follow = FollowStep2(dec, follow, sign); } line.angle = angleBest; line.hOffset = hOffsetBest; line.mag = hough[hOffsetBest][angleBest]; return line; } /** * * */ static DmtxPassFail FindTravelLimits(DmtxDecode *dec, DmtxRegion *reg, DmtxBestLine *line) { int i; int distSq, distSqMax; int xDiff, yDiff; int posRunning, negRunning; int posTravel, negTravel; int posWander, posWanderMin, posWanderMax, posWanderMinLock, posWanderMaxLock; int negWander, negWanderMin, negWanderMax, negWanderMinLock, negWanderMaxLock; int cosAngle, sinAngle; DmtxFollow followPos, followNeg; DmtxPixelLoc loc0, posMax, negMax; /* line->stepBeg is already known to sit on the best Hough line */ followPos = followNeg = FollowSeek(dec, reg, line->stepBeg); loc0 = followPos.loc; cosAngle = rHvX[line->angle]; sinAngle = rHvY[line->angle]; distSqMax = 0; posMax = negMax = followPos.loc; posTravel = negTravel = 0; posWander = posWanderMin = posWanderMax = posWanderMinLock = posWanderMaxLock = 0; negWander = negWanderMin = negWanderMax = negWanderMinLock = negWanderMaxLock = 0; for(i = 0; i < reg->stepsTotal/2; i++) { posRunning = (int)(i < 10 || abs(posWander) < abs(posTravel)); negRunning = (int)(i < 10 || abs(negWander) < abs(negTravel)); if(posRunning != 0) { xDiff = followPos.loc.X - loc0.X; yDiff = followPos.loc.Y - loc0.Y; posTravel = (cosAngle * xDiff) + (sinAngle * yDiff); posWander = (cosAngle * yDiff) - (sinAngle * xDiff); if(posWander >= -3*256 && posWander <= 3*256) { distSq = DistanceSquared(followPos.loc, negMax); if(distSq > distSqMax) { posMax = followPos.loc; distSqMax = distSq; line->stepPos = followPos.step; line->locPos = followPos.loc; posWanderMinLock = posWanderMin; posWanderMaxLock = posWanderMax; } } else { posWanderMin = min(posWanderMin, posWander); posWanderMax = max(posWanderMax, posWander); } } else if(!negRunning) { break; } if(negRunning != 0) { xDiff = followNeg.loc.X - loc0.X; yDiff = followNeg.loc.Y - loc0.Y; negTravel = (cosAngle * xDiff) + (sinAngle * yDiff); negWander = (cosAngle * yDiff) - (sinAngle * xDiff); if(negWander >= -3*256 && negWander < 3*256) { distSq = DistanceSquared(followNeg.loc, posMax); if(distSq > distSqMax) { negMax = followNeg.loc; distSqMax = distSq; line->stepNeg = followNeg.step; line->locNeg = followNeg.loc; negWanderMinLock = negWanderMin; negWanderMaxLock = negWanderMax; } } else { negWanderMin = min(negWanderMin, negWander); negWanderMax = max(negWanderMax, negWander); } } else if(!posRunning) { break; } /* CALLBACK_POINT_PLOT(followPos.loc, 2, 1, 2); CALLBACK_POINT_PLOT(followNeg.loc, 4, 1, 2); */ followPos = FollowStep(dec, reg, followPos, +1); followNeg = FollowStep(dec, reg, followNeg, -1); } line->devn = max(posWanderMaxLock - posWanderMinLock, negWanderMaxLock - negWanderMinLock)/256; line->distSq = distSqMax; /* CALLBACK_POINT_PLOT(posMax, 2, 1, 1); CALLBACK_POINT_PLOT(negMax, 2, 1, 1); */ return DmtxPass; } /** * * */ static DmtxPassFail MatrixRegionAlignCalibEdge(DmtxDecode *dec, DmtxRegion *reg, int edgeLoc) { int streamDir; int steps; int avoidAngle; int symbolShape; DmtxVector2 pTmp; DmtxPixelLoc loc0, loc1, locOrigin; DmtxBresLine line; DmtxFollow follow; DmtxBestLine bestLine; /* Determine pixel coordinates of origin */ pTmp.X = 0.0; pTmp.Y = 0.0; dmtxMatrix3VMultiplyBy(&pTmp, reg->fit2raw); locOrigin.X = (int)(pTmp.X + 0.5); locOrigin.Y = (int)(pTmp.Y + 0.5); if(dec->sizeIdxExpected == DmtxSymbolSquareAuto || (dec->sizeIdxExpected >= DmtxSymbol10x10 && dec->sizeIdxExpected <= DmtxSymbol144x144)) symbolShape = DmtxSymbolSquareAuto; else if(dec->sizeIdxExpected == DmtxSymbolRectAuto || (dec->sizeIdxExpected >= DmtxSymbol8x18 && dec->sizeIdxExpected <= DmtxSymbol16x48)) symbolShape = DmtxSymbolRectAuto; else symbolShape = DmtxSymbolShapeAuto; /* Determine end locations of test line */ if(edgeLoc == DmtxEdgeTop) { streamDir = reg->polarity * -1; avoidAngle = reg->leftLine.angle; follow = FollowSeekLoc(dec, reg->locT); pTmp.X = 0.8; pTmp.Y = (symbolShape == DmtxSymbolRectAuto) ? 0.2 : 0.6; } else { assert(edgeLoc == DmtxEdgeRight); streamDir = reg->polarity; avoidAngle = reg->bottomLine.angle; follow = FollowSeekLoc(dec, reg->locR); pTmp.X = (symbolShape == DmtxSymbolSquareAuto) ? 0.7 : 0.9; pTmp.Y = 0.8; } dmtxMatrix3VMultiplyBy(&pTmp, reg->fit2raw); loc1.X = (int)(pTmp.X + 0.5); loc1.Y = (int)(pTmp.Y + 0.5); loc0 = follow.loc; line = BresLineInit(loc0, loc1, locOrigin); steps = TrailBlazeGapped(dec, reg, line, streamDir); bestLine = FindBestSolidLine2(dec, loc0, steps, streamDir, avoidAngle); if(bestLine.mag < 5) { ; } if(edgeLoc == DmtxEdgeTop) { reg->topKnown = 1; reg->topAngle = bestLine.angle; reg->topLoc = bestLine.locBeg; } else { reg->rightKnown = 1; reg->rightAngle = bestLine.angle; reg->rightLoc = bestLine.locBeg; } return DmtxPass; } /** * * */ static DmtxBresLine BresLineInit(DmtxPixelLoc loc0, DmtxPixelLoc loc1, DmtxPixelLoc locInside) { int cp; DmtxBresLine line; DmtxPixelLoc *locBeg, *locEnd; /* XXX Verify that loc0 and loc1 are inbounds */ /* Values that stay the same after initialization */ line.loc0 = loc0; line.loc1 = loc1; line.xStep = (loc0.X < loc1.X) ? +1 : -1; line.yStep = (loc0.Y < loc1.Y) ? +1 : -1; line.xDelta = abs(loc1.X - loc0.X); line.yDelta = abs(loc1.Y - loc0.Y); line.steep = (int)(line.yDelta > line.xDelta); /* Take cross product to determine outward step */ if(line.steep != 0) { /* Point first vector up to get correct sign */ if(loc0.Y < loc1.Y) { locBeg = &loc0; locEnd = &loc1; } else { locBeg = &loc1; locEnd = &loc0; } cp = (((locEnd->X - locBeg->X) * (locInside.Y - locEnd->Y)) - ((locEnd->Y - locBeg->Y) * (locInside.X - locEnd->X))); line.xOut = (cp > 0) ? +1 : -1; line.yOut = 0; } else { /* Point first vector left to get correct sign */ if(loc0.X > loc1.X) { locBeg = &loc0; locEnd = &loc1; } else { locBeg = &loc1; locEnd = &loc0; } cp = (((locEnd->X - locBeg->X) * (locInside.Y - locEnd->Y)) - ((locEnd->Y - locBeg->Y) * (locInside.X - locEnd->X))); line.xOut = 0; line.yOut = (cp > 0) ? +1 : -1; } /* Values that change while stepping through line */ line.loc = loc0; line.travel = 0; line.outward = 0; line.error = (line.steep) ? line.yDelta/2 : line.xDelta/2; /* CALLBACK_POINT_PLOT(loc0, 3, 1, 1); CALLBACK_POINT_PLOT(loc1, 3, 1, 1); */ return line; } /** * * */ static DmtxPassFail BresLineGetStep(DmtxBresLine line, DmtxPixelLoc target, int *travel, int *outward) { /* Determine necessary step along and outward from Bresenham line */ if(line.steep != 0) { *travel = (line.yStep > 0) ? target.Y - line.loc.Y : line.loc.Y - target.Y; BresLineStep(&line, *travel, 0); *outward = (line.xOut > 0) ? target.X - line.loc.X : line.loc.X - target.X; assert(line.yOut == 0); } else { *travel = (line.xStep > 0) ? target.X - line.loc.X : line.loc.X - target.X; BresLineStep(&line, *travel, 0); *outward = (line.yOut > 0) ? target.Y - line.loc.Y : line.loc.Y - target.Y; assert(line.xOut == 0); } return DmtxPass; } /** * * */ static DmtxPassFail BresLineStep(DmtxBresLine *line, int travel, int outward) { int i; DmtxBresLine lineNew; lineNew = *line; assert(abs(travel) < 2); assert(abs(outward) >= 0); /* Perform forward step */ if(travel > 0) { lineNew.travel++; if(lineNew.steep != 0) { lineNew.loc.Y += lineNew.yStep; lineNew.error -= lineNew.xDelta; if(lineNew.error < 0) { lineNew.loc.X += lineNew.xStep; lineNew.error += lineNew.yDelta; } } else { lineNew.loc.X += lineNew.xStep; lineNew.error -= lineNew.yDelta; if(lineNew.error < 0) { lineNew.loc.Y += lineNew.yStep; lineNew.error += lineNew.xDelta; } } } else if(travel < 0) { lineNew.travel--; if(lineNew.steep != 0) { lineNew.loc.Y -= lineNew.yStep; lineNew.error += lineNew.xDelta; if(lineNew.error >= lineNew.yDelta) { lineNew.loc.X -= lineNew.xStep; lineNew.error -= lineNew.yDelta; } } else { lineNew.loc.X -= lineNew.xStep; lineNew.error += lineNew.yDelta; if(lineNew.error >= lineNew.xDelta) { lineNew.loc.Y -= lineNew.yStep; lineNew.error -= lineNew.xDelta; } } } for(i = 0; i < outward; i++) { /* Outward steps */ lineNew.outward++; lineNew.loc.X += lineNew.xOut; lineNew.loc.Y += lineNew.yOut; } *line = lineNew; return DmtxPass; } /** * * */ #ifdef NOTDEFINED static void WriteDiagnosticImage(DmtxDecode *dec, DmtxRegion *reg, char *imagePath) { int row, col; int width, height; unsigned char *cache; int rgb[3]; FILE *fp; DmtxVector2 p; DmtxImage *img; assert(reg != NULL); fp = fopen(imagePath, "wb"); if(fp == NULL) { exit(3); } width = dmtxDecodeGetProp(dec, DmtxPropWidth); height = dmtxDecodeGetProp(dec->image, DmtxPropHeight); img = dmtxImageCreate(NULL, width, height, DmtxPack24bppRGB); /* Populate image */ for(row = 0; row < height; row++) { for(col = 0; col < width; col++) { cache = dmtxDecodeGetCache(dec, col, row); if(cache == NULL) { rgb[0] = 0; rgb[1] = 0; rgb[2] = 128; } else { dmtxDecodeGetPixelValue(dec, col, row, 0, &rgb[0]); dmtxDecodeGetPixelValue(dec, col, row, 1, &rgb[1]); dmtxDecodeGetPixelValue(dec, col, row, 2, &rgb[2]); p.X = col; p.Y = row; dmtxMatrix3VMultiplyBy(&p, reg->raw2fit); if(p.X < 0.0 || p.X > 1.0 || p.Y < 0.0 || p.Y > 1.0) { rgb[0] = 0; rgb[1] = 0; rgb[2] = 128; } else if(p.X + p.Y > 1.0) { rgb[0] += (0.4 * (255 - rgb[0])); rgb[1] += (0.4 * (255 - rgb[1])); rgb[2] += (0.4 * (255 - rgb[2])); } } dmtxImageSetRgb(img, col, row, rgb); } } /* Write additional markers */ rgb[0] = 255; rgb[1] = 0; rgb[2] = 0; dmtxImageSetRgb(img, reg->topLoc.X, reg->topLoc.Y, rgb); dmtxImageSetRgb(img, reg->rightLoc.X, reg->rightLoc.Y, rgb); /* Write image to PNM file */ fprintf(fp, "P6\n%d %d\n255\n", width, height); for(row = height - 1; row >= 0; row--) { for(col = 0; col < width; col++) { dmtxImageGetRgb(img, col, row, rgb); fwrite(rgb, sizeof(char), 3, fp); } } dmtxImageDestroy(&img); fclose(fp); } #endif libdmtx-0.7.7/dmtxscangrid.c000066400000000000000000000110061423156660700160330ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxscangrid.c * \brief Scan grid tracking */ /** * \brief Initialize scan grid pattern * \param dec * \return Initialized grid */ static DmtxScanGrid InitScanGrid(DmtxDecode *dec) { int scale, smallestFeature; int xExtent, yExtent, maxExtent; int extent; DmtxScanGrid grid; memset(&grid, 0x00, sizeof(DmtxScanGrid)); scale = dmtxDecodeGetProp(dec, DmtxPropScale); smallestFeature = dmtxDecodeGetProp(dec, DmtxPropScanGap) / scale; grid.xMin = dmtxDecodeGetProp(dec, DmtxPropXmin); grid.xMax = dmtxDecodeGetProp(dec, DmtxPropXmax); grid.yMin = dmtxDecodeGetProp(dec, DmtxPropYmin); grid.yMax = dmtxDecodeGetProp(dec, DmtxPropYmax); /* Values that get set once */ xExtent = grid.xMax - grid.xMin; yExtent = grid.yMax - grid.yMin; maxExtent = (xExtent > yExtent) ? xExtent : yExtent; assert(maxExtent > 1); for(extent = 1; extent < maxExtent; extent = ((extent + 1) * 2) - 1) if(extent <= smallestFeature) grid.minExtent = extent; grid.maxExtent = extent; grid.xOffset = (grid.xMin + grid.xMax - grid.maxExtent) / 2; grid.yOffset = (grid.yMin + grid.yMax - grid.maxExtent) / 2; /* Values that get reset for every level */ grid.total = 1; grid.extent = grid.maxExtent; SetDerivedFields(&grid); return grid; } /** * \brief Return the next good location (which may be the current location), * and advance grid progress one position beyond that. If no good * locations remain then return DmtxRangeEnd. * \param grid * \return void */ static int PopGridLocation(DmtxScanGrid *grid, DmtxPixelLoc *locPtr) { int locStatus; do { locStatus = GetGridCoordinates(grid, locPtr); /* Always leave grid pointing at next available location */ grid->pixelCount++; } while(locStatus == DmtxRangeBad); return locStatus; } /** * \brief Extract current grid position in pixel coordinates and return * whether location is good, bad, or end * \param grid * \return Pixel location */ static int GetGridCoordinates(DmtxScanGrid *grid, DmtxPixelLoc *locPtr) { int count, half, quarter; DmtxPixelLoc loc; /* Initially pixelCount may fall beyond acceptable limits. Update grid * state before testing coordinates */ /* Jump to next cross pattern horizontally if current column is done */ if(grid->pixelCount >= grid->pixelTotal) { grid->pixelCount = 0; grid->xCenter += grid->jumpSize; } /* Jump to next cross pattern vertically if current row is done */ if(grid->xCenter > grid->maxExtent) { grid->xCenter = grid->startPos; grid->yCenter += grid->jumpSize; } /* Increment level when vertical step goes too far */ if(grid->yCenter > grid->maxExtent) { grid->total *= 4; grid->extent /= 2; SetDerivedFields(grid); } if(grid->extent == 0 || grid->extent < grid->minExtent) { locPtr->X = locPtr->Y = -1; return DmtxRangeEnd; } count = grid->pixelCount; assert(count < grid->pixelTotal); if(count == grid->pixelTotal - 1) { /* center pixel */ loc.X = grid->xCenter; loc.Y = grid->yCenter; } else { half = grid->pixelTotal / 2; quarter = half / 2; /* horizontal portion */ if(count < half) { loc.X = grid->xCenter + ((count < quarter) ? (count - quarter) : (half - count)); loc.Y = grid->yCenter; } /* vertical portion */ else { count -= half; loc.X = grid->xCenter; loc.Y = grid->yCenter + ((count < quarter) ? (count - quarter) : (half - count)); } } loc.X += grid->xOffset; loc.Y += grid->yOffset; *locPtr = loc; if(loc.X < grid->xMin || loc.X > grid->xMax || loc.Y < grid->yMin || loc.Y > grid->yMax) return DmtxRangeBad; return DmtxRangeGood; } /** * \brief Update derived fields based on current state * \param grid * \return void */ static void SetDerivedFields(DmtxScanGrid *grid) { grid->jumpSize = grid->extent + 1; grid->pixelTotal = 2 * grid->extent - 1; grid->startPos = grid->extent / 2; grid->pixelCount = 0; grid->xCenter = grid->yCenter = grid->startPos; } libdmtx-0.7.7/dmtxstatic.h000066400000000000000000000407131423156660700155440ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxstatic.h * \brief Static header */ #ifndef __DMTXSTATIC_H__ #define __DMTXSTATIC_H__ #define DmtxAlmostZero 0.000001 #define DmtxAlmostInfinity -1 #define DmtxValueC40Latch 230 #define DmtxValueTextLatch 239 #define DmtxValueX12Latch 238 #define DmtxValueEdifactLatch 240 #define DmtxValueBase256Latch 231 #define DmtxValueCTXUnlatch 254 #define DmtxValueEdifactUnlatch 31 #define DmtxValueAsciiPad 129 #define DmtxValueAsciiUpperShift 235 #define DmtxValueCTXShift1 0 #define DmtxValueCTXShift2 1 #define DmtxValueCTXShift3 2 #define DmtxValueFNC1 232 #define DmtxValueStructuredAppend 233 #define DmtxValueReaderProgramming 234 #define DmtxValue05Macro 236 #define DmtxValue06Macro 237 #define DmtxValueECI 241 #define DmtxC40TextBasicSet 0 #define DmtxC40TextShift1 1 #define DmtxC40TextShift2 2 #define DmtxC40TextShift3 3 #define DmtxUnlatchExplicit 0 #define DmtxUnlatchImplicit 1 #define DmtxChannelValid 0x00 #define DmtxChannelUnsupportedChar 0x01 << 0 #define DmtxChannelCannotUnlatch 0x01 << 1 #undef min #define min(X,Y) (((X) < (Y)) ? (X) : (Y)) #undef max #define max(X,Y) (((X) > (Y)) ? (X) : (Y)) typedef enum { DmtxEncodeNormal, /* Use normal scheme behavior (e.g., ASCII auto) */ DmtxEncodeCompact, /* Use only compact format within scheme */ DmtxEncodeFull /* Use only fully expanded format within scheme */ } DmtxEncodeOption; typedef enum { DmtxRangeGood, DmtxRangeBad, DmtxRangeEnd } DmtxRange; typedef enum { DmtxEdgeTop = 0x01 << 0, DmtxEdgeBottom = 0x01 << 1, DmtxEdgeLeft = 0x01 << 2, DmtxEdgeRight = 0x01 << 3 } DmtxEdge; typedef enum { DmtxMaskBit8 = 0x01 << 0, DmtxMaskBit7 = 0x01 << 1, DmtxMaskBit6 = 0x01 << 2, DmtxMaskBit5 = 0x01 << 3, DmtxMaskBit4 = 0x01 << 4, DmtxMaskBit3 = 0x01 << 5, DmtxMaskBit2 = 0x01 << 6, DmtxMaskBit1 = 0x01 << 7 } DmtxMaskBit; /** * @struct DmtxFollow * @brief DmtxFollow */ typedef struct DmtxFollow_struct { unsigned char *ptr; unsigned char neighbor; int step; DmtxPixelLoc loc; } DmtxFollow; /** * @struct DmtxBresLine * @brief DmtxBresLine */ typedef struct DmtxBresLine_struct { int xStep; int yStep; int xDelta; int yDelta; int steep; int xOut; int yOut; int travel; int outward; int error; DmtxPixelLoc loc; DmtxPixelLoc loc0; DmtxPixelLoc loc1; } DmtxBresLine; typedef struct C40TextState_struct { int shift; DmtxBoolean upperShift; } C40TextState; /* dmtxregion.c */ static double RightAngleTrueness(DmtxVector2 c0, DmtxVector2 c1, DmtxVector2 c2, double angle); static DmtxPointFlow MatrixRegionSeekEdge(DmtxDecode *dec, DmtxPixelLoc loc0); static DmtxPassFail MatrixRegionOrientation(DmtxDecode *dec, DmtxRegion *reg, DmtxPointFlow flowBegin); static long DistanceSquared(DmtxPixelLoc a, DmtxPixelLoc b); static int ReadModuleColor(DmtxDecode *dec, DmtxRegion *reg, int symbolRow, int symbolCol, int sizeIdx, int colorPlane); static DmtxPassFail MatrixRegionFindSize(DmtxDecode *dec, DmtxRegion *reg); static int CountJumpTally(DmtxDecode *dec, DmtxRegion *reg, int xStart, int yStart, DmtxDirection dir); static DmtxPointFlow GetPointFlow(DmtxDecode *dec, int colorPlane, DmtxPixelLoc loc, int arrive); static DmtxPointFlow FindStrongestNeighbor(DmtxDecode *dec, DmtxPointFlow center, int sign); static DmtxFollow FollowSeek(DmtxDecode *dec, DmtxRegion *reg, int seek); static DmtxFollow FollowSeekLoc(DmtxDecode *dec, DmtxPixelLoc loc); static DmtxFollow FollowStep(DmtxDecode *dec, DmtxRegion *reg, DmtxFollow followBeg, int sign); static DmtxFollow FollowStep2(DmtxDecode *dec, DmtxFollow followBeg, int sign); static DmtxPassFail TrailBlazeContinuous(DmtxDecode *dec, DmtxRegion *reg, DmtxPointFlow flowBegin, int maxDiagonal); static int TrailBlazeGapped(DmtxDecode *dec, DmtxRegion *reg, DmtxBresLine line, int streamDir); static int TrailClear(DmtxDecode *dec, DmtxRegion *reg, int clearMask); static DmtxBestLine FindBestSolidLine(DmtxDecode *dec, DmtxRegion *reg, int step0, int step1, int streamDir, int houghAvoid); static DmtxBestLine FindBestSolidLine2(DmtxDecode *dec, DmtxPixelLoc loc0, int tripSteps, int sign, int houghAvoid); static DmtxPassFail FindTravelLimits(DmtxDecode *dec, DmtxRegion *reg, DmtxBestLine *line); static DmtxPassFail MatrixRegionAlignCalibEdge(DmtxDecode *dec, DmtxRegion *reg, int whichEdge); static DmtxBresLine BresLineInit(DmtxPixelLoc loc0, DmtxPixelLoc loc1, DmtxPixelLoc locInside); static DmtxPassFail BresLineGetStep(DmtxBresLine line, DmtxPixelLoc target, int *travel, int *outward); static DmtxPassFail BresLineStep(DmtxBresLine *line, int travel, int outward); /*static void WriteDiagnosticImage(DmtxDecode *dec, DmtxRegion *reg, char *imagePath);*/ /* dmtxdecode.c */ static void TallyModuleJumps(DmtxDecode *dec, DmtxRegion *reg, int tally[][24], int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir); static DmtxPassFail PopulateArrayFromMatrix(DmtxDecode *dec, DmtxRegion *reg, DmtxMessage *msg); /* dmtxdecodescheme.c */ static DmtxPassFail DecodeDataStream(DmtxMessage *msg, int sizeIdx, unsigned char *outputStart); static int GetEncodationScheme(unsigned char cw); static void PushOutputWord(DmtxMessage *msg, int value); static void PushOutputC40TextWord(DmtxMessage *msg, C40TextState *state, int value); static void PushOutputMacroHeader(DmtxMessage *msg, int macroType); static void PushOutputMacroTrailer(DmtxMessage *msg); static unsigned char *DecodeSchemeAscii(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd); static unsigned char *DecodeSchemeC40Text(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd, DmtxScheme encScheme); static unsigned char *DecodeSchemeX12(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd); static unsigned char *DecodeSchemeEdifact(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd); static unsigned char *DecodeSchemeBase256(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd); /* dmtxencode.c */ static void PrintPattern(DmtxEncode *encode); static int EncodeDataCodewords(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1); /* dmtxplacemod.c */ static int ModulePlacementEcc200(unsigned char *modules, unsigned char *codewords, int sizeIdx, int moduleOnColor); static void PatternShapeStandard(unsigned char *modules, int mappingRows, int mappingCols, int row, int col, unsigned char *codeword, int moduleOnColor); static void PatternShapeSpecial1(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor); static void PatternShapeSpecial2(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor); static void PatternShapeSpecial3(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor); static void PatternShapeSpecial4(unsigned char *modules, int mappingRows, int mappingCols, unsigned char *codeword, int moduleOnColor); static void PlaceModule(unsigned char *modules, int mappingRows, int mappingCols, int row, int col, unsigned char *codeword, int mask, int moduleOnColor); /* dmtxreedsol.c */ static DmtxPassFail RsEncode(DmtxMessage *message, int sizeIdx); static DmtxPassFail RsDecode(unsigned char *code, int sizeIdx, int fix); static DmtxPassFail RsGenPoly(DmtxByteList *gen, int errorWordCount); static DmtxBoolean RsComputeSyndromes(DmtxByteList *syn, const DmtxByteList *rec, int blockErrorWords); static DmtxBoolean RsFindErrorLocatorPoly(DmtxByteList *elp, const DmtxByteList *syn, int errorWordCount, int maxCorrectable); static DmtxBoolean RsFindErrorLocations(DmtxByteList *loc, const DmtxByteList *elp); static DmtxPassFail RsRepairErrors(DmtxByteList *rec, const DmtxByteList *loc, const DmtxByteList *elp, const DmtxByteList *syn); /* dmtxscangrid.c */ static DmtxScanGrid InitScanGrid(DmtxDecode *dec); static int PopGridLocation(DmtxScanGrid *grid, /*@out@*/ DmtxPixelLoc *locPtr); static int GetGridCoordinates(DmtxScanGrid *grid, /*@out@*/ DmtxPixelLoc *locPtr); static void SetDerivedFields(DmtxScanGrid *grid); /* dmtxsymbol.c */ static int FindSymbolSize(int dataWords, int sizeIdxRequest); /* dmtximage.c */ static int GetBitsPerPixel(int pack); /* dmtxencodestream.c */ static DmtxEncodeStream StreamInit(DmtxByteList *input, DmtxByteList *output); static void StreamCopy(DmtxEncodeStream *dst, DmtxEncodeStream *src); static void StreamMarkComplete(DmtxEncodeStream *stream, int sizeIdx); static void StreamMarkInvalid(DmtxEncodeStream *stream, int reasonIdx); static void StreamMarkFatal(DmtxEncodeStream *stream, int reasonIdx); static void StreamOutputChainAppend(DmtxEncodeStream *stream, DmtxByte value); static DmtxByte StreamOutputChainRemoveLast(DmtxEncodeStream *stream); static void StreamOutputSet(DmtxEncodeStream *stream, int index, DmtxByte value); static DmtxBoolean StreamInputHasNext(DmtxEncodeStream *stream); static DmtxByte StreamInputPeekNext(DmtxEncodeStream *stream); static DmtxByte StreamInputAdvanceNext(DmtxEncodeStream *stream); static void StreamInputAdvancePrev(DmtxEncodeStream *stream); /* dmtxencodescheme.c */ static int EncodeSingleScheme(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1); static void EncodeNextChunk(DmtxEncodeStream *stream, int scheme, int subScheme, int sizeIdxRequest); static void EncodeChangeScheme(DmtxEncodeStream *stream, DmtxScheme targetScheme, int unlatchType); static int GetRemainingSymbolCapacity(int outputLength, int sizeIdx); /* dmtxencodeoptimize.c */ static int EncodeOptimizeBest(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, int fnc1); static void StreamAdvanceFromBest(DmtxEncodeStream *streamNext, DmtxEncodeStream *streamList, int targeteState, int sizeIdxRequest); static void AdvanceAsciiCompact(DmtxEncodeStream *streamNext, DmtxEncodeStream *streamList, int state, int inputNext, int sizeIdxRequest); static void AdvanceCTX(DmtxEncodeStream *streamNext, DmtxEncodeStream *streamList, int state, int inputNext, int ctxValueCount, int sizeIdxRequest); static void AdvanceEdifact(DmtxEncodeStream *streamNext, DmtxEncodeStream *streamList, int state, int inputNext, int sizeIdxRequest); static int GetScheme(int state); static DmtxBoolean ValidStateSwitch(int fromState, int targetState); /* dmtxencodeascii.c */ static void EncodeNextChunkAscii(DmtxEncodeStream *stream, int option); static void AppendValueAscii(DmtxEncodeStream *stream, DmtxByte value); static void CompleteIfDoneAscii(DmtxEncodeStream *stream, int sizeIdxRequest); static void PadRemainingInAscii(DmtxEncodeStream *stream, int sizeIdx); static DmtxByteList EncodeTmpRemainingInAscii(DmtxEncodeStream *stream, DmtxByte *storage, int capacity, DmtxPassFail *passFail); static DmtxByte Randomize253State(DmtxByte cwValue, int cwPosition); /* dmtxencodec40textx12.c */ static void EncodeNextChunkCTX(DmtxEncodeStream *stream, int sizeIdxRequest); static void AppendValuesCTX(DmtxEncodeStream *stream, DmtxByteList *valueList); static void AppendUnlatchCTX(DmtxEncodeStream *stream); static void CompleteIfDoneCTX(DmtxEncodeStream *stream, int sizeIdxRequest); static void CompletePartialC40Text(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest); static void CompletePartialX12(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest); static DmtxBoolean PartialX12ChunkRemains(DmtxEncodeStream *stream); static void PushCTXValues(DmtxByteList *valueList, DmtxByte inputValue, int targetScheme, DmtxPassFail *passFail, int fnc1); static DmtxBoolean IsCTX(int scheme); static void ShiftValueListBy3(DmtxByteList *list, DmtxPassFail *passFail); /* dmtxencodeedifact.c */ static void EncodeNextChunkEdifact(DmtxEncodeStream *stream); static void AppendValueEdifact(DmtxEncodeStream *stream, DmtxByte value); static void CompleteIfDoneEdifact(DmtxEncodeStream *stream, int sizeIdxRequest); /* dmtxencodebase256.c */ static void EncodeNextChunkBase256(DmtxEncodeStream *stream); static void AppendValueBase256(DmtxEncodeStream *stream, DmtxByte value); static void CompleteIfDoneBase256(DmtxEncodeStream *stream, int sizeIdxRequest); static void UpdateBase256ChainHeader(DmtxEncodeStream *stream, int perfectSizeIdx); static void Base256OutputChainInsertFirst(DmtxEncodeStream *stream); static void Base256OutputChainRemoveFirst(DmtxEncodeStream *stream); static DmtxByte Randomize255State(DmtxByte cwValue, int cwPosition); static unsigned char UnRandomize255State(unsigned char value, int idx); static const int dmtxNeighborNone = 8; static const int dmtxPatternX[] = { -1, 0, 1, 1, 1, 0, -1, -1 }; static const int dmtxPatternY[] = { -1, -1, -1, 0, 1, 1, 1, 0 }; static const DmtxPointFlow dmtxBlankEdge = { 0, 0, 0, DmtxUndefined, { -1, -1 } }; /*@ +charint @*/ static int rHvX[] = { 256, 256, 256, 256, 255, 255, 255, 254, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 237, 236, 234, 232, 230, 228, 226, 224, 222, 219, 217, 215, 212, 210, 207, 204, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 171, 168, 165, 161, 158, 154, 150, 147, 143, 139, 136, 132, 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 83, 79, 75, 71, 66, 62, 58, 53, 49, 44, 40, 36, 31, 27, 22, 18, 13, 9, 4, 0, -4, -9, -13, -18, -22, -27, -31, -36, -40, -44, -49, -53, -58, -62, -66, -71, -75, -79, -83, -88, -92, -96, -100, -104, -108, -112, -116, -120, -124, -128, -132, -136, -139, -143, -147, -150, -154, -158, -161, -165, -168, -171, -175, -178, -181, -184, -187, -190, -193, -196, -199, -202, -204, -207, -210, -212, -215, -217, -219, -222, -224, -226, -228, -230, -232, -234, -236, -237, -239, -241, -242, -243, -245, -246, -247, -248, -249, -250, -251, -252, -253, -254, -254, -255, -255, -255, -256, -256, -256 }; static int rHvY[] = { 0, 4, 9, 13, 18, 22, 27, 31, 36, 40, 44, 49, 53, 58, 62, 66, 71, 75, 79, 83, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 139, 143, 147, 150, 154, 158, 161, 165, 168, 171, 175, 178, 181, 184, 187, 190, 193, 196, 199, 202, 204, 207, 210, 212, 215, 217, 219, 222, 224, 226, 228, 230, 232, 234, 236, 237, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 254, 255, 255, 255, 256, 256, 256, 256, 256, 256, 256, 255, 255, 255, 254, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 237, 236, 234, 232, 230, 228, 226, 224, 222, 219, 217, 215, 212, 210, 207, 204, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 171, 168, 165, 161, 158, 154, 150, 147, 143, 139, 136, 132, 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 83, 79, 75, 71, 66, 62, 58, 53, 49, 44, 40, 36, 31, 27, 22, 18, 13, 9, 4 }; /*@ -charint @*/ enum DmtxErrorMessage { DmtxErrorUnknown, DmtxErrorUnsupportedCharacter, DmtxErrorNotOnByteBoundary, DmtxErrorIllegalParameterValue, DmtxErrorEmptyList, DmtxErrorOutOfBounds, DmtxErrorMessageTooLarge, DmtxErrorCantCompactNonDigits, DmtxErrorUnexpectedScheme, DmtxErrorIncompleteValueList }; static char *dmtxErrorMessage[] = { "Unknown error", "Unsupported character", "Not on byte boundary", "Illegal parameter value", "Encountered empty list", "Out of bounds", "Message too large", "Can't compact non-digits", "Encountered unexpected scheme", "Encountered incomplete value list" }; #endif libdmtx-0.7.7/dmtxsymbol.c000066400000000000000000000173151423156660700155570ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxsymbol.c * \brief Data Matrix symbol attributes */ /** * \brief Retrieve symbol index from rows and columns * \param rows * \param cols * \return sizeIdx value */ extern int getSizeIdxFromSymbolDimension(int rows, int cols) { int symbolRows, symbolCols, i; for (i=0; i<30; i++){ symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, i); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, i); if (rows==symbolRows && cols==symbolCols){ return i; } } return -1; } /** * \brief Retrieve property based on symbol size * \param attribute * \param sizeIdx * \return Attribute value */ extern int dmtxGetSymbolAttribute(int attribute, int sizeIdx) { static const int symbolRows[] = { 10, 12, 14, 16, 18, 20, 22, 24, 26, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144, 8, 8, 12, 12, 16, 16 }; static const int symbolCols[] = { 10, 12, 14, 16, 18, 20, 22, 24, 26, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144, 18, 32, 26, 36, 36, 48 }; static const int dataRegionRows[] = { 8, 10, 12, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22, 6, 6, 10, 10, 14, 14 }; static const int dataRegionCols[] = { 8, 10, 12, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22, 16, 14, 24, 16, 16, 22 }; static const int horizDataRegions[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6, 1, 2, 1, 2, 2, 2 }; static const int interleavedBlocks[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 10, 1, 1, 1, 1, 1, 1 }; static const int symbolDataWords[] = { 3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558, 5, 10, 16, 22, 32, 49 }; static const int blockErrorWords[] = { 5, 7, 10, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62, 7, 11, 14, 18, 24, 28 }; static const int blockMaxCorrectable[] = { 2, 3, 5, 6, 7, 9, 10, 12, 14, 18, 21, 24, 28, 34, 21, 28, 18, 24, 28, 34, 28, 34, 31, 31, 3, 5, 7, 9, 12, 14 }; if(sizeIdx < 0 || sizeIdx >= DmtxSymbolSquareCount + DmtxSymbolRectCount) return DmtxUndefined; switch(attribute) { case DmtxSymAttribSymbolRows: return symbolRows[sizeIdx]; case DmtxSymAttribSymbolCols: return symbolCols[sizeIdx]; case DmtxSymAttribDataRegionRows: return dataRegionRows[sizeIdx]; case DmtxSymAttribDataRegionCols: return dataRegionCols[sizeIdx]; case DmtxSymAttribHorizDataRegions: return horizDataRegions[sizeIdx]; case DmtxSymAttribVertDataRegions: return (sizeIdx < DmtxSymbolSquareCount) ? horizDataRegions[sizeIdx] : 1; case DmtxSymAttribMappingMatrixRows: return dataRegionRows[sizeIdx] * dmtxGetSymbolAttribute(DmtxSymAttribVertDataRegions, sizeIdx); case DmtxSymAttribMappingMatrixCols: return dataRegionCols[sizeIdx] * horizDataRegions[sizeIdx]; case DmtxSymAttribInterleavedBlocks: return interleavedBlocks[sizeIdx]; case DmtxSymAttribBlockErrorWords: return blockErrorWords[sizeIdx]; case DmtxSymAttribBlockMaxCorrectable: return blockMaxCorrectable[sizeIdx]; case DmtxSymAttribSymbolDataWords: return symbolDataWords[sizeIdx]; case DmtxSymAttribSymbolErrorWords: return blockErrorWords[sizeIdx] * interleavedBlocks[sizeIdx]; case DmtxSymAttribSymbolMaxCorrectable: return blockMaxCorrectable[sizeIdx] * interleavedBlocks[sizeIdx]; } return DmtxUndefined; } /** * \brief Retrieve data size for a specific symbol size and block number * \param sizeIdx * \param blockIdx * \return Attribute value */ extern int dmtxGetBlockDataSize(int sizeIdx, int blockIdx) { int symbolDataWords; int interleavedBlocks; int count; symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx); interleavedBlocks = dmtxGetSymbolAttribute(DmtxSymAttribInterleavedBlocks, sizeIdx); if(symbolDataWords < 1 || interleavedBlocks < 1) return DmtxUndefined; count = (int)(symbolDataWords/interleavedBlocks); return (sizeIdx == DmtxSymbol144x144 && blockIdx < 8) ? count + 1 : count; } /** * \brief Determine symbol size based on data size and requested properties * \param dataWords * \param sizeIdxRequest * \return Symbol size index (or DmtxUndefined if none) */ static int FindSymbolSize(int dataWords, int sizeIdxRequest) { int sizeIdx; int idxBeg, idxEnd; if(dataWords <= 0) return DmtxUndefined; if(sizeIdxRequest == DmtxSymbolSquareAuto || sizeIdxRequest == DmtxSymbolRectAuto) { if(sizeIdxRequest == DmtxSymbolSquareAuto) { idxBeg = 0; idxEnd = DmtxSymbolSquareCount; } else { idxBeg = DmtxSymbolSquareCount; idxEnd = DmtxSymbolSquareCount + DmtxSymbolRectCount; } for(sizeIdx = idxBeg; sizeIdx < idxEnd; sizeIdx++) { if(dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx) >= dataWords) break; } if(sizeIdx == idxEnd) return DmtxUndefined; } else { sizeIdx = sizeIdxRequest; } if(dataWords > dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, sizeIdx)) return DmtxUndefined; return sizeIdx; } libdmtx-0.7.7/dmtxtime.c000066400000000000000000000051311423156660700152010ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxtime.c * \brief Time handling */ #define DMTX_USEC_PER_SEC 1000000 #if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY) #include #include #define DMTX_TIME_PREC_USEC 1 /** * \brief GETTIMEOFDAY version * \return Time now */ extern DmtxTime dmtxTimeNow(void) { DmtxPassFail err; struct timeval tv; DmtxTime tNow; err = gettimeofday(&tv, NULL); if(err != 0) ; /* XXX handle error better here */ tNow.sec = tv.tv_sec; tNow.usec = tv.tv_usec; return tNow; } #elif defined(_MSC_VER) #include #define DMTX_TIME_PREC_USEC 1 /** * \brief MICROSOFT VC++ version * \return Time now */ extern DmtxTime dmtxTimeNow(void) { FILETIME ft; unsigned __int64 tm; DmtxTime tNow; GetSystemTimeAsFileTime(&ft); tm = ft.dwHighDateTime; tm <<= 32; tm |= ft.dwLowDateTime; tm /= 10; tNow.sec = tm / 1000000UL; tNow.usec = tm % 1000000UL; return tNow; } #else #include #define DMTX_TIME_PREC_USEC 1000000 /** * \brief Generic 1 second resolution version * \return Time now */ extern DmtxTime dmtxTimeNow(void) { time_t s; DmtxTime tNow; s = time(NULL); if(errno != 0) ; /* XXX handle error better here */ tNow.sec = s; tNow.usec = 0; return tNow; } #endif /** * \brief Add milliseconds to time t * \param t * \param msec * \return Adjusted time */ extern DmtxTime dmtxTimeAdd(DmtxTime t, long msec) { int usec; usec = msec * 1000; /* Ensure that time difference will register on local system */ if(usec > 0 && usec < DMTX_TIME_PREC_USEC) usec = DMTX_TIME_PREC_USEC; /* Add time */ t.sec += usec/DMTX_USEC_PER_SEC; t.usec += usec%DMTX_USEC_PER_SEC; /* Roll extra usecs into secs */ while(t.usec >= DMTX_USEC_PER_SEC) { t.sec++; t.usec -= DMTX_USEC_PER_SEC; } return t; } /** * \brief Determine whether the received timeout has been exceeded * \param timeout * \return 1 (true) | 0 (false) */ extern int dmtxTimeExceeded(DmtxTime timeout) { DmtxTime now; now = dmtxTimeNow(); return (now.sec > timeout.sec || (now.sec == timeout.sec && now.usec > timeout.usec)); } #undef DMTX_TIME_PREC_USEC #undef DMTX_USEC_PER_SEC libdmtx-0.7.7/dmtxvector2.c000066400000000000000000000065621423156660700156400ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file dmtxvector2.c * \brief 2D Vector math */ /** * * */ extern DmtxVector2 * dmtxVector2AddTo(DmtxVector2 *v1, const DmtxVector2 *v2) { v1->X += v2->X; v1->Y += v2->Y; return v1; } /** * * */ extern DmtxVector2 * dmtxVector2Add(DmtxVector2 *vOut, const DmtxVector2 *v1, const DmtxVector2 *v2) { *vOut = *v1; return dmtxVector2AddTo(vOut, v2); } /** * * */ extern DmtxVector2 * dmtxVector2SubFrom(DmtxVector2 *v1, const DmtxVector2 *v2) { v1->X -= v2->X; v1->Y -= v2->Y; return v1; } /** * * */ extern DmtxVector2 * dmtxVector2Sub(DmtxVector2 *vOut, const DmtxVector2 *v1, const DmtxVector2 *v2) { *vOut = *v1; return dmtxVector2SubFrom(vOut, v2); } /** * * */ extern DmtxVector2 * dmtxVector2ScaleBy(DmtxVector2 *v, double s) { v->X *= s; v->Y *= s; return v; } /** * * */ extern DmtxVector2 * dmtxVector2Scale(DmtxVector2 *vOut, const DmtxVector2 *v, double s) { *vOut = *v; return dmtxVector2ScaleBy(vOut, s); } /** * * */ extern double dmtxVector2Cross(const DmtxVector2 *v1, const DmtxVector2 *v2) { return (v1->X * v2->Y) - (v1->Y * v2->X); } /** * * */ extern double dmtxVector2Norm(DmtxVector2 *v) { double mag; mag = dmtxVector2Mag(v); if(mag <= DmtxAlmostZero) return -1.0; /* XXX this doesn't look clean */ dmtxVector2ScaleBy(v, 1/mag); return mag; } /** * * */ extern double dmtxVector2Dot(const DmtxVector2 *v1, const DmtxVector2 *v2) { return (v1->X * v2->X) + (v1->Y * v2->Y); } /** * * */ extern double dmtxVector2Mag(const DmtxVector2 *v) { return sqrt(v->X * v->X + v->Y * v->Y); } /** * * */ extern double dmtxDistanceFromRay2(const DmtxRay2 *r, const DmtxVector2 *q) { DmtxVector2 vSubTmp; /* Assumes that v is a unit vector */ assert(fabs(1.0 - dmtxVector2Mag(&(r->v))) <= DmtxAlmostZero); return dmtxVector2Cross(&(r->v), dmtxVector2Sub(&vSubTmp, q, &(r->p))); } /** * * */ extern double dmtxDistanceAlongRay2(const DmtxRay2 *r, const DmtxVector2 *q) { DmtxVector2 vSubTmp; #ifdef DEBUG /* Assumes that v is a unit vector */ if(fabs(1.0 - dmtxVector2Mag(&(r->v))) > DmtxAlmostZero) { ; /* XXX big error goes here */ } #endif return dmtxVector2Dot(dmtxVector2Sub(&vSubTmp, q, &(r->p)), &(r->v)); } /** * * */ extern DmtxPassFail dmtxRay2Intersect(DmtxVector2 *point, const DmtxRay2 *p0, const DmtxRay2 *p1) { double numer, denom; DmtxVector2 w; denom = dmtxVector2Cross(&(p1->v), &(p0->v)); if(fabs(denom) <= DmtxAlmostZero) return DmtxFail; dmtxVector2Sub(&w, &(p1->p), &(p0->p)); numer = dmtxVector2Cross(&(p1->v), &w); return dmtxPointAlongRay2(point, p0, numer/denom); } /** * * */ extern DmtxPassFail dmtxPointAlongRay2(DmtxVector2 *point, const DmtxRay2 *r, double t) { DmtxVector2 vTmp; /* Ray should always have unit length of 1 */ assert(fabs(1.0 - dmtxVector2Mag(&(r->v))) <= DmtxAlmostZero); dmtxVector2Scale(&vTmp, &(r->v), t); dmtxVector2Add(point, &(r->p), &vTmp); return DmtxPass; } libdmtx-0.7.7/libdmtx.pc.in000066400000000000000000000004141423156660700155750ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libdmtx URL: http://www.libdmtx.org/ Description: Library for reading and writing Data Matrix barcodes Version: @PACKAGE_VERSION@ Libs: -L${libdir} -ldmtx Cflags: -I${includedir} libdmtx-0.7.7/man/000077500000000000000000000000001423156660700137555ustar00rootroot00000000000000libdmtx-0.7.7/man/libdmtx.3000077500000000000000000000205241423156660700155120ustar00rootroot00000000000000.\" Man page for the libdmtx project. .\" .\" To view: $ groff -man -T ascii libdmtx.3 | less .\" To text: $ groff -man -T ascii libdmtx.3 | col -b | expand .\" .TH LIBDMTX 3 "June 2, 2011" .SH NAME libdmtx \- Data Matrix Encoding & Decoding Library 0.7.5 .SH SYNOPSIS \fB#include \fP cc file.c -ldmtx .SH DESCRIPTION \fIlibdmtx\fP is a software library that enables programs to read and write Data Matrix barcodes of the modern ECC200 variety. The library runs natively on several platforms, and can be accessed by multiple languages using the libdmtx language wrappers. The utility programs \fIdmtxread\fP and \fIdmtxwrite\fP provide a command line interface for libdmtx, and serve as a good reference for developers writing their own libdmtx-enabled programs. Data Matrix barcodes store data as a pattern of ON and OFF modules (often black on white) in a grid pattern that resembles a checkerboard. Like other 2D symbologies, Data Matrix barcodes have a large data capacity compared to their traditional 1D cousins, and employ sophisticated error correction techniques. Data Matrix barcodes can be square or rectangle in shape, and offer several encodation schemes for optimized storage of text and/or binary data. The Data Matrix symbology was invented and released into the public domain by RVSI Acuity CiMatrix. .SH ENCODING - Generating Data Matrix Barcodes C/C++ programs can generate a barcode with just a few basic calls to libdmtx: 1. Call \fBdmtxEncodeCreate()\fP Creates a new \fBDmtxEncode\fP structure and initializes the encoding process. This function must be called before using the other encoding functions. 2. Call \fBdmtxEncodeSetProp()\fP [optional] Allows you to control specific aspects of the encoding behavior. If this function is not called, libdmtx will use the defaults set by \fBdmtxEncodeCreate()\fP above. The complementary function, \fBdmtxEncodeGetProp()\fP, allows you to detect the current settings. 3. Call either \fBdmtxEncodeDataMatrix()\fP or \fBdmtxEncodeDataMosaic()\fP Call one of these functions to generate an image of the desired barcode type. Your program is responsible for dispatching the resulting output to its destination, whether that means displaying it on a screen, writing an image file, copying it elsewhere, etc... 4. Call \fBdmtxEncodeDestroy()\fP Releases memory allocated during the encoding process. .SH DECODING - Reading Data Matrix Barcodes Barcode reading takes more steps than barcode generation, mainly because libdmtx must find a barcode region before it can decode the message. However, this too is a relatively simple process that uses 4 main structures: \fBDmtxImage\fP holds image properties and a pointer to pixel data held by the calling program. \fBDmtxDecode\fP holds values for controlling decode behavior and tracking scan progress. When scanning a new image, calling programs should always create a new \fBDmtxDecode\fP structure instead of reusing an old one. \fBDmtxRegion\fP defines a 4-sided region in pixel coordinates. Regions may be found in almost any orientation, and their corners won't necessarily form right angles. libdmtx uses this structure to store the location of potential barcodes, which are then returned to the calling program one-at-a-time. \fBDmtxMessage\fP holds the decoded message after being extracted from the barcode region. A successfully decoded region will produce exactly one message. Use the following functions to find and decode Data Matrix barcodes: 1. Call \fBdmtxImageCreate()\fP Creates and initializes a new \fBDmtxImage\fP structure using pixel data provided by the calling application. Parameters include a pointer to the existing pixel array, image width, height, and the pixel packing format. 2. Call \fBdmtxImageSetProp()\fP [optional] Sets image properties to control the pixel mapping logic. These settings allow libdmtx to understand many native in-memory image layouts, thus preventing the extra work of transforming and copying data to a one-size-fits-all format. A \fBdmtxDecodeGetProp()\fP function is also available for detecting the current image properties. 3. Call \fBdmtxDecodeCreate()\fP Creates and initializes a new \fBDmtxDecode\fP struct, which designates the image to be scanned and initializes the scan grid pattern. This function must be called before any other scanning functions. 4. Call \fBdmtxDecodeSetProp()\fP [optional] Sets internal properties to control decoding behavior. This feature allows you to optimize performance and accuracy for specific image conditions. A \fBdmtxDecodeGetProp()\fP function is also available. 5. Call \fBdmtxRegionFindNext()\fP Searches every pixel location in a grid pattern looking for potential barcode regions. A \fBDmtxRegion\fP is returned whenever a potential barcode region is found, or if the final pixel location has been scanned. Subsequent calls to this function will resume the search where the previous call left off. 6. Call either \fBdmtxDecodeMatrixRegion()\fP or \fBdmtxDecodeMosaicRegion()\fP Extracts raw data from the barcode region and decodes the underlying message. 7. Call \fBdmtxMessageDestroy()\fP Releases memory held by a \fBDmtxMessage\fP struct. The complementary function, \fBdmtxMessageCreate()\fP, is automatically called by \fBdmtxDecodeMatrixRegion()\fP and therefore is not normally used by the calling program. 8. Call \fBdmtxRegionDestroy()\fP Releases memory held by a \fBDmtxRegion\fP struct. The complementary function, \fBdmtxRegionCreate()\fP, is automatically called by \fBdmtxRegionFindNext()\fP (actually \fBdmtxRegionScanPixel()\fP) and therefore is not normally used by the calling program. 9. Call \fBdmtxDecodeDestroy()\fP Releases memory held by a \fBDmtxDecode\fP struct. This is the complementary function to \fBdmtxDecodeCreate()\fP. 10. Call \fBdmtxImageDestroy()\fP Releases memory held by a \fBDmtxImage\fP struct, excluding the pixel array passed to \fBdmtxImageCreate()\fP. The calling program is responsible for releasing the pixel array memory, if required. .SH EXAMPLE PROGRAM This example program (available as simple_test.c in the source package) demonstrates \fIlibdmtx\fP functionality in both directions: encoding and decoding. It creates a Data Matrix barcode in memory, reads it back, and prints the decoded message. The final output message should match the original input string. #include #include #include #include #include int main(int argc, char *argv[]) { size_t width, height, bytesPerPixel; unsigned char str[] = "30Q324343430794image, DmtxPropWidth); height = dmtxImageGetProp(enc->image, DmtxPropHeight); bytesPerPixel = dmtxImageGetProp(enc->image, DmtxPropBytesPerPixel); pxl = (unsigned char *)malloc(width * height * bytesPerPixel); assert(pxl != NULL); memcpy(pxl, enc->image->pxl, width * height * bytesPerPixel); dmtxEncodeDestroy(&enc); /* 3) DECODE the Data Matrix barcode from the copied image */ img = dmtxImageCreate(pxl, width, height, DmtxPack24bppRGB); assert(img != NULL); dec = dmtxDecodeCreate(img, 1); assert(dec != NULL); reg = dmtxRegionFindNext(dec, NULL); if(reg != NULL) { msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined); if(msg != NULL) { fputs("output: \\"", stdout); fwrite(msg->output, sizeof(unsigned char), msg->outputIdx, stdout); fputs("\\"\\n", stdout); dmtxMessageDestroy(&msg); } dmtxRegionDestroy(®); } dmtxDecodeDestroy(&dec); dmtxImageDestroy(&img); free(pxl); exit(0); } .SH "SEE ALSO" \fIdmtxread\fP(1), \fIdmtxwrite\fP(1), \fIdmtxquery\fP(1) .SH STANDARDS ISO/IEC 16022:2000 .PP ANSI/AIM BC11 ISS .SH BUGS Post bug reports on GitHub issue tracker or email them to dmtx-bug@mva.name .SH AUTHOR Copyright (C) 2008, 2009 Mike Laughton Copyright (C) 2012-2016 Vadim A. Misbakh-Soloviov .\" end of man page libdmtx-0.7.7/script/000077500000000000000000000000001423156660700145065ustar00rootroot00000000000000libdmtx-0.7.7/script/check_all.sh000077500000000000000000000032021423156660700167470ustar00rootroot00000000000000#!/bin/sh function RunTest() { SCRIPT="$1" SCRIPT_TYPE=$(echo "$SCRIPT" | awk -F'.' '{print $NF}') echo " $SCRIPT" ERRORS=0 for dir in $(find "$LIBDMTX" -type d); do if [[ "$dir" != "$LIBDMTX" && "$dir" != "$LIBDMTX/test/simple_test" ]]; then continue fi for file in $(find $dir -maxdepth 1); do EXT=$(echo $file | awk -F'.' '{print $NF}') if [[ "$EXT" != "c" && "$EXT" != "h" && "$EXT" != "sh" && \ "$EXT" != "py" && "$EXT" != "pl" ]]; then continue fi if [[ "$(basename $file)" = "config.h" || "$(basename $file)" = "ltmain.sh" ]]; then continue fi if [[ $(cat $file | wc -l) -le 10 ]]; then #echo " skipping \"$file\" (trivial file)" continue fi if [[ "$SCRIPT_TYPE" = "sh" ]]; then $LIBDMTX/script/$SCRIPT $file ERRORS=$(( ERRORS + $? )) elif [[ "$SCRIPT_TYPE" = "pl" ]]; then PERL=$(which perl) if [[ $? -ne 0 ]]; then echo "No perl interpreter found. Skipping $SCRIPT test." else $PERL $LIBDMTX/script/$SCRIPT $file ERRORS=$(( ERRORS + $? )) fi fi done done return $ERRORS } LIBDMTX="$1" if [[ -z "$LIBDMTX" || ! -d "$LIBDMTX/script" ]]; then echo "Must provide valid LIBDMTX directory" exit 1 fi RunTest check_comments.sh RunTest check_copyright.sh RunTest check_license.sh RunTest check_spacing.sh RunTest check_whitespace.sh RunTest check_headers.pl RunTest check_todo.sh exit 0 libdmtx-0.7.7/script/check_comments.sh000077500000000000000000000005111423156660700200240ustar00rootroot00000000000000#!/bin/sh FILE="$1" LINE=$(grep -n "\*\{10\}" $FILE) if [[ $? -eq 0 ]]; then echo -e "Bad comment style found in $FILE on line(s):\n$LINE" exit 1 fi LINE=$(sed -n -e '1 =' -e '2,$ p' $FILE | grep -n "^\/\*\$") if [[ $? -eq 0 ]]; then echo -e "Bad comment style found in $FILE on line(s):\n$LINE" exit 2 fi exit 0 libdmtx-0.7.7/script/check_copyright.sh000077500000000000000000000003401423156660700202070ustar00rootroot00000000000000#!/bin/sh FILE="$1" # Every nontrivial source file must include a copyright line COPYRIGHT=$(grep "Copyright 2[[:digit:]]\{3\}" $FILE) if [[ $? -ne 0 ]]; then echo "Missing copyright text in $FILE" exit 1 fi exit 0 libdmtx-0.7.7/script/check_headers.pl000077500000000000000000000012211423156660700176120ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; use File::Basename; # TODO: Test still misses first function of each file my $errorCount = 0; undef my $closeLineNbr; undef my $lineNbrs; while(<>) { chomp; if(m/^}$/) { $closeLineNbr = $.; } elsif(!defined($closeLineNbr) || m/^$/ || m/^\*/ || m/^#/) { next; } elsif(m/^\/\*\*$/) { undef $closeLineNbr; } else { $lineNbrs = (defined $lineNbrs) ? "$lineNbrs, $." : $.; $errorCount++; undef $closeLineNbr; } } if($errorCount > 0) { print "Missing header comment in file \"" . basename($ARGV) . "\" at line(s) $lineNbrs\n"; exit(1); } exit(0); libdmtx-0.7.7/script/check_license.sh000077500000000000000000000010601423156660700176210ustar00rootroot00000000000000#!/bin/sh FILE="$1" TEST1="^ \* libdmtx - Data Matrix Encoding/Decoding Library\$" TEST2="^ \* See LICENSE file in the main project directory for full\$" TEST3="^ \* terms of use and distribution.\$" TEST4="^ \* Vadim A. Misbakh-Soloviov\$" COUNT=0 grep --silent "$TEST1" $FILE COUNT=$(( COUNT + $? )) grep --silent "$TEST2" $FILE COUNT=$(( COUNT + $? )) grep --silent "$TEST3" $FILE COUNT=$(( COUNT + $? )) grep --silent "$TEST4" $FILE COUNT=$(( COUNT + $? )) if [[ "$COUNT" -gt 0 ]]; then echo "Missing license text in $FILE" exit 1 fi exit 0 libdmtx-0.7.7/script/check_spacing.sh000077500000000000000000000016231423156660700176300ustar00rootroot00000000000000#!/bin/sh set -u FILE=$1 PATTERN="XXC_XX_X_XXX" COPYRIGHT=0 for i in $(seq 1 12); do LINE_TYPE=$(echo $PATTERN | cut -c$i) LINE_NBR=$((i + COPYRIGHT)) if [[ "$LINE_TYPE" = "C" ]]; then while true; do sed -n "${LINE_NBR}p" $FILE | grep --silent "^ \* Copyright" if [[ $? -eq 0 ]]; then COPYRIGHT=$((COPYRIGHT+1)) LINE_NBR=$((i + COPYRIGHT)) else COPYRIGHT=$((COPYRIGHT-1)) break fi done elif [[ "$LINE_TYPE" = "X" ]]; then sed -n "$LINE_NBR p" $FILE | grep --silent "^..*$" if [[ $? -ne 0 ]]; then echo "Expected line $LINE_NBR to be non-empty in $FILE" exit 1 fi else sed -n "$LINE_NBR p" $FILE | grep --silent "^[\/ ]\*$" if [[ $? -ne 0 ]]; then echo "Expected line $LINE_NBR to be empty in $FILE" exit 1 fi fi done exit 0 libdmtx-0.7.7/script/check_splint.sh000077500000000000000000000001401423156660700175060ustar00rootroot00000000000000#!/bin/sh #splint -linelen 999 -Disgreater -Disless dmtx.c splint -linelen 999 dmtx.c exit $? libdmtx-0.7.7/script/check_todo.sh000077500000000000000000000002671423156660700171540ustar00rootroot00000000000000#!/bin/sh FILE="$1" COUNT=$(grep -i -e "XXX" -e "TODO" -e "FIXME" $FILE | wc -l) if [[ "$COUNT" -gt 0 ]]; then printf "%4d TODO(s) remain in $FILE\n" $COUNT exit 1 fi exit 0 libdmtx-0.7.7/script/check_whitespace.sh000077500000000000000000000002411423156660700203330ustar00rootroot00000000000000#!/bin/sh FILE="$1" LINE=$(grep -n " $" $FILE) if [[ $? -eq 0 ]]; then echo -e "Trailing whitespace found in $FILE on line(s):\n$LINE" exit 1 fi exit 0 libdmtx-0.7.7/script/common_tasks.txt000066400000000000000000000054741423156660700177560ustar00rootroot00000000000000#!/make/me/a/sandwich Common Tasks ----------------------------------------------------------------- Generate splint warnings $ splint -posix-strict-lib dmtx.c Release Checklist ----------------------------------------------------------------- 1) o Include newly added files in lists below if appropriate 2) o Create copy of this file as living checklist 3) o Test for common style and formatting issues o $ script/check_all.sh . 4) o Review and close applicable bugs and feature requests 5) o Write and finalize release documentation o ReleaseNotes.txt (not in Git or source distribution) o ChangeLog o LICENSE o KNOWNBUG o NEWS o TODO o README o README.freebsd o README.cygwin o README.mingw o README.linux o README.unix o README.osx o man/libdmtx.3 6) o Update version number in appropriate files o configure.ac o dmtx.h o man/libdmtx.3 7) o Update release date in appropriate files o TODO o man/libdmtx.3 (be sure to sync w/ simple_test.c) 8) o Perform final test build o $ git status # no staged commits o $ git pull # get any pending updates o # final commit o $ sudo make uninstall && make clean && make distclean o $ ./autogen.sh && ./configure && make && make check && sudo make install o # Run tests and confirm it works. Start step over if changes are needed. 9) o Build and test tarballs o $ cd .. o $ git clone git://libdmtx.git.sourceforge.net/gitroot/libdmtx/libdmtx release o $ cd release o $ rm -Rf .git o $ find . -type d -name ".git" o $ ./autogen.sh && ./configure # don't build though o $ make dist-gzip o $ make dist-bzip2 o $ make dist-zip o Verify no extraneous files made their way into the distribution (especially in the wrapper directories) o $ md5sum libdmtx-0.8.0.* > MD5SUM.txt 10) o SourceForge release administration o Upload files to SourceForge o Publish news item 11) o Tag final release in Git (do this only after uploading to SourceForge in case something changes at the last minute) o $ git tag -a -m "Tagged v0.7.4" v0.7.4 o $ git push origin --tags 12) o Update minor number in unstable trunk (e.g., 0.8.0 -> 0.8.1) o Use file list from step 6 above o $ ./autogen.sh o $ ./configure o $ git commit -a o $ git push 13) o Check out tagged version so dmtx-utils and dmtx-wrapper builds will inherit correct version numbers o $ git checkout v0.7.4 o $ ./autogen.sh o $ ./configure o $ make clean o $ make o # sudo make install 13) o Update libdmtx.org with news item, download entry, and new project status 14) o Send message to libdmtx-announcements@lists.sourceforge.net with subject "libdmtx: 0.8.0 Released" and ReleaseNotes.txt as message body libdmtx-0.7.7/test/000077500000000000000000000000001423156660700141615ustar00rootroot00000000000000libdmtx-0.7.7/test/CMakeLists.txt000066400000000000000000000005751423156660700167300ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.12) enable_testing() add_executable(test_simple "simple_test/simple_test.c") target_link_libraries(test_simple PRIVATE dmtx) add_test(NAME test_simple COMMAND $) add_executable(test_unit "unit_test/unit_test.c") target_link_libraries(test_unit PRIVATE dmtx) add_test(NAME test_unit COMMAND $) libdmtx-0.7.7/test/Makefile.am000066400000000000000000000001161423156660700162130ustar00rootroot00000000000000SUBDIRS = simple_test #SUBDIRS = multi_test rotate_test simple_test unit_test libdmtx-0.7.7/test/compare_test/000077500000000000000000000000001423156660700166465ustar00rootroot00000000000000libdmtx-0.7.7/test/compare_test/Makefile000066400000000000000000000003441423156660700203070ustar00rootroot00000000000000test: @./compare_generated.sh @./compare_confirmed.sh @./compare_siemens.sh clean: rm -f compare_generated/barcode_*_?.pnm rm -f compare_confirmed/barcode_*_?.pnm rm -f compare_siemens/siemens_*_?.pnm .PHONY: test clean libdmtx-0.7.7/test/compare_test/TODO000066400000000000000000000002751423156660700173420ustar00rootroot00000000000000o Test that "best" option is always as good or better than others o Measure how often "best" gives exact results as a straight option o Count how often "best" beats "fast" (and visa versa) libdmtx-0.7.7/test/compare_test/compare_confirmed.sh000077500000000000000000000020071423156660700226600ustar00rootroot00000000000000#!/bin/sh ERROR_COUNT=0 echo "Comparing generated barcodes against confirmed results" echo "-----------------------------------------------------------------" for CONFIRMED in compare_confirmed/barcode_*_?.png; do GENERATED="compare_generated/$(basename $CONFIRMED | sed -e 's/^confirmed_/barcode_/')" if [[ ! -s "$GENERATED" ]]; then echo "FILE MISSING: Please run compare_generated.sh first." exit 1 fi GENERATED_MD5SUM=$(convert -depth 8 -type TrueColor $GENERATED pnm: | md5sum) GENERATED_ERROR=$? CONFIRMED_MD5SUM=$(convert -depth 8 -type TrueColor $CONFIRMED pnm: | md5sum) CONFIRMED_ERROR=$? if [[ "$GENERATED_ERROR" -ne 0 || "$CONFIRMED_ERROR" -ne 0 ]]; then echo "Error: convert failed" exit 1 fi if [[ "$GENERATED_MD5SUM" == "$CONFIRMED_MD5SUM" ]]; then echo "SUCCESS: $(basename $CONFIRMED)" else echo "FAILURE: $(basename $GENERATED)" ERROR_COUNT=$[$ERROR_COUNT + 1] fi done echo "$ERROR_COUNT difference(s) found" echo "" exit 0 libdmtx-0.7.7/test/compare_test/compare_confirmed/000077500000000000000000000000001423156660700223225ustar00rootroot00000000000000libdmtx-0.7.7/test/compare_test/compare_confirmed/barcode_004_a.png000066400000000000000000000003171423156660700253130ustar00rootroot00000000000000PNG  IHDRFFYU)bKGD#2 pHYsHHFk> vpAgFFj^IDAT(ϵұ 0 Dѓ(Rzl`x@Iq}Ez/:&ʏ+3VD56*&R?TQ0ŵt w&h_w_ŲkbIENDB`libdmtx-0.7.7/test/compare_test/compare_confirmed/barcode_014_c.png000066400000000000000000000004541423156660700253200ustar00rootroot00000000000000PNG  IHDRnn VbKGD#2 pHYsHHFk> vpAgnnR IDAT8͔1 E#&")#7@v_ccwpj'ZLCY@ ]Y Wħ cGGTU ݯ{5QYma_:VU3Ǣ 肂~H.j+v HQN1GƳVc>k"3T7&1IENDB`libdmtx-0.7.7/test/compare_test/compare_confirmed/barcode_060_e.png000066400000000000000000000002671423156660700253250ustar00rootroot00000000000000PNG  IHDRFFYU)bKGD݊ pHYs  ~[IDAT(ϵұ DK#dCj%ED z͗%U[W6E/*j}.顭 DAs\c 9IENDB`libdmtx-0.7.7/test/compare_test/compare_generated.sh000077500000000000000000000036221423156660700226540ustar00rootroot00000000000000#!/bin/sh #SCHEMES="b f a c t x e 8" SCHEMES="b a c t x e 8" DMTXWRITE="$(which dmtxwrite)" DMTXREAD="$(which dmtxread)" MOGRIFY=$(which mogrify) COMPARE_DIR="compare_generated" if [[ ! -x "$DMTXWRITE" ]]; then echo "Unable to execute \"$DMTXWRITE\"" exit 1 fi if [[ ! -x "$DMTXREAD" ]]; then echo "Unable to execute \"$DMTXREAD\"" exit 1 fi if [[ ! -x "$MOGRIFY" ]]; then echo "Unable to find or execute mogrify" exit 1 fi if [[ ! -d "$COMPARE_DIR" ]]; then $(which mkdir) "$COMPARE_DIR" fi ERROR_COUNT=0 echo "Generating and reading back barcodes from input messages" echo "-----------------------------------------------------------------" for file in input_messages/message_*.dat; do ENCODE=$(cat $file) MESSAGE=$(basename $file .dat | cut -d'_' -f2) for scheme in $SCHEMES; do OUTPUT="${COMPARE_DIR}/barcode_${MESSAGE}_${scheme}" $DMTXWRITE -e$scheme -o ${OUTPUT}.png $file 1>/dev/null 2>&1 ERROR=$? if [[ "$ERROR" -eq 70 ]]; then # XXX revisit this to use more specific error code when available echo " SKIP: message $MESSAGE scheme ${scheme} (unsupported character)" continue; elif [[ "$ERROR" -ne 0 && "$ERROR" -ne 70 ]]; then echo " ERROR: dmtxwrite failed" exit "$ERROR"; fi $MOGRIFY -depth 8 -type TrueColor ${OUTPUT}.png ERROR=$? if [[ $? -ne 0 ]]; then echo " ERROR: mogrify failed" exit "$ERROR"; fi DECODE=$($DMTXREAD ${OUTPUT}.png) ERROR=$? if [[ $? -ne 0 ]]; then echo " ERROR: dmtxread failed" exit "$ERROR"; fi if [[ "$ENCODE" == "$DECODE" ]]; then echo "SUCCESS: message $MESSAGE scheme ${scheme}" else echo "FAILURE: message $MESSAGE scheme ${scheme}" ERROR_COUNT=$[$ERROR_COUNT + 1] fi done done echo "$ERROR_COUNT error(s) encountered" echo "" exit 0 libdmtx-0.7.7/test/compare_test/compare_siemens.sh000077500000000000000000000012751423156660700223630ustar00rootroot00000000000000#!/bin/sh ERROR_COUNT=0 for PNG_BARCODE in compare_siemens/siemens_*_?.png; do PNM_BARCODE=$(echo $PNG_BARCODE | sed -e 's/\.png$/.pnm/') convert -depth 8 -type TrueColor $PNG_BARCODE $PNM_BARCODE done echo "Comparing generated barcodes against Seimens results" echo "-----------------------------------------------------------------" for file in compare_siemens/siemens_*_?.pnm; do TEST_BARCODE=compare_generated/$(basename $file | sed -e 's/^siemens_/barcode_/') if [[ ! -r "$TEST_BARCODE" ]]; then continue fi cmp $file $TEST_BARCODE if [[ $? -ne 0 ]]; then ERROR_COUNT=$[$ERROR_COUNT + 1] fi done echo "$ERROR_COUNT difference(s) found" echo "" exit 0 libdmtx-0.7.7/test/compare_test/compare_siemens/000077500000000000000000000000001423156660700220175ustar00rootroot00000000000000libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_000_8.png000066400000000000000000000006611423156660700250010ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge@IDATHՖAn@ pg~Y>ʷ}FIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_000_a.png000066400000000000000000000006601423156660700250510ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge?IDATHՖM0 'd᥏$ 4Ћ7D^kalk4 m=ql)(>l .с(%x/'FMtm7i$0NÖxF[3z_0f0E 7fՖ6=.]ku؋-kJMj3J vpAgeCIDATHՖ1 Eh Jd.)#b&9BJ(ojv(< c};>-ۘԅu H}F];"nۍf-Ӂi%53KL6ZGxL3c,ӦMҁF-ϭ ɼ6ڈe6zP,+fX8cYؾp*^؆6bYክe/ĬMp='nnsHfZ}3t`Z/V.?YΞP5)+4IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_000_f.png000066400000000000000000000005371423156660700250610ustar00rootroot00000000000000PNG  IHDRx-PbKGD#2 pHYsHHFk> vpAgOIDATHՖ10Q%#1@6IEr!M= Gm1޴$eZ2eaB2)L錻5+Ɇ ᪪xX1@֔c.L#ϝ2(-Pn)~*gFfZ2Xt>(Op %S?R5=_ P/-?3UF(~:#RV_eb佔%;TiĒIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_000_t.png000066400000000000000000000006511423156660700250740ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge8IDATHՖA0 D]GMbR7X@'P!uWR? D_rra X'.W62!ᑹ,|:$1+]w0X ۩ϐ \$u} u2T5; \G) L[61ɜf `=xv}k=Y Xtf4="c̬1ƲgF2*gݙEY,[¡ϬM`3;HzIo=ez[LF}oɴk fX{Xo78dl2KK߳?BԲf!IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_001_8.png000066400000000000000000000006561423156660700250060ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge=IDATHՖ10 E(Rp1 q1IbffʖvS5m?S%Nj9vLOzLiΗCݗLw9%]3Ś4:fz6eƁk7s˘їב{ ΘOj5N5@&0EXTȫLL?K}.-}@IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_001_a.png000066400000000000000000000006251423156660700250530ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾$IDATHՖ=n %G&bl Gta]|"kvǐ߫?w]Rx>|4n$-^ (%q/K."+XEц3mu` ;q+3w9oiE5.ADu6)>21O&{6 y}y\-AH6y>aHSHҀC6~LFgC.QYIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_001_c.png000066400000000000000000000006221423156660700250520ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾!IDATHՖ10Q IH| ~'P@\Rh#%.,渽]Oh݆Mlwm75:mʍYuh,QVDl(\z)Q*h@F1$tIAT?B>I8){:{|Uuc PQ%ԡbŪrZ.ڭ%>)24LMZ0W^efaxWCxN[C9hj_!Z0Qi~yPFLJ|QG%_Ĩr^~gCAO<IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_001_f.png000066400000000000000000000005411423156660700250550ustar00rootroot00000000000000PNG  IHDRx-PbKGD#2 pHYsHHFk> vpAgOIDATH10Љ(\{pHXb&b27SOak,f k?r-^ֲT1^XșVa mW9j Tq=% >*)(BΫjp PP fZ坷OMMOWdʚ3$0Hh2$i)?}3y! &UwߕKG)7!*yaS \/SyPCIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_001_t.png000066400000000000000000000006161423156660700250760ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾IDATHՖ1n0DQPrb_.H(\<ݙYOx}RO| !,(Ś8%rMf3b/)^pˊ(q&yzY%X"ChF( hCd+F$63Iw_3":t|$GLlٌ ^ҽ'\hǢz&ݫ>5eŭ"*{‰Rn+D>`W хyՊ& j~ٌ/'EIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_002_8.png000066400000000000000000000044611423156660700250050ustar00rootroot00000000000000PNG  IHDRp+bKGD#2 pHYsHHFk> vpAgIDATxAnJ`|H\G`y^W=&z/~ED\ԙ驮ֿz_/2r~_"`va=Z;jMsiv.nwqy.}ᣈ\egٮ~u[;'b)h+*{qI[Јk01L_g^ [^'EΓ1bIocĽsR[Q-Sς1#3U#BP7jAYNK@|qLո53u#{xZOknF>jk%@"4@jPODIWgTF #se9RXD]xD/e2B@8˜`cE5S=A?Lw1E5AO\tŠbV[T^.l'=%hO>+W3:ޙZ QVw3-%a!]]!&h{ӸwFj A;#U',^Aw׵Tl G胏<5ˢBLds1 [IAԔ8wxAU?!f Q GV^.$Tx)s5 }'+cizJޠsu.y_V"[tbRY1Ot܅ A^2p&)U1xZ5WJB'Ԅ=#wu{rPv2\ rf")l&4Phт\?%b'KZ9Q ?'9s /*0#Ps@j2l/ij\rFCŧ 5N(@gR^(rBvUMOI!ϟGںz)aW[jHhcmEQ}Ϫ@g~8C3n̢'}`@PLL< i QJ겧nK/ۗ&E˴uUTz(=!Zٯ 琴A@VT>5 vy/6 fx7e ēLx%7nE'l(d{[ T腒ڳ땶B3hH!(Z;/,-*< zP>rCQ!h._#Voݜ*46h+רsYcIUR혒neMsseO_>p(MoGg,"Dn{@q=ѡ0({hѫ ){X3P;O nCt܆w1%]qZȳU1ݚ5Y6E5XTt9΃| ,Ǣٟ]Th DV͎8{}X-]s/'"%R1+۷^?8LC!nYhkrPOW-0mnϞNm Unܝ'f=C(8ԇqHc4ZFwٻE򳭿{9!u!2X] ?U*B"{G"Y}&fgg3k?f)k:PTB2n ļH~(U 0|wZVAC@ټB!.} cW4ߵ]qp5h)t" _q::bP b06w]X3I>\:v~eBy\ "Og=Z 2,_EѫCpi#:n6WB\O<6Qjq17hdF/wwg>is( }x@qzظV'dUE 8wW2hp9Gz]cwq0<; __?w__迡5 IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_002_a.png000066400000000000000000000044131423156660700250530ustar00rootroot00000000000000PNG  IHDRp+bKGD#2 pHYsHHFk> vpAgIDATx\AnH ~~b}@ cOu0eU`3{NBiv6ӯ ݞP;?c ?nc|w۱4~^/wv1xiQm>c52'^ʰSqLڗ13ϟH%Օ:#h3?$yvLUrٖfPtXDA>.ށ3GF:6~0<,ū9#\¿p @'D+( .A!.q'_4"Rڐ<=?)Rd4LF/;=W: z}KxP17V{~Ě62+UԄ#} ݴ]ũU3~b%UDD2QD]43"7@^@a#>8@) .[ ?.8VbcN~4E<*x"_u>jP͠$4x[|wD4@<%n: ԹGFfSJ%I4rW'8~"TqxUͽ|,vHP fj"qp8bFf|B:kmǕ4T;U߽Imh5Ӈ<^'jΪ[=fPXIыGm ;rX5٬q?OfsD5kVUt.9{uR?}鄖cq!R{VD&ݠ"jtRp g9ʫN.1QG(h&Yh~Y\"nPHUvrwuBD6w2%mRJEE筺 Ζɍ :dVtq |[^'6 ZP6%pPL4:a>L**:w>S!>Emv76d:jw>PX0;0dJkq0Mw+iO.>䴓r :Tpz3db=N<*anoiT8q|Jz ҏl}raN=JsvO! ʲy[V/=yhVm?~Lfinbޣ(kRd7-6i0[67Vɍ9Ȱ4j{8j/`v* 4a]U3{ڋce$(K;(LPD S"Us 7u$=ril$>eb@5!kʞP"K!Za!MxZyoLsZAs K .wvkeelUf;Ґ L^~hV-J1SIILW}aaXA~, t&(IeX2QO+͠F3r Xj~^23+] +/5d8Tƃ+1knP{C BsvRѓը@s#2jJR(vIVNP-/M'r$I?i4Ms@7z mrKcC4ev~yOVұ9i1K遽q_MYEԄ5w }+ScjbLi]n;]4N5r&Iԁȸz^llrh[G5^F4I]VP[)ᑀrk֏`Z:t@򅛟wt_\QlU4_jtCZ jggٓU|y~AWK }<2N[d臻 T$ctjGCFDx~zA_XQΩQ8yK3&_ɑ!.1n5*oa?h`{?_B)qIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_002_c.png000066400000000000000000000052151423156660700250560ustar00rootroot00000000000000PNG  IHDR'bKGD#2 pHYsHHFk> vpAgL< IDATxMJ #&!M|,Z"~z˙Eϼ*-lT׫ /ש{]ow展ry:oBޢ,_s|0Nze_y/uc>I1c}\3O{֝^wM\ ;YGXwJֽ x/+:_9ǵzs:W]ۼ޿:8.wG V(e]ujzqP]}zM[N9< sjq!*[SwxDz:<s:0lס>,:u:aN6GZ~~B^ja񯫶>]_fOGtӚ=Cx N`9 =wx"'Hͤt6]|-&AkUwcgƝ'.Ownm.ǮSD+-?.5Th=8뷺VuI׽^.ű]1E/ xU#ܘ7"d*t=qe3^ Ny:螣kS <_}KԬCt]ØىeᯡTKtr90_cE>9d JZw76cLkYON>#1  Oz7 $SthE^Klr!]ză;.r;` 4E#P-QLN0r}}ᕩ;X'* M ujZ$]YO8 :7N(~w%/zىmNʄJɠ'!r۹/OԂOI91dYb‚ÕΈIv6[®+_`+vxa(?g^`N1r;.6Q_xYfH "7:0tW*S`4I:C=V訿ImvD19 ٩xR"&gRXkM#I[_muӞYRAt_sK_0wbLw~i(] |DOb_OzƯ̑_p>`aK8?1knrV ' ?k|RZwY{:da~7)-5*aL9֗+ov}\5o-Dx|c`Ι)k9%{z ]hMp֪JIuqu^kyj(}8ɮwI3'_iq+\#Rf vpAgSIDATx\=j 4HHDbcNXB~ GG ˿bkc[e|s۰Z]ކ.ʢmj @joq_[||M׫^mOo;GFKmPu}G;P%|L_7޼4*᣸:øŌ/G ^H9-\(鸷Dp5:X˽EQKp&xR!GW-E?F͑gi2ȳHCd\nHxKE1JߖY`C}v{d%j^D ED0z֩H4{uEQDӅpB!^;Lp:YK{!nU]~;h__(φև|U= && k̨!}\K kF F[E]F#B0i!w|I<-*ЖX;)wgڪN\ 2,<͆ʨpYͺt#1C00dO_Ca*X 'yަ ]CTH~c˺\̎]Q%A_pzUQ=#Ūibfֆԧ[FC%ôWY6ZaU̶+(xgE{kS1qq/\HEP%ESZ 񢨮gW+Z+VnO8!$cUO9Su>Abb|yk3O$ :Xz9Z\j^y-΁uI'N:DS jٟ%Rꈚ(cu\1<4+]ϩAEQo }N2ȸ@| -WEyuX~ӷ8a`Uwѣ,)I|rɘ s5QUQ_ M~ѩ?IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_002_t.png000066400000000000000000000036641423156660700251050ustar00rootroot00000000000000PNG  IHDRV7lbKGD#2 pHYsHHFk> vpAgCIDATx\KZ 4ʀa/Ɛ`',a(~*A*˟r';@<R}k:-/{u0xke)>|x,j fWT=&܏~5QƯe_GF^]mT8V2/0BhW]0n|Qiê;R ZQvV W=_%Q;.(C*Ɨ# ¨-,_yIyҍIrU!Dm6ԽH>6T|ECgJs T$K 犯(V0\*t"PaRU((GȮǂ8Og:0˽0 泩Xɳ1ޏFre(^!:YM x\KŸGOƮA&)XDUZW!e@he&oGۓ_R6o$GmU'N1 ڙYxƅ"P#9Rɤ|BfK̼[ٟMQ_Q]4b<\htΨf5YXU ($,=QR-EAĊ+҂`&g#G*FhbY! _Ԥ0*]thzuQ%mM+$#=sWvSaZ:e9ZX$]IBvxށ.DB%Uj& (YDֈ6&viȜeERYQ.f>6ܫí:VEI/EE.Ea +6ԸaH{=9͵ QmrEk *D(LͲkVE~guQifئD,a:ԍ[5Q\pYCKi9S~4>_E3 SR윖w-ڳ}^%d1{N)O_N/,To:Yp|7q,ⰾmNB;-Ձqy媨^CCPD3%VyD}3 Iy'ipo -*cYSkP9lɀ:[9 ޶1c.*դ\┄g4o;> Y]k VΤP vpAg]NdIDATXAn0 EXdr#bp% h+uq+HA?$9n^pREeҗU$"Y njPp1dS,xȠ n;I0F_=\B^R=#Uʞ:u nlhe)(=U)Us).0)Þf&8>6͈5RƐ؇ gJZõ9NT1WTIM5{9D+:mɫX%=d!UKƧO :HNDYZZs=vV.3`"&'+=5'#~:AQ\ Aef,,0u6y@S 4Sz:)E+^jrD"T{X}erh0)>b1ݠb,}T=40['/79 fmz8ڹ@xm&%!S5|Q5սMF٣Pܛ~N^CvZ3W< GCF>/C6Xp-bU :On/hIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_003_a.png000066400000000000000000000013121423156660700250470ustar00rootroot00000000000000PNG  IHDR3!,bKGD#2 pHYsHHFk> vpAg]NYIDATX=@ F((97a. $.7b)6(Q87~l 5 P&+% !Êoo>u>$s~>铒'7ovZkVWT ޢ́/ѣUؾE*@ Eu:9fCEjV{;DeVDV2ӻˆ3Vw@5 ҝN-񑯉Pn3NXIҋYS9(Ig΢C'2Xajl:hs?Q<$ZRt h-E:،)8l@nx9!ʇOIWSsϙ:$uWo p5W\a#Gkw]% 5Y̬ްoHKք0yKu9i&S'՗R!]@;Wr*uL%8;,UӠkEܵZvлH2Hҽ@2, '^EϞ#xX{TҚL@W vpAgEIDATXY10ÅK???1'~BJΑbo+]@ `jV[s_&WɌ։c16V9x(8XvIݓygig6XI^.+E[FRS".C#l̤LS˔7fdBl%+ez0xL}xO0&DFxnL0%uT>a^=Bb]b٬޸(.ogR3S2P̸ACq0ʪR'xbzA3.D(|܌>㏯ 䉊$%+r:u_:skIvN?p=Rqfܭ0]9=Ӓ{+vlaHXNӉ:8aОӼfA*Z5^Xó 9940'@x\d꽈,5T ;ȢtK(<;I;<ޢ8i~D]1-r,7CFwe ߼]hZA,;W J/]0GkEst1¤`r_ z?yG3|IS5 [hLem%cضuiٝ~1%Euo¿ŏGIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_003_f.png000066400000000000000000000011521423156660700250560ustar00rootroot00000000000000PNG  IHDR#3bKGD#2 pHYsHHFk> vpAgJ|IDATX;n0DWPRGM] RRL$Il=.mձDkNEIf]-a&Ĺt,.$fb̮P❒S6Y t7l]CBRgf8FAȏ1{}8DV+ ,H9Ad6Ŏ"@Ts m}HRr~q._z֨$*4m&9T]BSĐjz|vf!;j+5kZ RKј.9( *3L.f]ko*„ Q\ѷ>ⱵxЦ" b)踒(Bk}uq!Aj52RnrϘ«\>:4؝)vh"qn3s;<2<ú] (>;Dju+nߑ_H^8YqqIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_003_t.png000066400000000000000000000011521423156660700250740ustar00rootroot00000000000000PNG  IHDR#3bKGD#2 pHYsHHFk> vpAgJ|IDATX;n0DWPRGM] RRL$Il=.mձDkNEIf]-a&Ĺt,.$fb̮P❒S6Y t7l]CBRgf8FAȏ1{}8DV+ ,H9Ad6Ŏ"@Ts m}HRr~q._z֨$*4m&9T]BSĐjz|vf!;j+5kZ RKј.9( *3L.f]ko*„ Q\ѷ>ⱵxЦ" b)踒(Bk}uq!Aj52RnrϘ«\>:4؝)vh"qn3s;<2<ú] (>;Dju+nߑ_H^8YqqIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_004_8.png000066400000000000000000000003611423156660700250020ustar00rootroot00000000000000PNG  IHDRZZ|bKGD#2 pHYsHHFk> vpAgZZ#FIDAT8Ա 0,RfɄ.lR~ +^qGAdXT$DPEa"a*>J}xNs1g n|QB6{66 w-*)IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_004_a.png000066400000000000000000000003171423156660700250540ustar00rootroot00000000000000PNG  IHDRFFYU)bKGD#2 pHYsHHFk> vpAgFFj^IDAT(ϵұ 0 Dѓ(Rzl`x@Iq}Ez/:&ʏ+3VD56*&R?TQ0ŵt w&h_w_ŲkbIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_004_c.png000066400000000000000000000003461423156660700250600ustar00rootroot00000000000000PNG  IHDRPPjbKGD#2 pHYsHHFk> vpAgPPuIDAT(Ͻӱ \P2B6aH lQ@P|k-V0c7zAH&*ɬ'f*\oCŤ ^6C<<ɬp63%$W~ j~MIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_004_f.png000066400000000000000000000003171423156660700250610ustar00rootroot00000000000000PNG  IHDRFFYU)bKGD#2 pHYsHHFk> vpAgFFj^IDAT(ϵұ 0 Dѓ(Rzl`x@Iq}Ez/:&ʏ+3VD56*&R?TQ0ŵt w&h_w_ŲkbIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_004_t.png000066400000000000000000000003451423156660700251000ustar00rootroot00000000000000PNG  IHDRPPjbKGD#2 pHYsHHFk> vpAgPPtIDAT(Ͻ= g97ы`&X?WhSH=W;X#IoH0B2HFک \&3w`NR Ed!FQE\^$~`IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_005_8.png000066400000000000000000000010331423156660700250000ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ1n0 Eid#& 8/&DGA?e@ەjQEN80d"+e%iUJYyP8l*ӹ ^:9>ɤ'{]9="IM`|͏&e2%K$k(OIpHy.oӕ']qBo:Zn9u't>u/G]Iu I ?qrƗ'~>og=9SdNK|D_GN&-M395YkjoȻ'G_gfDUzI{~zsn=6* a_<{6qB_śªS0?~ιhz/5V[6{Wsy)RXnt|( ?zxߚ}ܗ{^꡼"IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_005_a.png000066400000000000000000000005441423156660700250570ustar00rootroot00000000000000PNG  IHDRx-PbKGD#2 pHYsHHFk> vpAgOIDATHՖ10p''1K 1? e 4iH\ km`+RbpGI_CY & nG:bC͙Pԙ,@2f!BJʣ>@OeQv<`OEdi㤇`A)隲օe)){TƸ1Ӗw> <[λ,Dt Rr0)%I~f- %/% ŚIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_005_c.png000066400000000000000000000006561423156660700250650ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge=IDATHՖ1n0 EA$3 s"'-.$j0ǁ&I|_羔ty0}->~ldehJGa kxù7ӳWajrg1o}b0I6ZL-?i~f zvfCPSn~]0 Οeh-2 `Zů#(ڗfHY썩L[a+cS)SfԆ7cʹ|U\ؗ28Ͼe6,uԁ/|UXS =Mf˸o:T{U2Vb;9=LjT= =;3]Yד_g}0IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_005_f.png000066400000000000000000000005441423156660700250640ustar00rootroot00000000000000PNG  IHDRx-PbKGD#2 pHYsHHFk> vpAgOIDATHՖ10p''1K 1? e 4iH\ km`+RbpGI_CY & nG:bC͙Pԙ,@2f!BJʣ>@OeQv<`OEdi㤇`A)隲օe)){TƸ1Ӗw> <[λ,Dt Rr0)%I~f- %/% ŚIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_005_t.png000066400000000000000000000006611423156660700251020ustar00rootroot00000000000000PNG  IHDRzLObKGD#2 pHYsHHFk> vpAge@IDATHՖ10 E((s܄\ X GHhFӭli7E/ ׺_3m~yLBQHp:_y-L΀E6؄I3\I&6a;/eRrYv 2*v+3<]$gi詩a-"͗ҭ [Q)XkCU2_"mW"6;%\|aԏ( -ʬ.9г*s"$Ш<3G!n\&zY _.`R%IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_006_8.png000066400000000000000000000005071423156660700250060ustar00rootroot00000000000000PNG  IHDRxx bKGD#2 pHYsHHFk> vpAgxxE[IDAT81 O,(97h&8;#mI\ ?(5)g-"ewi`u';aቻ7 ,)בWg^zt99C[&Bw=SxWzRMߧVx+GZswh^jK&)"Wfyi)6lڟ6oG RIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_006_a.png000066400000000000000000000003741423156660700250610ustar00rootroot00000000000000PNG  IHDRddXbKGD#2 pHYsHHFk> vpAgdd&^IDAT8= FQz1Lބ#02jQ 8ځ-$ucm\bB vpAgnnR IDAT8͔ D"BJ1$#]c\'@bo'e jgg._;7%@O",gzBSl`Is%2MMa U𕿌5/?QY McЬKMj=]IFb9TvmrV&PV2^7cYo9.IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_006_f.png000066400000000000000000000003741423156660700250660ustar00rootroot00000000000000PNG  IHDRddXbKGD#2 pHYsHHFk> vpAgdd&^IDAT8= FQz1Lބ#02jQ 8ځ-$ucm\bB vpAgnnR IDAT8Խ 0 ((=B6,&!e ; kHakv~w0LC\mIS(i-JJ?nb,VBDʬ匫)seV=vQOX.L: l4oü>l@s鯛# {ț|?|m]IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_007_8.png000066400000000000000000000010331423156660700250020ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ10 E=H#&! \ n#PR ; άE c_]-9oA\pU!|*I7^=ޜh3jix _O ="=NW"pų%#@!NЛ.bzs(9T((8R% )Jכ+_2Ue)<,8M%5Mp] VJOo>Mn{z5_p3W?濨65zSF-%eߝ(N Q폞l>}xsα}֜`)tp3+<\ΕÍ WT6<3R"D : P:t\#({?ܗVE:VIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_007_a.png000066400000000000000000000010261423156660700250550ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗA0 EȒ#&bH&e0ڭED,mD|;ޖ~e8$ˠ,2b)|us\vuexx҉j zdL&WLJ~oElNaSj!m H@OҲ3^zQ$~ZEQPFѪpZw?..Jeԗj;_N?I'^KZ_}Ɨ(_'l{<*swNMo5%v.E7?phGn#LRP%#E2ŗZX~%TG= o8ڶJWkf%~.>̉D8}9ν }Uo.b)7WοSw2?a?uGVjiCIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_007_c.png000066400000000000000000000010421423156660700250550ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗM0 Ȓ#&pJ q1z% {NHjNG,\gDyT1e/{OڅoحqE!qG<ϝ9(ֺt'*qh+G}9LqX26n/K0&ځP AUt LO0耐۞ɹ`롞С#ΜYhvߓsZcSth2]xȜ*{|Yk<~q({OnO,C vpAg떾'IDATHՖA E%G&r1M܄#da83ɟdѷ)ϵ'/?A?f"eH:T{CLTMlMxHFYP씫+EKmaBK4Q6<G$"wh([IQl^e$7׾*PA ڤ[h>*RW4}U!ɼ>*=1+iL&] ̉TGx _>Im\SnxJZ{*zf"L<>2K0ۜ &0}Po+ѿ%z' hIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_007_t.png000066400000000000000000000006301423156660700251000ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾'IDATHՖA E%G&r1M܄#da83ɟdѷ)ϵ'/?A?f"eH:T{CLTMlMxHFYP씫+EKmaBK4Q6<G$"wh([IQl^e$7׾*PA ڤ[h>*RW4}U!ɼ>*=1+iL&] ̉TGx _>Im\SnxJZ{*zf"L<>2K0ۜ &0}Po+ѿ%z' hIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_008_8.png000066400000000000000000000010341423156660700250040ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗAn0 E=b#&bHAb&9K֙YDK=[nS\zȠ؄wG>x[h"_7 z<ArJLDXk nRDBIjݹXԺxsS,9. x~yͩs|8A j&<ސ]U-d(xDɳ{r &|b-Aw]>M*;뎴ӽqubܬ-n&;rlc֝Ek ൑x vpAg IDATXݗ=0 ((sdr1$s}$jg[gVK1PgD6qqSM*㲧|)<Ľw~E!wpW<_xB<,Wݽ9S,k]yG8,ڂ!l57FI-8I8 3˯.C݇5,o9̑FF:#x{FV:O)8Q]`cބΰ@D*YrǛ_/^\Kyq^MO޼gɡ"iLz'g%"/,4 UWN)Qڀ_zw}ϗS@l֍7?lՃUjΕC?y5Vd[9z#lAGac6n%_N/S׾}SVWz(IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_008_c.png000066400000000000000000000010421423156660700250560ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗAn0q | c'<+&݃!Qx`yK{h=׵uV flyŸfoQZS_͎SN=[gҳRv싊1uid0{[~u?+?K$s5+pz4*pe/AT3ݳ_I"4''pGb7o5sB$)`dKg(Wk\'=y"IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_008_f.png000066400000000000000000000006211423156660700250630ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾 IDATHՖ10 E="e\ n#P@xM`4S~"";F36Oh~Rx-|>Ū$ӑ$.RP%jl[\[ZZ"Hgt}("25tdd# x BӐ/=QDOk r} " FSBE&?%fd IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_008_t.png000066400000000000000000000006211423156660700251010ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾 IDATHՖ10 E="e\ n#P@xM`4S~"";F36Oh~Rx-|>Ū$ӑ$.RP%jl[\[ZZ"Hgt}("25tdd# x BӐ/=QDOk r} " FSBE&?%fd IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_009_8.png000066400000000000000000000010371423156660700250100ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ10 E((9Bn2H\ nLliYm mѷ)%L?.ȠzPr$v,1j/_~\ ]ӍУi]f=oRa-+ǂb~B JQxsr8UEy@l.|/cXr8R8sAG+Q];p}8)M`щbU ) +T3暣u֚\~؁nH2ofﺸrǤw jam9?܎Λxa6IJ)y\qAeFW}+?Jw3:%7CE;ppjNڀG=Brځ[ܬpޣ9s ֏ dir+Z0:m9N Dr/T1?ieIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_009_a.png000066400000000000000000000010221423156660700250530ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ=0_DG&b@b&> ycLmY- )ocǭ>DK7} m\r猉R.kMV [\<3}ЗC>W=0k2ǝk$s?i/҅/cUqS 4ǗǗZZdT&8mAjNWn?hA? <=(E@GW|v .=|:_k%qR9CnSy՗;dAaƟz|)tDt/WG{~Jřf7&v^=nm>:sC lI#ΜƝ!_Wݹr΋`RsnTE+Oe޵T3w4]]lI __a뫄=IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_009_c.png000066400000000000000000000011411423156660700250570ustar00rootroot00000000000000PNG  IHDR#3bKGD#2 pHYsHHFk> vpAgJ|IDATX10 $>O $@u&wthFv{1mQiν R&R;z;}wTdipߊQ`u_2Fqs-|&!E1+0<4"rJPq6! ;_r@=#T14J$☓GvtmN5$(P]M/%Q n^}SH00,WYiѡ:GՑ? 4Wz&"\u "$tB 09A+\6*P͛TeiMFƍ $EJELȩf+EԑNژbY*ҊiZ9[m2U19`%]##3QE\ âFKM{7Sf&n vpAg떾'IDATHՖAn0 D`#&?CMrY q~.fc}_':9No nc6C=T9o vce (IvxlHvxihx5Rږ[k)C{hTeB 0Xl+ 0X3jQڅi:-KF;LnCBD[uZ~Ovj)p 'jUX $Fϡ0؊Bb,Hs4>Rv::l׷l'%tV: iB$LO_V?Ch=' IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_009_t.png000066400000000000000000000006301423156660700251020ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾'IDATHՖAn0 D`#&?CMrY q~.fc}_':9No nc6C=T9o vce (IvxlHvxihx5Rږ[k)C{hTeB 0Xl+ 0X3jQڅi:-KF;LnCBD[uZ~Ovj)p 'jUX $Fϡ0؊Bb,Hs4>Rv::l׷l'%tV: iB$LO_V?Ch=' IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_010_8.png000066400000000000000000000010351423156660700247760ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗMn0 2G&,Y9n,^[1~uI3%wA*Bd(ێ vLx 3 <<-BE$j &QH$|I"@4yH1)ԒFY&9~ Lq"'r |B9,B.|Q|)5Rw\~^EoȈ`yPtQ4ߜ9'KRQUYU^M;po{W^<9"Jz۞Q Xj'8 f{WlD}ՙ'h{rO=嚏#}ޖ=ГolaܦcX6}9|Гܦ-2/yqܗnXb3IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_010_a.png000066400000000000000000000010251423156660700250460ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ=n0Qd}1$sbxqVIFk>S|e8n{p3׬iR-qI\e(&ͦǛCbلY&Ng~%BT[o=ėOTyVg4Sl꫞ `T vpAg떾IDATHՖA0D[\rs&9Kbį39LoSIuuss}~mfu˰$G}afJdƯ[ɹam QP#^0.t(,P-f=+gV+-ߊ2Dɾv5N:`XB( vpAg떾IDATHՖA0D[\rs&9Kbį39LoSIuuss}~mfu˰$G}afJdƯ[ɹam QP#^0.t(,P-f=+gV+-ߊ2Dɾv5N:`XB( vpAg IDATXݗ; Dr#pb17 `/mf,xU9kSJK&"x#1Tt0æ'210P%AEYt߳&YJ?xl9U[~l6pLN_ЁPWӖ'QR;p wstlyI2S1km<99N%9Ϛ?[1}s]7) UKg#O:0êt?`ͣ}.ym?{ӔW31(t櫎O~ϒŤA6Q0Ӈ _8<c~կ~όs0mEQZr{qzכ)Go/3,_?n?;䯿IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_011_8.png000066400000000000000000000005501423156660700250000ustar00rootroot00000000000000PNG  IHDRx-PbKGD#2 pHYsHHFk> vpAgOIDATHՖ10 7pROO ER\Ņ LCqɥ @%s;a\ J.|*ZqJJتB&XIYwiPCyBU,)0ƽRNrNr/ NbƤ$ݐjgMN]S3hC ZO:4I+o\Be7wUɽ*ig=$m&٦K1!p+󕡗 'olQ_fIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_011_a.png000066400000000000000000000005151423156660700250520ustar00rootroot00000000000000PNG  IHDRxx bKGD#2 pHYsHHFk> vpAgxxE[IDAT8͕10 H'H| ~'PR 6t[sa1),;1gx݆6ito" FrIsK Wtod{~d@sm%c¸he\F;0:JצUʲ64^J?6>Jͣ4W=XzzJ]Xk/f\IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_011_c.png000066400000000000000000000006231423156660700250540ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾"IDATHՖ=0p#&bH q1&>(YWFSYgf>K KBf`DԡԲZ#&Z-Q/-2ے=#+gY]$Dl4g;GPV17`F- Fk!kљ #"CW%Eϸn+Q7lfk|uTَ[R#tȇ s1\NJ\m ^ fU01yibY]i]aTd1?[_ C:IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_011_f.png000066400000000000000000000005131423156660700250550ustar00rootroot00000000000000PNG  IHDRxx bKGD#2 pHYsHHFk> vpAgxxE[IDAT8͕1 EĐ1G&bPŖZc|.ccŁE$@%8pEjąpe6JY1O;XAk3W\jjvaj",zq`P<̻{){0f"=?Vr5mZWV}yVᘵEHkݤ+8_DR>XfIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_011_t.png000066400000000000000000000005131423156660700250730ustar00rootroot00000000000000PNG  IHDRxx bKGD#2 pHYsHHFk> vpAgxxE[IDAT8͕1 EĐ1G&bPŖZc|.ccŁE$@%8pEjąpe6JY1O;XAk3W\jjvaj",zq`P<̻{){0f"=?Vr5mZWV}yVᘵEHkݤ+8_DR>XfIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_012_8.png000066400000000000000000000010251423156660700247770ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ1n@ D((97\ $.7#Pnpf B)gAco(W"i m6)\2:ׄ*V3V7ߏY tF14o2'gN@Xd{Ee:$8%؄1C-."@m2Z;'>Ze|WIC C3xc<|~95YQ,eP_kfdv1K![O=|!/5I/_aj"}XA %/>t<MjNU<)V{ WμWRMѬְˮGw-[Aa'^O6EXJq?9З0;M\M>br!H߿=?@S\3IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_012_a.png000066400000000000000000000010241423156660700250470ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗK0D;b%GMCR.܄#dQS6Dɶda#?Սo딮?>|XtNƫɶ܁ۂbz7l=GsJ67=>> MRdcxͰL\VC eDtKy*FݛlXDB h:c9! O|kMU^9 v'NхswJޘrKzy-OxR(=w~*_0Y LC~2+W7 ?cS($;˽64c똛!-ξ\my׆-=Gs[f~r^4$ל}s>F#Ưڼ埚{<fIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_012_c.png000066400000000000000000000010331423156660700250510ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ10 E(Rrdr1$X&2ljS xPXocq!%"1R; "^<鎄/ʗ{<,2څofcC^RZrrr X!oޜ?JFƽŕgv,\}̩rA_89$PKO"st| %VCgG*iU'M=8xGSSZǛIoRd.}#/Wt==yg#|QjZT;pskZg~2mmOw&H܋y}/GND-s>¤a=xݶoЊk98!iQrnG+uw-ʳ [Ӿxuoo?eN]g__=BmIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_012_f.png000066400000000000000000000006171423156660700250630ustar00rootroot00000000000000PNG  IHDR|bKGD#2 pHYsHHFk> vpAg떾IDATHՖAn0 D2GMŐ@bpe_L'!/fa%c{`x_'S7P1$G.4'` , F[Cz+CacnYOVU1@|x*C|aPQkj 3'5G%jOc*#Ru%1?[?D.XfUŖIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_012_t.png000066400000000000000000000010221423156660700250700ustar00rootroot00000000000000PNG  IHDR;bKGD#2 pHYsHHFk> vpAg IDATXݗ10 D((9BnH\,$GL1VuShb.o[+^D6gHDbg=EQ3_jEJ{-2i\O1CM;>k\U?^*pp}SҪǛ'耞Ma}7oVΦ W_7ृCayqfDICB<}ʉN[G b?71Yx%l(@er믏+6_k\|9c'ڀ_OW~NX j9Pth%fF ᙏG,^m-ǒ{,?KlKp>!ny 5ۈu-Ǣ(+˦j aY_z﷮ܾ#8,O/<ݫnD|cfG\_?wXt*GIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_013_8.png000066400000000000000000000063611423156660700250100ustar00rootroot00000000000000PNG  IHDRJbKGD#2 pHYsHHFk> vpAg IDATx]AJ t"KM"p,@rCBH_'gM&Ppec$A?qοyK@g[yͬ#eYoW0RzXw~4 g9Z/U/_pXWhEK4!YrϯMc[9drO|ݎf9~_VŞ} KNy)G9w^v G /h,i}Oi!wV'd/k\f\1ʁ#رᭋu<:>űn V7K&);:]X'A ;;~OW%t6! };BAMunzmqfفܵ9M{žuP0@[ d0Ś.\uYŕW+˰|ؙ0p#AA'F|#npZg)6ܐ(/NoI,qşi5bFgduJ R^>0^ MA'db[?cJ R+9+FD SJ=6"] Nu0ۨ3RAh'K;7e1r:̤So&N+wM#XB9|$Tce;OC1AWK`F"spV S Kq/jCfOpES1SJ/9yF}i91`yXs M&jAYo*i$D |n]V=A@"}Yc &@?$q_P ,.SfsI9w4R-O/5!0[ rF՝SN|]@ճkȮHCZ b"Pdf9a ]5=)i\pC^s #Qy}OjFc.qSHN% &WHmY]Fʹͥsyf4¨ #Ie3Ff@T(P g,, :A wfJQ?(Ksi:IL Ya%AAH;Ki(*QɚOp ЂXL;G1 $*eKI:XH@2/V|!(G`!9gδeȦPq֖׫ ] Q{D=a12B1`!ffXd41$@dTs P$:EP1͟$'cLl C\{m>ف0oOߑk v6KX_Lm {kBMyj4X.7d ;JW=A@H̪-A{A=cBr@a k>,䪷O[n!YZ) jUbcN@lJV;uWOX x^4u$75q@NWҰ#fq"#zcd`geϑӜֽ08˗" _ D]L&.F-7Biˉ=7qD׼8A@b2_qdmȌdQ"P}H D9ME †53{%?FR:in7y^@& AӼu:K?ýwd13 *ia'V@X:uJuT|D׼`Z1)F ?Afr ;BZFׄjś?WoV ?XLi5程)N}1NI {hp\rYLK 1&(M'M4`ER\Cn8씦roYF|{tI}9V<ٴxց i#PJf(]8:̰.&e$Y_bmv6ֹ*r%z{C<JYw[ :AApFGnYx _RѭT%AA6f19Lj\GMsռ8Av {0i251MZ U7,Pd dҟ?$@1]$2 ly'UuڪreU%$.6?*XЃxLzU ڵ9A u̲H"3#Qnתe4A>AXOhp|dJ[ש0'wOcix"3!4 Asu0hZ$JH4#X6* a?{ID3&NO>icfX}O3eԯ"gJ#v.tmTuR{ j^rكLqJj[xgk`;|@5i[ ;\ۅ:†(_޺~]x ³4Ѩq OTz `p:na(䋡ԊbdW&ыaK,rI@bh8e9zi˞ ?'I?:]IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_013_a.png000066400000000000000000000050771423156660700250640ustar00rootroot00000000000000PNG  IHDR'bKGD#2 pHYsHHFk> vpAgL< IDATx]=nJ BX u|˙Jiyn #3 i};G=?t/Ë~ hc[(O*QFàzVƿLB<}tLOT$uZ-/stʪ}9)7ď~c41، 6.S'mUpk:Zy#!'qso9ht?)/ƗB,!_΄85G$?MiƃYFS"&o1Olܧ!-K;w- 8%@v49O!4n2e?+7FtyD] 8uco8ec: qPo?O1OeGBùVc۬u;3Um<%892q5 DXAǷאnv.w?Q{'&TTS>?ppW.3(Sca9:WV2nxvhU28a I%:Ny$(ԈS+^q|>1qcn=8'ʟCvTǞn)bOHva Gi<DTP#`0q_85c:N}-߿?O4C6| Ax\$d /ƋSzua3q7AOsoi<0!$ƒL%n<yHnHx =;P' __ja0;OOI[qh#2VUO^.q,8-3I_/:IWug3 5u<Ժha:mtm}XIiutP 0;jAx_>yZ9 rW,:>,_xze4).g*zu`Np(9WU:|p7C9˝[_Q'9RV:UgG}z@xٖlstIc~26 'Rz;:5]q+xe M<̎_:^v.ſof$kjBO[7\'DD$p&tr͖vȰ+2,RIR)fGh½d\OҥGjI//Xߜ>d1YWU[6T&t;j¸΃mءεlYzZ 9 kOQG߲ᡉ u !&_4^FtC+iDŽ(5/hֳC '+~7֪xfXx~`-+\12ׂSv^ssӜK%:du׋JĎ1{w|M,.|a5kGdM#(f*EX&'H,xm2?(J ϼ?"l<Ob1Cfu~8>5SOJx:;#nvYWbX"<Ȅm/h?2g%3H:t}2XҟvdxC]b F,_ϟu:sprIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_013_c.png000066400000000000000000000077551423156660700250730ustar00rootroot00000000000000PNG  IHDRllhˆbKGD#2 pHYsHHFk> vpAgll7|IDATx]A*; 4b#pb-X@U0oc)$oVp?w~6۱ ݗ~ܯ>w:=Î[ݵa֮{vM ]yEnp9.Ԧ%Rppmd1ކvZ]ݰv]=.>ayN_/J W +&T\=y sUoŢ6-Fv r/{{:aPc,>ű e y1m! nC~]wl3\瘖ClVZ> nNK]bo'.?c!l߀?Ɓg Z> n[O>b9fZg7q9cL**k ![pz"k#ҝba9b-vx*{6Ãslmk{tk#*?nXa#.aXb!kL{* GxOFzM-e `WXbC]\ɏt2% H> H,"+ n0:ϕ̕#g =hq˽5d{ q$e \ nO% rT&@Z0 rDtޥA7 L dO8@PӀ QG)a8t!7$ԣ0En"׊ ق )kMX@bx#ݎ4pp$d.˽7wט鞩W[ p&(Qނ. "+{.(x(į]Vf7 0;W!v峋x vPC5Qřz+3: rRSpp%rgOj=C65yʯ 2Smܛu%c DҷL];](@|J%ëkd nEA%Pt["tɐq$) n#QTגg$"BR?}gTg@ ‘b(`uD0tbbjYd&gϩ6ݡMsjcYSCW43iyz<|/Mpbr~)6*c@dxқ4^ZXk*mp\XBxdd*)ðY\> n%f60lP:cswE`FjdTp 5]A·ZwptD\|_wn?1Otʑ,*HCx"hpTPT% .b6ޣ ˦Ksv1۔C%ʙGv -Sܑ?[ n  F)SL.;}5 3Lu.s _Y-(XS4Y䙵]O;"+#:xv !әbQf n:~гF3 "U`P_t£X n\O?zp_Cpp u;i}*ғb1;ea7w&lj#y_&GE +ZppjHi}lkRz*mp_,8e%v:'UtE  iH i'|8338K]'jLtOC^P7 k7ܷ{ꞄԙbHv$'#Uۋc"墷&.(H`.KULRppi^h=j@BXV>H G77+-rgS:F.W>gG~ I@rgM,y-v{S"K9?-O U;1w,f ǬMK'+z}F3)aN7= $G̈́E 7 _K"sӧ" 2F@ʶb éNbQg#5vOR҆>zcL =t,7SgU>XE R?ߪ{5bxq2Nvi=DkVeUEk?Ǯ8n,,]x:#Ζ3q& >[pp ySq`. *Z@V~nA\ptf\&׊9x[; /Hxm{?k;R}+ 0GrI<ɑ- pN=`f($PC3*O?هG)! ?'BolX۠HD5҈Rn3 nӃ68P !㮝ϣ-1s686!d5 aUMyڈne]in#ԉTeHɬFC2-q8P]T!(605=|L(+<3Z0x@gnåjdi&lf yXթE艗EߏjD\CƤ yYZi#9M5o[7GՂ~C I3-dS:gTpX W%  :jX EH>n7 #bclb)Pm,`'y}~UpfBYJIENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_013_f.png000066400000000000000000000044621423156660700250660ustar00rootroot00000000000000PNG  IHDRp+bKGD#2 pHYsHHFk> vpAgIDATxA"K DXuH\ nRGe-3^8Ũa Frg:pFk_2~tES\gpovxǾtG堷?/r=[ҿ^??kŠLy{/S~jr[^86=W z-P#<7-K8¥ZVz]േ2>/Q sVeLOЦԮK{Z/޷|W lo=wT47=sDh_W^Y iY8(խRA?B_>bо>W_j5n*i ŠEŹs[T fzq--6kK"D%Xg@oگ}G\]&hV!|gE V)7轸{'K2'sn;,ű~lʳ!:P~+"˭|T= %.[TKYQC(88.O~:gdS ܟ%fmfz_~-U9 U!ٴBL?3IpL9Lu@Cѫ: LQQ*AW<gJA%#o^Ԓ33_ׂ~u.y=Y"%a uxxItTTP@FG:fZ2wUJ^7:P?iXb?&uCeWdDWP mUͅn :a[=QB.d+ՍKmO 4cw2JE :(٬ qG tpxOsT(^ Kcrf4?ʼnK#0%RhB!Zx0c@i٨hOݮ y::wZy&&-<()jOkWH'QE!q.}8\!o`箙Æ|˨Rzn=mѤC *P;*ՎFz`%NIOoa[߮KԁI*lPHi/.?X oXhMA|7"\݆JП[PF Kc#546Ye=pցccM ⪜5 ;gnBΏ%z7-]Q"yT*$LKyC\zR j_/'ZZN(Nf>lɶn% '}I}w>Us(5=$c<\5(pK86xo>Y͟6R`Yo.GԋAB֨kgí)B6P(\`!}wWf1/~qv: r6)sK*6_ Z ͸eXˎ )zknbPuwuMy}4ք#ȧШo~d{?t>|LL@׎MeO'.ܭ7Q(\c]2e{21٥n9]9kyOvH!h3[1*=ԲAmu,y@B jܩb|eG53527 P&.yb)(inySJ˧N{"Ӷ_@9$V/^RgVV$ǯՃ&oxyϕ!T|gqMN%vǸ鐻Wq2PIgfV*anl}.*M%/ =p}厚[]Fpy$r=yq)(l4=~_⑛;~9VHUط嘫 A1u1h68)Û=e_bOUGKRCA<7Sj7n+(%rv!.y'\ûVes-U!(}јBҶLoy\' Ɵqk1]jY_?=AA*zx&IENDB`libdmtx-0.7.7/test/compare_test/compare_siemens/siemens_013_t.png000066400000000000000000000044621423156660700251040ustar00rootroot00000000000000PNG  IHDRp+bKGD#2 pHYsHHFk> vpAgIDATxA"K DXuH\ nRGe-3^8Ũa Frg:pFk_2~tES\gpovxǾtG堷?/r=[ҿ^??kŠLy{/S~jr[^86=W z-P#<7-K8¥ZVz]േ2>/Q sVeLOЦԮK{Z/޷|W lo=wT47=sDh_W^Y iY8(խRA?B_>bо>W_j5n*i ŠEŹs[T fzq--6kK"D%Xg@oگ}G\]&hV!|gE V)7轸{'K2'sn;,ű~lʳ!:P~+"˭|T= %.[TKYQC(88.O~:gdS ܟ%fmfz_~-U9 U!ٴBL?3IpL9Lu@Cѫ: LQQ*AW<gJA%#o^Ԓ33_ׂ~u.y=Y"%a uxxItTTP@FG:fZ2wUJ^7:P?iXb?&uCeWdDWP mUͅn :a[=QB.d+ՍKmO 4cw2JE :(٬ qG tpxOsT(^ Kcrf4?ʼnK#0%RhB!Zx0c@i٨hOݮ y::wZy&&-<()jOkWH'QE!q.}8\!o`箙Æ|˨Rzn=mѤC *P;*ՎFz`%NIOoa[߮KԁI*lPHi/.?X oXhMA|7"\݆JП[PF Kc#546Ye=pցccM ⪜5 ;gnBΏ%z7-]Q"yT*$LKyC\zR j_/'ZZN(Nf>lɶn% '}I}w>Us(5=$c<\5(pK86xo>Y͟6R`Yo.GԋAB֨kgí)B6P(\`!}wWf1/~qv: r6)sK*6_ Z ͸eXˎ )zknbPuwuMy}4ք#ȧШo~d{?t>|LL@׎MeO'.ܭ7Q(\c]2e{21٥n9]9kyOvH!h3[1*=ԲAmu,y@B jܩb|eG53527 P&.yb)(inySJ˧N{"Ӷ_@9$V/^RgVV$ǯՃ&oxyϕ!T|gqMN%vǸ鐻Wq2PIgfV*anl}.*M%/ =p}厚[]Fpy$r=yq)(l4=~_⑛;~9VHUط嘫 A1u1h68)Û=e_bOUGKRCA<7Sj7n+(%rv!.y'\ûVes-U!(}јBҶLoy\' Ɵqk1]jY_?=AA*zx&IENDB`libdmtx-0.7.7/test/compare_test/input_messages/000077500000000000000000000000001423156660700216745ustar00rootroot00000000000000libdmtx-0.7.7/test/compare_test/input_messages/message_000.dat000066400000000000000000000000461423156660700243710ustar00rootroot00000000000000abcdefghijklmnopqrsABCDEFGHIJKLMNOPQRSlibdmtx-0.7.7/test/compare_test/input_messages/message_001.dat000066400000000000000000000000441423156660700243700ustar00rootroot00000000000000abcdefghijklmABCDEFGHIJKLM0123456789libdmtx-0.7.7/test/compare_test/input_messages/message_002.dat000066400000000000000000000010501423156660700243670ustar00rootroot00000000000000libdmtx is a shared library for Linux that can be used to read (scan & decode) and write (encode & print) 2D Data Matrix barcode symbols. It is released under the LGPL and can be used and distributed freely under these terms. Data Matrix barcodes are two-dimensional symbols that hold a dense pattern of data with built-in error correction. The Data Matrix symbology (sometimes referred to as DataMatrix) was invented and released into the public domain by RVSI Acuity CiMatrix. Wikipedia has a good article on the symbology and its characteristics.libdmtx-0.7.7/test/compare_test/input_messages/message_003.dat000066400000000000000000000001421423156660700243710ustar00rootroot00000000000000This test case contains newline charactes, including in the middle and at the end of the message. libdmtx-0.7.7/test/compare_test/input_messages/message_004.dat000066400000000000000000000000061423156660700243710ustar00rootroot00000000000000123456libdmtx-0.7.7/test/compare_test/input_messages/message_005.dat000066400000000000000000000000741423156660700243770ustar00rootroot00000000000000123456789012345678901234567890123456789012345678901234567890libdmtx-0.7.7/test/compare_test/input_messages/message_006.dat000066400000000000000000000000231423156660700243720ustar00rootroot0000000000000030Q324343430794<@<>;=>F@>Blibdmtx-0.7.7/test/compare_test/input_messages/message_053.dat000066400000000000000000000003711423156660700244020ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)(libdmtx-0.7.7/test/compare_test/input_messages/message_054.dat000066400000000000000000000003721423156660700244040ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('libdmtx-0.7.7/test/compare_test/input_messages/message_055.dat000066400000000000000000000007641423156660700244120ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('MNVOQN-0&$)&.,RS[46,,UVT51VW_5;6622YZb;;77]^g@@FF?@CFFEDBdfcdemGKCEKKjjsIIPMnoxRRUURSstruu~Y\wx^bz|yz{`_^\bc_aaeeb}~fdhhkmpoknoorryzxy~|~|}~燇⊊싈libdmtx-0.7.7/test/compare_test/input_messages/message_056.dat000066400000000000000000000017501423156660700244070ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('MNVOQN-0&$)&.,RS[46,,UVT51VW_5;6622YZb;;77]^g@@FF?@CFFEDBdfcdemGKCEKKjjsIIPMnoxRRUURSstruu~Y\wx^bz|yz{`_^\bc_aaeeb}~fdhhkmpoknoorryzxy~|~|}~燇⊊싈⏍ꏐ򐎤嗚񖗧򠣱饣ô檬ʺ󮬹ﱭνüҿ뼺! ,; H*\ȰÇ# FQŋ3j"Em׮Uɓ(S\ɲ%ˌ.MThR5iD4Rj҂ JѣH&fŞ?2uGiȪ 6 ٳhӪ]˶[d%Mbdc˦;WgWe-`ξ [ǐ#KL2^zi0f͋AVs6mV5K1Yװlibdmtx-0.7.7/test/compare_test/input_messages/message_057.dat000066400000000000000000000022601423156660700244050ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('MNVOQN-0&$)&.,RS[46,,UVT51VW_5;6622YZb;;77]^g@@FF?@CFFEDBdfcdemGKCEKKjjsIIPMnoxRRUURSstruu~Y\wx^bz|yz{`_^\bc_aaeeb}~fdhhkmpoknoorryzxy~|~|}~燇⊊싈⏍ꏐ򐎤嗚񖗧򠣱饣ô檬ʺ󮬹ﱭνüҿ뼺! ,; H*\ȰÇ# FQŋ3j"Em׮Uɓ(S\ɲ%ˌ.MThR5iD4Rj҂ JѣH&fŞ?2uGiȪ 6 ٳhӪ]˶[d%Mbdc˦;WgWe-`ξ [ǐ#KL2^zi0f͋AVs6mV5K1Yװcw~+b_Ӷ]$EkӔi}\J0ͼR!Mk{oaȡ^ }aӫ8y__h&gz=Xi8&h& " >`:_- M`XȟUZ?&#fц6& `8# (B!I8aB 0ӈtU]3MYXLlibdmtx-0.7.7/test/compare_test/input_messages/message_058.dat000066400000000000000000000030221423156660700244030ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('MNVOQN-0&$)&.,RS[46,,UVT51VW_5;6622YZb;;77]^g@@FF?@CFFEDBdfcdemGKCEKKjjsIIPMnoxRRUURSstruu~Y\wx^bz|yz{`_^\bc_aaeeb}~fdhhkmpoknoorryzxy~|~|}~燇⊊싈⏍ꏐ򐎤嗚񖗧򠣱饣ô檬ʺ󮬹ﱭνüҿ뼺! ,; H*\ȰÇ# FQŋ3j"Em׮Uɓ(S\ɲ%ˌ.MThR5iD4Rj҂ JѣH&fŞ?2uGiȪ 6 ٳhӪ]˶[d%Mbdc˦;WgWe-`ξ [ǐ#KL2^zi0f͋AVs6mV5K1Yװcw~+b_Ӷ]$EkӔi}\J0ͼR!Mk{oaȡ^ }aӫ8y__h&gz=Xi8&h& " >`:_- M`XȟUZ?&#fц6& `8# (B!I8aB 0ӈtU]3MYXLu4p# 1l馚"`%?٘ԙgcRN=)H`Ջcfً2UTJB5P8c, P6ӥK5Dʘf +fpE"9*B6VI*2\h(3L/ [\/kŅ4dL^zc谁 8Vɵhi ^y2v)B8L6n)+Jd YX::dC2(2ݔN<󎴰PmgO:` L6l(jkw(gCN 7#ӦIg dt 0sI1ϒ%ZɲM=aװ3Nl Э2Wlibdmtx-0.7.7/test/compare_test/input_messages/message_059.dat000066400000000000000000000030231423156660700244050ustar00rootroot00000000000000GIF89a;   !#!$"$!&$'%'$(&)*(,*,)-,/./-1/2 425342   648574 97:  <:= ><@<>;=>F@>BDDMDFCGEI""!)IJH''KKT$)('MNVOQN-0&$)&.,RS[46,,UVT51VW_5;6622YZb;;77]^g@@FF?@CFFEDBdfcdemGKCEKKjjsIIPMnoxRRUURSstruu~Y\wx^bz|yz{`_^\bc_aaeeb}~fdhhkmpoknoorryzxy~|~|}~燇⊊싈⏍ꏐ򐎤嗚񖗧򠣱饣ô檬ʺ󮬹ﱭνüҿ뼺! ,; H*\ȰÇ# FQŋ3j"Em׮Uɓ(S\ɲ%ˌ.MThR5iD4Rj҂ JѣH&fŞ?2uGiȪ 6 ٳhӪ]˶[d%Mbdc˦;WgWe-`ξ [ǐ#KL2^zi0f͋AVs6mV5K1Yװcw~+b_Ӷ]$EkӔi}\J0ͼR!Mk{oaȡ^ }aӫ8y__h&gz=Xi8&h& " >`:_- M`XȟUZ?&#fц6& `8# (B!I8aB 0ӈtU]3MYXLu4p# 1l馚"`%?٘ԙgcRN=)H`Ջcfً2UTJB5P8c, P6ӥK5Dʘf +fpE"9*B6VI*2\h(3L/ [\/kŅ4dL^zc谁 8Vɵhi ^y2v)B8L6n)+Jd YX::dC2(2ݔN<󎴰PmgO:` L6l(jkw(gCN 7#ӦIg dt 0sI1ϒ%ZɲM=aװ3Nl Э2Wlibdmtx-0.7.7/test/compare_test/input_messages/message_060.dat000066400000000000000000000000021423156660700243670ustar00rootroot00000000000000ABlibdmtx-0.7.7/test/multi_test/000077500000000000000000000000001423156660700163525ustar00rootroot00000000000000libdmtx-0.7.7/test/multi_test/Makefile.am000066400000000000000000000007251423156660700204120ustar00rootroot00000000000000AM_CPPFLAGS = -Wshadow -Wall -pedantic -std=c99 check_PROGRAMS = multi_test multi_test_SOURCES = dmtx.c dmtxaccel.c dmtxdecode2.c dmtxhough.c \ dmtxregion2.c dmtxsobel.c dmtxvaluegrid.c kiss_fft.c kiss_fftr.c \ multi_test.c multi_test.h visualize.c if TARGET_MACOSX multi_test_LDFLAGS = -lm -lSDL -lSDL_image -lSDL_gfx -lSDMmain -framework Cocoa -lpthread else multi_test_LDFLAGS = -lm -lSDL -lSDL_image -lSDL_gfx -lpthread -L/usr/local/lib -L/usr/lib/mingw endif libdmtx-0.7.7/test/multi_test/_kiss_fft_guts.h000066400000000000000000000132421423156660700215360ustar00rootroot00000000000000/* Copyright (c) 2003-2010, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* kiss_fft.h defines kiss_fft_scalar as either short or a float type and defines typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ #include "kiss_fft.h" #include #define MAXFACTORS 32 /* e.g. an fft of length 128 has 4 factors as far as kissfft is concerned 4*4*4*2 */ struct kiss_fft_state{ int nfft; int inverse; int factors[2*MAXFACTORS]; kiss_fft_cpx twiddles[1]; }; /* Explanation of macros dealing with complex math: C_MUL(m,a,b) : m = a*b C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise C_SUB( res, a,b) : res = a - b C_SUBFROM( res , a) : res -= a C_ADDTO( res , a) : res += a * */ #ifdef FIXED_POINT #if (FIXED_POINT==32) # define FRACBITS 31 # define SAMPPROD int64_t #define SAMP_MAX 2147483647 #else # define FRACBITS 15 # define SAMPPROD int32_t #define SAMP_MAX 32767 #endif #define SAMP_MIN -SAMP_MAX #if defined(CHECK_OVERFLOW) # define CHECK_OVERFLOW_OP(a,op,b) \ if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } #endif # define smul(a,b) ( (SAMPPROD)(a)*(b) ) # define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) # define S_MUL(a,b) sround( smul(a,b) ) # define C_MUL(m,a,b) \ do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) # define DIVSCALAR(x,k) \ (x) = sround( smul( x, SAMP_MAX/k ) ) # define C_FIXDIV(c,div) \ do { DIVSCALAR( (c).r , div); \ DIVSCALAR( (c).i , div); }while (0) # define C_MULBYSCALAR( c, s ) \ do{ (c).r = sround( smul( (c).r , s ) ) ;\ (c).i = sround( smul( (c).i , s ) ) ; }while(0) #else /* not FIXED_POINT*/ # define S_MUL(a,b) ( (a)*(b) ) #define C_MUL(m,a,b) \ do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) # define C_FIXDIV(c,div) /* NOOP */ # define C_MULBYSCALAR( c, s ) \ do{ (c).r *= (s);\ (c).i *= (s); }while(0) #endif #ifndef CHECK_OVERFLOW_OP # define CHECK_OVERFLOW_OP(a,op,b) /* noop */ #endif #define C_ADD( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,+,(b).r)\ CHECK_OVERFLOW_OP((a).i,+,(b).i)\ (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ }while(0) #define C_SUB( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,-,(b).r)\ CHECK_OVERFLOW_OP((a).i,-,(b).i)\ (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ }while(0) #define C_ADDTO( res , a)\ do { \ CHECK_OVERFLOW_OP((res).r,+,(a).r)\ CHECK_OVERFLOW_OP((res).i,+,(a).i)\ (res).r += (a).r; (res).i += (a).i;\ }while(0) #define C_SUBFROM( res , a)\ do {\ CHECK_OVERFLOW_OP((res).r,-,(a).r)\ CHECK_OVERFLOW_OP((res).i,-,(a).i)\ (res).r -= (a).r; (res).i -= (a).i; \ }while(0) #ifdef FIXED_POINT # define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) # define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) # define HALF_OF(x) ((x)>>1) #elif defined(USE_SIMD) # define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) # define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) # define HALF_OF(x) ((x)*_mm_set1_ps(.5)) #else # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) # define HALF_OF(x) ((x)*.5) #endif #define kf_cexp(x,phase) \ do{ \ (x)->r = KISS_FFT_COS(phase);\ (x)->i = KISS_FFT_SIN(phase);\ }while(0) /* a debugging function */ #define pcpx(c)\ fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) #ifdef KISS_FFT_USE_ALLOCA // define this to allow use of alloca instead of malloc for temporary buffers // Temporary buffers are used in two case: // 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 // 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. #include #define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) #define KISS_FFT_TMP_FREE(ptr) #else #define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) #define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) #endif libdmtx-0.7.7/test/multi_test/dmtx.c000066400000000000000000000000301423156660700174630ustar00rootroot00000000000000#include "../../dmtx.c" libdmtx-0.7.7/test/multi_test/dmtxaccel.c000066400000000000000000000073521423156660700204710ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxaccel.c */ #include #include #include #include "../../dmtx.h" #include "multi_test.h" /** * * */ DmtxAccel * AccelCreate(DmtxSobel *sobel) { int sWidth, sHeight; int vWidth, vHeight; int hWidth, hHeight; DmtxAccel *accel; accel = (DmtxAccel *)calloc(1, sizeof(DmtxAccel)); if(accel == NULL) return NULL; sWidth = dmtxValueGridGetWidth(sobel->v); sHeight = dmtxValueGridGetHeight(sobel->v); vWidth = sWidth - 1; vHeight = sHeight; hWidth = sWidth; hHeight = sHeight - 1; accel->vv = dmtxValueGridCreate(vWidth, vHeight, DmtxEdgeVertical, sobel->v); accel->vb = dmtxValueGridCreate(vWidth, vHeight, DmtxEdgeVertical, sobel->b); accel->vs = dmtxValueGridCreate(vWidth, vHeight, DmtxEdgeVertical, sobel->s); accel->hb = dmtxValueGridCreate(hWidth, hHeight, DmtxEdgeHorizontal, sobel->b); accel->hh = dmtxValueGridCreate(hWidth, hHeight, DmtxEdgeHorizontal, sobel->h); accel->hs = dmtxValueGridCreate(hWidth, hHeight, DmtxEdgeHorizontal, sobel->s); if(accel->vv == NULL || accel->vb == NULL || accel->vs == NULL || accel->hb == NULL || accel->hh == NULL || accel->hs == NULL) { AccelDestroy(&accel); return NULL; } return accel; } /** * * */ DmtxPassFail AccelDestroy(DmtxAccel **accel) { if(accel == NULL || *accel == NULL) return DmtxFail; dmtxValueGridDestroy(&((*accel)->vs)); dmtxValueGridDestroy(&((*accel)->hs)); dmtxValueGridDestroy(&((*accel)->hh)); dmtxValueGridDestroy(&((*accel)->hb)); dmtxValueGridDestroy(&((*accel)->vb)); dmtxValueGridDestroy(&((*accel)->vv)); free(*accel); *accel = NULL; return DmtxPass; } #define RETURN_FAIL_IF(c) if(c) { return DmtxFail; } /** * * */ DmtxPassFail AccelPopulate(DmtxDecode2 *dec) { DmtxAccel *accel; assert(dec != NULL && dec->accel != NULL); accel = dec->accel; RETURN_FAIL_IF(AccelPopulateLocal(accel->vv) == DmtxFail); RETURN_FAIL_IF(AccelPopulateLocal(accel->vb) == DmtxFail); RETURN_FAIL_IF(AccelPopulateLocal(accel->hb) == DmtxFail); RETURN_FAIL_IF(AccelPopulateLocal(accel->hh) == DmtxFail); RETURN_FAIL_IF(AccelPopulateLocal(accel->hs) == DmtxFail); RETURN_FAIL_IF(AccelPopulateLocal(accel->vs) == DmtxFail); dec->fn.dmtxValueGridCallback(accel->vv, 4); dec->fn.dmtxValueGridCallback(accel->vb, 5); dec->fn.dmtxValueGridCallback(accel->hb, 7); dec->fn.dmtxValueGridCallback(accel->hh, 8); dec->fn.dmtxValueGridCallback(accel->hs, 9); dec->fn.dmtxValueGridCallback(accel->vs, 6); return DmtxPass; } #undef RETURN_FAIL_IF /** * * */ DmtxPassFail AccelPopulateLocal(DmtxValueGrid *acc) { int sWidth, sHeight; int aWidth, aHeight; int sIdx, sIdxNext, sInc, aIdx; int x, y; DmtxValueGrid *sob; sob = acc->ref; sWidth = dmtxValueGridGetWidth(sob); sHeight = dmtxValueGridGetHeight(sob); switch(acc->type) { case DmtxEdgeVertical: aWidth = sWidth - 1; aHeight = sHeight; sInc = 1; break; case DmtxEdgeHorizontal: aWidth = sWidth; aHeight = sHeight - 1; sInc = sWidth; break; default: return DmtxFail; } for(y = 0; y < aHeight; y++) { sIdx = y * sWidth; aIdx = y * aWidth; for(x = 0; x < aWidth; x++) { sIdxNext = sIdx + sInc; acc->value[aIdx] = sob->value[sIdxNext] - sob->value[sIdx]; aIdx++; sIdx++; } } return DmtxPass; } libdmtx-0.7.7/test/multi_test/dmtxdecode2.c000066400000000000000000000133111423156660700207170ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxdecode2.c */ #include #include #include "../../dmtx.h" #include "multi_test.h" /** * * */ DmtxDecode2 * dmtxDecode2Create(DmtxImage *img) { DmtxDecode2 *dec; dec = (DmtxDecode2 *)calloc(1, sizeof(DmtxDecode2)); if(dec == NULL) return NULL; PopulateVanishBounds(dec); return dec; } /** * * */ DmtxPassFail dmtxDecode2Destroy(DmtxDecode2 **dec) { if(dec == NULL || *dec == NULL) return DmtxFail; decode2ReleaseCacheMemory(*dec); free(*dec); *dec = NULL; return DmtxPass; } #define RETURN_FAIL_IF(c) \ if(c) { \ decode2ReleaseCacheMemory(dec); \ return DmtxFail; \ } /** * * */ void PopulateVanishBounds(DmtxDecode2 *dec) { int d, phi; for(phi = 0; phi < 128; phi++) for(d = 0; d < 64; d++) dec->corners[d][phi] = GetVanishCorners(d, phi); } /** * * */ DmtxVanishCorners GetVanishCorners(int d, int phi) { DmtxVanishCorners vBound; DmtxVectorPair locs, dirs; int zone; int dFull, phiFull; double l, phiRad, bucketRad; DmtxVector2 v; dFull = d - 32; phiFull = (dFull < 0) ? phi + 128 : phi; assert(phiFull >= 0 && phiFull < 256); phiRad = phi * (M_PI/128.0); /* Infinity */ if(dFull == 0) { zone = GetZone(phiFull, NULL); locs = GetZoneCornerLocs(zone); dirs.a.X = dirs.b.X = cos(phiRad); /* XXX does phiRad point in this direction, or right angle? */ dirs.a.Y = dirs.b.Y = sin(phiRad); } else { bucketRad = abs(dFull) * (M_PI/96.0); l = 32/tan(bucketRad); zone = GetZone(phiFull, &l); locs = GetZoneCornerLocs(zone); v.X = l * cos(phiRad); v.Y = l * sin(phiRad); /* XXX remember phiRad may not point in direction you think */ dmtxVector2Sub(&dirs.a, &v, &locs.a); dmtxVector2Sub(&dirs.b, &v, &locs.b); dmtxVector2Norm(&dirs.a); /* I think this is necessary */ dmtxVector2Norm(&dirs.b); } vBound.zone = zone; vBound.lineA.p = locs.a; vBound.lineA.v = dirs.a; vBound.lineB.p = locs.b; vBound.lineB.v = dirs.b; return vBound; } /** * * */ int GetZone(int phiFull, double *distance) { int zone0 = 0; int zone1, zone2; double phiRad, xComp, yComp; if(phiFull < 32 || phiFull >= 224) zone1 = DmtxOctantTop; else if(phiFull < 96) zone1 = DmtxOctantLeft; else if(phiFull < 160) zone1 = DmtxOctantBottom; else zone1 = DmtxOctantRight; /* Orthagonal directions */ if(phiFull == 0 || phiFull == 64 || phiFull == 128 || phiFull == 196) return (distance != NULL && *distance < 32.0) ? zone0 : zone1; if(phiFull < 64) zone2 = DmtxOctantTopLeft; else if(phiFull < 128) zone2 = DmtxOctantBottomLeft; else if(phiFull < 192) zone2 = DmtxOctantBottomRight; else zone2 = DmtxOctantTopRight; /* Non-orthagonal vanishing point at infinity */ if(distance == NULL) return zone2; /* Must be a finite non-orthagonal vanishing point */ phiRad = phiFull * (M_PI/128.0); xComp = fabs(32.0/cos(phiRad)); /* remember phiRad may not point in direction you think */ yComp = fabs(32.0/sin(phiRad)); if(*distance > max(xComp,yComp)) return zone2; else if(*distance > min(xComp,yComp)) return zone1; return zone0; } /** * * */ DmtxVectorPair GetZoneCornerLocs(DmtxOctantType zone) { const DmtxVector2 p00 = { 0.0, 0.0 }; /* should be { -32.0, -32.0 } ? */ const DmtxVector2 p10 = { 1.0, 0.0 }; const DmtxVector2 p11 = { 1.0, 1.0 }; const DmtxVector2 p01 = { 0.0, 1.0 }; DmtxVectorPair locs; switch(zone) { case DmtxOctantTop: locs.a = p11; locs.b = p01; break; case DmtxOctantLeft: locs.a = p01; locs.b = p00; break; case DmtxOctantBottom: locs.a = p00; locs.b = p10; break; case DmtxOctantRight: locs.a = p10; locs.b = p11; break; case DmtxOctantTopLeft: case DmtxOctantBottomRight: locs.a = p00; locs.b = p11; break; case DmtxOctantBottomLeft: case DmtxOctantTopRight: default: /* XXX this feels wrong */ locs.a = p10; locs.b = p01; break; } return locs; } /** * * */ DmtxPassFail dmtxDecode2SetImage(DmtxDecode2 *dec, DmtxImage *img) { if(dec == NULL) return DmtxFail; dec->image = img; /* XXX decide here how big and how small to scale the image, and what level to go to */ /* store it in the decode struct */ /* Free existing buffers if sized incorrectly */ /* if(buffers are allocated but sized incorrectly) */ RETURN_FAIL_IF(decode2ReleaseCacheMemory(dec) == DmtxFail); /* Allocate new buffers if necessary */ /* if(buffers are not allocated) */ dec->sobel = SobelCreate(dec->image); RETURN_FAIL_IF(dec->sobel == NULL); dec->accel = AccelCreate(dec->sobel); RETURN_FAIL_IF(dec->accel == NULL); dec->hough = HoughCreate(1,1); RETURN_FAIL_IF(dec->hough == NULL); /* Necessary to zero out buffers? */ RETURN_FAIL_IF(SobelPopulate(dec) == DmtxFail); RETURN_FAIL_IF(AccelPopulate(dec) == DmtxFail); RETURN_FAIL_IF(HoughPopulate(dec) == DmtxFail); return DmtxPass; } #undef RETURN_FAIL_IF /** * * */ DmtxPassFail decode2ReleaseCacheMemory(DmtxDecode2 *dec) { if(dec == NULL) return DmtxFail; HoughDestroy(&(dec->hough)); AccelDestroy(&(dec->accel)); SobelDestroy(&(dec->sobel)); return DmtxPass; } libdmtx-0.7.7/test/multi_test/dmtxhough.c000066400000000000000000000314421423156660700205310ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxhough.c */ #include #include #include #include "multi_test.h" /** * * */ DmtxHough * HoughCreate(int cols, int rows) { DmtxHough *hough; hough = (DmtxHough *)calloc(1, sizeof(DmtxHough)); if(hough == NULL) return NULL; hough->cols = cols; hough->rows = rows; hough->count = hough->rows * hough->cols; hough->line = (DmtxHoughLocal *)calloc(hough->count, sizeof(DmtxHoughLocal)); hough->maxima = (DmtxHoughLocal *)calloc(hough->count, sizeof(DmtxHoughLocal)); hough->vanish = (DmtxHoughLocal *)calloc(hough->count, sizeof(DmtxHoughLocal)); if(hough->line == NULL || hough->maxima == NULL || hough->vanish == NULL) { HoughDestroy(&hough); return NULL; } return hough; } /** * * */ DmtxPassFail HoughDestroy(DmtxHough **grid) { if(grid == NULL || *grid == NULL) return DmtxFail; if((*grid)->vanish != NULL) free((*grid)->vanish); if((*grid)->maxima != NULL) free((*grid)->maxima); if((*grid)->line != NULL) free((*grid)->line); free(*grid); *grid = NULL; return DmtxPass; } #define RETURN_FAIL_IF(c) \ if(c) { \ HoughDestroy(&(dec->hough)); \ return DmtxFail; \ } /** * * */ DmtxPassFail HoughPopulate(DmtxDecode2 *dec) { int row, col, idx; DmtxHoughLocal *line, *maxima, *vanish; assert(dec->hough != NULL); for(row = 0; row < dec->hough->rows; row++) { for(col = 0; col < dec->hough->cols; col++) { idx = 0; /* will eventually be [hCol * width + hRol]; */ line = &(dec->hough->line[idx]); maxima = &(dec->hough->maxima[idx]); vanish = &(dec->hough->vanish[idx]); RETURN_FAIL_IF(LineHoughAccumulate(line, dec) == DmtxFail); dec->fn.dmtxHoughLocalCallback(line, 0); RETURN_FAIL_IF(MaximaHoughAccumulate(maxima, line, dec) == DmtxFail); dec->fn.dmtxHoughLocalCallback(maxima, 1); RETURN_FAIL_IF(VanishHoughAccumulate(vanish, maxima) == DmtxFail); dec->fn.dmtxHoughLocalCallback(vanish, 2); } } return DmtxPass; } #undef RETURN_FAIL_IF /** * Similar to HoughPopulate(), except starts from hough instead of sobel */ /* HoughMerge() { DmtxScanProgress newProgress; DmtxHough oldGrid, newGrid; oldGrid = dec->hough; // Merges raw hough grid into next larger grid newCols = (oldGrid->cols + 1)/2; newRows = (oldGrid->rows + 1)/2; newGrid = HoughCreate(newCols, newRows); if(newGrid == NULL) return DmtxFail; for(each new local) merge together info from appropriate old locals dec->hough = newGrid; // Destroy original hough data HoughDestroy(&(dec->hough)); return DmtxPass; } */ /** * * */ DmtxPassFail LineHoughAccumulate(DmtxHoughLocal *lhRegion, DmtxDecode2 *dec) { int rRow, rCol; int iRow, iCol; int iWidth, iHeight; int phi; ZeroCrossing vvZXing, vbZXing, hbZXing, hhZXing, hsZXing, vsZXing; DmtxAccel *accel; memset(lhRegion, 0x00, sizeof(DmtxHoughLocal)); assert(dec != NULL && dec->accel != NULL); accel = dec->accel; /* Global coordinate system */ iWidth = dmtxImageGetProp(dec->image, DmtxPropWidth); iHeight = dmtxImageGetProp(dec->image, DmtxPropHeight); lhRegion->xOrigin = gState.localOffsetX; lhRegion->yOrigin = gState.localOffsetY; /* calculate dOffset ? */ for(rRow = 0; rRow < 64; rRow++) { iRow = lhRegion->yOrigin + rRow; if(iRow >= iHeight) continue; for(rCol = 0; rCol < 64; rCol++) { iCol = lhRegion->xOrigin + rCol; if(iCol >= iWidth) continue; vvZXing = GetZeroCrossing(accel->vv, iCol, iRow); vbZXing = GetZeroCrossing(accel->vb, iCol, iRow); hbZXing = GetZeroCrossing(accel->hb, iCol, iRow); hhZXing = GetZeroCrossing(accel->hh, iCol, iRow); hsZXing = GetZeroCrossing(accel->hs, iCol, iRow); vsZXing = GetZeroCrossing(accel->vs, iCol, iRow); if(vvZXing.mag > 0) { if(gState.displayEdge == 1) dec->fn.zeroCrossingCallback(vvZXing, 0); for(phi = 0; phi < 16; phi++) HoughLocalAccumulateEdge(lhRegion, phi, vvZXing); for(phi = 112; phi < 128; phi++) HoughLocalAccumulateEdge(lhRegion, phi, vvZXing); } if(vbZXing.mag > 0) { if(gState.displayEdge == 2) dec->fn.zeroCrossingCallback(vbZXing, 0); for(phi = 16; phi < 32; phi++) HoughLocalAccumulateEdge(lhRegion, phi, vbZXing); } if(hbZXing.mag > 0) { if(gState.displayEdge == 3) dec->fn.zeroCrossingCallback(hbZXing, 0); for(phi = 32; phi < 48; phi++) HoughLocalAccumulateEdge(lhRegion, phi, hbZXing); } if(hhZXing.mag > 0) { if(gState.displayEdge == 4) dec->fn.zeroCrossingCallback(hhZXing, 0); for(phi = 48; phi < 80; phi++) HoughLocalAccumulateEdge(lhRegion, phi, hhZXing); } if(hsZXing.mag > 0) { if(gState.displayEdge == 5) dec->fn.zeroCrossingCallback(hsZXing, 0); for(phi = 80; phi < 96; phi++) HoughLocalAccumulateEdge(lhRegion, phi, hsZXing); } if(vsZXing.mag > 0) { if(gState.displayEdge == 6) dec->fn.zeroCrossingCallback(vsZXing, 0); for(phi = 96; phi < 112; phi++) HoughLocalAccumulateEdge(lhRegion, phi, vsZXing); } } } return DmtxPass; } /** * * * */ DmtxPassFail MaximaHoughAccumulate(DmtxHoughLocal *mhRegion, DmtxHoughLocal *lhRegion, DmtxDecode2 *dec) { int phi, d; for(phi = 0; phi < 128; phi++) for(d = 0; d < 64; d++) mhRegion->bucket[d][phi] = GetMaximaWeight(lhRegion, phi, d); return DmtxPass; } /** * * */ int GetMaximaWeight(DmtxHoughLocal *line, int phi, int d) { int val, valDn, valUp, valDnDn, valUpUp; int weight; val = line->bucket[d][phi]; valDn = (d >= 1) ? line->bucket[d - 1][phi] : 0; valUp = (d <= 62) ? line->bucket[d + 1][phi] : 0; /* Line is outranked by immediate neigbor in same direction (not a maxima) */ if(valDn > val || valUp > val) return 0; valDnDn = (d >= 2) ? line->bucket[d - 2][phi] : 0; valUpUp = (d <= 61) ? line->bucket[d + 2][phi] : 0; /* weight = (6 * val) - 2 * (valUp + valDn) - (valUpUp + valDnDn); */ /* weight = (5 * val) - 2 * (valUp + valDn) - (valUpUp + valDnDn); */ weight = (5 * val) - (valUp + valDn) - 2 * (valUpUp + valDnDn); /* weight = (3 * val) - (valUp + valDn + valUpUp + valDnDn); */ /* weight = (3 * val) - 2 * (valUp + valDn); */ return (weight > 0) ? weight : 0; } /** * * */ DmtxPassFail VanishHoughAccumulate(DmtxHoughLocal *vanish, DmtxHoughLocal *line) { int i, d, phi, val; int dLine, phiLine; int phi128, phiBeg, phiEnd; int dPrev; for(dLine = 0; dLine < 64; dLine++) { for(phiLine = 0; phiLine < 128; phiLine++) { val = line->bucket[dLine][phiLine]; if(val == 0) continue; phiBeg = phiLine - 20; phiEnd = phiLine + 20; dPrev = DmtxUndefined; for(phi = phiBeg; phi <= phiEnd; phi++) { phi128 = ((phi + 128) & 0x7f); d = GetVanishBucket(phi128, phiLine, dLine); if(d == DmtxUndefined) continue; /* Flip current d value if opposite range from phiLine */ if(phi < 0 || phi > 127) d = 63 - d; /* Flip previous d value if crossing max phi */ if(dPrev != DmtxUndefined && phi128 == 0) dPrev = 63 - dPrev; if(dPrev == DmtxUndefined || dPrev == d) { vanish->bucket[d][phi128] += val; } else if(dPrev < d) { for(i = dPrev + 1; i <= d; i++) vanish->bucket[i][phi128] += val; } else { for(i = dPrev - 1; i >= d; i--) vanish->bucket[i][phi128] += val; } dPrev = d; } } } return DmtxPass; } /** * * */ int GetVanishBucket(int phiBucket, int phiCompare, int dCompare) { int bucket; int phiDelta; double d, u, x; double bucketRad, phiDeltaRad, phiCompareRad; double bucketF; if(phiBucket == phiCompare) return 32; /* Infinity */ phiDelta = phiCompare - phiBucket; if(phiDelta < -64) phiDelta += 128; else if(phiDelta > 64) phiDelta -= 128; phiCompareRad = phiCompare * (M_PI/128.0); phiDeltaRad = phiDelta * (M_PI/128.0); d = UncompactOffset(dCompare, phiCompare, 64); u = 32.0 * (cos(phiCompareRad) + sin(phiCompareRad)); x = fabs((d - u)/sin(phiDeltaRad)); if(x < 0.0001) return DmtxUndefined; bucketRad = atan(32.0/x); assert(bucketRad > 0.0); /* map 0 -> pi/2 to 0 -> 64 */ bucketF = bucketRad * (96.0/M_PI); bucket = (bucketF > 0.0) ? (int)(bucketF + 0.5) : (int)(bucketF - 0.5); if(phiDelta * (d - u) < 0.0) bucket = -bucket; bucket += 32; if(bucket < 0) bucket = DmtxUndefined; else if(bucket > 63) bucket = DmtxUndefined; return bucket; } /** * * */ ZeroCrossing GetZeroCrossing(DmtxValueGrid *accel, int iCol, int iRow) { int aInc, aIdx, aIdxNext; int aRow, aCol; int aWidth, aHeight; int aHere, aNext, aPrev; double smidge; const ZeroCrossing emptyEdge = { 0, 0, 0, 0.0, 0.0 }; ZeroCrossing edge; assert(accel->type == DmtxEdgeVertical || accel->type == DmtxEdgeHorizontal); aWidth = dmtxValueGridGetWidth(accel); aHeight = dmtxValueGridGetHeight(accel); /* XXX add better bounds checking of aIdxNext now that we're comparing diagonals */ if(accel->type == DmtxEdgeVertical) { aRow = iRow - 1; aCol = iCol - 2; aInc = 1; } else { /* DmtxEdgeHorizontal */ aRow = iRow - 2; aCol = iCol - 1; aInc = aWidth; } aIdx = aRow * aWidth + aCol; aIdxNext = aIdx + aInc; aHere = accel->value[aIdx]; aNext = accel->value[aIdxNext]; edge = emptyEdge; if(OPPOSITE_SIGNS(aHere, aNext)) { /* Zero crossing: Neighbors with opposite signs [-10,+10] */ smidge = abs(aHere/(aHere - aNext)); edge = SetZeroCrossingFromIndex(accel, iCol, iRow, smidge); } else if(aHere == 0 && aNext != 0) { if(!(accel->type == DmtxEdgeVertical && aCol == 0) && !(accel->type == DmtxEdgeHorizontal && aRow == 0)) { aPrev = accel->value[aIdx-aInc]; if(OPPOSITE_SIGNS(aPrev, aNext)) { /* Zero crossing: Opposite signs separated by zero [-10,0,+10] */ smidge = 0.0; edge = SetZeroCrossingFromIndex(accel, iCol, iRow, smidge); } } } return edge; } /** * 0 < smidge < 1 * */ ZeroCrossing SetZeroCrossingFromIndex(DmtxValueGrid *accel, int iCol, int iRow, double smidge) { int sCol, sRow, sIdx; ZeroCrossing edge; DmtxValueGrid *sobel = accel->ref; edge.iCol = iCol; edge.iRow = iRow; if(accel->type == DmtxEdgeVertical) { edge.x = (double)iCol + smidge; edge.y = (double)iRow + 0.5; } else /* DmtxEdgeHorizontal */ { edge.x = (double)iCol + 0.5; edge.y = (double)iRow + smidge; } sRow = iRow - 1; sCol = iCol - 1; if(sCol < 0 || sCol >= sobel->width || sRow < 0 || sRow >= sobel->height) { /* Sobel location out of bounds */ edge.mag = 0; } else { /* Use Sobel value that falls directly between 2 accel locations */ sIdx = sRow * dmtxValueGridGetWidth(sobel) + sCol; edge.mag = abs(sobel->value[sIdx]); } return edge; } /** * * */ DmtxPassFail HoughLocalAccumulateEdge(DmtxHoughLocal *line, int phi, ZeroCrossing edge) { double d; int dInt; d = HoughGetLocalOffset(edge.x - line->xOrigin, edge.y - line->yOrigin, phi); dInt = (int)d; assert(dInt >= 0 && dInt < 64); assert(phi >= 0 && phi < 128); line->bucket[dInt][phi] += edge.mag; return DmtxPass; } /** * * */ double HoughGetLocalOffset(double xLoc, double yLoc, int phi) { double phiRad, sinPhi, cosPhi; double scale, d; phiRad = (phi * M_PI)/128.0; sinPhi = sin(phiRad); cosPhi = cos(phiRad); if(phi <= 64) { scale = 1.0 / (sinPhi + cosPhi); d = (xLoc * cosPhi + yLoc * sinPhi) * scale; } else { scale = 1.0 / (sinPhi - cosPhi); d = ((xLoc * cosPhi + yLoc * sinPhi) - (cosPhi * 64.0)) * scale; } return d; } libdmtx-0.7.7/test/multi_test/dmtxregion2.c000066400000000000000000001033211423156660700207600ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxregion2.c */ #include #include #include #include "../../dmtx.h" #include "multi_test.h" #include "kiss_fftr.h" #define RETURN_FAIL_IF(c) if(c) { return DmtxFail; } /* struct Deskew { DmtxMatrix3 fit2raw; DmtxMatrix3 raw2fit; } struct Timing { double shift; double period; } struct AlignmentGrid { Deskew align; Timing vTiming; Timing hTiming; } */ /** * * */ DmtxPassFail dmtxRegion2FindNext(DmtxDecode2 *dec) { int i, j; int phiDiff, phiDiffTmp; DmtxBoolean regionFound; DmtxPassFail passFail; VanishPointSort vPoints; DmtxOrient orient; vPoints = dmtxFindVanishPoints(dec->hough->vanish); dec->fn.vanishPointCallback(&vPoints, 0); for(i = 0, regionFound = DmtxFalse; i < vPoints.count && regionFound == DmtxFalse; i++) { for(j = i + 1; j < vPoints.count; j++) { phiDiffTmp = abs(vPoints.bucket[i].phi - vPoints.bucket[j].phi); phiDiff = (phiDiffTmp < 64) ? phiDiffTmp : 128 - phiDiffTmp; /* Reject angle combinations that are too close */ if(phiDiff < 36) continue; /* Build oriented (but still untimed) grid from vanish points */ passFail = OrientRegion(&orient, vPoints.bucket[i], vPoints.bucket[j], dec); if(passFail == DmtxFail) continue; /* Build timed grid from untimed grid and line hough */ /* align = CalibrateRegion(region, orient, lHough, &passFail); if(passFail == DmtxFail) continue; */ /* // timings = dmtxFindGridTiming(dec->hough->line, &vPoints); err = dmtxBuildGridFromTimings(&grid, timings.timing[i], timings.timing[j]); if(err == DmtxFail) continue; // Keep trying dec->fn.timingCallback(&timings.timing[i], &timings.timing[j], 1); // Hack together raw2fitFull and fit2rawFull outside since we need app data AddFullTransforms(&grid); err = dmtxFindRegionWithinGrid(®ion, &grid, &houghCache, dec, fn); regionFound = (err == DmtxPass) ? DmtxTrue : DmtxFalse; if(regionFound == DmtxTrue) { region.sizeIdx = dmtxGetSizeIdx(region.width, region.height); if(region.sizeIdx >= DmtxSymbol10x10 && region.sizeIdx <= DmtxSymbol16x48) dmtxDecodeSymbol(®ion, dec); } regionFound = DmtxTrue; // break out of outer loop break; // break out of inner loop */ } } return DmtxPass; } /** * * */ DmtxPassFail OrientRegion(DmtxOrient *orient, DmtxHoughBucket v0, DmtxHoughBucket v1, DmtxDecode2 *dec) { DmtxRay2 v0a, v0b, v1a, v1b; v0a = dec->corners[v0.d][v0.phi].lineA; v0b = dec->corners[v0.d][v0.phi].lineB; v1a = dec->corners[v1.d][v1.phi].lineA; v1b = dec->corners[v1.d][v1.phi].lineB; /* RegionFromSides(v0a, v0b, v1a, v1b); */ return DmtxPass; } /** * * */ /* DmtxPassFail CalibrateRegion(DmtxOrient *orient) { input: orientation (transformation matrix) hough line cache output: timed alignment grid description: fourier transform receives evenly spaced samples to be taken in both directions (v and h) -> tranforms into frequency space -> major frequency emerges determine shift as post-processing step (can't remember how I did it before at the moment) need to interpolate values between true locations in line hough because "evenly spaced" describes positions in the normalized fitted region whereas the hough values are aligned along raw image coordinates step size should be determined based on desired fft dimensions (maybe 64 steps?) ... check what we did for poc steps: for each step 0-63 upward along Y axis xFit = 0 yFit = 1-63 xRaw,yRaw = VMult(fit2raw,xFit,yFit) lineFit = (xRaw - 0, yRaw - 0) (duh) phi = lineFit angle (something like atan2) d = vector2Mag(lineFit) interpolate hough[d][phi] and add to fft that was easy return DmtxPass; } */ /** * Future structure of dmtxRegion2FindNext() (after getting orientation and timing working again) * * TODO: * o Is there a way to name HoughGridPopulate() and HoughGridMerge() to show they are sister functions? * * hough lines, hough line maxima, and hough vanish can be calculated for entire image at once * for each local: * for each combination of vpoint pairs: * step outward * if step takes us into adjacent local * add maxima from adjacent local into existing vpoint hough accumulator * rescan for updated vpoints (using original centerline, adding only portion of region?) * get new timing (?) * continue stepping path where we left off? * * progress: * level * row * col * vanish point combo (?) */ /* dmtxRegion2FindNext(DmtxDecode2 *dec) { if(starting new level other than the first) HoughGridMerge(); // for each new local while(dec->localIdx < dec->localCount) { // each valid combination of vanish points for(dec->vPointIdx < 28) { vPointI = xyz; vPointJ = xyz; while(perimeter condition not met) { stepOutward() if(first step in new local, including first) { add new region to central vanish hough update vanish points (shouldn't move too far) record that new local was added to stats redo timing } test perimeter for known strip patterns if(perimeter says that barcode region is found) { save current progress ("?") return region; } } } } return NULL; } */ /** * * */ double UncompactOffset(double compactedOffset, int phiIdx, int extent) { double phiRad; double scale; double posMax, negMax; phiRad = M_PI * phiIdx / 128.0; if(phiIdx < 64) { posMax = extent * (cos(phiRad) + sin(phiRad)); negMax = 0.0; } else { posMax = extent * sin(phiRad); negMax = extent * cos(phiRad); } assert(extent > 0); scale = (posMax - negMax) / extent; return (compactedOffset * scale) + negMax; } /** * * */ void AddToVanishPointSort(VanishPointSort *sort, DmtxHoughBucket bucket) { int i, startHere; int phiDiff, phiDiffTmp; DmtxBoolean isFull; DmtxHoughBucket *lastBucket; isFull = (sort->count == ANGLE_SORT_MAX_COUNT) ? DmtxTrue : DmtxFalse; lastBucket = &(sort->bucket[ANGLE_SORT_MAX_COUNT - 1]); /* Array is full and incoming bucket is already weakest */ if(isFull && bucket.val < lastBucket->val) return; startHere = DmtxUndefined; /* If sort already has entry near this angle then either: * a) Overwrite the old one without shifting if stronger * b) Reject the new one completely if weaker */ for(i = 0; i < sort->count; i++) { phiDiffTmp = abs(bucket.phi - sort->bucket[i].phi); phiDiff = (phiDiffTmp < 64) ? phiDiffTmp : 128 - phiDiffTmp; if(phiDiff < 10) { /* Similar angle is already represented with stronger magnitude */ if(bucket.val < sort->bucket[i].val) { return; } /* Found similar-but-weaker angle that will be overwritten */ else { sort->bucket[i] = bucket; startHere = i; break; } } } if(startHere == DmtxUndefined) { if(isFull) *lastBucket = bucket; else sort->bucket[sort->count++] = bucket; startHere = sort->count - 1; } /* Shift weak entries downward */ for(i = startHere; i > 0; i--) { if(bucket.val > sort->bucket[i-1].val) { sort->bucket[i] = sort->bucket[i-1]; sort->bucket[i-1] = bucket; } else { break; } } } /** * * */ VanishPointSort dmtxFindVanishPoints(DmtxHoughLocal *vHough) { DmtxHoughBucket bucket; VanishPointSort sort; memset(&sort, 0x00, sizeof(VanishPointSort)); for(bucket.phi = 0; bucket.phi < 128; bucket.phi++) { for(bucket.d = 0; bucket.d < 64; bucket.d++) { bucket.val = vHough->bucket[bucket.d][bucket.phi]; AddToVanishPointSort(&sort, bucket); } } return sort; } /** * * */ void AddToMaximaSort(HoughMaximaSort *sort, int maximaMag) { int i; /* If new entry would be weakest (or only) one in list, then append */ if(sort->count == 0 || maximaMag < sort->mag[sort->count - 1]) { if(sort->count + 1 < MAXIMA_SORT_MAX_COUNT) sort->mag[sort->count++] = maximaMag; return; } /* Otherwise shift the weaker entries downward */ for(i = sort->count - 1; i >= 0; i--) { if(maximaMag > sort->mag[i]) { if(i + 1 < MAXIMA_SORT_MAX_COUNT) sort->mag[i+1] = sort->mag[i]; sort->mag[i] = maximaMag; } } if(sort->count < MAXIMA_SORT_MAX_COUNT) sort->count++; } /** * Return sum of top 8 maximum points (hmmm) * btw, can we skip this step entirely? */ DmtxHoughBucket GetAngleSumAtPhi(DmtxHoughLocal *line, int phi) { int i, d; int prev, here, next; DmtxHoughBucket bucket; HoughMaximaSort sort; memset(&sort, 0x00, sizeof(HoughMaximaSort)); /* Handle last condition separately; one sided comparison */ prev = line->bucket[62][phi]; here = line->bucket[63][phi]; if(here > prev) AddToMaximaSort(&sort, here); /* Handle first condition separately; one sided comparison */ here = line->bucket[0][phi]; next = line->bucket[1][phi]; if(here > next) AddToMaximaSort(&sort, here); /* Handle remaining conditions as two sided comparisons */ for(d = 2; d < 64; d++) { prev = here; here = next; next = line->bucket[d][phi]; if(here > 0 && here >= prev && here >= next) AddToMaximaSort(&sort, here); } bucket.d = 0; bucket.phi = phi; bucket.val = 0; for(i = 0; i < 8; i++) bucket.val += sort.mag[i]; return bucket; } /** * * */ void AddToTimingSort(DmtxTimingSort *sort, Timing timing) { int i; if(timing.mag < 1.0) /* XXX or some minimum threshold */ return; /* If new entry would be weakest (or only) one in list, then append */ if(sort->count == 0 || timing.mag < sort->timing[sort->count - 1].mag) { if(sort->count + 1 < TIMING_SORT_MAX_COUNT) sort->timing[sort->count++] = timing; return; } /* Otherwise shift the weaker entries downward */ for(i = sort->count - 1; i >= 0; i--) { if(timing.mag > sort->timing[i].mag) { if(i + 1 < TIMING_SORT_MAX_COUNT) sort->timing[i+1] = sort->timing[i]; sort->timing[i] = timing; } } if(sort->count < TIMING_SORT_MAX_COUNT) sort->count++; } /** * * */ DmtxTimingSort dmtxFindGridTiming(DmtxHoughLocal *line, VanishPointSort *vPoints) { int x, y, fitMag, fitMax, fitOff, attempts, iter; int i, vSortIdx, phi; kiss_fftr_cfg cfg = NULL; kiss_fft_scalar rin[NFFT]; kiss_fft_cpx sout[NFFT/2+1]; kiss_fft_scalar mag[NFFT/2+1]; int maxIdx; Timing timing; DmtxTimingSort timings; memset(&timings, 0x00, sizeof(DmtxTimingSort)); for(vSortIdx = 0; vSortIdx < vPoints->count; vSortIdx++) { phi = vPoints->bucket[vSortIdx].phi; /* Load FFT input array */ for(i = 0; i < NFFT; i++) { rin[i] = (i < 64) ? line->bucket[i][phi] : 0; } /* Execute FFT */ memset(sout, 0x00, sizeof(kiss_fft_cpx) * (NFFT/2 + 1)); cfg = kiss_fftr_alloc(NFFT, 0, 0, 0); kiss_fftr(cfg, rin, sout); free(cfg); /* Select best result */ maxIdx = NFFT/9-1; for(i = 0; i < NFFT/9-1; i++) mag[i] = 0.0; for(i = NFFT/9-1; i < NFFT/2+1; i++) { mag[i] = sout[i].r * sout[i].r + sout[i].i * sout[i].i; if(mag[i] > mag[maxIdx]) maxIdx = i; } timing.phi = phi; timing.period = NFFT / (double)maxIdx; timing.mag = mag[maxIdx]; /* Find best offset */ fitOff = fitMax = 0; attempts = (int)timing.period + 1; for(x = 0; x < attempts; x++) { fitMag = 0; for(iter = 0; ; iter++) { y = x + (int)(iter * timing.period); if(y >= 64) break; fitMag += line->bucket[y][timing.phi]; } if(x == 0 || fitMag > fitMax) { fitMax = fitMag; fitOff = x; } } timing.shift = fitOff; AddToTimingSort(&timings, timing); } return timings; } /** * * */ DmtxRay2 HoughCompactToRay(int phi, double d) { double phiRad; double dScaled; DmtxRay2 rStart, rLine; memset(&rStart, 0x00, sizeof(DmtxRay2)); memset(&rLine, 0x00, sizeof(DmtxRay2)); rStart.p.X = rStart.p.Y = 0.0; phiRad = phi * M_PI/128.0; rStart.v.X = cos(phiRad); rStart.v.Y = sin(phiRad); rLine.v.X = -rStart.v.Y; rLine.v.Y = rStart.v.X; dScaled = UncompactOffset(d, phi, LOCAL_SIZE); dmtxPointAlongRay2(&(rLine.p), &rStart, dScaled); return rLine; } /** * * * */ DmtxPassFail dmtxBuildGridFromTimings(AlignmentGrid *grid, Timing vp0, Timing vp1) { RegionLines rl0, rl1, *flat, *steep; DmtxVector2 p00, p10, p11, p01; DmtxMatrix3 fit2raw, raw2fit, mScale; /* (1) -- later compare all possible combinations for strongest pair */ rl0.timing = vp0; rl0.gridCount = (int)((64.0 - vp0.shift)/vp0.period); rl0.dA = vp0.shift; rl0.dB = vp0.shift + vp0.period * rl0.gridCount; /* doesn't work but whatever */ rl1.timing = vp1; rl1.gridCount = (int)((64.0 - vp1.shift)/vp1.period); rl1.dA = vp1.shift; rl1.dB = vp1.shift + vp1.period * rl1.gridCount; /* doesn't work but whatever */ /* flat[0] is the bottom flat line */ /* flat[1] is the top flat line */ /* steep[0] is the left steep line */ /* steep[1] is the right line */ /* Line with angle closest to horizontal is flatest */ if(abs(64 - rl0.timing.phi) < abs(64 - rl1.timing.phi)) { flat = &rl0; steep = &rl1; } else { flat = &rl1; steep = &rl0; } flat->line[0] = HoughCompactToRay(flat->timing.phi, flat->dA); flat->line[1] = HoughCompactToRay(flat->timing.phi, flat->dB); if(steep->timing.phi < 64) { steep->line[0] = HoughCompactToRay(steep->timing.phi, steep->dA); steep->line[1] = HoughCompactToRay(steep->timing.phi, steep->dB); } else { steep->line[0] = HoughCompactToRay(steep->timing.phi, steep->dB); steep->line[1] = HoughCompactToRay(steep->timing.phi, steep->dA); } RETURN_FAIL_IF(dmtxRay2Intersect(&p00, &(flat->line[0]), &(steep->line[0])) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p10, &(flat->line[0]), &(steep->line[1])) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p11, &(flat->line[1]), &(steep->line[1])) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p01, &(flat->line[1]), &(steep->line[0])) == DmtxFail); RETURN_FAIL_IF(RegionUpdateCorners(fit2raw, raw2fit, p00, p10, p11, p01) == DmtxFail); grid->rowCount = flat->gridCount; grid->colCount = steep->gridCount; /* raw2fit: Final transformation fits single origin module */ dmtxMatrix3Identity(mScale); dmtxMatrix3Multiply(grid->raw2fitActive, raw2fit, mScale); /* fit2raw: Abstract away display nuances of multi_test application */ dmtxMatrix3Identity(mScale); dmtxMatrix3Multiply(grid->fit2rawActive, mScale, fit2raw); return DmtxPass; } /** * * */ StripStats GenStripPatternStats(unsigned char *strip, int stripLength, int startState, int contrast) { int i, jumpAmount, jumpThreshold; int finderLow, finderHigh, timingLow, timingHigh; int surpriseCount, jumpCount, contrastSum; int newState, currentState; StripStats stats; assert(startState == MODULE_HIGH || startState == MODULE_LOW); jumpThreshold = (contrast*40)/100; finderLow = finderHigh = timingLow = timingHigh = 0; surpriseCount = jumpCount = contrastSum = 0; currentState = startState; memset(&stats, 0x00, sizeof(StripStats)); for(i = 0; i < stripLength; i++) { if(i > 0) { jumpAmount = strip[i] - strip[i-1]; /* Tally jump statistics if occurred */ if(abs(jumpAmount) > jumpThreshold) { jumpCount++; contrastSum += abs(jumpAmount); newState = (jumpAmount > 0) ? MODULE_HIGH : MODULE_LOW; /* Surprise! We jumped but landed in the same state */ if(newState == currentState) surpriseCount++; else currentState = newState; } } /* Increment appropriate finder pattern */ if(currentState == MODULE_HIGH) finderHigh++; else finderLow++; /* Increment appropriate timing pattern */ if(currentState ^ (i & 0x01)) timingHigh++; else timingLow++; } stats.jumps = jumpCount; stats.surprises = surpriseCount; stats.finderErrors = (finderHigh < finderLow) ? finderHigh : finderLow; stats.timingErrors = (timingHigh < timingLow) ? timingHigh : timingLow; if(jumpCount > 0) { stats.contrast = (int)((double)contrastSum/jumpCount + 0.5); stats.finderBest = (finderHigh > finderLow) ? MODULE_HIGH : MODULE_LOW; stats.timingBest = (timingHigh > timingLow) ? MODULE_HIGH : MODULE_LOW; } else { stats.contrast = 0; stats.finderBest = MODULE_UNKNOWN; stats.timingBest = MODULE_UNKNOWN; } return stats; } /** * * */ DmtxPassFail dmtxFindRegionWithinGrid(GridRegion *region, AlignmentGrid *grid, DmtxHoughLocal *line, DmtxDecode *dec, DmtxCallbacks *fn) { int goodCount; int finderSides; DmtxDirection sideDir; DmtxBarType innerType, outerType; GridRegion regGrow; memset(®Grow, 0x00, sizeof(GridRegion)); regGrow.grid = *grid; /* Capture local copy of grid for tweaking */ regGrow.x = regGrow.grid.colCount / 2; regGrow.y = regGrow.grid.rowCount / 2; regGrow.width = 2; regGrow.height = 2; regGrow.sizeIdx = DmtxUndefined; regGrow.onColor = regGrow.offColor = 0; regGrow.contrast = 20; /* low initial value */ /* Assume the starting region will be far enough away from any side that * the expansion rules won't need to work at the very smallest sizes */ /* Grow region */ finderSides = DmtxDirNone; for(goodCount = 0, sideDir = DmtxDirDown; goodCount < 4; sideDir = RotateCW(sideDir)) { if(regGrow.width > 26 || regGrow.height > 26) return DmtxFail; innerType = TestSideForPattern(®Grow, dec->image, sideDir, 0); outerType = TestSideForPattern(®Grow, dec->image, sideDir, 1); /** * Make smarter ... maybe: * 1) Check that next-farther out strips are consistent (easy) * 2) Check that the colors are all consistent (not as easy) */ if(innerType == DmtxBarNone || outerType == DmtxBarNone) { /* RegionExpand(®Grow, sideDir, line, fn); */ finderSides = DmtxDirNone; goodCount = 0; } else { if(innerType == DmtxBarFinder) finderSides |= sideDir; goodCount++; } /* fn->perimeterCallback(®Grow, sideDir, innerType); */ } regGrow.finderSides = finderSides; *region = regGrow; return DmtxPass; } /** * * */ int dmtxReadModuleColor(DmtxImage *img, AlignmentGrid *grid, int symbolRow, int symbolCol, int colorPlane) { int err; int i; int color, colorTmp; double sampleX[] = { 0.5, 0.4, 0.5, 0.6, 0.5 }; double sampleY[] = { 0.5, 0.5, 0.4, 0.5, 0.6 }; DmtxVector2 p; color = 0; for(i = 0; i < 5; i++) { p.X = (1.0/grid->colCount) * (symbolCol + sampleX[i]); p.Y = (1.0/grid->rowCount) * (symbolRow + sampleY[i]); dmtxMatrix3VMultiplyBy(&p, grid->fit2rawFull); /* Should use dmtxDecodeGetPixelValue() later to properly handle pixel skipping */ err = dmtxImageGetPixelValue(img, p.X, p.Y, colorPlane, &colorTmp); if(err == DmtxFail) return 0; color += colorTmp; } return color/5; } /** * * */ DmtxBarType TestSideForPattern(GridRegion *region, DmtxImage *img, DmtxDirection side, int offset) { int i; int col, colBeg; int row, rowBeg; int extent; unsigned char colorStrip[26] = { 0 }; StripStats *stats, statsHigh, statsLow; assert(region->width <= 26 && region->height <= 26); assert(side == DmtxDirUp || side == DmtxDirLeft || side == DmtxDirDown || side == DmtxDirRight); /* Note opposite meaning (DmtxDirUp means "top", extent is horizontal) */ extent = (side & DmtxDirVertical) ? region->width : region->height; if(extent < 3) return DmtxBarNone; switch(side) { case DmtxDirUp: colBeg = region->x; rowBeg = (region->y + region->height - 1) + offset; break; case DmtxDirLeft: colBeg = region->x - offset; rowBeg = region->y; break; case DmtxDirDown: colBeg = region->x; rowBeg = region->y - offset; break; case DmtxDirRight: colBeg = (region->x + region->width - 1) + offset; rowBeg = region->y; break; default: return DmtxUndefined; } /* Sample and hold colors at each module along both inner and outer edges */ col = colBeg; row = rowBeg; for(i = 0; i < extent; i++) { colorStrip[i] = dmtxReadModuleColor(img, &(region->grid), row, col, 0); if(side & DmtxDirVertical) col++; else row++; } statsHigh = GenStripPatternStats(colorStrip, extent, MODULE_HIGH, region->contrast); statsLow = GenStripPatternStats(colorStrip, extent, MODULE_LOW, region->contrast); stats = (statsHigh.surprises > statsLow.surprises) ? &statsLow : &statsHigh; if(stats->contrast > region->contrast) { region->contrast = stats->contrast; } if(stats->finderErrors < stats->timingErrors) { if(stats->finderErrors < 2) { return DmtxBarFinder; } } else if(stats->finderErrors > stats->timingErrors) { if(stats->timingErrors < 2) { return DmtxBarTiming; } } return DmtxBarNone; } #define DmtxAlmostZero 0.000001 /** * * */ DmtxPassFail Vector2Norm(DmtxVector2 *v) { double mag; mag = dmtxVector2Mag(v); if(mag <= DmtxAlmostZero) return DmtxFail; dmtxVector2ScaleBy(v, 1/mag); return DmtxTrue; } /** * * */ DmtxPassFail RayFromPoints(DmtxRay2 *ray, DmtxVector2 p0, DmtxVector2 p1) { DmtxRay2 r; r.tMin = 0.0; r.tMax = 1.0; r.p = p0; dmtxVector2Sub(&r.v, &p1, &p0); RETURN_FAIL_IF(Vector2Norm(&r.v) == DmtxFail); *ray = r; return DmtxPass; } /** * Not a generic function. Notice that it uses fit2rawActive. * */ DmtxPassFail dmtxRegionToSides(GridRegion *region, DmtxRegionSides *regionSides) { DmtxVector2 p00, p10, p11, p01; DmtxRegionSides rs; p00.X = p01.X = region->x * (1.0/region->grid.colCount); p10.X = p11.X = (region->x + region->width) * (1.0/region->grid.colCount); p00.Y = p10.Y = region->y * (1.0/region->grid.rowCount); p01.Y = p11.Y = (region->y + region->height) * (1.0/region->grid.rowCount); dmtxMatrix3VMultiplyBy(&p00, region->grid.fit2rawActive); dmtxMatrix3VMultiplyBy(&p10, region->grid.fit2rawActive); dmtxMatrix3VMultiplyBy(&p11, region->grid.fit2rawActive); dmtxMatrix3VMultiplyBy(&p01, region->grid.fit2rawActive); RETURN_FAIL_IF(RayFromPoints(&rs.bottom, p00, p10) == DmtxFail); RETURN_FAIL_IF(RayFromPoints(&rs.right, p10, p11) == DmtxFail); RETURN_FAIL_IF(RayFromPoints(&rs.top, p11, p01) == DmtxFail); RETURN_FAIL_IF(RayFromPoints(&rs.left, p01, p00) == DmtxFail); *regionSides = rs; return DmtxPass; } /** * * */ DmtxPassFail dmtxRegionUpdateFromSides(GridRegion *region, DmtxRegionSides regionSides) { DmtxVector2 p00, p10, p11, p01; RETURN_FAIL_IF(dmtxRay2Intersect(&p00, &(regionSides.left), &(regionSides.bottom)) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p10, &(regionSides.bottom), &(regionSides.right)) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p11, &(regionSides.right), &(regionSides.top)) == DmtxFail); RETURN_FAIL_IF(dmtxRay2Intersect(&p01, &(regionSides.top), &(regionSides.left)) == DmtxFail); RETURN_FAIL_IF(RegionUpdateCorners(region->grid.fit2rawActive, region->grid.raw2fitActive, p00, p10, p11, p01) == DmtxFail); /* need to update fit2rawFull and raw2fitFull here too? */ return DmtxPass; } /** * * */ DmtxPassFail RegionExpand(GridRegion *region, DmtxDirection sideDir, DmtxHoughLocal *line, DmtxCallbacks *fn) { DmtxRegionSides regionSides; DmtxRay2 *sideRay; switch(sideDir) { case DmtxDirDown: region->y--; region->height++; sideRay = &(regionSides.bottom); break; case DmtxDirUp: region->height++; sideRay = &(regionSides.top); break; case DmtxDirLeft: region->x--; region->width++; sideRay = &(regionSides.left); break; case DmtxDirRight: region->width++; sideRay = &(regionSides.right); break; default: return DmtxFail; } return DmtxPass; } /** * * */ int dmtxGetSizeIdx(int a, int b) { int i, rows, cols; const int totalSymbolSizes = 30; /* is there a better way to determine this? */ for(i = 0; i < totalSymbolSizes; i++) { rows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, i); cols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, i); if((rows == a && cols == b) || (rows == b && cols == a)) return i; } return DmtxUndefined; } /** * * */ DmtxPassFail RegionUpdateCorners(DmtxMatrix3 fit2raw, DmtxMatrix3 raw2fit, DmtxVector2 p00, DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01) { /* double xMax, yMax; */ double tx, ty, phi, shx, scx, scy, skx, sky; double dimOT, dimOR, dimTX, dimRX; /* , ratio; */ DmtxVector2 vOT, vOR, vTX, vRX, vTmp; DmtxMatrix3 m, mtxy, mphi, mshx, mscx, mscy, mscxy, msky, mskx; /* xMax = (double)(dmtxDecodeGetProp(dec, DmtxPropWidth) - 1); yMax = (double)(dmtxDecodeGetProp(dec, DmtxPropHeight) - 1); if(p00.X < 0.0 || p00.Y < 0.0 || p00.X > xMax || p00.Y > yMax || p01.X < 0.0 || p01.Y < 0.0 || p01.X > xMax || p01.Y > yMax || p10.X < 0.0 || p10.Y < 0.0 || p10.X > xMax || p10.Y > yMax) return DmtxFail; */ dimOT = dmtxVector2Mag(dmtxVector2Sub(&vOT, &p01, &p00)); /* XXX could use MagSquared() */ dimOR = dmtxVector2Mag(dmtxVector2Sub(&vOR, &p10, &p00)); dimTX = dmtxVector2Mag(dmtxVector2Sub(&vTX, &p11, &p01)); dimRX = dmtxVector2Mag(dmtxVector2Sub(&vRX, &p11, &p10)); /* Verify that sides are reasonably long */ /* if(dimOT <= 8.0 || dimOR <= 8.0 || dimTX <= 8.0 || dimRX <= 8.0) return DmtxFail; */ /* Verify that the 4 corners define a reasonably fat quadrilateral */ /* ratio = dimOT / dimRX; if(ratio <= 0.5 || ratio >= 2.0) return DmtxFail; */ /* ratio = dimOR / dimTX; if(ratio <= 0.5 || ratio >= 2.0) return DmtxFail; */ /* Verify this is not a bowtie shape */ /* if(dmtxVector2Cross(&vOR, &vRX) <= 0.0 || dmtxVector2Cross(&vOT, &vTX) >= 0.0) return DmtxFail; */ /* if(RightAngleTrueness(p00, p10, p11, M_PI_2) <= dec->squareDevn) return DmtxFail; if(RightAngleTrueness(p10, p11, p01, M_PI_2) <= dec->squareDevn) return DmtxFail; */ /* Calculate values needed for transformations */ tx = -1 * p00.X; ty = -1 * p00.Y; dmtxMatrix3Translate(mtxy, tx, ty); phi = atan2(vOT.X, vOT.Y); dmtxMatrix3Rotate(mphi, phi); dmtxMatrix3Multiply(m, mtxy, mphi); dmtxMatrix3VMultiply(&vTmp, &p10, m); shx = -vTmp.Y / vTmp.X; dmtxMatrix3Shear(mshx, 0.0, shx); dmtxMatrix3MultiplyBy(m, mshx); scx = 1.0/vTmp.X; dmtxMatrix3Scale(mscx, scx, 1.0); dmtxMatrix3MultiplyBy(m, mscx); dmtxMatrix3VMultiply(&vTmp, &p11, m); scy = 1.0/vTmp.Y; dmtxMatrix3Scale(mscy, 1.0, scy); dmtxMatrix3MultiplyBy(m, mscy); dmtxMatrix3VMultiply(&vTmp, &p11, m); skx = vTmp.X; dmtxMatrix3LineSkewSide(mskx, 1.0, skx, 1.0); dmtxMatrix3MultiplyBy(m, mskx); dmtxMatrix3VMultiply(&vTmp, &p01, m); sky = vTmp.Y; dmtxMatrix3LineSkewTop(msky, sky, 1.0, 1.0); dmtxMatrix3Multiply(raw2fit, m, msky); /* Create inverse matrix by reverse (avoid straight matrix inversion) */ dmtxMatrix3LineSkewTopInv(msky, sky, 1.0, 1.0); dmtxMatrix3LineSkewSideInv(mskx, 1.0, skx, 1.0); dmtxMatrix3Multiply(m, msky, mskx); dmtxMatrix3Scale(mscxy, 1.0/scx, 1.0/scy); dmtxMatrix3MultiplyBy(m, mscxy); dmtxMatrix3Shear(mshx, 0.0, -shx); dmtxMatrix3MultiplyBy(m, mshx); dmtxMatrix3Rotate(mphi, -phi); dmtxMatrix3MultiplyBy(m, mphi); dmtxMatrix3Translate(mtxy, -tx, -ty); dmtxMatrix3Multiply(fit2raw, m, mtxy); return DmtxPass; } /** * * */ DmtxPassFail dmtxDecodeSymbol(GridRegion *region, DmtxDecode *dec) { static int prefix = 0; int onColor, offColor; DmtxVector2 p00, p10, p11, p01; DmtxRegion reg; DmtxMessage *msg; /* Since we now hold 2 adjacent timing bars, find colors */ RETURN_FAIL_IF(GetOnOffColors(region, dec, &onColor, &offColor) == DmtxFail); p00.X = p01.X = region->x * (1.0/region->grid.colCount); p10.X = p11.X = (region->x + region->width) * (1.0/region->grid.colCount); p00.Y = p10.Y = region->y * (1.0/region->grid.rowCount); p01.Y = p11.Y = (region->y + region->height) * (1.0/region->grid.rowCount); dmtxMatrix3VMultiplyBy(&p00, region->grid.fit2rawFull); dmtxMatrix3VMultiplyBy(&p10, region->grid.fit2rawFull); dmtxMatrix3VMultiplyBy(&p11, region->grid.fit2rawFull); dmtxMatrix3VMultiplyBy(&p01, region->grid.fit2rawFull); /* Update DmtxRegion with detected corners */ switch(region->finderSides) { case (DmtxDirLeft | DmtxDirDown): RETURN_FAIL_IF(dmtxRegionUpdateCorners(dec, ®, p00, p10, p11, p01) == DmtxFail); break; case (DmtxDirDown | DmtxDirRight): RETURN_FAIL_IF(dmtxRegionUpdateCorners(dec, ®, p10, p11, p01, p00) == DmtxFail); break; case (DmtxDirRight | DmtxDirUp): RETURN_FAIL_IF(dmtxRegionUpdateCorners(dec, ®, p11, p01, p00, p10) == DmtxFail); break; case (DmtxDirUp | DmtxDirLeft): RETURN_FAIL_IF(dmtxRegionUpdateCorners(dec, ®, p01, p00, p10, p11) == DmtxFail); break; default: return DmtxFail; } /* Populate old-style region */ reg.flowBegin.plane = 0; /* or 1, or 2 (0 = red, 1 = green, etc...) */ reg.onColor = onColor; reg.offColor = offColor; reg.sizeIdx = region->sizeIdx; reg.symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, region->sizeIdx); reg.symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, region->sizeIdx); reg.mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, region->sizeIdx); reg.mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, region->sizeIdx); msg = dmtxDecodeMatrixRegion(dec, ®, DmtxUndefined); if(msg == NULL) return DmtxFail; fprintf(stdout, "%d: ", prefix); prefix = (prefix == 9) ? 0 : prefix + 1; fwrite(msg->output, sizeof(char), msg->outputIdx, stdout); fputc('\n', stdout); fflush(stdout); return DmtxPass; } /** * * */ DmtxPassFail GetOnOffColors(GridRegion *region, const DmtxDecode *dec, int *onColor, int *offColor) { int colBeg, rowBeg; DmtxDirection d0, d1; ColorTally t0, t1; /* add assertion to guarantee 2 adjancent timing bars */ /* Start tally at intersection of timing bars */ colBeg = (region->finderSides & DmtxDirLeft) ? region->x + region->width - 1 : region->x; rowBeg = (region->finderSides & DmtxDirDown) ? region->y + region->height - 1 : region->y; switch(region->finderSides) { case (DmtxDirLeft | DmtxDirDown): d0 = DmtxDirLeft; d1 = DmtxDirDown; break; case (DmtxDirDown | DmtxDirRight): d0 = DmtxDirDown; d1 = DmtxDirRight; break; case (DmtxDirRight | DmtxDirUp): d0 = DmtxDirRight; d1 = DmtxDirUp; break; case (DmtxDirUp | DmtxDirLeft): d0 = DmtxDirUp; d1 = DmtxDirLeft; break; default: return DmtxFail; } t0 = GetTimingColors(region, dec, colBeg, rowBeg, d0); t1 = GetTimingColors(region, dec, colBeg, rowBeg, d1); if(t0.evnCount + t1.evnCount == 0 || t0.oddCount + t1.oddCount == 0) return DmtxFail; *onColor = (t0.oddColor + t1.oddColor)/(t0.oddCount + t1.oddCount); *offColor = (t0.evnColor + t1.evnColor)/(t0.evnCount + t1.evnCount); return DmtxPass; } /** * * */ ColorTally GetTimingColors(GridRegion *region, const DmtxDecode *dec, int colBeg, int rowBeg, DmtxDirection dir) { int i, row, col, extent; int sample; ColorTally colors; colors.evnColor = 0; colors.evnCount = 0; colors.oddColor = 0; colors.oddCount = 0; /* Note opposite meaning (DmtxDirUp means "top", extent is horizontal) */ extent = (dir & DmtxDirVertical) ? region->width : region->height; col = colBeg; row = rowBeg; for(i = 0; i < extent; i++) { sample = dmtxReadModuleColor(dec->image, &(region->grid), row, col, 0); if(i & 0x01) { colors.oddColor += sample; colors.oddCount++; } else { colors.evnColor += sample; colors.evnCount++; } switch(dir) { case DmtxDirUp: row++; break; case DmtxDirLeft: col--; break; case DmtxDirDown: row--; break; case DmtxDirRight: col++; break; default: return colors; /* XXX should be an error condition */ } } /* consider having PerimeterEdgeTest() call this function when ready? */ return colors; } libdmtx-0.7.7/test/multi_test/dmtxsobel.c000066400000000000000000000116541423156660700205260ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxsobel.c */ #include #include #include #include "../../dmtx.h" #include "multi_test.h" /** * * */ DmtxSobel * SobelCreate(DmtxImage *img) { int sWidth, sHeight; DmtxSobel *sobel; sobel = (DmtxSobel *)calloc(1, sizeof(DmtxSobel)); if(sobel == NULL) return NULL; sWidth = dmtxImageGetProp(img, DmtxPropWidth) - 2; sHeight = dmtxImageGetProp(img, DmtxPropHeight) - 2; sobel->v = dmtxValueGridCreate(sWidth, sHeight, DmtxEdgeVertical, NULL); sobel->b = dmtxValueGridCreate(sWidth, sHeight, DmtxEdgeBackslash, NULL); sobel->h = dmtxValueGridCreate(sWidth, sHeight, DmtxEdgeHorizontal, NULL); sobel->s = dmtxValueGridCreate(sWidth, sHeight, DmtxEdgeSlash, NULL); if(sobel->v == NULL || sobel->b == NULL || sobel->h == NULL || sobel->s == NULL) { SobelDestroy(&sobel); return NULL; } return sobel; } /** * * */ DmtxPassFail SobelDestroy(DmtxSobel **sobel) { if(sobel == NULL || *sobel == NULL) return DmtxFail; dmtxValueGridDestroy(&((*sobel)->s)); dmtxValueGridDestroy(&((*sobel)->h)); dmtxValueGridDestroy(&((*sobel)->b)); dmtxValueGridDestroy(&((*sobel)->v)); free(*sobel); *sobel = NULL; return DmtxPass; } /** * 3x3 Sobel Kernel * */ DmtxPassFail SobelPopulate(DmtxDecode2 *dec) { int bytesPerPixel, rowSizeBytes, colorPlane; int sx, sy; int py, pOffset; int vMag, bMag, hMag, sMag; int colorLoLf, colorLoMd, colorLoRt; int colorMdRt, colorHiRt, colorHiMd; int colorHiLf, colorMdLf, colorMdMd; int idx; int sWidth, sHeight; DmtxSobel *sobel = dec->sobel; DmtxImage *img = dec->image; assert(dec != NULL); sobel = dec->sobel; img = dec->image; assert(sobel != NULL && img != NULL); sWidth = dmtxImageGetProp(img, DmtxPropWidth) - 2; sHeight = dmtxImageGetProp(img, DmtxPropHeight) - 2; rowSizeBytes = dmtxImageGetProp(img, DmtxPropRowSizeBytes); bytesPerPixel = dmtxImageGetProp(img, DmtxPropBytesPerPixel); colorPlane = 1; /* XXX need to make some decisions here */ for(sy = 0; sy < sHeight; sy++) { py = sHeight - sy; pOffset = py * rowSizeBytes + colorPlane; colorHiLf = img->pxl[pOffset - rowSizeBytes]; colorMdLf = img->pxl[pOffset]; colorLoLf = img->pxl[pOffset + rowSizeBytes]; pOffset += bytesPerPixel; colorHiMd = img->pxl[pOffset - rowSizeBytes]; colorMdMd = img->pxl[pOffset]; colorLoMd = img->pxl[pOffset + rowSizeBytes]; pOffset += bytesPerPixel; colorHiRt = img->pxl[pOffset - rowSizeBytes]; colorMdRt = img->pxl[pOffset]; colorLoRt = img->pxl[pOffset + rowSizeBytes]; for(sx = 0; sx < sWidth; sx++) { /** * -1 0 1 * -2 0 2 * -1 0 1 */ vMag = colorHiRt; vMag += colorMdRt * 2; vMag += colorLoRt; vMag -= colorHiLf; vMag -= colorMdLf * 2; vMag -= colorLoLf; /** * 0 1 2 * -1 0 1 * -2 -1 0 */ bMag = colorMdLf; bMag += colorLoLf * 2; bMag += colorLoMd; bMag -= colorMdRt; bMag -= colorHiRt * 2; bMag -= colorHiMd; /** * 1 2 1 * 0 0 0 * -1 -2 -1 */ hMag = colorHiLf; hMag += colorHiMd * 2; hMag += colorHiRt; hMag -= colorLoLf; hMag -= colorLoMd * 2; hMag -= colorLoRt; /** * -2 -1 0 * -1 0 1 * 0 1 2 */ sMag = colorLoMd; sMag += colorLoRt * 2; sMag += colorMdRt; sMag -= colorHiMd; sMag -= colorHiLf * 2; sMag -= colorMdLf; /** * If implementing these operations using MMX, can load 2 * registers with 4 doubleword values and subtract (PSUBD). */ idx = sy * sWidth + sx; sobel->v->value[idx] = vMag; sobel->b->value[idx] = bMag; sobel->h->value[idx] = hMag; sobel->s->value[idx] = sMag; colorHiLf = colorHiMd; colorMdLf = colorMdMd; colorLoLf = colorLoMd; colorHiMd = colorHiRt; colorMdMd = colorMdRt; colorLoMd = colorLoRt; pOffset += bytesPerPixel; colorHiRt = img->pxl[pOffset - rowSizeBytes]; colorMdRt = img->pxl[pOffset]; colorLoRt = img->pxl[pOffset + rowSizeBytes]; } } dec->fn.dmtxValueGridCallback(sobel->v, 0); dec->fn.dmtxValueGridCallback(sobel->b, 1); dec->fn.dmtxValueGridCallback(sobel->h, 2); dec->fn.dmtxValueGridCallback(sobel->s, 3); return DmtxPass; } libdmtx-0.7.7/test/multi_test/dmtxvaluegrid.c000066400000000000000000000035101423156660700213740ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file dmtxvaluegrid.c */ #include #include #include #include "../../dmtx.h" #include "multi_test.h" /** * * */ DmtxValueGrid * dmtxValueGridCreate(int width, int height, int type, DmtxValueGrid *ref) { DmtxValueGrid *valueGrid; valueGrid = (DmtxValueGrid *)calloc(1, sizeof(DmtxValueGrid)); if(valueGrid == NULL) return NULL; valueGrid->width = width; valueGrid->height = height; valueGrid->type = type; valueGrid->ref = ref; valueGrid->value = (int *)malloc(width * height * sizeof(int)); if(valueGrid->value == NULL) { dmtxValueGridDestroy(&valueGrid); return NULL; } return valueGrid; } /** * * */ DmtxPassFail dmtxValueGridDestroy(DmtxValueGrid **valueGrid) { if(valueGrid == NULL || *valueGrid == NULL) return DmtxFail; if((*valueGrid)->value != NULL) free((*valueGrid)->value); free(*valueGrid); *valueGrid = NULL; return DmtxPass; } /** * * */ int dmtxValueGridGetWidth(DmtxValueGrid *valueGrid) { if(valueGrid == NULL) return DmtxUndefined; return valueGrid->width; } /** * * */ int dmtxValueGridGetHeight(DmtxValueGrid *valueGrid) { if(valueGrid == NULL) return DmtxUndefined; return valueGrid->height; } /** * * */ int dmtxValueGridGetValue(DmtxValueGrid *valueGrid, int x, int y) { int idx; if(valueGrid == NULL) return DmtxUndefined; if(x < 0 || x >= valueGrid->width || y < 0 || y >= valueGrid->height) return 0; idx = y * valueGrid->width + x; return valueGrid->value[idx]; } libdmtx-0.7.7/test/multi_test/kiss_fft.c000066400000000000000000000306241423156660700203330ustar00rootroot00000000000000/* Copyright (c) 2003-2010, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "_kiss_fft_guts.h" /* The guts header contains all the multiplication and addition macros that are defined for fixed or floating point complex numbers. It also delares the kf_ internal functions. */ static void kf_bfly2( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m ) { kiss_fft_cpx * Fout2; kiss_fft_cpx * tw1 = st->twiddles; kiss_fft_cpx t; Fout2 = Fout + m; do{ C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); C_MUL (t, *Fout2 , *tw1); tw1 += fstride; C_SUB( *Fout2 , *Fout , t ); C_ADDTO( *Fout , t ); ++Fout2; ++Fout; }while (--m); } static void kf_bfly4( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, const size_t m ) { kiss_fft_cpx *tw1,*tw2,*tw3; kiss_fft_cpx scratch[6]; size_t k=m; const size_t m2=2*m; const size_t m3=3*m; tw3 = tw2 = tw1 = st->twiddles; do { C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); C_MUL(scratch[0],Fout[m] , *tw1 ); C_MUL(scratch[1],Fout[m2] , *tw2 ); C_MUL(scratch[2],Fout[m3] , *tw3 ); C_SUB( scratch[5] , *Fout, scratch[1] ); C_ADDTO(*Fout, scratch[1]); C_ADD( scratch[3] , scratch[0] , scratch[2] ); C_SUB( scratch[4] , scratch[0] , scratch[2] ); C_SUB( Fout[m2], *Fout, scratch[3] ); tw1 += fstride; tw2 += fstride*2; tw3 += fstride*3; C_ADDTO( *Fout , scratch[3] ); if(st->inverse) { Fout[m].r = scratch[5].r - scratch[4].i; Fout[m].i = scratch[5].i + scratch[4].r; Fout[m3].r = scratch[5].r + scratch[4].i; Fout[m3].i = scratch[5].i - scratch[4].r; }else{ Fout[m].r = scratch[5].r + scratch[4].i; Fout[m].i = scratch[5].i - scratch[4].r; Fout[m3].r = scratch[5].r - scratch[4].i; Fout[m3].i = scratch[5].i + scratch[4].r; } ++Fout; }while(--k); } static void kf_bfly3( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, size_t m ) { size_t k=m; const size_t m2 = 2*m; kiss_fft_cpx *tw1,*tw2; kiss_fft_cpx scratch[5]; kiss_fft_cpx epi3; epi3 = st->twiddles[fstride*m]; tw1=tw2=st->twiddles; do{ C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); C_MUL(scratch[1],Fout[m] , *tw1); C_MUL(scratch[2],Fout[m2] , *tw2); C_ADD(scratch[3],scratch[1],scratch[2]); C_SUB(scratch[0],scratch[1],scratch[2]); tw1 += fstride; tw2 += fstride*2; Fout[m].r = Fout->r - HALF_OF(scratch[3].r); Fout[m].i = Fout->i - HALF_OF(scratch[3].i); C_MULBYSCALAR( scratch[0] , epi3.i ); C_ADDTO(*Fout,scratch[3]); Fout[m2].r = Fout[m].r + scratch[0].i; Fout[m2].i = Fout[m].i - scratch[0].r; Fout[m].r -= scratch[0].i; Fout[m].i += scratch[0].r; ++Fout; }while(--k); } static void kf_bfly5( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m ) { kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; int u; kiss_fft_cpx scratch[13]; kiss_fft_cpx * twiddles = st->twiddles; kiss_fft_cpx *tw; kiss_fft_cpx ya,yb; ya = twiddles[fstride*m]; yb = twiddles[fstride*2*m]; Fout0=Fout; Fout1=Fout0+m; Fout2=Fout0+2*m; Fout3=Fout0+3*m; Fout4=Fout0+4*m; tw=st->twiddles; for ( u=0; ur += scratch[7].r + scratch[8].r; Fout0->i += scratch[7].i + scratch[8].i; scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); C_SUB(*Fout1,scratch[5],scratch[6]); C_ADD(*Fout4,scratch[5],scratch[6]); scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); C_ADD(*Fout2,scratch[11],scratch[12]); C_SUB(*Fout3,scratch[11],scratch[12]); ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; } } /* perform the butterfly for one stage of a mixed radix FFT */ static void kf_bfly_generic( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m, int p ) { int u,k,q1,q; kiss_fft_cpx * twiddles = st->twiddles; kiss_fft_cpx t; int Norig = st->nfft; kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); for ( u=0; u=Norig) twidx-=Norig; C_MUL(t,scratch[q] , twiddles[twidx] ); C_ADDTO( Fout[ k ] ,t); } k += m; } } KISS_FFT_TMP_FREE(scratch); } static void kf_work( kiss_fft_cpx * Fout, const kiss_fft_cpx * f, const size_t fstride, int in_stride, int * factors, const kiss_fft_cfg st ) { kiss_fft_cpx * Fout_beg=Fout; const int p=*factors++; /* the radix */ const int m=*factors++; /* stage's fft length/p */ const kiss_fft_cpx * Fout_end = Fout + p*m; #ifdef _OPENMP /* // use openmp extensions at the // top-level (not recursive) */ if (fstride==1 && p<=5) { int k; /* // execute the p different work units in different threads */ # pragma omp parallel for for (k=0;k floor_sqrt) p = n; /* no more factors, skip to end */ } n /= p; *facbuf++ = p; *facbuf++ = n; } while (n > 1); } /* * * User-callable function to allocate all necessary storage space for the fft. * * The return value is a contiguous block of memory, allocated with malloc. As such, * It can be freed with free(), rather than a kiss_fft-specific function. * */ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) { kiss_fft_cfg st=NULL; size_t memneeded = sizeof(struct kiss_fft_state) + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ if ( lenmem==NULL ) { st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); }else{ if (mem != NULL && *lenmem >= memneeded) st = (kiss_fft_cfg)mem; *lenmem = memneeded; } if (st) { int i; st->nfft=nfft; st->inverse = inverse_fft; for (i=0;iinverse) phase *= -1; kf_cexp(st->twiddles+i, phase ); } kf_factor(nfft,st->factors); } return st; } void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) { if (fin == fout) { /* //NOTE: this is not really an in-place FFT algorithm. //It just performs an out-of-place FFT into a temp buffer */ kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); kf_work(tmpbuf,fin,1,in_stride, st->factors,st); memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); KISS_FFT_TMP_FREE(tmpbuf); }else{ kf_work( fout, fin, 1,in_stride, st->factors,st ); } } void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) { kiss_fft_stride(cfg,fin,fout,1); } void kiss_fft_cleanup(void) { /* // nothing needed any more */ } int kiss_fft_next_fast_size(int n) { while(1) { int m=n; while ( (m%2) == 0 ) m/=2; while ( (m%3) == 0 ) m/=3; while ( (m%5) == 0 ) m/=5; if (m<=1) break; /* n is completely factorable by twos, threes, and fives */ n++; } return n; } libdmtx-0.7.7/test/multi_test/kiss_fft.h000066400000000000000000000063501423156660700203370ustar00rootroot00000000000000#ifndef KISS_FFT_H #define KISS_FFT_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* ATTENTION! If you would like a : -- a utility that will handle the caching of fft objects -- real-only (no imaginary time component ) FFT -- a multi-dimensional FFT -- a command-line utility to perform ffts -- a command-line utility to perform fast-convolution filtering Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c in the tools/ directory. */ #ifdef USE_SIMD # include # define kiss_fft_scalar __m128 #define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) #define KISS_FFT_FREE _mm_free #else #define KISS_FFT_MALLOC malloc #define KISS_FFT_FREE free #endif #ifdef FIXED_POINT #include # if (FIXED_POINT == 32) # define kiss_fft_scalar int32_t # else # define kiss_fft_scalar int16_t # endif #else # ifndef kiss_fft_scalar /* default is float */ # define kiss_fft_scalar float # endif #endif typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; typedef struct kiss_fft_state* kiss_fft_cfg; /* * kiss_fft_alloc * * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. * * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); * * The return value from fft_alloc is a cfg buffer used internally * by the fft routine or NULL. * * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. * The returned value should be free()d when done to avoid memory leaks. * * The state can be placed in a user supplied buffer 'mem': * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, * then the function places the cfg in mem and the size used in *lenmem * and returns mem. * * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), * then the function returns NULL and places the minimum cfg * buffer size in *lenmem. * */ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); /* * kiss_fft(cfg,in_out_buf) * * Perform an FFT on a complex input buffer. * for a forward FFT, * fin should be f[0] , f[1] , ... ,f[nfft-1] * fout will be F[0] , F[1] , ... ,F[nfft-1] * Note that each element is complex and can be accessed like f[k].r and f[k].i * */ void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); /* A more generic version of the above function. It reads its input from every Nth sample. * */ void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); /* If kiss_fft_alloc allocated a buffer, it is one contiguous buffer and can be simply free()d when no longer needed*/ #define kiss_fft_free free /* Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up your compiler output to call this before you exit. */ void kiss_fft_cleanup(void); /* * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) */ int kiss_fft_next_fast_size(int n); /* for real ffts, we need an even size */ #define kiss_fftr_next_fast_size_real(n) \ (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) #ifdef __cplusplus } #endif #endif libdmtx-0.7.7/test/multi_test/kiss_fftr.c000066400000000000000000000134431423156660700205150ustar00rootroot00000000000000/* Copyright (c) 2003-2004, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "kiss_fftr.h" #include "_kiss_fft_guts.h" struct kiss_fftr_state{ kiss_fft_cfg substate; kiss_fft_cpx * tmpbuf; kiss_fft_cpx * super_twiddles; #ifdef USE_SIMD void * pad; #endif }; kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) { int i; kiss_fftr_cfg st = NULL; size_t subsize, memneeded; if (nfft & 1) { fprintf(stderr,"Real FFT optimization must be even.\n"); return NULL; } nfft >>= 1; kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); if (lenmem == NULL) { st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); } else { if (*lenmem >= memneeded) st = (kiss_fftr_cfg) mem; *lenmem = memneeded; } if (!st) return NULL; st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); st->super_twiddles = st->tmpbuf + nfft; kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); for (i = 0; i < nfft/2; ++i) { double phase = -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); if (inverse_fft) phase *= -1; kf_cexp (st->super_twiddles+i,phase); } return st; } void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) { /* input buffer timedata is stored row-wise */ int k,ncfft; kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; if ( st->substate->inverse) { fprintf(stderr,"kiss fft usage error: improper alloc\n"); exit(1); } ncfft = st->substate->nfft; /*perform the parallel fft of two real signals packed in real,imag*/ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); /* The real part of the DC element of the frequency spectrum in st->tmpbuf * contains the sum of the even-numbered elements of the input time sequence * The imag part is the sum of the odd-numbered elements * * The sum of tdc.r and tdc.i is the sum of the input time sequence. * yielding DC of input time sequence * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... * yielding Nyquist bin of input time sequence */ tdc.r = st->tmpbuf[0].r; tdc.i = st->tmpbuf[0].i; C_FIXDIV(tdc,2); CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); freqdata[0].r = tdc.r + tdc.i; freqdata[ncfft].r = tdc.r - tdc.i; #ifdef USE_SIMD freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); #else freqdata[ncfft].i = freqdata[0].i = 0; #endif for ( k=1;k <= ncfft/2 ; ++k ) { fpk = st->tmpbuf[k]; fpnk.r = st->tmpbuf[ncfft-k].r; fpnk.i = - st->tmpbuf[ncfft-k].i; C_FIXDIV(fpk,2); C_FIXDIV(fpnk,2); C_ADD( f1k, fpk , fpnk ); C_SUB( f2k, fpk , fpnk ); C_MUL( tw , f2k , st->super_twiddles[k-1]); freqdata[k].r = HALF_OF(f1k.r + tw.r); freqdata[k].i = HALF_OF(f1k.i + tw.i); freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); } } void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) { /* input buffer timedata is stored row-wise */ int k, ncfft; if (st->substate->inverse == 0) { fprintf (stderr, "kiss fft usage error: improper alloc\n"); exit (1); } ncfft = st->substate->nfft; st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; C_FIXDIV(st->tmpbuf[0],2); for (k = 1; k <= ncfft / 2; ++k) { kiss_fft_cpx fk, fnkc, fek, fok, tmp; fk = freqdata[k]; fnkc.r = freqdata[ncfft - k].r; fnkc.i = -freqdata[ncfft - k].i; C_FIXDIV( fk , 2 ); C_FIXDIV( fnkc , 2 ); C_ADD (fek, fk, fnkc); C_SUB (tmp, fk, fnkc); C_MUL (fok, tmp, st->super_twiddles[k-1]); C_ADD (st->tmpbuf[k], fek, fok); C_SUB (st->tmpbuf[ncfft - k], fek, fok); #ifdef USE_SIMD st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); #else st->tmpbuf[ncfft - k].i *= -1; #endif } kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); } libdmtx-0.7.7/test/multi_test/kiss_fftr.h000066400000000000000000000015211423156660700205140ustar00rootroot00000000000000#ifndef KISS_FTR_H #define KISS_FTR_H #include "kiss_fft.h" #ifdef __cplusplus extern "C" { #endif /* Real optimized version can save about 45% cpu time vs. complex fft of a real seq. */ typedef struct kiss_fftr_state *kiss_fftr_cfg; kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); /* nfft must be even If you don't care to allocate space, use mem = lenmem = NULL */ void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); /* input timedata has nfft scalar points output freqdata has nfft/2+1 complex points */ void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); /* input freqdata has nfft/2+1 complex points output timedata has nfft scalar points */ #define kiss_fftr_free free #ifdef __cplusplus } #endif #endif libdmtx-0.7.7/test/multi_test/multi_test.c000066400000000000000000000417441423156660700207210ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file multi_test.c */ /** * This "multi_test" program is for experimental algorithms. Please * consider this code to be untested, unoptimized, and even unstable. * If something works here it will make its way into libdmtx "proper" * only after being properly written, tuned, and tested. */ /** * PLAN * x [ done ] Tapered bucket calculation (4 wks) * / [12-Jan] Build orientation from new vanish point info (2 wks) * o [19-Jan] FFT for grid alignment (1 wk) * o [26-Jan] Save scan progress between calls (1 wk) * o [02-Feb] Outward stepping (1 wk) * o [16-Feb] All grid locations (not just one) (2 wks) * o [02-Mar] Region spanning hough alignment (2 wks) * o [16-Mar] Multiscale hough (2 wks) * o [30-Mar] Integrate w/ libdmtx proper as experimental option (2 wks) * o [06-Apr] Release 0.7.4 (1 wk) * o [ next ] Bug fixes and site changes * * TODO * x Eliminate maxima candidates based on all 8 neighbors * x Populate maxima weight as sum of center and all 8 neighbors (?) * x Add interpolation to skipped d values on steep vanish points * x Tweak shaped bucket weighting to not overcount parallel condition * x Display local maxima * x Create better method of isolating true maxima * o Create mechanism to save scan progress between calls * o Work on nonlinearity of hough points presented by dot peen symbols */ #include #include #include #include #include #include "../../dmtx.h" #include "multi_test.h" AppState gState; int main(int argc, char *argv[]) { UserOptions opt; SDL_Event event; SDL_Rect imageLoc; Uint32 bgColorB; DmtxDecode *dec; DmtxDecode2 *dec2; SDL_Rect clipRect; if(HandleArgs(&opt, &argc, &argv) == DmtxFail) { exit(1); } gState = InitAppState(); /* Load image */ gState.picture = IMG_Load(opt.imagePath); if(gState.picture == NULL) { fprintf(stderr, "Unable to load image \"%s\": %s\n", opt.imagePath, SDL_GetError()); exit(1); } atexit(SDL_Quit); /* Initialize SDL library */ if(SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); exit(2); } gState.screen = SetWindowSize(gState.windowWidth, gState.windowHeight); NudgeImage(CTRL_COL1_X, gState.picture->w, &(gState.imageLocX)); NudgeImage(gState.windowHeight, gState.picture->h, &(gState.imageLocY)); bgColorB = SDL_MapRGBA(gState.screen->format, 100, 100, 100, 255); /* Create surface to hold image pixels to be scanned */ gState.local = SDL_CreateRGBSurface(SDL_SWSURFACE, LOCAL_SIZE, LOCAL_SIZE, 32, 0, 0, 0, 0); gState.imgActive = dmtxImageCreate(gState.local->pixels, gState.local->w, gState.local->h, DmtxPack32bppXRGB); assert(gState.imgActive != NULL); /* Create another surface for scaling purposes */ gState.localTmp = SDL_CreateRGBSurface(SDL_SWSURFACE, LOCAL_SIZE, LOCAL_SIZE, 32, 0, 0, 0, 0); gState.imgFull = CreateDmtxImage(gState.picture); assert(gState.imgFull != NULL); gState.dmtxImage = CreateDmtxImage(gState.picture); assert(gState.dmtxImage != NULL); dec = dmtxDecodeCreate(gState.imgFull, 1); assert(dec != NULL); dec2 = dmtxDecode2Create(); assert(dec2 != NULL); /* dmtxDecode2SetScale(dec2); */ /* Set up callback functions */ dec2->fn.dmtxValueGridCallback = ValueGridCallback; dec2->fn.zeroCrossingCallback = ZeroCrossingCallback; dec2->fn.dmtxHoughLocalCallback = HoughLocalCallback; dec2->fn.vanishPointCallback = VanishPointCallback; dec2->fn.timingCallback = TimingCallback; dec2->fn.gridCallback = GridCallback; dec2->fn.perimeterCallback = PerimeterCallback; for(;;) { SDL_Delay(10); while(SDL_PollEvent(&event)) HandleEvent(&event, &gState, gState.picture, &gState.screen); if(gState.quit == DmtxTrue) break; imageLoc.x = gState.imageLocX; imageLoc.y = gState.imageLocY; captureLocalPortion(gState.local, gState.localTmp, gState.picture, gState.screen, &gState, imageLoc); /* Start with blank canvas */ SDL_FillRect(gState.screen, NULL, bgColorB); /* Draw image to main canvas area */ clipRect.w = CTRL_COL1_X - 1; clipRect.h = gState.windowHeight; clipRect.x = 0; clipRect.y = 0; SDL_SetClipRect(gState.screen, &clipRect); SDL_BlitSurface(gState.picture, NULL, gState.screen, &imageLoc); SDL_SetClipRect(gState.screen, NULL); DrawActiveBorder(gState.screen, gState.activeExtent); ShowActiveRegion(gState.screen, gState.local); SDL_LockSurface(gState.local); if(dmtxDecode2SetImage(dec2, gState.dmtxImage) == DmtxFail) break; dmtxRegion2FindNext(dec2); SDL_UnlockSurface(gState.local); /* Dump FFT results */ if(gState.printValues == DmtxTrue) gState.printValues = DmtxFalse; SDL_Flip(gState.screen); } SDL_FreeSurface(gState.localTmp); SDL_FreeSurface(gState.local); dmtxDecode2Destroy(&dec2); dmtxDecodeDestroy(&dec); dmtxImageDestroy(&gState.dmtxImage); dmtxImageDestroy(&gState.imgFull); dmtxImageDestroy(&gState.imgActive); exit(0); } /** * * */ DmtxPassFail HandleArgs(UserOptions *opt, int *argcp, char **argvp[]) { memset(opt, 0x00, sizeof(UserOptions)); if(*argcp < 2) { fprintf(stdout, "Argument required\n"); return DmtxFail; } else { opt->imagePath = (*argvp)[1]; } return DmtxPass; } /** * * */ AppState InitAppState(void) { AppState state; memset(&state, 0x00, sizeof(AppState)); state.picture = NULL; state.windowWidth = 640; state.windowHeight = 518; state.activeExtent = 64; state.imgActive = NULL; state.imgFull = NULL; state.autoNudge = DmtxFalse; state.displayEdge = 0; state.displayVanish = DmtxFalse; state.displayTiming = DmtxTrue; state.printValues = DmtxFalse; state.imageLocX = 0; state.imageLocY = 0; state.imageOffsetX = 0; state.imageOffsetY = 0; state.localOffsetX = 0; state.localOffsetY = 0; state.leftButton = SDL_RELEASED; state.rightButton = SDL_RELEASED; state.pointerX = 0; state.pointerY = 0; state.quit = DmtxFalse; state.screen = NULL; state.local = NULL; state.localTmp = NULL; return state; } /** * * */ DmtxImage * CreateDmtxImage(SDL_Surface *sdlImage) { DmtxPackOrder packOrder; DmtxImage *dmtxImage = NULL; switch(sdlImage->format->BytesPerPixel) { case 1: packOrder = DmtxPack8bppK; break; case 3: packOrder = DmtxPack24bppRGB; break; case 4: packOrder = DmtxPack32bppXRGB; break; default: return NULL; } dmtxImage = dmtxImageCreate(sdlImage->pixels, sdlImage->w, sdlImage->h, packOrder); if(dmtxImage == NULL) return NULL; if(sdlImage->format->BytesPerPixel > 1) { dmtxImageSetProp(dmtxImage, DmtxPropRowPadBytes, sdlImage->pitch - (sdlImage->w * sdlImage->format->BytesPerPixel)); } return dmtxImage; } /** * * */ void AddFullTransforms(AlignmentGrid *grid) { DmtxMatrix3 mTranslate, mScale, mTmp; if(gState.activeExtent == 64) { dmtxMatrix3Translate(mTranslate, gState.imageLocX - 288, 518 - (227 + gState.imageLocY + gState.picture->h)); dmtxMatrix3Multiply(grid->raw2fitFull, mTranslate, grid->raw2fitActive); /* not tested */ } else { dmtxMatrix3Scale(mScale, 2.0, 2.0); dmtxMatrix3Multiply(mTmp, mScale, grid->raw2fitActive); dmtxMatrix3Copy(grid->raw2fitActive, mTmp); dmtxMatrix3Translate(mTranslate, gState.imageLocX - 304, 518 - (243 + gState.imageLocY + gState.picture->h)); dmtxMatrix3Multiply(grid->raw2fitFull, mTranslate, grid->raw2fitActive); /* not tested */ } if(gState.activeExtent == 64) { dmtxMatrix3Translate(mTranslate, 288 - gState.imageLocX, 227 + gState.imageLocY + gState.picture->h - 518); } else { dmtxMatrix3Scale(mScale, 0.5, 0.5); dmtxMatrix3Multiply(mTmp, grid->fit2rawActive, mScale); dmtxMatrix3Copy(grid->fit2rawActive, mTmp); dmtxMatrix3Translate(mTranslate, 304 - gState.imageLocX, 243 + gState.imageLocY + gState.picture->h - 518); } dmtxMatrix3Multiply(grid->fit2rawFull, grid->fit2rawActive, mTranslate); } void captureLocalPortion(SDL_Surface *local, SDL_Surface *localTmp, SDL_Surface *picture, SDL_Surface *screen, AppState *state, SDL_Rect imageLoc) { int i, j; Uint32 bgColorK; SDL_Rect clipRect; bgColorK = SDL_MapRGBA(screen->format, 0, 0, 0, 255); /* Capture portion of image that falls within highlighted region */ /* Use blitsurface if 1:1, otherwise scale */ SDL_FillRect(local, NULL, bgColorK); if(state->activeExtent == 64) { clipRect.x = state->localOffsetX; clipRect.y = picture->h - (state->localOffsetY + 64) - 1; clipRect.w = LOCAL_SIZE; clipRect.h = LOCAL_SIZE; SDL_BlitSurface(picture, &clipRect, local, NULL); } else if(state->activeExtent == 32) { Uint8 localBpp; Uint8 *writePixel, *readTL, *readTR, *readBL, *readBR; /* first blit, then expand */ clipRect.x = (screen->w - state->activeExtent)/2 - imageLoc.x; clipRect.y = (screen->h - state->activeExtent)/2 - imageLoc.y; clipRect.w = LOCAL_SIZE; clipRect.h = LOCAL_SIZE; SDL_BlitSurface(picture, &clipRect, localTmp, NULL); localBpp = local->format->BytesPerPixel; SDL_LockSurface(local); SDL_LockSurface(localTmp); for(i = 0; i < 64; i++) { for(j = 0; j < 64; j++) { readTL = (Uint8 *)localTmp->pixels + ((i/2) * 64 + (j/2)) * localBpp; readTR = readTL + localBpp; readBL = readTL + (64 * localBpp); readBR = readBL + localBpp; writePixel = (Uint8 *)local->pixels + ((i * 64 + j) * localBpp); /* memcpy(writePixel, readTL, localBpp); nearest neighbor */ if(!(i & 0x01) && !(j & 0x01)) { memcpy(writePixel, readTL, localBpp); } else if((i & 0x01) && !(j & 0x01)) { writePixel[0] = ((Uint16)readTL[0] + (Uint16)readBL[0])/2; writePixel[1] = ((Uint16)readTL[1] + (Uint16)readBL[1])/2; writePixel[2] = ((Uint16)readTL[2] + (Uint16)readBL[2])/2; writePixel[3] = ((Uint16)readTL[3] + (Uint16)readBL[3])/2; } else if(!(i & 0x01) && (j & 0x01)) { writePixel[0] = ((Uint16)readTL[0] + (Uint16)readTR[0])/2; writePixel[1] = ((Uint16)readTL[1] + (Uint16)readTR[1])/2; writePixel[2] = ((Uint16)readTL[2] + (Uint16)readTR[2])/2; writePixel[3] = ((Uint16)readTL[3] + (Uint16)readTR[3])/2; } else { writePixel[0] = ((Uint16)readTL[0] + (Uint16)readTR[0] + (Uint16)readBL[0] + (Uint16)readBR[0])/4; writePixel[1] = ((Uint16)readTL[1] + (Uint16)readTR[1] + (Uint16)readBL[1] + (Uint16)readBR[1])/4; writePixel[2] = ((Uint16)readTL[2] + (Uint16)readTR[2] + (Uint16)readBL[2] + (Uint16)readBR[2])/4; writePixel[3] = ((Uint16)readTL[3] + (Uint16)readTR[3] + (Uint16)readBL[3] + (Uint16)readBR[3])/4; } } } SDL_UnlockSurface(localTmp); SDL_UnlockSurface(local); } } /** * * */ SDL_Surface * SetWindowSize(int windowWidth, int windowHeight) { SDL_Surface *screen; screen = SDL_SetVideoMode(windowWidth, windowHeight, 32, SDL_HWSURFACE | SDL_DOUBLEBUF); /* | SDL_RESIZABLE); */ if(screen == NULL) { fprintf(stderr, "Couldn't set %dx%dx32 video mode: %s\n", windowWidth, windowHeight, SDL_GetError()); exit(1); } return screen; } /** * * */ DmtxPassFail HandleEvent(SDL_Event *event, AppState *state, SDL_Surface *picture, SDL_Surface **screen) { int nudgeRequired = DmtxFalse; switch(event->type) { case SDL_KEYDOWN: switch(event->key.keysym.sym) { case SDLK_ESCAPE: state->quit = DmtxTrue; break; case SDLK_0: state->displayEdge = 0; break; case SDLK_1: state->displayEdge = 1; break; case SDLK_2: state->displayEdge = 2; break; case SDLK_3: state->displayEdge = 3; break; case SDLK_4: state->displayEdge = 4; break; case SDLK_5: state->displayEdge = 5; break; case SDLK_6: state->displayEdge = 6; break; case SDLK_l: fprintf(stdout, "Image Location: (%d, %d)\n", state->imageLocX, state->imageLocY); break; case SDLK_n: state->autoNudge = (state->autoNudge == DmtxTrue) ? DmtxFalse : DmtxTrue; fprintf(stdout, "autoNudge %s\n", (state->autoNudge == DmtxTrue) ? "ON" : "OFF"); break; case SDLK_p: state->printValues = (state->printValues == DmtxTrue) ? DmtxFalse : DmtxTrue; break; case SDLK_t: state->displayTiming = (state->displayTiming == DmtxTrue) ? DmtxFalse : DmtxTrue; break; case SDLK_v: state->displayVanish = (state->displayVanish == DmtxTrue) ? DmtxFalse : DmtxTrue; break; case SDLK_UP: state->imageLocY--; nudgeRequired = DmtxTrue; break; case SDLK_DOWN: state->imageLocY++; nudgeRequired = DmtxTrue; break; case SDLK_RIGHT: state->imageLocX++; nudgeRequired = DmtxTrue; break; case SDLK_LEFT: state->imageLocX--; nudgeRequired = DmtxTrue; break; default: break; } break; case SDL_MOUSEBUTTONDOWN: switch(event->button.button) { case SDL_BUTTON_LEFT: state->leftButton = event->button.state; break; case SDL_BUTTON_RIGHT: state->rightButton = event->button.state; break; case SDL_BUTTON_WHEELDOWN: if(state->activeExtent > 32) state->activeExtent /= 2; break; case SDL_BUTTON_WHEELUP: if(state->activeExtent < 64) state->activeExtent *= 2; break; } break; case SDL_MOUSEBUTTONUP: switch(event->button.button) { case SDL_BUTTON_LEFT: state->leftButton = event->button.state; break; case SDL_BUTTON_RIGHT: state->rightButton = event->button.state; break; } break; case SDL_MOUSEMOTION: state->pointerX = event->motion.x; state->pointerY = event->motion.y; if(state->leftButton == SDL_PRESSED) { state->imageLocX += event->motion.xrel; state->imageLocY += event->motion.yrel; nudgeRequired = DmtxTrue; } break; case SDL_QUIT: state->quit = DmtxTrue; break; case SDL_VIDEORESIZE: state->windowWidth = event->resize.w; state->windowHeight = event->resize.h; *screen = SetWindowSize(state->windowWidth, state->windowHeight); nudgeRequired = DmtxTrue; break; default: break; } if(nudgeRequired == DmtxTrue) { NudgeImage(CTRL_COL1_X, picture->w, &(state->imageLocX)); NudgeImage(state->windowHeight, picture->h, &(state->imageLocY)); } /* Offset from bottom left corner of screen to bottom left corner of image */ state->imageOffsetX = state->imageLocX; state->imageOffsetY = (state->screen->h - state->picture->h) - state->imageLocY; /* Location of active area relative to bottom left corner of picture */ if(gState.activeExtent == 64) { state->localOffsetX = 158 - state->imageOffsetX; state->localOffsetY = 227 - state->imageOffsetY; } else { state->localOffsetX = 174 - state->imageOffsetX; state->localOffsetY = 243 - state->imageOffsetY; } return DmtxPass; } /** * * */ DmtxPassFail NudgeImage(int windowExtent, int pictureExtent, Sint16 *imageLoc) { int minReveal = 16; if(*imageLoc < minReveal - pictureExtent) *imageLoc = minReveal - pictureExtent; else if(*imageLoc > windowExtent - minReveal) *imageLoc = windowExtent - minReveal; return DmtxPass; } libdmtx-0.7.7/test/multi_test/multi_test.h000066400000000000000000000334661423156660700207300ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file multi_test.h */ #include #include "../../dmtx.h" #define max(N,M) ((N > M) ? N : M) #define min(N,M) ((N < M) ? N : M) #define OPPOSITE_SIGNS(a,b) (a != 0 && b != 0 && ((a > 0) != (b > 0))) #define LOCAL_SIZE 64 #define MAXIMA_SORT_MAX_COUNT 8 #define ANGLE_SORT_MAX_COUNT 8 #define TIMING_SORT_MAX_COUNT 8 #define NFFT 64 /* FFT input size */ #define HOUGH_D_EXTENT 64 #define HOUGH_PHI_EXTENT 128 /* Layout constants */ #define CTRL_COL1_X 380 #define CTRL_COL2_X 445 #define CTRL_COL3_X 510 #define CTRL_COL4_X 575 #define CTRL_ROW1_Y 0 #define CTRL_ROW2_Y 65 #define CTRL_ROW3_Y 130 #define CTRL_ROW4_Y 195 #define CTRL_ROW5_Y 260 #define CTRL_ROW6_Y 325 #define CTRL_ROW7_Y 390 #define MODULE_LOW 0 #define MODULE_HIGH 1 #define MODULE_UNKNOWN 2 #define RotateCCW(N) ((N < 8) ? (N << 1) : 1) #define RotateCW(N) ((N > 1) ? (N >> 1) : 8) typedef struct UserOptions_struct { const char *imagePath; } UserOptions; typedef struct AppState_struct { int windowWidth; int windowHeight; int activeExtent; DmtxImage *imgActive; DmtxImage *imgFull; DmtxImage *dmtxImage; DmtxBoolean autoNudge; int displayEdge; DmtxBoolean displayVanish; DmtxBoolean displayTiming; DmtxBoolean displayZXings; DmtxBoolean printValues; Sint16 imageLocX; Sint16 imageLocY; Sint16 imageOffsetX; /* X offset of right-handed image origin from screen origin */ Sint16 imageOffsetY; /* Y offset of right-handed image origin from screen origin */ Sint16 localOffsetX; /* X offset of active area */ Sint16 localOffsetY; /* Y offset of active area */ Uint8 leftButton; Uint8 rightButton; Uint16 pointerX; Uint16 pointerY; DmtxBoolean quit; SDL_Surface *picture; SDL_Surface *screen; SDL_Surface *local; SDL_Surface *localTmp; } AppState; typedef enum { DmtxEdgeVertical, DmtxEdgeBackslash, DmtxEdgeHorizontal, DmtxEdgeSlash } DmtxEdgeType; typedef enum { DmtxOctantTop = 0x01, DmtxOctantLeft = 0x01 << 1, DmtxOctantBottom = 0x01 << 2, DmtxOctantRight = 0x01 << 3, DmtxOctantTopLeft = (DmtxOctantTop | DmtxOctantLeft), DmtxOctantBottomLeft = (DmtxOctantBottom | DmtxOctantLeft), DmtxOctantBottomRight = (DmtxOctantBottom | DmtxOctantRight), DmtxOctantTopRight = (DmtxOctantTop | DmtxOctantRight) } DmtxOctantType; typedef struct DmtxValueGrid_struct DmtxValueGrid; struct DmtxValueGrid_struct { int width; int height; int type; int *value; DmtxValueGrid *ref; }; typedef struct DmtxSobel_struct DmtxSobel; struct DmtxSobel_struct { DmtxValueGrid *v; DmtxValueGrid *b; DmtxValueGrid *h; DmtxValueGrid *s; }; typedef struct DmtxAccel_struct DmtxAccel; struct DmtxAccel_struct { DmtxValueGrid *vv; DmtxValueGrid *vb; DmtxValueGrid *hb; DmtxValueGrid *hh; DmtxValueGrid *hs; DmtxValueGrid *vs; }; struct ZeroCrossing_struct { int iCol; int iRow; int mag; double x; double y; }; typedef struct ZeroCrossing_struct ZeroCrossing; typedef struct DmtxHoughBucket_struct DmtxHoughBucket; struct DmtxHoughBucket_struct { int phi; int d; int val; }; typedef struct DmtxHoughLocal_struct DmtxHoughLocal; struct DmtxHoughLocal_struct { int xOrigin; int yOrigin; int dOrigin[128]; int bucket[64][128]; /* [rows][cols] */ /* later change to 65 */ }; typedef struct DmtxHough_struct DmtxHough; struct DmtxHough_struct { int rows; int cols; int count; DmtxHoughLocal *line; DmtxHoughLocal *maxima; DmtxHoughLocal *vanish; }; typedef struct HoughMaximaSort_struct { int count; int mag[MAXIMA_SORT_MAX_COUNT]; } HoughMaximaSort; typedef struct VanishPointSort_struct { int count; DmtxHoughBucket bucket[ANGLE_SORT_MAX_COUNT]; } VanishPointSort; typedef struct Timing_struct { int phi; double shift; double period; double mag; } Timing; struct DmtxTimingSort_struct { int count; Timing timing[TIMING_SORT_MAX_COUNT]; }; typedef struct DmtxTimingSort_struct DmtxTimingSort; typedef struct DmtxOrient_struct DmtxOrient; struct DmtxOrient_struct { /* add supporting values used to build tranformation matrices here */ DmtxMatrix3 raw2fitLocal; DmtxMatrix3 raw2fit; DmtxMatrix3 fit2rawLocal; DmtxMatrix3 fit2raw; }; typedef struct AlignmentGrid_struct { int rowCount; int colCount; DmtxMatrix3 raw2fitActive; DmtxMatrix3 raw2fitFull; DmtxMatrix3 fit2rawActive; DmtxMatrix3 fit2rawFull; } AlignmentGrid; typedef struct GridRegion_struct { AlignmentGrid grid; int x; int y; int width; int height; int sizeIdx; int onColor; int offColor; int finderSides; int contrast; } GridRegion; typedef struct RegionLines_struct { int gridCount; Timing timing; double dA, dB; DmtxRay2 line[2]; } RegionLines; struct DmtxRegionSides_struct { DmtxRay2 top; DmtxRay2 left; DmtxRay2 bottom; DmtxRay2 right; }; typedef struct DmtxRegionSides_struct DmtxRegionSides; /* All values in GridRegionGrowth should be negative because list * is combined with the positive values of DmtxSymbolSize enum */ typedef enum { GridRegionGrowthUp = -5, GridRegionGrowthLeft = -4, GridRegionGrowthDown = -3, GridRegionGrowthRight = -2, GridRegionGrowthError = -1 } GridRegionGrowth; typedef enum { DmtxBarNone = 0x00, DmtxBarTiming = 0x01 << 0, DmtxBarFinder = 0x01 << 1, DmtxBarInterior = 0x01 << 2, DmtxBarExterior = 0x01 << 3 } DmtxBarType; /* Only used internally */ typedef struct ColorTally_struct { int evnCount; int oddCount; int evnColor; int oddColor; } ColorTally; struct StripStats_struct { int jumps; int surprises; int finderErrors; int timingErrors; int contrast; int finderBest; int timingBest; }; typedef struct StripStats_struct StripStats; struct DmtxCallbacks_struct { void (*dmtxValueGridCallback)(DmtxValueGrid *, int); void (*zeroCrossingCallback)(ZeroCrossing, int); void (*dmtxHoughLocalCallback)(DmtxHoughLocal *, int); void (*vanishPointCallback)(VanishPointSort *, int); void (*timingCallback)(Timing *, Timing *, int); void (*gridCallback)(AlignmentGrid *, int); void (*perimeterCallback)(GridRegion *, DmtxDirection, DmtxBarType); }; typedef struct DmtxCallbacks_struct DmtxCallbacks; typedef struct DmtxVectorPair_struct DmtxVectorPair; struct DmtxVectorPair_struct { DmtxVector2 a; DmtxVector2 b; }; typedef struct DmtxVanishCorners_struct DmtxVanishCorners; struct DmtxVanishCorners_struct { unsigned char zone; DmtxRay2 lineA; /* XXX consider switching to DmtxVectorPair later? */ DmtxRay2 lineB; }; struct DmtxDecode2_struct { DmtxImage *image; DmtxSobel *sobel; DmtxAccel *accel; DmtxHough *hough; DmtxVanishCorners corners[64][128]; /* XXX temporary location */ DmtxCallbacks fn; }; typedef struct DmtxDecode2_struct DmtxDecode2; /* Application level functions */ DmtxPassFail HandleArgs(UserOptions *opt, int *argcp, char **argvp[]); AppState InitAppState(void); DmtxImage *CreateDmtxImage(SDL_Surface *sdlImage); void AddFullTransforms(AlignmentGrid *grid); SDL_Surface *SetWindowSize(int windowWidth, int windowHeight); void captureLocalPortion(SDL_Surface *local, SDL_Surface *localTmp, SDL_Surface *picture, SDL_Surface *screen, AppState *state, SDL_Rect imageLoc); DmtxPassFail HandleEvent(SDL_Event *event, AppState *state, SDL_Surface *picture, SDL_Surface **screen); DmtxPassFail NudgeImage(int windowExtent, int pictureExtent, Sint16 *imageLoc); /*static void WriteDiagnosticImage(DmtxDecode *dec, char *imagePath);*/ /* Image processing functions */ DmtxPassFail dmtxRegion2FindNext(DmtxDecode2 *dec); DmtxPassFail OrientRegion(DmtxOrient *orient, DmtxHoughBucket v0, DmtxHoughBucket v1, DmtxDecode2 *dec); double UncompactOffset(double d, int phiIdx, int extent); void AddToVanishPointSort(VanishPointSort *sort, DmtxHoughBucket bucket); VanishPointSort dmtxFindVanishPoints(DmtxHoughLocal *vHough); void AddToMaximaSort(HoughMaximaSort *sort, int maximaMag); DmtxHoughBucket GetAngleSumAtPhi(DmtxHoughLocal *local, int phi); void AddToTimingSort(DmtxTimingSort *sort, Timing timing); DmtxTimingSort dmtxFindGridTiming(DmtxHoughLocal *local, VanishPointSort *sort); DmtxRay2 HoughCompactToRay(int phi, double d); DmtxPassFail dmtxBuildGridFromTimings(AlignmentGrid *grid, Timing vp0, Timing vp1); StripStats GenStripPatternStats(unsigned char *strip, int stripLength, int startState, int contrast); GridRegion NudgeStripLimits(GridRegion *region, DmtxDirection side, int nudgeStyle); DmtxPassFail dmtxFindRegionWithinGrid(GridRegion *region, AlignmentGrid *grid, DmtxHoughLocal *local, DmtxDecode *dec, DmtxCallbacks *fn); int dmtxReadModuleColor(DmtxImage *img, AlignmentGrid *grid, int symbolRow, int symbolCol, int colorPlane); DmtxBarType TestSideForPattern(GridRegion *region, DmtxImage *img, DmtxDirection side, int offset); DmtxPassFail RegionExpand(GridRegion *region, DmtxDirection dir, DmtxHoughLocal *local, DmtxCallbacks *fn); int dmtxGetSizeIdx(int a, int b); DmtxPassFail RegionUpdateCorners(DmtxMatrix3 fit2raw, DmtxMatrix3 raw2fit, DmtxVector2 p00, DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01); DmtxPassFail dmtxDecodeSymbol(GridRegion *region, DmtxDecode *dec); DmtxPassFail GetOnOffColors(GridRegion *region, const DmtxDecode *dec, int *onColor, int *offColor); ColorTally GetTimingColors(GridRegion *region, const DmtxDecode *dec, int colBeg, int rowBeg, DmtxDirection dir); /* Process visualization functions */ void ValueGridCallback(DmtxValueGrid *valueGrid, int id); void ZeroCrossingCallback(ZeroCrossing zXing, int id); void HoughLocalCallback(DmtxHoughLocal *hough, int id); void VanishPointCallback(VanishPointSort *vPoints, int id); void TimingCallback(Timing *timing0, Timing *timing1, int id); void GridCallback(AlignmentGrid *grid, int id); void PerimeterCallback(GridRegion *region, DmtxDirection side, DmtxBarType type); void BlitSobelGrid(SDL_Surface *screen, DmtxValueGrid *cache, int x, int y, int screenY, int screenX); void BlitHoughLocal(SDL_Surface *screen, DmtxHoughLocal *hough, int screenY, int screenX); void ShowActiveRegion(SDL_Surface *screen, SDL_Surface *active); void BlitActiveRegion(SDL_Surface *screen, SDL_Surface *active, int zoom, int screenY, int screenX); Uint32 GetPixel(SDL_Surface *surface, int x, int y); void PutPixel(SDL_Surface *surface, int x, int y, Uint32 color); int RayIntersect(double *t, DmtxRay2 p0, DmtxRay2 p1); int IntersectBox(DmtxRay2 ray, DmtxVector2 bb0, DmtxVector2 bb1, DmtxVector2 *p0, DmtxVector2 *p1); void DrawActiveBorder(SDL_Surface *screen, int activeExtent); void DrawLine(SDL_Surface *screen, int baseExtent, int screenX, int screenY, int phi, double d, int displayScale, Uint32 color); void DrawVanishingPoints(SDL_Surface *screen, VanishPointSort *sort, int screenY, int screenX); void DrawTimingDots(SDL_Surface *screen, Timing *timing, int screenY, int screenX); void DrawNormalizedRegion(SDL_Surface *screen, DmtxImage *img, AlignmentGrid *grid, AppState *state, int screenY, int screenX); Sint16 Clamp(Sint16 x, Sint16 xMin, Sint16 extent); void DrawSymbolPreview(SDL_Surface *screen, DmtxImage *img, AlignmentGrid *grid, AppState *state, int screenY, int screenX); void DrawPerimeterPatterns(SDL_Surface *screen, GridRegion *region, AppState *state, DmtxDirection side, DmtxBarType type); void DrawPerimeterSide(SDL_Surface *screen, int x00, int y00, int x11, int y11, int dispModExtent, DmtxDirection side, DmtxBarType type); /* dmtxvaluegrid.c */ DmtxValueGrid *dmtxValueGridCreate(int width, int height, int type, DmtxValueGrid *ref); DmtxPassFail dmtxValueGridDestroy(DmtxValueGrid **valueGrid); int dmtxValueGridGetWidth(DmtxValueGrid *valueGrid); int dmtxValueGridGetHeight(DmtxValueGrid *valueGrid); int dmtxValueGridGetValue(DmtxValueGrid *valueGrid, int x, int y); /* dmtxsobel.c */ DmtxSobel *SobelCreate(DmtxImage *img); DmtxPassFail SobelDestroy(DmtxSobel **sobel); DmtxPassFail SobelPopulate(DmtxDecode2 *dec); DmtxAccel *AccelCreate(DmtxSobel *sobel); DmtxPassFail AccelDestroy(DmtxAccel **accel); DmtxPassFail AccelPopulate(DmtxDecode2 *dec); DmtxPassFail AccelPopulateLocal(DmtxValueGrid *acc); /* dmtxdecode2.c */ DmtxDecode2 *dmtxDecode2Create(); DmtxPassFail dmtxDecode2Destroy(DmtxDecode2 **dec); void PopulateVanishBounds(DmtxDecode2 *dec); DmtxVanishCorners GetVanishCorners(int d, int phi); int GetZone(int phiFull, double *distance); DmtxVectorPair GetZoneCornerLocs(DmtxOctantType zone); DmtxPassFail dmtxDecode2SetImage(DmtxDecode2 *dec, DmtxImage *img); DmtxPassFail decode2ReleaseCacheMemory(DmtxDecode2 *dec); /* dmtxhough.c */ DmtxHough *HoughCreate(int cols, int rows); DmtxPassFail HoughDestroy(DmtxHough **grid); DmtxPassFail HoughPopulate(DmtxDecode2 *dec); DmtxPassFail LineHoughAccumulate(DmtxHoughLocal *lhRegion, DmtxDecode2 *dec); DmtxPassFail MaximaHoughAccumulate(DmtxHoughLocal *mhRegion, DmtxHoughLocal *lhRegion, DmtxDecode2 *dec); int GetMaximaWeight(DmtxHoughLocal *lhRegion, int phi, int d); DmtxPassFail VanishHoughAccumulate(DmtxHoughLocal *lhRegion, DmtxHoughLocal *vhRegion); int GetVanishBucket(int phiBucket, int phiCompare, int dCompare); ZeroCrossing GetZeroCrossing(DmtxValueGrid *accel, int iCol, int iRow); ZeroCrossing SetZeroCrossingFromIndex(DmtxValueGrid *accel, int aCol, int aRow, double smidg); DmtxPassFail HoughLocalAccumulateEdge(DmtxHoughLocal *local, int phi, ZeroCrossing edge); double HoughGetLocalOffset(double xLoc, double yLoc, int phi); extern AppState gState; libdmtx-0.7.7/test/multi_test/visualize.c000066400000000000000000000542721423156660700205430ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2010 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file visualize.c */ #include #include #include #include #include "../../dmtx.h" #include "multi_test.h" void ValueGridCallback(DmtxValueGrid *valueGrid, int id) { int x, y; int imageFlipY; int imageHeight; imageHeight = dmtxImageGetProp(gState.dmtxImage, DmtxPropHeight); imageFlipY = gState.screen->h - (gState.imageLocY + imageHeight) - 1; x = gState.localOffsetX - 1; /* not sure why it needs -1 to align */ y = gState.localOffsetY; switch(id) { case 0: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW2_Y, CTRL_COL1_X); break; case 1: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW2_Y, CTRL_COL2_X); break; case 2: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW2_Y, CTRL_COL3_X); break; case 3: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW2_Y, CTRL_COL4_X); break; case 4: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW3_Y, CTRL_COL1_X); break; case 5: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW3_Y, CTRL_COL2_X); break; case 6: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW3_Y, CTRL_COL4_X); break; case 7: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW4_Y, CTRL_COL2_X); break; case 8: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW4_Y, CTRL_COL3_X); break; case 9: BlitSobelGrid(gState.screen, valueGrid, x, y, CTRL_ROW4_Y, CTRL_COL4_X); break; default: break; } } void ZeroCrossingCallback(ZeroCrossing zXing, int id) { int xInt, yInt; if(zXing.mag < 50) return; xInt = gState.imageOffsetX + (int)zXing.x; yInt = gState.screen->h - (gState.imageOffsetY + (int)zXing.y) - 1; if(xInt < 0 || xInt > CTRL_COL1_X - 2 || yInt < 0 || yInt > gState.screen->h - 1) return; switch(id) { case 0: PutPixel(gState.screen, xInt, yInt, 0x00ff0000); break; default: break; } } void HoughLocalCallback(DmtxHoughLocal *hough, int id) { switch(id) { case 0: BlitHoughLocal(gState.screen, hough, CTRL_ROW5_Y, CTRL_COL1_X + 1); break; case 1: BlitHoughLocal(gState.screen, hough, CTRL_ROW6_Y, CTRL_COL1_X + 1); break; case 2: BlitHoughLocal(gState.screen, hough, CTRL_ROW7_Y, CTRL_COL1_X + 1); break; } } void VanishPointCallback(VanishPointSort *vPoints, int id) { if(gState.displayVanish == DmtxFalse) return; DrawVanishingPoints(gState.screen, vPoints, CTRL_ROW7_Y, CTRL_COL1_X + 1); } void TimingCallback(Timing *timing0, Timing *timing1, int id) { int i; int rowNum; rowNum = (id == 0) ? CTRL_ROW6_Y : CTRL_ROW5_Y; /* Should this be called before, as soon as local is captured? */ BlitActiveRegion(gState.screen, gState.local, 2, CTRL_ROW5_Y, CTRL_COL3_X); if(gState.displayTiming == DmtxFalse) return; /* Draw timed and untimed region lines */ if(gState.displayTiming == DmtxTrue) { DrawTimingDots(gState.screen, timing0, rowNum, CTRL_COL1_X); DrawTimingDots(gState.screen, timing1, rowNum, CTRL_COL1_X); for(i = -64; i <= 64; i++) { DrawLine(gState.screen, 64, CTRL_COL3_X, CTRL_ROW5_Y, timing0->phi, timing0->shift + (timing0->period * i), 2, 0xff0000ff); DrawLine(gState.screen, 64, CTRL_COL3_X, CTRL_ROW5_Y, timing1->phi, timing1->shift + (timing1->period * i), 2, 0xff0000ff); } } } void GridCallback(AlignmentGrid *grid, int id) { switch(id) { case 0: DrawSymbolPreview(gState.screen, gState.imgFull, grid, &gState, CTRL_ROW5_Y, CTRL_COL1_X + 1); break; case 1: DrawNormalizedRegion(gState.screen, gState.imgFull, grid, &gState, CTRL_ROW5_Y, CTRL_COL3_X); break; } } void PerimeterCallback(GridRegion *region, DmtxDirection side, DmtxBarType type) { DrawPerimeterPatterns(gState.screen, region, &gState, side, type); } /******************************************************************************/ void ShowActiveRegion(SDL_Surface *screen, SDL_Surface *active) { BlitActiveRegion(screen, active, 1, CTRL_ROW1_Y, CTRL_COL1_X); BlitActiveRegion(screen, active, 1, CTRL_ROW1_Y, CTRL_COL2_X); BlitActiveRegion(screen, active, 1, CTRL_ROW1_Y, CTRL_COL3_X); BlitActiveRegion(screen, active, 1, CTRL_ROW1_Y, CTRL_COL4_X); } /** * * */ void BlitActiveRegion(SDL_Surface *screen, SDL_Surface *active, int zoom, int screenY, int screenX) { SDL_Surface *src; SDL_Rect clipRect; clipRect.w = LOCAL_SIZE; clipRect.h = LOCAL_SIZE; clipRect.x = screenX; clipRect.y = screenY; if(zoom == 1) { SDL_BlitSurface(active, NULL, screen, &clipRect); } else { /* DO NOT USE SMOOTHING OPTION -- distorts symbol proportions */ src = zoomSurface(active, 2.0, 2.0, 0 /* smoothing */); SDL_BlitSurface(src, NULL, screen, &clipRect); SDL_FreeSurface(src); } } /** * * */ void BlitSobelGrid(SDL_Surface *screen, DmtxValueGrid *cache, int x, int y, int screenY, int screenX) { int row, col; unsigned char rgb[3]; int width, height; int offset; int flow; unsigned char pixbuf[12288]; /* 64 * 64 * 3 */ int maxFlowMag = 1020; SDL_Surface *surface; SDL_Rect clipRect; Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif width = height = 64; for(row = 0; row < 64; row++) { for(col = 0; col < 64; col++) { flow = dmtxValueGridGetValue(cache, col + x, row + y); if(flow > 0) { rgb[0] = 0; rgb[1] = (int)((abs(flow) * 254.0)/maxFlowMag + 0.5); rgb[2] = 0; } else { rgb[0] = (int)((abs(flow) * 254.0)/maxFlowMag + 0.5); rgb[1] = 0; rgb[2] = 0; } offset = ((height - row - 1) * width + col) * 3; pixbuf[offset] = rgb[0]; pixbuf[offset+1] = rgb[1]; pixbuf[offset+2] = rgb[2]; } } clipRect.w = LOCAL_SIZE; clipRect.h = LOCAL_SIZE; clipRect.x = screenX; clipRect.y = screenY; surface = SDL_CreateRGBSurfaceFrom(pixbuf, width, height, 24, width * 3, rmask, gmask, bmask, 0); SDL_BlitSurface(surface, NULL, screen, &clipRect); SDL_FreeSurface(surface); } /** * * */ void BlitHoughLocal(SDL_Surface *screen, DmtxHoughLocal *hough, int screenY, int screenX) { int row, col; int width, height; int maxVal; int rgb[3]; unsigned int cache; int offset; unsigned char pixbuf[24576]; /* 128 * 64 * 3 */ SDL_Surface *surface; SDL_Rect clipRect; Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif width = 128; height = LOCAL_SIZE; maxVal = 0; for(row = 0; row < height; row++) { for(col = 0; col < width; col++) { if(hough->bucket[row][col] > maxVal) maxVal = hough->bucket[row][col]; } } for(row = 0; row < height; row++) { for(col = 0; col < width; col++) { cache = hough->bucket[row][col]; rgb[0] = rgb[1] = rgb[2] = (int)((cache * 254.0)/maxVal + 0.5); offset = ((height - row - 1) * width + col) * 3; pixbuf[offset] = rgb[0]; pixbuf[offset+1] = rgb[1]; pixbuf[offset+2] = rgb[2]; } } clipRect.w = width; clipRect.h = height; clipRect.x = screenX; clipRect.y = screenY; surface = SDL_CreateRGBSurfaceFrom(pixbuf, width, height, 24, width * 3, rmask, gmask, bmask, 0); SDL_BlitSurface(surface, NULL, screen, &clipRect); SDL_FreeSurface(surface); } /** * * */ Uint32 GetPixel(SDL_Surface *surface, int x, int y) { int bpp = surface->format->BytesPerPixel; Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; switch(bpp) { case 1: return *p; case 2: return *(Uint16 *)p; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) return (p[0] << 16) | (p[1] << 8) | p[2]; else return p[0] | (p[1] << 8) | (p[2] << 16); case 4: return *(Uint32 *)p; default: return 0; } } void PutPixel(SDL_Surface *surface, int x, int y, Uint32 color) { int bpp = surface->format->BytesPerPixel; Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; switch(bpp) { case 1: *p = color; break; case 2: *(Uint16 *)p = color; break; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (color >> 16) & 0xff; p[1] = (color >> 8) & 0xff; p[2] = color & 0xff; } else { p[0] = color & 0xff; p[1] = (color >> 8) & 0xff; p[2] = (color >> 16) & 0xff; } break; case 4: *(Uint32 *)p = color; break; } } /** * * */ int RayIntersect(double *t, DmtxRay2 p0, DmtxRay2 p1) { double numer, denom; DmtxVector2 w; denom = dmtxVector2Cross(&(p1.v), &(p0.v)); if(fabs(denom) <= 0.000001) return DmtxFail; dmtxVector2Sub(&w, &(p1.p), &(p0.p)); numer = dmtxVector2Cross(&(p1.v), &w); *t = numer/denom; return DmtxTrue; } /** * * */ int IntersectBox(DmtxRay2 ray, DmtxVector2 bb0, DmtxVector2 bb1, DmtxVector2 *p0, DmtxVector2 *p1) { double tTmp, xMin, xMax, yMin, yMax; DmtxVector2 p[2]; int tCount = 0; double extent; DmtxRay2 rBtm, rTop, rLft, rRgt; DmtxVector2 unitX = { 1.0, 0.0 }; DmtxVector2 unitY = { 0.0, 1.0 }; if(bb0.X < bb1.X) { xMin = bb0.X; xMax = bb1.X; } else { xMin = bb1.X; xMax = bb0.X; } if(bb0.Y < bb1.Y) { yMin = bb0.Y; yMax = bb1.Y; } else { yMin = bb1.Y; yMax = bb0.Y; } extent = xMax - xMin; rBtm.p.X = rTop.p.X = rLft.p.X = xMin; rRgt.p.X = xMax; rBtm.p.Y = rLft.p.Y = rRgt.p.Y = yMin; rTop.p.Y = yMax; rBtm.v = rTop.v = unitX; rLft.v = rRgt.v = unitY; if(RayIntersect(&tTmp, rBtm, ray) == DmtxPass && tTmp >= 0.0 && tTmp < extent) dmtxPointAlongRay2(&(p[tCount++]), &rBtm, tTmp); if(RayIntersect(&tTmp, rTop, ray) == DmtxPass && tTmp >= 0.0 && tTmp < extent) dmtxPointAlongRay2(&(p[tCount++]), &rTop, tTmp); if(RayIntersect(&tTmp, rLft, ray) == DmtxPass && tTmp >= 0.0 && tTmp < extent) dmtxPointAlongRay2(&(p[tCount++]), &rLft, tTmp); if(RayIntersect(&tTmp, rRgt, ray) == DmtxPass && tTmp >= 0.0 && tTmp < extent) dmtxPointAlongRay2(&(p[tCount++]), &rRgt, tTmp); if(tCount != 2) return DmtxFail; *p0 = p[0]; *p1 = p[1]; return DmtxTrue; } /** * * */ void DrawActiveBorder(SDL_Surface *screen, int activeExtent) { Sint16 x00, y00; Sint16 x10, y10; Sint16 x11, y11; Sint16 x01, y01; x01 = 158; y01 = 226; x00 = x01; y00 = y01 + activeExtent + 1; x10 = x00 + activeExtent + 1; y10 = y00; x11 = x10; y11 = y01; lineColor(screen, x00, y00, x10, y10, 0x0000ffff); lineColor(screen, x10, y10, x11, y11, 0x0000ffff); lineColor(screen, x11, y11, x01, y01, 0x0000ffff); lineColor(screen, x01, y01, x00, y00, 0x0000ffff); } /** * * */ void DrawLine(SDL_Surface *screen, int baseExtent, int screenX, int screenY, int phi, double d, int displayScale, Uint32 color) { int scaledExtent; DmtxVector2 bb0, bb1; DmtxVector2 p0, p1; DmtxRay2 rLine; DmtxPixelLoc d0, d1; scaledExtent = baseExtent * displayScale; bb0.X = bb0.Y = 0.0; bb1.X = bb1.Y = scaledExtent - 1; rLine = HoughCompactToRay(phi, d); dmtxVector2ScaleBy(&rLine.p, (double)displayScale); p0.X = p0.Y = p1.X = p1.Y = 0.0; if(IntersectBox(rLine, bb0, bb1, &p0, &p1) == DmtxFalse) return; d0.X = (int)(p0.X + 0.5) + screenX; d1.X = (int)(p1.X + 0.5) + screenX; d0.Y = screenY + (scaledExtent - (int)(p0.Y + 0.5) - 1); d1.Y = screenY + (scaledExtent - (int)(p1.Y + 0.5) - 1); lineColor(screen, d0.X, d0.Y, d1.X, d1.Y, color); } /** * * */ void DrawVanishingPoints(SDL_Surface *screen, VanishPointSort *sort, int screenY, int screenX) { int sortIdx; DmtxPixelLoc d0, d1; Uint32 rgba; for(sortIdx = 0; sortIdx < sort->count; sortIdx++) { d0.X = screenX + sort->bucket[sortIdx].phi - 2; d1.X = d0.X + 4; d0.Y = screenY + (63 - sort->bucket[sortIdx].d) - 2;; d1.Y = d0.Y + 4; if(sortIdx < 2) rgba = 0xff0000ff; /* red: strongest */ else if(sortIdx < 4) rgba = 0x999900ff; /* yellow: weaker */ else if(sortIdx < 6) rgba = 0x007700ff; /* green: even weaker */ else rgba = 0x000077ff; /* blue: weakest */ rectangleColor(screen, d0.X, d0.Y, d1.X, d1.Y, rgba); } } /** * * */ void DrawTimingDots(SDL_Surface *screen, Timing *timing, int screenY, int screenX) { int i, d; for(i = 0; i < 64; i++) { d = (int)(i * timing->period + timing->shift); if(d >= 64) break; PutPixel(screen, screenX + timing->phi, screenY + 63 - d, 0x00ff0000); } } /** * * */ void DrawNormalizedRegion(SDL_Surface *screen, DmtxImage *img, AlignmentGrid *region, AppState *state, int screenY, int screenX) { unsigned char pixbuf[49152]; /* 128 * 128 * 3 */ unsigned char *ptrFit, *ptrRaw; SDL_Rect clipRect; SDL_Surface *surface; Uint32 rmask, gmask, bmask, amask; int x, yImage, yDmtx; int xRaw, yRaw; int extent = 128; int modulesToDisplay = 16; int dispModExtent = extent/modulesToDisplay; int bytesPerRow = extent * 3; DmtxVector2 pFit, pRaw, pRawActive, pTmp, pCtr; DmtxVector2 gridTest; int shiftX, shiftY; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif SDL_LockSurface(state->picture); pTmp.X = pTmp.Y = state->activeExtent/2.0; dmtxMatrix3VMultiply(&pCtr, &pTmp, region->raw2fitActive); for(yImage = 0; yImage < extent; yImage++) { for(x = 0; x < extent; x++) { yDmtx = (extent - 1) - yImage; /* Adjust fitted input so unfitted center is display centered */ pFit.X = ((x-extent/2) * (double)modulesToDisplay) / (region->colCount * extent) + pCtr.X; pFit.Y = ((yDmtx-extent/2) * (double)modulesToDisplay) / (region->rowCount * extent) + pCtr.Y; dmtxMatrix3VMultiply(&pRaw, &pFit, region->fit2rawFull); dmtxMatrix3VMultiply(&pRawActive, &pFit, region->fit2rawActive); xRaw = (pRaw.X >= 0.0) ? (int)(pRaw.X + 0.5) : (int)(pRaw.X - 0.5); yRaw = (pRaw.Y >= 0.0) ? (int)(pRaw.Y + 0.5) : (int)(pRaw.Y - 0.5); ptrFit = pixbuf + (yImage * bytesPerRow + x * 3); if(xRaw < 0 || xRaw >= img->width || yRaw < 0 || yRaw >= img->height) { ptrFit[0] = 0; ptrFit[1] = 0; ptrFit[2] = 0; } else { ptrRaw = (unsigned char *)img->pxl + dmtxImageGetByteOffset(img, xRaw, yRaw); if(pRawActive.X < 0.0 || pRawActive.X >= state->activeExtent - 1 || pRawActive.Y < 0.0 || pRawActive.Y >= state->activeExtent - 1) { ptrFit[0] = ptrRaw[0]/2; ptrFit[1] = ptrRaw[1]/2; ptrFit[2] = ptrRaw[2]/2; } else { ptrFit[0] = ptrRaw[0]; ptrFit[1] = ptrRaw[1]; ptrFit[2] = ptrRaw[2]; } } } } gridTest.X = pCtr.X * region->colCount * dispModExtent; gridTest.X += (gridTest.X >= 0.0) ? 0.5 : -0.5; shiftX = (int)gridTest.X % dispModExtent; gridTest.Y = pCtr.Y * region->rowCount * dispModExtent; gridTest.Y += (gridTest.Y >= 0.0) ? 0.5 : -0.5; shiftY = (int)gridTest.Y % dispModExtent; for(yImage = 0; yImage < extent; yImage++) { yDmtx = (extent - 1) - yImage; for(x = 0; x < extent; x++) { if((yDmtx + shiftY) % dispModExtent != 0 && (x + shiftX) % dispModExtent != 0) continue; ptrFit = pixbuf + (yImage * bytesPerRow + x * 3); /* If reeaally dark then add brightened grid lines */ if(ptrFit[0] + ptrFit[1] + ptrFit[2] < 60) { ptrFit[0] += (255 - ptrFit[0])/8; ptrFit[1] += (255 - ptrFit[1])/8; ptrFit[2] += (255 - ptrFit[2])/8; } /* Otherwise add darkened grid lines */ else { ptrFit[0] = (ptrFit[0] * 8)/10; ptrFit[1] = (ptrFit[1] * 8)/10; ptrFit[2] = (ptrFit[2] * 8)/10; } } } clipRect.w = extent; clipRect.h = extent; clipRect.x = screenX; clipRect.y = screenY; surface = SDL_CreateRGBSurfaceFrom(pixbuf, extent, extent, 24, extent * 3, rmask, gmask, bmask, 0); SDL_BlitSurface(surface, NULL, screen, &clipRect); SDL_FreeSurface(surface); SDL_UnlockSurface(state->picture); } /** * * */ Sint16 Clamp(Sint16 x, Sint16 xMin, Sint16 extent) { Sint16 xMax; if(x < xMin) return xMin; xMax = xMin + extent - 1; if(x > xMax) return xMax; return x; } /** * * */ void DrawSymbolPreview(SDL_Surface *screen, DmtxImage *img, AlignmentGrid *grid, AppState *state, int screenY, int screenX) { DmtxVector2 pTmp, pCtr; DmtxVector2 gridTest; int shiftX /*, shiftY*/; /* int rColor, gColor, bColor, color; */ /* Sint16 x1, y1, x2, y2; */ int extent = 128; int modulesToDisplay = 16; int dispModExtent = extent/modulesToDisplay; /* int row, col; */ int /*rowBeg,*/ colBeg; int /*rowEnd,*/ colEnd; SDL_LockSurface(state->picture); pTmp.X = pTmp.Y = state->activeExtent/2.0; dmtxMatrix3VMultiply(&pCtr, &pTmp, grid->raw2fitActive); gridTest.X = pCtr.X * grid->colCount * dispModExtent; gridTest.X += (gridTest.X >= 0.0) ? 0.5 : -0.5; shiftX = 64 - (int)gridTest.X; colBeg = (shiftX < 0) ? 0 : -shiftX/8 - 1; colEnd = max(colBeg + 17, grid->colCount); /* something is uninitialized ... verify in cygwin fprintf(stdout, "colBeg:%d colEnd:%d\n", colBeg, colEnd); fflush(stdout); gridTest.Y = pCtr.Y * grid->rowCount * dispModExtent; gridTest.Y += (gridTest.Y >= 0.0) ? 0.5 : -0.5; shiftY = 64 - (int)gridTest.Y; rowBeg = (shiftY < 0) ? 0 : -shiftY/8 - 1; rowEnd = max(rowBeg + 17, grid->rowCount); for(row = rowBeg; row < rowEnd; row++) { y1 = row * dispModExtent + shiftY; y2 = y1 + dispModExtent - 1; y1 = (extent - 1 - y1); y2 = (extent - 1 - y2); y1 = Clamp(y1 + screenY, screenY, 128); y2 = Clamp(y2 + screenY, screenY, 128); for(col = colBeg; col < colEnd; col++) { rColor = dmtxReadModuleColor(img, grid, row, col, 0); gColor = dmtxReadModuleColor(img, grid, row, col, 1); bColor = dmtxReadModuleColor(img, grid, row, col, 2); color = (rColor << 24) | (gColor << 16) | (bColor << 8) | 0xff; x1 = col * dispModExtent + shiftX; x2 = x1 + dispModExtent - 1; x1 = Clamp(x1 + screenX, screenX, 128); x2 = Clamp(x2 + screenX, screenX, 128); boxColor(screen, x1, y1, x2, y2, color); } } */ SDL_UnlockSurface(state->picture); } /** * * */ void DrawPerimeterPatterns(SDL_Surface *screen, GridRegion *region, AppState *state, DmtxDirection side, DmtxBarType type) { DmtxVector2 pTmp, pCtr; DmtxVector2 gridTest; int shiftX, shiftY; int extent = 128; int modulesToDisplay = 16; int dispModExtent = extent/modulesToDisplay; Sint16 x00, y00; Sint16 x11, y11; pTmp.X = pTmp.Y = state->activeExtent/2.0; dmtxMatrix3VMultiply(&pCtr, &pTmp, region->grid.raw2fitActive); gridTest.X = pCtr.X * region->grid.colCount * dispModExtent; gridTest.X += (gridTest.X >= 0.0) ? 0.5 : -0.5; shiftX = 64 - (int)gridTest.X; gridTest.Y = pCtr.Y * region->grid.rowCount * dispModExtent; gridTest.Y += (gridTest.Y >= 0.0) ? 0.5 : -0.5; shiftY = 63 - (int)gridTest.Y; /* Calculate corner positions */ x00 = region->x * dispModExtent + shiftX; y00 = region->y * dispModExtent + shiftY; x11 = x00 + region->width * dispModExtent; y11 = y00 + region->height * dispModExtent; DrawPerimeterSide(screen, x00, y00, x11, y11, dispModExtent, side, type); } /** * * */ void DrawPerimeterSide(SDL_Surface *screen, int x00, int y00, int x11, int y11, int dispModExtent, DmtxDirection side, DmtxBarType type) { Sint16 xBeg, yBeg; Sint16 xEnd, yEnd; int extent = 128; const int screenX = CTRL_COL1_X + 1; const int screenY = CTRL_ROW5_Y; Uint32 color; switch(side) { case DmtxDirUp: xBeg = x00; xEnd = x11; yBeg = yEnd = y11 - dispModExtent/2; break; case DmtxDirLeft: yBeg = y00; yEnd = y11; xBeg = xEnd = x00 + dispModExtent/2; break; case DmtxDirDown: xBeg = x00; xEnd = x11; yBeg = yEnd = y00 + dispModExtent/2; break; case DmtxDirRight: yBeg = y00; yEnd = y11; xBeg = xEnd = x11 - dispModExtent/2; break; default: xBeg = x00; yBeg = y00; xEnd = x11; yEnd = y11; break; } yBeg = (extent - 1 - yBeg); yEnd = (extent - 1 - yEnd); xBeg = Clamp(xBeg + screenX, screenX, 128); yBeg = Clamp(yBeg + screenY, screenY, 128); xEnd = Clamp(xEnd + screenX, screenX, 128); yEnd = Clamp(yEnd + screenY, screenY, 128); if(type & DmtxBarFinder) color = 0x0000ffff; else if(type & DmtxBarTiming) color = 0xff0000ff; else color = 0x00ff00ff; lineColor(screen, xBeg, yBeg, xEnd, yEnd, color); } libdmtx-0.7.7/test/rotate_test/000077500000000000000000000000001423156660700165165ustar00rootroot00000000000000libdmtx-0.7.7/test/rotate_test/Makefile.am000066400000000000000000000006241423156660700205540ustar00rootroot00000000000000AM_CPPFLAGS = -Wshadow -Wall -pedantic -std=c99 check_PROGRAMS = rotate_test rotate_test_SOURCES = rotate_test.c callback.c display.c image.c dmtx.c EXTRA_rotate_test_SOURCES = callback.h display.h rotate_test.h image.h if TARGET_MACOSX rotate_test_LDFLAGS = -lm -lpng -framework OpenGL -lSDL -lSDLmain -framework Cocoa -lpthread else rotate_test_LDFLAGS = -lm -lpng -lGL -lGLU -lSDL -lpthread endif libdmtx-0.7.7/test/rotate_test/README000077500000000000000000000007111423156660700174000ustar00rootroot00000000000000This test program uses OpenGL (Mesa) to simulate camera input, and writes decoded Data Matrix streams to STDOUT. Right-click on the window to cycle through the test images. To add your own images for testing, just overwrite any image in the "rotate_test/images" directory with another 256x256 PNG file. If you want to add brand new images then you will have to update the gFilename[] and gFileCount variables at the top of rotate_test.c to include them. libdmtx-0.7.7/test/rotate_test/callback.c000066400000000000000000000237271423156660700204310ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file callback.c */ #include #include "rotate_test.h" #include "callback.h" #include "display.h" #include "image.h" #include "dmtx.h" #define DMTX_DISPLAY_SQUARE 1 #define DMTX_DISPLAY_POINT 2 #define DMTX_DISPLAY_CIRCLE 3 /** * * */ void BuildMatrixCallback2(DmtxRegion *region) { int i, j; int offset; float scale = 1.0/200.0; DmtxVector2 point; DmtxMatrix3 m0, m1, mInv; int rgb[3]; dmtxMatrix3Translate(m0, -(320 - 200)/2.0, -(320 - 200)/2.0); dmtxMatrix3Scale(m1, scale, scale); dmtxMatrix3Multiply(mInv, m0, m1); dmtxMatrix3MultiplyBy(mInv, region->fit2raw); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT, GL_LINE); for(i = 0; i < 320; i++) { for(j = 0; j < 320; j++) { point.X = j; point.Y = i; dmtxMatrix3VMultiplyBy(&point, mInv); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 0, &rgb[0]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 1, &rgb[1]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 2, &rgb[2]); offset = (320 * i + j) * 3; passTwoPxl[offset + 0] = rgb[0]; passTwoPxl[offset + 1] = rgb[1]; passTwoPxl[offset + 2] = rgb[2]; /* dmtxPixelFromColor3(passTwoPxl[i*320+j], &clr); */ } } DrawPane3(NULL, passTwoPxl); glViewport(646, 324, 320, 320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5, 319.5, -0.5, 319.5, -1.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(0.0, 1.0, 0.0); glBegin(GL_QUADS); glVertex2f( 60.0, 60.0); glVertex2f(260.0, 60.0); glVertex2f(260.0, 260.0); glVertex2f( 60.0, 260.0); glEnd(); } /** * * */ void BuildMatrixCallback3(DmtxMatrix3 mChainInv) { int i, j; int offset; float scale = 1.0/100.0; DmtxVector2 point; DmtxMatrix3 m0, m1, mInv; int rgb[3]; dmtxMatrix3Scale(m0, scale, scale); dmtxMatrix3Translate(m1, -(320 - 200)/2.0, -(320 - 200)/2.0); dmtxMatrix3Multiply(mInv, m1, m0); dmtxMatrix3MultiplyBy(mInv, mChainInv); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT, GL_LINE); for(i = 0; i < 320; i++) { for(j = 0; j < 320; j++) { point.X = j; point.Y = i; dmtxMatrix3VMultiplyBy(&point, mInv); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 0, &rgb[0]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 1, &rgb[1]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 2, &rgb[2]); offset = (320 * i + j) * 3; passTwoPxl[offset + 0] = rgb[0]; passTwoPxl[offset + 1] = rgb[1]; passTwoPxl[offset + 2] = rgb[2]; } } DrawPane4(NULL, passTwoPxl); glViewport(2, 2, 320, 320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5, 319.5, -0.5, 319.5, -1.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(0.0, 1.0, 0.0); glBegin(GL_QUADS); glVertex2f( 60.0, 60.0); glVertex2f(160.0, 60.0); glVertex2f(160.0, 160.0); glVertex2f( 60.0, 160.0); /** glVertex2f( 60.0, 60.0); glVertex2f(260.0, 60.0); glVertex2f(260.0, 260.0); glVertex2f( 60.0, 260.0); */ glEnd(); } /** * * */ void BuildMatrixCallback4(DmtxMatrix3 mChainInv) { int i, j; int offset; float scale = 1.0/200.0; DmtxVector2 point; DmtxMatrix3 m0, m1, mInv; int rgb[3]; dmtxMatrix3Scale(m0, scale, scale); dmtxMatrix3Translate(m1, -(320 - 200)/2.0, -(320 - 200)/2.0); dmtxMatrix3Multiply(mInv, m1, m0); dmtxMatrix3MultiplyBy(mInv, mChainInv); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT, GL_LINE); for(i = 0; i < 320; i++) { for(j = 0; j < 320; j++) { point.X = j; point.Y = i; dmtxMatrix3VMultiplyBy(&point, mInv); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 0, &rgb[0]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 1, &rgb[1]); dmtxImageGetPixelValue(gImage, (int)(point.X+0.5), (int)(point.Y+0.5), 2, &rgb[2]); offset = (320 * i + j) * 3; passTwoPxl[offset + 0] = rgb[0]; passTwoPxl[offset + 1] = rgb[1]; passTwoPxl[offset + 2] = rgb[2]; } } DrawPane5(NULL, passTwoPxl); glViewport(324, 2, 320, 320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5, 319.5, -0.5, 319.5, -1.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(0.0, 1.0, 0.0); glBegin(GL_QUADS); glVertex2f( 60.0, 60.0); glVertex2f(260.0, 60.0); glVertex2f(260.0, 260.0); glVertex2f( 60.0, 260.0); glEnd(); } /** * * */ void PlotPointCallback(DmtxPixelLoc loc, int colorInt, int paneNbr, int dispType) { int color; DmtxImage *image = NULL; DmtxVector2 point; point.X = loc.X; point.Y = loc.Y; switch(paneNbr) { case 1: glViewport(2, 324, 320, 320); break; case 2: glViewport(324, 324, 320, 320); /* image = passOnePxl; */ break; case 3: glViewport(646, 324, 320, 320); break; case 4: glViewport(2, 2, 320, 320); break; case 5: glViewport(324, 2, 320, 320); break; case 6: glViewport(646, 2, 320, 320); break; } if(image != NULL) { switch(colorInt) { case 1: color = ColorRed; break; case 2: color = ColorGreen; break; case 3: color = ColorBlue; break; case 4: color = ColorYellow; break; default: color = ColorWhite; break; } plotPoint(image, point.Y, point.X, color); /* plotPoint(image, point.Y + 1, point.X - 1, color); plotPoint(image, point.Y + 1, point.X + 1, color); plotPoint(image, point.Y - 1, point.X - 1, color); plotPoint(image, point.Y - 1, point.X + 1, color); */ } else { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5, 319.5, -0.5, 319.5, -1.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT, GL_LINE); switch(colorInt) { case 1: glColor3f(1.0, 0.0, 0.0); break; case 2: glColor3f(0.0, 1.0, 0.0); break; case 3: glColor3f(0.0, 0.0, 1.0); break; case 4: glColor3f(1.0, 1.0, 0.0); break; default: glColor3f(1.0, 1.0, 1.0); break; } if(dispType == DMTX_DISPLAY_SQUARE) { glBegin(GL_QUADS); glVertex2f(point.X - 3, point.Y - 3); glVertex2f(point.X + 3, point.Y - 3); glVertex2f(point.X + 3, point.Y + 3); glVertex2f(point.X - 3, point.Y + 3); glEnd(); } else if(dispType == DMTX_DISPLAY_POINT) { int x = (int)(point.X + 0.5); int y = (int)(point.Y + 0.5); glBegin(GL_POINTS); glVertex2f(x, y); glEnd(); } } } /** * * */ void XfrmPlotPointCallback(DmtxVector2 point, DmtxMatrix3 xfrm, int paneNbr, int dispType) { float scale = 100.0; DmtxMatrix3 m, m0, m1; if(xfrm != NULL) { dmtxMatrix3Copy(m, xfrm); } else { dmtxMatrix3Identity(m); } dmtxMatrix3Scale(m0, scale, scale); dmtxMatrix3Translate(m1, (320 - 200)/2.0, (320 - 200)/2.0); dmtxMatrix3MultiplyBy(m, m0); dmtxMatrix3MultiplyBy(m, m1); dmtxMatrix3VMultiplyBy(&point, m); /* PlotPointCallback(point, 3, paneNbr, dispType); */ } /** * * */ void PlotModuleCallback(DmtxDecode *info, DmtxRegion *region, int row, int col, int color) { int modSize, halfModsize, padSize; /* float t; */ /* Adjust for addition of finder bar */ row++; col++; halfModsize = (int)(100.0 / (region->mappingCols + 2) + 0.5); /* Because 100 == 200/2 */ modSize = 2 * halfModsize; padSize = (320 - ((region->mappingCols + 2) * modSize))/2; /* Set for 6th pane */ DrawPaneBorder(645, 1, 322, 322); glRasterPos2i(1, 1); glPolygonMode(GL_FRONT, GL_FILL); /* Clamp color to extreme foreground or background color */ /* t = dmtxDistanceAlongRay3(&(region->gradient.ray), &color); t = (t < region->gradient.tMid) ? region->gradient.tMin : region->gradient.tMax; dmtxPointAlongRay3(&color, &(region->gradient.ray), t); */ if(color == 1) { glColor3f(0.0, 0.0, 0.0); } else { glColor3f(1.0, 1.0, 1.0); } glBegin(GL_QUADS); glVertex2f(modSize*(col+0.5) + padSize - halfModsize, modSize*(row+0.5) + padSize - halfModsize); glVertex2f(modSize*(col+0.5) + padSize + halfModsize, modSize*(row+0.5) + padSize - halfModsize); glVertex2f(modSize*(col+0.5) + padSize + halfModsize, modSize*(row+0.5) + padSize + halfModsize); glVertex2f(modSize*(col+0.5) + padSize - halfModsize, modSize*(row+0.5) + padSize + halfModsize); glEnd(); } /** * * */ void FinalCallback(DmtxDecode *decode, DmtxRegion *region) { int row, col; int symbolRows, symbolCols; int moduleStatus; /* DmtxColor3 black = { 0.0, 0.0, 0.0 }; DmtxColor3 white = { 255.0, 255.0, 255.0 }; */ symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, region->sizeIdx); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, region->sizeIdx); for(row = 0; row < symbolRows; row++) { for(col = 0; col < symbolCols; col++) { /* moduleStatus = dmtxSymbolModuleStatus(message, region->sizeIdx, row, col); */ PlotModuleCallback(decode, region, row, col, (moduleStatus & DmtxModuleOnRGB) ? 1 : 0); } } } libdmtx-0.7.7/test/rotate_test/callback.h000066400000000000000000000015021423156660700204210ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file callback.h */ #ifndef __CALLBACK_H__ #define __CALLBACK_H__ void BuildMatrixCallback2(DmtxRegion *region); void BuildMatrixCallback3(DmtxMatrix3 region); void BuildMatrixCallback4(DmtxMatrix3 region); void PlotPointCallback(DmtxPixelLoc loc, int colorInt, int paneNbr, int dispType); void XfrmPlotPointCallback(DmtxVector2 point, DmtxMatrix3 xfrm, int paneNbr, int dispType); void FinalCallback(DmtxDecode *decode, DmtxRegion *region); /*void PlotModuleCallback(DmtxDecode *info, DmtxRegion *region, int row, int col, DmtxColor3 color);*/ #endif libdmtx-0.7.7/test/rotate_test/display.c000066400000000000000000000124471423156660700203370ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file display.c */ #include #include #include #include #include #include "dmtx.h" #include "rotate_test.h" #include "display.h" /** * * */ SDL_Surface *initDisplay(void) { SDL_Surface *screen; SDL_Init(SDL_INIT_VIDEO); screen = SDL_SetVideoMode(968, 646, 16, SDL_OPENGL | SDL_RESIZABLE); if(!screen) { fprintf(stderr, "Couldn't set 968x646 GL video mode: %s\n", SDL_GetError()); SDL_Quit(); exit(2); } SDL_WM_SetCaption("GL Test", "GL Test"); glClearColor(0.0, 0.0, 0.3, 1.0); return screen; } /** * * */ void DrawBarCode(void) { glColor3f(0.95, 0.95, 0.95); glBegin(GL_QUADS); glTexCoord2d(0.0, 0.0); glVertex3f(-2.0, -2.0, 0.0); glTexCoord2d(1.0, 0.0); glVertex3f( 2.0, -2.0, 0.0); glTexCoord2d(1.0, 1.0); glVertex3f( 2.0, 2.0, 0.0); glTexCoord2d(0.0, 1.0); glVertex3f(-2.0, 2.0, 0.0); glEnd(); } /** * * */ void ReshapeWindow(int width, int height) { glViewport(2, 324, (GLint)320, (GLint)320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /** * * */ void DrawBorders(SDL_Surface *screen) { /* window and pane borders */ DrawPaneBorder( 0, 0, 646, 968); DrawPaneBorder( 1, 1, 322, 322); DrawPaneBorder(323, 1, 322, 322); DrawPaneBorder(645, 1, 322, 322); DrawPaneBorder( 1, 323, 322, 322); DrawPaneBorder(323, 323, 322, 322); DrawPaneBorder(645, 323, 322, 322); } /** * * */ void DrawGeneratedImage(SDL_Surface *screen) { /* rotate barcode surface */ glViewport(2, 324, (GLint)320, (GLint)320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_BACK, GL_LINE); glEnable(GL_TEXTURE_2D); glPushMatrix(); glRotatef(view_rotx, 1.0, 0.0, 0.0); glRotatef(view_roty, 0.0, 1.0, 0.0); glRotatef(view_rotz, 0.0, 0.0, 1.0); glRotatef(angle, 0.0, 0.0, 1.0); glCallList(barcodeList); glPopMatrix(); } /** * * */ void DrawPane2(SDL_Surface *screen, unsigned char *pxl) { DrawPaneBorder(323, 323, 322, 322); /* XXX drawn twice */ glRasterPos2i(1, 1); glDrawPixels(320, 320, GL_RGB, GL_UNSIGNED_BYTE, pxl); } /** * * */ void DrawPane3(SDL_Surface *screen, unsigned char *pxl) { DrawPaneBorder(645, 323, 322, 322); /* XXX drawn twice */ glRasterPos2i(1, 1); glDrawPixels(320, 320, GL_RGB, GL_UNSIGNED_BYTE, pxl); } /** * * */ void DrawPane4(SDL_Surface *screen, unsigned char *pxl) { DrawPaneBorder(1, 1, 322, 322); /* XXX drawn twice */ glRasterPos2i(1, 1); glDrawPixels(320, 320, GL_RGB, GL_UNSIGNED_BYTE, pxl); } /** * * */ void DrawPane5(SDL_Surface *screen, unsigned char *pxl) { DrawPaneBorder(323, 1, 322, 322); /* XXX drawn twice */ glRasterPos2i(1, 1); glDrawPixels(320, 320, GL_RGB, GL_UNSIGNED_BYTE, pxl); } /** * * */ void DrawPane6(SDL_Surface *screen, unsigned char *pxl) { DrawPaneBorder(645, 1, 322, 322); /* XXX drawn twice */ glRasterPos2i(1, 1); if(pxl != NULL) glDrawPixels(320, 320, GL_RGB, GL_UNSIGNED_BYTE, pxl); } /** * * */ void DrawPaneBorder(GLint x, GLint y, GLint h, GLint w) { glDisable(GL_TEXTURE_2D); glColor3f(0.6, 0.6, 1.0); glPolygonMode(GL_FRONT, GL_LINE); glViewport(x, y, w, w); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5, w-0.5, -0.5, w-0.5, -1.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBegin(GL_QUADS); glVertex2f(0, 0); glVertex2f(w-1, 0); glVertex2f(w-1, h-1); glVertex2f(0, h-1); glEnd(); } /** * * */ int HandleEvent(SDL_Event *event, SDL_Surface *screen) { int width, height; switch(event->type) { case SDL_VIDEORESIZE: screen = SDL_SetVideoMode(event->resize.w, event->resize.h, 16, SDL_OPENGL | SDL_RESIZABLE); if(screen) { ReshapeWindow(screen->w, screen->h); } else { /* Uh oh, we couldn't set the new video mode? */; return(1); } break; case SDL_QUIT: return(1); break; case SDL_MOUSEMOTION: view_rotx = ((event->motion.y-160)/2.0); view_roty = ((event->motion.x-160)/2.0); break; case SDL_KEYDOWN: switch(event->key.keysym.sym) { case SDLK_ESCAPE: return(1); break; default: break; } break; case SDL_MOUSEBUTTONDOWN: switch(event->button.button) { case SDL_BUTTON_RIGHT: free(texturePxl); texturePxl = (unsigned char *)loadTextureImage(&width, &height); break; case SDL_BUTTON_LEFT: fprintf(stdout, "left click\n"); break; default: break; } break; } return(0); } libdmtx-0.7.7/test/rotate_test/display.h000066400000000000000000000020271423156660700203350ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file display.h */ #include #include #include GLfloat view_rotx, view_roty, view_rotz; GLfloat angle; GLuint barcodeTexture; GLint barcodeList; SDL_Surface *initDisplay(void); void DrawBarCode(void); void ReshapeWindow(int width, int height); void DrawGeneratedImage(SDL_Surface *screen); void DrawBorders(SDL_Surface *screen); void DrawPane2(SDL_Surface *screen, unsigned char *pxl); void DrawPane3(SDL_Surface *screen, unsigned char *pxl); void DrawPane4(SDL_Surface *screen, unsigned char *pxl); void DrawPane5(SDL_Surface *screen, unsigned char *pxl); void DrawPane6(SDL_Surface *screen, unsigned char *pxl); int HandleEvent(SDL_Event *event, SDL_Surface *screen); void DrawPaneBorder(GLint x, GLint y, GLint h, GLint w); libdmtx-0.7.7/test/rotate_test/dmtx.c000066400000000000000000000005251423156660700176400ustar00rootroot00000000000000#define CALLBACK_POINT_PLOT(a,b,c,d) PlotPointCallback(a,b,c,d) #define CALLBACK_POINT_XFRM(a,b,c,d) XfrmPlotPointCallback(a,b,c,d) #define CALLBACK_MODULE(a,b,c,d,e) PlotModuleCallback(a,b,c,d,e) #define CALLBACK_MATRIX(a) BuildMatrixCallback2(a) #define CALLBACK_FINAL(a,b) FinalCallback(a,b) #include "../../dmtx.c" libdmtx-0.7.7/test/rotate_test/image.c000066400000000000000000000137661423156660700177610ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file image.c */ #include #include #include #include #include #include #include #include "dmtx.h" #include "rotate_test.h" #include "image.h" /** * * */ unsigned char * loadTextureImage(int *width, int *height) { unsigned char *pxl; int error; char filepath[128]; strcpy(filepath, "images/"); strcat(filepath, gFilename[gFileIdx]); fprintf(stdout, "Opening %s\n", filepath); pxl = loadPng(filepath, width, height); assert(pxl != NULL); gFileIdx++; if(gFileIdx == gFileCount) gFileIdx = 0; /* Set up texture */ glGenTextures(1, &barcodeTexture); glBindTexture(GL_TEXTURE_2D, barcodeTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); /* Read barcode image */ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, *width, *height, GL_RGB, GL_UNSIGNED_BYTE, pxl); /* Create the barcode list */ barcodeList = glGenLists(1); glNewList(barcodeList, GL_COMPILE); DrawBarCode(); glEndList(); return pxl; } /** * * */ unsigned char * loadPng(char *filename, int *width, int *height) { png_byte pngHeader[8]; FILE *fp; int headerTestSize = sizeof(pngHeader); int isPng; int bitDepth, color_type, interlace_type, compression_type, filter_method; int row; png_uint_32 png_width, png_height; png_structp png_ptr; png_infop info_ptr; png_infop end_info; png_bytepp row_pointers; unsigned char *pxl = NULL; fp = fopen(filename, "rb"); if(!fp) return NULL; fread(pngHeader, 1, headerTestSize, fp); isPng = !png_sig_cmp(pngHeader, 0, headerTestSize); if(!isPng) return NULL; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) return NULL; info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } end_info = png_create_info_struct(png_ptr); if(!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return NULL; } if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return NULL; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, headerTestSize); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bitDepth, &color_type, &interlace_type, &compression_type, &filter_method); png_set_strip_16(png_ptr); png_set_strip_alpha(png_ptr); png_set_packswap(png_ptr); if(color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bitDepth, &color_type, &interlace_type, &compression_type, &filter_method); *width = (int)png_width; *height = (int)png_height; row_pointers = (png_bytepp)png_malloc(png_ptr, sizeof(png_bytep) * png_height); if(row_pointers == NULL) { fprintf(stdout, "Fatal error!\n"); fflush(stdout); /* XXX finish later */ ; /* FatalError(1, "Error while during malloc for row_pointers"); */ } for(row = 0; row < *height; row++) { row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); } png_read_image(png_ptr, row_pointers); png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); pxl = (unsigned char *)malloc((*width) * (*height) * 3); assert(pxl != NULL); for(row = 0; row < *height; row++) { memcpy(pxl + (row * (*width) * 3), row_pointers[(*height) - row - 1], (*width) * 3); } for(row = 0; row < (*height); row++) { png_free(png_ptr, row_pointers[row]); } png_free(png_ptr, row_pointers); fclose(fp); return pxl; } /** * * */ void plotPoint(DmtxImage *img, float rowFloat, float colFloat, int targetColor) { int i, row, col; float xFloat, yFloat; int offset[4]; int color[4]; row = (int)rowFloat; col = (int)colFloat; xFloat = colFloat - col; yFloat = rowFloat - row; offset[0] = row * img->width + col; offset[1] = row * img->width + (col + 1); offset[2] = (row + 1) * img->width + col; offset[3] = (row + 1) * img->width + (col + 1); color[0] = clampRGB(255.0 * ((1.0 - xFloat) * (1.0 - yFloat))); color[1] = clampRGB(255.0 * (xFloat * (1.0 - yFloat))); color[2] = clampRGB(255.0 * ((1.0 - xFloat) * yFloat)); color[3] = clampRGB(255.0 * (xFloat * yFloat)); for(i = 0; i < 4; i++) { if((i == 1 || i== 3) && col + 1 > 319) continue; else if((i == 2 || i== 3) && row + 1 > 319) continue; if(targetColor & (ColorWhite | ColorRed | ColorYellow)) img->pxl[offset[i]*3+0] = max(img->pxl[offset[i]*3+0], color[i]); if(targetColor & (ColorWhite | ColorGreen | ColorYellow)) img->pxl[offset[i]*3+1] = max(img->pxl[offset[i]*3+1], color[i]); if(targetColor & (ColorWhite | ColorBlue)) img->pxl[offset[i]*3+2] = max(img->pxl[offset[i]*3+2], color[i]); } } /** * * */ int clampRGB(float color) { if(color < 0.0) return 0; else if(color > 255.0) return 255; else return (int)(color + 0.5); } libdmtx-0.7.7/test/rotate_test/image.h000066400000000000000000000015621423156660700177550ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file image.h */ #ifndef __IMAGE_H__ #define __IMAGE_H__ #define IMAGE_NO_ERROR 0 #define IMAGE_ERROR 1 #define IMAGE_NOT_PNG 2 typedef enum { ColorWhite = 0x01 << 0, ColorRed = 0x01 << 1, ColorGreen = 0x01 << 2, ColorBlue = 0x01 << 3, ColorYellow = 0x01 << 4 } ColorEnum; /*void captureImage(DmtxImage *img, DmtxImage *imgTmp);*/ unsigned char *loadTextureImage(int *width, int *height); unsigned char *loadPng(char *filename, int *width, int *height); void plotPoint(DmtxImage *img, float rowFloat, float colFloat, int targetColor); int clampRGB(float color); #endif libdmtx-0.7.7/test/rotate_test/images/000077500000000000000000000000001423156660700177635ustar00rootroot00000000000000libdmtx-0.7.7/test/rotate_test/images/test_image01.png000077500000000000000000000415031423156660700227610ustar00rootroot00000000000000PNG  IHDRkXTPLTE  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~wP IDATx}w\Tgm{o 7jE,QPi*QA1jbBoF&"Ma" [wv o>|ߝ̀y{ϯo@A@A@A@A@A@A@A@A]s /w?}קCj:|x͛7o?~}^?2?w>~}rxȻ e_޻_޶GO=y:ݟOC 7m;W3Zz{Z]fՄLgW3>v33iaɌo^o|%u~kbN&UvV}]U~ 3Ug1V^™@3 y|guLgo|z,-~ǢSjjR⮸$ gGgKkSsL**LvwN)wJwN1M}pw¯ 7 qv[oˬe^&wUWuzOV΁Tձr/n6s܍[O߱uSoV»o_I=n?Sۿ 6{kݦ6_nޯzP̋s'GKO^Ng=~3`oihR0%dP=C7֗_r==AF.G}'?zӡާc.W6H*WZW Wŧ*R/ņD1J=a/0NiAR2Uӊax>4Y{Z$):[d^S~;sh8t^Ƽ`*3WBViJ+iE u7U/yG@v_3Uz?W*zqu ukj: w͘%.mdj+Mv{I/k40Us-+,TP5r[cKg73b;[jf2ջ| -nnbis)$-Ԏ9L L9_r %o7JMz(*Hd;O1]Kpft uZ/Wb9-#k0=RUהp=;yhw=y!>%-===3+Zje?XظG&0+RBR`,3zXgɆ_TTl[Ac(53W]؉cG\}s0>#1A!ag.76>H_"-1"Ֆ؂˂9p61ףN9`o~@V&jh~TL^ .DO07- &0baZɭs;[[;GNF^ϩ5-VjZB-Q1~-XrLdXXXxi u0 M ?r ݝ0Ɓ|j9 7c^Ud\lnv OS*L ܧpn+wZJSSA{3#I]rnffgܸyNiu ?.ha`Q]Gw;ɷU-9 q'0@'+lY\EQAAIMx’6T3VXYXQVWxzӄgK+,5:UZ_]̪ OgWzs!Nb̩'O`|-".Rͺ/e+-++N?>b0{oXT *| -V_qsM0 pmPj._%$\6T6nԈc&W񎼔 g͜{̆n?~~>B0_Eo32r*N`y] ҟ>34fQc̖[ak]+%Hʯ0>t$ ㇭W,OFFJ\tƤ&L\DEEY!XVZ\Thڤ ƌ9r$044߲? ^S+ylUrr&TJCv:m5eUM o> ƪӳg~Z+69Ӡfki3nl//%1GL>~6GjwC fk-cBM+U/I>u;A<{66丳Ͷmv=Xc5ѐ4T7D&m`N^d"Hd| Hf#FOcمe75f k{9glWȉc~9̦&f(>O;u* S 9InY,6yY Q_E# c@DGIJLT/`5 N2 w·IF]acw*J?0R`R-Q)%]>w#NdQ^Jվü&\ߏteZKAzY #k ;{{;9nOoޝ;m#7&9ey3'-<9`RJLh> rwQJ@dfYuE%[KzAlɿ|X[IVrؾ`ǬkcipsƝΧ] ­&6(*/κck:tڼVCN_4(&n)w;p9s50E\AF4uS'vF4Q4(髷߽yTWS@e S sp3Oh*K%hA_SYnj\\RǼ;'EO ;i8ƍC}Qc'Ro:9iOk]UЊ^F&9kVnH.V\"02Vlq9p2} iqΡy2,66@Ub\!|GODlF>~ޖ;E6z0e,0iWQ;z}my˪؂ Pr1&ďxQFtxs 3V-_K"D)9,+v:894)ɈK-ZR[W}w"){C|oQfNEk;Z[8#Fo7r=tbVnA'&"no`=R'z+M H߁Qy'D 0aJs94,1Lk,8/GpG_._8nX_+-ʺpqMVTnj ˜ /`?Ǝ=fzja .򠩺i"')2NH1߮^p SDۍI6R"٢"eI8bdf_%cHW\h%$TZ,?V6Yg.B1 7&(pzK ^ )*"Faƍ7in5k Qf,] D'mLuՔ LC&b~s\pKϙp_$ KFsDH˩l3%W.W], NR{OFLb":->T0w4ms~u℧ HbR/i0B"+ d^Ȣ1X(%*"., 6d\؄ 'qt]vn|0uDqh6a$,?{|yw/YaHh@7c16bԄU|T And S8Nh"U(,X}nRqE}Lwof^RhL1wo_=HG|"p;(fFZ*.zXLWrv3; XyX íyB3D/]OvM@ b,/2CHCPGSt&Z"TM$j(4ȃ(7z$>ݑ!p5gN."l#ɛ4nD b,>}&%JA SZ""t4=dVW`"TIDAT2hG=WޟA":8nuŝcy*vA E7])zY;XEV%ʉnYUQVˮHHY (2}Ge7E}`&! ~iqg E3SL^E_LHMHOuidzT6u4e{@EQ|"^l˦ (,PI<xt( Pr$eӾHH3q HW{@ӡs4!Br΃ήRIhH`NNr*ATǪ_.8G# H{k\ߑ^$8wuE)Q6-W?hv is##Ӎt e[̌ ICZKq9RU[]\\MYYUC{SD|Q"6􀝱X`4 U5!$'9kBXE=[z~0-XWMa!x,Royl 48z,RPqogs}m}3?u_9AD*-J=AAQ\ҭ;HZQ{\jm Ueew1-#@QkG$:E ( $GaDKKKIr>+ ;)>^%Ҋ~m4EI㏂s!m#8Co?|x];mqô x <w{S4$8u0d/r5͖dAjH$' `Δ91A>8ƃC鏅 44RpSa CsG%2$<MN !6 HT 9rp$ 5l"/%ed%^k EE8wY[mF^O!H_'r%>l^ZT9)~x<-G" |(u,^SRdĸA]I{Ǯ\=up֦n3 8G{$8"AF+sJ+[/L틾撤^D`"kt(WWYĥfW;𣾶 Ln msGކ#Q1HZQ˂QC-g}諧mUI$ .مm{J=u뼷?\V6+.nlZ{cuYqV[\DYWZ9**y>{߾jdDJ$mT; 4mpZyǓWo߽yX̭=邿˲sU B++I#9V@; 6۸E$!X>}ZUt+>ƛH)EK,݅f0Ϊ\ G+Jʀe_K|uc]PrS.FyhpVuB>_ {ݠEV>,5A%Fv!)1._JP**-Xfh~'Tͤs.)rOyqEKdW҇z>΋qduLӶ3| s{"z4\د߾{vS%GhM#~"0xg;;(DWrJfA~Ɋi܈Y Z93| ؇U@ :fxk{gOD@o4Lu[5^}K._.רZXk7]N(ú'ʹkgZ!Kx 3 @1K"85~$bTdκeHT+E)74K ;NCXI܆i7.J H+*MeUCĴ(3mU5[]?n3 p='~xh>%UyMUU]7,Z٢bRJ#g!/"Ɓ RSWv`3*rjp*!@GD?Lբn`YLOMFB,xZ&~ @:G(GED;療$}CBSσf(vTPN<1=\]a;k#>FGk8E"4K 8( 5BO0v)eBte";O(ءڎB\^sTTƣȡB1K+ b @fE8fC_8:𺁲9.=u¡ w!if#h--a `u<_Ϊ/lv4?1%=y!NQ{ 6B'o)('9}jG-.X|w!Q勧5 A Ӑ(vuuy99뾍cr>XrS 'oPeVCôȫ ,n kG)nj2Hrzy9/+劲ӧ E T_Du-ռhaqvr-+,#z bE=l"Nك]aizm/PBG,y^\Co/xU:TAfШE`y2$RZj]=]aRSZXrR.n G@@bG&܌xX]K+GVy⮃;̊3TXvx:TTBmMJ|@7CTy=*s@qct9 .{Im%3S ː=nogqE'w dg%G]̂W"v[C5UN]=a;  J O(+.,Cpszh`MМg<{Guu6QHSO $bM=MwلX|w!|Jr*|;bԘ fnv z\60?-U׵pΪy (^ Z0(G|n*ɿ ̓RӠ)(4B;|a52=s#ښ8|o+&ȅu} nNzz-E[0YU\zpeK ܳ Sܢzl똼\LC}=ZONO_\FFȒ scoU&mzhP3<'Eyȉ@Ycǎ!v eOqZBSu"KU544)K6o\%lP3\X>;*v߼w S 4oGi wp'FuEQi dxxWrR2KtÅ_HJ2@"ӦR^9&1q4]g'L4e,q9%(Ėlޜ<$#H O=Bt2@C5ihś/\I1; 'f/S6qh8MLJ%^p@ugApUx(P&"pٟ!B(B?@QRhT=AHr DSs)_2L5AT[)HDf7)2w"7CyNP1'9~A I5@R0LUaQ Bn0"PdNk?.3; BOY|XdNacA䃟g )#Žêϣ6MdC"(PLqމyiT \SjƸ=I xb.%;iT KM-nÐn0Ke@/La8A2X~1iXt)nP=eFa_6XB3#Gqtj%D5%z[qZ#R@7ĥJ hiyyKdHzp7/- ;fAi=fzŢC4AEΜt4}>6E /޼}b=\^VW@x)\JVFVg̳u5 ; jhImGԡq rgPJiTL~@ 11,ާ 2*eC{JR•sHShf!n6guUԄAWOg~EDNi(}!(g`fzf`. A:a 24a|vpdF wh; NongC̋ϟWf_4!xCSEd!1ү݇en^\QѿoFGҫf) G-JUû&XyO$n!Oڬsao<%"nG3B (5?W؆i_ %\ωN ֋W'47V]F"ㆵAr76%:0TC]8Y;Ar  39|$㦓 j@ &Jrҡgf&FzTaKol4;+ o:C.zEp-<)?%W#la8 &o] q2_LKC]MK[17 r 68szbhg:M8 }/9v)Z|j!jN!`8=c*-U%D(5Gx1NB3=W`)HRvy'-O ҂7_2:eZhL0n o7-DvHW]i;iCʮ(GEvyÒl,9|x }Mf1̚6yLInolbIcBvq!) d5gOTJpJfՌff%_-̃@Ec.DtN)kE AS1qP(2\+Kq"Of攮T] ZAp!ZSQ[#~2F5a r]pqms9/v8!M?|rJ;hO5%lK5\*Ccc%o @F(p1__dhf;SPnrܰ-hA٢ؠQ]@_]-EY2Fha u=|2̹3o}^O6=a0o=>2\QCƒnPĄ(Ǥ /TSVQQQV/9WbAZo 426[p 䧇 A^Cc- V.F_PgM!`MU]$'/HVF7:O?VAk^ںvSP^NnL.CD4mj%9s$/VD@EsKHoY jqSݼ{'Q/zWYwځQEML02ۼ= #{3cI5>V..|Uc\^qyPsȗP!Ei parc8;!O*PsܚY~'b.*/qM1Dsc3"X}!'Myo 7:HxdF5!`SX 0ag:4\eieTAZr(zPt,cA 7^ M)'|2t"ca/$SW|dhPφ6!EzC75vV w2$aBk*kBA'n1wıjmC 0}7^KZV}-5x^E!L1T$pe&{p/NWU>?ﳡސ@U,ĢF=  h!'GEc.CjCw^y\gQy=U&o⍢Çȼyq!cRFya#S GlHFQ*8!/7 3.  H_hUpD܉>-C/+8?A=!u5+A)+ EB¢FA8 @z6Yӧ)E \T&!N'OhgM2)sB#?j. l%&w y"M1+*/Qd0ʓQyC>R:K@n*`K{pA>ungφfhȳ]АO7=*D Rvoۯx`Gkʹ/7oC\O^箤ߩl{7 e_[MYAv7Ye5m}/֓ Z߽`5TZ AoCh PT^O޿}?z{"5t?㯏??WQ o^                o/(|k.IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image02.png000077500000000000000000000712421423156660700227650ustar00rootroot00000000000000PNG  IHDR?1 IDATxy$U'|{/rZWI}m,/xl`lb3,7|9g6|0 f xfRK^ݵgF{EDfu[.PTvdd,/p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p先ιr.VN8\ 1@Hb/8Ger_~gƐ1 ӛ<+U pE Q a??(x? OVie,Z>>|O.V+ge-s׫~oL%N_[OO6> Kuk:8Iq2jtEH%˟u#wAgj'P(U7$VZƌ!Da[8S[}*JqR#}І^뷅DJBe[^|RO-3RZ(ʝSȒ[*q;Ze-GQU$Dpp2׃ػ*!@HwVJg-s ٹc~ӟ~+x}_roch ` v Ƃ1@@%hpkQJ!Ęc@c ks\0 kSrwdQHGp)߮YPty^wW$w;?UQH&_k1O* )~;@P YI*inmNon4FGǦ'mSGOw =KIZRjnǙ4hc0I%֐!®I"H;1Ql-"j##kc5?\"%DM0!9'2)D9UuZ)߱I!#*[2~'D(quE-Y)=f-HeV* C" EH R)˟[+J! PI%BJ)PW^~somm1:1,,s}_ʧϟ~3XFay_k-9m iIRZ-1tր 0F85"eFRjmkL"g)ЋB ZC>Q_)>R *P<_$QR<|]ϳ%%J  Ї T?!TJd  !TR*DdCd@&Nˮx=MNm=?k_oB'D"l@d5u:c9Ib<9t5X@C֑#8*X!PjX"RB&\c#GX"#["'x)4~s:MѠ LeX`[ ! WfeyUPx)0RgEUfBWkcV}͕\}ŵ/6o{~SWDK FBAwy!ypE"/Ց809={X!!1م# z90t!B)%I)B Vk!&Z"Kʭ7?[0"Jī{'P$E'.&cdžwxrZ`DN[+$kk:鮎wo(n5Ez`mJXKA]Ye I%,\M.SQgKՊD!]C%cj*ƉQ.O"`\U\e8,{PD4hGjmHf:wZ;,c06V#a_$F$Iz$DԉjEsJ6??sݮv%؋w)5na0V(I D MLXDɧ3 =~í9[8x>2J?ɳ'ࠊ9ӮWG\ic+JvmϨvC'+_<*gC%F*4ZV艧ڋo|멧Ʒj1F;爬sNΞI "F q"8P0~ޖp`R$JDi+E5FJ$LgWſ=Q@:*D$kQr.t9֬,pn+۔ގ̀Gvo3Ԃ UHa-4|Br+Ɓ RZ!%CE)G ] (A; @p@:3W@('&s3,R-˃ D3XST H6 dv~!dl,Еy@qЏ͗!䳡d KBoU}ZcOl ޒvwH 96>ӆ]lΖgႂ>l1M%Rb~C9+Ub#r@z@@)\ӖwBK^~ށGlVSA/ޕ#(!7'S=r!( 0#bS(Jy(U B2aDaǓ9'AX+DRJcD!\"%(0atLBČA@ I?w*,0j< "R*m?=pnQw,{ę\*섖_vB2 Z kLv 26LY0צM*?`^8WpLlBK]{H%10Xxc"^aAQe#D4"6@O. a uA+1?066o=8ſ?Ad,8@!) &0,')*@N! BLw25*B$J@*?,| DI(CK?Քzrw@AS;(6 < dAŹ3x+slxel($"e[lAd*1ЇЗTOS@ = RgC!l d^&w CkF9+' l*B@{R(~:JڼS= \]PE}rBσ@OpX!Oe3@]ahmI9Dp@@+!{Bs,y=[ j/+B=!U% P,}!EJ| N9($f֠T?sW,IZC>QaJd 1Π `5~<o%FlPiJIm2Ke&WETΙH:CIW$& FJ}]w*A̽徕Y#,% >C$Dv @^awA@Bx1K8 !HG1HAJ̓戓|Nt!o3=Dk+1c>'9EDhM)dVp6#Q[% U;O*kJ9ƉZEjNڨ"5*HH,T%$~ù7~g kNT'?\X\Y̱=|#/@YaLah~an0R aix8j*0 e9iof-c"9sOGrbu`a"iY)*>F,پaeQIbBX62Ӈr@p #Ľ| e% (8.w#|Ͻ-FFf½;;JN\ ˅J8"gK(ddSKP@E(d0IRsLcJ9`Y-G AyT6D:ZN(6A8HS$|ŝlJ.~o?x(S sE”/; 0[Ɩ G [ĂW15q )ljŻGw;ۿϟ/'w~k_C%w_>%5.'9pB!go z c3ۄc;ܥWBƩ0@ݣbAVp"Iq`X$f!dH}y {R"GEQQ"ɧ~.4{bz oE~(=?x#ό`k8 P;C@L*>刯NI#'N֪5!\>~wsq7w~l}x kڦa\g %DHJ[@u9K,RB:r)i@94Z qԡ%=B(=Br PXBWtLCaKsYά= =BD&AaiS9CU 1MJAwXJ-"Ng5RWh36:Z1F?}sm7⋾ep ,a! rBXo 0T6*yV% WdVK c/\Q,d(6}y0ޥأ!%o""9 \!?3#R( L9#@! ^ = ̀ vpQYB upzAPYA)#%Al{ PH˄Fdk*Lmv !:#޳3s6\u{_K yacL#2A,A[ާV+K]4?zR m>ShK $&UV\T?j&|eY8uO^0#BG{ԝ*YĶ]!Z6/pfn[\xwZܱ}fÞ4GbB%n@A@)Y9 pVdf[VQB͇ˊD_o_}= E?P ލ(6]sQI 턁_27rvJ*CN 7ggƒ>w -S̊ !˩@QqEQbcs\<+_`ۖ=C?~B:u9R;^?W]E7?:RǙ8A( yPSakzɭ/WQuZ_w4Gj#*IZwoo{[<~ً_s7W˜ri@ ͻ@٠ %wb{JAK/?1!5|õ>g{vݻ[X\Ͽǟ:q4j2񹇎\53'_Wݿ?7Ɲʱ0, "AuF(P:+, p|̞|uW?Շo}c / ht*٤٨^OMN}ϝ0ž {HD$L۠YR7!3N#/upuKn} o~H^xR67N4O,nh©=9>q]bBc i>+*L'L5COKƆ0\gnO^_Hզ7M׫FctvV6MNY_o}Z \{ _ N 0\gy=7~֛ǎo4FuĮ;<__tUA Z|(p k&rY$sT3p%^qF0G?}T5@.$P$.lXV0j)Tu733F'G叿U?~u? N+%gf6L_Z}︍ 3XZ p>|]v}eWuY ڎkc@<րBn71c],K=߭%B~Bu hEWkr^RJ-.-BWD6v+&Szk˦W]?~y7}$ܩuRyoG]uӧ?^7~+ [x[6k\23+=GV1On޳kMy~ZzjxFW'军zX&gFI'}^C?23;swܱ{׽__<{C 0Kȟ/mqSQ.ߜ8}A䗊MӶ !pbf&7`M]t&mm,}:I9pwH@kAürT44c'NF&H'>ۉ⽗3?Ex^̟sbbӏe4Y(}]%60{W ޟYgq;vƶnC.))By*QJi:'/sѽa\&nPOk]{v8Z|#'yJbr%_g5;7W/套^?7|ݙ"$}Eiv:e#L Js>Vk|Wԉ'N7*A7^ʍT8PfXm?q4FcTGĒt$\u5۶^>^ֻ>Oٳ c۷mg:B6RXG*P߲ɰ0"!pǎ%#}Dnʬ߷0S~O\QOR N|h g{w`U"=Ծ ڹc{C7,v(8aeV22;Gt J+Gqc8!8&Ko޼}ttɥ1[tõ8cK>{Q|]{/~_cv\04ַlSEH-JەTs cDH[CBQKsPD0x\8r o'޵o߾OLGffz}tfmJew=Iz}o~___o_x+vj~PCEt xL7J\#r$Sm5C'W~o%4Ǿw(S?sNr<$MPKcij353ԯ]S/eϦ'S+9h &(t*HiI&Z҉@~b=(64-q-UdJ![xo>нr ߫91115;7;q :r[^tK.w⏧6n[^SeRcE-vHJft ͛ߧ:[UR,w^ }> =<XkE9a~~nfvvЁܰ7Gc?[ _r!htK%q DKIE(th)Zŏ@4pl$ @d @w(~AƯod$#)%B!,?~ȱZuۖmRbVVg&TPP9!r q^ "rR Щ 8OP (u~d (L}~98'#+=͑6EU½Hr` ,!j&=' 6J'@0`:Us yX+@. G<eoQəVkQw~߼{/T x{_(,_Z?*PϞ{EQL>jA 1B*. k8\Q^K=vDn3ߺt%~՚h T D8n0x$6锗\\XU0zv[Vы "OV oC1u?Of~e?LH>TSp\H: pI"{#/(=cQ<^ F]P+kĔ rNR3&Q 4~?y{kZݹp)L-S@ `X?plbծI9_?'It `rvo{i“7^(z^:ɕe>G:DX7 ueO' ;>ή<)z^57\j0B*f}@Sh ] .݈K+>dQo H^skf;9ץ/6 hzN|L s`un .0).D cCGC[SkCWF`f!N@08Kԃ: ME"PH? ~ɡev6Ы?kv7{{.Ci_uHi6WP+@ @=^G]! z@^p@PYZ~{6oYmHEd)sϼ?m6Ǫg[J?q`5ZZ#>(z2`p\ JD!3ݽ|zr D) !z_u ~y䶉zl=cR/|2d&*|>VVP5eD*W^J$G[cc*jhj75D auwZ%X y}lCu+yqޮpPPڸۓHB*)UBjU52TQ8Ƌj~'{>?ί..?V%"B#!ͅj|4OjgcsjDw_m\CGҐQV8?PZD %SQJBJ)TTU^VkS}dL٬m޼\Vwo7-㓵j{. zR%Jn%1\k8H<^|Bcz֋[^oxn;B՟_ʦz=P(6LTjMԚX$q/~~l6F7cY؀@Nr)\-T"}^TM^Vd$PUjM2jԟ s';B$!tfgVTR21>kI\sPR@"%aszP:V3?d7\R\0>&:&Ib!hkZoOMmjm5&{(ssJ%ڙK ,36mٵcO5~bƈFC‚QgNH:)@#%FG'3G B}D"O@Kz itsu%1+[rjԪU?9"Ԟ?177|=_"%!`ir֣h(rjD))y@ţ+\"\\N8s$s2E ] BJG9O~ n&ǎ뎯R8INj6:۳'OT+IhiiiqT091qvT"p1skD2?&`Z_V53'^xd /P\4숄,dYӁC( {淾v/,,@Y\v;u{qq^&IܑZF&&GFFnD=DVJX JFaML >00U E m brfg?Ǐn($hMs'N.իbq)iTRԍ fffg6Mmc8ϥk4XlH~f AC>0hy,Q%:2K#d#'ᵡ}MOousј'famgvnVc}I f#916a~aH_k AQS !k3{K @`8zI3gwq{~ BE/ ?N L8&otYNmڮnK۱^Z:U׬%U#cl1 slYF[gi)S|1DHJRFGPA[恜;z) f`Pb5-Ӊ+?\<.-V#j+Ȧ&&Y gO;T2*}t3D/ .SsNoKpO8R\*2F#v^Y2:Dn_+R'Aqڱc#R{ ٮP74EJj(ifx+V A {B (t< k}۝n.8quW- sV!rsS?SQ^\:/VR366"V"~:[N`r/^&R)ӺErqqRHENwi=1ތMۍѣ3Dӂ,2ʖ@1:\k S#.o H˷Jm$ד0 !&)Y\/%DQә'g[͖6niindJEz Pr !+і(2:Gai-` 9J9FH9B QZ͛:"Hn6ʀ1fddm;} sQ((6؂Xmي-a%<[:yj鹒,ў *yP=Bl eCh恭[.;]cL^Z/-Ǎ1ꍺӉ(*P@.=&$}AC8in{v] j+B ryA"s a S8Td-] z^F[@Yh6N.w@js \`IPq!7/dZ;q8kR^+tP~98Z 3E3:C3Hw7YϹ+.1q<>>ɀT47?WV\F?dP #4\k&FKptsdʓeNɒ#02"ńϲkR r$e+F &qEK]jIX՚sٜEN` *P0+Zp^L<"[9!$[@[TRk{ zMF@r$P8k(zZS>k6MmkJ>u;NFCv'0KK[6ocGG@I{E07tF}u`OFf yr g2 !ѫY{w_ZEEkaYqcRO'/k3kQ!C:@gyiR;B8o K) #yk@), !H)j /PJQ:a^j'&ZZzTx4 Fp4l:y"4bcIb[ 9oM"e489"P[OCA.( Y笵cE|3O< QX\Vd5V䩸^Y`2B}kB;gR{.^g+_[Rqk(Fj~ad( ^_h $ ,FD_Xι-] ـuK.BkF7h^j'## hkHJ!tB ҂pyj¬RPJ>}lk_(SN{vऐ>SHdE`0H\)tӮjJ(jFUF}D*V*zyA墇B-%0Sӳ3g.cDb^^az9E{JJZvqh-4RnXJT`k @Aݬԅ@ᩕ k R?4ED)yVvpeG %vnWXHDQ~c޼E874ҨTڋ,S'VCV+am$Y{;+Ds=w%9ZI]Tjmf2xc`Xb)k+׋.rBV.]YVG6t''8Qju)J8$@.*B!?ZK k^XXjqA Ox ; gꐏ {҄$C Yڰa$qἢ IDATVs⸭h1i~"υ>lU @MB P!џp<DZR?PgB)PT+,@D!.A2͑e0 篴ZcG>D3'ǬM6n f͉+B ov"N-hk LӮ< ;!vVkBEӛwIvg9R:E. 4@HBR rx5I51(RZ |8",y S Vb=恬K r>2ϗReQeZʕy9a&k]8G֒+G"*@i9ثq}pM/ڵsUk<5'r@JUNC6Mو^}>Qi}@ >Rj(^jǛ I ɍӚ025ǁʫ9zsݵ-{b43ssd@AxkwBK]ZkEχ.7?cKId dPb6Lj+eչJ[1kɘvZ *@:rG|E6EXRj=O@gcǏ@T (* ȑR/hyVH߳`$L f@fJ2qΗmh' ;ZʚR*DI YdœgceEh8O;~ڵ3a!ji/RJ#T*Qx(`O.ס7ԎmLGFz'j XbCzupy5S'Ԩ?~R *`mqJO No|${sw YȊ} M\~1uƚZU@tVO2̟T>,"淫}էϤ6`::9y/CGDz}|ȚZc'GB^aDp)c0*Iڹ$QplҵՊPW2W. A!>+*Bk[Q*ڶuG>sZKlwm`%k*r@#1@A5d&3yDQW쎅*@5 jwb,V*1QWRAKS P07o9tɔܼiG<;BpĖ@_h c8Tr PB2c4W x8^grfc%euR9)@X/Ӆle:1GTZ/qjv衴ET`""s.P,Q(Yے'CVc]ZBpXm|e5@BTl'NZ͑81ZF"Iэ8-DV |beK` p&5gR{lAǬz\"nr=w!޽|Q|Vbq@ʸSxwemT}.4PR1"cLV50V+*جO  tk۷l;v Jedʉys-_* LHBDcziDUB! cb"Չ1qDvU51΢1xf#$!$V, #hI(G @,@{ KG!sC}}휭sQ?W?OVnCe /x#C k+w_>4}Х9>:g@fô"SS A #4(v$KB Yd^(D=Raܾm[t  ];w7:ͿpeӎP!0@iU4(!ȹjLҴ d?&޾%B$IiR٥D[ p}4nO+hڰyӞW{Ge:-stHTIB BfjȀdiiv{a$R:=H\$@ Dky8QśFxcF"ۣvn|ˆM8B9wdr, >RIJPc'I %Tʜ'i񀖢]wmo 3Oس#Gg,306:gFgiGJn/)L1[pfÔ rf@c`H@w{IB זQس빗_፛?pϐ LNlx巖{A*`DunƒjuBzX@/euQ@)YZ^~£ޚR/H` 1pX sQ3YVy։+Ф0::sǞ #dȠ0|ߙnFqr@@ʃ!VQU""E)J[I~R`Jbi,E4Xjy_+O=ϝ~S/J/7@ 08Ch#`rMdɀI[HBdjoLuulPJҬ22Gh =HiF[797۴߻ɉ 4)5fL@I*<"T]YXHB낀ͽp~񵵕㸗$}!}?RRx֋3VZ<(aa {ݧMMμxP?x'٩g>~ :;fB P 畤,Q)9 !Ed~)㸧OoO DqJ6Bp3vO?|B[omo]d|lv})xA 6RDQS֩0RIJgmDуqܓJ7ҩWP3ͿiȰy^Bz q]?Z# V1S aߓh&gᑣ]5zo4Ji7PCO+@2lSZsGS껸цR mfuyy$MæarnU2Vo G7+A"aq %:I_6:x<vpٙ-;G4֋x6VXxt$Jd8FD(8B*)]=$I}/Еx(evt.@`UJ J x .oLx~BhOص_vf͑ o';92u8Pvfn<ʪ03dTJៅ׮k?qJv<40FÃ߶=x2hn(}޻\$)Dh8ڼq*xԗ20?I3 >8N Z$s0+fR)Z!:BNԕk?d2qUY) YRIhy.d<Y- Ҧ|| -np/K '<5;{6Mzsn`Gdfzs3vK#xAPBwβh#PVD4\֘ +tCӓ:B87%9B@{&)UYFBcy1EylBɩ;S 1qP}tƦ&'zSpbs/gϞ;n2nR:=lF) afyfpQI kk+8H" ?Ԁh*l͐t `+̲i 0E8-^}s1W^}G[6mt>9rhx" C˖K Ol۹c7i `qq~̆5P2˲FeҞB¥3$6{ҪM Njկ +30X2l04P  XQ} )h6lZ [dn/k7nﴛ_~u{q? ƭOo~ce) BK7o|f?ܿo~>֋h7PC1h2/厐A{KEQpKyRTAõFef.` @Ih0qsw<7jjzn]|qqic덍qMX][n]{_ya_޸yՐu,(D1w\*.IETTJva}׺3pϿ~1J VpnrDIl]77A 54iS]P 1Ss;7shzv'ourKo( àFqLiDàW+O3,(̿A!%/ `srE=bw5mܷx'1p,.ـ!=؜\6l`p8 "lUI:)3>22>;= >m8Q@={Lr:bzac,3sx(Vy8 a a*L'oA@O6`?҄U L ], ]&O !4bK"8Zy&ϣ t:cvn={~'}/J 'h.Ljڭx331>d2;#G~_`raimycxN_|؇?wc~ch,no8ںNzxxGhyyw@JVjǏej!]| J=VHt]& ^n6CcKLm*1EZ;י}BG~6 .L=~~zW?[^2nhQ7h׺fkd0;JOWo) 8uzL|S\ 5 ݱaB>8OФN*ڤ T߬Z~& c/"8>kp`a]!c^/:}*cQHBx4 /R )ܶc?ݻ{2 IѸJF3zALoXZ^ s=HgdfrO9fp S:W5T= p -ÁjP n޼˷烳BpT'0xaT Y870% VNTȥAĉBu3יz1LRzr$0 RB1*M EkB|QDyL8& 0&蓰j6(xg|l|ffv|ٱNnaF SB @-TM*jEq)had/hhBJi`P0h^Pkt,! 0~@TEIDATr[zATaFH!|? }X"$ U]zO " B XD푉RnE FXZYiZ;w4>6Eѱ[{|"1)+'>pAhs:àAYp_|vD*,I$Y @ gŬn$3_/ls5 `P>xa;x/Ejjn}k={}wɌʆ  3L+ ܺu3zGXaAk[]&TlxlR)10Q#\#]S&A I2'y] !Į;4 3.((H` ;~EAE񱉉W_}vťeCʒbEy1%tNڈ-0rWL',__Pe=g>)!nwo/w0 `H13ȅݯΜ;YLv*p:"%wxj4 p8%mLfCiieXJaMcʋpn*f 2#X LZ)I)02.w!14rbcB'C.,Av_44FQm'& ᐂˆ T8? )\Nfk™g2el`s8~MCn/*<$!L8a< j5Qݼ&Z T+0z[lݺiwv*s~ߟ3BZ<&۲^|8OBLu쵭]X#BJ BtOcsSQHuǏt2"c@핕;E.ѣ<* B9O?lbAƄU k/xufzm[ߺqoWοp4GGH%B(; (‰4wdm2eQRRK-h2 U\}h~B]glOgΚhS=*I!DE2"39<>pQP1A~"Ϫb>#P?*p<C~,Z?m0kaU&,{e {3^cȎ'\zc|5Ȣ&FP☥nҠ̭qAx"H_<O;[O\ka!H1*DzaH 1<쏙گFj OHʠ(ҼQ"쒌1UVN>KD9p n߾9qp8@JuT!^J-qS.ZI11@DMm T8B*֩1`ł0.[1`8|xwp wG[2ƍ+7n_ts*~:ToGe.NFraPQApA-ǠF4l-t"D.iTTQae^cY>]A1jİ4fW12/?;OC; p1t!l1i>=Ps¢ ׋(0ig-[5SeFLhm@RJ(\ŹwcYr8XۊnWRc8(:QtQQyW }kdp- [ q#0x49עe-bXcƹrXZaˠhnULmݒjmݺ}=N$n0p7 FqJ_s;KeL˥p]S-ɭWʊRe-?D*b\Z|u rZr [O.t\Oϝ T ҏIqt,.-LOM,_y2&z h56S#]4ePќ4L;F3aE*2&`n? @>E]ȭ!W/;3e<E|Q xjvb3d?ťKK̹Sg b ޡK*$0(3zǭ~}_@f_'$gfb}~G֩pg{1 1ƅrZ_oR $I@/ֻiW;o Tǥ(PzAQ.YeM~8Y+'\(]]^^wZGW߄ZjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZjZ`,w-IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image03.png000077500000000000000000002205351423156660700227670ustar00rootroot00000000000000PNG  IHDR?1 IDATxgw$gv&piQWʒlHvE2#9sf|X{vfwfV-tl6ٴfU< Hu ")3FɓHdF"{s+#!@*R@(/*+B0PJ1o[Ǘ+jpB!XE#ԐJZ>?DMOOz\>0 a|'ؘ't2-BzQ$$ BHH!P(?ZW^wn) RmJ }.*?!J) 1o)xjB)PRsq/?{)r#Y!e\q "!q,.=ߎX$bӞ/_ϹR@@)%eGoPFNO8cCFT|)5: $/tC/D^)E ա@ ڭGO?,kvRͤAlmm)W>ZAl:3Bڴ7kbLU!ԀPH!tR~KB0 kjX< A,2 Jv|>F0|w150%@)FR}#"AɡY_ܲga!8$}|0<6B\3ȚM_;+Ad2JMmzt4_-0xro/c߬$d~/R/_PJ{I\t=%9Xb}?F#D9? s cJ)!$+0.ٹ ,Ӓ&)iCl\.|#澵$Rz>-=~^?#zk?џBICo:w@WIy{^j., o5C_jNLfw?N>O4;Im]uR2I0aD,C#`a5٢bj593@!H)v]X(@.-rl[#(LPbz"Oϼm!la2,ˌs>*@,H&T&[BA|ߺukum!{_>{U.NN*ܲ8;϶(WWW!7AX iBLK1X h/OMOR(FFFznRTeQ 9r@7c@"T1"AKp(N3ƞ?vs\$i:<:AMR)Lt+ 1vXի;;aBl۶,# |M `x2Ş(k8UIy>'ix{0VWVVFfskkF.JN݆TZ.///=yʕU$TaH@a=I솔'E] };<}GɎZ)d.ԐTOj?Å."xvRV;F *8cYIiC@RH1l/f06v}_\\7Sy1FVMLN#TmZ!DWloe( AIEHJəEk4I`u;K6tMN.[\\\]] ,߿\u3-X('O^^ϲScxEʔT￐x)  Ճ/x^%1Cx^%ۅ4Ds l@~ƉeYiDnV2 2~ccbRJ)cٌ6}!٩=)UJIJq\+ 0 ffSC Bkn^A4өÈ"\D AG7޺u u,R"ɋ8[v? CK%ާaK+룣,vvvRi' | |Rm63<oݺAR_h:@ PV@&XK"y N͋ OI'?yhvJZP(H\0 J81, 5cI8pk.@'Ƙ:R*@*✻37/S # p]WsGg&(±Basc}b,? ܞ"g~"2qb^M IDFG9Lȍgg G{O>\.0l0:o6n;y~snfv2;;\B ɇP^̦A虠}O>9::Jeq:,j4uTV%`A("" Jtd,P\RϾ!믱 9B ʼnq# t:԰ټL&cF*?zH `Bì:Iyva'_?D0|llʚ,?Gn;:_|6VJid#7nޜ{'UJM%Vb[i/y 5UqIp 8Ud4ztIekkP8bL` ]B*mۜ_$ZɃu'Z$ "?Hl cE:s>E׍[&]QRBaH)`\Ѭ7^sfZfBɔ2tzNc`:EmJ)1LI RJ+>%"m1²,+WiISEk|)L&#tDh۵,VAʪ˽(7ʤA0I_u!7ryUy8t^a;rzVT{{{'''v; bY.Hn||RL &zm|=JicL> v21 7N3:{3q)tfM jRʅt:mYɤRH)T@ .A~ͮBeRrB1777;;+(v{$1wW_mllJ\.'\2 LJ}ٕ+Wa^ז.L^cM.8BqjLpA^k6GqV4m2ƪrXNN9'[Jv҈BEB0T_ K J)q֑̚A2o eZ( tI@{x(! 9{{{=բ )!0L z^ict:-%G X!1>5:')l;<rlX6\Xoݼ|+ᓵj\/zW_O&'';VQ X(o|ˏv67"+%)5?~мcL@1Sk9<(؏}PvvXR8b5Lp? +˃ @:PJ)Pi~&t< _UMhq @%P (B@Dlu]϶Rl#NC FRjAu]m0B*8T:S `1 73v{.8 0nUJAH(!ӊpiyy~an|r2I?z R |yR ƵO{7R! qb#[EI)aI>  !?xh~_`PPC`_g!QtZ-ι뺮;J}\ cZM| IDAT,=Gmt/ZV(v*cD) @BVliLO_2m+J)z~v:nn+&)jjhC!4qR.LlJx4va$?pnR.Fhfgg&&]ߏB1d2JNW+] suf( ݛ1}y:5N&iJ ˲&g.__lf2f~eu% BXf׮]s?~ܼ};wa?˰з,LFUT>zBaC-M$L "!aa>N BF]cB]c#-I'O ڭz#M/,,AvT<. k뺗;˗/?z֭[b1/..t6S&(Ak?󿌢bш!&fd.Ӡ1&GB aJ);BOsR \}jqC?p}zx~T ⌅ )! =Kjbǒ[ӳۓW!J1 !D(jZ^0l6C\٨ժvu^!HdaH'˶G`Bi_80YE3(H?jM 3edHч=o6+kkܨm۾+ FFF,lbqrr2IGQ$tGO&ԓކJ)˲tW;餬թZ˻wi'@yĶ_<<<|񫯾C7ˍnEϗvFFF ACzp@mn;".( XY!g@ H)1P!Z}  b c@*ZԏH G)D @1@ jxG\:.qiT*Jay\wttv{0I[mA@%F8}u?tj2iDZJ4iCS85k9E#a8w+*2NOOS)-w*(JJEOrGzՉ0%z n&]ɏ}_}LP8ZZZ[pgnfv7xcnn\.Oŝׯ?xpeepppp%lrjuqqZڶM)MRN<'kKw?]N9I@鞵HN@)R #xW::vzL{B^=דR(ڛBHc!Rxn@ 6oJH.ү#W}B?"˧Oh :I%'s[NLR6jUJXeYTJaY 3PvWv8;uZ'L1 :cē` 5[Vj?X{8u7YaQO$6EQZ)33mi۶1B+KՓrNq4u0 C*I| 9PIudtjSU3V/!SML@Akӗ/_F R㱱rWEߢƖee2Յ_v qn^~=%kZ( >KwYZZj4ApwO?]ZY. loo7ͣbB0{}{o}Bylj/3k \{xxU(^Jq "߄oRvpBݨ1O͎qN>k3Wpc`q,RL.fAS1u;b~R B` )& Ra:19t{{1!:Xi'YXLx~N-^36HaT*&@tˋ\H t;Nv]#+ !m~'>T s|Rw;s]wdd{{{TڞUL>wʕzn766&'';;;׮]+SSS'''\RT,xss3 39!# ^SB0r;Npst|XDJ6m$ 8: 衻POFzZqj!|t?^C1֓|4 z:ClZjt^cJ)LjE FJAPPPJ%#.c8Lz\^b!yGHL)eE|EG]U:G4R`rrrsss߶m)d\e@]{?C0 Bheeh^;Ϟ=cagn]y8[[[|~zzX,6M]-oy{뭷066K)g.ٶx;}i ;<ȔJEQ!4)ѝBBP(jUF(Ņ4΁JV7 0Z/ |E `/;'AŠ8s>u]wbbbbbclsKBRq.caʶ !zmAGA!АbuM/]J=ztG(X[^bY5?;z?=yҗ_~iY۷>}?99,..jgooﭷկ~o?|~WU&9._|1QJ~Gw;R BH[AjVC-cV!---gx͛7}ݧOz\~dda>{ѣGBamu%۷o7jjtNʕ WVVr\~wb^^^>88B6M] o~QkzZ=.(- rv !@$7:7iIR2dKP2T,g2D\0RIX$"JVC Lr˲ө|#0 XX|歵5Tʲ޽{޽~k\=:<}o???tB̌[[[Nݩ,,,Z=;;[T"ʓ'O'':NESSSGRzT|6pyqeeÏ> N  !aRr] rl3,XeG,]3h2U 5q 0ƚlJcP4,߇ 3 {ewJ''{dEJ%h&m[Aa, B(IنC~!VFJ M/uM``$C. 8J̵3Ώ2 A゚ٍ)HL-H"`#Nz#jSSS}B7s]?<88t:Lfuuj6kWwvv>[oΖY޸>:6vT8~uݻzpppp;wmo4_ׯO\?9=ɳYAL_*KP*€" ~ɓ'Ϸ*r?,"9R))P쏎JWP!?TJR)&@ ~#d/U P؟P@H_1FB"9pd%0:vfɉ D0Db=W N 1ce11(1 5aP˲lv PiKPI<-Mz=G_#t=,J}'=6H BV 6u{M}Lam%ņaRB JAMӲL T_2 (kviz/H7oޔR~_޹c%]h:B@yJL&~e}}aqW_|qҥl>|Y:|ٳgovXuBp֭1ZBG! #H)ɏB{wecSJ0֏bR_{̘I4i19"CN[ݏciPN)H@RSүCP! &#\ ɕ* uRrrrt4M˱mՎZ,useJI]kRa)11b҅-R)%?334|ie ;VCd žARB )2 ? JoS*iĘq,P˗/ohX~bCl:SV[:ҙIeW_}夬եNoٳgKUK3b<͙(aX* kkkRivv\.+kLJGzP.9I|6eW'.=zpRλB@) H C(PbB1&B:p뙣? U^ @A(P@?!~>գRP)J@ @(BE RP*#1@qI8ЭcP@ )RA ̘JҩiUzz4m˲L۶,4B`1QQD~csҍ*a9pevtP7}k<\?0`PBimYeږmۖc[MA Ӷ˲ B@@P )"Qa" !:W@ dSӜ32(\ fyӴ<7սL*ͤ_~Q-+ݼqR.]^Y1,kp|rrt||wgQv푑ѱѱ\^.*R͊^tikkka~^l4=z;ރ8ww(!_޽919Ź7 ;B:TRH)$,eI!TPi ֆGj8:5s'$'jhf @MS~)GJM@%ap Ŏ(Aq;<NMN#y}v]r^w{nʶMàb E< BD~ jZim(qܱSqT10N^v;NzRje8TqQ4MIQ"aAE,3a5I4MbԤGB2"hIBڶ}L&!weej\*nlnXˏ>Z^^zXPpaaR ./.V+n3?7~јiZ.,,=zkOOOaxtttg[FG4\ɉVc9eMiro.ps@ :"1aB4X%A?z)} A1""EL)1AJ)0&FP(r2l*d2N*%zjR.[-.6L1ee2HIE|\ͅcPbAL˰+rҙT*؎eۖiI)%C bk O DcQ\.@B0%llYmYgeY&5)1gU#˲r\<B2c_uRNi<=W0fPze DT*#EqժbbbT,Bc.--roܸѣ|>vtt\V=ܼvt|L)t:J"4 PP2>>w !85 !o3c0ܤPS?C\R _0"%lee_wyZik={Ν;J^XXdSݛvg}v֭$?ydss4ͯߟ?v__N&X@#{ݾ}/?vv!C/LǺ$ 110aC_=c䑜'%^\\LRRIO8<:TʶS&RfyrrRբ(0MYvjFh4a^QGNNMX ~o_uV*g2飣t:nWWWFJ 0jx|||{{{lllrro~Bhuuɓ'eݿW_=<<\XXzJyzzX(/j4i'Tzwg-ˮT?HV·*EaMdB#~ PJ'cY/8JH)TT,@cQ N(PRI}O+˲ JR"ggSR "7 비t:Τl2Mg-VA5uE뽺Ȳu\m  @JʪSOw~:iZuneI' !޻"b3vEVAJ${/37?EqԠ8:I) _yT˅|. \,' 1 zSҫ0ɥ` wBT*r9!\^A.אrbwuzI$e6 ) UZj=:2R*rZ}}}ܪpV[\\o...\]]%확w}w}_IV.FJa.y].cf(4{k{ > gy`Ett7YaH Z$!h[[;c08[hd%4ǙWH 8 %k>}lC27:Ff''0@0 <抹|M+Z] Ҭш>h}+Y,Zp`مPT)}^E5iZR5(x9g\[[yW J/o߽{W.OJ^_|DTZZZz_|ݻQ/_O>=>>__?SOOϓ'O?}7x~gg&F1⢛\YkVWW''G?ih@3T=㜓Mve b1D>:c 0Ҕ!3mFd 8gF :f^1d&$J )k$Dp ÜqP90EQVzVc |ϣD uևas| ɕDz=!Xg(I(IѨTFtyu}[Vkz#jDQI @|_H%$)|Y,:0+t|/rV( TR8}Cc4Z+T77^aNHA\0_(*mJ Zϝ`"o. 4q7hoCΚR|p`ZJŋJ155e-퇇RJ^\MLLYkGFFHEgggSSSAMMNA9;>>. r[c<ήOrݘY)Х8w6`u:c[C.ZmN~K$G4rl1IIHDŽr(h ٲRZxBy!㠤'@A~B&gql!TQ @'J)򤔌y)RRjc9J J9kRq)ϳư9 G,:!U(EZk2΅"cbG,:f-gx/% Ho9Bprr"(/nllm_|O{{{/..}3τ[[[a800ppkC$:|}www__/277w~~į:88gHVuvv{.}hY(D!!g˦E01I`> <# ;"Zc4&d ]Ȼڳ38חc*Cãi 詌܉1v1:1N;41+O )S^wΦR$21 ^"1Sυ:)B*/JYEQC͘sh).oXV^F:jJ' Z]anKR0UQ,m:km$PR8l/+?9>_/O>ӟWVVcSSS?~\]])J>}S &1>}9??~ߟ?ogcc9Eu?\=zblR~ɫ˘RT! ъl9C@XAQTM7;i҆UNDEصaܝgR*BGkDm8TYzZ$R)(8LA"yOm)Z.=pNtuuJOzQ ardD 5`1DŽswƠct+ZS)s`cIa )<%< no|mH[X'SGIxL({{>^x (J67ꈬa.--]__w__4}vJ7|$gϖ...&&&'''裏;::DZSkm ϞNM=Eĝ-kXWWbV,i0<44t{{{||#GFF~\B EQtppٳ'O Zk}uwu-,J%ё1R)A}ʽBa#Q,sH"@J:_\\ܛXkV&MyxMd2'65IVQ&=p"LE9wzl^Q֨Vj:;:hJӃ̚*|͵TD:.5k˹CD&%*3!@M&W(f  %6PHP8JvW׷QikLcp`T՛ιA4"@)/KE:g,jg9sƤޒL0~h˔T+D( :FZ<|TW$NNN?Os||'Cvvv=zۻ422BD?_|A{<u^?8:$|>hDR)ѰcL*8\.yk-p _8μ0&-K!HS3cL& H.}TlS\ݡO>>9>_<>22ի\.˗/wvv^~=11sxxh4>|HO>Mdqqj⢔rhhhiiccc?w}~c{{sXppp?o4b #۫}2nԬ&&d)9nC" 220EO9C㴎$k U\ <)ciH2C.$}藒OLLPÇR[Y[stt$C氯G֣ HjʒLB4(y:2$ߛl~~sr`)hD`)ɐ@kmHZ%QY410iX{CYĤ8d D `]b\ڢ Wl cV0 @IO 1`6162ZkM+ -"k8B('Y",{Qy%X5$q&WB8ɵzCkԓ `NG˺#d4J{&(MBO艀+PIV><9>>|8022d~ͅC[KKK=}_~88竫KKK CCCO>.\\\$oЭK x? JW_˄P5m*U9p.90&6N񮠌:bVc&L\?icY\ aY &cM3%KƐ`ܙO/.D8իZtQlvFsANs.tm eZ[:.%Rdj5"*Zk9WB9/0 sjn-5)Ar $O,:OTX,z0d~'IJI<_o%2+ s}}}Qs/^wuuMLLL̜ ?|Ǐfff斗/..fff{{{"Z*wz8]|~aaajjjrrfgWWWم۷oƞ?o=::4޳3:/..;P8ˀ-ʸҚN-elZm& NlV⤡k$ٻz;gMglSG`RxB)-c;!.kIM%~Iz;,E Vqډ4ד~'݉Z c L.Oj1!B}:h u8(@ A!294ZV.Р~0 3T $9&qoԃ889/J@pq~P*<ϻl/>真~ӧ˕Jett4_*rrׯVVV=zvOO766>|0>>ވ*&&&VVV:::fgg?~888855E$I޿ٳMD||ӧO_OKO> UP:<>ꫯFGGW_mmm---=|wލOMM,//wuuÇc뽽Ϟ=[YY#a}>|kkKk=>>Eы/6661+++###AOOOW*#BZZ}vvvu}mgƒIZGM*f1ev4Zkm ,(d͉PӸ8dQMY6z<0!<ҲwQc**9Pp+d5qB9 &ϛǞ362IDZkhmhͤ|OmfJ)."+K큟+蝝Q=<(kbV**I(Ӣ?>>yM /&!DR9Pk}sjJ;]c蘣@ZV*8W'''?{vvv|||ui@塡ˇӐklll}};;;޽R<}tee|ddR<|j}}}ll0 Ñ>9VWWGGG߽{wuu566׿ulbX,r=zT8==Z֥u`]JH.: [#q2>KhhJ6ٔ8TUj3n IDAT:2ު~&\)J=I_9 8@t Yk#TZ'IRyȹB ΄9p)Ȣ(VkBp2U
|x5?VV1ur axsy%7UGAH΁C$CWo8N侯,%URSoĐsisZCcK<m%h< ),B[GGOnܩE>o$RJSyL&59ƝKn r&:::KRpV#L&Aw]xժFu@;@%=/Jvc 8x~Zu(8G8RDPJR4BL)f5}9MmM*Ĵ%T"7xC|>_._)v5D9lmnnnl\^97:<KKK?ZWw7offf7oPNGGGGGG/.߼yjRkFĕAA$o+9wvv$Ǐ_u|||ppp/Ç=]^Y{U|d"eur2CVDEv@_* OQљ/s\I/S? Z;&U `21gRCɁ g4ccȔ:!cccc3.T:ƙ5ک\9wRJLy"\@vQ7iء(QoiΩN4n!t иø2)= ӛd\gzxzzzqeyiiixxGGG?<<|>|$Ύso߼???/刡ݐn>ˊVeV.ф[jJsYloT㜎b`+٨֚Q ɤ( @2#*`sȹP Nt"r RDˤ A0=L/1`'`uF3N'!0$ Z˙iN`GJ5=MshAǸLEhk-gFZimF-:]'c8nGG'DKL&k44H !R( H7IL{zz᳴ .*aZn!$ ZF( H:)o#4c1fC1@I&\ht $ 2I I<Iqƹr*STag](B8g-p8g$pc.H`W\ME)X#ǚc' yA葑asKe8!`jh;oRs:^H! e9mRB)U7)],|%h~Q}}ppυι(JY΁dT*A@& g6DY\x"K]a +8&UP?|u0:::77zuu544Ѿprv|||tttyyV=}Ù/..hʶ^V IPi,ݚOzf̃f%8I`ƹ 33!/ W*  sbXO* k+0jd90<3q9CsPιS"KdYJuZ{ZDr˜q \.ӃLQN:YMexW&Q3wssspp$=*vRʳ3ejM7C?V[BT>ZY 1YR|>WV.gglcmO}R%0.5 %XK &s.c)NWsPK)%$i\ ,gL0JyQtRzJmQc' .L^cMl:т Cz#I,QHވc9 *ߓ\$ITWOO;91kꡩ3FAow{WVWV7O/_~he}c{ϟ?_\ZzѳϷ777fllLy9RJz* yι&W< U=െ1gl*>3W#UfApL0ɕP<2(JZKjVJNpT z7$abh́p @$N` YdLLk I&E:4 kXsJ>8 G4U^;bJ2'F;d&ޔ m$tDQdMjw،3&a qIW aܨk{BFQ }g'@%Ib*G K`GQC,Tg8It5aG{~ A$歘K nDRJvxxxpp(d_(vvvo>~ѣG'''''ˏ? 1ᛤ垍5ZY'*Q!eشUΒ3N?q A.WT*%&VJ9Z[9QTr6$eiտ_琒!s Le fENT\!1w )Z[ѫӺ[oC Zӈ@Xk͑F5WBp&(3լ"WJ5 < Ցw4''Z2)Q4!V f:gApѾSoJOP=Z0YJDQxO "友gϞ=;==]]]=<<899蘘8;;:::z17W*וR_~7oVWW_jss<:);(ze~ۚyXhd>ȴ%l&5Zgu clGGqqWuDT,B P$˝L%P[½5QLpT~ieS D:R h-3|I{8B""QquL`i紴YLLc!~#q #1uV$vcbB΋:N*ƅ!X.3J$4( h$1&R )9hCzz+Х3&%I )΂ h4m'O}k{{{Z/^8<<_{zz %۟篿˗kkk?yO8[9|,UA~Z|iz2XLCFb R%F[ښ\k)$1$p}^ww'}7FlK = 0&n#b-Yy:8)%sp )F+G8Z# raS@ @pn˰ELq侯28ihm3V)My Jp?r|&ꍤ[g^._ ]^C-o2z===333 ұK5?m=& ^nȃb:99 WAdSNKNKE QiorP1 @']v:l;S0~T ðP( @dUBpF̅+91Xks&V;˅Lr[xeZU |7hR[bP,欵JxApa[@w^33Oı0jh.B90IDQR!(LZ֔ݫ~:Y1I0-֖!Y UsgS(h(xPD|GGO_YY!\./,, a]V=o/^}3ZahM]wdjؼ'?;he0=}:҄2u'Z_ D6qs 73<HSg$XcdȄl4tH gԒ 4EQ5,:ziPa8ʼn"y1fuqIOxz}NkBB1719,@PB0 hYG:jjh8"6j5ZsĵZE)mZ="(`Z+EJI5zAF4 >|ڲ|(8ZBQV]xMc,wFEe6ޞպçt wލ 333~ڻwFGGV)SC6mci&ݫ{ ޯe&e~ێFs [-R?|>%siFd)#}% 86U&$M-XdY6і<KpORR n5DfO9(A(L "sHf7rOO.I|rl<;;'`ѐ\$Ib({zR(~z?, }Ř\ӿ::H Mu+JKϟ_e^S!M669W(jZx{T ΞmnnMMMMOO=44tzzÇr[z> LVFL}KzH¥ZY/Z\dz>;jѿAyn;cp%}gc|It:Q6]q !c'c\@٠N9J*%) ÐvsɴEv(`ԆqӛjqR.2:aqQ((J$QJ%Vz4:Zk#XkqDJHgւsI)/..đvȝ\͜2uvf.q$' #?88 U@:$$E)=@F/Jj . E αuQP( BWQbB}cZ-b\.omm}_} &&&|?99yǏ?~ssH]oBft]V|ÛlٚhҭyvR)Ydk:F.+3Τ&#+km[susTXSgt0݋·J I̖{MpF֥6cސB\rc@0N1y~yBT'd0 +k(upΤ>!mʱh!am967I _6$ \B6@sZs΂&I}qdFM '$>D\15AlsNѺ^7 tR6f"vs~rrr}y911qV::>>\WWW[[ׯJwIEA.+`hߎo3 nKl;1#ZbFKT H 7KBZ[#}- ՚s~vvfH#SߚN{^P*tFHt1⨑i6>te\D 9S" `mRV 3/A\Td 8zpP3P(VG]zB"RQ Kٯ)D.Ut$)1dc "ˋYKH$9bDN&;¢I`h!ZQ * 'f٩ib4,--A066իÍ˫m2J\}?gv7ۥkȪiξMZ}#7YG?0Yi$`0Rr:3-HB%GD)g3o,1.T*J!5|6I6t sxޗ:Ѻ|>_ܰ9˪ jQxZn9e#j\],)Gs'ΥYrƃxꃈ: FA]i\ %!2|x_e3!]?(/0 IDAT~h.ACJ"gC0,xasUIZ&pa[['F)hmmoߒIE߿=<mg"lb039ri.En:ӆ`~?_׾ܹs>я~K/+ַ}ݟ秧vYo.֧+ug Y1C v.w1M`# l|D6=[mdI}_,O`@ 2IcQAs%8lGYH/ר=58Fo$dDRqyJ)S_?Wt<_C3q߿8:y;6'˿7}kGGGկ~W_}^Z,6vt|l;nB"bROp[M} c6vj^k4':00s 9)Fu߯(ABCJ)iD""ܾ}_wl1MzacVoLq>9'J``h(#ZL7?7|W2W_}w~o__|QJ7M޽{F6[bǗjCɯr>E[i]yR R k|DpX8|#SJ=g'1ͺ[t5,"{I12:GY<~P,HD輟wkuL#jZK8::8*o*Dʤ4i )Qv&QUL4 GxvIJ^a^sn|wo~g޹s/߿?[~w}! 0JBKZ5NE43h& b@ɄJIPgIkg_p#"\(/21yf^}J20Cq^%9а[|p07A1ı(2wжZfre) c4 C4fW1a}0A"w۲:?Z=LaKwo'E@tH0߸ {4_xqYpttZx㍟~^}7|7'~_?M^\\4MےeuM\?!9>>K#ژHQ'Q ds΂H@o6@,"wy8??kׯYܹs?Ϋc;s~~~rrz+h|#!b)d1] YmPIDQRM`A;aR4_/pYι˔]5F|15MXTb""X7'Mb qf닛5DXff3Ht9b?::NjVǷƐ,:`ܾr\GM7 @5%)urrѣa^x7||~˩A(;mJiRvj=b6(9?k]BbӉ/lֶmRI)ݺu+8ɱr}7SUwه؜a rQ q}_!:7F@1FEEdvnG91wZ`Zl6+@PӶFt5'2Z?ӔK!T9g՘Ko.(")G??7/-lhbg|1wS9Tq#L:q ²7:j:P׶'~"%:rSDCl5`$bMZF%qh~&ܬ8PSӷ$3K*jK7&HDRB F3z歘9IU )3Hgd,_~jx"G?>|hӏ<ޟAjHeKkY~8Ԃ{>q6bw溚OWPB.pk-aRBj-1k/lZ1D"a"# hUcuuZNS(u2cn: w0.mN&K鯌McL)>@...ļ{KտKD}Obf)OuW4ofOt1{.O5FN7%!X ;V^;4j*bX1^ީҖYayo˚Ai0VJ,մ$b4Y爁#$Pal۶i(׳We#͒1[ʳ;<<42lH$iHb#)@J7;`Yn߽{z{g?3o߾Z6Rm!Z^j:5gF |nD5ڠ0YD@w##>8{޽{zzαsGGGWWW޷TbLsG: {;vjM9`省4ݸ&@# aX6@)D&jq بɮ'!d(f68I%FՕ 7ۏ6P]D~ݗcXUdkqM Q81q{{]^^7;ӯor|w/,Z((TTA-Wq4靂 1 `uM4b&V `2;2{ ,e15edL,%+ϝkU:H\VHeѤ ///W+8w\6i4 E)+aU 8vrXMӤg*)X̦#X&*zujDXVc͆lKeې`ͶRQoN@g9@m( LL@R̃hJ$b۴sαCML!EZ/%qf&5ϩ`jyC$fdSGE5ZMA0c46|@ն TfJx, 0O<}.ir #َ{[렫HN,|*Qkw5X-$tgBﱾ~kks#̌Mz菎W5|m !pBRC+sJBB]jFR={ ggg5*"cauO l>!缉ݒci@)ك8dG.  B_ )AmA ctJ$F%⎞`x||4bÙ ԨfR`K11s7kzפHz#޳9 :R[' Ѵe TŶ0".Wj%CN-u$!7Dwf[X|6ݬkUl L;G ["BIS6DG wd!y*M~}u㠪{ o}޽{LHjVDS|Bʧղ^#8czeljԨJ[.#:Q'Ai0iA 4F$3;A eǡU|R/Wטرi;LL8hpNꛬHo<;T$@Sc*CM{JɟR'jΝ;2`= 2#ŪjϮlDS`fc>4tv+_ 2G}QlXv@ [p%zJYgDPJJr_MFEU5Ěe] bGUػ۫hRNJM$Y>-"" BnНrL "b BLcXVgggWWW-PBJI M+HD qt4K;4ѩTV* `!Jb@P4TCb$T%@q `?| rGiHhIN-8}Sþ#/)vF ,1=C:AIQ uQ:DLcdF'PȆIR$6ZT"h!b8UM18Z@bi\4`cH }Ü2j(ϝo/|&LKDYƉUum><  \ͬi(Ij㼤tg4L;PaFvjb 'Jw\k<~2_Td=ۀ>3^yj̘mz[ȉal6juX2Y7ё^$umDEĉ%$a[kOc(8ZMX+x Gݺ,][m!ώ =Klܛg<0QaTbHa@A6}PR;|^;u0jV횶h]yE5Dk5i;A qq7}jRʦ*e86IeL}W_4̆zWR[qMӨFBBC!bdn6)qf6^?|D r!z͢7q"9,=(Z׊DaӔt&ʇMϬFFLgEu U߯U{MlD܎A*,˱Lr{EV:.*֚MF #oXMBϲg?eldQ$%&@I&63 "0(j}@aJ RRbS|ٽO~ܭ82~l6[Tf l25/dB *1&X,L0_`nfvlq1Q^c!ppp9ʟGM73A\~ld?x/Wfp%E,JM:0~ry 6)F`59rU"g [Х`Q Y%ˆ1"ZqG( "fm!fã զ?*1-~.9bIٗPh,)OfRe[x:}1PF1N6fLB"B)yCmT]S8kL.PsMIbi|2E5޽K4Yӓܲ,[W0ag];#lGk5:xug*ݢ:}yyHdPv#3t-+ 8Glz0)JsW6geFkJ!_^+-ZjWb?P XĂ4SPiO>ԑN Mԋ<]nqJu);bIDgs;{cj&wF{n6~L;[s>Hl+0]W9q p2ѱ#B xvCi֋?;RәfmnV1|*?ie]jD$+$"Ţ8+`3S1$PN稔/\ڽhOoc.Mu|y*w'N1 "12f<`Q=Ėq&OO,3BSߏ̜)""{/ dATdkJZQgyiiL.i:>NOQ ?ŰE bgo׃/0 Ym]{mx |U* yjߊCMu(,mڔA 敲I $\9*J!ͭ+P(9&_.ދ1E7tղ ;];c'\5ۧN(Y;j&pv /Mӄae*&fߵ~ 6_T,QU.pAJS,Ӌqd*X+pi~W4WM}W;%ܐ IDAT)CJ^U;gsKI%!3@F~%ӤgUۃA"EBNCRUj²yMʙS#jvPQ/s*hfh|4q<7h=YuC7tUS`G{ݯqz- MB:s(c@5xb4U':!d%& G~a)2s: 8<<|FӍ:z9B版tK&\'(]dR4l?PaϮj繚TWD P%!:;آq6-]*K@UTYe".qK!& 4%U#¤fj]$R !?XFbqHJhq`U!}_)\;*2Ĩa,E>haָͶKPS&H=4-z̞E"7>/,ߺbSTr"T !7}0ʄMPۂWLZVUM+{zy@`C\./..ѣG\^F۶{6 I~4#]_l?˺{~3.'MM,ܞGoSkröRMP~\%I&0U*Jn={_,/oqbrվDu@&TT@=r;4aCDcׄmև}߯)%DStoN!3gEg}DR2g,]AGkM#w-c0".%R ؏C(}o8"0"0QBt<Ѣ*:zFdDaD`PE01&d$gwn ޒ1lð*`΁|~px %fuyyYwi^?uSԬ; %;AcGm|8,L#C#YȫjٟU(Cc1ir4D@hʣLOWP& +9ocnbԄ%:"033IW#sc6F ^ϐtZPt~mlIKI9}F_v2U ?~måXgꬣl94d4Z(N@䈈J"E&$BTA!@4yD&,C$"f!)d,1f7Ϗb6 r~~ۏ>xpM2_|FjW=ӣ@WWW5W5چT]<,:bmI7wPim*\ujy"t{ L"b0 iz)'-.(>TJ")0wdUQ 1 EMMDPM:fB8ϰ CL=ж_{?Ͻs/s>{?6bqzzZ _i)(P \ 8"i,APP2*Y -zZTS.*~"pr-m&$}].K քBE4q.Qa#8q#"BEQՓyɤ]ݺuk>m[^y1Ƌ_<8{ڶ],'''px*$iq:׶DQCIɑeDLĠ)aX4+翇͛ "Q"qU>Q3@-,Tm4>#[]ӔMgu PAs l~pxx۶i&Ç??ӏ?1۷oA uyOf-iMŽv?lBvdcrm; @dh:-ݻ} м[,GGGX}7q*[ <~fbOR)arv kgvo|GM# GI Gb6[ imӄHD0;TQ@Ȩ (&$ČӺVvDz)l6t]cbƻ?w޽{nzx0 GR D 88o`Щ Zcׂ+q)' 5T0UM:Wu\!ނό٣W$0o UE @! RN9"ק.bjVu[L=cZ8>,ϵ+a0BH?O˥ɝ< Ne_&x7/{H4Q'j 'd;O^4툨4![Jlܗh(dRْɔ<*Ncp)$*()dDPU~"И -WEuWd;)܏ݮBՂ3@RbdfFQ3 ݻ|0 ]׵'j^˰W~2517gUc@}-i}WpMS#B[aU~D9M)،i2$BRAc_nvjdGuQ+$( {D\ʝF1=r1lR-O(Ei:1@$d̼\.C"p\U N}߯˾CDP']B]:??& +v6@$[qmfL8 a{(1q\y؎2dYs"kO| !*fe<-("N0c QS\;b d>[{i~~l1_,!'mw @R<2aKxӗa|Ht׋'u4V7(:lJW" ܽxe4 ;ʦ0l]AQQP$b/ڶIќoTd&AD4fiMG13E"ڳCss@`.Lt @ 1 'Grw9ߎ)FիժN9i&tJhnZ,?@دK*e_YI'E$3):;{u`CKL|*8!^aWCՈ2\g @Ka m;9Ո ١ 3q4v'"o3m=%rqY#h;ei&*&IF} A\6\*PVv3m쎎JĤEaP ø`Hl:(1&Y74 (}V6@sِ# ryY0#fZSLԘٻ7l>it{U*%lbLe7)/94_RziT;sBA*=J*$',7|}@ףG;M 56jț ͏l6fjy^q/Bfe8B`"EJ ZCd0_*p^~奮ZMCs4fGìtԶM;nSRJ& {v#1 fIs WM+FkHk;Tr_ǔl6VپVUo;IHmӵ  (=y"00j#טTqc= IJSB֟un6lF6x)pQLybZ77sM3`4X6eEl63sQ &h;9-2EnNxlæ5+~ة/ck[ήڍ. `yi~x0zFXVa灝aZL IARJTB45PTd,2 Lӟ{ϭk%I@ @AJ)*BH<0ZJG!` c\C`%")iZ$Z=4yWNy6w/ø1 5A>@So;g;/8#@Okk po߶لQEU:i^=?l\ 1\\^z5f%"i!N|"tM58 RJð6 ^QTp#9N)"2fD})p10vRJ1}?cΙ0HZ<!ND(=`O0ajUIpcxqms͖ ;;õB"7Z!j$QbbߢBM;Ba ciej/IP#0,r- ~mɉH뼥Lowϫy7:d Z#ᑭiÃ~~$DD1,fDͰIx$؛(2݅(!:rXc67Q$ubZ;+YBdf@XjBP$B&η#rLH)gH`S2yfvt]W O-H,N9Zן M/,_*Rh7;>r}} ^Zfctw >γ2I )%#+7$&;k+"1#YR. RT "!DShk*(rm^H D]J'*$H5dsf2@HDQ6!B$8 (FFJ$O<<B\_س%>wT4s:u^3ˆ:!E־Nv}lN ͔_vsg[l69_.[::e(bŬ:̳9ǀ!t)!%H5艃$롟%+ #q$ Jf͂ ĦDL@(D4(QC]#|HSr.[uB}kG9ׇAD\]c.7&D`1\;omc* xNWJm육8&Pc%+oFMDD@̭DCtLB} #BDȬODfM4CPSü;rH 峕 0J"+1RJs}%[M(8ggg3c|Ӷ1 d2lŹE~smR+[֐4hG6@tEh۶@cN4۳[LdiRL!5vZܨL~qFuͬ30IL1VbL!2^h#\:$ BIDǮ9qf_("L'"jyuuU4SJQ)%0KtDi\^]\hPGCH "JƧ~̳]3ept*E;99] H)GV3~-Ղ?qr ;==M)*sN4s]^q{?D$SIcCn㉚|5 x)v2=ok!$+o >%,TRa$%m)DIU5FIɆy`jrHut*l6OmXu !DcD)1:itdo=R'TGvqWOX.kn׾swmiI>8}Srj%b~8Xb(NŁD|38N0I5$ FUH A;e$Tb0&J1G})D,J4#O}IgsP}6mO ra`AE촌ok#7'<<Azvdwn !sٟ0u{/B֠B1 irx^ X)%HUr!mw%XݚDJ,CjDMJbx% wE*}x*1F$29ezf&f^Do.0;٠7ڄY-u@cґ}8UvVTXyT;:{eVkb0Qւdڔx'Q皦dIYRUdfѨSؚ yT; ;HiOnDR^ d#cfCt f&b2b(g*Vj"ruuED }'|'y0S=8smZYvz_*8e܄S97k$iR\'"c&vQ @ک\q:QLDI'@RpQ`!Si!sNb#!F3ZSBEv:([jB_/l G{W>X,=&Oy &զsQ=g>h ny޿젚<ծON2P:]adα"fèSiSIa2yjDtǟl *a2>0ݥM^kTHk@`Z 9`f)"K4M J%L,$ӃR)#"%Apqp~v6haDH|C:Fjݾ}fRJE4,Bit.ƍB@$3vƥ IDATqRj'yAOqT` oȿ"\RRJ!% AW2f{9z3qnRA'i?Gn߾y;uMa_R2{Lwam [E21jm]B٧$|4yJi35Mb h5ۤj75AJ6haY8i @)KyWɺ2uЭ1iQPdTDbx*T ) 9¶iPBV3I /l%PɊfen׮'4gjkw7IS11FIEVWKDX,LԺm۶-:l8C#3ιmEG$1IJS ։@̓mT7ȹ@1\@Ҩ7trC.Ã;H23( i"{ we6a;+k}vkiKL}CJ!5TV.kFa`Qh@[n;mJzNJɺ;3SejRU@ER$9׹l6Iv"'u0] <|t>:rD$JTIL.Ϟ^]נbpQZF|8 ^#4#R㭤|ybg$BT?SCZ zZ]Y+Lr0 mNU/WY0ݝI5_=R" fdo^LJ_1m yf;'ٟ;-1 `3tx&D<ճO3Y#˯#$@%&DsWf8:n00ހ M5MC(Q9 *LOl4a" Dd|C Q}mLw=@2GXuyokŽϾGM"Z^#lY9=| T*Z@X?[_,v~]]ك(Ώ]cl:Ƭ_ks.6M3=*8fCl' D:k[CB5T @2 *Rhʌ!*2Fi0 <$v|~W(~gدm/5-#IMP 9R”"{wE??ӟd\~Yy M)BA& o5سs7糪`-@y-g*}('ړXMemBW#Bh%+%B!& oc$%}aHDG# =h>u.ũ H$ע \m?1Y 4 ":ǐ%_6@ ZO??}C _B\C'\?J/ 0\ѼEoy]O0"1'%̞4Q/7U.F406SG7g̬Q0!Vr_)%/T /[)Mv<]^:vFT,Xq1fc%Ui{Q.u3S7ʒi.m_|&?ejJ۷}CR]=n(DkH=6t3׎k`fs*sB8u}/>`^@sEhfH>ŚSVI 2.An^~W^ӪENbY$8wW7@}Y5M+Gؠ@0}~<_]F(%@n/;)48ϟ?T$XJ1~}=1?wg?Npv3$~xxONGߎ#O͛ι)MfFhyn??]PMU=wF:5M}䜟';UF"Z4x(QXW ?\ͯ0d6MRҒR7;L}_!|>U23*7b nJX9"2̇Wj;)M0!7}@U³-Ky(".BPmE7@ fN_ ƛϹn6Bw85^U۶EPjaG{&N]#)߾ywN;Q4U$rY7ײV5sEs?oڗonfRt1x 5sPy  B= lBUT.xEPUײ1+rJ[+ݬx7s~V۠F v(}uvD\ਢ@Nc.TϮBnY{QK*)~sĺ_kon5.9"k^5tV] KʪӮPUYƺVK9rd.>z~~97M3M7jq$˰e)_uoLKDIKM+:k1&:/*k}uz9 ?ˆK)=N01IF@,"E.d/p+֧UrV R\1f"VQU_rx*R[Vu1P*nbV'J Uu&bѯnKy䔿WUl5Gb#2,epx lO x\m9OW^ےj]wi Rjױ^*3 QU9A4l.Ơn>O9J"/sK%h2A,/+z{}E>};#} ߼obU@58.hԟ-ȗpբ?0D~TkL&j #/C\PD͠vܽysmHyvmRr-GɚeBx:[?w޽=͏ޝa8inƊ|Tߟ"9ƨ6`@TܛRsz>}!!I0' K'eQviۦ;KQcKd r;„W,8LkXo`QWB)e \s$kf ``j`j6˹qZZq"_4K$U JC.QOh)gm"$L~!ӓV)2a͗F/ /Md &;VR f5)DXcTL:6yȿo>ޮ64zNf qx)#jINg7"jٯH)ENP i+}KDT@۔KY\/UxkW+t]!k%JV'㳈_2p<=|T\7_ 32W":AsUѪU?~W1>@ӿ. ׈[kGRm\^vശ p{xbzf\ן]94N)4*xp*B_ods0+~fWy9h漒v;38K6Ͷ\րzIp6(?Snƨ: /k"@,*y*r=s\%!-MU Z>ήi*p}/1bV`%,a+ŒY \qy(XrR PNѦ:UMCK T-bzB݉f[ X9/Z`O6`o~6e ]ۼ\ڛj߾@3Y P$Ij̹04 LgȮoF.mWKRJv~7њo4о1oJ):wz7I 4M۶W26BAzIKѯaLGLo} n* UhL77 +GT ns} >y1oc>>ׇkv\"p@:"J)iǣ̑a SNREDx {}H_׃ud]kjUtkmUv}/V }1[D_sAl.8i ]XQLTiCBXxjb!03RՅKӬr?,_)pn{:yLү뱛 mh0u8c1P0VƝs}U}xem Z~ ^B Xo˗Ct o<~q適_QP뺏?:{]\_m<5;VA"u~+n*hCo."Ҵקczv~la3-ޤXVo*kulfAKL*9='?yC`9%jabpk7ORpIW^2TfJU n~D.P%)I)umi6D)%'zLkWwo3 !}͛?}DZiRu2D:)_Jh_|jVÇnst^SJ "]?ovDUԵqAQE|/% ?nkfzoUM2WfaV68_TY7nrB1񴏋Ela*&Λ< 5kUcXaFk~w8i<]c/B@8)v}Ǵe34R:i>JI =Exu}/[ti^6V7n%۶~k%n}or6 1@NyӒt) @wE4a~^La eSH=~xZV̌ܩe!]#*6MS$"^m {Ũ'TVk0RqL)zLj!k\]#J7]se2hL6f8 q]9fS.5&[** 8R5  LܧoŽ}+H.6`u6dV"$؋@fo޼qx{y\Wxr*l|鵢l^jSݼ &-z7RXYP!RH1[{uЭq}wIJh 揖fjts=A "]paqE n6'3#!2IU%Kjp"8qH/"Z TV}⵲͞B.í^i97@X^9T_NjsKE(n[7+|p6x")Y7u IDATT:>ϵJXPe)~:~8ߐ)բb`fv&5x)oVw .#_ʀjF@b1΅8:~E0*H6aԳY5ROOϟ?3ppˉ|>}Ӹp=`h`=x5їƻwYl 7)%QQ&((xB"i?9Zwi [RۆᅈXMW ъؼrG?!VlxHܱCժW,~U'g#3[ٮw=\ݻӢ*RUIں*2Ǯ&ՂL\^% >=iB"ow%IN KISJds9wΏMRIbŠ!ڥ<8'shBT@2C`fM\ 4# H@F @hie%G!)0太Zd?" O' L%Iů>%E3$*a؄R߹j:i\)S*`AHߚDBu,kHڬU .#DW"r%[xm'Ƌtnwv#U03`""> fF]lD$j}캓|Lf;{"?Qh]@x^R~ $D0` ЪC/\w^?~篿 ʔDd"Y_f*CSTH\|B0"P-Զ۷orJ)a4䜹v{<[:Oٿ ( @mh7u5Yz9$YUwEg[⼞9Qq#_p/;K?r0q}mvg HDS yB9k&̓|C"P-p)*VL >i m8UMiTKoĀ3"Bvԥ2F+%yn4o7ȅI+erMWU*osr|>9g}f0wyB8)\JQԦ19 iѾfkHM9\i]ƿy#$[Yc~9W"qn6XZh"RvR\CF*/}~w!Zu%g]2&ITJ15b)%OyL+P,)M@(jao͌(1x8*`[⺈o|x'32N^ھym{=.J\wv+.+ ԩ-U)- S ?aj# O֌qwwp'@Rbm} "8$o:+L1?k#"SNX,41\$}eSP"iW+cC$66^v|vjQ+i~:`.fU9$kPhN[61z\2RLLZ9xg-=#RQ` D!^趴Yͳ)7677[ ݜEanx]í۔8PڶMr=iJȹK/}tu *08SlE="wZ*%61*2+Q]1TJQjɒtϠ#rK8آ2 2ZmLjdH)镍q)eFCMHq!ѕ9gF2]bJ&ЅalJCssy̳h Jq4!,4 9Otz>]#.Hu找lȰϦxMQTL% |#A~VXy` tm /.^|LM)f86L6VJkC)7&SxWFL7@ vd)%.j)0)HHn!4}ӇК24@FPAGd{]&9?~zZ>~߫%N||zv" Tx>imt>iJɩL2hnG;16!JRJhX y?Lg3Cة(xȧǏ܄pwwuc-'ͮkoD5n _At nm6vKsR* a8>=PqtuW*IɕĪLR'nߝ޽s3w@%drm02-d t.%4.a M HQ4;sJcemt<|zbDA"Cƽ^Љv1x~~~&#g)D&;1v `M g%)tD@9gBE3()cˌ9yWd}!bN?~ 秧gmߵ@q 0KLzJTp^2oY>\Ӯ ׺˚dn, ]gH/Ʀ{os.8ۇsiuˮ/!-q""9i耴*KQb˪MH&jC\R@8 n !Zumih2սZ; '3&d-&"J)4VJbR+t:riiBb c;7x|K Z b@nbkᑿ9]`[4TK2sա`+8-`sfM;x(/"^D@ ffH&!r>}b+9Oc:Isqb ↹h'Dsj/ZP(o?}z y`8$61 ] 8p6YN`&J}ߴ|>,`!HQѸ"υs;F15ERH<\`UB*]}4+}8 <^Eyo69@pJ7b:EDJGo}s&fU)uA8p88½ytVnc*Etc9kfu]A4[40'oaʷ]"8c۶ĘK6k0 4J)(s}=z 䖶m⋦ C>=>_*%Qڜs.F2"*R-M׊"!XJ0fy&Q<1Oѫ @)YKR21L 9KJEr| `"&(LK,EMV/9x36ج]/Go\MpůE4+"8؛7o>i ~7Nd2{.:Y iжCl۟i)d%.mm7$Z&wpߋdf$V3z7!4B20yO:#oߞ8$f\R1Q3;᡿o{\lHNb f: g-Uvnߏy/ N~<l:P`KYG,$lfT|hfт-f0+fkYLEr>?=vldt#l[ &4S!@e 3Y `C}la¤UJq@ ]ֺuZ1~& b߷[s.%SYb"@W]r|::\"zPOϟvTJR! ;qEB0t}OHjJ]ׅ<; AE0h "&[!Rsjrf$b06jP (G *8}= tU10&]. -H"h552B^N> fNMltGFOԲ!:= 2@<]!?/Os#0ǦݡXN#38gm[|#2b1"͙j4}o&a0z<ʠR Ni:8FD׎KIW/}t<>mHy7)OEt:I.ǧ)44-u]SJ!4e-4XiQ%3x{r)ِ x.*-%:YfpͶjRmӇ=ZEyP4w|z4 a<6d21fQY!Yq23 M 0k@Jȱ qN.6?xNyw_p<>O PE,M(!@*sڶ4 O}+e4U93XUi("4dfvȀeJrCQ!fI}cdFGzI1K.RD`rΚ1!<2Ҿmx$4!UY۟̚M$+22vׇhSPEW *jTu4 *=Q_2Gľ.,jH+C?db}bȺg(GBCUN9FԼ+USS6 "p  t-h6 1c`®=|WˁkÙMԹ\E`!zN1)u\ra8vo|OÛ7o|R?>>;4eI*% .zMC6&HbէE r| !P$95:M l8#,T̨ 0"7!K4"' \Y|@wo1d)c$< ڥ:vU,7tzPl4,DsHΈv ZhN!Ƈ7wg-e1 VY}&s<_eqvC#`&`ENy̒ VӜ8M4lb[ &EzQUKJyJSʹns.:Wr`LdU43+R > LW Ís`ԍWKE(,LGvo޼.yWH#q0_\ӡ7N{OC)er~Nbc|xxݮk|z|"\/~3XM*1UPaRL235Bw5X[ASj6㘆x>,,K)9'J- S6"I؈P DRp38_=-vڻ߄S??Lcf/ˇϏ߽t:1LӰgg)9,oMG;"r~ՙhmԍ8sިB.]MD')9RbfUt4&0A1'kF89D +5YkPs XCVw{ "@!P `QEFnk2†B<[!v/gX/N$]#fP&sfӇo?|=?19OyRJɪx>1UJ",IULHyYHIcg IDAT LrsQ5"M`H%~`lL"i0 &!SsI46]KSqbo䜟S[ *Rs 11! "OK3$j.75 sp3,j;`515ۮM4ӜY"e&S#D `JӇNqFb hv:'S Ox0f[-,9Ef3.ߏ|(0 L>W̬\T"&7Σiǜ.6P娠)2Rm9Rt<_ͫ]s{QѧSUM Y4HfVLv }l-~^!H[7"[$ 59x_Y$7b ^V܂TLZY>Zz|,Ȁ!N4!FFk:cMC))I9a83b۾CҸ(>ZTqB1hE@ fq*C:JVm88?j0ssg Z] MJ.rN5@$= PDC,F3u|$pn C1ъ`MC۶ZX7Ю:}/BRXV]g./B 003,E=!31EՈФdRТĵ}mvdMĨ`s * FCU=G-1}w蕨5!m12ٽ;dowwD3iƜ_W9OMCۮ?} Q鲔1@b^tGu0cákRfA%6$"HD4oH>|4MIё焧y lr1xH8wD,,MDߎDbxp?O?\@ϟ~pgm({ߍu]ϧyY/ÐKY.rYRJ)NB0R9*LbR*qJsbU,C%4N&I0|w䥮PJ3ȞS>ϧDlFD4 7CoxbVbNyrD.p3齨Dpwgr7[{f{af1t[(%T5={OېΉo81Iuw'Nxǻ;EF nDUe5qiZB<]sjH͈dh%[h,$MS2|~\e]W3[nLJ_<ϗiy1Ӕ2gyk<>}{<梚Ex0><~4zs5s:4J UA4mfKX\wc @:tBV$!Gׯe|H;̐"ZJd`&fo`. NbTؼbDC2H-| ̍'g ^J,$9lVvN)l92p;9{ .3"bv"4*8ݻ{Ji]Ws@B;ҋp}1%>>ܗ!2( %fHͥ'f67]RΪ0vu[387'(-tg0~`O9= n/M ubClC8lHJ`g7d,/XF&h39Y PTc}6~]ef,f`1f'G1NҬV"!mR /BAaDx W‡gsRUZӘ@ħ9! k5t<?,92;U'"^M?8YdS}t&sqiLIF3S\.痗! t: a R~6Lb2'E"͍v+oyHY cClk![j- ߂̕( TH9#̃< @,-PW#F/7U7s;G 3Ω屢Ffvf./_y~zn ?WM F|mvQg#)haGaNީd("6S6wmY7`:Sif;'aI53H٦ˑHLdp<=+~;ܵa1>fK.b& }NZic zksTV K`r0 TksUޟdҪn nBᤠ"28i-K)ӧu]8ӺU Je=S] ?8>02벬R˲j-WUVJtՀ) )U=Yyĕۏݘ'@b7vXƂ?kfVw !܎_}OСxjsˉ<eJD0Sڛ~Щa m`3 f{ 8*b ՉPy 8}ݍZF6]zcY̢y":4q"ID2dSÐ9 M)&ZNs/__^^_vXđJYY{Oa~A;.ݻ{,3cw`<}j1\ lvޝi7,εKK?t;](ì"y+pwn & p #q HP A#TR0Wb1;9-,xllUHB؀$)y'Uսn6 $8Ұ(q <"TKydp~9駏?yr/?k9Nal0T5c8LU-ZsΌ<;ro٩Ý77_.7ss<_͛CD7!'dHd\F$9DD }Z1F J.ДB1K VH<a^%=nAe $[.( G% d].Jb~ NJT9 UPC8i>Q6NNs 7ȩu1DƐ[=nO>tvqZ@NfN(km8#U_oЊr~>祬vp0'|b}3AܣoO4L~{f&a{rx!j>|nG=UUJ䆜`oP*ۅt?,GK$"Άfc7΀! hgGM0rqw (1tq3RJ[4qT,AR&f' $ f #aҏXTxۅ:` !%&mfBSop*P?ׯ߿}{!O>xWVӯ_~%ABPM_}+y\`uAxd``Pj`07o ٛan06Ga6w2WWMFj%) ^A6Q2&GSWؔ:p 2鶆A X}m`+} g'WJj*MիWG Dkh $;) Ruml' nG> 00mF ^켖V-ixZAr"ɞ'4NGj;ka:d'Y#ͶzKi>W[F\70zP9n. ]z}բZ$k)(Xl^]u(=5m3=]WwYHa9w[YSHE Y`gFauoptIU dĕIRciYFwl y$3@as'p{Yu^slg^v*[|CV|# 9ߎܫ'-8L8b$$q$I\%;Yr5|AvA\ZuwB*26?M5JZ&fƦ1.7ZU=:W--5m{N. tѐF-BW637] 4sW2`(>qcv;7Rʒ3 det0%6g E)xxTjhEgflg_yvH1UZlSh<{[Aޢd"ѫEy=eQ ]zWXVT%mnv91޷,=K8q`H^b6ϖt {,@)% daaN-N4;j3ZNr=7M -X[ED^|{Ok-۴fEk*̵":\3O7”ii_!IDAT}~o"t: uNUYjRPyuZpPKj1!Z[եQJJZ\diHȡnd) 9aL29-@y],0.p5'US[7?-,ߎx{bUoU{ܼw!Lw&EIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image04.png000077500000000000000000001437061423156660700227740ustar00rootroot00000000000000PNG  IHDR?1tEXtCommentCreated with The GIMPd%n IDATxْ̽%ɑ%vKF( 4pFFZ"|O u624Ԟwq7SU>_KDF.@HJVT䍻nGWݢUC2edsу#s@]AsWflV X633#IRDDDU9|sʿHݟ޾6W_.ΒriOOW5۬}9jrq2! ҅~;ٱwo[ǭzwxg9YN%OٳEje>4>[_`vC0: n ^SǫmV/Kn4"!8IJyӈcVn=B0jg9ˮm%u[d!NXD#w+ Y|LDB! Qrl;p!!NÕu8IY$ 3h6K$ Ҕ "fZv T0Dcvp2|ď>ßaha~6Gmq|zx9~nqկWvuzzq\՘$x=ӣx<{;;|^Z敊M]PHsltW# nB0 M顺 N;8r^0 f.01[wC 3' 533wEg/?싯{#3jboBLf"8`R0 gP\r N}}vNTY~< G[mP﮻;I8\xtOf|xOwOz3@{O{5ۨS4at(vJwj?Y.NSylУ&>8;}pq{z$3XwcnÊޥ~9;+ivS@H)kFJbtڈz~?r/c+ߖwp7R`C5A'LI n̠;x?ޝ;yrZ@UU?fwEVk';tb{Y!o+޹OOoٳ/?XRNS8\ܵ\z^|m9L;>{Nsݶ΃60=;>AsiҪ=GW>wv]VXft@(AtKiik9%OC&@l.)"%O "y a QV>sKD>vf)o+ӣ{vzL?OZgO]R88kʛǍivuyORÜ~F4$ӟOr+ZZΩ݊LbۆwGoO dSb;oV S5R0t"` E 1!θt&!Bq-q-B݈@ nL]߭%pfKPQ # I}M%7, V-Rވ #_fw9=yp~YWWU Lܧc'31Ԩ+4T3CM?zO|> ;͢gUtSh ?zqUb6B<:ޑ d#4 Be A Í1e}Np#z9t$87s47BuY @.wч0$!y`b_wC9҈~9 xG]'}圄\,EY W̘4(-¨.@(ts7A_hZz&"f.BH;#5ZpQeР(%l@pT=fl'5R7IkKD8NTnlp*Z$"gOϿ'3-{]9xwY Ԩ=;}Rsm@Lx[\.˅9\]@&q2KJdp@%3\i :\ 瓣3@Z g]fQ`Pq*Ml}2(]59\͓lMd)=C(` 9dCCŷg[ӻkSZ$Z [O"XPp!+O.DDel fV-7}/g{IhѐmOTw0B!*H A@@*eDTfQCSütխKyRBR4!,iѩ#Į ,a i(%2K.4Gnp~7gW? }R)"0Z v7T^^yHD W \sY5gԳ&< !+RMzcozl8xj??:`0G-[I?P1[7ͧ_W qC  .zy_,^79[䈁Rq;0;>,UgU ]Ql@"1 ԠFؐHJsf!`hQ\%(`(ФBSVK)*cvIn@u.{:^PpIkjp;oRzY;.7 ~O'eb1P=2FE~;sz@؂o;߹UTm_/w)Ї"d@z^98{.aqgfB!Z"s&Pj̐Ne]";2_8npRG4cͧ~?5x8Wy"3R,аld`70ЈQܜ]< XR Zw.c7ť9.H矴>ۙ3gP3 wо@0dG(@=$,$ `7:Px:BaE(%G Fs>> ߐohdFhb q)`MD_UT0ώL#4jmZ" 侻lЃćۦx<.O.?|O&̂ D2& P3RL!M!\(.bLJ蔝Jet)UM:]3?O\߰Z{oOW#zxo7 ]aᝑF]Pzld7Ô{dz*o΅6|)L#irUw~dZ>ξQgsi^\XVk9 箄3 `s6hVYD5Iْ2=VWV kJ4)Ft P='pm?}v#sy]7a:dm?H8xU(vO| 4kol,"iJ֜-"a/!HgH7މћ]P-Jki.wMlMb1td ½14M!v8ҨJnB2(w)$$/?g() `?1ZFnDDPǶI(_KŬnVɬP L`"D[v }^ŝ9=/s6\Su?%0k@b>yS9x#_*畹ZNgOK.|ͳKEB@"Q*o+ݵL KT`2>K#$@juPR ZD$ T`!p PY:a tL_&_ 1-+=4Y-DYʹݟQlV6 o } qZ;s7DGvq(Bg4 D[~ēCpJFN&Ć 4SRx2.QZXJIkW" 0# ٪Θ,<[tѱlRV湴 N0èNNK6RPPtp۰m}{pifer5OR)Xˤ@DͲ,2Z!b'sqC#)qS-,FP4^Jc@"F$9e^^gO{}4{(޻w/z,T`MiګT&Ȏl؆ez[5){u=LoRWzꋫRH;ڃ1o{bn4l݈)2t莝f;>V-nS8_T/p|$h:`D2&ʙK^_}btΣ"EG[F2E#1i@aD&%- cVl}ϮggTV\˥^,tU$a-h>.~n|Dv[ה|/q8̛"{b=~ΩZC@an%m/7@; 6֟bqi(K0FhmQ#I9ްvpR@s #-H(rmJk6sfblgDpBût]WXWu .//:.S}J%qZ^Qerq,k`+\Cn[)&E4vpx#iԅ(M9!<~g*Ro7DU,JN]1t ?b(NFrv&[Dh}HtaD zq7|߻د~ѣy{AfB0 ٔkfE_:o/ʗkKUUhf5u'0_-.nAzwyM\ o<ʝ~HY 79t0dV.F]F?K{tq]FPfϔ!06^g/ϾyϯN$s'>3?9ٽ֤ sA}}NI=ty]W:Re{م>",K6HXQ"(#,4O鹡xvD jnPzV0ǰOu{nC߹/d9.2>f@|Sz;@qW+,7/`)z(1HHU-[2qlh߮bmo__'xܥ_벝ipd w˔lHϮ֗WgK_Z5z̫d!vHe2h׾qyC)|SGCM-6d .38ck|khN9]KY^1uY@V8~P>xK&VxAZq%9CA('k2z'3_&.jUf C=-J .X‰:]J[{fQjRJvaR3$0`(ce*LNܽ7!zwP2zF' V^ٷѕ01Jy>%ԝq@C\{:vѫ<:C|w"p^жD8P9HYy&: j%I!Һ@ۂ2 kePLwB/pqŀ B/dAiNBQB0+t;00It+Uª W>1B^=aӈ][Co 12-N̦M1ԧ!īѵHb>zdWs{jpQŋhN@lƫkQƯZ_*6T IJ z$HDς)Pmcof妐qTs*},.܅NCBF᎙*\e+dw0mG B) V7"ڮN8}cpWdvZR@{9{ Q7(S̓ \Jbp}t.a8>lw@uH.Za= (H:J.Ǎ &O `$6[$<6Ȥ^\u[ vmWI"AkX  Z {J }c5K۠U_U&l%@VVO'6G %Ts,Xs bR&܊PĨt쀩,BDD_~f͉xCs(#mڪtA  B*D@ dl#}Btd Dz!rE1 Ryi qڪk3a[c#65 m/й\kx>oo* 4&)NxmQIn/H RZq9y!)nԔ-Z*trsn74nņjڿᯑr;rqک}kSԖ);Ln?~V&(/7ufEBhc$]PvJlpRYҬYHׂSiäI>ApV7^Vy륨 pyFD(R:~a3?ebiD$Hm&HUp)$f 긌Um4p&@ qz)cGt"TUn`ۭC0?m9@7 KިO[2)!$(B;kІ8h6ܪC5~.u>\t$Ll|$Zck0ߖmctʇL|r#n_&#( >}f\d o7TVH0#d@ >Q4M !''gbÇ2=9>sr|zzsyu~)媝ώպ[YΙ}0= H쫛Q 3Fl[`l<L]cb,8W1ˬP퉖pi,ZK"eZw /pq;(7:xqT]$!@B]-Q㮠iW7T5*o.Ϳ[U63%l( &8鋕7)l܌CsMxoۙ N:v'^xmJ`;;KXhvv!3M^ Yi.>|l~l9.5)nf[9!Y-}^x-Si(xNyv5x#NFW¿p@?b7~"xD$4Mlp;w6C/#L"g"Ȋ6BL`DGJKpSa+˔B))PA>m1ߤ"_ fhYX[clsC*(@%0x9B(18 Nv#M'70ER0RbDDbHVQ#M yt޿s~/dL`b[KѴ3Hbd|zrv设Y6=JPxA!dĠ9䪤s67e6I24 ֒""F'=;ܑ )/;Bnc2th)`XfvJ(4VoS|+ƶcodqTnnUMRM8M7FI=swAD j+Z.F GS LnE\شi5}k x8.܅rNOb(,<Ô8/3TQO?o{<_bl "\1s.d;ߊ#H0iºOѶ*""]⭁Go; 0" lR]ON>9pqm]"Md0/MqCY6?½ԗ/0~]KNNK}F@2omWM?~uW<:~r4YC&%>հDuk`~r~w8]]4!f[>5a aQ}5 RLn䔯k0 5;M\w;hH!|jN ETL@z_Q7hrS7\)dvOd˖AQ!Cf~_?]:fM%V 0/ ݁6O__>d5oۆsnD ! f֜Ҟ8{paټ[Mv-=(\tA'K|귻a$޲NAbrpPnr4mh[sx'$'{w?ǿ݌Sa&yne'^)*_%A exD6sUٓ躑J'3:z-D$T"4pW S{GL>_cju|޾m,bzc1ݪa}r`~|j>2fyX HdP5Sq]2UQm֩0\&ɭ#4QOfG;1Ncz9}mË@X)Bó<!pvE<- H>_ڣPk^6gc"Rg\-0?yg~ Fj"y:z5~`ݝC$Ty"!X%@T-""2l12Ra u|qvm 6bDG6Degĕ 3%΄- MfQ00z3a4BANg`s|ooW9ۺWa{z~sz,]r8֣_]Ҕ%dkzy-l?9#~7XNFհ f(&fW.>T74"h/7.ya.<`/tbtPh׸[2bDRhHc$!Ȱu\j-*\у8;'xN2O߻+t˫gqr2;>=f桝3oFAsܔzyF~{ⳋǁB- _OBL N wlSB^ <}] /v`>_6[0QlbՋN-bw̰̏.oQZ[!̮..WլiCDʋw}I׳[m۞J[<#ރ4X>_ο~OחOψ9KJܤ[>H̓)Nʠ1ZS }ٷMG[OwxIʝv'(`Mna[zVwc;WCץo.~u[ˋ’[  ;]3nyP|$+8+Ѽ{hmvF"~LMxI(3[ 5-`|8aW[`6wiN-x ֬U{,Vϖ_FiYq2u+Frq,挋̮ٹ}.>*+ebxrqWy \<8 Mgp@ ̪H׎oӡ{ _ Vfm7/xα&~uD5R(Q~? i9pN^x,jnRb Bb mvS YcsVG3 Mv6YfqLl.q>1!u}Յ\\jrC&bvrg;;@N R. J](UoBU!SE6#v [d˹"]ᕺ!t@MSZĴmlOmqfpnBJz:5*;({ATНV$Lr±Q8RePS;YQReH tobAre/qs0й< !ko1i\uhm4 4t׫~{ӶsִG}g$m 2'(T#] >\E~Q:0UbD0BnffbX<B{9^`ت@ DyV1vn$֞f#KjspSUP2OQ?TTYe/B bA)֛A*نe7]׍;ڨ6 S8@u1Nʪ-FPV)5q9#8#:$2(J)9gjrH\ 9Hix}L9s-:V TX&`U9n׈eGdg% 1|)>EFy2βk<\L}14^1ȑn_=} yiYBL 6a/i~9~ϺO)b:Ifw8?>-W{۶]V"eBhm[ư95L>RilXE3W`MoD?&]=^@f >?D]8^Vbo&r/bCL{4o~_x?]r\NY4}_\>39o3]<;9 5&M(R] QGȱn>.EfA`S6cͭN%ÍW[&t֎ظrGgb%e,"iq1ʛ} Tmߙ ^Sxgqv$|yI?۟/ٯ嫋.[~rj - eSm3 MNfJZzӭLoR& Uf߅,Kߝa܃&)m!½Ai $;I&0B@7SQ^-5]ѯ'O?=:?g/'OqY3R9䥟0nǭon`2 ܝev{<ꐨ#7s/Sڛ䵲ӽ|y[W;]B ^)_EN3B.Sr]nYJ0av!=z?s ţ."Hh"Y+m,ce?{dٕ\̽ϭ`8|ԈDڔD!2%>Kv?Haٖ([0MC 9uٙkCsj 0踨.TWw+ü!2{{5?|DN̜{Ց"T * *[fo+P./Oc3h^fCv6?b:]?Wda y^xٮqr IDAT_\}8 b CO_Na;[rЁ.[?6)jSd?ǽ)Gܠɭ;N7>ËYnNH" j_{??ZOˍ5-^Pc8 "#uw/'sU[vTR\ l!BhvRs'#VhUqv3wWUz!<=~HKo1$ڦn%p8GCoPD5I~vG"Qx.}L*kYzN8g`?hӉd Ͽ?o%YKrZn Nh֮UP֪EL,Z;ѸfVإep9wэH/T <[&ּy3ߕYDlb.XﶻT~DskK~ Y=@&؊} 7fǞ,%jV=-׻$ WEVbt 03kenJsΟ5бzWjQ.y?Ƅu[LwZPZECoj3+d߷~wÿ<FF3Sv^y9 rw;+IzyfHõ,Mfhhګp#ۊ$7k2KLuzL٫4*nMu0oNNy65@cozQ]}T͞F%s%@W9UZ>< #o,kza+}Oլ 7LMC@@e>Z"6Ű~_;{vwnۺmdE$[4Xoz/?Aً2Ȍ ?2Szl|iŲ5gR48eSdS}Jy^{{U7}Aesf7Jդvs,<# jW5;7,Yv{x{4hA!m𠼚?geh?M5CY/UQܼ_!ug z{ Z N ~!׿O>~/{O֕`-~(3&IW~yĶ٫R޻7IhGc-ihю}be9%uPiLJq 5&N*YJŸ__{k&x4˄(G 8خvv=Te]X9!j]~y_}41?P228h5{ DG-x@uo껿w~Slz>}~|l1"B؟rZ ~m0W[R62΄\&bR:,.?0%8I![ίŽs]gaz4C%nO{̝~H+G3o`R[(#w6]UQ-c `7%7Ċ\!8J֎p =#ofuvDɁ w  m5׳^>}|_~}rg$1yTO0'.njh 1`07GѫVm׬ X=fxYƒ9d;c9լO؛~cmG: oǁ Gzչtuf:v ok3tU&a_݆tAB5}JK @l(6 m'5w~EuuLo Nw;ph]~p \ czDbׯg}g-]fIVcuVWS.)WPV{y3Zu_K>l>,N 툡d5RJd~=]Hm/U'\Ϋ4Lep6%zh=l\.W,ٺU#.=nn|7f U¬M7p@{k x[1\ֽR,8Ľ7i7/Hqa߆"^|O?ٟ}[4Ʀ2L0ve$5ssVr#%%{2Qn.9QT'$k 1׆p @jk~͡]:[!YoB{<}UҁUؓ.ﵕ 4B? =̤5|W=@9mv/ƶvY>{d~2x۫Yf BK˲<wn!v'#24D3;_{_o%>G{?/G?]nkn`[Dc1eq7$L Q{jnLFsH =PQUFRӔz%].Uw, y#Ѯ2A^XgxV82N]=>e*!S.D| yן,#kEBf]_v#^5zW' X V;oj1hO䝩t,Vr;ʼoR-˧__>|^J9nN|~f~:ZcpDDYzFDfR=>YR5>}-f$+Ƽ^4ɼ`P>هG}`qd^cYHcj)amGX0m ͊"=B\>HAj{B䗢C K:Y[zE\2(ŀ&ѹsxmu2-:tcCy-m^>^@ypA'׾+װS_ӗ>{???ÿY?1sޟ9N703!qY׾t{^\Jo2d&0\ɀFʰbIt)U"%A+y$$ռsyk#)9ҕg|)$Y%qg ?Tp{J.,{{{h/Po+c9Y)*H+ɈFdMH ta@rnͽTwWj=$k}C/toG]m];ZG_06,/7oxb^?}o| FߞN]X~2>\-)C˞>INvA˵2̹AoI> 5]%(K$Ku@dh^pG BACb&d ͊Tn3H5G "%ק=Q?2| ՗ "ffF2=QPync@o%^G`-cNnx]ړ3 /vss  "m'n_?_}?ݿ_ɷ18;/(ׯLGϟ=*aIB} 92mM(:b-Z 9/X%NkJнLy yRERf$S5,BoQMXaPB5=' CH<8jg|=56@ ,TJ #0Jm;ܭ-sU 1 GI]4@]0/*a)mNwс<ס N?o~/?{w>>ƺηϞxオ'n-:㛿'j 5 $LX(=,x}Zm!q(1h͍6m$8`QB+9 B>M>/> õ|4_hlEL4U^4ׁDwK*cjܿW,LE]5<6W; 'kr<GL!n^|R_vka, wB? ۊWO>_[G_ҦBDoo^q9o};[.ma8̃5ExN gϫ@u3Xy"lN/ f2SaE΄:O+;jn. M\ֽL= H4=PM- BÀ -KbXK}ls4a2å|` Le+u,V9i:i47ݼo_{hrXf ۆ<{wx $_W^nz:D'16u}&'ET-Mqx}Yxӗ U do$Y Ne,ނ6%fMXVd#9wOuyA}v S> .mz_axַݬ5on[.-NW`'% @:JS+g{RF~ (ȏ:GۅvߵLa l pr JDﵙjzE  IO=^|7_;7|16SPɁYX:kѧTA~'2|k(zٳ/QSh@6F~{/kj[dž!F(̓H/҂tsSn &`i"'[~2uSelF1*>DH8;ܪ}(G.EXDkB3t 03 1 F`"M@|_u ڿ.*3{nR/v!/r@eWus"UBC<KG;_5FvlP4iVfn]^TzAo7d%s&MtCwk_ߏMcpl ?w|'Xo=X<#-ht"Ilkd%Lf^^Dk&_yhv{ڌhB+S$90tLli1%uqG jusΝ*j"㼇|=D׳}&C̢2FMT@Uho:7w0׀r1_!Gzp[lGTAr@sSqUgDGv]M[H2lmYԘuYnAA0atXXU<]՟KQJ0WrMю/۶7]micp@fM>! OIquxJFZȮM&JN+ڮ(]]nj'})ZoA%@/ἣa!fEqCV_W 8`3 MSON#b}iV:_q:J+Aܐkb36̒F,my5an5up3x>D{lvϵvѼ~H3L$Lҭ ݚn)`v;{Bv+^̣U(%>uz.)e¸s8H8ĚYq_ dPUb}\Wmz5V=-uyX)йK6jMh  M2"a**?Y5w3ëU#'ސn~1OtmNmmA@Eʧ|{с81vU_7%P7x+ɷӼ IL%YǾj;40%TnCɩ|8ŴE$ysGvbŚʘZ Q$b;Փ)  k&K%tM│zJnf$w_=3("=F|2/%1"mqŖZfƊْm^{kO& iGH,XG8]UMuu9_ 8859pHh`r܅٢P|%6d Mil9}Y N*fݔ A]niHnkX: c`=klFnC:FH%А馤$h;@k2KF0G.a: a _zStE3P a&'6U+bzb0N+k5󽠫h;}Nm%}6<& ?kmFB0b! d#4G*8'DZJ YSM?`-VvMKk;`6Rݠ>64[x4מg2s)3yˤd 0Tf1Z{!@rcO~8)~A!na f6vJls(-5ey)Jc:: ЬAٵB}^*R Kl_ ]T2 )4%(z@e(7ˁ1Sogn`&{7'e`2! Zkh} wq7@`%μ_#}[b ac0"TAFgZS7HH7CV ɼGSJz͑s3$LgTm:LMC}fp}p}+y߂HgcKؼmf~* UZn l:4U%ɫh yu>J#EG~g3}Y7< 9)saWu.<.۠ İ6d[#ym 17kb==ld> ACHmҷĠmDt)Y! i${FcV_ܨ)zoj`0 ֤ni_X47[!B4XsB揻i廿..]k P)^ s|TC1X۩,ɹ FbȒ$ӶbH3S;SOPljRDA~R _ c j m>b-O`16mbʴe82iL.T0 F^pBq4=R\I+4ЊjOBVu$H!5n-m:?ןyLpͱMF?/!޹XƮ7>rn1xeVKBb $B(|H0쁃؜U[dWV֍s_(k21MqnAXGlhٍ!H'SGzGn 9 r)5,J²e2#@3c4)*Q 9blXcdʫnEi_y$)o G\F47oـG_{`{R\Z!F KMیG$9-+xvAٓGf_9a jڍGFhMpARargI qɋ xAM?,,G:n) IDAT &7 NX7 5r nPӐ&+ 4М3ڠ& FXEndD6"RPG_q *U .ڤt.DD+W Z3uuvK&9x58$Yj2)mG|n|qoqZ^# )TI3f6+~θѝA5Dt\B2@LN֝A 2OkOAsߕD.VĬz#7z!fџHybPhKo|yY*wM?P7]AMJ6A-{S:nxci?߀ tDhMy/4]H EzRU׫Y ŘkqTj\Ni^m4 @4rt~61ֶnhNOvHav${bsdOa,Ѽx8ԙ>Vжb|C}0aN(M!dsQD2- }YӧkZ}ppwQH3f_s)iUԭWgwzޮhH0/c_阩 ש0_|8?ZeG\@!o2[ {:Zu!2JT , |+ }?]T`{]4]bF4e_Mcx~$gK p F?`陧Fg`p4kO@f9򵐪;Z4nq۪C#|MŰHhFy)PE)d0RJS:jr<݈FY#5hʉJ{ \{st |N̦xfWǧz_x,$tKm> h~ gbAVbb:,5g&6jV3M|n1Chz1Nrԋz=9I+f_aW(XS`Ʋ,ym)%DcZ2i$`^@;R۱M ȬyY:&$K|8y ߶b3D*{H(@JZ,+Ė& gF Mjnܛ:_n{yƨxߠ5_#]uW bՈ>{ cE!,Ja PLh 2i.!omYp:MGBHD*z <ezHEUJ,%θWѬ%sp6`:>5h+J{3ZLAk%ϢTSw[RMOͰf9e|Җ-в@B>&eV j=LBy pR/}TcZrH9ftHm$1e&!ї[NP"7Й mh= 2a$b I^.MA36k@ ™vy?lCk[r8>h+enkMo{G L%/Cח\oNsV Q^zV ޮv>35,"r?di| SkhQ[zluDVg"o[e` A_W'@Ņ u6(-0YҶ$\)κ}qAor7Dxwسh 2!`Y*4~tzp^LܗH'fWڪr1wFFi\n9 s}7B$W#XO6w~sw{9nn1El+s05n,H0= ti dFo.bҕ-#_o6b(?O>[iJytu[10]ug~O(FҐv(:.ةTj*"k̼'LJ݇  iؓcn`jmư%`.h^N^.*)X9=۫Oh 0\nL.Q: H{s+w8y7;(^?|#wT>%Edo?I&Q7M&S蘺20s YFW";Frplжh ʉoxL$ ;&s7w}gҿ]빋fJ .ie٠Mg"C1ONc{_T0mD Ʊ)#3ʔ3~vvܙڹEY|4R֌-&BvQ[j<6oȒ(RsB^I:ZF|?IqO% pqc}jUCTs_Rdk&ɻe(]>t;#6NrXvݓW6o9P*־_ p^"v%X j x?k2+zMD@}cms#KZ4VRMx2YCʯV h/t13%)q5Ѐmb0ȘL"&t\?d=}St@mR r##,'.4 flܠC:ŽտT(ӳhJg{iO.מ\j̆}䫑5QAS@ԥIJ{Ziq޵+{KOyl0k9UNnql , zZym:nϬay-46UTsse.\/cƅo=`6;f1s1$rCg !<7[1FHKfX"AGzVAD%eY?\L khMF~JCa~]CF(?:2EdV0.maQ,O" ?  5̬< +jg֜ R]F$Zj 57eC?rsnnq{oL'd:a:U zl>1%xWiXnbn;,jBE\C}Ʀ1nG規[& ̈"/ %)c-es-ٙt|5bV㨥yuBagzx9]zk5z鍴z󟞀@ôx hqo`AYB)FbWm殝u]noNI?{ 6DazJQ dDH?rw@OuKWzRd"ƴBfMds$dRnZ|3d̊iQwa{W?~/^:nZoQiLGoZܹmH/_soe<G7NwXnN0CfeGܤ^VP6)>[>_`a(3L[]g.'k29 fQke  s4gb FҨFm]9f7IݻEKR]k 2ϟ׏e:_nެw'oS*&NŊreE#5sg kCp4h#d ِC<^GV$NfL4Jófy?ZkN2'k` R4r@JFpdLY1e+BMl~":/=M*28r&Ht}iwWvD\Ħl#F2n=gefiFF歛Y*c'.efU%gEqa$ʉ}C,,K/S1b aBF0t""EH,3 h!-m)&&cK8Ddcp~F. o($@Z6C\©1BNGls.ؖ-[ȖlZ)- 3*cKA( gr*maљB)2Kf^ y92[՝%X:s铭s;svgMi3 1T [ʄDHH$РґH$Ȉb0H@ [.AZˑE"6 !JBbAQ"cȐW<)EILآ!gDK1a(%*SshNPK}4/˂K7T8cYց L4sIP؍~Mث ?XTBqìb~7wz~y:&61rZk13b(E86Adt('bX QmĆ*8MN a`HYE9b!DQDƠ1`kMJM9d?#9*R(b9x5Rf"֪53sQ[Z2= bKdYaYj1Zօe}43 EY(@!+c!tTzHx-5_\~zMx5y_z׋ ?+=_x9;o#^]]qg+z˟n{}tpS AVDdL JK2 "H#jJU§Ă[+,BiԀK$[-1 Ch$" CL!n+bKFqDVScWQ4x*jYઆ bɖPbp3pqb#T t&kRY &g(,0Eu?d,Oߞխ,O f:ʣk/1ӫ~_&v9kn-#B?==T@VP0%͇J ㄍD1a5$ F" E !I,Dh $LLd52F L$" c >L5ҽ$B Й cҒPQWWf62̬Wf"xf2۳5u" %KkE#EDJ@yȺN,{n TZaK%KHh-I,CQVr@ͪ X&`hD⹝"ٽW+zǬHm΅_KƳ;^}_ѹ~ycNY} gyW^;LPUk<6*$BO 1R#[a/`(4Ll 4|-ѫ-!#-XP-P ((J٪W˼V~tQB03+] m4K Y,FFVh f~ IDAT-3ld"P*`d1jK(KJRe G1#MHhBIꅭ7|÷g_󥻏9|{֋U[/VܿlMέìD{G#(*π(tm?$*sʆ&la^$4~^|«~so(ٕ'Z`D„AJ`LRa {I/5 Y?&, cɲʧV ib!$6 19q+J(͖k Q)(DR@IgSDA[T} "l$JAKmfi"&`9R2JX0e^B P))2zMޝ#2#n":Xs{s˟m/{| NŦHy"l{I|e~t͕Ϟ/}͟[ìYo3"yҚ#U]gͨܙ K݃A(*ׯlEn6AsBcKR(b2bXLE`P `T+#HrLPMa@"ZA dX`D%EDbS" kڋHHyNߋ+Vzu MMvE2C Rq[Ȭ"H̺Wp"iΪ qQG =ȊH9C.`8fCKGc,.>=X"Yq6f8t<%4Ξ>L㌐ 4Β(wFy"_~,4t񭳧W_^Z8K+ c<|{XǬH^n-6Vw(k 66׷"IJ"d;;f}O7孟W_/4N7A8?օ+ .o,$q懶9B4!]cYtL -XMa !$hd6F]/PHT2 A hA Xd$1ވ , bILLZ"BG$BU@"dBU+u-xd" :g#в'V2[ˢm`1 XX˥fdR *Y(+v`9+@.lW/Ξ:\YxzaK'֋7$[?o95wKKg_~d:|{яܿt^& ֋;Ƴ O8C&$Ξ:B^p{X _S}{'[o?hza碀vA>q @^EB4&fn (*H,!Z`d A $JhL9"bU bF@ǵ(,f#Jq*VhjL"" Mɑ88@ŕRв)/\ `rsav0GE1(%a,D%!P^$+oέ/X{^7´v<xn|i|Ls(?>|~!0oN%q<@Cd-$ʗ]w@f9X{gHjCL*Z@z)B1ı)(/one\TH3w@8TYDHk.P.eAAPkdp."&P r`P5Iͬ> 8gF16K^a}+q +4J}rlQ{kRs~@vїZ[HZafq%r`݋x07g{kpFCE~bT*?s,s,(5Ϲ׋0Hl5uEWGgkWFIuR ؉8Y˄w :+bf`ںLZa Z,i bf' 'ʛ"!!r 9ˠ(fEVyɹKаQNzDc (B;$Z~jsXIs5 zV/?}WþEl7 ҷ_}=zC"f&D֐eü^r~:?K?9Xw?|{̄(qT,EFƍO&b$ *VxvBe"uf rf10&ANA4#Ȃ$DDs]"ȌHLb NÓF8D j ΜQq@hl)@2VAIʲt8ҹ*i[ޙT b!RB@ YaRJ SV.F`+U89nrﰥln<^LI_)DZaI3d0fE]lY(+7gwYEвa;\0j}rb܅8*hg `ᴂ 2knH0F`@(U[X82M,~δ "YAlrV "i ) U޳d`fp+D9%*,j k%+l^aisF6+ ` (ED+s ㉑$q<}G"/m/WeM3p4rW+o^8kw}X(i-/l.>xzcr~G:Bxv=->ۗVYoÅ3{VZW߿fh-y8 T҂D_X@a% 58E#-3GhXP*Zcא(Zd$a`b)R;L H-)FAq%$RrL$c), ģy.`([(J %nJH% @Bjvr {3΋DD<(Lpg hx/XEiV 4mw)+zi^x~=Ѧc45:>JrG$`ܩ =ZҖFKȭ=E/.cLb3A .E@+5&BV'* DBBC'u'#)*AJ+e2W+K0"F'Wu!z`*C)% (9[+*!X: X[Q-* 3xoFytn_em<[O֑ܵG!<#<,ӍkZ8G"XOnrƍ e /A^:& z[xޡ[bKwtvoK>[6_)6$M6~;e Y$2Pd,m…3qE1Rl AB096##$f"dkh^KUsu/a3Xm7]̂A -)pɊdU(JBj B!%L 8)+FPr,` [Ltҳ * ;8*{%e<Ef$4_'3pA9`-0! VA"P  !!Fq2 "6#'[L5 XZ6UEyj'5!D6s*"bɑ@eYf-  E) VsV5 @"%h VX;N 1a!lQ̔WCm%bn !V&@U˿sL"Iꅟ||l}<eEڅ癐T;o?ۺ6V/lX{d;o׿>9A#v-3yVXo潭t4<h{ 21 w[{}FeDB)`@ 6FxL B "BKC(D2_ j7nJh`يe_Q1e1Z.V +9E3n"D~CDnk .c ~Vuȸڹs^̥GY; }cthH)lפּ{ׯ>Llʓ/zG0rɵ3(Gwj9ϗ*'?-oI-Z;>@k2lVWD B YYbLwG!"DȪ>;A0E `B\TZ.U&Wn4c~|Nr=^aL(C̈Qq .}7A<B3{f-?֯<}N?:?fmڕ' ~G00 ~>tAװQO*,U(<!Blbb5MrG%+l f! 7")< D# +b X(֨K}(; %ۄ?9[uFie?b__j.=ò.8._ Ƿ>[ü{_^\:{sޭ/] z1~{o~|=]C_p柟 #ɔ,(BĹv1t\2iW`j 򩣎{ʕ Qr`S;D!Bwqd$RXg 3LLe "r`h-[g("m3eґG(phwC`sگP'hZK@]jr7XbjP*pX`]IYsz/`CE YVzse:"|I\61ZA!t2C&‡UM 6zZUj@3%~i6iV1vvӭ q8Z[FЅ;\/_-۹0M28:|[0WG%{Y5pZDyjE t2c ʮn( qϬ2 q7j} qbe|;q=ACgl]䉔9\0u_#}zGN4V?? q׿W/lޑaG}gM3WkiIl݀*w0VUV*!+!tdq%n Bs-BHR atjC0H8g'NjCTJj)VDϐ([[w6#"Be /,v@r ݃p.vq!Qju y,O}kso4xnא1d6%=Xֽ'tkwo]tn0sqvi3jn5}cvQ:Gz,N$(VUVgT 1=$b< /} |OcX0Wi=  .#9hT`lp#i$Ԥ*H`Q4 IDAT}PѭvLVӊ*.bV 1->qf4R$\W= ,Km J4SU~،nV0L(u,*Fj0Q,}ãuzȔϣ \a,زtCWnˋIKpW|}s;n2  ?~hi[_g$֟EWvyk߭,.?i\L]ͱ 1P8'i%dj}~G|; FPH43Q= iCuB&ڕ܀&, x0W淍) `[MBuf۳YQa:!vuzGhSTx$n"dA65\{CvY'Ѻ7z"*UZMW"lZaԀCA"{WTJXZYhJAwG%RTVD8py\+3?_sƭZˇm2iXLJ@<;U?JXqa\i4t_uPh?^B緓r}W|i_zsaH76vT']^kRer0Ɉ)N^(90T"PU!XMj=W JL@N,+U)XVR;lg9䭞[HPG{铭S#h'wdnb[`Cz˚{FCA0kDЮ(h`@hsfk]AIA'zo^J#8kxD [˵_QB[dDs#<~1T;YrΫTionWt58WۧT`ޣN߀//͵A;Z#WDKx9;o_~÷VsGùi^=X< 7`q@z||HQtP7 ylmҨ,]f'RL*Sui˵ֿK9Zm?Bvo2=jNM,q48 &dy:p89Q3-f?|H> bFv|*8bIQ꧈ŦMZ&<#ݫn0ʘm7pPcp2Uwߝ7E?L P﫽QZpG{EQpvoF|ݕ{ojpf߻fQ!\C\%)h,lg B@!>Yn@$0אc^-> zŵf;q3LZ$iLd8/oYFg#/X(Q,zaeU&'/:k7|bK_Xq|X +zd,8Ƴȼ{K&|m-R) Y섁8t@]d 94 |{7QwQ-O0ew,^W[eңHu<[_@s~-&ba(bNaB/nE?(Zm6eU`І1|L{d+2"ӣ~:lz$,,O[W:.4/zATUZ+M*NWBlt@^EgwdY&aR=/I8u-ps0 ?N`hp2#mM"rPO/݋={ =Xy1Ӝ~d oo^.럾g@{QS7<`4CJJV|i}I) a֎E𡳛Y5 Pܪs;!nkbOaaw{<ȴeӛ̟o4aC>^ɨwҁd7*xWos1{ Knt[GsLTͳ,GuǞQ\%,_c>TtG[qT@Qyh~y矿9:==4Fhei`h0˪/O/ ;KO6Xl\Yk4|w}޳P6z½E;>K/qf7­Fs y#-v5F#ַ()^Þؔ}٥zh&5:nxtƏ{m&x܎eڝ D쵉4@ٽdxna.85FX߱?U&p29xJn}iL(v5W~F5}XG[mf'|ПOwoPk~ҩ㹼Ej B=0(m^\8zI߻y򕫰\p{Q\{\Yx>X@XE ';MGQX4bϬ}zj\8%*=8=XfIDڮTal'>'N]yP0zO:u<֝3ޤq.C=#4'yu8'QQ! @߮}o}[MEj@ ^?XFvcaAV s,rtsWKTꡚIA\{PM6 J $x5qk|{~֞E`yib q{0M2Ba&B(*x< 0]~&2 4F^!F(6EUf+8_>Iݑo#rl h EF>@n΋U\~4ZLM`0)f]/Nœȗj~D?|emS_Y}&%>ШVW8w잶yo|i-ӊrvٝ%=7 G]'"";9 ހO Y"ҁaj šA#a<98=>H1t-顬aYbP_9XDu"2M^:d& ۳}s{ ^W󃬟&;|0B{˥6 I-܊i&*JK.'9ƪ}A@A^f=#=boT롡ss;K5aK|| |D4 1x Wf9L0>u-ӀU~_Kۀ p@C< 5aҹݹt0^2D_ u~{/_rw`}{4|:%|cA/lطh 2֘4B 2V?oUMaRazIⅦ ~!і Kak 2qElvu~Jc$UVl%e4C$c^8AA*mk]:ӼNk$A(_ :'5*AĨ U/_}#φYP~_5!NWř$G tS[ia1i]:?K;4ϗ(xpsͥq&HEEgZ#Jg輎5vGdlk@ƋNv[_w cu85VpgUo>'4:5k/_\@x!x:Q+~z|8+^I )/wwO[GmH|id3Lc[|1F…,#fRUv5Zn-9:WT{W(4'IN>tݴA H)ye!4x39XRnC?HդJF탢)f3 -|/r|PPHKGáJsYT,O"VVBì7^>O 5WK!df*ʤ V_L6"TQHwliZ/>WНcQٚk{*=xvUӻB rSXd}:G.O͏:E!΅TQx[yɭπ(LdbDŽ`†Dm7h939doyƙ;gRB>@M;;?1=Uw Ne"50a |Gޅ=m!/yghiBG\.?#liݿvtc(0F=7,q1R O5 X6NG\[>YM?⶧P9:l~PY>¶|lݣow30 5_G4e= ^ :)ݛxrH |nT3e<sv=~F t+bƔt@44o`? 4@_4Q<Tp`^Tgx_3~^GjR"(`*UFRnݔ&T^"ƢXkthFLL1 ݃ŇV~}2t-1\",;^Xz1;+/p&mQt^fxvʽG<.oXk|ӗV;,տKZ'@-e Bjޠ_rw3fU׍Ta >US=}N/ c.nd&/FnG>J믿5EN =G2N Q*jT7 '!Wԡ3(]K!וw)SK{w\_mLlRh"Co4lx:!WzsˆaAİpWl-*ߔ#~[s13 :C)_|wܮ Å?=Ƿl<=ܯz ^tǷ>DTDR$PQŴ`8HGj xH۵ەtv͟Jԓ嫾ĮUq9^Uo$ˊt<{!V`1L=\ pcnD YPW+k{o%+Rhj/6j/m>q9+z gw]󁘊F>1U!uih`D4rB՛?IcX}mNA6~^vP#h<[<W-v5y훵{fmRtr s{s˟m/}וW_16oN)57! דgw}^QROJoDٚ,ϵbzp1|7wߟX@D2g{~>>F/ bSIOl)M,O_,e47cԴ5*z;IQ5ɱz5-1}7>Gy k 0 f&JM3'SgmZss3KaaӨ|H- c3DrR"1M(/-Q֘ٽWqq8ޖ~w5k 66 9vT@u!O8*{頟w&Aב tNZ^Щ4^U`뿨b5e._|͵\Jk's;Igy*B`8'~pfk9Ri# v?@羅=~QG'1 q GʣGK9Ap(b^kwyϣ8gkwѠ֋ >ql5|bd80+w݆;x,tO`t:% l},wjdn"Ķo'֌ , /S.18dm?#@>'7Z'_}\VUa{ҹݛcX_i8iET "s@cH3V]Fy%rmO:pJ~2c2JM6;XZwv5|k?ƱGR e{N6'@wȪ-]2{ה˜i^&̴s4_Q<Ȧ +87)ړQG抈#tF-B/F2O#82'u*qi~x}]K{? +x›=\|Ƹ4K_<ַW\) !$i H@ǫHy-'2Z6C]CTofuJHF;S!VKOk"Yc7ZoJpixǷZŹ?W5>1^ȳ&/8;qGĶؑԶo d C/^.6wM]8-e5:Xl>ׯ>~:ρ?w}s@Tu4Wl|wK;Ԟy\_k#. ;#]?WZOŒvIDAT0:w@_dTxpX$LG[ ٫e4'LMWLCo,49 <~3v}mC9Ǭ^Q#y=::?tW>pkA+n^3 #eV}kD;joe06vTOQpýfm+|q<;I{G4'E9"0\]?O5j_'5!IռWC| l Elx,"kN 9?.{i&jt!;&U1 ;^y/?[YV<t3> paf@3 5^*Ǘaj$z#=_e错4fMm!Ɩ2i$$#;4 ٫UM;MC-S SJd0WÁhZttvO8x; 'G5{ ~TR|8#Z\9i=oi{FhUU~FzݪV2J`AY'+L g X}AY珕gfѴbP~SBG`# cOȕÇwAy}z^&I}[-`!#AGW)@@]8(?3|":J:"&G$F!Q 8?D_ም.T" y(uxX3~J4Ƴ~4c?"p~!@cX]H*$¤)#oalr 6 Mp>gaDDdya@cٳjóa'?g6V/]{W_T9L"~;}}bӖAa:n,\D>t! s۵]g>~@OcOֹB齪$5xguZwX<}}@iLX cю^?d&1zOXL{S=sO;y,t ErTyԏtlZHO S(ès!y6em8"t}!Oz2d|$fߣƍE@1D…kEVVAȓ,&9βtf; 8j IR'$٭IpW{b&-w|L eФ{՛Ej_W : qk| "Q'“|wH_QGeǰ5a5ƄvPG庭lX\w3J)wZZu7-L6 f9O̼|A֮D$pFg 2qsB2B&lן5=ᗟSN.w{Ǧ IDIaW c3%U]RMȯvbD(۱-5*ugPOe >}"=[0ănŬ1z6ģi,kAf䤥3ޏm[0]AGqw84* QWѣ3w?( !40Ș\:qw/yt8k,UΘukejQ6{zffWȺeuIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image05.png000077500000000000000000000421431423156660700227660ustar00rootroot00000000000000PNG  IHDR?1bKGD pHYs.#.#x?vtIME%E IDATx}y\U9;N/Y:ݝ;K/ {Qك*CQ@¸2*QGGqM5@T dOӝ=?]}KUu;=շ^zپgxOІ [GnYd t1|q}-@C^A0t>BHΧO D\{ᨵ`΍5>'0й P? B$wE"B|eAi0γNwyHq͸n/~ɒ%s+72~ o_)^W! o{eAHPxf F@% @oIBRD$@`%A ~ DgPǀ>Ga{u@ui*?m8e@bU/ YB_j+ ˁvOϫ>6_[_. ţB]"TH<OWqQ$ GNt7ʈ,k6~F(& ꃤA`b~CKeysw|;Qxy(Ϫ_.pz,+Q!nEr ] &xj &@-4_~ؚ]W"jq[@$4[G t^!~vGwzI~YLQpw[?YнVNׇCa͢~}=꾆VjmBԉ)uWbcC$%@qR:`l._SNpXnl.' k!P6 Polg`5{;;J a@W/[[6:u້O, Z@XP\n]y 5 _-' 0lZ}wq@sWMkCMCo }*,kG 0ϸ(kZbZy @<L;4pt`Y1.%| ! XFru%Dh]bpp$aA;zAW0(P'Rx(qufSв|}~R .rhcde573S0ͤdn^y >8|MZxq9e<~Nm^O3~F)iҥng4dO>\:q86n` Py1hQ : MtHٳgo'=eh`4*~XH`&ނ8'՜^DAdW<"BRȡX /h^ GI.P6LAuE*0pca|h!ewu yVTs&[֫_T#Y0 \?3Wɋt&Hit) buARt:+ ݦٳgh߮=a` #cM9ny $(j;Ef(NqxW5Dr h֝!4U!_U |Bk SUlC+azΟݔ3n()xEεD[W:>X?tl~jPaMB*˛yAz6μ3|$)~L L$]P6!}ӓ}0XәIVԁjn9\Rt>UEjSŨH b{.XΎU{'&Ӫ0Uѕ,~"5Dd`ILfzBMFLDKQWMĂvxpK>16IS,Հ:]|h?o^*~f f\VT9>*+3S/#?d/\zõ h5/$B<%2oanD)#E~ԗ0 qW -"i`g$pFva/# F+~ҩaLDC95Ldd ٻ^ഏNH̙/e9D"~4"YUyc~U,*mջh HS eeɍW{A]zlV҈`I%7r}%K"R9CZ]/aZsS"]F=P6o^pLAIJI3)Ao 0#(2Y#$q$XaM>t߬r#+@pFze?~[fp.8y+҉u"Ctn#:!۰K8anU׷v?7^zAIW)藡hL4Hn{W ̨}*CO( "=vʰþM,~g#ĺh<@kU,3zƤ1P)0v)W GPz@ӌ!M6.q曞w~`Ry&oIFU(1`^d4L(P0 M6TH;^:)*~0K@4`|lPUt9cb)RT[c7opC]S0syj6@?@)@ҕ2 b5Bk2p} 2Frgw~ZAB#Bst"Qn) =i}Ȉ)]xgH`ZU7Bbr<GwMCxya!1䚹\) ewl%ǧRV;3ߴrG!B)\ߐʰY(݇v.,#y0UL-:s~}YZICUMF70#Ֆ^;ܦ ̒1jeSR 6pL44NdhW<t,1{НcyƬuϽw_Y6>2ڎ7x$ 0{3nuF1w)ST>Ć$oWj;3w468m0Lv:+A /("W?}6(\#/s?[kRDPo3ʶgҊ ~d{yMY;s j-]m)cdT%Bf01ړ43?>6yzے"!-&f}K2'ݶ~OvHZAj ΦOlM(\e-{EVLaVJdB E6 xx~>>};VIp|sV/k;SfOz u&>>HD70 ʛici_t=XZzsCF×yy7;Gp@) 8NkXW A @"QebKZF me;B|bIGnvBW_vG3=Ow?}yldӎ$7 #Lg"q:i5 v ϭIf8E[O T:NN#;bcKnοZiյ{pX !^C†DIش|w-P66Vޜb pMU;ct=|cr;O?x\͇b`2?g"?fAC8'@lnJx=-!J°ÕxVU#j a "ȅlZ(1 y f7)snqTظWSBx^)'`5BnW?Qƞp{5ܺXC1>*'9l誎ذ)k3 ,b+1mTNȼO]P6vjͅ n/B,e5HDSqINb`ܺ XSř B 02 /9bP6v^DŽ6,qN"DPA j#c_+CZ/Ѽ{ۊeL90A&r_udq?2S$3" 4Fct??g~I^5NPŚ~)TW f<~ќ9s (V{D:BLy{0BDZNB75"Vړ25CZNbLҌ햌Bϭʀh,7᳍㣍,=N Ēv>sFV5Lrzrg# hشTH9ܩ0tn5pDd tHWHpgp'A#ʎ=iS`街^XY`2P*ƌx ISnjF(9.$ sݘ0.tb~5v N2$DȄGݳVptO$益4C>|owJuI_s*˅ +w'41 ٴ֕gcD$s1 +Uê_^'kޚ]>!Yb3-x)a2vE_ U+ \*Vˇz:&E A )l (6iAwj>)h޹yؚ]\+~b T5VWx2` xA@  ?B͙@DR!?Vk^󆶚nZ\;+ϿhU ΊAh/i֫.t1O=-7];Kƃ?k_0^~|{XlQoJEDPEbUC$T҇d P[m3궍 QL:Wj>7h`e6W*(#(alnt,v? n֔ԀyJ5c;`@ݔ{e^x p-Ph2~q@t-Rˎ:ǹեGpy bG޾. kSo=vTjH aMY?~1dTbc-szr> %Śp%'1 sYyvOlJjb̃ B~OG 8B e\u%LE /ai %T}ϔo8[#k/$ȧWe@PO¥)RsY?5OӉ)p9<g҃C )ߗ%Ge&Xf #)W@Ph sx?d@c Y7qEs, SLAڳ8's={HpL+{^* tW xSnv8x~;ysr'X0*`aT@2EDﴥ+D!&b2;O'-da"N~IyvU6j37xq#2.j g_)_MG!,2)_=lj[4}䕞 IDAT#{=8:1Q 0])b˫qʻo՛$)~~^Y)tjKG=˽8V,6~ZZZV9ֱ}p |5W5 CH 1*!0iV] @PUe/5w`cZ$WnȻF_8xZ)8Re^;"0JÀ!Y7 ]pQGTT {WSĊ [ qln?=s)OnbEĊc6y8 –}5͗ԝTqL /^z~K/'(-f0xpB /ǹ< /^NDϙPP OH[ 'O^g 'Op"lS2= d@Oq@/iHAHxW?8~"dq_#5+x:)8HQNG9^3:" __[c\|BΔ:V]QdSLCL+O̻ fCl/^N6j/ ]I?%qFW秂GaJ]uADY}Vs$_#EgKOe5#o":%B.O r˾yJZ!(Xɉbk3Ip_&\pWS߲}[WxE8߫$ R+YS__};22t)0?j pw(I|HP"d+b੗@guχs>oAWLAɂ7eίw]S[hg%B^|~o8¾G_^xkKVJp%|D(r۽xw;]ƾ,6=Sf)S;W`TM8;}SۿJSP6PvMrI~|oV瘡4T_tgمOSߑs|^I&+y7dۧхD?<+N{Ĉf5{S):]=P%mS]_6-:nz~ϝ'JR j/ۓUsrM^şd ^އ{z JHL[/m3|g gtaOzנwxS v?oLk1b0@I뤒I.þiMR;;:=7Ħ?m ҖϽejoJz"{kmڇZ_|!O0SU{' ۙ{ b/_2fbŹ=][?Ǿ{1BTdZwN(qkq?RsK[NC$c xpӊbδ_nط4F كC?Hx8h޷?fg\`8g_cq[cAJAKT&}MZ dBU}CߖM^QcoP\} {Wpŗ5 Q}"Q"{B1 G#Hl]'|I_c6>lc) D-#Ju?J=e0z@e_,WbULA#^(ɷ7Pn">@r&%f+1`л/سv£FwW|܁?(ROj&E*Hg+ b) ÄN2 rɍxeڨw܈{0l'~ɇ=z&;o}Pn)xe#c4^%O@AqJX pYsê"/ySt /G%uuୟ(uu]SU]6@ĴCtyLe%D@ FOTeӚo* )cKxJňu Pq8!+suSe0@(r*XD,k0x7CV^Q3u~o! V1U@pEfˀBd\8b^- E" |T԰be^zYo=x$KV΁A_ ,MA*LD0n~SP/TW~DVrf 0ox4E@.mCM??[b?|֟l{#Lc)t]Q夓)T' D?KծY]dߏB (B4첇;~ZoY(?X1L[S  "iPq~:/K4V;rIEެ J ZoXd|=[7:2Kcmv?ρ{3a-/tttXjI4(*Աi."B!r=9oH0mM\^NR &뇜uN~x~X Do-y)~B c+mK;B}LqzRMA.,)PDB!C2bj|3&  BY}C9#lX7FC7y>b(˻5hhc敖GOg@J_SFP>fc,DPdlN544~{|U?Iu 3TPbyyW@$Jf 9lSW͝Sc}B7GG床_!A% ] Rsr6^+$"~M'SL76 +K/[w_ӱ|f6-RiRJPX)NۄX`RXģ^/FYi°5Yjd@IJa#?tQƤ뮫9JϦIi6sV$?kv*?> A>X``SoFWh_'pZYwtH ڕ7 2Нd {$" x1l {>zn x ޼i֍dKZfa`PB XqdS^Ӯ_* ?莿@ Nٰ !7 8uZgs^z@}(Snvh_o22àܲ9⚃@ 8VV6nh]G%%{2rV G߃!Qvn~GJמ}_ L@b>bN?7׎#  Q{0rLĜ}$00RGHhmڹvJOW,='`ma_nr7\7$[}?}m+V.Ik3Stu~m ˕/!"0)|!mW3 v AݧG"5UT|ѕ `)BBכ; MZ4_+d`_!E K!"qRߦ]|`OCc zDn G&k! }S@lt Bhh9njXcAqvƗAcb9aӼ DБc$՛e%ScqUKӤO{ke{|CϾӿnk߹c)An#Acܥ & V.] Zbm_&uÝDofs;Ck9ӳȭT;imW˖-kzpzs_:oO C[=;,:H:\3e-opoQ bO.k; ˷ JJ_IphZ#v\;,}+L>uqEs*[\dB3-31 _ұ1f:q @xLJ~_mw $2Lַ]roD0mL٠* 4sW =x *Z"[ Uƒ;ݾG@ئwzj…vtAv> L2=k^E5pb)w_]|ɋ.JniJ48QAFo,uv:4IԺ؁K;I CgvP`h<#( [d~1N;ٜw>9Y9r_,O<ĂU #s-'MnΩ9ukYTW0~5MeN ܤx5[1mTmׇY=rb08(3 ˑAw<Ł;CB.̴I-eO7o^R1,d_A p@*<+k|ʁCXHp(65\Uz_W Hdp{i D=)8(3Q$ !Gu/;wndXAȢ ^^?X*gu@|6oL-vn()Z>mnxlCs`@||YP0/4vDm.Ž3w&M9t{:+a9gX{ W>uMiLsk_E+@< W ژDHڡuBT43#OxS z+ K=2yԜo\[,Yrg&5jgƽ_$hůkbI#Ԓ^!=inJة {T,7qIDATRmp3 ED"X'4B% Ѯ g7h!`AXy.nӿ3gN2W}@y4jRo<3AN{␝ήV`xn%|B$QawV7W>1M0ÅNQOȔCϞtٶc1arLEd.* H[fLD[nrU#xŊv ئ{qFBn~u><5˪jPvˎ4@Zt ) PzS σlLRm-0trߺ+]dI 7qe<n| m<%gUGɢ=?l6Vhdu9I6`LO\F Wų?_>IJs5/e`,!HuSFjc,^)OBUgZLat @t^v(WH s61p^r*p3SqP`0m9.طTzͷO-4\ēt"wܱvD(°koXM[d"?M t)`\+aZ Zd_zwVvѶ7)8(O5-0D&f{xv1T$}mfUH빽*q`U-Nt@:6"Uai7'Pbo/d \?v\y{Ug&^mwŦ/:\ 5M𐌕USHbde[- ۅV! :EGX(ڋ O[/1`,vRl TX4})0bh X~T!?ѡNgB{a8z~IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image06.png000077500000000000000000000004661423156660700227710ustar00rootroot00000000000000PNG  IHDRf:%PLTEA4bKGDH pHYs  tIME9 IDATh aK HY=#1@$"6[s}'@Y`͛>_ju%@N 88-$@Ԩm[$ϋ6_5j9tz%\@Ck{p~8:'Ga[itxJՉ<IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image07.png000077500000000000000000000136101423156660700227650ustar00rootroot00000000000000PNG  IHDRyOIDATx]MrK-K_/>Px##$. ʇ#;1՘FF"JM<9xs<9ɿ)@/h eo,1OLlØꠇi1Ο~MDti Llpw7zԋS7~aߎ^̘'`wgn/^L3 ûyy'ӫ}72_˜o|0\˜9Or%f3| ڼEvIZϳ<|[ׂ1a9Xf [> o\yF_w_>}nc=L7o 4(+?_Q߾ǽc1wji-SR|L \/~v?806BL}}Tl~Xp߯/*1_??w81?wXxËU䷇t+?^I1 w q|ޢ.)%0@qN_$_w2:1FqF7hO:G(rWL q U)z~ [_~ z5~N )zd}}Ux号 P{ك1:!%} Ӊ Zc6lR)B2e&|Y0?@[CG*WBXX@_ i^oixRi' ʺS#f Hmzu ?_ W^kRPҶccP\@m:f*mC׼VfO` 6^;̸u*m;sw0\M5{9/9^aǨ@z5 Wڷ^[x=Ho v T R̀Y taUDt5䚻8Atԯ:4aKݜ9B Z]T'qrR\>$np78zRiM 4X-a* AJO.GJ1~f5pnWoe>UGjԱԕ;~1#*f e ]WL6r ' g\Gu\TZey Z9'G5a O*mYMtՄS . _EpQim@&6N'8+&<|u%l5)1`Un8ZSh; DVuN0zkObG= #_ǝjƌi@fLe+'bLpر@s2Yvr_s#Rs9WE} X/M: cÁ"y4W뿆0W0ukwb>\\| 4"vN0ؖ2;B_ǝHmt>B\L aj3ӧ}h?'abu8_-E}J{Ȩwd.3q3C>pxGP^8 a@fat\v)_g}"_\ޑL E}2 D̯1{G*gFwYh̩(ۓ'H:C>~ WVq@:C> <5vxeI2 5tfpm:K}")7wk>_Rr2u!ůLp\)lB)_gUnHzf,}rrH|L ^d.f|+VP;8A>R+Y՗Mr0 em3|,g G#ԇ'TQ19K8.a,8AIV}Y'%^e[>Ș@'A>!?,EwXZ՗ D; yse1|D;ϐ'XQ?Y:(?s Vq?RIOpYWU=R+դ ?WPqWK\S~+ˍ0ʾ*r+?'ܨ;"9MM$ʲ7<5n.ד|?IemUA }ܜc??)eA.d߹3sn.ԓ7ʕe:\ξ*2jjyI,;0;*2jYɊܢ︊CH;(Ϭ,(g5uYr+˱]Jl{V9AUP/zHB`.ZUy8"n1Usi-:˷>;9c0җSGUQsY-B“2^NPQ>b.Z>gGnQ>ŭkUngP`\5"!YZ 'n*}5l7\}#,[9AP NZ>W@Y fU8V6VЇJyər':$GSl 7Q1Jl'pEVwH`˙9A 5\ygbDFm;ҧihJ ,Y*6swwx^%wGs>ݘ'.Yňw .YwīO R9K£ns.I@+) a5\$>ʻy\$wT~ܜsUH:* NqsH;/ e}G@`;n!7c:Ӿ# wsC񐣾J[ZwV8A!JKV}L̋s1D}]AZ4ǎfNP7pCVO:D}]Ecwfqs!דP_GawD]}7c0;"Hرp iI;b$d|-OoABj\ nӞ Z;"gk @ܵ1' =AҾ#gIs],bVO&f'H"QϒNuO"dB6O=A"^,iTtz26[p" "%کN`d[Lqb41YRC;"@+ Q=I 5гD;=U8דI "%6A"dl{,gIG (֓<)sAϒNug*L}̝+0Ov!ƶ#̹P;>hDŽ"ƍKrkGwH-cz 8wvޕC`; 1۹h]IApxzN.3 ()E@Ev8=s'yޑ"FAӷ R :wٕaODN&H7]Vή$';#ĝǠ}˜'%֎&K?rrn#Oc&y8lUn(JϡOy$YwBs P/bDŽCV0|=FHҋ1ӛftYp?z@;{L>mFg`Ep}zی# _h/bǤOWʌ#42rw552; h/bzL>ftAu!b1=F`ig}:Ҙ潤`7'80"gi@iXtrs PAf&mO`}v{Ĺ*vLH|°I2TĹcŝׯ`B}հ} ܖ\mz1! Dۢ*ЪcBc>ᠶaDrO'קհWO0ܤfUkz{0! eh5lg=s+)Ͷin`̹;8fsׂ OcTA Q3`LZcH0GP wG~ce !4}CTdI=&^sOwxY'r*2)\;Ҋ,AL=&^rOwxY@4}^:xL'υg;L+@\Ws3wR:/ y!J~;x9'gqyxs)", DsLE0GQCEɆpıyHuv87O)pX~g[kwZAoֱ[[w`^wu _݁U_ʟ;3xۻϝg ]9݁q3k>w;0"s+mu]wÎs\}]}O/'躗pm`Ὄo#8A^#}۠R*1Xr"84Dv̸ǜ`rw^W <9xs%^7=IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image08.png000077500000000000000000001151641423156660700227750ustar00rootroot00000000000000PNG  IHDRkXTPLTE !*/)&3)',277:19BA43;74C6+9CMGDECEP^?1OE@SE:?LVbG1ER_]NCeM<`MJQU\]SNJYfkR;ZVUWWZdUK`UWoYF_]aPbs[`bc^]k\Se_Xf]b^`hj]Xh]^n]Nw\EcaeXeqz^AWi{hfjkfesdZudUfgppdepe`xdPmejdhvZrllunlp`pyk_ep|tkqhpwrmlwklhoko}zlhvoiopyrptlTmjmQnbbyss|uswo\ptxsrhw}qrlwow~renvswvwq}iw{u}z{pv}xkzavy{~{g}oz^{}q|f{va~ymmii~y~uojpjkpqg}x~yvnuȝu¡ʣĪɨêȩ«¨˦α̱ųʲ̱͸Ӭطйոո˻žҽߵø¬Ŵӽɹʩʯ̶̿ݹ]bKGDH pHYs  tIME |hG IDATx| \.<}i&ݘXu=fxTLV8[Ť :#:%239耐0KW|bD(hP4eAum< %sݟ<G|g]89>wߏ {?g/~?;vp38ru3>@}c9pD> ?>BBn_g?/5siz4? }a|*h#26r׮۷o۶m H ]D_2//>b$V(@lMC p@_T|/{{_)T<]._׿b|`|AIw"e5''XfvSYjH?Tt2'u<_ !-WYzx]LLq ;ƿ(Mw?0`LN_b%貲&>Q1B1$/>:X =P?~qs>|eO4m<)g|_II!qj' %Ёbޫ7 ٤<~gSRHToOC? ҟ#=R~E>*(࣬I€Ja}/i4 sM T8D?MLy?/y^{.,QЄ,YW<*4" O~qK]W _?(>?yPP~=* 'S21NfJR23)s J~ghO/k '`sqh•WVn/n]{I1=MEO@oy}M>td*jI@ ’?uVU]PuAFkWPD=9,S+K5{=q?u5s'O@4A#$<"7?')"BXDHewGSmE)$mnh7tSz-@@k5$`H'瞠NQ+ԙЍGϜO44ӟt)1.3㈰vr'c\xrH(+%4v4mjLjɂzR ]CXBRt{^- M H㠉e܆FՓ'癎\vG9;\\.K!r;ܠBNw'ohh\a_gGums[[kOcNA?:WM0BS-k$``,7%iR#~h2PƅxLp]vOn55d"Ma ((myV(<]mGPe5!a^F!!gct8h0Mfj5@h{vLNA$̢Oʵܝn߾= X0M۷mn $%޾]Q`2Fnz???_@e`tt5ðBFQX`*XI bfrd:۠B`ed(Ŷm@wWm~~۷Cz0?j}Fz 6 FUULN@}``Dï F0::22p,)zF>MաL뒬E\X(ЇD'( ij6(?g)t/8C=Л AMAoo5 Ԁv*/DYư@XB @ǿ=`G:`->A2FZt)X$[ @ iND/7vŞA><5+|}}B}|}^_ Č*nmM7zM~ey";=n4 \:=4<,'/ A@oNza!qZa&A*@8 oJp4>>9ǀak=E/4@A) :bB0tjV0,̑[U*fU"o:Pp0Z$fQOuޚ{~`jFLJ*m}^'^1@i$ze@kE_pK_'}!rAīCX`z&nWΩ, *1G#xC}~ x/ Y @'ִn@ 4 Jn lo#F 7*@ |i^/"˩ozY M0bje=nf[1QICR,x3̨(r n X)C,u". "$ |ׯNZPE[ F}!/ZZ 'Θ1y/ZD[ V3,El!*ZV#8^Q4S%Z*.).S + n %44~jsAAl@ rF@=ܨ@K__f̘_O~r ~ HF F~>/, dńESPAaW*=˿uޯ.z+>F ?oPqArK]H[[ ީj $Tp#,WfTc@_؃)\o?|b6xYI?  9YV),ZE'ȟ]-^w~vP(@S? N{~ ~'kJKj'Uj[>/\Q HN}+-?pge4@"ag( E _^>Çv']a>mIaTP!@okZ$x`޵<̒Y o"xbOINܠ/"GT ?_*EgK뫪{{znO؀脚[ⓐcd&-"XolB /u$oK3|ժUx1]4Q)T: zSiI=MjRUnY."t)ӝ`q EH/Dkp /_oP{V*z{ޮrI$,M3-dfAy6R0 @ȯ(pgx=X B7` a}`їk,KM&;&Z:TV!3SK/qX;D KR!3z_&i[-z-j)3XUp5-I*Fzʪ zB@W`ٌ? saT$ BNg*g͘)8.V#"##lN39Y23OI7@T K/ R5k  +RQV/HP]Wb@F틹{(*R`_U`q}S ldATKo0s(+aP^[D@G.)RA%n u)6 h})K xx/_+i{7x/gGd6GXX~fM B`Abّ LOG3HBX)_Db" 2 LN)#M˥vK'?xx/W<@ۿ7W?@sooECo/'xW8Ĉ` {֛-֡1]\Z@p W2m0d0XO0.}+&qf$Y>,PF\\ ,[&3oZe C?zX%&&_'qXP"2h CL5[I Yu`~p#L/  0Q >a .斖2a# 轞j߼bE* ;vlޱ#2%9ZAS׵pŊe$A0&  zAz ]|zp%r3pхsoOVfqWV6UPQ/L|A0룪Ks -iڣjQe$ >`;4`fu ,\b٪U@GѐeJzh K| LzݥL2l:t9>UA,~,]XhX@Pq.P__. (sI>+n-ۀe6ؼ!v-gB*ͭb Ak=} nXud~M?)S`aټ!">2Y]EK6t@ ,SS̐]RRR!%' ͵AUFU[U? y(ܽVZH,[b  6@~sa;r Un"Xr)}Q)/Ug+IIPX Nl`SUw"e w+(5X:o&֖f6O[_ՐV*  ~>+~`+. !6\7:ZSsڿ3:hF<H́+G``iN*@豽U 1B`P‘L-nM 2FG /"&Uc |S]_J.Rtؾ qo]?i… 6@6@~l/y|l*g8^Mjo%x.\[ת5:@p ?ImA *hHaqP(9X̲ZeOt0:l.{_)@#S)BCK+@so=f>xTiJ?ҁ} 60E\g~忼rd7 1]`?x ]A &HO=_ΧBhX>#De7[VjSDU% dRe=f_LNY0PH &Ť([~kdI@h*Pߜ>Fxj+xֱs{6,S;Vl;?OP22lQ3828r;,`~~͠) @..@z PjIXb1Ե!¶̹z՚W72QGS .I9lBm' z{{oݺHۿ?⭫~!b6oٌFͦǥ]FˠʬՃCGL48Qj 3I(Dg7YEY95J~^'DqDIB 5r.DN+T- IDAT_j[ ^_"‡Ld(`FZFftn!;\"NmBsZF`;},37g1tќP3OjcW!&,f.Y,j%@ t. (FTv QH3; &9hߌOM S ULo̽{wN_!KсKVv`d0GiJmx+$(z/Dѳi*lO0\[JjD@yh?t5ț;lJi?-VFP?wsz504A܃gHO﹤ P11vܜU/ YTP ?$A$R\e(@b@U*$t"zh#*2„P6MAp"L@W'  Hd^q֝=]s; t'İv&=eXM}.[;هf0i+stm|+jJgGh 4 [$@&̌˜ؾ >lՇo8 N ݽ7Ѹo_p>~>|xߗ OKmT% o%8޳5g :F1Rkq'p:vKNY ;d&~p6A xP}pHt(11PGa"#%b$hGFܨʦ\md60 NDxf߾{fM'N^P\[]]9ӗ\X薶̜d<x&PB@7+r*f R9mbpo g6ЈXbiy}Y86gwlWS 36#)#w!>`2 ls{sS]ee( 0\̬S9uZH.C6dFV0[< sa"9#hbݟ[;.Lp A:61qL 姰~"?38ӟP\zA{ӛ[:*O%7*q}SSř0%K<9j06lIzI\K\B!tg+{b~+=6 BEȝDisCm(Gn|||X~:U=?'xNx‚Z "b:G AcNfqVΙS9s VfeUKu! 00 U@ɛ%e55ȗ@/b† R\ԋm@:ss0Uc?7%~w?ǹtz4 ifΜ=裏O_qg4(8J{kG]K3 }y9%%g K04fr9I&,T|Cכ͊tq&6bٞiVU+.A{~ر|P#: ,?H񡡾ȟ?}?~m3g>G|kg邂^iim;c8ss@#peN )9S" f\p"IK-P_Za| f5p cbafun׃(֧{P^h}ZQ]A??!'=GydFPQ|x}le=z}F/9C鋥Iڤa].x2C۶o,u0f:ƸlosJeuIӺ,%]6ꭱy!&\OٛsV/QT^3 ̧ `@ٍUS@c8:\}z^maC,x9dCl%";$[bhx⩪Mך6-̢sHkyzyS׊BE m!8s.^[ sfzq!(OM[r0zϴRAzx_ ,="ñK2 [KJVk~BbĮLXHbr @k"lЪeXiI)1f2hܗ 7w,Y'rwXb=ۋۓݯN6~MiᲅJOb<ꤿOjUW!y;dCl5 }g-l"Tdw c!i' eÛӃ5kxp&&͖ZC6̃$47~Nh@.leBAC% ؀O+sy< T:x1J3L8#~@ 0Ll Vg0HzX8KvLH Cj$nNN㻹u7v 2:Nۿ8 DiR1Ij+ _\fzrӧϙN Z5H>~<+s;ɐF~5=r=.VHa"WVeͲoSȠ눬cG 4.HLOM͝€)$Rp,?uBU\lho~ܔ1}*E sӗ}M7](;E; KOWRXm45LG7T,d 6a:MOJ"z2Ki$խm37]!kiP*_ZzR؂=``xdT.s^ŘjRyui7n4EP|dAw.|$Z3ܟ6i;e= Qlw%FGF e dwݣC/&P4&)0 <2Y uf҃: 'gL=pZK)lj+ Tv3 ԒT5X"`H>\0:lJ3q{o<.}nT $HZB[wӧ?X`_j:I7ʂ5ǤunІSS(ۓeS\j\jg2_ᓍgd;5KU(vw7`z,#Mp,? 6pf.Y υ3$+zlԀC 8HpnYYd(]A;6drDtFؤBf &vh0[NVOzK5U3sTuV!x/ƿp㱽7?5j__ӄ@`n+;c @VpSdaNdwVV&y^ 5oPHK@&Mbm&Ir~'ϓu\>-?? 9Eed :C))nncrfINZ W[nuf6ɬ%;R@>l6u4~Rٱx͞2 :;{<##[I5ߚA=+7{'~Y~(.yu{;$ 8"&J:cc elN.3HApcKF2gc]2S~`.Y>mBD~Bq^I zgktBF@`*K-Mkӏ=1C=G'e̚%e_2%hЙAb\'w^ݻc@!T)qq,@OOeJpdjNABǠiv\N viNDe}',ٺ;!!mB-A?oiKf>E uf>ģ':F$xGeO>Sƿs^0[ ȁ?0&c""88Nt.˔qcg@O:#*1D0 Pm+XDr; rl"(%K~@v 7sB`Vǀ8}{<xꩧ 9r÷0MI=ԝ0"""&$tbݱU\"BH^pgN85a2]Xk27[ls9Tݧ\[6Fؾ\Cq}$pc?ģLP嵄C`:_TTT p"8kpcc݉c33S DgNf#ZRsl4-vٛ\5 Wťe "J-N`̙/(<覞G(H<5G%*zxpXIbGcS5 W3;9 S,pS q \SKKv"֕-@GHD$٥( |)QVsTcGx1sL"@<˂ߓ#Oi(~'^/hz @ɃξF"b#˳1_XBcpsh!(R_p)f6],)ZEv%ZӱyWpSG3rS7V,]=@xƫMKg 3~IWP386h7!, @̋!*d_{Jac `m P>:(2|R̿cr:YD@%H󘀂xFI)];:ĆJ#.W7.AI=B! ~;0?8;#,,OYe+SuutAHw:cIu3,))'dYC&t/7K6nWmbyfW|@ #>?yd=O# y=O1?ݔ `l@D/BzUVյtu 4lnqBw[.Q㎫B爚H23uxu5):D:iI?Ƚȟt޸H"|ڄ dOs|x=8O2C Y nu} =cZo`-v$8cleG@m8v9NoSX-r#wtnfgIX?!,ʵhPB$ tO?=vbp O0]SxUoC JjU 6'ߔ#G%b,8ll), &-)TVҭ;!. X>(Xm4Lv\ƍdL щDŀ%ka8%J"SƇN^D3FX>  #veI=D+ N#.#D,"t.y0זYEZ+:(\)}T@8f =y\Rs<&ҁ%sH?eܿwC~@HB`訬+qhk̈waqviaVd$P8e% dU`%٢17YulKSKу`=S Q x3/ArO('yyUeK4&//q5{ؽ䔔Ǒ:,-+GhME .)d}+v`PlKG m(tsSӵӰՃPƀ'<Oe?<KX0^+坉aA LݝAEiOfdAjuZ&+ƫ5r_ctrޖ+Tw<c7vjgQv͕+w%NKx茌ѳ%l??쬜7̝WLKWz8P^FGC8"p<}WV%g+vs&_eRC UzJ }Ifype75_:+7 &Pa?#J~W^/K Wy^HOGG]uy;"w8x# }͵,N<5yFvBV BY(idYՒZiWnvo/ Pf*f V+^yeH@cXhx9n0q7'&Uw* ؽ՟xuJ^Z>Y!%9u()i>*˒B8\u W@ $L̓^yEҿ^s@AFyѼ3Wmް !@pdM̫PPc)ee)nC@CA"RV^V5d],nss3nq`<;eǁlL6@*piiq@Kس$$< :?maƌى=EE?Λ"[yʗt u ,!!!!͖W1 fg"@w'\DPg*:ڂMQaV@69سhS$x?/;-> ^O1cvcXK[9M乞w #mvc 64Md9Qm16Q ڪ @FBj )n7M IDAT.]u) d_kd[!]\P\ˑr;\hjQTTzi[qaw4k(z@FAgTڜjkEF&?Bt)"V[[[uQeN @ݳC8cႢ.7$+J]vJKV`eASu: pn7U`fAHH;a#|ͫc8Q]]Q^TgWcu\K 3LB*[Ҳ$2V4)6`7jInq:ˆS+U׎~ q R<`:tڕ9Λ Y7׮;Qk.݁("Vnkl䁄@>~4:!=VPLDgW!__'@E [_ {y+deZ^cD- Axy(Oz YlCc'7ݙ"p_pgQQC.ED1b_~DPsN] qH]Kn'׏1Z,)&_,hvcS, ڞ {lRxɍ'B`xqsee;K>%% ,e܋692a>ʐS"=KމO# *D3YZ 5ۘh N!n:^KW н;{Q$ȕdt {y,{DD ?ԷECu,.%KZ\JI»%#Ycu sLAV*80@v|~:V~ӑTwݚ5i ڥo|.oKW&>}w{ˆ ~&'N&&&'&&!<TK~0EM;7ÖU(T 7 &n3a]lWUͶdvKMDiMWJ Q4P`6[ @RMk6mڲr׮]7`  CN(Hl102*f DlļƼW$ƤF<␘vT v@^`@Y61$jp31jƸl@FEUM!tc>}Ďtv˚-붬h6 q9$cftv. DӍoWgC!,geQQe^5昈 h-R-L[$L<X߷[i(iw.%|٢<FфF@R&JFi=g_h1 #vELpd(^ HzwHŚ|V@|FvZ~~Ʊ;כ8eߴJXGFmm#i MR4a;$6~w_8Df./W9`kHB,˥GԢI&_H' cJJVVX["`ŊLpx$o-;[3_PCjڑe oZ=: j8!wԸ='ʆ 1j̹tJ|a#GN2rkHR]jD1Y:#s1qCZdF`pxp`[Y1IŬxÁPZ<&<ƛǠց wkVowPXۚsغgJ(?~ecH rb{*;hQ[,>)O \A6mmhh'4(1:ؾѦT.Fjnxl, ϯ?v,.\ٻZ95[6!3/4kJ4ځH8Q'cc"?T714;})G TTfP蜑mE5Jz@=GtK0E#?5WLS {X]Q]щǏ 6'ge0zD%ni pkee)gaUm?~K翰lsŋ [(?o7mEEA؏}jKgEw߻]&c}gwXhR2 (HN̮8;F$M&kBlkGP>@mSl_R0T):.~\3nitKrICgHcio#0E0'-MWFҏ!##.ʠ(moP&KH=5I}E}c^z^ 4˷EM?UBRukKnݳ[(JoDvvى[eg@m܀z؏RGLm^nZ]͖0%쏌4EEqsQaޟ64\i>z}6H^V7Wi@޶4ʟ{ܹy/min 7֬YZLJ\@*sXjf HkBphނg{e@ NJg8hf1n?s³caծְa&cXyo3¸^e+'{655u>zvTjx%(@ܱTŊsҟ^v/|k_{AH q&rp5Uҹ+Hh|l޽y@S$PGX)gxx_ F1@{Y/f:0uQaV^',, f5a#J 6dZT{\9=vz UյWnB|gϞ/|EWxˋ"囨5ph%{kJ@4(0a S48z.oDCϨ $#rQ>@b]&@(_O$6,6(*"`D 4 r޶8][ ڐ5xu_^Wk ,xy'9/ܴ /ߐb\Y}/P RE'tD( #&"0nU~n:c(gE\vElu")SY:Ξm >} K2Jϕ^ьgU@7 1`|j/@zlCCųs^xmuw_"xFZ r)p`)J j'ؚ1rnch { 0a`" 17f]خ(>j0а(9i +Wve X (" GA9sjnDLjJj0$@`us/YrqyXyI8]x0^ 3ٌa6^-v 4W P8 27*Dk[!h@Ek;P4,U6Tw^P`LtYIDw-VZ u%!H ؿdopgg^+Dny!01E.W1Aخmx$3O .DH2544 b# *kn4x4hoWڛkN//*ڻt70/t1Ė|ABB4OJ` ؍ T%??/[=026Vw"pC>WY{; )RJ XèϤ eϦ9d5ꭉ}ãM==͵yC TBdHsֆxiiiFi0`fCZ7Hy ,,y[4Z9sO_Ww!Ѳk CzhIxQpxp߬zlް,8"88`ǁ;r`Z/x>*JG":L}T^wO_HP+^߯-_\"jnnj!jod,߸n^?g' 9lYbŰx q 77X;8wӖWxTϰP܄8Nxyȝn=I6,3" <r`fu//4Rx@u509zZLX䥾##]՜Rp477ԴowбWZ@+ -=?&MYx|,J~H#Z`+\z隥-} mݞ@B 55H?wSQXc)+๢C;vD yuqA'grE00^1JFujzs^hmoہooԦG57O҉Yegqpū qڵ[ׁKɁ9ӧiMa!`Oה wj'YO5n06 rr/X`^Ȱ}[T"`5v!Yl>ئtM a W t$d,vMjCܱv9LJpbӔ:ݤc&xiڡ -EڊtguTmPvB"m+2*UZy8 11!}}}|_f4pg ;- g/~{us;?3CpnۍXԪ.@E @MH_Q6ly T84_@BM;.udU2 ]!Ӳ8VFr6wpL?N?ì_,>8wa/i9_o9? oC(|R%tS/uv7*-g T·'~3 ~ځdH'X Q}6[.jUΙ0b-T&LKtU% 74Z\+||}a~zZEܝ܆ȆW/9w99tpɋCs R_vv$ `HI"@Tk;O,߿C]o8wUUAja^O'!~ˍxgg`Oyó_3R(~^TL}fONP̞ܸxl-nVv:-*!B[Y 7lxbC _~7 ?0m'gn}}#~H~Nɏcɼʓ ƹd Z*PuT.?W|EJ@ƿ}er [0ā&ݘ9?{7VgKRBM_ԠIty`떭@ LE0!k7; hDl _TX+:saٯtPOϝ?υK vAy-+WǕ3oax%\&&W{?#y{c֍1[Z[: T)Mb.A \;6 'xWNNxGrg\I73?6@әs.N_ZlH7"+^ao/7xٷk/<(Y.~ .mӴY$AK]hwq!ʊOBx`BU\)N7 ]]dG|,0Y6dQ,. pym>|ODxz3DT]q}~o}8o fqԡCl3 wG$z`g]䁮ζ[6[6nA'PPLU/|7䂲ԅ,"`rt *)*S>#4VΞCe3x}]!&\曻^nᾨ(~i$+QqaB[l6G֟z훏YZ?)(&,K_dR-t." IDAT.:dIԀrqn6yT> :Ef<H{j=Ͼ6һ: r|/!}}_WQY0 [*\B@BS~ !sU9Ҩd 9(fL VF!%i(;yL/3x >@ "igs1E_sd;(*nr [޵k *]Dp߿Țg~gC}(50C_91SDs{gξ,}Μ;73ͭ[O}tp1 jH)"T;:] Nظc.ʆD\}ܥXUy <,pQ9'4ɃPίoͅ+/\3wP#ӧ;k?Bs ~׷xr>fT339+ H}aQr%v/ރkd(xei >敷/u8;iwՌqyn T.^gʁϤD"MtH&qzB|ɕ˗Ϝ9s*xg`X"Ƴo9}~_}ՅO\F.Wٱrh.um xhnnjmiݡZ9"EVy.]Ԯ㓧jNP @B,:xZ&0k`*9PS~ lti 4sgDP;xCx xf,X> .-iN3S{% ILLT@1_WmۗTc5*\z H/Q J66H .L_zI~bpQlgu,Ο;sCɡ{|,p¿a7m}tADׄm x?.:R~`<.r/"jX>N TR(d"#\>{mƟW ,(;'(`.Jً l 2Nyr8#.AA,5t:]$ .ҳ;^<.Ej\B:R"|k8 p<2XyLaƲ+?(opC>%O.?yA¬`A8Ä.Bv][ۨz@}hȦ rY7Ǒ FFjeCtV?4in.%LS/ڵCo}]xregEq'Q ֭# )v& %A[u͝h$*T|B1 GV=?y?uxx"(@ gMKr/>G5ޏ7kM~ןW ?ͅ  T5Eon*ူJ>3!wwui<^o0:;M {`d3#׳^~.1eCo9VکIٲ#Q,oKE5!%汌}rE矇T]^p# ^q)$}!`! d@UA @]uvI7D48`dh#xjOxނGF+#l6뀋G/pïK;\faN8tkP >}Psusv+D7!^:Ң*J@_p1%Huзw ;H#G*H!ƶ8P_j~jgE|%H\:=WBj\s(@&zϯ]K뽄|e~xziRW.kr/.=>`Cލir%S[U6TM$AJ$~*8`kGղQ6˜(>S3]zDtxT!4Z{hկe,8Wvܱ?ɐr}u׷pݸt3Ƀu p~!wWJ0.)F]"8W@7@: Kx:meUiD6ֺ2GFZ6Kax0Fx )bmj8}/pCp.tu{~_+ӿo=?wqzlpA>}LH L.Y*Bx*.7EaVA7'BqS(/z~ZwɎ53ʲHPEp08<-Co f yggͭ욛veT^) vW,.|1}VfM G]ampz|!pkgW=~ktB>TTmjjzDUEev (}嬣*Ӄacp559\ùhbCܿВ0 8QCGLix,ǑA}`꙰sɕ̭4~ ۷?W  ࣏S-Qg"@~au1%tvIg@ NFٵ_hasF""{w085ӪFopDPy@z7ͥ ɳr>gfjxT_|\BWt/_=C >*Y-#\R, ~jbce`?Ձ @[^عF)#oU[F\ yRp27s%qD{—'T< (z$3 [q5I~۷u6Υu WԂ0{ |K" b.z#vrE[?c "<3*_Χ;>2bȟr<P2 R7M.,M(d \~? #,ET˞@}QNA,D UʀeJFvu>7ܓ#jz,xoﳣ .tXA\8z+c߉z;<  Bص[JA8AoC._<ٌ/ &(4c~XE|;~gz_ղX{bl/f٢(l[J2x8SHT t$is#,엋X~MX(ǕaIѾ (suGUP2:r틆"!.u?l\f?)xb OBװ_V񷈢RhbpsX \ΦO1ͧ u\R@$~P%DE.ЍdCw7'ݨLLae{~/<醴WyeCF p`8%'Mz]Ο0w! ê?h#䡛2儭neg۽-C53i(aLq< ]p{8^A!u A p|~|d T$<Ζ7ԭ`f ! }dS]h%D] yuz-j E Bg -MC1~L8`fOrq-><;J%P@)ppp&~,-BB`Y-;4@ӗ홽 [$Bϥ`Sy]BTd>|jZP$ =4^.~Ƕz{{ (ȱW{gt0b\..q`04AD=`^\`q_}Y@v+&PRZZIJ&nm*+Hi?OmSN>} BL(iPsC|Ű8NaC~P q?b_HdlRH7H3TaEɍnNLwuI8/$H D"oo{,$)>I CL Eq x~ 4bomiml|hG C=p>Nl/֓] wS3]}.}7ݠ-~SVB^U$Hxgx<zy >W$0SgFRfR/WR/ll\ ޏ "H/oP W6  >h,{۶ɹo‚2xsỶP 1~P`Q(Z[}1#SQ JP #ԧ& *yJ# %hmwnA)(d) =$X0A=~c)E k㼲=FOtr>אR)+ (X=qڲ7,O# ڵ)!w}niHFB$p< hww+ I:\/i!x;"7==?"DI@-)=)u6=_a U [AKs@`-;KsLK *#/]d vt@{a3z{=B~" D@VxTzB .8.{ :d=ωu%Ix}AИ;@Rf[T[@n0v LNko4#ɇtxB@ɠ' Px@\Az #z u0IUx#[q\IX͕`q"F_`xP%r=*b(Q+A/-"[1\{٪"C%ex^{z6@m~\plܑY}nFeA ]P9e~!Ǹ}+JU4O?U,l %Ԋ6¸c[[2v^`nq2=lI=^L.!(J H}L -kSWGbrPGC.z^ R Va B't+X A04q0^AHD: 1 0օ(JG2!p*·J \7gI v #p֋/*{͑1`/@{K$BDk# zL==_ &G*sJ* }o p X *rak>[> p[18{ê Ec F?F A w]r1uiٻË4T0 ?0 W @RgʂD}}!!Emam끀-~|:K0w(IX 294UFt:6~CFA+hZ[x Q:]_`;T'iApFzųux/9)r"7,=pSq" ܚ_Q ,ퟟ9]W2fU p@ovI 5| U? D@NԃdAFR 5"BL0I GB- Io7j8g{sOܚ%޼d}v-1/ "P]H Ks;8JU4C^P JQ {' #L.)#X BrEUma(^z˟\]DvG`xc`G Pa{g>$.8=ݾQ T,S-:!ɁA/Y)1yu~༐@#DދBzO@E! zCpUB A~[&]2+# "s'`3\CyEǻ'2>iG iP4HJNunKKџ24?X2(P@DEB)pDa U:FȨ 7_NpN0wWu88`̹ÙquO} Fe]HB" !wx~˂Ȁ=BZ憡IFZ 1y d &Ōo Jܸ~ӆ56M~l]xvp|!('vp @]vBԁ>2@O WP3+n |HO04u-K@C2ߦ: e&а~NF͜U ѨB!Y[p QȦlc(d.LÞWSɁ:04#H8HW8c4.RD$16)4Q$1=I &"|Ύ0gc[ssÆ Wu1Xpn5-nwXXW(FmL1x*$h`aLihmoA f?|zx2XrzR834(:NU22$ E'W"4- {Wm5qZ1V+zB2@(PD_ H} . ,}'h D)!v VY !&QIad%O6B,f&fC=EbxzS.eʼn а~ÆVyb3cܸpwc3~{.IDATR!# qOA/;p&$`dPH^UC` 4A؞Zk(i-L3O2bNL21#=T 5ڰi k[u&Q_;7so qIߍɅ8,(E z>;ڙi2o*("{Ao}XD+b Ȃqz(鄑~,2aiO=эANyM'b^[7l ۰k12׮Ͳ}>7'jf^~{X  ԟ*ȍR@p)Yx f5ߡӓ`Ʀv䀖f2"hC uQx5j?!eF5TBffVZ AݕЃZ,ژ2A%' ?jtr5_v-L+$`v$>8$59;$@:A3"h8송!DT(c9rï1TA q&:pyC&y36tSsP*W8g&VݷzӖ'vMLLS+a}C뛤x! 걔JЅ(HS/֡%܅Iz@ 5,A,'=A*8e@nji3fZZWy ~L/!2#m4T "<=^pS3Aæt:½spXzM&~AUl`,F!/bv] VG{w]HVTH~?O0A_pn`5i,]C/rCiuUɛ֠7<"IBOBBBO2 @@nAXsau6lhhSs1"Dt37(dQJeK=%t69YTjCrPCKX{IË 0U`iJNݛ["^K *Dq`Bbt[p@3QCGllzǟ mROϠp+^lQn@J[avf(> _威9X,N)JG\ua>xdA7K0KŴ^Xb?.r!T'3jA*EQHs5qov:9 ZT^-&`!#0XRi@*7,I/aB ED@Ѡ`[ǐPҲl#f傅LsG KbGT*܃pRP 8 O B[qMp ƍxU:Kt /xCQ3S^Nʩ .Y)Q67:C]8TK/s"9I_TctTt,wЩuV.aג8(7RsJ#Jf0nP6m׍ [a?%o嶓3mbV\LKM LDJw&_qc5 "Uܳ `Iq9=jLC@揙zl @MCF׸ieUK%_m3R4į {eK^63M0R([o$F;zD(yP%44,Xo @QLT@9 p  ա'!VHT@}>ppI0s*SaMRX ;r{EYo"$XJWSłJ |й+&n:~Ԋrq`d^՘ci9.ҍtR2,@79("L4AE>0n:37^A%*& z{Gg}-3?@21٬} Hyr\sXU)ns]9{I =b:֦NW ?.rmE$yJ%b|YJ,u]P]wS1)d*lkvC=q@Gw$y 8(5, 1d>XǚeW*NTy.\V  "T!òGlVZګ9kyމm 0a@nXW-Rzہ^ GOG_Dz~``~I=ⷭW_kI"7ݙdd;>ח8bD!٪"J.tZ)\dY$L )S\0IRvS$̀)PL*jDp|?Eݽ\J>T hy!88A J-"JdU6+Vdk ֳɢ(HC SIMxau3gd.K -2)%|'jV9cpsSTD Ƞ@()Uc…P qAOJ=H:ۃWz# re?\#5iG?O@G+S|S'r,hϧgYWRI jO AF.Gɠ[cc9?FHdaGR^*Gcy+yVoJ}f` ͼh1x? `(LID(|r366|~~rT}g\TQJ gTeOأ'VAeb@_wTjx@'كw."Pt PJXDt*#a2:$a 9:}6 ێ/$P&t WY||(R1S+دJRUWZT@(9DLE&yf^D/epRڇ=kʠ;I';*]y17d :_& 4dB8mAL%b@SANPNRP09+vKoY*E#TAmբ I76V#Q>D+Xgu%)ǒ9 4{BNgGG4# .4)JO9Cz˖ۜbz 7lm UMѪ?DB1CWǤ? 3FJ8`@c 1~TN2&}90 !~pEп0MwRp̩!9MfHB^ꔗn 51z.jii f今t;p*JSR=SIGǬѣP v8C:3C'²BM`8;.j .@] ۊxd:U%(r+aL?fBzy#?۵{q5n@R %74K\N5h aKbwEr0m9BQSab eJYN/0RTy\ޔƔy [\mO7I>6@)|&e-˞I A)K $3DD` ( 1O-U ^c啽u ]='1%" "\LĨO-=.!S&5&,K28Of9ù0>Ƥ4LsFH^LEPJvtljn|ɍ6<bSfIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image09.png000077500000000000000000001251271423156660700227760ustar00rootroot00000000000000PNG  IHDRkXTPLTE)))***+++,,,...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~bKGDH pHYs )ItIME "X IDATxcٲ.̽gvKqwwOABp;Ĉv?_3g`f=OtH:tUꩧdkVG#WV-z=7[x~?zs.wM/? xd~8\q-K 6.fx?9NS<7\ *$}ê~Xso;B]^_`y<?^׍ qC ]op7+~}q}NY.hv.z,.is؝v@3 @"^uz~E].x?ƶ"5þ\ؾQL_ݙ~ }+Ao ~OHo9nLo,q9l&沛w`3/XYo0.[Q_NaZ2Z>A Q7r:lNp8l.a)W "{~'ܹAς/u%|`QWFrM.lcۜ~V6o`% {3` \5ˢaćwn/XiҲ'nۅqĢyjvydatbtzzyan2 H Y? |Xo͋ vۂy]H@.p`OR!K}l3旭&yjq8NP@  Lr}Qe bh{'+`95p8`ؘݯ"pmVj1,sf  a / 7/= #{p7f0xlˎc2lFmh[[6pF!Gƒ/\Ys/BHt:fw#sA6{ ^ `v04YFӬyryF紸|QXG?ApU.0x@0G@umvr/C{ >5P @#7]6.~\^2 N7JF%H@o*RWXQ7H @= h =SZm d3~w# }ߤ>@W#; NcZp6;=@(n?w~p(?;Fw1]^vS_vh6` n @~n 42p`v`cpvcON9>4:5DB@>a~)~L.:MEuY`E!.`rⴺn?>?~k_xi(DyH0'< $bFsX@a àx@ϬߗB_ahۑh?XH|9'95*//a|_< 5Юec.5ru ۜ@ l'?r[~6!BkF q  A߂AMvp J`$(X[~Ì[Vqٌj0@}u0BA,=σdAց= T   W @Hs |"`AVBpAr: [ .b.դC< #2sP@-؁Px(DH <{60$P)v`_#BVV#!`< ĝGF'&&fg 7[mv;_t]>}}{>x2٫w><~իG^?zOn?|`'?yსp7qxrtzvnAg2-mݷf `}rm7! jV8}C  Z;@ba7kH_#+HcW+у3K3^->>^zǷ߹yw/]>w̕K=~3O;rܹC{{p}'8vh}?xO|gϏ<}o_={ι[7^~رS9|ȩS箝<+׮߾߾~34q|drrd4 Ƶ@{D~jYbʁ-#KRC5O]X x,n>`.!u:Mf]o~qԁjdB&KNȴ2Yb2n˶|ss3SoG =yy'^>w콻\ҲnnFo\t%ծ7Yufl6[eѱ޽zvxgo?y2ࣻ?A[2u#nyQ7%ݚ0Dk,DmuW[Zt-Z h}r`kzR2 Bg Y"! $F 9"~M[ݘ[X2.heݲӇw^}q:ۗo޼xxnxN- Hbm8 Lf 6pV^o_X_46ѷ/|]K^vܻuƭke>On̖h\"I'q<̈ē)l2csh,ʥձʎM}[xzVഛzS>{ ^KQہ",zjCh2F6](VH6тg36@Vw͊{4?÷뭮+,`D,7L“I82No(d J!hl*!$2">[2<O+K-(mlqkہ;h1E./ !pYQyjy>~\@eP2hC14"ޡ.$ /ct:-V"k+k\=ל/Pv"`B)&fR,98>CʕT6#^W\Qd +U)'~49S~8h6mV9@5.!b~'̍ 61vYo2;Aā?J0K# E(cL#rMVP$o噲mP/zȥ,G)4)VĥfUf^SE[S[k{UYIZrnaEOOvחV4&'k2 3S2 bKDmR|JfQS͌LO~xvdӌbCp`E g%bԈn~dr1B ^L(5NF6`iȿf ,+2-užME\6"P'mLIj߾u͛|՛޼tɟlonh\SXQҾy۶'B6LSbccKLu^ZX4 F$h5 .B@ W>ہkC0 ђQ4/0+';Q )wzPr8 ،#`iܑ|6Ce;w7toٽc}uny^| ws޽{v-:jq"J19Ie `YFR^TkA<$:v<Vnynf'@@Jz*+"/CHJ >!ߪD*IT}ڶ}CΟz۱%Dže Ҧ5&N;ן<{<0?;eL>9wG_8upK;trWOic2e J^--flpc2ȼ;C@TT܎PeX:`]K :mX҄Ĥ7  6EwŋXD2?7޺e۞gMM?繡7~5>yqbd2g^޿q}?g'u9}ǃuU yX@Ptr tCP%A.v{* x? x!|u M6R(zu<β]HNtgԇ`.S)d6O)޾`ٛ>=z8;69t04<٥مϋ s1=}b'ލ~{=٭|ro4~xy}/=u֋wnwGw/oȈ k!6Xy+_n^ӊ?P,.Zjjzk qzc6[XyPV .B:`@HM.Eۋ.|xucu9eܨ`3|4x<ˣv MhGF|v|thl|s>ՙfۏ^ycg^{qr`ɗ׎~~VDͽa0[&6< 75`lqk`V2䄾VOP#m\0,PL\ L`=3.0yxw/\9tSS:ʃQ q-8 E 3sdch6ΌN-tc&ߎ>>05׽pЍWo=wΞƥ#&ŤFvD  JpAHjH]*ǿU:)pMCŭ1'HE Z]>hqQE$A_m QbYl^8X Dze_Z6,.MΎOBj[mƬO}ʭ[7qŎ"E־[A=+5D  6jB[([ėd8rI`Kch~,!r B5 2u k{`=aitћXtz;wp#M9tr!=DX6ȍzp\XCMn,er 45aE0L C [KC`?z#׶̴H=0}8Mw}͢uPA0`X D(=^8B.ƚ7XlڠeT$^ <;/ YޓW{w6b/|`Ca,bwM;z mڱϝ[tK>Ak]4>53?=̓;ⓓ/ '|] u>(r@ sC0=TE(/{% rIk2$E|vZq]S~<}G,֎ % #O<4_2L3_+%S g/ԕƐ_?fEo.<ϵp9XBARq4d ,3&''!gˮ]޾ލw5=wʍ>M6pz.K2Q^Ά*>ld߷,4uzǧd3C ?숷@U _l vH @r>]<16 1, D1 hr`F *#j3TvlfqIyV\An*#%sO--~50VŎ t8ڭH+,ނ4J/]:ZD=_нb1՗rT5q~WVKa`ۚߖg8pDj6S, \A&qu?D."L^ ؜䢸M[;x_/M:l6J30]u)* rޠX7U }`Y~ߟq6VKw|4"OZ*ҔDm4AHQևqCDTĺ0r?6l(aLTd ϠQJ@.Uiڄ8&)5e{<}ܩ3_{Դnqa `,f~CA۝6YZm~__T!woȡɲ\)ċ ge4UWU&=&FJ6ni]X ׮_Ñd H"tWq\ơ9!_K%|PZRӵDV"=Msw}2 @?lQB H+~E+~naw-2y $+I)=gswnU~}{$Ӣc ʼ$M\OcE|tJ E0 $"@Hh6D!11DAgHd&L$3bT͖q9rBJ+J!?Le>[LN؜+ X IDATCÍyH e\ڃBX O0FcZ\Ea:|úak_۰]NԥҶM)i[f;Fw܅(+UzumHNWۤAM wc#Mq>_MRh‚8/&rUBjjͣ+5r(3 Vh:kj:KҵqJyRzb^YCq~JJD\@e`8 @!I11~ڰ0H%n!Kqc~v؊: ;Plen76 w%I0@>"{SAZlSDTge|[D(Q$*C'2܊x>Cđ|- k 2ҒK2yiiʸʊƍ-jkj K%b+d18҄++HDn}43b( a d&"'Pl}nPC"YL'$  V\bXP!UZ.Cg(D4Q)kTb$KP\d)3b8@A 9_\X͊K-(/ol/ljJJVJayƎBia"!#Qh2Ҳʪc3Ӕ9j?ĘGM.5'D,PfؾQs`( {}U6}~s4NjLP ސYUbzVr|bJJF*p"y0VPrٲDMSII%5- e%H9 YqqaJjMCvLHPHj^TT'f\5N~uZM6 Q؁/zQ}cR[kK2 f '7+LN E/Pb1ptPV6uoj*Xj*JKS3$DmYJzRΖ9BZhܪBM&.-JW]SPXI#OJ,(*Me e L:cx9{7)|^űrذ 6>7tWӷ =nvpB.7p*}v֝+n9Z_Q/h`4?#P.x*-p*%SX|D+Uj\ )!!<5-^c )-0"B OJrG+l,),NŇSIp䘨hJ,A%*$NU$'6fUlYnEn6[-`Xpm.}C/nHh[ηk _$5q#n'D&G q2Ea9LNe)Q H0MJPJ5HHP XLaR#c9 "xA`2Çoذ6f]:"%NKRrt0芺cc7--&@'͆Z@ *`C2X,U?ȝb7/G޿ߐԻ\ KbaxM&eX"OR6EHO%s9|*V*AQH. bE|Paiyt6.qd *F@AGG៑ ^SIDqii,G ujz~MH.;xeDZW/=|O}臱c>|x72o}862A״ + .m5E}[OBI"Yx!˃5|aNZG 5Ggq\<$z,-2 D0ET2DE0!`D8aD*`͏kq"K奦&6v5_Mc?ks.ԙ-krc:h;g<"[EEEןbX*n?﨨m*OHV&It.J'F ıڬ*:#NJgYXa\%b0,:H M A!DG (bb a7DDg@PukRN4ZkMsxx,r9kmNKꩡi({I%tg/ aˋ0vq7nvV4:fq9 jT4@Ef xv$YQV%XRr@ѼXI['HTSP82yAA/F?J(zC ԷtX͆YB:DRҼ8r@(d"A$lP7] (QkeZ d1MID=$23%1Rivi.ƎSh$r|H*IX! Eai<) @2.::"*Na"7D"QT.x GhISfTWW5l:pd`˩} ,QL\$Ȱ2/ 5"b ,1^P7 م8L,KqE\)_r9 l2M x.LZ@Ea#׭@ذn}D$OD%XeNUyNFYe]eYu}MIqۦ=GO]R1uRU/0Ss_^:"tXQ/ Bm|p42-bOyR-֦0 8*8ˆp<ǥr,X)֭[n:? DEFB!f1G$ 0AIrtD8Z!~񧰈H29&WCۧvmԷzwcCؚ_UܞSR?o|wvyUQsW`(efA[ :i:XN',N,6;pQQax蘘kqQ"D>rCTp\_>|mxO$QRsrziIuvQEOs]澦斖:6!CVrj|EJvcmݵ\u"J Ώ Yk?Q :%̎A_bo'_ˊ$e\D4`8BLXDtDD4v]$.! F.k.<|ڵ7Q1yՕڋvoo+QӐ$$tW&gl.)n-x@דp}ӯw\`?~5{|>֢ X,q0[%,2O"RGPĥV+Tb5 F11~ [Gg%8 uyEe(}no{yAG]V '扤ʄ$<1b<{TYJج@wz ~g լ}>&!.KI)<6F 2420gxظbT<i}$QHS*ʳs3rR!NR2UiŅ9Y)%ىb6#'W]\}n4`Zf/* &ۗǦw{4A;jD e-p͜Uʢ")*T*@gt2S@Yt2-O!RhuD2J_aļMFy^N6+)?HSS֞Z[[QYZ&JjT9Ee_Xi:swL.W x\;jS7sM;ѱD7ԽDM3 'ն7%qY )IxXiB`LG\xUV?^WYU1n@={vjڲڋʒvƉsR `-K,DrX^,&w8הih_?=d?'v|m!S!1q mj^Q !GsH8P~<EED3XPƮ/Oo׹uh:cƎ?ؿ`[|_(WuTn4FP'ȿ_Ma5qvjgNF#$2琩¢"QJ6NKVзyS[}*6j/]T[^ ,6ЮGNw7v1U($kBV>- G#:=s`?tlyH=IbtEmʊ4L$C`DGl1\yjNWa&(ɲؽ;mWs{aO۰sy'`;KJ>ܴڇ>SV>c7Em^lFq"\p y};d Vh^!)R]IiB\̣3<3*Tqc嚌΃=9yKO򊢖uM9y55EUI NP$v08v~P7{\Xthʧa5Xp,;8'6rr\vbå?^’|qjnwF՘qL2;6#SXsǁ])Z}SצMUMK76T&eWj y,O]qdP:ةqܘމ]#~{VV|}@gEPud"~[vvna6LsVdAyQb1Y#Fd<8"LFgHԔװٝ#9ZXRһqKu992ȊS4t5,6@oh*׼03 q,QN l|>rT!6=^:rhޥ`4ꦆLKMVyYo?݉Jm_Ntn$mɾ<eD(?sĎ-'m=pZ-akvlһĭ=336ToJM@N}?Q=&F8AlfzJ8otfC.arԹ،&,6t׎u:lVĥֺlHɏTD>...-kݿm[{]xئԸW/ Ett_=бM؜m C$%9u葽w_2fQ>wtƏ25qXM6Mrf:%V6S +С脫l,il+/HMbJ*SE첆暺|֞tenJwo]RavnS[Esee}]F8.+!̐2xě&Gކ "!|htz*wC`gN|O[_Zl 3AAmde&?+nlb1ݣ%JxqU91'~#..7zWOρǷ?6pi߱ؤ[j՞)Iϑ$b% .//7P'jXGf?-}&6Zý;.GY4ﳢ(?WӨZfx0so/>ZmMThӓrJmi.S5dnUPx{.t)+-m'6 lQO:C H]?ύN-H 4dDdQ~ޅ017ρF/ʌ%[;3/ B= n;57<{n"A'ֶg?8wwnΓUH%6ȕޔ^ՕPڲ{[?yEykr˫)XLDQt2!g}{nxA 7Z@?#@a''GBڼOO~_v A4±gf } #6 USNͼz֍^]>C,MHPR ݵE+o;@ozrӷtTn.M% (lMJ^ ^31yꨶ `"tfaViqqJ4FQs6u^˼0²Zg_-}}*)";A'G"U*U,D޸5]YܒUۿgoۑ}gu],?a~w؁iιpT{s(< M~M |n,`3Xs`wVfbbpI&4J[nh,LL/nLhho(l)ֽyۦ[W5l.̫i,O.HʒHDUDf T*+lllu̙yò~N?g:B!@@7dYdeY  tt6Q A4qcԓc׿rٞ|Mjʋ4 U&y * %rkz+vnn8Rعgݸt`ξeJUIV6>FV`h UR(+K 92P$rvxYgq8MK?qj[()f''e|iNvSvY51}vrx,y0;&V/WIjƬ͏MB]Ƕ(S lᨰP3rr] R+jS'/عΙ'<}ef}sّ<&7,PRl2WRk*9'Js<qY,K"H*&4\V~ˌ=_Mg=6v޼dYjX}m3 48:>N{k(4.[ruJYՙEYɭMݕyu}oa˗9)J̐eƫev`x4ܻ^exDpL)jtvYHbTtȥ+4 JEj{cEgC{GoSɶm Ui;_KvaeMA~P,4U1,G`1CC1*,\c?d^BY%9P H%ònq~~jrrhxdճ'޽s!@f%LL'brpB>:2N犴qBT$./ks ªD2'5"V5qiYr@!cqeJJ<8?07';=%YeBVSxJ\.`ryJ\^yɬ e΅L%<:blΥrjjaM𗁓ߠ_9ƪՌ"6CJkչd&#p8\^8Q8J+#sXD$V'sR28uP"saoS8b1%6ȓ'ˈDBpUĸ̼̒\6#;~ªҢ܌T!*KcJznO͍=`6gcij5]WB8G|!*cbiYj(Wx|$#6.h4i55 ۻ;:*2<۷sss_skc]UnFb $&7VSQٚ_xWyJk33sR3ܲʲԴ҆Mۺ rU*b5"njR&+s۩Sg._qW/=ųo֞?Yݖz^ Vp#)tAS EjáЕL.S8d'd+ )ٙeٙyjyBJdREXDғ9ZmI\*Bfomܴm]wWS +#193+??5##[J/-S$feīr@MRK]8&)C ;ZrF:X9)dqSs4,K-" %->QQRR5ijmFEsmCW#Ξ>wcM] )R+VkRZ5/IH#xrvܽ1xȁºέw5U&zOd$wdX(h`2-.N.yo޸66;y籷}:<r F{Em{r=yn%UQ9s(9 AAs ,P*箹ʴ{}n/hCƚs0|] gIxa(11XJ$ӧK|<816>믞﵍թŷͯݺwbskF->UqVev^82QȨ".}maq68|7Bwo߸ziM/MƽE y`UTR|i[i)hWPP2t>Zڵ6eevz~su~>:?>935<12ۡ7> \uZO =Munܿ[[uLT Kx4=w_=<50;eeayuabߜlxƾ=\m2IR,ٛ~2wP^o (J*dDAEӰt:oHrE340nkv>\N."qZ Pb-ՠ)ѴKv. \^TZTׂ,3%o݇q(oϽظِHB؉݋'w]a%;@kpwgYXYZv.@lmǒ gHC%aMR+t^ZRE%GFS XZqOMTVvx7x7?Vy|\@u<0`{\+ڿɨ\{/ a^@)rnC!Jh. NS𱑬&t캡l{wҗO]'h^{so3;8DfƦ 0Op"Hp(5iX ~a=)O0%q8 Bq]swW@hXN1H@P &0QtV%1iɨXGlVKiŤ3D[|Vy^fZSe-χ&0qcFW_?xKǶ ȃv-`m"8?ᮋX#+VDYr)o\|@`5Zr9J}Lt,Fs0L6:G0bb"t[$`RThJ.k[FKr^c{}wWoūW Oj,oxvpl=6 {]tv:a<(ov|_^Ã\,qCʢ+~Gϓ'tsGX)u8y בDr"#yFQL {⯿(6"& KHT&eQ|ʤ1IU|f11.ЉWxc n _C׻th~Ȧ2e<ªRw>Zǹ7??\_ߦʄl*": Aࣰ4Dt L)\\h|ɋDD!QёD42":@p( D@DGbqH&'L6mӟg&&g'gfVWKӻ.}րG[+ k;N~4I=pΥneBkh=N ${FcXBĉL4+DЉyhbIMΎ(YqT<@rlDL 'БQhԉh&"18@" 1Q:&}|7ilhqȇɉog>|syAkgs&PV^t^-f+/=26== k;t)\.9~<&xFgeK8HZYQUXP\VS:p[-gN*8KUh M&ED%ɮDZ9(I_Y/wh¾g\Z]]uɏG'^d{Puߦq*aDDIĘ(bb (eILTL &FK)mvµ+շ*+mRWTAQp,z}FQT+4& QIĆl .ʕi B[rT"ǖ* SeTrDll$=.&z( U $kE7j\| Mcx[,xTk02Pc(,Dǡx,fH꬚A  s0_ ;.pC|S`a+%<ȬyW.YLb FzC#nYND\1#-"rt( oHHk̪,?R/QY9]-41QT IБŢ D(ޡQ1P`pQH 76m%glmAP gߔD췑ã֩m/3zUE9\2O)@2R,b $>'j@%bAT'i:[znijrBaP:aA2'94@DR|9/3T*#c""b#|2rD&H5R`dDbNt`T~rtf~4e\u87wX}0+e08B@WPm`k4 US<q\ s($NM)/5 K 'FFB2@eE4+i\Y%2'1$f3JKEl# H QDD!fAВPh":%"cL+c@%Qbi> K!k T@4 z©{{mΎVSI #&H\1yL618/HMB(3\Nⰱ1ʩ(b Ϡ4!G ShTp$2)Ơ"AEӨ| fX<-pTT(ΛWn IDATV8x!~k!Y}gJɗ5D>!AaN0ht5뵉)EIx"$4 ?q"6B  tYs JDrg8,@ESH11$Z[69WG7\sЇщF>~b [ŷ3מ /ו2Ӎ8EјZ>S(2 Qz]8[Yiٶ$BO'2 lZ'@ P$^mpGg)LR6&`8"1D*C #(s"X&Asbe]MKACh,Y~=u'`ª`ܮwʓhsijM^J𱀞Gg DcNLH2[[$ H42 ؈Da4 ]*URGqT:^"H, b" 8 Y;o?=9r*f4vg(LrX ;npL_?\w='9OTPs&!7lQi!Ph4.s  p|6KeM6TLIMJMO1h*B7'fgf$85QYcJ0Mf QEX"O%Ԙt嵭׮pwtQ_9!!UUo Wg- 0: ~D( 8&c7jϔY5yy͝T.W)Mbph *D 2 %h'.O)LK<}L}^T]d%dRd֓Yu*PFH01DΔc4)42sBfzBխ[.r \QgP+ 9ϝ{>LBa-ǙϽyyxo6n`\9Pito s@iG}`IHʭiq'XiIdbFfʢP&O汹~dt8]wέ^İך{4ufJזr4VpeA۵וXLϝ@8 }5B}w(C2D< :@{:^xf P),SEdt,$SmjֿzRn`3LRVBY>{ɍh8\@T{ n{RET~ן:Č@Ʒbu sq@ϵt~ .‡@(4+(zikkW]/d8:CYjG+-ɪ55-ejX! )Q%IUU )R.C{v1g--M#L\]f}k^.yޫ/\S@ xV>_٨at(^o2>?fi|!-ˆۚo~xXyý<, 1UdT_t2<+8^l4TeUW4^y,JBpdS5ּWkOf(T̜Ʈ˫=rˌ_O/'pu^ҡ0p" u"8pk YG;[BarLGcrHk4u Ρ3N:K[[V(l=~2^>hʲ=\Z~cT޸cy`9Avt  f:fG! %LMdkn^ln9[>v93p b,?wNRUuJ6#)5NYʯ{^sz?*bS{ڰ=!hq odx r_%@?8?G Y m Vlnj&Md,I_trwf)$ >d%I۰gubHuFWK)+Qx6L*9q-񁻳dlqitUYۍ 咄O]@W?w?^o?oWB0a}7wۆ: K.S y,.L%lޛۭvd*R,:@HNjMAJϕ*; K^qII襾̤U-eq)iw\%<4qt︸; Sg%Y;~`(tOЌVfr4l5G! sd7]._Ӹ(o nMK3PDyQb|<'eq^zghn ިfBAQ-֓uM7 YqWлX:;<dUяo_=taߩ`R ]Hpba2jNř&lRpLUg ϱJ ǚTt6[c..WWo\q`//dұĢB/wu%%$fB7?lxdoOPB`9N<]Y;A<;;~6C0bKUbÅ61qrBN\ ptRiՖZhjtϽ6uM?3Ph4NfYm55Kbi3L=ۻpQ;;Y}i_)9:{W˫(%eSx4>{/ׂw Y9|GA@ A7*MdHeȉr:^l)tLh. %%m7}L>pʄY)Yi*bH%&ڤ KZ DD팈z|->`>'ʻ ~ 'AF8|~Sx}$^bHiUJ*D64<0x׍m{;ͦkJJ.e YZpR_'{zƊյ"F4:>6 |#j1~B'_+ex+6ˣ T<Ǖh9xFˍr% ٍ淏:sÍgtl9;CH( h߇QG %"*%鹒m{4sapx(ח?~{ܱ ^|W8;S \ GXbhC)!) j&GuƆOz6+|UK^|.7_C&H(s1EMz@n8T$F!t 5+4{[ѿgշzO'8wB \ @ږ^8 DL7Ihڦu:zKX]1eXZ3ʭV 1xIlM B{w6La?ж̚; AeY pH3 žgB/ .-`aef+@R4fY3N5+ld^/%B9,Ǔ9!\^]zVscVBtnAseb8E `kڴL\Yy5om/6(v򛫭dPѸh)NQ^#,z8MX=u(tobx yœ2Lo8IU),Q NX ļaR~$h<>꺥37^yf$z ߘ P7= x;{W*NUWHt][/ s^Mq=`3/xf¢Z?_QX5orCa/o"!6N 38ZV@)!3J5( [I1,}ʆwLꮔʴ ^D0ZRYR>A&k~(۝9Ƥn}L)ޥ *K?cZQ/On|۠35ww51 S_xawLU//`(vϡiI1DL$(\Ţd2O xqΐ+8!GK8bdU N(; y{[_|4Rz]v:=WX]^ F}Kg;l6.NEN~y=^RumYٹz=Bpc `381*ZDXǩT>(LP%]:|{o. 'ToTwtV0x㓋SS?-/,}ٳQ!a4|aE!q]H?BhDx<2*~{=HvuACd%o`!{#1Qd!AgYfU*`.fw/[ [tfIǎ:^?p.BJL~ǥ|sȚ}{ i6ޝe;wlV,ec{CNu"~ @u.@|*7 [d4JaR DW|"Ekl2[gozwo],c2"/*~;;CU!xQ>ܡj/g8ɝ;ww {U}]έW'?CʓFKEU/AeJ0:\zâ4\F3xtpڡ?D tCTrEK*mm&v_h/^pl:=/OT)dFS؜[P?n_~ȖԺxmiqhw'ۓ,eSEi;#qwwF=i/$<$<ma b3@c,=\i? w?M \/eԔ/)(:ydmS]A1L/;>˛37.e﮼*svS\M\B[ eLfTo5 f)`vZm-nz:1+śded`&w}^?]5 p_Qt|zjS76^hl{Oo\yHӍww>=v̫τ_?~;yҕdkẸkz+V!a}ΚIy >ن~ ֤ơZ:c&6;n:ʿdch!@ӇG!W3zUiܹKm^{w;7&=IgL;&X9W]4̫Mӎ+v{ڻwϵ|)-࠽{~7fͨp~ǃQprU_՟at]w280܎QU z3[`˿}5_,*ߎo 0n2P(K}vf@9bz~zF˯/}terWͪ{h"teҽ6<;ŻC׼ݡ;Or< Oߵ_wl~X\_wBK zgߣꋞjK^ձ:~  1AXa IDAT}XdNm~hAn=0A珷Z~g{áG{>&dU]u V)ڕ'?]0{k}#?$kIW ~ųo<2:Mu'r3*Uw4tg.=ә\[՚Ew}Nv45ܫgt]xFۇ[qSǙgznORtqSEŅׯ?|vx뇂P8IrϬxRjRi o4XON;\.C>Ի=D^u<>M|~sփzztoxhajeqzkuqemiپso{;[ ϱpPxugζz9xI?+m<*F l\> @;vk7 |ȇᑑ/>Z83yai._w]f8hy{ҙ}w_=Y?;ʚ*7gV=띺wf~zaջsarbU49T 5gy`ڔw;3}{)vv31Zޅҡ]}PfeERv 籶qi06 @z8f"Gp{< A>}c:bGX\k~B[mmMUWu|rg']}~ad"7XshdwnÙܙ5 x|KMO+D[oϼʩ\bSTk/l:B `}jsq@ƹmD-_j#;NXW-a;:hBwvn}cgun|/ }6r~zRXL[_/Fo]}uλ'y5Ξ(:\z?6IXqCkFo>20X~rQI穹צfOvu3c5Ʈ1i}}tP}B!~  9CS ?g6L̼̄ Kbj9-(TnArrZ[==}^ ݜv=3{m%ϴtkoklvc`j}EymbY9٪iɯpݻ^/=)'W>UWY[UZSPz2Q\&,͠u*sVA-].K..+O),ih((+*).=}lymmEEESYaiqe"TCCҪ ]T~wcS'g>L,̎OLy4,F8<ERp1C8"T8NQ)7%e$HFB!<[P=NdMV%fZ5Lf-bnjzr`H 0xaATfkiMfNjzAekRɚhSbBp|:gt:/Or1Wrb]B#(RM^MJ9' .$d\sy~BvIYaEf+MMgםfIyR <)њtѪUzDiISSJ jkhsp18!E@SQl$ PAF%cȐ z,aHBi."`h\Oe T!<2&:A#:Wb* /a R3xL@kusBBѦOOYUTKFZZ0"b3fh3&664w?zlgGǙcjD!._`Q*g ey+4RTWEŦb>IB?5ĐhJLd$Ect eȔJ<BҡgbJg6t'myI$[Րh4jZ2KErX$"Z9"Ԩ)r!f+N2s3,e d451;;\׮^nr*]qr }jtttnaz؇/3/ cO]|u U!Œ8lt DDŐQ1#BF)C[@.4BSs/ ϯl|}b{[_.U쇜I]EfAQ֖g>}}ƥseYu%iY٩9 IRQ'WJi8G/4zJFpY\TZ\3 4p% 9WEEu-s b|@£8L"]f$d Q%)3 ZD<Ů[]ó=YW}~k(t-=Pq 4MBac^ [k޷v/h@3uu5-::߿or~ 3~ u*KQq?PtH|D'`slI$"HҋYJSl&2mcWgWW76zn{= E=~yum…qޖ1M' K m,\58rawe1L-M;V \ll—5(p |^l'WWNKĈRy4*t6@b"8*‚i ӦT>ZAO؃27. )ܾ ʛ߽vnv9A_ھ ,aMʆ/ n?28 ~Z&]~, !ׯO"2<"!Il>9Z1$ d&5合;K+T^piɺN˽p(@ c_vjJ7 N7p{vcJ~~ ocx}!H~]>Oit!%8GXXm*(1<v2'66Ʊ㬓>ج I^G]#MfFi{{Asvٳ8hWy+)+/.+S}H癃|;nJC,Q5ܧ8˲Ix q.;Ov]}*qh',^Aܶ0X!ɹǔI!8wkW=qomEqmc}ӡ}GZ_Q 8XmEESk+wW LvrK22'ʎ 71Y E(= "BT 4"qnrT\3g!- ˺uYQ,_i2Wui~@+D` ?K?+ʏHF p*[J/Irb7~cUA2Ǭp,U'@GH2I;WFG6c/jYU]'XK[t%q ž B߁I $V AbKȦu|4V=V_֎h]{ˊ|jOW59.:5rX&bp|G]'@UF)C5UWSt8Hmg"Q-X~L}bnZɸ-oڊЍ.Pa+~r;Kv5Stum5{J/O~ڃ'=\\VQVy?>Ctm]C xKBd"wEVl8TAѰG6YRx$KF->z*;ڀ]-E*,[# X44/_4ՐrGmJ9⊽V>nWޒ}77UWU-)ik(~Kϣ 'OA?kц"ڵE-deqT`B4^Rx\( 5kp`DR3XɉXL],";@¢f@S'66U5%%5 uN<_Y冣?[RgOi/m*B+-*R";fh-~ĵeN2MT(8CAoFKMKIĆ RY+/B&/]_\g-EWAS,N.FzFBsSCӳр?tdÐY^5DM1 +N"$Z-IP +?Q݁m^[3ߦmr^r~f9,rD/59xhg׶QS^VSYY]S_RұSvtZyaGG۹14@S$&Z,Kى[wF'Gh# Y3hΐ9ӡ @g.J!w5:!aJoX!HG3ұY̦SAߊ j+KwU=/xve3,Aכ*:VSsߜ%<'XkhH<6͗sG0P1tITr$_BkC=÷o]^3)FE7ŇxQ'؄ _q1/ixƋEw    OL []T^lSMSU;ה=UZw@s U^q{??meu/z%?,,fK59 VˡJhv6 WCKp$̤x&0fxxu=K8&N&`DBxVSiQVd1 /N.̈́&S#Ռq@mh`I Ft01ࠛ`j'cl:̫gy ±G;_w4wDE3i&,dcQRx#3qanUyE3R +cRi.ڈ%"aBx%XncX<Y.k%H@P 8mdn.tk Nzӧۏ|M͍mյ%UiX?9ʙ媙i@GFha EVC5g %@6]XѭgHX#2@,U4 9da3Z@x-NTT 2$*\Q`qUe1ِ]ubRrkLPwwϝ3싧~G_>ͅ>աO7:٣-z/LphE"x+-$15֐d^ ay}.~npҬa\7QZ\tJ`KA0 Z[-Q5U$|QAqۂge^88j(HVXnx<2y%n&-("bK ]6K7n3bhzUG≍)94Dž7 >V@+D*՝$Ih36eMK``jL3\SMuѢb`G d@=}$ @x'G{֢ &|Q&mQU)X/4p""Lm9CE=2LR "HLDºlkoHb19B)]-,X"!2["!̬;cG&ˆ^9t[]\& fwӞ6i!\Qh|ٿs6=#Sc,D3E"7\;鼏4M [2&\1[7,u0-M{JqlNJ$^sX́p4Ǯ2B@Mp(}Vk(4*q%œA /Fzzൾ#ݷn,Zn~?94'Ff{fV&o\sozovwv=}h_ϥ˷u]:8|wJl¿ 8h2ʀg4I8ǰ*ؚȤݣ)UO\"M`8ڽM]m 8 /OGhEGFgdž KzJ\]U96W4EH8fIL B~kn=IfvPVWɵ`|hw{r`CW+=]Wݷn O/DGIW1D|IlsTU%⤳ۘ$0_H!n+dN/1Y07`۰b-ܲOå0z,6OD³q^GAD"3ħdp /M , ﾿2<0KD#,,R; XDdHIDAT-F{?O8T#p4 aieVK*B{4!LOOs<TbSpt.&Kәpp=ׇGf 8?`b^&#z]{e-Sk)/ے%Cld.fm0Ul24Cy]Q)E18c4v?Z; 4]Uf oKpnd5;y7GX7޹//wߕW3'(B=Q'gp8؆::X=s%Yj1cli& ]*ig FưVV!iA O<^y$mxNO_YP5'L l\y췬 2l9&_E%$T3ɇ]ܶK)|d=dP:;״Mٱc.@}Dkx"[ +N? BEICie%V%"-.`iBȲi {MZ]GHD?c0"S/0 |q ?fDYLJjvfիS1F\hKVQhe &:[_4.='v\a_m; n;LK:"|^HvZ* ^b2O_ *l;2#a^fyvWFC۰(PغFl GKMX~;_6 Ϣc*$͹k O(ß}vtaZ &b|d S}9*y"C b47ũi0̼) bȆ!q&kE*QddM>&F'!wtƅsf!᛫f$RfW͠/\ů2)˦Sc]U\!D6(X= àֺz0(YX8<33=?"ȧTٍʅ$3<'ˢ˼֚Қ$f-[W=FѹJ0>7O'ca};LѤ%骦Re1E~w`/d:irUN)Vu?LUؐe"s۪6DⰶVn+' 8&kT;=BkcڛR9Ng;Y[bbtCaR2Rpu)Z Wg7bfR $* XdǾ퇿̷RLw7ًQq,d+v09"yN*ZDPPj1ݹ$XZ֟/ݙ&&T醦Ǵe^L49+`c|4!L6Ȋ%Xv_8I݆]XNEˬg|EE{mS$ WNm(V)lA'rȫ.h1=є4IT+͋.=$:QLam[0m?jq`3yH7za)Lw041MS9XA1@ۘ4BN{'>i߂X_wc7жSր_2IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image10.png000077500000000000000000000011501423156660700227530ustar00rootroot00000000000000PNG  IHDRf:%PLTEٟIDAThKN0б!G(g`džp,v177!7h$]cɢTcǟ<@ @(NF߶L򪇺c/H^NAW8#&5Z(hW^ hA 邭AP׏3P@To¤Ve-!PZY*J)`sQ[ mE04kUtCYs@܂GD M?FՄh2;Y/]N60H N-` $Fz=-pT&7!j8zW FOz^f+d6`9PhFAz}^'Y@d=l [Di#|C=o^тOA5=q>Ro xyh@|'8AaA@q#\`Zp5ىMaa Dhb}@..l`~w%@Y tIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image11.png000077500000000000000000001751211423156660700227660ustar00rootroot00000000000000PNG  IHDR?1bKGD pHYs  tIME/&* IDATxڬ֎,Xs}y57:mH|̏R}FT8l$\?o-q?8+| w{Yq_}lysvÌwrMp!k[T`,-Nb90ۀN%._[s}S/˛v@N0*/}m\7_W@DիZU_ (( 9ڝBxW+AWou/>eJҺ/]I.M?~v.VCysۿUff/UDUo~7Ro5(L`-\vKcy ,E {mmt@D Iǐ`JF0'|% ѝ)Bj.A=8z.d dU#2H^fխlUWUeYՊUUU,t"莈#™`Jd=Sa̪ g6j^uu!nhn& fM&յpd&sDu +6ac%AD!ʤ& $PͨDJQBZ*(QlkHEIX `'uXyg+2Hioo~#"T}{{~6I7n- U- ׵ˎ6n>mo8BBn:[JŞ/W`??'+r/XM}u'^]_]^Q8#2Lz1>!n }_w Ǽ___S$6_q+$&^ei<zP5K2!(֦foci0n>3cSs(tqgO}yϿYd1|z%}ai_8xūDE]/_XnRw>ɇg%B8]6h]ggT <&F4I꒴&E[n̪)"qştme&WUVmPImԥ KXbQOQ"Y{Q3ɐK+ZAvOO/M,E7@ėYy2DX`/fItDEL.8za*8#3DEUU 0iZ 8D3"l-m`4 c3RQh.ϟPM~ӿץxaTiyeT3C#>";S'ꈊUU(D^W$;v._S&6^U_y?)a8zjL6ʨ: ]0]b|dG fm !2&HDueI/~q"z۹rK4'ÐSvwvۥK1/h -b/b ~q៼eF>' 15*^їt3|5y)G L1>K;S^/4k_zˮjƂXyg% ]S-k&?ʊ#qfd$L08QH÷zߏ^ck5QQV0!4]rpqz :γ2~&<%ZsOM᠜ qF(0Be4AN bҾb&1yNԇfpgN `&3uPLd3ӘdcdNj(KX^.PhЯ#M$Qą˥1 {O&5:c}_k$Ƙ" /uS.i5z>)ű4ݠؚYO~}LbUWU]D.ZvD*uȨ8yq,16 ?;Ū㬈HtVvTcZ.,Ufՙ݆mK!IiΈ^P`l=5aK*y1?x*4v.Uҥ{eqINΩ6wVw×s_.长rwWFU5We?yhTOԻrR 8q)Ёb?}~@}}E.cxxC1⯑nWI!Qp_$H^ҪbT!i8mwn^DqGBHȊYA p!p###|8kTV՝eY' C܄! ]!c PCfǰ1쓢%g`ܗ6,ȆM8hH5DPzԜl6'7ޮɯwRNd]R¥C|3\Ole.4;,V ZEsT*jّKejiȈ@D3#U(qDB[ckEdXňm7`Ԭx=C[J#Q8]MgGzlDqh ,gY{K8;0(Ty#HpouD̶ VHKrs+}4CW\BTCMR|a{{WyYvɓ~kJP_<O-L^DshzE?z/^zb^e6C~Rpm)w@DE,>c%z; tOγ3ia[hGqqGiö ˙܀ jD਎#qǙUgԁr;bjVȊٴ .+ȳnafp ‰ﳇ36\C"JdSyo jwtkoQ٧c7<|jl*azMtWU[jKe"mWLBǪjU) rjsr[^uiwU9s=}\H,6{fKmOe$[U'fT̎b s{BuU=I<$ $TfAgqY߻Q;qf@wGyd}:޶fI=3&!3*ήhYTgVD<+nh *jAn^5Y;LNɩjo*GA04pPȈї!3Y>듮%jE#3]W_6W\>N_ Mk5J_֍֥h bO.0^Ɇf@ؙf%YK3qI^|A?9r33/U.TL}q<{\3Tzc ψyQY*6߶Y VUg_*cc!4xvUzSMHtU lTDUG"b68Spʎy+6ɨjD`Cf7W.``p\+KozV@P63]$3-4!b_.TcmQ9.H%]/Rr84JEW ? \P j.bIVeww6C&i\Qʕ윓&?1A>"E9bF-.ٟ.ۜU8~{U'0,`;1PǙq?aF7U 8%;ѝq>¶3Q'g5WZ&P;Е ˄2NDZ]rwI*Q;\pBn^a.3A,+ SinrR?Yk}Üt~0o|/˳O'WV&]g+˗nWJO׷Nt~bts/~1`*@ l/k {XwNt Ք̽#뀶 7^*츏3A B@d(n!mx?Q7Ӷo^]0L= :`rb{gyDD\œo gD2lLY )h\u@ iU&]ya6|dىkMCr-XRēҡu1s| İ RLOvuFLe9^z8h]9`SfԜw[a蔘Lz-ꬤSSUlog"fTwEf<8gYQ i63߳8 mNہ8⎸3j۵}Ff ٕS8Lbw&;˺rA TH.߬wnNoBo\epڪauL# ufry{r"DvAivq|!FDe}('T%*7sQr3_-oԮFf*~h9yk2 К5?rkۺĩ0C벿Pye/gݵN+MI ,:(t=I/AuYW' ?=<ıoY:x#<8\Q1""JCmV>@YmSrIzh3^V0Lnr!7S\>̜,Q[ۀ\cmWU~`#eun+i\RX}!#NMMyVEbVws5A ˇw =DWf =U=H6!ΦOo9)< uSܮ9A/%KgCK~)jD۞kZSEL֊DEuYbwIڭ7 ygZ X !V]qxD @%Vv7MucX|qQ8Nz+s [bSFцV[n,wвy2O;lH4qi,&Qc&CŪj1nR .ln#_*sݚj(iYm`".( WuDFf Z\$}l$ׂY1 [ u)VRdwtM۶0^T@v7U IDAT0g/ko<&>ӗįgmhWQ5;"Gڿ.nqq_RH {A"C?x|u$` Vy_߿?|mcv 3u:ZgtrKp621 WS9iȋeIo#05{Ы7 Gd]>yp! P\p7wY(9 t̾Zs@FEhx쩠[[!Fge d5| 6\U"{C. 4b}b֜cl滃mPM!n?_YIMO/ZŋCjN6 ]Nc=8"b l,;ʸ?+UY}~st<g!8ڍ2+Z8n yuVdg(6#U=EmlȬ8OhG81ot3\l4j"\?m͸33ŵU˓(&1tl V,+q jN2 -oafi>ƾm4T-<2>f#w1tqm-X(Wuү,4L:^_^߼* \Oa{j(n\V-ϩpprtGl߷0=@':MچJ.͸ΌZcsyW399kDmQnYM~|$mFk5dj}}5KZ\~y^G[-׿HfٹVGO7b9 2|<:9,4<@8d&J>~;en8>ϣJVADNDMiM%2;GI#8\fmww"*&69Flyy_Ch׼_m8帟SUD~Ͽ}RHc@4b۴ixY#ʅ)pi<Ҏ[{I͙stl8Qfn7VS8p ΈyFd-TRqpٍ kglB/LJQMˋ}:Ό BvEהLL6˭jsEin< H5ڷQuU3о2~@pBc@ooF`氛ڛFsw-rB&squFƜ+7+~Y7_̗+"L/Cy߲ZkYd-5Qdg:8+aawߏq\6ܬ.XL|% V$#*Xֻo&qqGLD;V 9܌**giJ{6\,DTAg:*q;o4j"Ee)HrBՈ‰9'1nm}s7 yZn&m۶Mو^,1Ȇx4nooC$˧J+2k񳋎QvCQM\2?@6-c5繧;EgeUocN QQ6 mfhwUeegWdd!6y~u$a'>< J)*VEz;UB"l-]j cwwJ=-q5̽5ָj]Zp=*5&[JY]im+:pmS`7]c(7J~tM.fg7۶ooᄍ9V!ۅJSRm~{MYuSk b8= IKo5z Eo\̟-^Ն:I"h{DQ ,_2)=p`x8 gs*[ɇ{%"8Crg%+ñmncb`z`o7v4]GU܏> W*"r*dg7qrUg/ި9r69ʺ,WDm{G(?Maİ6oO!|aPjo-a1vO)?vذQdo6DQ={a4HE$9KZ/<'|fϴ՟_K_W㟊!nG |jWiO=s(6 e*YD]s۷w{DD [a8Tp wVUGlt4) }m+|x4K",pճ0ߟlqΦתNQlralCo64̫wa> 7ͩ whch\7dNlbnJu l%5^t_%lyZr1XLK!EsJOq$)keJ7W?^z˫ܶ{T~?>@B: rt>{5g} w?8hj7޾Ș >Lo=$}߶m'v9Ll9~auݷmm~>D{, tM}]tv]-L7mۿ{w"wt}0tX;vYp '|If%{}JIp(g~<v=H n׳xqO3Kuˏ2"Q1e2gEB\ (3藾[h摅S6fÙoo@.2Q=l}7h>oۻm{,k]g$ nmv󂒢o[}m×l\p9t>*:EavϧT̾W{5ZJqDTB nooooo&UFpo>)6I>YN׸ 6} Ђ}Z|RG01>_S衹৩ZSp|ijiqM{ن3m,TM1O}=P~^sV cO'Uc۴o~aoq`k/)UGAgSΥ m{N=-K]fMfGwyc۰ݥN8zǭee|s}pyG *iPaC46 NRy;nN&aw-nLͮ 9z9kh$փ1?))`ۧPyAv _:\_W߭ B_a_H9E P^Z>*܇Ҧnov{"ߤxu&רM.BÙ*nԪ}ΰBͶȕlmN*2oU7S"HFh8Ug ٶ=+SkTZ jTg)e[ܐ3TH1%5P6>mN(94\YͪY@} y1[hZ{/*3M1_|_#1>xr;[{`{| /m~@hl zl" o7]U65ojMU5]f&Gt%$o9mLĪ9U]$íBUEYsE aTo ?neo67Ԉht>V_^s.IIX.fD,AA U<͹VyfrfeeY!ŋs};)sg{IV$;Mi/Wd]K_O>3 v2 U ym!ʪ:5YgO:4cRRΨДi5bj󩳰z@I)$*=g ŊA9u'wwO Bϑü@D=cxv>r~3u#S'l=*>1 饸x=v_ÿh =X6T5GΐKo+ɠgBl%SVa&k75@4@Ug@IΚ3i^-3)ShQ`If/GfVe5fD2e 9Q馍wa0Ge2i[ݛQÈXz'ԋ(R5|Q}&}8} B<۶/ӆ;yCh_o*s.F%Glt~$EĚ0lW;}9+IY4CC*X:IHh*h̍:ޢ=݌V pr'7OF1 %׮l%f'݆t[q6Ye~a>:+augm @:w^3~o=1>vL *f]bM`,u9 k̻E#mF1km[Ni:aݵλ|jez=`CHv~M|X~'Wm> !W3TX!{У9#tX۱lYz30zqΆOQzHg-ȃU8Cָľert3 MY^LL47pȣ5s(tp.[b_kc7&9_K_Wx_Y~ɼ8]/~zs[lni'!Bw䮈`tU)l;5]R>Da9p!g3.v'^0>KG]蔼~lx?}2 ?_$8>7 +ɉ?:iPuցpSud;z`gY}6 \GQ*Еt`= }-wӭ?^' AHa3 Pr'~X WTGSݝKgU|yֲΰ}w\x?ΕJId<jC5iOťP(9ML xKn x[|ș0N/x?LS!E[;6z뺑y%IM/X`UϿO}~? 6}FG^TxBY 99@С$w?Nzܢbw.0ܴŧ PD'}Dlj:`c@ll;:M3g /mmk{j^6vpУa J޹J32s„Fc~[?W&g^`H;pITw~LvM-++ܺ)yp`xǛX)3Çyd>O_?3XJa2t{mv,0V$yZtgVx!uxǟ3~xl8OkQx@┕4x'1o>kAY/l5F|->~v{@_Ϯ&2áX>+XY^?3 Q5w5]x~_[ٍÑ~W+ߠm|?(aܱVqMR7Y;0s3-ZPa%pG{YnUJKL-KS8U_vAݨ,$(k͖b7C54pD ΄Zkǣ-v[)cNa),0!_PMmx8uzu?ex*="T80QOVhU:+SP>DwNbR,i -L<pf㙐A#MF,Ujj !I24 "ZXm 1࢘'\.o<bü̗oYsl(JD(ͭٲX)W~Dx7Pff5=cI 2/ƑqDۺ7'rmGЫ M4nyEa<6G**=bD Yf4-wCZv(E,wBS=ƿfAv[nV̊Q9fZ)~_nߗR5},I3oj屔%BG-Ѯ@6`rFcXk)K"Q27r)ݼy'aY]TAy,u=1|N]*7o~f*7cN ֯qgkEOUDhL"E5 D!A^ه,@BWFrmy@122<`-bš/ToS.Lf܋(u){+?j?074RwnM<~[*NPѬZ/҄BMYy]PL",%O../)&A֎ۊ9sw'Tb܈"M.@=*XnYĻ17}^\,fGl>'F~;8 do btWN9gc&`Xk$cY.E3s4kŖbK\gm+(#ܽ[K-mM/ "7iuFhh料UC%q΢đ0M-7--{R& &9cNȉY[[DC `|1'p8P"OI›zܯp<yhOOvaVCW@O'ˈxYܯ.s )z>EL~R83 IpG+V֖{[j)nY|fR?v{IugU-jk~{q?#R Fh}}<\ik:>rn7@w'b5 V=t܊~[s|3Q;y,eǭSnФ4Zhڲ,ˏ[歶fZW,ojK].hD`@3<- pؚ`8'qi!$)͗yQ "֣gT0&>9nј9'-ͼYR4yd9ax\C<:H8d _|wTXߓCwupwoށ%+A!^Vy>`"WKbO`%Qo۲,wkA,dp4jHKio?ߗ)e &<%3Q[a'Hg-?HQn?pi͊[-7?y>B@Aĉ%8A4Q$HHhVQUW }Yį ĺ2c ؑpk#~̌83ׅKV5g&ʪ &$p is8(/o`*l;AL Dn7=lAVg+ք%B `.SwIE%i.F^ f, BN’.Yg%`UsG%>q/SmȾ g>lhV,f?ޜ}WBw谎n x;Ìl|Я}7L!1 )鯭ң|]2a[Ļ4AiUuVt6H݋Lsœ\2 0k|Sj*ͣxrojú#RHT=CZbVt>Rќ)]Ӥ%}HVJD)q;'BUլZ[mn~|q_osqu8:`Jm⥺wGڢ:qx3A\z#oѮONK}uЛ~c˭u,#xAsxGI;|=r?roYD 4RV [xlh1J-nYxwm3aM@?r#nކg2'ՙYkK]"P}ydLTE{ItJ:gu6@G)rr徔9<4x^)f!-0P-ӕg3BB4f>*O54jKV[}VahՓE?GCYmY|HqrHHAi"XI lkuvN ^9EŒ:}hZXEc"k%N@F}wTAji5 Ns]5ᚐ_~Vuuf[xҬT[ˏۏDy@xzs[Pm8do^TTD@Լ5+$)˔TY;Jqr"sw3kcc|Z[kYsg*"|x{+XV4.jCknr`GDao72lݡ]@d&-·{&u ϳ8xovQj%(RjYh(ͺKr<غ3o~u1&D(E4a$ %΄D9,$)YѝOk'jΥciQ a*gƔdSJI%PtZ9L5o69jN FOdD Nc3Ro,eV>Zm%91%b a Q$ ڱla*S F0QR$\&yLFC+͓x4$LΦ:G<](ǵG.C9{3smg_tIp gK*qYښUuee0A/5(v wW^"/hץ#͕YZn $u)mYj-Yrqw2)O"wXhD$gP7>_mrvB=ƾo.G-9Y.)eWDI( %p95u 7HB/]RK6J~!e~G%=v^h>;Z*VC%7$V{`V]L\ΒDLj;/HcL_mdP'G m9Jnfnʎ "aVJa5+% Z $ VVv$D+k狦6-=mVʚ$:IsoaD&eeQyqG<=yѢ5̪ASRS4:OsVUYYZ#J%$-ءXx<_RYu`z _-usF:~Om60% ?1vL`Nk?jk—$1`Y3JS!~?G~'B XL 'rlU7D,AA իٕHtII@/|;.݆ktwv )iRC$I'e@4 ^Bu0@7] ܁z8z?soވ8iIu\qU HXYIC]o:L_Yغ63y\{ed7  Ue4)ĪB&`!fI$c;o$*@'^Wv/ߩ`K-D-&ÉESiZ?j <%e!!ػJۆD &"&Bf/3xUZWgr)<3cƒwa,J!ʚR鯏\tDDIJ1pp`Z.SC(^)膿6hW悔W~.o> ߖ*nCS\eNsR ͚tvs P,Kp7p}(+HZx [cU~;KVaLY%OsTaQ-^;94zMڵrnN)2`&$<3 j7y]>W;~ne3pEt۝Fn6$NCQW_L7+7k#: ~39V6&& IDAT~d=04${ 0o,9T%%FJ@&]s> Z:;v { '7a<"TkC0&r|ye>`aURif֬yt]8Xpf^ ; VGD|j5:ժ8?tq|i'6aٚ 6YO^Yxyk-,RCzfH6)O1{ dt*}6 r^?f|6uuΚU!,pW~S,` ׸նbRq =g>MgvX D ZVY…\Lnެ,eeJ Q$,ĭZ-V5󍩺Ke.OkʘL/mR^  Z%L$tu[8M2͙RR0<Dt洜tƻ閍aN#w"5͎=i_u.t9u=e)-obQe@pxGchV֒ZKxxb83-9<k&뉈j%j 4 !,J^̭X}<G}<ǭXI.o_Ϟ 2* v[EshGZU H1b>QK5R-ZC4{3`5E9Z[)V*rSDkY(+EXX UΙX^9j-hV = &Mi֬,UGx>:S#/SޯANM6?֜״Oߍuʚ2_)O2.=8&z Ǻ',@*'NwOWV*Ӕ.oIRap` RFVєJ s'TzI҅+x+FQts{֍InS&5w;sR`$(A5PZ YZj&B*DXʣ<̉4%P@HdJ2_ ]셞~ hY;aBӝA(8G9l߿Ch}TdmP̘Mԇދ 'C<^ݪm-5s!ܽ"rEJ2翿51+IK\Ҝ4 3cD)`2B$z~!Sj>r&ɢ,g\He 7!IB 1IMhjrÝ3$#e&s4ϬJW E`3}{O&MWz}e)laIOc*<ôWԜU0($Ige 9*kŬ"jA?{2nhH,jh|[I`2RCH91{w]Y@84];UTLa&4 N`yMD2_.fEY(^iΚkf`$P(?u\Yґwy0LĪ`BYuff'CHS1rd3 (Bwc 9@x@鄕!!q?Z.Eg./H|wy9P"X#Z/ӊBw>&U]+R3$V𼓜>.)H)a:H*1g,^mַޚsF,2 ̑"'䦘-}v2'o?PwP=d.@4Q $\iSvL@ÐzxL`O}=Gә\?L `1lXI` 8iX4QjZA\^uU!~ B I&L@ "ta%֢b$#z!!0`D2#4LhZQ$/ZSZrQIBFFИz&y;)(MOqkٸx'ә[{2i`{ erlS3w GA=<8!!$i!AZ3[7s.JA9L6eY3!F fQQ\}<)1T'=qߘzG;RKr)ru~jP>|R*EUE.{d.+x"Ago!t?^|^ٯg&HH?ftN/z:T 1 ;z:& ~1fN |Z{vh&hnte6w T1r dK{߷{oDE2iP"AL%&CtlYg9w}+M!P0YLSLV: 2LP)uaJOPu珟VaE0!cH=YDKF R,0v{nzMQ$Y313߽n9Zݢ%Te5$g`@OgΚ[ &BeYWi(!?@'PcxeM1Hy }§?z>fWzt$+y9F{!|5;K!"OS.dt sJəHdAbޚʚ3ZtQp``u,$,, Q`J*w|aGYk~}Z\$RТkPD9q HDo͚X 7wH6'TXU_s_@BרLVmm iz&،nL pURZWJQpgkހO@CQϞOx؁'!\<ې{momff6M9p}muj [ yqf tGL׋{ҨG3c j4]lZH9)XF ($l5Иv;$ @D#)ʖʅQ¼*! q3l> ƥwï;j< IC#{6{ԥ.Z WUV%>7O ǖ9ܐanm[z$fZETPt̊#NO3߷v}Ͷ۽?!33&?)BAY%@^gk~'B1r,WL~b<ʄ"aٺWV)*17Yxi)1yxQTa f`;YbxyRʺRYkEw,`0 ͺ>cQ+JY8-RwdR I>&cr2'2L%Ke]UEYihƯt+n(zQ=ܬ۶mm[{ip,3o6giv{~ovom&}r~&qK<D9s/@10V,>7qrg9>)BJw"mV\D׊޼GCcyZ` t<-XDIUk 澬H#Gt,C}##֍%7c.J$*:򛙒ay;^l_N\ Yyv߷{5w'bjY3KQI۾o|Z=n|>s~֞>7AqhQ pQs;둞yr$8/%R 6Mm˺^Vq"+XSjfbRDEc m,6oVq- ȒÏ̳(@"Zl3FӺG]DVEcx0XaDU@r0 XbFCzB(x&a~hm";1K|(;/7Oѯ>uP;ﷷ۶ٶ۾[Z0qJp3mo6בHw׍393!(q:O#HA]=FϙC~?՘ L* =gB,H޷;,)T324@ Zm({R8[L!#H O/%weKU}es%ǧ3LE(Y9"=f_".$3 D xw-}kޒk]X I#Z |՜z-"##u}۶}{o7ݶ{3x4 `Y 0Ҹ[H $$">gr4gLWԆ|5ɸ(L9|Kj|7*:)g7O Edus݃,)7!jN0̎t{L{|d-6s3F8*ZE WeeP )I͜Z}SlOu-ZlH 8ҏsNLYT%i7` H7f`UÓx@ѡ'ӐGmCvo7{wx ,PF@X5kcҡbaxnx b:i Cs*u?Gʢ,>%(\ZU(`0OJALD-6f6&;/.ehZ\/?~uUiQ9I0$.kŊ:j;upٽvk6frNq:=cCG݉tFB Or]ǥ*uRC4L#.,YYPݱYvfz,….k]j,L#kclEhr!#̓Čd|4=gi#`܃-LХAD JAr:q&!@@e\jKH"Z+cAb!l[ {o߷{mn-Z3cH#4W12w'G(w8 Q˧Uyyz?g'ïXy|: mf# t.IIUmeן??\aa1SknFkh쎻fvk֮kԕKZ׵=zPȆI3)\EZ=iePiYwmӟ0'5/Z +kGLt! ,R֪jCQ)̴3ak1ѭmo~|7B!lDiJxF6BD0F0 e0߬P}9Ӳv4wvWRo,+ɇL/7Q&s7NUb?*Ja07BEJ49F¶}4AZ.֥h!!f! R%8խ#C0-90Qkm[{ﹷ#IX.E*Pk] 4q)Ӹ=u5#Z1szs#V ppFd=CaP(!OFަ3)#+{|:8EW <fF2+!e)ڒP]Vj[~Es?#dJ[˽NU^..u )eKA6/G6 ^3!DRֲk %hm۷7{{o޷C.*EV9}, 8kO40 jX?㈥ua&eH+W0k0fɖn0"(T,Ly@F R'y?Ff\ӿX_(d<=Di_{h1V$,!C Le]+[m!5zE+*Qz:$TaHe"kE2TQIwYmoo{5sDffv3띉Ţr?u~Y■=us1@#I^.T3KS8h=#(B:!$(q@ "L@Jqz)Wt1R"従P8"gDJP$ \1!Z4@٘"Ic_l_zh>?[{ˎDd;#Hyw3xA k1e+c]Zp0_WEIUtVԅ#ZEU.?z)k$yv a`lffm߶޺flt|h"&2gEY~wޛ{%3a 03  9!J W:3}%=ǽ\,))H_D1 O!_FbVIC ktQfFL/$ `NAu@S4 TΞW 8![ez&1~uN?|&)ȱ{N9\Kx - 1ZWpq!hPuq]W2Rr*Q!L;zjs0:sj2ͽP׿~w[ߛԃ,ͣ#2:I){kwAVDַmm۶[wLᢵT%-BTCy A|޶m[wl4.@Z 1fm߂Zo!)=1O0OIå&`eՃy~yUA/Iߞ!Geս0ʓ1'g\a`"튿2|YdٜዛkI1vɪA\ O`=Y d0S:" $5,lR6J-ɑ|Ƕ۶mv0{S*=*%B˕\Dڶhmmo} 0++˵?u] Hؙ|!d*p,۞tϑsla0 3*)c˓uc%q]ͻl#w-xZhI>\A PS_@>h3''X>}..+>H z ,>#~ux9?HĆgz̭"Ճ" TNTJյ2a灠s@Foby{mmo-UZuf 7d(ɥ\,UUVUKE[߷|mn ZMzyãu4!=2ZD+ӘzRڝ[I.VDjy=y:s/0G/!Li,"E`LH953."~, br&fx%z٧VfΑa  $ g; 8ȣ43)LIiZ+C@Y rBwom/"2"]B"-"gȈaC=L[> H?\Vio~Y﷽m=݅fԥ??\U Ľz&bpOFat"sJ?%Ϊ-U>8>Dvlgӏ5#e9+ D14`$XS GׁCC3iJ1&'@!>Ȫnt:H_-0){p9Ѱ< }K̠o e TlzkU>`"aRDȬ@͑c9{žX1QJ-H)hnh-Ju??kJvo۾߷}|0Le]z],\)"8S}P3[Mc:)C`VZYوa'Y&ؠk(É>hys;IW~_ K1B-€k=:3yz|3+Γbd | UXD' KE}rȩA:T_L)!1`6 Q<DHg;>zAP!NurzY/;#^`uJ˙gCg_y^xf em̬aXn[˶֢?\U ""u^f=ۿ-[nCi{G$Q )Lʌf=GŨKѪEY T^Z8DٶZHG^ a?hATqp1RY4!h]-3RTK-\D2r׏NlAlTp!Qe8c娚- 0Xā9" h$уYAO3bڴ| ]G>s'jesTw׍0B0ш8U*z#$̭~osxv?PXTWUԄFYmns" TAJRXU@)L$*ZfZV{۶m pjK }Dh88(f(UTy,bcPxE?t RN';N @:ۚ=&9X4UTUO#Ӭ[8\{ kcyeiUyF7BL1AJP$<^$~LR΀ڑ13Ž2#2\ eUA 3=T ,#2sm{gR B$aQbsrW"J PlZkf{n7j{%ndRs݋f29%:11fmdb^EG_+We|!K}zRQ= /Id%r !ͼE7kfo7ksJZX%:WUUa~~o{FRP.1 a$AĢsKRѰs)}]{v޺2({n[ fݣ;܁ǠannZXG@1|rx %ʪR]A>\` 'X?Y h<*Abg4Ef =zDQKAIHuh>'10]~ќ3Gy|fXX4{,d%AY˯auͷ5,Y RJQEP'1+ 1 'BY[xM`ڥx.to* J7$ɇ\JBzzEEpݺ5mph$SӣL/fokľC>Cc$I3omvJx@KN!8 {g-7s*Ns$ W<9͟Ch%` j)U m~*\/5 6EFR/3 _4Y%e\!SW ›f̪;>z1@XXf=! KE+R"f0$`clxV-U7}aO>>^@yZ;Ȉb%NJH)eY벰h$!P4ֶ͚2_~@v @D=NiIJ˪T/PIebU_. )%ո7 m(L]HG kq|!F`mw{k{8sV2CDJQQ m|vkm2 Hxf2y&OIf n e ٜi'q@I6Og<|4D˹RywJ _:1-zvw RZUK3˙&c"eHc@Qelgڀ +@#_SEP?OAcżCZa;z~GuYUUUsDUӉ=ff1^GRyDEHYbJZG5 %mvo7JZjsZZ+XH@Ɍrw9#oL Kެo[>tO-,Zgի^UjRBw0ֵ _R׵:~$||r;+P4c1qT>Mq3iAcުB﫱 |c4&KYPm37C1=1jGXԼcыx٘` {8%uVtAY p]?qK(%ݶGeAKTr, Hmۗl}3ߘ͉ToweFiI6-Ji}t-c"26ƶ/;~ga@z43@UCItEϯ>FVzn*w 0}[kOss]`oBtP6!+c%>KgxF;4 jP;s!DYP`۴ٚoAH`Q]DplcϯmfA ά2=uf,"™p޷m>z7׶_n9}-"612 iHfmY?+r=5ztGûEXf }h e"ڤ OP=e\Qbȇ`=}*<m*D %A%ajUt۰/<&  28k T@i}bW!Ih5Pd ,J; d̺xߘ2¿ 4&Z Q0 eLK$0k9G$Dq/ʬ|ޘ[pz@"f˰nFN7Ͼ~Q HR0TͰy[T=zW&GFxdOϧe_=ْ=~leFv#Ɔf7xp&'Ἥq#) fwa.jISh/<^gdta:o0R84Ac :FB!&ѠKnAC~l|_@~oL#P8J*4ʬY`'%&/ @l=qW555ei7X.RD>n6$u}nmD{ ;'ĠBXZ[TC?f)h n ݴվl&!4mdH wkP ldК\Ƕr>Ҟkx ͆W!ZKD0+.-&'Ӯ($AXD?w&S#'A!^v=< 4!=Q1#)blyȿ>6n*!" (^nt%>eG~!Lx&*Ҡ}3('aac2lx!XDI eO$N&m,dL>6Fxd"33(Kp@X7G2 76̆w"VDB{A[æ+4U"FxtC a/L$F|(|U9{( W6]n7D>fq[--,ZgiT cf`L5$4A []ϓF F)0|xv-I[[EL]iMY(dȥ|}5$z2dn7T03g>w#-lq:<.642G,c,WG%τ۶@z־m6}3Q!CHz[uL p2e^1vt(>MQnۮJ]QlZ,,$h/ j$9D 9;&zL<<9!MnvߖҔb&%&LQ9UiŬA&`twWO)$Hw1zmɹۢME@Bp`X$i9;˓?܃)5ycmMak$e7? ?c.l4 <2)2EcOp3_7[ (pO/[y͞ep$F""HGFAH}x 6fma"V0EA2Hhك)8I9TAD$$U5Ʒ&ݵj-BǼ&eUUuo2h?ۍ?[.L|Tbܠ58IIDZN<x'#5 mwݕ2R#~R=҅ޣ‡-IcV aIk&y _#P~xW8HZ(3b}ux2:ǚ7T;XlP/S"^ȕQF|JKSxzɐS ~vӏo{Mk^-o{?$ cg@Ѿ zk\i(-z_辠5QsI K6!h_>zn 0zX;u\3exq,@p8l-1̡ov?,-pWJ@bvyǞ UGU󷦝N}GusݪOWzb`C_zaE6<nKJ}`+%I sey?YKkLs%41#,R(s,!ges^8N##'<2SD"F3)ĶayT13/ LA$|` bQyr07siUM nȥLǓLL$\ "dŦҚT䏈03# &M(YDSf%1Μܪ/~~:Ȕi{@$$$3&9$QO{k2@6A1 >}@0L efmF$Qf-snA-wå,}9/PO˻`}H#. HwaoL!8+?^7jvM_}O;gǡy(UR?o0-M^u3\oz9l-~T}wDL]ʟ_35ǮIgޓ9#Fyor- @\gd&5Mo,ܖEmR.~j| H9DĜ,ޖ;Kn2P^J‰pD@BN)H r7զLh6D_"Ԥ5ց10Duif*дi&MX7-BU[S5 tMr$p"j.T'11$P B؅57RF))SJhDLʩ:}?S^s#.g_rS.8_EW!v1%]ʙb"~[}ZP%IqHS]TfJs'W6%M9fZY>Y{_KtOR9xT=4e a5P?sFo|}_3zS'L Ɠ4Fôʔ}?}n?YzϮ sw'@Q=FTB *@y "J+ֆ0ʐ2I2eEڱj&N4뼚 }džn2ښ)G@QvG7& 7 s=(Ex^RN- N0!c(J+vr}~I!D4MFF."踭HYȉs69 NsM}_eg{c3il.bb 7ޝy 5Ĉtirl8Ճԭfw<]Y00/˶M?+OCϊyfiݗdɛCuϾ:7׾ޛ|C"QbuBm]LJP { 54#5c̥[xl~/O?ļJ;P>11@aOJ-*_sԣʝt=Ky" S1^̜--3=JN }ZI ω{ Ҽgϸ%8`%IVqW37S?I QMnyyuzL~c~p3'nHUei}}:sȫջ\k?NaOW>񇙴TxR]#ܸ=BD B< EʷX.l2(mrě?DR Htk B k²N403K~)x.b-מLN'b~p <9]Έ5P͖=t]3O pEN/NN +N8|IXlԷuHω+%}A[dx~>s0J}\=O'"RYԳQM Vf,^,1KXM~G=w(?VT6t}H M?7Ox5 p>d8gl2-q~ "L  =`NrZ(п?'<|Go??Xb=ٔI""v11eE %moWO>',yCb3h Ō0"'>}]ډ9Oav}g%htFԚu"I^;|9:ߏyg\1̗!I|^.{gĴ3^L}ZGB;-?~9rsF Gx_H*KH8=zBeOwmKT5\߁ )Uq ^{zl0ϼ*?\sZ~p}z}mm@kMߐ&[`8}Jd;wettEf|ʃ)EZmk}ǵڱ>)18N1Lr3<ڴY<89,Djd8wIA` 8t*a ?^:%_6w8Ov'Fx/:u3לuIyΜ@,0 :_BN^[^](3ExWkνt)"d>)PYF&Q:{^y3R-8jhwϢsG/Z Ӿّ5'˒zJ0ŎQ;: ̃32w/e6b7vQasd[!%/s7Yq$ẇ\ӎ;`71Y9%}Rpqhϐt|;Sŧed7[P/}+ޛ4Vz|Q<}/}.O 8\P)ĮH Vl ~|hь\^[$Fr8r <7[`=zv Ïq׽'4ſ>݋`sn>~9ȪfyO.?{ϼf]SَٵOyO|*0Y^Wkfu9iD+8C898i??_bޝy Ca %_88d~erV\jΝ]`N/'Jјu;$c'>-ѮtA;?;kBŌ"c Qc]nt)p34Gjp3Qs^hK̙<=3NBq؛ djœccgB>*E\f/I}LW7LfĮ3~9ߠ@ɹ {^&8~mӞcU9mv<TB^=LVoD~fО3[y2_1{>՞Y@QB݁O<^;xx""# સz@XxY:͝und[a6-)NyuoHrOLs 7Q|j( IDATf`~L<9W籃gt߉ڸR6V2h-7{0فOsyG;3N6vP-j]e#zY(a‡*F{ .@DEÞM̾c׻u?%G{=LY2mM≍rLN ~?|1*z:NH\wSrg0IR::*T$ a&5.13}N?^Y4#%2Í鑑`/Q=d $IwXmmûG7aq2?:cg8M䊉ᆌ޷r ip[³D%9/|S>{uP{='C/x:h)M$j|Y{rTJ:Suhҳ?)C S+س8p꯷yLO8b8uGҭr>EEL۾. 0 ۺݺeЇ}"+&EK/"9̬-]r13GDc$Ma$K:qس/';%w8ƞ/d"P>foT&DȩِE!mV APX^LlI@g{[$J?L"\-GFa,IHN0O#g(E#rxpvvtS7̾*Y {1VaB)[š" mm~o$*IDyGN@r0C\{u/3TAdfkIyZb{kϱԻP|`UoILB|4Rg{DV2Fر=)^Qz]o0H ύמ_ՏSa ! |YC`jcE>{)wuXunn hIb/W#" a5ۺuBnX -l 4Yd=F+[hȌFD ,[kژ9u(B{j(/9?qߌi ]͝|\B),Ƌg$#cf9 1n9} M1̜De2e=>1kzGxZ2yEL/\pcaJiC4bnwݯw_s`KABG<Ȕ FX#1YSнah·ְ+l8+=H7۶k[ad>sDNBIȀ7%lE.ηoM+q$|Q%ޱ4GY5^[M)#G$`c?xXđ8{g0dĬ+7?,0L&@`nNaۆn`=mrRTO%Q۶[>֯,Ǭ@>5hg!cN̏`>,Kw f#ɳGT}zpcdH"MS)|?"HG[nmq_rSw;~ iY[Ҝ6ITLSUY$qfzcw_O[+0<#2D'Y_}rO{ot%<=3T)*`ϽgJ (ҟr]Rc_kzx>%eʅ%C(ERsl.dJI$є "3"J l,(!z^3\sх@P$L;=jȑ06# oA2B"T&FfToME&S.u?O]W7J*QnsgS{d" kT"Y O|p$D^VQmnf h?yƏONifݾ}m rDE3*FPY򎋟XS|`~8O‡_ " isGL˰*HQS#< 7A6m$eд~^_XT{[>`f6S؂iJ! t Sb 6mn5ix \t*Z Jml66}dYtQ(_lFz m~W̔RiOg?1q޺] jK_g(+l<>_$ XF`܄emD{nwb 7P1h?_ALFS)inևEۢ8΁W^у9*kfDEe0MJ`E[SH,) * xI[H痈rS"EtYh$N3{JB 2.¼nancX4,9kꀅYzl;‘`vjkRysvFFbuwIoe'LJ32ƴ}vhfk24.#P/E (Z'mۈPESK/3+uO}}}̋%(ތ,ٱ܁Z4qi+o (cwM*, eT,jd6N}m FJEDcS P\6V`}Z6hx3Ϥ0#ܬY8馁TcP@ޟ4T=&aT^f'bK0 790Sjݮ(9W%ie`fiM>%q{ sx8I=W:H#HfqfpI$޴@x"tXwHYR82l+{ +uXbYlʷei4GF:ٰAp7RfVQ,~!œdg&!4ٛ&Fa , UbҐwz Q)"^Ttg}ŃE@nR ˜vGk7 + a!tm$g&ܼ}~]"a#,0xS"8ݚ{`k>Ƞt q')03t6!;NΥu8J|8THo}o0j3mji|?jO"fCl->PxTKU3N0QK ?hS>Q߻6Ћ|Oe.J ]T#\1KxFtkBr#mie:ulExĠ4!4` ,KtO,&JM R]S3 NJ@vRYݹ54V؏ݡjJrq\><11Q2 مRjړ tI$"& "cF~D z5aH#`iaAe,R䄄R{y"%U'׌eal 6H2HHUnUU \ZBNG$ɧ{ϯOuKwOOw& UW8T3KyL%3ߡ*2_8HO@qTD:GdSHLdiv] ᔧKQ{IByx{%i͇ZZ WEw0X&9GXrV<͌jo I,FX 43$6喙%;EOfj6q^I컉MD=3\$ۖe P}DV>%悔oV7~X~Z|},IM~W*23B+.My#|v} v>E.r?=\g`m|<]Y͔|Wy~/견˒&u1p3S jڨ(9@-Lz__p,f-hc]O}Tlf wu汔dϯ:5};݀M3oT;BfkR.+gIU˺K搞EH8lbKyZeuRx7*DZ7i80D櫴R!aϨQCuU Nj,&%]_E BwQSg!Zƒa7`QkTJ*x5Qf / QTq(UnmXnet:ݡ꒿ Qa>_r^3-#4~%!j-?ꦤ,h“T[3P?ק*G]sYLUvt6\@(N>_F{|N+)ں~?=w{;PnwF2r#u! m>t-'9{#=bUUݫ%m@\SDv8xM?f|ҷ+1"owT2:YRDTWӖMUC MX JIm*0_{ӗMUi2Л"h Su5ץKe2^IDAT~78u//TsU\V }%C݈J?I{.\a]"v6ի`~ͮo* JҴQWCʂTx*Nt"Wo鶊ޥ~#U)꺬C}Q|VO:KKM֚RCjʩBjB !5j>ܗ!Fi^ka#EL}qT[uYR [Kq BjE$%RU>߻A/ ~3"^eȎFw?&>?\gl[p+^Um}I (l"fa"l9F6hDe]"E4= jffY}s⟟NWLM1 %ɧ,kc '+JtӖ,$.ACuuuՊD.ZCׇ*/Z*Tt 0T|'a˺zkpnTCP%@!!,2ֹy{E赕ڠ( a;Y;O*mR yz&QMus+o3-WMvrV|G(w~pW0NҁBݵPU/"8K@ڰT4adx$DE[һTw;sT#Rc"{1no]K]d ?ug H#%DnBa%[&YG)z *:ɤLA^od; tJF#`lwd?rc\[z(Rl<0+.)0S~)%`{ü5j~ErA" L4؆G{mbgw{=1h߱%B3{ҶƳ?̘Rr/p]m96.O㼗T# De8"qlQib0_B7n?#X$ݔxf@v`V8[Ÿ?s,=(9z,B9Ss,J.w>O|$z2XcQ \q{ND7[uc' p^ʩ&Oɹ$1֮keyP+YLݵ5cR*ڷ]sH56O=<vgM JXpѻ3RBy򮆄3.UVsg #r.y}3{vFfm_Rr~ׯsUwȵo'\KOfNqN;uQO=Oxro&Nٵd2wz=εIk4N11mݭm{nZ')u^1/ق!7N_"?{%4]hf,C:3G;l8@.gAl=۫A@&DŽ= 4[\&}[7ty(ߞQ38 ^/w;S,nўτ1o@FJp$AtGT>&*&Te#.eӪ:'6#_Oe&(b"lJy3+T-2t17f=Dh☶ qF'Ն{7CƗ~;RgS7ũ_ \DN]nw5F ;E/ڽ`U$ 옷Wk/4[v^&:GIG^AjaP-[Ho\,wJH/xnԝtZo{L~a&$>.b,!E~p279PW[iu=l>O}\z+d21nA{k @-~JÄHQ .nHJ0%xUĉ)f47EU(=7G7|X' }ʹEk)xl5g:υsMCOǺ[+M-'zt[˄ta5e?[N8t q8rϣ9&Yh@iM/h3,ɓ[-CZ˩ؗiZ8hQ9^uCA#eʧn' Ղ͛;q{f*%}s4b`f8$jB& Cq6EL>@f砈0Tv͹AuBdKTj-ZPeSk]knB֜c6덊cng*qB9rUlڿ?wzLkp I̕*b1/| 3*%9qM+N"Fh$!.~gCpHqUivg-( %g7Rƾ8NU NM] 9)cB{ /P>A!96q *~ڶ5mkˣHmhA2 #6ՌZ>gMDyfV %4vJ=[vk ]CIH-`NSgY4~ u qK 9-H]E}[fYISA!ML1Nj׸wԗ\;^)o Tpu/N'm{?|mҹ>mh'ܦ~&Y/}K (m>QK?L~<^ ?ǛӠCtTi'" գ v)jq}90XuCgPѡOi\nayvqlzR77#r>VKv6ᅰͥ/| lU t^fJ7u C<Ŭ+ybM4YxVG|/Î]g4>*M`cdG DcY\u0Ǵdx/6'GdT?N! c=̂ɱ4-&gJKZtߜ27Lng )BoϠgBH׎S77 ү7f9fUpƳObIK)6`GߑneBni\_7~M=`:'σ{TCG67],|!ZkzH plaþM]gђFP㔂 [fJ~/%wPp ֦QΔ ?7_7)MS"lݥp}Kb\XoH$\xTɞ'PE{Wu̵|_EEcutٹSU:կ_lzz Glj`[ jzT; UZ]qr8sn^۠NsIi>]VgTa!1PFasWE@0j35'fxy:5UHw "Q<<||zX{.rȐ60u] d5"i]CjDiISJUI0j,F-NzVQr= $Q)5DqƗ$5j3y5~Q3m8;*jZ׬ >1/2jvis ř6|R֣0xnƏөA 9"'B; 901ϫWH3,;p[~ƾ H-kJR$J?܂Zn!Z"ʧ^i}q Z=ZQlU$lBb}CX:U (ZGt"h.**b=^K4ȉF< ?vϡ V@ F|0# Zi,%T$VUՖpJN5 ̙nAD}LvTdC*z߯Oc 4?#jR}Tl1VD ]~nM%vM˩;TAh=/pE{~_=j_Mo`Hyi:IgOQq !PqƷ9 ]D-QD]T'<^ S<.RJe) DLpPXًC#jR7jKf6݇G)fx\w+w) ]W/ex4{j)0X䯴d.@fD n %:oWjƖYIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image12.png000077500000000000000000001010721423156660700227610ustar00rootroot00000000000000PNG  IHDRkXTPLTE;4975C88@@4@88KB78A5L>9BA8HK4C;=@K7?C;@@:O?x׮}L?]_裏>/GW_W˗\/~qW>Ǔ'}ȑ[n޺c˖[ŋz'x?x>?9|2y =~s~|~wӦ~w|翿uz;Mǟam< m?_ e|A7#l_z|bd͛7߶lڹ yk~ 8Ϗ c{h؀|7໴~z{G?O'~Ї?Їۿ}v[n^~=?>ad|fgg!sF& fv < ^Kr,vӧҹ`sOtm:t:c15Mz2iZT"a-FH,#h?n'7vھg׮~]/Ћc/z]?x :_?/Q?g͛sz2Ϫ=ɪlo@6k4G~fc9Zo9v>Y͝[;i}hl2iI]3t<7 o!d2ibzi?K[Oiiv#\]/|{׮_|v56!ڃ]޾u9x~~cMsX/qx|a jR3ad9$Nҹ3gN>yС>:q[! й{3ϼ:vdNxN"@2}<h4' $Iܧ,sQ oS/`^y^{G|X??~ut[g!s$k%!9H@A H47{3<;=۵-]wmO0֙3i{RY=HFh73Z&8d`vv'R`U>埾 _i^x)ڀPTW|+'nm-g3g[Kg.\]$}ݐ-]:OpswWN8s9W͏6A5HMk G"7eXH+ ~޻ V`S?`s7̋囇ˋ+7ofd,f`3Nk鱓m"q'kN;9Խ#N>}K~HEq$Itt$-Z-,%|qWv? 2녧 lpi [n_˰T(KG6_\_^K>A{зI'WN83s6Pϟ뱈Ff0J'$ o|޺ukc `{'vz=-՗wuѵvoPɓg|xp >b*E D/޲vɕHl'ڍ("&@&lg =O峧~pĩ?rܼ9) c`ء Npy^^N4~+dS={%?ܐ]0tCW5`BA,J@ȘWyp48t^>{@Kn^'[vӖbe!3`>9ϲ!>ڽ=>d ./mY=p7,F$"#AA$ b0 r: c+H"pq+o]u[J_l[.Дgp̛Gu$0'RBхӈ<ل\_] JP;rBd9{@rxɓ[Ėم l>v"馳:rX}ZER'U_/D~D @$_,  kap"jh I>! Kh e˗Ξy6S 8w9Yf  O3ǎ?"]wugȻ~O} b3vT@_LWѡD4#@$y 1#<~Q r._:sc mv3'坃b0LLxN$VSRd[o <.c/c~3 oXHuIc/8B=G:w>|О2E`⃰/l9t;NT!wZ ǙLL!A[jK/\p(0˫%o61;+.4w^=ڿv Y[EOX睋]¡kz߯'uT ibx *iHѱap˶@a"D8H]l(@sd_}ycM?x#\3EiGjIgԚBT쒽[Xݓ(@!0'@3 7ltqs.;g?p9ϛ>?!ph˥5?A1Ǽd)E7sJopxk9iji@2oC ( )%7G CH&tn{$ÃJcDAl#5F]tLx__seKp'|0V(glI:Nki0N տzO drNaH9yQE:4C +bTH&-Lc9y.ʌ[vAP8"5C V D 1y1*ܛC*'sp_7+x3鬉8_;wW^}N>.?2޹Vr9==NR Yz#]BlGQ,RH5A$™TL`V;ypC݄=wITZŌTdm:wB H% V"VCf.0"$d9Hn5$p787PbB#ÜY/?Ntn}V]~#ANCs*1Jr,D3-g# n$'+ay/ ?Ǥipd~1ʤƸv 7 g?eCxl¯fqW y[CC>*>[B=ϏU9@ք9kج6p\_,C,Lj!b{>-$D_I*^TlxHBbIz-Lu ?0+qW;;{{9]X9#ڿ@zY(`'eS9CrA9}?Y~HC6\ga!''񘘎6 GQ@BHءo:S ?E9ĈA;si>Ez۷t}_?Y͌vlٿyl@}lnKWg؞D"2K͌o]}MLO! gwO?vz!#uT d:0Av1l h:'O`w|>.r((}}J a?+yx :>< HDfBO7^D(t,4&tg+zNKLLUI&L&U蔔 s[7?* :t}( =!$s)_|fOMSLBn(0ȏ$?#̄._8\@OO}9%:#8 ϑ6C(H,Ŵ_J\gpOHQM6骆k쭗i}$vgO_?;8e Ȧ=6"jHә *@B83 /`B"t|h62=P8 Oi,_|y=-u6n{i:r) ?W'SDߡ@"D~|p&ۀ,YӱO ?| ៍)]zSdhl+c?@ 'Df&C=ܾӡ~UV}.ޱH NWH2S*2r fM7>6@sgOD%y>~?ru"i%T$![Ϭ(8u߸v V2#Tde)ꓑx~/4=3~x]CUm7\[[G yWi@*_q rCc=Es!tЙ-=s? lK:O[C:5yfa9$;HhPCgY!)ĺaA\`}EB#͵tG [޾i/X3LVH eGeE" `wЮQ;[^Jw['/\ɑNh72TJ4$?n:,i'Φa ח1&DrSӄ|kn7l7>:wpp} 'ln} 1D$m#$Ĵp儾5%Q @"yx7o;~mRnΜ>= yƓ'HahdOhB(*ݣ <~fSo _;8SMsŖk bGi`ZqGy|Ԯuts?NKV#Q!cpkJ(6~rPGI0N$.+uﴔ@CY|3<1Ah pO *궞ޞ20kV>H~!J Wi7? 20'UŎNlrs>o>8Dg3/h|YXRN'TiF'd^_ʤuWe<2A:HdfpCe"O[䛃#XHkOK˪U.n}{{ccuצh2&"/b jZOPBQFJ;ȻZS= Cg]sN)Ce\Np)a}( sC:kHtO  (Λlt˼phfrffj&4wccu+-nZ`] xAfRsi.gVFbYقqԱ+ ` IDAT8-}}ԙSҮ]p~ гVx#zVB^yBy 9`f2l L&c@G뷾H"px꥞;.m/t4x<._@]CSӆ x9lV`SRQHWѢXG3JUcsMoHG_7='4CОLD=dHSp*˚WA-mP:PKhcHlpܞ;][tq/tQ^UW7<پgӆe:Ú. +ӦjX3RLsdEQ.2]dfhWN9s(DVڢ49S\pG5˰ 8"0b{@ 55JoumO>p/%׷m}1x۽t.p*| =J?!¦X?FQwdcz$vD$͢88B I!.fd!29Lg"8E2Hn=kesVď-X6\8<10<0'=:Wԭio ̼q`SR_pMK$ XdQg#ʈP D[H-cj_K qDaWGdaf-/|m(o?ױv GҿkM;GF >Xcńү/Fi7` g5t@'>q>dl6GQN]`髒͑O#uH yh=ޯkjhny7Dp9yۿX)򔖗8KK:5{CaCu?>8Aa # hޮGhpR&ms%e+MUPV`7kXAQ`/?hHg*Bl۾2膪.ZRFRp{E%e"Cm5+ظm``๿H$BdTd%?l AZEX;t]ggd-7mg3Na qEoGovU{iaK3d E+ *=KݞEee%R[aD44PSYZz;Z@cф^|qͦ_fDX/"S2CS'ΞC6J[egĖ2e)VрhFA$#뙒MO 7pl:'m-Jf4CJg9)4Pf{ɩm nk9SY',amneS7\([W6ǟuuM`sq Tc2B'GO={IW źfb%4 RNp d fG|I+vstF?hڶپnE]!ikhkouM˗Uux?Z\ϡ1`):kFw(]sFSfƿ<2ǤG;ꂋ) 6w+{{GCoLGi\a;ᙙ'|rttt{RYYK$}lkWr`dtSSU_MqKsP3\3W"5&A^-:N&]㪝p0LG!RN\Ri1=?2 J8,]#͵k@u+>x׎@y3w'gF<^zIZFxc<~6W/sGMlPPx@_¸^s>y) /6qe1琣#uǥKA/o:9,Ô гi2:~x]Sx`^S m2{Y!iC8v(401 #0Qd>Hqwbdpӣ>nH(<(y-+;{:{'0W 3"H'UZ^L=Fq&SZ:)zruiUfI&RWR3b1XUK,Cv6}BS# +_k|G= qUu9Sϭ_OZ*w׷~+CiFvT^zu 4 dmN G($ip WMG#%E5͑aUHIqGY"ZU_JiCAZU:eyqPh@=oIC=7U{|^'1:u,{h{5-[V^^步 ˉя}{խȰ̜ ]LT/Br椀(ҥӖs%Vd˯IV/̗e .LiK kx>}};,Ul!CG6T z߲z狭t.pI+x- `KODFK f^WEVHGQo{M":ㅐ]fuZ.ǷrUs[[CbmR/EһZm8a 37%oIntdd/ Ws<>_0v_aj/4C*b(sdqʪ6\RfTj;oE@ns",HB"ɺiUPM0_tocXzxʝ4o׫=;`Y\^}>Ҿ Tyѵk뫽^nR‹d"UGe ǢCQz :bLqg$ p`~-sgz6({Z9>$ T77?7LrrvUjhhj4}aEnzɝ _\E췱th) {[Zj>b'I |g #)EW%b F591UUY oSėx+66+-ZH++-ZzzCӜmkXQ~`hj*<$B^ r-p@+ [ ܹqղenI_S>xx2L J[HrԑH!=yCuR'bPCb=U@ dt)cX`a~b7ə uKidIIex{ӗ+HܮОY Y!} ~r$ \N~o dU}{7Y /.uW'k&N$;$90.U8eY?JILt{J0M?@C1'Ss 䬧CVVT~s`*TeJx;2QZ";v/ts=H XgYehh3Nbl $@A8P`rYl@~.ք(kLBVu &C|FZa'GHIVl;rlx`แaB#*|r` ۳|lDgk)z*q:sȶ,~8ɒ.l !~㹁hДnT1ꦸ=$C4;kk马y+ψV,p~&ߺCXhp]}t_g[kccccS5{=o?<IP3 J{lh{ l!. Hiw2w8ظm=pO[}#,I's-0q JA ]e9^I ׸!v93pIn`idGגkEF2ж<޸Ȟ,(/" ʐ%(q5/'i[x`pБŕ(qrHFQ;3V<9`i|+0"V[C|>xMOuz\=-j _79)[t"o+R/"_P I(#v+k*Wx*ݞ#„QՋzҏCWWy*+_?ub b'AԷ=њeյ ox=3 p)PQ-riX yRarEt@Nl]NV;TT|_Zuz˃ò̌nx^;Liʕ+j$LȾsA Il M1Hq5FO k[Zis/Y":9ȧ%*iMe-(Τ\ R,˱`Ӟq$DA?:]wyŲǛ߻yjFE'Sd׭^r>%0>6nxtÆ]QIPG rw|b=ց3DE`C@uc"hDqpJY_" :i7Y9֤NˌSUFkg&OdI@}d\Zz5Z?#Hx |if{Pt0Jh`pN$/P\2<-?.)hO:(fcqp#W e u#k4S!Zh{K. {@}|`m_;>Mv6/w|l*+l@IFֈpz,<xlc%Z46 VY*I{K`쀚a(|l(KX0F @Yk/"mؒXslzfj0-rb`xoO^:=52qﱰ~3#;_)c+rsGvMrGi`0OТ2{ds2%K.S▬թ&52E> e0nV5k {`(5;>5=3ֆe\hu8! wz5* Bk}dJ:KִP!Q>W +*<zB-u L38_j1sG 9ހÂQ]*<2WXPjE e0%69Eo^mPi7P(_#% 4?xEhxURlMp("\óo\\\\:`g^ 'QӡͫL7/]Hڰ"BXۢe{<³] :0 y(n+a%խ#$oa̡hiU /@(ȹd^ Bϟ<}]Uq>Κ!;+_Ir,d - \.@=}5%.\;h4#lD/r>DYlƗP D xE=+W 'y9T-wS4 tpNVm;S!Tp׮98u55K!^e>Ϡ$ `75X3dY9b.na(:@#P #mX=5~7{0w O'___FSniikV_txЉ= 'ʑʞ j|͂0$6b)R-S/siCz0DE72?z 0dߺCF6?3jCMMM =@ms]&A݄_ YXr> *G( l:oGU \s.#:aL5QgO;%oJp:@*5 E !frP4~~f&"VCfC!rWx˒O^'ᄸ.XWKu33HnP$KI"~ih&xSBSs`ǚnzn$l+_bŢ%?#b}鬹)2C_=C#$1yk_Ip Y9"1tH=3$jB6R`Ur ȫ ljVI*%g9 xqF(x+d!6-[fj[X -`;PkQ:LdjP]$.7 :L` 61f2b,IL(XݿJ+csȴklR\L`E$ U^7!ENp0$Xi#).tۻm ?O]SGo{CnK$Cm& FIKbsf|uM] PQqϲZ_{?*yBCg `%d_\5+,"YFsb/^fM HCw 2=ⱍUx]ƞ{).Y>1Asׯ8E}[]%W &ɠV**U >9i17 B,>wfTGs]x#ۗ28W U~C殲rMLOO5#v{G񁷾%h.INtA I= (ǒFNϽ.w;0>} g3jQUHFu?YUYK ] |gxh$Y'_m*wϋo <+p@V0l,O(3 OhGr$+VQU'B}F;λYʝ`Pa)a-w )w tBWu#p4heeY9p2\{|>meɒ#R,,C7{eb%/`rsM) 3sIψ5'V&-<`v>XNWSGګ(f}(دt_VvQuEAuzњEGtQSf!J&vo<Ɔ/"n%Ӱwv%`.la=TVjE.TPG8"DvKuM-=+K0P\FֺCȪ5@ŔaHT XLD$\qms#e 0y<-Zې zkRF _p,./NH}Y]sYyaƗ S!HB ^weE"؇{|{v4W\.+WE}1>aqJ`Ug' cVlJxjլpIДb$cv?~8gB@Os=iۊ U@YT51rʥ. E施#q5GId$Bj5 q'[ S&h1TiK{y]قtv eԌ J(e}*"3Pױwd>tIpA_[G<O HnuOR쐊rځr2^D8^-2C.}9_ U+4,0Lj0c*Ĵ?lkE4լ4^TU}Ձ7 EJh;MZAu VvAIIN3gHH" ` g3*N`A6 w!O@' P${E;._#EU>dkqx`+]%{BiƩȾI4_S3aUop>II5I ID(…VU%Za0IlY9 (]Rd CKs:H|kmkr3/k!lc3u3V<ܮ] Dg ) *95"T "̉ge^ At9ÚTU¤Ք4AB9Fz_ ( Uv=)d: [}e]w 汞 袷>Y+/E Ek5Fy1aV7m_T20ʀT.(6\z#ӃM5g J"[rW h7ArY ݾE:ct@E#ŒZjU7r -Q"jpuŏ_N]YgYUN& 2jD.c~ {0QgwR\x۽rM=.uH3O}|cYyBfObWAQ(ؾDϤ-%D8pb8Hps 0@M )SFTQ JU׹ɝ/ |Y5 l K*L m,Q!0m]{a5Mǣ[nЬK,?U,(ƃֵ#`ey2쬚;3ko2/UOXA/Ƿ火[sHF$2dŔGv~siyy5ypCTP: ]1xr  w٪a0a TWGpxkYޔm|0ՊE`叴6]j%؋ZE >MDfrVm\uˣʔ,iuOˎg*)DחϺQǨ^ŷ9D'Hh{ \d>SC_JI )K'zo# P,T@!廹lcBφJpR.Y!h íc(IaA1R-.9̜2pe=O<2k0ff S9bw%/cK7@8wUT`l MO0-M >f)X~͕Г(Qc+6wZ&6ź/aI)b" ~R̖{X:unה*%u"2 Ⱥn˹Ixl9"]@F=E̅ OLx4ۓ7B'# wX /\5[ R >TeҺRPY#=UZ|zAD ́5"n %頻Y\K!SSa{,]/ϊ q5i'#&V5ԛb^FHz[S+RݍR4^@͋Q1oV½p[\<(nE89-@Vݠ~CjIT&YIig-L:!9BTi6 6y)OW3av/\jͶA\=cL2ɯ"C4^?Ϩ6?M)4QDp_cVL0;4bGMqT^W.V ' k0?V!oZA;RRz|Aߟ+_$</h621dS#8]c9|RtZqn0*p ՝Y[yrr2OTpȉTY>w]R$7$C { s76y\.z6F\O͚LGxRIJ0JB,vBSe8ڧs _C}SުI2׸EFJB@}kjhV/Hd#_;3ր~aҥʺVX9OE~DYQq<97>$RČᘞ LaCc6`VՅ'f*Sa~f9S Xє*c2z'K)oaSv'23_ Fp<@a2quO+<, ": !({sF,:0Cu9ڰGu#kg+ev!$uї4p ]VDFӅ1:5_ 4cxL;GzP&i1Bp=nHJf0XHҺ$&%yau<Tyǵ,6 %UdCX&9L dpUY LE/?݊kTѝiɤX$&i;Qg(e@mVߨ &LCOm紘^;&HD̚*#60c+/$%r^C…'=?޴NJg oB%#=Q"^qoy82} _Q"o=n|ƒdN˗UX6z, <$U̘ sV3au&Zu#]1e4p_&-Ø-!sf@ 1CM`Kh؛&% $ld2!sO "9Fn W[FBXd7!M&m,-B |yRz)k_\ L/,}\k 3DZ٬*/9`SJAA L;Jn ʪnB.Bead=x59Oba17> P,&7 e~nTD5sD@"?Mv GyV,2 b0~)2WvLQStgOYNz xѽ50*n5X :oH*e͕JTt6sV zi%QFϖ$O#Ƹ9Ց4*CaX,ʳĬɆ3LMZywK慹LVxXrE|0J$Tvװg<zmugqodGQ&I6 / [(CxB$jq|6@@Z`]E<=g>2%5:!Êڲ(a>4ɤJ^$$`@pl Umqe6b͵bxue yAecY/^b@@@$rba3x Y+PӺ<+&LU-*Z[vfo9Nn`Ⱥ 2',# KGt|\=+ {HfHr v'VAV϶AhEj՟]ɰ[y;bqz< ¨pLĔt:m=ΠtsmI T$sF# 2%L%0* L"-fU/35مkgfE|03}apJu0"fq)x^Mk1 Ycju׳% YyJR4@ "\5QD6( S  G/a|(Ǜfŷڗ"$%2E#\)^NZ& J$2@('.l@J J-}U&<ڽuKyn*WºT4$̒k*͈)ŭ7s>IIJ2;R 42SVkR* 0,x+gJ ZIE HLt (m@ D#Dh/&Wc,k!Df#@ uHdeن0H`L^Y*%B.j1M3mfvi>vwxEfzzf{wOvc[篈/^D<Z5D#XA}ļ<<"|ud<'[Uլ>CC=:Szg{!s 5懙B[mȩKIS(z:3)^}(Ir' < c2IDAT}V*xz|rw]-1 ;FV4B-i&= cUxxpp17Kj9ѝT~jHvI4;Nǩ+HQwy|bXߢ?\k"Y1V f*P.1ׁ8^Ӵfo J޳ -(exP~#zh p1OqFgfl4U5.( l{ݡo g P: ܠQ]z1{ j͌0xW>;JT `K؆-=Zqi1L0[=PᆣG*)so>y /CesЗ}yfq~Fq1>d~~"`Yn灭~p73$Lym^KvVwݸoslߡc?Ij;~7efXw̽I3&s,9}c)iV0d}w`/p_G~(bLj>ٖ0!Z(LWឨtEDJۘǫ ,tNXlAcA?O !'p*?΍!!w žGO6(K}bB俒~:$-EފEa⭂y j)~j'F|WzWCZWsU7(.%2h;$N"NPV]K܋[=0-44Sʘo]O+.8S.g3*pL^QbZk: F Z@^W~;P,E2 o/@\sօoN>t.`s'v3>˙G#.eurOfʉ ZĆa! ||Ȩ$!/V0 Z"@-9*c>WҍuXbljCuȻ5I^\Q]hA"^dB`/m|u}8_Cg*^}TGӔS4Ӥ=SjzFD,q:?4UQ`xG\WJ/*P8v$A51O*f'*F;93Hl(>D'Z!ӼMTۣ33*\ W#vwSIu QX FYyPοoόBn䢉ZPFi L5=Ng]¥:΀ҬtlB:R{F,8D,PH @̊"# a>g>BX XI* ; /Tc)~f 0~~$ay&RNGtU 8Ki0#5%cH }PpG*/UJF.{E SoeOuGk\-#ٮ![M- A «[J9;OGS$Gm6 UQ>g Wg7L8L_9)^TGoпɸS+Է:RuodœVЖ0=덕Eu0vB~B#^U~Fh`K/^=I#eٟ;֧Rx=fl%K%iہHoZtپ7v%  ?ϐ{kC]yz'\}2c>l93Ǐ .qm,CM `.C?pޓWܽGx]Q46!n27\TP) fv}7z 5G?I_i- ^MT iX7_1f|-jTt,| @ *י:>.| & LR ~ nW~,+@;f 9+ȩ.Уԕ~ U9ۙM"T *boy XJEx!:9lj?`]-I{[Wq@s4DUJ6u970uY K'nrBupejf?=R-V/˧_o}ٯN><?9棲 ݕHs]W恻'l kLguzր:<=`jPBcf|ܾO9N>~)_ѯ.hX9f. _aw2zӱ+%vK56^,2 U&=r`{( Mf|zqrݡg8ystPxAIn/btd$M{±LP8yY]a|©p|'[[K?)8ГIXHYvd!#YcCi'Xq' ?=E5 ],PA!.8mq)C :&N]V6Y#;?LR$X لXpCN{]]oYso>_/l+A P&!epr% :G7p-)| pwu}|싍pw K 'FjA~o_[ụ_GVz'`e ?i ryiw[p0~ F_@zjog2S>I׹'c&O,~ JX\Q(>w~pT )'#󾶆x[nu]@:tQu NǠ OѩqqtR3o& tJ!_FG옡>wm 6,НQJp<p;HPbN+^PvGygKV`~19'{a}kGfj.-^,…i% ?`72@~[DODa|FT&ƒUC]`*k'>78'wrpmP$pX]v py"~{3iK;Q |rZUb7ejcXP[EY2|:N]qMؠɻ\w;>A Ho@u} a·ܩ7 w 'vK\_+ Ny (4 m glVjs6íba ]k&6:ٓ@G;qw"U<_+E}݄V ^ fJ'|3? uy}'#LoZ#m@ puUI1tCXz.wIETbfH \;zŀ*TY= Ϙ=7b:;T: ic|/ -P=W2d^5Ov9?7=MOqg\]-3=*s]酯h} .9#"=5#Chq3& (KM`[ԻB+g8|pG0v`8[{1]LhmCXy[q`A(/dK׸ 83v(}?yIV ^~a4.hv+'%pu]sL@]G U]_2*0 l︸7v۸ ?pPgُ\+Ϟ-A$ZHj(~ϟ3+Q#oп\AX.c Y@8k>pc ӛ">! lY=9HOU ٓbOa1vDZ9K3z9֭s-Vo &냫aZE&Y *Ez?ym1Է.o(e(Kόx/X:`|[OĐrYOkѕ&E Klb{Y9@4 {b4rp?wWy"{^]Y3hM5ذeϧ4w ӉfJ'x;|; B;. OD"p@x|\Y]<@4X}>9sf~=H84vx.?2'H{ʏ=H?RS僽@w(s3︻k2\߅[Ԃg8g`Z:}cޠyD1 1|~?fm.q߳b(FU pR޾ {/rAXamER!H [GǴl󷿵b.`¢ﱠr8τ{Lup?f5K^]z ,y@c/e0j6〔v]_ ϮӺ]Pn mrU^<4E21!RA0H$"l{R~5y@z{f)K{ W_߽0␊R L&{idOqwiw K܀L{/ZMKOے6\.X}Ä{{lT^|xƟ=A;_n׿iRO=^Ggw8bpO/ =]l >rVU#gncemGfmyX,{ו}յ tOdk==^l{,Y{ ^Yޖ& 9/O_)g6?accQ6Dko6OlK`@컭5[c{]fXԶͷxA;Ք=,˻͜i)Z y5G]M^fQ,X./﮿w6fy}gRkx9o݄o7Ke3^zY~#\o \_k  V3OGx sΑ~2 p}r/yj.m6{ ;7677qK2{l\&F~`uzYٻ^,24wU,]ٻŽg7]`5,kk3qus#m=`.77ÒlR V2{{kK/Իø^~//X,-FkXZ,ny1ٸn ^^zY~D/  e(xgo 'q X9/؜7/Y߷,ߓņReOyAb]7o !^/7e0 H|U/o_ۯ}<̞-R ?6񣍙vgY]?ga?w_,E{yx S3/`pG2}mIENDB`libdmtx-0.7.7/test/rotate_test/images/test_image13.png000077500000000000000000000156471423156660700227760ustar00rootroot00000000000000PNG  IHDRkXTPLTE  $$%%&&''''(((())--..//001111222233445566778899::;;<<<<==>>??@@BBCCDDOOPPQQZZZZ[[[[^^````aaaabbbbccddeeffffgggghhjjjjmmnnooooppppxx{{||偁恁傂悂慅荍莎菏萐铓镕閖ꖖ闗ꗗꘘꙙꚚ뚚ꛛ뛛ꜜ뜜띝랞럟쟟렠좢죣ﮮﯯ pHYs+tIME9EवsIDATx]Tյϋd&T`R4Tl@<,Q P"[HiKTRHpR_ E(/ C!&:i=ygτ?{3g!={w= _?  `X, `X, `X, `Xt>w6as8%?&Jg^Y;gVuʣg?k7zunVR%=s+gn뗎nʟOW~O~b; PYbNCs~^Rq%?'GTQXSi铧YɏVg~%[g,zUO}hHF{t5o@WOj9;׈@#mh%==K>̞1yh8=%[aލ.Y;g͝\=={LȕGd׍%]]`̼N֝,{aޘ`Z𐼑йSƍ"uÑ4NsA?{{W[#G+G/0{O\[od /'[~ 8~tuU^4e蠡SS9ʽ;;DLb3 &C\; @!w&=9ƎqGd زkO3SgN}p|X?ܳàP2[}f,Xk;# -Z"ŬO^ر"u,zl\5wA{ꥭћQq4bd'H_0U'd9Vjr0v<_<yW U2gfGMhhn 5]NwoMY(bg039Z8[$g@ij91:RcX8e{7bc׀-;^}ḣi4[ae oSEms2M dA|qƃ.*AI *S|/K2ɡA.1KDg]4t8gD,mk@ZovLRNě0Jl;PFC&Н /%z.Jbh'ЇPw %+U; %:l GB $F@Uɲz=1Lp @ LIN匞w1Bd(uf$Ϻ'#*vl`3|s@)=gl* ̀0i (;,0;0ނe%`ndSI^?|{*@ wgvw p~Y|x˼y?du8P]u)[ՔN*ĉ:y5%} n$۷gg)UIfq >/tiCK p)7Ã$: 2 /ݿ^>ue;]# .`Ugg 3-N4/BF>mXH; ?g/BһC wBB2 ȧ-ɠrv< =ЍƸҝ{Im% ifxss bGi@DSuQiY9LPiG_(B .yCb~HR Ο>O!8(\& (4U*jTT?Bko;S7wNR"Mkw{vm!ʧkE9t?;AYdq#Bz'"1}9QT!W%뮞ɒtI(h5Ջu.]9 bE]f_a_7Хp VS3X%WYGQ@; π \,١(-+ ?" Nk7y*)YjAi*J(3:RWOSUtBT}@>9kܮ Qj|G,J :dE#awں- Qr@M C}~n&3G}z07TªhԚ@}D y 9 "ehHEYGClP΀〴w岮6Ru^& ̀+BDRD!/SXfg[DE@ hkmyRX"}Eb 79jL(lkV+6+r5NY dQ@}1!xY6P>bB-1mlS \xtڸ@1j(G|'^L]{{HdQ6,*\-,X"ý3SR3zQ\[B%{Jk;K Phƅp-={L!H:򖻜ׂ)I'ojfFt`A0W gIIL,i!{EiaI eD\Hlkaoi1,L[(@jN BHx]33a64 㟒]-Jí4|i;3m /ޭ7$lT(qr]AHG,2%nCLA ;iO`O 6;E"^aBPDuS/lSM}PauT.:PqdVķ T| 7Mָ! =0~ZBqㆵ1YCmȆj&(mKSJ6xpHu??YZ'NKԉjS uN-W«Hp!|=C\;E3Vr˟>q 6s@ZlTE;7AJ&1qŠXxY)8mU R(V O2SS}RMh *Rx2BWp(nuizgRY43\Of1VcZ; }{v>E O6C!r<Kp$a$ )o0E8FeS<Ew~HhاhoPQjP@Hdk M b4he>MӰ0Z7Txjq| 6sO6AJ\L#bv,Ȝc%odtnPP!OO'1jsvlӈWRqBYus7ݰANj{>&SixH Zވ̲ANj|SbpI Xh>7x5YE4l[:?")(=*%lBfqޅEp'Fa<#͌/m1f"O) fp;'CÀZQW}jPb_f@R oY F,N r%:i}Ou*1)YWJ{܂QwxIAB^U]z[jfa0Ӗr}PpዊEo&F/3Skʋ:ehNQ (*fYb-ѨǑDq78ܛFcy(6=Vڇ#qE4veavEO*E8\i/܊ i\]6Ӄ? 7R8k!ԥ>^eDE1J1M-3 H`ꓧxK>m|1rps HQ!v OADF`* A׬i@)O1QQ4(+E]o> (\ds^>FPJQutgXI8KJ%3JiPtMi Vk@i@S@[-)UAq+%6X) v=* ~E~ko4SiuL?3J5ť.򁭠V'/4P{qqGYLVA^)6If˲? v)#)LW 6֢Ra\PXo*˛)*I ]s$|D'αJP3@ADp[MtҘ_:bb"}0=Z`>Q`-xc#9XJh*ь #yhˇo9W# Iol\AJHFi@lIo/zOz^ďd25y{c +m)\NR=x3!L =\7:K :fJ%|,H,z @7(ZPthqވo&´ >lpb,K[Qq&:Kk}C5NsF0$>PmfRsy * ǝA3QE~6&U$9sWEq)| \n:ȧ(UBsr3';99` k73&)J(^|#f&b*Dbnj>Ww5x~mL7z?7o/nn|qg?5, `X, `X, `X, `X, `X, `X, `X, `X, `7 "x"SH?IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image14.png000077500000000000000000001460161423156660700227720ustar00rootroot00000000000000PNG  IHDRkXTPLTE$,('&()$".")&%$!)-8(#1,!5.A($7$(4,#='&=-#C*&D1,?/-E0/<1,K5,E-/L5+N5*U/1C5093+[-2I2/X21T52J14R72P;0U16^;8O3:\B3Z:7[89V@4`A7R>5f;Z=9c9:j5=k3?f:\@=a9Fh:En?DgD1^!A1{]]ml WѲd g:l7npp—ժfy?9"K9v$''Gs5624042626bn5߳[gokfjz*0`ժUV-7O\ظxZ׭P8Q,''đdOp?-[nuOG31.%N} ;@n q >AkKRzn`ecm |;ggP\?|ї)*K_r8 VLL@suvn[Zǧ7ZZQZjիVs`:'~Hp0DE*{,8  | ^"8ws\݂,H dX) s[qw 6U9=Xc1.:V s3 P{7GRsdyy1>̗2WYPۛSZ[榦x, 7K74*T"H I؀:OP7oq%I=$"RĒpW' -v8[]]]H㦿qBt xdmGUaÆu̱3n>{w>tdG{![sF@?x 07˖:+w_"&wIDxmBZ%  ๹u ElGHr@L?(9MK%^.YJO {1mڴ= ysK[=v #>lnjlhHojfkia`kmb53_='pc!,CcSK'ip hcgIbjT"e}dbyTh8ߺ͏~"22r»\7m0Q,z˖>tnYYyLBЌ2Z]n:Ήss.ܐ^p cêՆML-l-P}+[+ vV[Y骟MFP#%V.^H )T KO%h]78],{CzqcDS(H 齢HZG;P.wW/0^$@/ejQ/uFWmq,F5ٿvw(`fkn/7,$XJSS[K3K [t[XٚZZ[L;0e9Do9t/8>=%.Q,Ӛ4* >m:lI ħ&%=qGA56p耓 \ `+s˨I}}' X#Xj`Ԫ-ΆU̝={0=U߀ ,!1a~;ka [ۮ^eukk3ptj5SՔHep LLm}PAA(~z*C1<\`*&h !E RוegK Jhqvt&:t0_yDW XU76Yakqp4t>2!սc󮐠].w[ r3t9|c|Gd Y㺕.OP븑'ƋaTeBT8p?(W-p|.3#>IT1?gZH z3燶#ΗZKXw4-pax))~LuB| 9[Y_pokm3M ffV|#]NMl֢Je%tTAD$75]D^5Ctt@dp8NG(+@ _JK Hh@X3^m{5Ce5DwxUAwY;Se:&迥}}}R RoNu.o3ݝ)!;R5z8i*߀&R8+j?2=1BdR$x zP 4㥉@3rW'RrEHj&X%O|/0 =͕_eW ^m7`_[B ik X\ow㬲6Exw8lr43X_-+&+MVD0fk7rƁ ?$33g$? L3*I<N7#Elx5222gdB! 5vuDrs⺅AFxR yC7bie~#yzsd^j+kkJ̬I qSSj#4HgJ 4("]TRd ?DN^pg9@* rwqtqCy^^^zK`^ݓSnd33c}Lb1^n1tgxUBt%mmagñ'_aA+-sWRܓ'w*2HP7@D}1*,HzDF [89!^n\=긙/[h_ e_F! W^ s]y cZf3%;T/|Ox/_d u33v+MMoi]ћ1a%unoڼMh&dSu&dRPKenJ8By@A0w+r=% e pswt\ e Qǧ_P,1[N2o@UnAHQ7 "OIݰ} d|UFV${F,-`8n7?L` 6[2eHϐ>b:BA )d܀ 2>XiAiicp @Gwwߠ~ו1rk@J&T덌ڍQ(81c cx9} n >WwK{{K d|uaXkХ>iuKxhf&<>#1`?y1 p9ܮ4&T9.t܌ Ih 3K~HD,yDdV ˷=G˙&:߄o c'> > վLH͈#6b:I}ƾz:VP'K>E\7/y%I4S4򠛉*h7@JdF.ˏ.2a/ ?v"A7⎃γ2 u _Ll>R(ϔgSzGzOM'5$`j??h(< i8vߕKR JO֟orɂ̄Q|o᯿(8Xux7U71@PC|6[ڟ)x?T G;!b9\эB?AtI]ϬW#ZFj<"`FyCe%F3򂺳}Fo^%d&|#_w~ O1' z?>qOj`7F317wf^D\#EmOW9xE:B,z ͛+WجqCV:Գ2qS'(x-35C!%dƁYR/?>ߏ뇝 Hr4 D"BDxk71 Dikbqc($+0_({y+W|7|y: n鳻My1aT8nH;%\(3^`i}@hLyv>NkEYNPTRgO0M T`T_xx`:1zcaw'Ϟ?ڵڵkOXBH ]h@_} 4X&|4J١ݘJrBϭ㹬u万U|/y]u8$,Lv1*UxYd-vF/}#r~w/A(?U_վX//cu/q\a ɏCCQ AP>`RS#rSd$߇/ӈoK_vX4E ܲ)`r*gC%gy+,Vحݺ3nxOZe}HxHXd2dQNk$V"3h+\}B"?>1TJΆ{aF 3 :pk9?ag(=bߗMUiiCܘ 0 21 6 Yә\ VG/Oˣ]aaғ_<-x?c V>r ) ~RZb)uy 1%>}T?$EZ "F`@nk@K7Ȃ14Ę{J&+hZcx0+LLlM-ViF>3:h w~|_k*m'P :jb%yH^PPgEE%'J*ddfAs=vFSv9|gph@mx|؉XPDoK<߽{wZYU49=ފ75lrOgrtY :9;W%+L=y`/gw AZzn05!U+ {$ҚJeRYyĉ5' '*OT*Ɓb R`b:\b$"@1<6`,=Fi]Ǔіk! oD r%U9i;g3\KKKXXLf;m,mfZ-;ׇGՎ?$\qFI75[C%2 bfx.-),-ԍnɑeԜ֝T׫jd'JhXu\cԝX+ܗ(B'yB柈=tIw˜Iɪ?RR90z6wXɟ224g8(PVRֺqGP.w7_w__w4\/JXAǗ(R$:zP&Sj/u)?~\QxuIIIيR0<<":F"D*oWw;{n૯j:miiz[;{;ҥnWJzK{s{uwM-uN֕^(P{/֕bA-%OwwwjwZRyJժ]O¸^[@0`{|2ШPE]z j]ilnlT58;W 5/Vi5͝-Ly\R^7:653=Ѿqdxroww⫹bP=ܩnP7vuZ]jՍFmK+N4ƺss;[zZztFUiMQBqc¤4Q0) +%$iH_åeLeeҚ - ;_z~÷FrV_OQ }X=#goOM={:tɩG[xw=qg|lV?1}w{o`wﻎC7ݝz|"\1]Vt3 IDAT׻tvkFsAUW_MS}Z0"`&.7wPݣ[[\lhbl]ZFR]^O{{Q[i'ϪT5z8F޺:pG՞wQw{nB.z4 ghy49;pv$֦MONOƻƀό 5:90988t0&)|~F?j U@=``wowq (3E@-nuUkU]h{:ZXTu7i+55MzsO'g?~cah|hxh|233s~ѩ۳foOO֙cccM7枼xdùO~wGo @nK=&P?,*~`r=Wv^l~un?nVCsFH*>WwN^GϮAmW^U fC5WWR=}gMN4`trbr$a=xŹGπG>h>5d4a9qby[L1Pŧ/}{>=;C9990<:22qX^0RwKr"9es;މvBA6uw;*]QuktukZ񬩶Iժ>\TezO^@L%mdyٳw۷SkIqT2%4iU\@]+M"/Ā_ @ { =!bPsfQ(`$lTc wn@}=i 禐ӣ,E!}7<060,?4twRw_aC} t6# Q@*Шi.Q$QٺF0S+?_gyA,x'O?nq9Лz [&>iJd#ǿSa0`,j~3Oh]0fPwOأ< GۯS`G/`=ew{MUn_H՛ %Ii`nq)%` 䃙 zLg(SAOϙLK`ѡa3 P9R9Ҁy29̼il /ws$@!\EWtqև/3.DZDԪ*ert ժF-bO^%2n8LG̿ޓ-L Q_0I:hI,fp{tR%T>;Kտ%X'x0Cc0>裡B&aB\E8.KBs.&Bt߫UI3VM]B}4KdrPOTg>L>ϙS3;?9A9h|{\Az\Iᜮ#X Lea09@-`BOk;;:w':Ehעߓ%QIK3KjLXC RًE*_^2&̑NGO?G:|4*D$F'8-38dp5@)ݦ:ìtNLͳRaj|9B|r ɰ00Gn!Ar.$r-80W_0I_C鿵U:2@ #xϋO=?Q@wzp <ᜮ dyq {V)<060xg(ޏwO 7A 78wy*`^yhT$x4%9 "Jv 1x=D04.w >' gO,>︇ 1S Sp~~J"Ǩd#Հn7.O [.CQz3;]BA\)DY;Xw%0|~Q ǤPth04I*j^0̧X 1:vtV8@=fa[L9ܺ|H,:{hBD[z#&XEpz7zAB \^QXl:qJyJY[wADbgHՀRy{9.s~)᫙ x$QvߜCOsbxrxp'g#LX^Y?/{D"xu ~pW!0~{i: z.h4;>xDYVV,(WTZuP \`\q.͓"Q0Pm`hMi&@=9:lC?7EOFx0 MP();7FuF M~t?>t{+lV.f=}׻Zz龳!jDPӪQ>M;;4 oS+-wQf9z ;:;l6؈pNK_8$ٙ./ ՝S! ,-@t?&`}`Mg˧Iɧi0#O>)6G?9Znc Gt'nO`'n2_qNsIQ`GWgt~5W{'Pմmih{N^场E}Rި:,PHD~EZuQUYS~R}y%e}4?_1)egO; hfr8a}G@iă{4{o]80xk [o͋}t*ޡ!G?h^9]nuM֪ӻpz{w{UvfN5;%튕Vղ55545hE/@uf anYdGӀ.5|Zt @,]? )5L*+Q0%1)"E+_~ONGg8wzcQ s MvqTZgtk{;4~ZuMZ"_kkzKX^UTU:˃ ] 1'P5)DAa4(@S`)>ߦj聉M o33?h;i<~ {\"Gnvt^6섶wpph!pa6AW+m 6 ՚%/4:Oq`/R!x߻s5{ku>' 꼋JE};H[{ =WVvI{w9KpnzUs B_TQ{LQQ֪mRUITN983N]*s R_7 ki=Ш*iA^>1IyISeiS5,ЋŇl:< N<<\߫Z@Z18tḪWV^iTmo88^RY~Hz8)>UT_iԪʋ󕵪3 iBV}2ze[]y\bBLyǂ3I;$eڶ) S qQ;$Hŕ:uꓔ:u IosP&WdPe8Q[#~2—7P"@y6!ۻmU9 %k;sV=v:Kmٗl>Y0¤ e9’sUR'ITMIQ UUǻ).}7$0쒦poZ_BWIbIUނ<ŗ!BD_*'o?YZ%?#K(0=tƧM;`CW )AFS/`BQ&H/$u4X|Ak+#Q*~gcA\am[eyV4Wڑr)q+k˒gJϤexwʡ͂ZuCdGRL$ E$GE+5d \y8 'g'L9p rxJN:Zg״Ph w0'QxA,y(?R+k+U%J=6b] _<'γ ٣g? w7$(*ե/jw&MΉ=,/,-Ƕx$Ty8l NVIsnɑKOID|m o|=}||9{@ťyI1!4a϶uVVm K+ktPP+W+znMSA-8#M=(oOz$84@迅9Hjm{Ӥ_Ң}bMu5MX Bm]CbueI""~(*6י+U y<1QlHS gg_^PHVQ ?]s庆]ԃ|A]/ syذ/VZQT?;# ~Xx@{T3 g)v#DW "FNs{Xfͼathz~P,N`aq⧜֞I-hYLQx(iNv{"rrrPF*//\{d'g޽Grr *5j5UN4u5ҝp0JNoyFSWu,C!/)枰,߃{vJlvvk[˚ʫ?y-c60R]M>aIUʿfz:9I/i/m[`LbsGQT&=(nɖс\ez%")]%W|=mL<don]Hd(O }.8kkڛ $yI /  +UT8u"?%Jnn`Fֿ۬zHأ$H} &{OgG)om(/LJN'~[vvPQ>GĝV>wZx0JNXDtĮMwnXDXDot",<ߞ+} LՠJ$⤔ImWo~Dh?Mg߳avޞCjO30Pga>l߰éy>!Ie>M*i3'Vڄl'(DCu#XnWJW_o΃va`9鐉O]>n޻Nk?IٛY8Tˡ;?_VE?`D^[W,)./+L wyNwI5nM;ƒAk-9%2i!T*>*+3[7md(-vrCSKwܢӀ: ɿR*gF" B%JCz9g5!4HϘ@MkN4jx>KuVlsPw77ˤCEMK]K/c IDATxؤ|)U]-dT*2p#FQW@PQVTdA8b= >u@|P_k+͗<"QهhTP"ddK) wwE;T^pTN(]^@v~x 6yop]ij% 梛8P  #|~rS?*)IDh<@T!O ^/ MIHO?%I ~w^_|Qeqvrrgz' Ħ,]:yʗn+"&,b_"/{uxiI{eEmKş}kwtB'%J=UZ!MlYq h6{s{ m>&_V[I+ “DbmimG9ồu5]Ů֕ISfNW(ti<7^"ʑO$"PoGKK%΁陹YJst6 MlnMN;~ _|GϷE|v('-__xR#x̆/" ˏgƥI:D6Ap7/"`kK244rZUJy::8v`q\Q)9uKlDB|-<_*/WΡHSG$J>һ۶Ӆ eUʪ2U#R슰_Sؽ/wmp]BgpxW/$ Q4Y( ^ݤHH,k2zU_JcB|}ZU|Ի Oo+=pPB|8#QS3G^L*/jm G=H/+ HxPބ66 {:o]moR !| i*===W/> H$`ӟ*9OgW_M` 05d fz ~;iY_:_)LP]Ҷ۷#J}')/jڮ\.NoIOd{+,g9P 7DZ4|NۓOT*˕FmR__N ѱ2BUZ\_jTŮjniiYKTҨRYJ)(/(KMCJK3'?Wr˿V5ޛu| t6&JFJOmɨ*I|(uvw^hl*ogaݱtgGShiL.Nhr*M1KeMrr婺C\l) ]ߣmj蚐T$/1WWˆ{i6LW\Ri5V8SMek))(*ӟfZ3,i vzC0`^m6 m(~Z, 0*(*jn|iy`߮T+> ;9?JheD)V*e--m/N\u *C',l{XBn$CuN}5l?rYOTW9|R쏟}qꋪg,<}ؔ,x>}gn, )?7ET|xo̮|}B"$K]_Jn%͗|^L_J.zn6 9oxݦX_|3ok򂼜_8;;ykKOɓvE$W-MHt6l۶}쒺w5I/m-*S6՜i:q"9]&IKWpuzC>^[yl96+|dvз rt[JOCý Ǔw;2JG$Em~mbgxCchvy ۸6453s&_rrۑlTDtqߛ#b{phv4]qqRU_#; gzaz9msΫCW[z32JUEaHH-L$jGޏwvz^5h9]5B?5x`̗ɒMw{ΜRu{I-)rΦτayS[@/,VRQsizB3:;7/1{UV>壧?,.Қŋ'HMe2 c{csu5I1+>Hu]؋Wգ}2Ez¯(%V "CFgoN>^^gF'd xI]ϳ#CWe ^i6~wghP[{4cI6:8 (44T79v}3 gwa%{̑4%98Nrr>FdWΩўEp30ӅoH*;W6(a${}-9>!{cɡ:/\nƍzUxGJDUY\/)C! چK_2;\{oǯ ^O]9u}CF;pa: >#=?aa>bEO0IC>AOgG5ug9woʫ`Ty 2 4d(1XKI }Ch 'B!mSŇYˡSرVǺlU{Z }.Y!(߽?XAT+Fl?d2RUpV22.*eL>Z*( q| 425CJ\n(X;]:Kpgt4;;(Q-' P> >Zȥ'#v'x56we}|naܼ2pq7}:XT*|(.:>p}A}+'R{<;B7 cpJh`o…'I~9Jz<3/| `i$W h$r[á.'p{㗳gO7VGޒeogÍCUjg_(6RnjNϩ}̌۷iWm~`eNxөLavصiYYvvJ/3f|FIQᲘ,8eeE%COK}+ '%uu&(E\%ђ 0WYXTvP֮3LaB`Ӣm6Z'ߨn~sK(\ųOD._pF :{0(9Yңӑ ![o qGSEYaAu{䬂byw`.hvBVZCgqD&bUӊs3)k/LƖ.Ĺ@>c_7X4c«f}T¯ JaBXpi-Ju:?}T ܿ`y ^&, JKKqjkW$QH MR/l OPmiGĤƤ!F^ۙ !SEW ;X2\3MjD2@b9V A:C>mU`;:L N,|xa"G5VP6%$e"t'J e"%Durcxs~p$ ,|uDcM=52YgZjxk]]׵r1eGK<=YZ.-HONɨlz?'=k0|5 &遖vR$(^8|=;(g$oN.`AfA+\L D݄̀)[zJQqڵ‚ŕol  u}s;hrgh_hׄ9RFRSvq%Fcw`Ӷs[wHW](eoKI$-:%G3So8 ~XR af>kw.N a m4se 4¥ (?<Y*1A6/\IPFҞS-ga@+WbW"KK _^B`Ϟl|12h5U'ښrnzK{^F+}Or:Xy,ʻ$]^~=ې!i,䔔"řf%N6Xmv@|^Nʞ a3=7둋jE rXW,̐oEs8+GBadzjO.HC 8P`gVzj0xH~ᬸP%Q&NX l>4 ?ݬEo}ojȮʔ9bS#Jx}43d[ ;RYel* fbbhLPiq la\AKDS#uU% h680A _2\ǫQĉ WΠ;ݩR[9CA tyչy.#Ƨ'oܝ*dM튷l@eD1cr^@wyρ >LG.M=F TDL6J\!ƄN(.>U\I6=fR!4vf<#,B "9Tpr92E$B>.*fkZ3jGgq^2w&xkW^]&>*t.Γ(%1DktN mOD ^YԥX>L]Ta'3CC##NDarXe~4z ݵ77vO*zR+ O$X.//yM;ETb,W 0Z,=. у&+6$^),7\zW[_S[KRQ?Zn3#F8DT;g"Yz][\Y/&'gʏ-m-: [~t!8X wCHil=cndy5sX_ug YM}?{u ,D9`;IJ7%% M<=LxKs9{,k*8!-=X˗e*\$HNp¨1*4R!G ރ8q njI(Ad"`5$C'؏a4YEXL*#e\ NZ$V?E-3I*"@#Ov5z?yעU*d<t:;gy D=;]zdl$ Kⴿ>@k%贉4Hp;3Ǵr*#] n}S7=H*Ȇ`Ldqb@):ʼ\B/|ΈQWidzlJtӴӃz|}M0wzs=={U&ᛨ_g +T'~!W# {U㯷>^U?3#5e[JRJ";-+5y2KeqDJI` AӶ1M$3FbM=pc9edaޮ]clC('}^7\ '.|^e&TG 6=g(E E8#a߾[Y Z/ '7Ax|;.Y;? O$2swGX] G7OJNgPd6Xtl)-ZZMF@= d8u4|0'[^onj3 r^PSW:ݢ,.`+T70'兂zӠ?ۆFjOÁVHoG?:𴾁UB.BIxչ7rFp~ ٭EzP^"D$v"' H%wo^|#wijwn,y)]v함Dv33 x)m =31g,8]I Y٘q08\ Q5|~q80X3Rπ^+L`nsՊA4Zȴ.)V#> b 5"jGш`$~e>Y͵KZ2lk7. SjUtC`p2:57PpX t/ђG(aT5tXdB>$<:)QU[)|w{5Ԕb hd&;S&trnF*ۂpOT-0Gwj}_H}"?Ӕ`Ø{5U%הx ?~SN$:2_<(.7ՕX0 xGz:sU-t48!/$ d'ZJݤ'8"fٲ}{ ε(9l!Y=Y\&0ayrOnP @;qFD]%X@'Ëp\xdr=RTKzQ)˫Nu7?Xvu~Ʒ(x <nm>?zzjsHxA"k_r;쵓xى3^8\Pp26^S(êrt"@v)r+nUIxѡ;wˌh0m;. Xa|aSHGtj꣩@MHtM9 fdpK ,B/~zvfޱǨ><|H5{wo~: kե;25=(: w{ԝUpؖD&ǣ'~<97# &i1-۷aWMgulWޓZP][svlY!+}۞*nth"p ʃDmn21qOW"iW`UU*i(0~FNNJ曵553mcfoBՁcoTר[h$ r.Edtڪvm0^oji٥GijOkK sB>XtuPjM[011Հl;"fܱ9D>7*H%V~m3.ͯ.IL4o\JyA12L ŋHaϦA3 8B[:iu6$fXXB\c|Й`<#>k>1"?57r/Ӿx&ڒ.&OM``RBaύуQ.Yns9&T/ z}OK8+'~nGj%1J.:ONOgߴ33Ye2e3hR B\/ _|V,,W+[V:)s@C¸ o7 V _C'٠Z"1bX]]]s{sm/TQs韙ulX_M̩lP6緡)0RWU&A)D8('>c|Ԕ46;R(Y@_ vG>[R(xOpc=CaF&"g!$?rD  d7 MIcZH6j|qᣯ6{~"{o^Z ^LlXl~daF ,3c' {4}gJb*?Xn~uCJ0)lhۻDԾY#H(6TMȭ>y++;;: )l8sbD[j5]byf5:t Xp;A L̎4X 3'Bbr:l_֡ptdn~F)>|T΃NWZh bf|_]yk},O!fgcVkp]3\jcTK, o׶]< ;Yy[Q \J\T$IB!ݗ&e"Vo֝\!NcvfU:]P$)#e:Ñ)(05$x>%|LUB>O$V+-ۨw<9 {z;ߕz)Z\YS+Kp\T),S_B&Ѱ䗶nA}ےѐl.LMߞ.e󐥠YRż)%|Pg'y,4dv~Vh<+)ĕj>l.4aFIDÑ8'pqb<_̯čuWs8bQ^?fz]\!Gza:;߫-sv?\<=PW.V";'''JAjiiI줌{w)Rv٩pw`pπ~x+{(c(/ M:kVsWv>T{ bH[,4am}qqTGۘs M@|.V|pi~.qgS3H3ɃOw.0*(OsxGYYǸYDzʹ܂cr.?_pw6ãalt_>V&g\p0;} HdAvNF0˃l dYS!9=5΂f= b a\iD 8)f.Nc(|u~/w.赿[<ݿjsL/Ք Q@Mm 0?kZRa ҪDZRPk_QV%-$ZZ~nW)kE&~\~VɯG+R ,--Ŭ /Wx-͔p$r }#7ݥf|hf!rXeJBȠ6Z @AO׳']+R 'f{vQ=1Z ̀G3^%OMq<{$<̠J&~dzDAt;ާ"Lڣ#I)AC7`Կg*JDm?! 1h+FFe`^'MǸB•h",'H1h7h^mKSTЩ'axnvuu)G1ˑd1QH4|mDDi j$~?`LpdLcp 0f1Eb LӁjDfMFddflG('`5)*T(JQuVzK1o ݔDu"hJMhikX Bc"Nv}!g(?:Ɔ$~jEe r~;<2e8ugꥅpi~0R޾pxѡWKaMvvP9?S|46iï,Z(@}/\h)+DgD>ugkW%E篮!?n*Ǡ ?>}O:2gGsaԉłZ13Z'|r>>`oou5g!]Y+a7+BzP?{:`Va>䛉LBOIL( 0YpJJу-H`Vy ,ԕxEx\ސԀ@UCz"f  AϾez*DqDL5RLf5֑:\a'ޅܑKhbBze OоDa>Ej:B&vrtπWAQ-FT=}Tk_0Չ3:pDp?0D6S LSE">S` ` Im6@ @ uW%l5OO~z)<@5d"!ӚAy|7ZSS [Zo-Ê;V_xRȥ…BA%vV8?p(pq>?ê1Vs9تB*M nZD=C2a>Y{kv x^CYX }>`KQu `2) v"﷾~t͌xu=࿧> ܻ~s6x0Sx4 a"WBVoK-.Bs?+P~D{& G^ 5q0ܡ*MK6;#Rwr[{C`l)Ƃ?1UɌK&ܟ[B\`_چܚT /RC$XYIT*/8 :"k$.e)aL֖mL)`?_)R nQ /fpEra^!0 |d|~/WD^ yhD|VD&`hU -7n|~5t^ |K%4joO1o-\~c.& ,,_4cUu|v%5$B>ۙst3M޳}{۷e.;+)=yOJNa3=>zP' uە"NVrg'''sDb,8S>re5wq hȡ8vc@MFp zy".:z&'7MRN>'N┐&o=3tg-v8wN6p]]ikskUF ^\éH\OrbËQ^0hOJu:BFmύ9&ܸ{n|8>!e<ĥu#Mkw/Cdo($|neUm,0nƥ@ <{i! J}lnr?or`o=ϭt;˫\Q3=0C3gV.,+ddg 1`%)ܞ®~dй0`xM4ln Oqǵ[9AW65! 7gF q; 2zʥQtHN }KkΆev+K+˳1^{HK /<-_=Ž$~Tmhd 2x4P> 'Jrf +`x\v^w%rڏ2^E> mg}>l5לѷnN=υRl@4x5s7wX TU[]ynl(.Kܸ]~X$Old0/C9,.C;sܘ$!3cz:@KIA^ɫ|$g-CI+dzW!mCNF#o֣#!![B#yG}QNxc3{Q%pW+Q\wzѽ_Kˢ JZɅ/VF ɕ,΄}lm "l8N)v%ཷmՖ_o^ؙ)Tc2> 9tz[Cj̽8ŌxvnhPVrQQ~9B#t: N^;}@Jm>ls?k5Yyz F$~եؠST)QLjƤAi8@K+wgF 2S[\◑dpqa)sbǮf>+#[P~ZenYȥ vuZvNmnsh&!WH$-*63>N- GH%vs&D:4t @*R◉ɩ1MBUkN1 ^^]ʟ?]qe.r.5.JYٹm;YeQbi#epXVtBK?$'Q/|1H߽q8vHU(# t or A P&QHAD# ~O'~|FS$3-~[czsEO>ZHtmzfn|Rٗ/F$TuujBc0MD}!7e66Vl{ln #EM Btd:8θ;hu`Z^Wdp5Gd'xOCgBh2ԕ"1٬ECJV&F|(:F5A_ǻ=Ç 6Z" hvvZ6wϿf-/;7?S6wRVV>[Apy?U!) qw 0/1#) P?@*Ny^\b)ˢ Iǰ<5eiASy|QxM^?T>C#f? |V@rjC6lӣ]rsC8 ~!bn?oH# +vy?zW+-= X>S􋛳WU2L7rӲdmH7,-krI>N~!4RF#^&:20b~o?R"/,y&&䅅 ))gmUQBFiQv8^iT x.83J "f g4d(s  d1Y73yJl)8@)$CLyaXc޳,ެ&.^Cs$ۆ&0RzAih߈$W*jN+ό4#q%iB"|qSNm*^!05K)nm~*DklBC7~Ou1n}}i}<íovٟ럽_XFh|;)efTGFc>+_9TDӝ`pff!),5#ߍJJ:\)4C(w^}ÁJOuhy+C{T5,q<ۋ>:U5m430^/8hCܜ1 sۜgmc1z3 z~虽qVA(̈́{㳍HA*X@+8 ޶ [PZ#G-/,.JA.#T|{Jn%a60Y`8VЙ|8Y؇W>G ܀=*LDQO"(p &zETL4 B vWbevRaeӵekڷ\8dxIeӵpx|ZLH;DYb\֮b\Axtvjr$H0)ʬ# FUJGTT$E.%3'Wսp n1£p щϝ £>XmuC~EFv}}s4vcj,w ,uMA3'.p0o<)ಹ+/^g##C˓!O }hZfP @b֖6ؑ?z{3#w[q S^Jhtx]┾+6Wb;.9'&ڼ*)FCE:~3jN./Ȋwkb`a:!:^+;-PQfCenx]P^TC{g3M5}@0(%|5T2\FMfhNЋd=;?A3$7ѭ̹:k0YrR1u υ81shKy,vz1n wJ fmт.#QuMI:@@uDch~ ӣ)JG~skW v}S=/ds8(9HRWi$() lzͪG⊞6}B-8zM?MD1^+:5+)rslKeqy|J.ɯk{Q C8@hjA!t?D&$gMpzV׮]?x_C'hHH K6VIje J`,6QX dC\yjb\*/('xvm+c`uCug1_qj+HzJoQɮI;YQMb~*+H%U% r eAw޴ܿkpbq^h) h*;0<]6a}spٙw"hŮ>Xnz>OBulbqj&8`TiZ<ݮSi9Q4?Dcǰʀz|4vq52`R`R ߡcXd)z|>O8؈lB7QyĔsR[[XTu]r)+ut0X\X4{iudrtuN{PF1A_dߥ;1rfo.nL~{ld:^[L<$3b-^Sp"5ee|? AF6^=q0PgT,=!NwD~2z}b1Z˷ϻ"=)sgWn\^x6\\X/fNP&ޣ=} i: 0 KsRl`zǎ+khRrs2+9Ԝz>'#+9=Xh[Z.8A 2)u\Iv08P{Zm>/_ <2_ݿսk׾X-|!?h;*dYX,~DxVFgB^J+Q]FW/uIc4y<;#eq6h0z;H! 뿴z~zC4 4G߭E[@u_Wz )JEGzTU25HuH0,OfJD$2FYݾg[ A `F+_Hf௃scUa&HL\pPoW#5%.ؽvLQNIf2XY[lMNJIIɫW6Sh$%@>D M 9 -z=5/hI8pb~o,>K@hp.=Z] q pWkeU%DrH /Sht:Pm ɋ7I eH/NM\WJtm}>߸?"3U}! g Ql4B<qqfFm)I)`-}᷿IዔMIcN|Z 26<Ӥѷs:z"Av;?|ýDųKZy-?P(TJھ=(3M?v233@FP"Qe*"]FLt7vifCt)ttS,emRg*~5&d٪S};}<~YV>~m;ZU|MF*VW<-I(.#4鎾V HJɪC 'ڐ^녊sY >\.]ۅz3P~5gΜ'?p;,7LsacĆ jv7o1u) BfOw}+W6o[5%b %JZ]ur&`o^Xj 鱹K7͝mN.[Url7W2sv՞cHG2-MT=#s= v0KDokkU CKc5_/_}V'ƭY*zg 㓳X We׬ffUdemam뢮8=emccW$q;7$%_/$a]6 8n9&̰~qj_u58É1MM@c\e>z?U9+3^ށh㖔 ;n/J^?mZViQV%69~O҄V3{6g\db]vvUZBWW&R=2DvY/NKZ:doj{ /zmWY#aCe+a;4`zl\Цz_8053_Ԁ-/ \;swf#`u69X{[Ɔ+S6nHZp3/]u8[d7h٪իw \'>٣+ٙ7*{l= ⺝HYyE]"..aڔ#V愸ce{̛֫N8 X!V5%{j$UyF܅v~TSt vyӋx+:\;7abϏOݻws/Mvp72Qv}td7Qe}!"ښ_[a>䲷N5}pf8*5$ 5+cZtR ҤmYRRY2+dvsVb"5>cx}!}ט((-)yT[hȞfM# p‚Ƴm-ˀm+{?)xXwLw0'͙7gL`Vj¦)9ŵ玿S~t`ֺl Ƕ/Z6U"&gU"`.GyvꚶfVmտbᓏ? 8`u8vx Dn r02׽ק9tjG1nj| U-}BeUu!b _)WU+P[[G=:wqJoזem\U_Ww$+{7//\~sUMWՕd%-}*^-CN6g֥+ZPk8a0x^Wr\}bA\fɶԷq=a֣qf2ךhy^?xu[";;x µfԩ ^=51zyY霧óKv[IT_2E,sneeUqvj³ɹ/{!ssmړG//IOL\WOVSj{Ӯ)q+R3rwWTuN:hgiPܝYXywK>6 .?m=ZY|x]`84 匭 uKm7/Kؠ啕3#MFމUQWglpeV# 00CڶIXXqheqK_23H_ ˶ZkmjnwOszN[%.Y\%>זN4Aa vvo,==ӟ^$j Vfe4^}54tp$f1jhtР+J b>@.q-iФ:%bךKKXfni4z3Wh?[%VH)),!>amBB5kpg׾-2-eC摚 k.LJpQUzy dc\;jh51mu3%Y/o],\!kD G+ s^}xK'\qy(ο'؟.͗\e,<@C@gPcUyebdT33 IDATtt%%zaUNUmMu- \WZo5:-fnjﰞ\X-<o9κCzֈ5 'ż6E(`q#yE~q95yukCֶ丸/&&HYJw-)+Zj(fMD %E{s3־uq` ;N>Nܒsd;߿c纤nz¢ykW$eYw‚Fs;a Μ\j'X~X~kH+P)}"™ Dr5`,z Z}߷$/mɂp7Isxׄޱ1]\ >?^X޼ N](Ku'6&>r?~z*xU떽*/>ѹscWmS5 W3e1-p<]U{ @hWIkE;"#۳oih04h66b5m^l3*3 %4:x8:?- /(nty=/;m);=61 HvYxܹs{l#Yyᬡ|ñ􂚳vP>5-g[9>h,ug 8Np`Eanm[T|D.V?NaU^-<;J8bOzpEɖWm/1t`4q]cx\ow[J3+ꟖByM_f)-OXWԖUdc8~[߯ccU^\sif& 8z@c"X'f9CCᚁiR\7]ӦԨ`ypA t}T7mC޼Mi/=[BƮCʢy{wUb9vs 5::ʚM/=᥄g77?)bO\ef=9yv޳KR,ٛv g/ƶF{Fe0466*(9as/ :8w>n`GBמG@he0޽0WoY!2q\Ÿ8s_^K6T?z;G?#M;{m5v@ X~kV7tr_}oJMxyyss'n/.+s~zhV|ʶm) V,Y^;?~zŅBlҟc-ayE<"n 8X/bjjl!QpMㅲõon,^^X_ \\a }tֶf3lq;^ڌzJqUsׅMMjݻ7%,7ǟH|!å[7Q֦?)!Q Mͅ&,ee\#bURj6P➊k.VV ix XaOq1zs3WT׳u+z0+.gqݬK+RvV,(I߂cO{CXݻY)圫yB_U.爞9Ϸm8e[rzA;o+<;R ?ggΝb]IE߁WXx…s~ 2sp¬Ŭklm2#l& W.2gr5F"# gU_Vf}U+ٙSV/^8~IRrV؞S(ʟ˫9Y]}5{ ~JYjUER~^XUY&Vy+_U%5Dax _Ӽ_r.$6$veVYPC<̏_>_5}%t|O{ J4 Y-N,; F}3:rqS{Ty#3PSavXQ!-55#a@fk7&]WHqq23srr"=0e}fҷl-/.Z8{KJRmwo,ugnLOb<ؿ0{[ 9? CvIQse͍_=SoxcgL׹,AhUc=։V<±a:[.Lg̿]r˚@fj Z -K_K)x́$㙳/{E-X Ԣ֭ߺLKAo`/C-Dp.6Q\`{&{`?|w'ڛyrrmo^^ަMiYy2LnjjKiiipOM۴)#-Ljl x7g?INfGז^6+fM<{MON&_k/\pKűX!U#vgO/շx3E:}>jnka@瑻XDdMVX]ĺ.}-zõZ .BV*,UTh^2PXZVZΕm;~EvmfxVzVZƜ佒!-e%$}|\d\3$C;WL+U5vQ%Jb@#Vk?o/,,x^8.d {0־|6TP( ĨA`wg!fauv]k;vs,^pVQs'?>y"gs\۸Q?wcj!cMpgcG?R{Ȼ%V܍r'']YQ"bYeQI?feڝUSm7d,xDo {DR.XS]Ywyk^7CƈUUnkj@N1iL zEۣQ5eGIF&6͛77z]]7>]Y.#Vtb/n]FB͈Njq&`MxSMxN5smj"4 X[_Xp"tfl2.TUag'nIӦ#ȗ{{J(0VԠ }P4^LI Hƥ/$,pwv[4$$9 7}CCIngu{  +f~: .֞Vh 1[xpnWY9#f:z8:rRC3W[*dZ*. 6U$2vH؟'u$K<_Qe)F(}G}|ژ:g>gHr}Nis ivz -gOݬNҦMg+Ţ5-0p6 "@4b9> h ïA4ELsMB>J4 2bp@qeҧػzR F Nwɇ2`qypABЃ@ڠT\` 8 q NݙSó or<^7$׭ep 6J|@t 9_A6H#æVIxh.XDRpu&}BA04b$8RGd_}c~~^#6X?/0 ~dz:r?8էP;K>@sdjg{Wݟx<>7I7>Vt9:W<};;qMnU^t[\%V X:8.EB.kHYIly0CtPS ,a*8 0 >W I$@C=p;n>'֒sA9?T \ˎ.@| D{$BoGi N EӌI/#a5oPeRcG\"{*"d2t0HGWM h}:D % A Ѐ!и<$InW}q'Gl`j= 6B{739wحcaھ1^ >y03OQF|?PJ/I@W#%Ys މ|Ǹ#@kK#2Qu?>廰RJjcֱC>Ed`&ZAunp3pN B07iQ4CC%aL0C俀Ƞȓ5{CxSPoOF%tAxSꗈ I?"4$ /+7 / Ƿq  2ݷz=`DGhFPDb;[1JX d~w4@ `ԌH0ꧢJ@4"]&W@٫3j'4pGA B @$1Pt'\쀜V"~b ! BG&$@@/ pB b2(73?Pnr3D£&K d0*C*2 ;u:`F")-w (>7_ e>Dq4$s%a$~ C}nX h-,:H:90 d l'r&X׭]h<V´Ff4pL!;DH#ndxɃ  Qi25GX~'z$} d)%Ɛ~?J/%74!z8sG\#ff;D79 !:Isj ́SLP#dFSW0w$0Qx2T2E.C|KA8Vޙw52;M p @IF\i^h&p8 n)eCTpI />a ^:P{γ1`刑쉉S#|5ū;ۘ&Kc.lZ1XN@!c* @j ~#K%S !oJ>OpMIuBt Qr])+.>vU(jD+&#-`Жv'i ~Ӣ6d:°h?|)<A3! -˱@rRB!Rg|ƾ2xx *0K@o[YxG+z{Tq nP$n/|\N vw &H?@t~ϐm.4Npc`6`@ ) y!<, hͤN;H"BZ( h\?hYHW%ITVSD q( MgT\ܵa>+A[&0Pd@CsF!i%w0o_=b!F¿ - `$)1?f+r$Y} @*Ĉ3G<̇b*#zcwlX >¯3lBh pGGFP0@䅖KvS3p)D($ÿ8&K TcbDH/}Öi^G8 '>S:`^_% ;E,r 73pou|9[0,p? % Ef|n:QBca8$"1pvX/"f@dH=? 6a[诎+D;2ḣF$9!=C40&ς?ot9㲢K\Q 4vB>Sl5.&,>R& Eg!(Q~9V] 0"4e 8޹\,2Dcl1D(#f ?@*ԝQ,dB]7A88/H Glx(ŁĀ&/A pq+8!&D&;|A.~[d})rI{z!fAQk'V{a&Sۛ)C &>c ऱNlLIDAT(|W p4*MDq@}`{wkaO|;$"20,L5ĕh%`Jbm 7-6 B$Vfoe 0bR<?5s@~@b?ՇHPEP4`ȝq2c*Ǔ"t 4-#wmIyll'iC \.W?)`,JqJ!LH (!AaB10pd>{04D/;*$ '5i6"l' x W $Kw)78?j?| *K 4Bm {}=B+Bl)ÔP  Aapx70\eR~Jo9Ii(@a"H DdmaJ@) &@#p}) ?9I!'aR\ T~ %@$w,%TO=:ΦRbX@cR(,BȤHiW0] @^6%0.RM hQ"H %1@p(50= 'ߓD6]J)瓟L D=@Ǭ.%`BO JYI (?/%07Rz zJ`uk@bz .6%'zJ@hw)J +:NJ@M 3l?3%1nOf[ɋphN?`Ϸ%C%% M &SV?#%'SM +zwC#pD}=4fd n~J"?-q؜vQHb+-1;f:EJ J%)^ٔkFG)F<M &-qwF~ b\6% (wx$x{ERp|t錍 bpzaşd=ÉlaJ  C!X:2ơPfSRlJ*GM  hFHd#BO"Ƚb4S=!a~\7ܳEh6oM4^nD ؀Fu;<>G404a6%0P\`pєH)ԗP(HWSA"MMЊa bjn \щAnc`~v?ȆiCncQ8j;DS)()Q6=$#"54B~黔_$) ~#,$IDnߥƢ)[K d@&%=BntQw}p+ vMX@l$#,1 Nhn7ZN.S!n"MM8 3&LE&A*1J.I/𽔀<.g5;ry@yܢQ. Hv&>=6t 1AX 4o0`r м|71%\U7:M Y%+Xݠ#0%)2 Xϔ"I"hL}a4P n#B E03(" @ q9^j5wDl7 Sxm:`T *05A As42E T0T Nef?k*yPQ% |C  =>P+nn1p|68Jq[mΛ]]+es;)p=;pj=8< IENDB`libdmtx-0.7.7/test/rotate_test/images/test_image15.png000077500000000000000000000743731423156660700230010ustar00rootroot00000000000000PNG  IHDR?1 IDATxw\ՙ-o9r@ a 6<كό<~a80c{1l"ʩZsU]8W=7VU1KTWݪus4ad~v1:~?mlgwe~Xwf]VKȚ>|!f46{g{쭷7W El]rI]} )c/X})Q'Y|OgYk EOphX,W\`ȿ~̨x ,ssT\_whz#Gs'w9D !E<D U3͉nHfg)[((<cp}PV&xϗ PYE`˥L_~זш"ˋ@tT|iǦ~T()#z@I}~KjӉD0˃qxFyrRw:#S?mfaj*+hłrX,T*Q|N,]-O!0&Kj^/2OHcc£O_:֯G,ƙ}8H*+19v0l|0wev]]oLM?+hөz U۪^Ͽ}`YRv }U]Dn^h_cl~l6-**g?>s{<$B!t0`2nGc#ziW08@f bhx6tY0 p PPHtŢ  0} DY0hTfLt7`fF3l(,@ q atL{l=ǎ!L{|z||L}@`UZdn7l6u/(ݤ vLJ 1*.+ dC! '&Dn="z 5u(C{{1vjU*beJүNLJIjEJF\燐ѱ%%*@cc m>A?7*(0fæM1 Xaÿ́aF:jkbHGv;BX%%hlu:E55:rQR"q0 DWy]T]6EQ(*-MօðZQ\`Պ-x7gvP0XaY!uV s2 ZU廯cc͕)uuapP憳,JKQ_/s`(K<ȒeeXD톇qh*fÊif-:}>:u:,Y"SsӮc`=3cN s7-deP~3o 14pj0xW7 ոa7 w5n , )Ŕh)!?B"a70@0~N0Q <\77@~?a EWNA7p  ђsEssxE N!}SS˚(h,ԊK贒Fx\e ea%O#dV-yxDX l@4SdAN@7ZA0V A~>e'oYa9h+gi* ;?M&P'$zpR \D !uՒݔѪîF{]o6tXoj7ZHbLFbΊnE:fLO>d~6($,y頽d <٘rdBYVq8syw7T[`dUUYX_:mG3կro094oli-`Ļ2Pˆ e"g4[*^% WcȒP.R7pA #&χ6pv@~~`e&0w!Ħ̤2 7d v;Z[ٙra6n)z&z6c%%G_TDz\ H*!?v;v8Z'ݝ{aT(G @񈦀-D : dY@?PII'MnF Lg ˓uHL BF9|>V0!ύH(&'16QLL֌`dժ_aJty "y^Au) V% M#QQ]XHK~UCzzaݙlA=28AEWq-xq8D3VT8-χ!LN"/>?'1`2R ,pe"DQ^}<#3p6|dY . с>x<z9)xߤkoGO-@WB!qш28DHM(AL߳A+ZDA̧30\7rP[ax>fSS @2x$<FA r׿NqS[a|>(y nʘnaaX93AihS, iKszsږC 8C$9gvz1>ak ̣O/X+ϧ°rf$00\!^H3‾0*D Incc$&&d$~LMav AkΔdub1LMQnX+ yG%()S~]oۛg| YhoGg'fg1>Q`j[^/^(Haafhr(kPHX*|>)zm `fa6-2 QVׇYD(`[brb0cc @Cy9?C}ʡE$@ oĔ\gg13ϤoKU7KEc arRi6AC^vNZ`.z3Mz\LBy_bgrļ<\CYX#E/Bj#b˾! 7Tv:((@(㘚\lPQGǮhKL&1)H>1 ZB$O~N']X0ߏ˗ 9ddcȹt9 jNyyxm?>aj = eEsij\ժuÃt(̤Iex##1;{MPS&L`rH'.5IR|щ B瘶%%xPT*#:Ckk>X^X bg]. @[ !{%[D?>1LOK ۝QE(Rb E0 \.Q: JއBUƐ1,Ҿ͆ⴴ xA4Y \YB`DMUngn}v( /W YWTࡇ i CB~axvCCb$DHd!i͢鐗9P$k.N7WIs A#XfK39eQ0JdvD˥ɲ7|,%둟LKQtrFGey!M-n7a\1yӃ>LNd `v]]-@(50pN}}Ӣ 2~yJȴL Zk{Ûo.8FaHjԶnb%dQTJX8v mZx#@S #D(O0Ȱa%|X_:`'[`.Tf 8R14 ;k**P\bx<\Vz)VM&~],"ȱnğ4skj˷HdZf*=h &TLvTR]" !V=xa+,\"r s$gJ~)2Ш.H4$*Ų[6 u׆˃NƔr?!^xr}L-TV Щp:17SۋN K\#Y-=8O sW!nBCbB Kk_˕iX,t"GQU\b1tu뻣w`|^f xDf̽BVㅆLb2 -B]p1>N*Zag92kSK':S9E"*k% d8"DY?X+LA=> b4w.HbQσR*~ga8  so0虥x>J--EO\Ao/~<ӪcR,@Ő4OA-A)Zj}267t4Hcc*N+S qDlx.b AnNEE(,ěo?BԦ>Jѱn6\H|x!1A9$9DR9LMQ}:lj CPx[o  0&=&J7'#Z ѨkHFF*Z( D~*OH"cm CeS3 P`2!?hj2k_S0USR-X zU@:JV6 [\ DĆY #U6`2,^EPRbPJ7wdMIl9E84(@c4rf_J1hHhB @m_J{&Sag>Į8 E@["nr !Ke )w($/EmWфBp%%hjªUظLa+,  (,ĢEؿ_hSKwU{$cq3(+CY{`{W_e(bYg@-;2?.QlV=iߊ 37+P\Zף#fV-Xv;;ٴ'11 eg2SRWsb)d$KUz/x<-Sؼ"2 Jd DF6'I$`n(^~/u)J)/OV4 m@aa{7YLsӚlHZ`DD8ҁߏӧ56",̱y⁩C(ՅE01H㘝=OP@4I /%\LM **O76:h Q[K "cx ",݅$96OU;2" Ǒ:;y3hIMU1 #~^(X9(F(1I qY\aLO!3dx8d$^z kܤ1 pt( Uaas84^EW/ K$u$AQ|cr !mDQ.Dm9z?.^,TctZ*x^/\20 IDATˢXf3 tvbb€O}B̓L)7 |`YLNjddڒHwÈ #%!6t ߋ|j+L,GWZ1n7FHL=I}0##1yb رCњHRry d@44$_ ۋ'Ȧȩ0dn,dy}0D*az@YھMN~ KJ _͊Hid[:"XV[ŋq睹bUH H5D@W*}kj+ I˂O@"Ak+aL(-ҥ.Mj >zz00mҺ.LOOH[׍O]%%\bYi)7 Gs$J<~@mI5-(*B?PRmbg֯Ǻu(/wR7F 7"fV+ē;lغqi#!O@"1 |!ԪA C29f3,AC/I~i %Xa!>yL(.ٌy7q`/aMwSS*d\ }^RƮ]ק. gM&ر#iZs*08-׿QSñ.]⿗]9XCCC i nUT\ Ӝ"(ee\`r44HeQqNM>P[a,`{ yy|Y+`R ==nUVS q ZR<\9L>9{Ka`nN VF le9 4wiyjA'zx'M -- x ԨDL9ŒR~w`##R)(Wzj( 4S=3#skRpthg9\˗ގ$tFoڙr(l! s/LJ41$m@y|tJѪ2j16/WbF/Tȶ[i^C,mJ&!֯7O+YyjU?"JeDOM0MJ V!H];LK̠MF^(:7!; v_4Z0NalZX SdM9dƵH)W%/j+~?^+q#6nDm-px.) :Ә-*+ X6iUӠM*JV6Il0dʣu몑hy@("pE*aø~TU91Y\(iA.-b@S q`fF8Zh/ $@i;t4i }3iXˋTׇ6>ӧqnŷ%06&C# %^PW@ -XKF@ZSAt ٩mmؾ"Gb~Xe1= d4{_^/?pݽ[4xM LAEEZn# 28xPwP7uO6J,ΝJ%eSxr&KhoDhΩ!Q_D^MI2Rux Pz{#BΏ(۹`@i)*+֖tznW<2dDN~a`2Kh'e"VPq ۹$=Y?^Qu"uzX`ŋ8!1oS jVIJ }^0ndڵ\FǓ@t:46"?ssi,k}r8S!&Lٜ[&!D}=gx9:v$zttq4ΝK@} `U/)6|lض V6紦J-m/gvƱc8|W e`nTUuq JEd **8C CK"?2żMg0BC^SfM&lߞ&~ʛZXrD2[?gѡԄ'7 e0`.ܙ%E];/zr d?˦ZEE3r @Vhl[fVH()>-[mcDN'a4fG94dK1س=SE9)X)_ʈ[Pɡ( t O_~?q9`,jjqL&45!// t?XPj٥=ɛbӰ#RоIniK`@Yl6<.\F0{|طƩS8zO{{7֖#eem J-] pɄ' :JZz$,D8it!򼾩) Ar7݄5kp~Ä 0ee7q(:; rJ?.}rTV+/ J-]Dd**D'!sЫQ.z S6";: aB.\TjEa!?,Fjm7c2TU!_^@ooZ eعhnNnhoOb2:4Nr:qʢ_ dF\0=q8wWetua tH#)I*EWPT}FGHi \'' fv;-ŠKUHۥbAB(.ͨ,TXFI_Q 0Gw7_dY*aP ;s==\͊bIiXl)[y06&!y!2heϗ7KrfS҂XuUKOt NY[5yJ6`Q],]`< eQ`ۍJ,YI<'j݉ [a,G[%X W66%D#xXԄ(L &^{ =9/\H YNM FQ-Y@D ڸ7݄Z_I%s$xDJv-7qtN`0SS-RACyy (/\9LB,?"ZVGjC4!EV^Eg„lv_*ZNW"#'Lc#-vQ&k[$Wq% =FXF#-æMH,?_]:đ̡p^$q2Q"=}&P_[K d4EffXۥt:磪 'O"F"j m!F1< EUVƶmؼMM2N2/CQ hiAOߏH_:-SM,#FȮZE "( c ՙ A6&ccxAY#%J'K?afe%֭֭XR{Z%Q"/?bt o z{V܉Ra#@=n܈+сӧ%L8tRvY\q UAu:45%]V!e? EgjN:ժ,~E`PQSrSr\8v N[Uĺ(u:_۱q#W%VW֛fV+ߏo|켩2=Id2xڠ|QEZn8XZȂF kZL(#x03mmt7&Cl1j|ؽP˵'ߞQRҶdʕX+WytBĜ/ )aR [aA, 1iroP0S_RkPK'̬C2Z,$t)VƪUX ,xi+Y-鿝JI z%ϴB NRR@-w{$ťKBwC`cchkS[]ɀ\Hnrq3d ny,F|cX7ߌz}62\Z[q0Μ $ 6/2(l!00Q^ S@:*+9魷bѢRD#<EBZ: UA-=?_F2I3ӎ ]Պ}? T1ӧq"yjo^eY^/ [a8] x) ]wSB \)0X Ҳ<Q@ZZhP8dNN16%P\J>jU4z [`&\_'NFj+D8XN`8) {^;=<bNى~h~[DZłҜ(*TSH%m}м >[o娫͆PS[aHJ7lzWP1:6Y4M*NqZi086__`48EFU\7O! BC/hjC0lKPD>%v%mkd6cqlI 7꘽qh ^P]O҂p H-WTȈN76(}~~>|<NGEEuVY(4jeasFQ^wu(D':7S'i8|I+iqȡKfX,֭x{k_K֠*+4iAX/O\c'S ˟XV# VskK01q%iJq  CENOSB[09I8cp_h!q &'} ad$-ob>lۆ+QTgM޼6iø.z+vĒ%;1.u a( Fy *–  gS c2it'nQmlF4gqxJ_\} k4| ^-SOS2GX p8}!Y0 sı!d,َx-ڊImnO)A- H8gb@O0>{Gq$ܹS;X8;kӬjŊb0ccj1W!aD"2o {b2D&-DdBY֯ǖ-\hvclnb3r&&VĶmش n7;Ÿ|,}\px@NSm tr_)HJ@Z$2J 1N$KV8]]'#1۹^ 8^` ͐ `>q4c=0 ,se03gS>v9UK7QSի܌kj,AIX3QrU&a\KpFGGraQbtLB{;_]l^O9ff@VDPšCطU|C|4o*$gؿX2\R?ΞŊg8̳=1$Xn7zKؖr()8x=9NѣQJs0W1<Y^^aO bpͩpHUs3n 7 O<#Gt8 %?ɽ_͛q#}5?O`vsZ;y3~VoB chp:Q_ p8w?A2 ")fw^ؑZʥ‹/! OZ 2IJ)F@dseV+wR}*/mtTÉDjsDav7c6NDb訔Fw^T%K8C"9x=G)xL(χX͛};/ł3g_G  "ĦMx{};-ӉgӜ"ޠXDp-X^X,7ľ}xmNjz*+c]H!D ф kzX!q֭u cZQUO7s>5%Z}ߏ^qĪU!x*̯S&6lH&X odž=ػKr>=޴`7([ǶmXVF3"u=*PixS㥗?PфP LE`^Ԩ 'B8ygϢ>rCuH8toWW.}B2@}}*x<ҋgQfƍ2*:)E Y:Sp ffՏko1Ff9_f-cׅv8s? 'Obxp->ӉK΢o_}Ϫ*?{y} q #Vj&td IDAT=d[Y Q9ZԛF ߽z5Q`Bs" >ΟO˱*oT CZAp-޳{ސz+`~\mn]w ,Ҋx^qL!: ?Ν˴u=x& Dv4mڄݻӃo~"&w͐ [ap۱k|+WX `jJi"HCqRU?'E+W05nSE)Y(yFdAYk*ĎIx(,ҥصK⩧(i\*DF+i9dzɔ,'Iׇo3ODr􄕬!1s^Յ ʕY0YlW4;^vf:' uF٤'& @Ú5XF樤(0RioraqO/p­rɆbF&SLzi yCDZxDaA,J=&. Ãg12l8Job Ԫ+WD0< NLv6f%3c1-xVDH*L]ݚ$d%0CmQ,PY؁{Á|~ڤ_=2+W'ZtJ0<|/*'YQP"U$)?_ljI"!<0 %J5j ,XyW_T]SM

e˰d|ܯ@v`'8n׋K\eers8d .<Hm"ŊT2R>-O9k?zPa'!@mm2ʔZCUkID"z/Yέd5 507Dpg)<+FE.3DN @]"&vMQNQN~{s)a1pbnQ7oN׋nK&'*Zp*~_4,?j)4yNC~>jji7 u󣟥JOn]/Y' \\ԁ$eop8A\\W@2'ۋ+Wp-hn6,cBL$4U9P{ rD[p=iTb^&)p-]W%S^fҢy,WEj ^˅~yy 09):$C  KV-~QL&sMDR|^~;387_!v]Vs++O0LgJT z(O'6]72FFC% Ԫ+)lXm-dc0K$Fn]wrB!x{e|+:A*a|Ki£ .%Cv44h&#IRf~KsD"xe< 08(S bdwj<6ST SZ zzʁ!)/TU3`㕶ocJ[ zlt+^CVbx^Ҋى)a:sX(3v;ʸFS])IWg'zzp'L$P @Z:Q?watv)9Ԡhqz.PRdFE+UUXbxb-x,a+Պn77s[PؼwߍFS}Vuⲑzz8;뙥Kč.7:@[V$y⭷p8ΟOӸL@TBԧ7Xaty^SI ֮m X "beǕ+=pY!R@1PSm+JX*tŁC<$ Y(ANNupZX(3f$A DbЛo]w[PUEX FM+h}bE]=󡭍3UURJ ͆}^rǃF9 Thme^|Q~6͓ɐFYDg . GE޽&*P,}e暬ԇVҨ=;?p"/O[w`g8sr'u vi)jkw}=jj9f*t`R2n>"2< #b  +XV#χ'+ 'eLV8&.]‹/qw\a'?{-ŏ~DZ*'GG曓1djd4k2x J-V**Y.'X ~FLM=RKbRE>bxM*^Uz& C`"~;.Fm>ZZP\]cuw+}0Ν˹J'D,&@=Wq:;5~\'j6oV$Z;--LaRK C!~kQ.o^k|sI'J8!/ɣnG?~8**I# 7HךLΖBb6J9JѨl SQ8VxccpׇǦMJ ~vºu*lFRAKxN¤btO?_?ux-*`&̠~tMXX/ <.\=`ժ l_yipCU"bC%#W,XI*LeRաCx?nD"ZEr46IEO R4qF׬Iq]8wNC2cJ+Yފq*AIX(YQ|E\.55sVT;pi_xeG,+Ýwrڝ3=gs!GM ::kڀ{/Vt8P[jE$"ɫ<Dv(*Bs3vX"myvA}Ie%6l͸68_T-Eav]FdDu~/ I,BanwqЇ*FF,/hm6`Fw^QZ p[!)?G0G%phm%7a-]F#UH^WB}ϑFq1nۍ;rew NNEb0ݸ&sd[*eEӸ)j8v }K)]c>n^-uE!t:PqghP@<%f?Q9,En7~<2]y>.$::UCAAZ"FE{ vfgOF*R88bND"8v,XߏvIԄ-[3{IEtv==Jg9XXw J?7nć>olLZ",+Q_Wb׺uяЦlx{>d_L:?;хX ##xe<4Ο'7`z.`^%%hnG $T5)D"9X3&v_/IZBA[`y',8|Z C}K@ף;rӃ&JkOAo/^/.9LN*'`q!^/]͛9y1'aPU[,e,>|]S |mTKbgtض ߎ˕~qhpB(,vHe5%SaxgRq=()06Gt:,]z &񘟏K ,?-*R娬dv? `l?/$ZZ nm$dE}nG-UWKCg'N'16G]Y]RKETd1wg 3L$鬤H a_0 MAfnM]LTy̵Ukİ1KaO"$,dtx_t>}cjJc/}y뚚з/ %[e 6f5GQ̔QjgE^*kײv-/2clly52/ !'G(VVrރ } &pf+|CI|[ Ν8i0>Ad];M ,!e7p-c6oP$$._χj61Q_ETTx-Ӈ 5dY駜;GM YY]p̌\ɨQZkmSoɲe@u5eeOW?(RΜf MnNK#5Ք ATWs |R("#d޴abB۽zƒ }?c3۝^Jk9t(3gJOs3/b3lKQ>KC{RSâER+PS?NNfٻ(yFYQR±c9c֗!`^Ƞ쌓[[9}9zg~63n?x b~6lq6w/?Ջ֬avUGx8nOC7\m:śKtwtvbK|6gO.^ؼcYTs>[ihٻ_j W$'#I7s*sj~̻*;?G[)R'+#}ND~el,LD Lo0yoj-[سr_4=x>_wT^ب!!a>ݧQQܼ)۸ᤥ?ni;4y7%fNŋgYS'.?9yt "$$wGΦ-[J:aK .櫒Nr9yJm-."zFrㆼQ:_gp_k;*i).9bbafSU;s]4CXLܿϻjSӟMMrsyqpP[K{;11SVFAr SXHs3&y\LD1kǏsqqAWǏv-viB?cgP\Nv6>Ư-(C9x0kʕ+ܻg<]\O9 BBHMjZnj['l俭UvkӦwij`q2y:OZѰl}leb.o_ߴDd$7q#h/ž}U/5:`r%:Nx8l߮~Uv@0|8Kf+*rc2;͛k]IefW&\a;/b>61-XU@zzvk":~ܼm™3??^#B|#:r8HJ_whpY2*+ v֮]#4T39TUx F&9#G$ !tS#"X";G ,)/ƥd^ξv1o$vjjd0wN@w7Z|ꢮ+W8|"zFfIV `&tM}CQ1yq#~h*3?^l^}\Z'De%{a"~ڍ)][rKJ2\fd(ӧMs3eel$&2t(W}-3>}dFȑٴ4Is11?ĉsE?p.՚{DFJH[:!#Htp1O 7V[Ua.;&U̶mlc1gݻܻ… | J|<3zlp`9Zcxmo/-T?z\1{ʛcjjjvkÆ`|"C8E<b y%K͕H[ !=ݠliѾ0RN cw/_~ԙyF AYT֭sDEE}=3g2lrV+YY͛ܿϊ2++(œPU efWP ySWǟdؼS}p;[s"8g à=ei))n1[0ks 8NK'YColܼ+{LIqK|MM$&2ll?}ǵkZży^?!8}wMn5/f|5Uc ̙Cj*{(}FtZmUޚ\浅:wWݛ?Q4Kf͒NC,\Ⱦ}+I* ¨Y ΝA6~|s=ßgd<1޽ž[G-@P;B6ՀB_,{ټӧq8$/ h K(#5r}֒w޻rg)qAh(V1e F门0r$u-~}xyZ2W ̛/ݳsc::7oRQa\E$nNDC׼S@FqD8Y srG1͖-Y-Y]aa)r(W*( R];gO,enu3CwB'qES}GK ˗{-)]PboyݽKTo2O?oy m.\`dMH0,_4뻖ԇpKU О=/Yѣ?QxN [YEL}[ZN8G˓:$hX, N\Jl,V1l\f[]KY=z0rHjm>t0\6>QSB nh.PMI{6\"}|S SS_ եEo,,WWDGKzfdfҳ'.с*lQOSP@ahzf@\NM !!tp]6fQS˜?_\Sۃ0@;el~.=0|7 7%1&oyiuuo"]ܼ+ XWt 5UAĎРQP=YY}Ġ(| Ok7~\Fcc~-8\.gi`3 oe$%%odP߼IQEի))\_y3ʂƟ_T ~4[ڋ0>+V0x0=mm$$0}Zr֭S ܆( ӳkX6nd8UUcT͊i|٩؆1|8:֭|/632h0s8 BP<驛1O)ݻntD{pBuOw7ׯ>sߐի\̀\Kɓ|)۶vl,#F<!УC2e =]c]fJ!銚.ѢǬn'Ea68=v"ȱ2^v7 ƽS {*ʼn 9:U:;5^aLfL,pIqPxNdv1il y8T~N J]c,ZDz:Ni/ANW6WUZ6YARPfqzk e#_A׿R{&gz߾$%`Ac &O&33\,\aa|Kh!?k;e iϮ`' C_0bLDTmmCn./]lv~Q=[9}缁+ՂNn'4qXbA:FDCϞNnJs& [oE?.th< szm0f shFO845 )on)_Dɓgiizr2V+{z5ׯ3n c`7xzz cE8PK_ ןbעEқ pH)Ō44ٌL@/£ 7ذpe˼vjj(*bZ[(űmGr=C\yyؽի#G=@m-۶qJ|<7nlde1a /q9VYVVeXofC~m Mx>Z-PMBK0{6=LjjI֭CU;`=z=:UA>{ۓq8(-uۿG!PA*/gz֬L3'Lॗd\BAk2l\HA_7(%5!4S'JD j~91Ny=1BtH}Tfɞ9sBv%_ouKVbJ҆ ls*@:)gf&˖1y)/d *+<^pvg7(nNgFO=EYn*{ '7,̤g47$EERTbbLmWB>T$1~+_""D_?'yYNȇd e 7ꉢfu6n ">ǂIe+tRwedV੧ufX'w.yW_e,g5RPƏZ[}J|Xՙ^))f8+Źs;6,ٽ[?Ȩ7M-蔓!+&xBY</,,`Q 2HS1Q]ƍr׈jdGgΰn۶<44Ч˖lCiiږk*hm-۷d7Ѽ/ZSTDAc(̚śo2e wS^ZO H22<ٳM)^ =![z> 0\?ȷ߲?S2oO.B̙C^^1Cgw7ۧpA+ (-_DGDMᷖI'l SwEWC &F>.af 6}06WK\O?ʹi'`0r$={8lnz! >F ÃWݿ_|Yї0n>h ỶxntQ+40j~wlA [Sw|ɑp{IUF -Y-y0In47]|!ViR%RdX{ Q,V"ÔDBHAwpC%n~*$Z+bBr;9_5>?ÒT bUyיΖ-hلZhhިãuE&!ĦP^t H멀 )=J[p}9RF)[AӁBZ`'E#he ;8Z7Rv0M`adFx&- #b(JAР%5'$Wp@ѨpIJs-Q@4zmhNZ3QpݯS =Ez-rؽý$%{KUpTR=ˠt,8V2.Z؏rǪE!3Wj\'] 累?=<2Ij]RMKM;TH̲a0'D.w 4d9U"]SeBT֔o=NDH(&Y1ogEnW A(E>aIt%gWl&bȍLfQ%Ro^w[Y b@q5^J  m!xţ2X UGDP^=BKP]N!kmQQՒB$(^TzB=2,@5nn%(@ؓ ЉxMK $B2tY ʏ_l&$oHVvk7,`PSAҝh CTR"*4j6S[?Е @!Tpi=$ --m{ԩ2^}Une!AEk*R _C GPSU>7 *)H&Ank 1I#:/PL91Mn!_!H|[ UEDV,#~щVa`sϛmK|Ătc2ǧ k2tL{^J=ZDuBbvgZA4ҌJ-uQnr,M&,xE6 PfSifʈS޲o@#-/rvX^Qj܊!Ru8rC|nX3P.*Hh1.ڬ@h?uzA\#oY zI$ q6 vj rZ }A5m)e~ qmQJ녁P$y ;H.}n1Dth:G{ ⻰Hc2ԏJK=IL.HA̯vZf;CBWHP+֘Rp١3-~QƁdV16ho8,~ʣo;QA>N[ T~%2 R"PW+s=X `-US|vp+r .>bIe "cI R*$(.H,ƺ>~(bnG *'zP8}`Ie9im pVBQ/3\;mշcL A cut6%U,,u:ۭv. 1FnAP ^^dAK@I\|pHR2)DHm=F"¤YbڷI<k.[%KzM n f~Qփn01ƣ4q$q8zEJGK*I+PtaM[\lD}Wk5xZzkDb{v{W*G10,t+g18_0ZfN .4yH0G"+蜚 @ J`P}"8H yd{q\>jm+KKq2Xޖ33-!Mz~5ir@[ޖw((s,cvr*O $=^-;myB񠼤w*.2tx}͈ & ,@a>FH+`M˷@NinwWҸU@zT _JV}z("U?Y‚̍xu)JPtVHy˧r7xb[|P- Y,x/^ZCefœ+r'~OB @I=m[)8{?vxSh,|AQWoj5ukQ1v^ K||>\wJ7~}f@:Em Z12~ +yJ^aTzEB,lQMyKްdm0H$PV6 )kkVnZƞq$ŝ ke]+/X5fF5aO2R&ږ PkCp8hVnF!K=*@\ zwقE&%Tʘj{;juiB$Er H xo]jy I'cS=FrrA隍CHxcKm%TV9 |+RTDMȔ?_-#:" U yi&t ./r4M) " z@lby,~I޶7%¦s߈gB`ݽJuxf@XQNy[ 3qV@e܊|m[O Ɵ)p{Z{@`#aW=Ƚ9r960|a#Xz(wKSa%HU˛Q rSºǶAv+)*3KV kas ZnIS>'I.^Xq!Fl"Oy o.~D<&` g1$C, %Ԧ'M 0U)}\;MOU~ бZ*nNQZ .K|"ZͫS>!,:cr y'&iG8 Ei/Fh %e}| /1jT^+pjD=ڥҧ3tLj 5|um]q_" ,o3Y#|WYp[80ȴHh$ǦDY 0#Xu{x;K)5_<;rl{Z{L,1@CeTga@XqU/CC_;tf/P覆,CSQh)=4Bk'd,&er *-"8@ *NhlgCɂj+1,;k=蓭yK&U:GO B #1P(J"`;C.Aw_`% W-k~pC$Пfl  ˒տ't t?ORTwX; U͋( \*A9zF7 6>܇1LNgye/ t pbղ.6a);ķ+`xYObFA8V|DĹxw1>ꥐ01~-#ReщϠW,Y}LXĮl% t؊s[)&njK6]>rsS7"  (ك'B>uAc ݒ1Hp$&'q̳X8/iPmV6^iI)~Q_|`&r²F ?󟡴Yw,..UH&_οί_@05j$ԏteI8D~s5U<6ߩ.O9b|/ʝo QCYV]v,JK#`R<]~Y0FU Z"V$@ rd%Og˻r9Dt"%ߩ8ni(l`/ #gof+A_Գ^$@`S-r\OVjP`RXmC }hMI[HTgXhs_#s).9wT5A6U3M v\*}($V9xa9yhqKJҪJ@pCZD{g-C8Je_E|"`RR\kP<4b:S&K;ϠȈ1vsbyCx| bB myjbkovpD.^˰y/;>bU,߆f#zo` PXC’ެp6Mcl@<ߺJ}ճELMx0`eajRE']4IM"jTbtSj Q " "> X+'6577JЩ($sa@ϋ#e=FY憣 \ *@鑜b+vXiR3{±/@op*+]oq7Qg`JUb*")4T7uΗZ\#8"~4ˇ`d:lUª1z8SV6"FąBޤ9!4stnTe}_TfUR62 IDAT6 97tޣ4kǩz4 1ҩmg߾b/,Q(xf] ":*!bOxEHMw";hx](km2@&R-&qW݄Ĭ0KD*/C)רQβt1E0Ac9kHanƬrcM /0ǃ.R 'ԊyZShmTVD B|eF&PևkYcCKa.$DcʙX`'*V|.z2-ڰa`jI$wodQ)MH~]>"3'LH j4&-c Fyk&!kԀ҂Vƽ0qrpBkMC=K! $dwck3HtrL5ڴ7E*Ix/yh|7HG*Wj5ӕk9.,`P;T$"()lEuE#`~Z\di L^&#.nGY%?;VF+.6Durr/U'^ўe01 /Fd/K~CR lpwݨ$)b pAx*Fxwը0>({J|Ӛ7ZBUXR ޙPYڜ'b !3 *_iQE9]Pn<<ېo&t<"A3Dz,V]]N[jN1V[EvwD*խ0MMn U1}nLIslLN`hQT|1(ka%Y`N7wਗ਼neӞa@(')Q>y!9ż $@En^p· bTqrr%dp V}b6"^Q(qtJU "U>-V$,:`(MeP' pU+A>C&]|Se(@ͽB-: f*fH\RU ;fΘh-<(h,マL}ҏ&c/BT=+STk a \pSFB׮H(aaj*zĤrxbR=$fWl т.ٌv,`2H#Jyоc;ahVgA\o:V Zfq:~Y!>b׆Hg'IΩ +OfB`0D"-]hYޟ9!@&CtzeBcU ZUPZ0b8©p-6%TYDñ.&rM]R!vQ^I$4 MonJ#HMn4OEpںeMq%=Be%t QOL{^ácO1yvW4m+UqݦNmJ<8SJ`r qR@Sf u4HEE!+wp[f:Xdtve#(Rřx7wF pkf~YQDn=j} 4HUFkZدԇR!bs)[ iZ\ `ɳ#(1\Edba9As7y ]*!dO(9pe8\N^<KՅ_:Le\~l-Y*]uaue(05!7T(4llPt&0n&K8ZD悌 ,ad_&ŨEߠ9{܆(ioCӛs FEb,^JaԼI\@nlZZ٤~du 8x5@Y@Bhr/e%`,,Y&E a%{]^ U f}ÛrH a텈} JOzfwSe]@ NձR aex7 lF}›!yoi)H NnR`#6*lj95ӅŸ0^3Q:H]7ZC|%=HhUy{`.1<-Hs &n<*獔(-96 4UٵaDJp 4 m0YDτxd"vV~Kyf , ["|<7M= <d~yHL3'?.jhZnkjæPM=|q WVEn.ãTU|؂9F&LAa&`9//;6;)Q&4f3 зbȘ0ki rJ^/P q|#&ٓ^cB"טOd~mIl*(¥Xl HB\D`bVA` ot,K?K.M3ՙYGkw *ﰠ)*گZVw솵<܀uDbT/ߔ2T@G]`F? ._,w\KBt!C-0L)xہT/"d#طdD-qpp$)=DE¯}~aiBɜ<,(.8Y:(xTpt{PǺCa?LziI\AO,Q*hR ^1 LpO8a}rP}R媁EP]vؽ2_JW 5mJKht4/#T"a  E#rwqc4l0F+Aņ<*QU;(|C Pb';bU^mo(hH#|ɹ: ȋgu'fH}IB>iRKE:#>+#rE|+ℕ%UF e.s0\g!]"G$GGxII`C)!8*"_E2nXLW2kI OD^ΎT-΅BlxLUj>B@ +%ӠY'Gх?:/L=AbtߊVQ(;gvJۯ."z&$>ӧZ븣Wea=hdĆbi=@1 ФPޫ٥Ј1 H1Cƴj &p"M,piRlgQh%vk?w4kyC-HMʓۘAEy5}:c5^5X,5Ɓ!Ea,3+b7ڒVJ:ҽV\%i"kK!j26Tq--hZf3 bft2M Pmfy4ޑZ:/MkYy_ٟ?4Jm Ȑ}; zj3%dV##$nɆ[2@*'PP&duLn*-9ڸHu%*bLQ:H|x$b 6Eu'4ş"'JRe\ܶ p=o'FZnȻ>Z,CwM[k`5'eqD*x̲W蚻&EeP%!>n2TdM%A@H~&)X:ܱԗ_V U4P s#]Pw3#k(~0/;:1n?_5ap f[yR nT,)L y!DB.PsT `"@;nB0ZcLo ۖIx꣧5GJpgbjJXUri]u{r-y7S^Ia [6j "]/`țܛ-PRnWij T?wv=eۼL|ED*\h4QU$y>ĕ3(ʙu\*hjcyj-Bo=5X꒬hj^TkJ<#$3T'J{xoj,(nN0a;b1Ha^FBxq=X fÕK]w8m5ǹ h? 4^z @\dIJb<٥ )l bԖxEY_B/nrgDgz6/$ B؏T.[̷Y4&{_`59:VpsX[*s^Cg 0"*w.+Vsf~, Ӣw54vVejdc.f玪&Fe)M߰s={0GE&n,[nnLVY1C% a箘dòcqk $ JQǔ{wN N-3dRr/DD}(S ;G8s\U$kۏ)D5# |Q]ugֽ(竮8at Pa'۳],DwЩ//@vm&@-2h?) 1tky"]Ҍy@AT %J!jQen/ !p`!XJK"TnYUUI՝Aw{EvXvT]ȟ6T-^TK0(_1sO(Sm*g,u7֚iwH\D0nq)Jyͳv؈ @ s{F`0 8 @ޛMl߄bɫ#D_}t2kFOs!)Ov3o9BmkQmh~6ɍ)mYԯ\rɑا{Fi$!=ܥFLN ^&OgX4)t>8y0o~"i2GnhgPdnQ#^*9YUG%6OsiNf0lUx,xQfuǨo=:b(9Ic͑,8Q`㈅6WTr M}@UXҵ!BN!{Ї SnOBV 2r ҺfIͤ}cfW̲)ΠK6>݊_ĩ@V *9h{^S%K|;`ݞ\E<.J5Q@9Pbqݥ6:Z AsO@=@|R>LT e楆.| WL}7q~ߵ^0R:/}Va]D1+L穂3ƻݳ8sD $M9:A]O6A7mLKzL[kN\QqOvFbjXR\Yw0H5ywcZ>ARp)ѮF^l[Xd2g+%p:%ny2quʈgK*l`uv6&So쭠ˁy$BP;c@r`Ɲ :5& Xn %Ѓ{\4MvZ`dgP]N/\!fDɚUO^VP0u[99-'f [+)#hA!qL( \G=ZƶOj!tRIf[(FHfs(|]wmS@D#u2h2WCWk>ݥ3Uru~R)nTM*Pm,Vg6{?AmL}Cn "i a#Zm2I0MO E]kM4##Z? ~Z.M*mZ5.Ӿj^ܮkG;cKM-'+@! eS[ukv|j/c"LC)m 2񀐩P-@4w $˖NQ ZvbqyCB y(=BDmOG#H֒~B`+sUت @ؽ5~1U`._mG4 nPДs%F--'6"@a(䍶)޺J)+MlXݰH"5i/M{;cOdV'&b UBعO55gQ⸵5] l/oO, sI7V(Ӆ]>D[vtK:4Qo^T TJ E3jN)Xb_QhN!T\O(.g$VmmaK|p77cRmVáWXI*Fyks5t r޷T J3Zm:֔6ѡ郓ù(t֕1gOTv4 ,Q[؍Cʅ@8P^X D0f?,T- 0OOвxC]- <: ~MZb Q۫l *1t?Дhhխ êmw"b7/U qOlEQ=æ$B]Cޒ/:5x4 a3%N*M~%) X,(y+5"S+5m|kdrK!CY*O꧈嘥_ªct67Xߟ3; ߲Q+r;ðats+dz-Oمe4(L=7cmDž]"[rj4VYKQ89M)QALZijMh^(%u^G\Umb'r;]RON2#+V6冈;-vGW(=9)mSt"0d%|o]M"bCD (eҳ\زu=t: Fj-Gl[I!6uq5Vv%A ^}%r$9T̓deC 7CC~P^Hw3Umh)X l(GE6X/5r}Id*鍕w+ٜ:jR yS rD6y*É[5`J$ŝe!,~oi$ZBL᜼@Y߿ȧ]r꺊9,D23A+>};!Y GBͯf:8]( pBm.q >Y0y떶U!>^p( )H䒰7Hgg(^E.Fܰz_l7ڸdOKePAJ ; <3ƱoJ͊:ΦsD7JH?l+EԬf߿£ZGYYSUӣC$*r ]錹-zvwbZC<:,P3% }M[ɖrIEYy&. L\j͆? Z0AryW6 RbW6"NBenF b@5ċ**xcMK$ 9)6:x?FŞ%mA^Gϳ$퉶+¤Q*UBݭ %zYLI>vI~/m;C5*4CJM'\.,X|uWPWKÒAtVsfvŧ~t5?Ɠ]COJ&pY‚Cp\KsĘ|ҭaVgso5J'>5}Kf#ZK\1p/"с1do\U)z;(`Ἐ-% SޗGI#'ĩ@_7ZMR)6&جGSj)]|DdI~A컧VD\Dw8[ۀX?^0P["J 0Ij1$ E`~G<8U,Bo*nR=a(9Ҳ49Th%S7VbCuJ VL'9ߺKPjKюHG.qI\8ZEóv0:qkG7% i__GHeE0<]ߓ S]3o'do>+sWٻ9JQ6EsZܷ:47X.^8@Q,ug˫'sKۗp|ߤn Fқ}%)-]Bٌ.J"(PY|`ҩ7#2l``*Sӹ~:j \u@4;YD BX cqKqxM5mSńbHQDJPQ jr4)}k"J v@x )e14踑A//w *uR"]2e8uY"ԪNZg E/y*fS\ š>b)v3G᪔CH(HIV2KdJzM6)MjK׬]A5.EpΣKMYqSѵI>u, N* 0:z-ԌrR`zE‰Q [S:b~{ub4Y`NLl`hNwJDuRoXXjDJ 5Rϟ6gqRC ] Pd?.QzIN<44JaUq\(M:{BIcGA%~ϖi 932A6zE헨J(R1M=T 2!3m.{$v__zzZޙek[Sd؉15#ma&_\|unX;yu!//[YTB5]0,|elsg't@؋=,ۜ_`樫Q+ Ͼj?E2!B0)֍tFBp=/js@_tyyiR{[dpPa3(l.?쎍y)#QIu%7^֍/a0?wZ/w~t1ͩ䎠CPs1-"2.irSlH%\հנmdB4ȯjv3tϘ wOKcKh@'I* t&jܠh&D * JwZȸd̐IpT@wndamc2lrSeRٞh#0żV.Z CfG(TeFEifqk dYݩmVF*ϻ*2^hc27יޢ > Md {"\8!ڔFcØOڝ24tH 4܁xB}/c~%В {',D2ɕzN|6gAlnp m.<+wRR_Y #2xH1>ea%{i&24*:e4og~ 79R՚17OXx9"E7g+~&Ed4ŇTZ3&٫/S-ɛF>Z$4n"M8Ԩ)&$W1{:r}ZMg\^ZrAܶbex Y9Յ\.eN㕭,. ߅ o>m&0abK),JyζO2Y nUY{B*Rh\'x{9̷;lvqil/zn87,(dc:L6^S?{HӠ epN#cgەY핹no R ¹T$KWW!Ð#V>.69? 7EԢ(U'xQ>dFS 9fw}9`_e i+A7cQHivSP3DQAN =Z3.B5|Q)."/ tP(/1҃N(bVTAT,}/մ+ܗr>yQ=i/2nӵ?;5cϘڶ(!T`sBl(Ĥu hq ^$/KJG/>9:ed:N0ch ,F,4[# *=o;4ŝCoֵaK ox#+=Q RRLpgEߜr*T:TX"%R/;By={jR 7/U(3jͺcrcpc3#G2w5bw)1E) ZIùJ;Ա6sթ@-2e@MW hHOmOt-R /3T%uPԳKz~q+&Tv4Z{(v߹vզꑄ?>N.H~wL_$oѪ# .U7DfR(= M?T,yj7ۗ&5~ 2ƛ'7>[şbޠAgh1E< ,b% *)Uku\nb%x,9 V/;{**&Ea$k~G1F/I\lR .]Rn0TyraDT96Z"AھU5 sBxlL.鈙iH@2eX :<<8ٞњ "lB@N~Q\AS-i^Uz!L=WngiJԼ~uZ͉R 㼪"ڋBZ?Jot*Du׎-Įǐe oW %XO=C:AjOjUr/.I{+M|P9#2bpYoAKGQxN};RAFezfҲqbYշR} }گ2›ip2[Ct.A͹]r0FͿ>k˓`*dNC*TZX:dTBY,Y%.ǍfZ0L1k4yRnn9*Ҭ/yKڮ}l'ՒLQ-rK)|>xحBcwfiL^Zz0ȓnDI*to|kQ8[w8 YX_sgWNqmxS0[(NkqӇDDEm}ĐAC}u艰JQ7YpTú~O-F{mJ|AI}kN.wANߞvem!< W^rt >#T1\o}J HKe05@J}h8d9(<@ g Jq@-%=H[Xo'ʁfWͧH\cW(5|Lugj: E}@mV0@`ԕeqRhvywN&S?$07 uH3N 6:&eru.i]">_jc^(^n-W5DNjIBs yqـ.2PJf@d0 W?Gs@j,rRDOF7ݳ\rҥ°D̀DK)ܺu_O$ >LUþ'uh|dW>8کZbZ} J.K~ Z'= WְtӲEd)S1tK?tZM(T(3|T.[W7*>(jxAhx$ڸ2[Xx2h>'%Ҩ*&"Vfeaa4^Ů,_2ϰ.vW^DoG^>g!|o!Ng`ŇL٪SEX91KoPkCfJiUKbݰ !‰,WE)੃ꄇL`^9[&րLoAv\'^ oF^\*oBzK]n@L.37fKi;-*jwQB]ZP{"+άD';$ܑPwmf=E(yY[К|oPʉZ0*?e z}΢;\el :H](*ye@Rem3qOB&,7=yG_sx, )X \j]"ifLRYC +$ %6:%o !XTQF9hߟS`hpw V LWU[ɳ\/}#0V!PgEkX+]DO᨜*5݉+ ~JC;BB7)EmAc?[N^J LQo1%uQX~+)0<ěT>⃌ܮw(.b\'wd9ͳL!'97YeFy܌+<_Z_2RZl譅tOJtL 7,ޥn*m7u_LVAa& 7Xʹ/=ݚUBf?C ),MT`5*`BWB%UAW5/a/]MKg5J D'9"RG ]u =モ(s:n^ orkUPGaI^jVRFxS{!|y"*Y*,e]i 'EV`cMoV ]2 v͜^،b,C^InxY:wЈ(Tu2Bwۙ*fށ̊xҪ7jj$p3g1ٓ[9<ήS&1e<gs|PUlFW.<͆!e/0uiAkf<=BN.M!jyLy/P;?*@<~'U? )zO2D 5(碪MUTHWP~#gR;IxZ/1a(:딾3oJR@,"She8 f@X9C* L  + 7 S)QU^rG B5q#B)b;U\ . YcVK/(%xL]X>U}=dD'Sj`u>M<xu'aNK+R~ec,> UTk(Tz}u|ro´疰oMJv%ur6@N%ّڥ|ִ= 85[u\oV;dVѕ :$IдQ5?Bl GJ؝`OiiX9&4vZziӫ_4 F \}&Cu@"m}Pcquɸհ+?!Š 5F gX)UtXm.a IDATdFH4=WĜXbuS,ӗ'Boa}+һBЭcŊfM#=hSE9}}Yg+}a|sw)CMFQ|]?P,K,e6xJ S){v05*(= *LDeuU1^r _tPSX}T:( o0{j%%5&..G~B_f: ?n6RPCB _TAid$`$;Im |V6mQ4ptẋy]}[78iYEa 4Jx [u@ ]N:-lX@2?zT ]MdJ~k}lՏY<,IEt)kTxnBmQ/>Dd#׻*[?3}Vi6ֽ4hE,BO5vZgmqPNt>$7\nǰzw~mB>CT?S9|*Z3Exth+i'P#+#O܋@|!,O.6 WY!M>zܧ@5Yuqg hEMpmD_Y<_K805ԠOND JrO2deIOVN0So_9[?~˲^Q{e>p/ I2[ōqBHHYJK7# fZadҤZT=ܱ}(H{Az>hvֲؕ bCNhb2=kUF骣Iwu8#yX%2"W:ГK@$'iqq$@]օK9 6 uj{ pA}Nk[*T|A޵ gvޞKl=Dzz'Lﭿ?Tˊh'e@[P͙i%=l[݈O:{J[BuwQ0aD]zg(hrX˻g[a)Xmgk#3c@pt ]X&炇@U1Tz^)f ;Q_ZDU#Cԧl9P]Zap_h/um1FCV8(MI\ ~J\[a-- AE4 &Th*X@':ȓÞ0rgy mv5:KIZmV?[Fok4^:?2F#l\p )rCIBlǩAol/ RcT+B4&L"gh߰`,ԋOm%8o-$F !Ta)[ tf:T̶fa2T#Fu6{@t :mn`ȋ(bzMŅEn<# xz$[1ά̒*ܔ|^Os~U +09QH7jٸ=?]P,xP&L`dA@-SLAU/2Ud#~ ;EP&ӂIu/-121'DT2Nc@&4(m,43P!}uf)E}s~U.!?oii>o0QBCna rxRwp.,+AdG="_]1d*x =}ʯBͳ&ndwxzղ@a ^Zzt/YhKӍ0ϔKZ0~> nGWPaHAdlCQC=gm{S<0Q o]bb9칋_N~u,eY oQo+wkU_\tuKae. cv,o!p$Eb~֒ϔJ4$15E1TOрzwgroZtGܒӟ4HF/,@bpJJu)ԕaVEx뚂kj# jV94g*Kb9V'diL{I];}Cݓ"#7(K몇oS \?%5:χI1.;ArQU mLjz z\i5ǔnLk1cTtez@^i.VdJcmXrվN +f m4Y`Uh>)kg_}$Â29gs^_bcW`(3d{Sˡ2US>ѣ'gG⋌ژEbm gA$NZpEiE"9FH6W +ChR*KIF6n4[1*׏p J@"sܾ043?.P#`M#o#,#wG9d4 (v @ D#INu5 UG{W BCGЭHSJ0SNc7_)b0l;g"ob O ͢1%݅b'2)RǒP=>m=@ ]ȮQ ΅hT H1z#`fX{Q8Lk=K˔=7xJAi=@M2Y[l2,qz*SâgI(3RLcbҕ0mƓM`Jh hK )?y`xCԩATn@{ZnYb _Ì k)a[&~V/ HefRU-⩇g#Q,rc2aC\5}wrV閧fPȤ `~3&4Ya WGH3_R|}Ƚ|-Ý`9iګU|El.2/P ̅ z b/Ul]X`HfȦUv>;5yɅ0Qd^UACMء4ˊY*0ӟ=)nesȫA<@uy"BFےl"'0%';C(y%1t6(O29@?N;FlBM j]ݾ4i^WWndF)충 1!2־g!j6OA q[u򏦢bkU@Տ"nJ pgA$as{^ة4ҙc3^E~ F"NEx(.~k06 Vs7 9г)$d3eˆƳSYz VoP1!]A7$HUS+[ &#jΆxY][m@l/^3 x\חz^ x}ԡ#"zG ;omvw(7S387Yv?K|Ԅ*zR47Pl^aguH7j":?=y_h$q~O%'7ڎrvbʘhM)N R7vBS"/K `(wֻ27;1+&mZrTicf8zTcVG=]β،,QT aWS:] e}T#}G@R$x\zZxC0mSS/pxDhTܡt@zůWIQqUxCa߽5UJtj(L )83Pߤxd}/MK$Y}¡%B[6 f3~Q=eհ!Kvul?(Խژf|Mٖa7dF, w-a)iHsJ;ӌԎ&~#jznC*|,fJ=騃9#a]Vx1rf܊9(ZÖZc:RL Tg>1!3ޮ`C#RL'RJ_{[R#VyyLTJ7}TK>!#-NsՌtt"q+xj{7d1 U wAcjls,hczNm&p;RtE?nA:8+s =6i1Cu}) |cJ=XlOM-⢨]I)jQ}[rrR\Cӫ!w L*/jߘx-`{(0Z]5i@Lɥ7-0!l\uxJB2nUur-K[2y53M]ԍףtpPKb."rQ?Œ~br]pI ltdKf}(L,L Pw% 9hocFtJ$E,%"S%o ?^:Rފ M]{Eyl ˼l@g\uBr#u'#~W~v4㤮 Y _߿a ^$}Y jA-YISu~ IDATLCR. ,IwNww̍K,bǔ;h:tzhuQR] bdNm&쒘D[_]978F9v"ȮnΨd,4u@L3VcW kwAÃ]nfBDq+wYDDZ̜ͥA-b HWPe0lfyN^XP]eBtjnA܍njAZ'-`Ku'% 󨍷r]Cao=E$CpsId#0Des^w["̠D_3VUv^%>3>+)Բ_Z1)03iB#TB-C9n?m:i,&͌Qȸ wHCc1!2wEdZ!G"[5}&A_ 7Z؍C(kI, ҶY8?,J'yC9W%vZ2=M_/m=vѶmT mN J96c?MM}H ̫Qدh@:0&=" #P.ѷpE~2 {Mm>Tr2Mț6v) b3%Jsh˝&4^fk%~cGnNwY!C" ا{JX7Ti*׻1XZ"M=;= ▦iT% RfMFnA7ZWpl֛d9qwk_fSMR[hݯ.}wJ堲#L[ìhf ^XzKo}.doI_**AR%nX [jſbbN O`O== ׇ[!g c\c%8HIc T\O㼑V?ʔx3p;^i )lm3-*N {hi*tM3 ð*P9?c=S Y<=CL(iTϽQL) G3s;gͤהD%ӘRSl.΁ʸ7lpCa$ }|(WmJOjM\DO8ld5> ljS zέ@Ex+P~`!ܨa+ܸT̆%Y,rl0 <`D%$K?oՆCvK=.㫶r28L]UK.a0lA]+ T, RPk?ܮVE1S $Oy\ۏc6كY+l':jO!9".rW[5r]mԹ"Zf\ΣU $hygOv k\/FFOe Uo>emkz7Գw0(2 ܑT<0 ySh+T9Y=K'ldpe#oqWRV&JoĢ @\B{J/W^8))ywf`V?w<+D2{TFES2 kg~+~Np)8V>$bpY ].!?A2m9MbXTfV`CefͭKJ/aȮC3å"a 䦭xgŞQP:duIicak*!MdQ(nW= qW9B+:Jf5=aă ,۩3 OnݒP)¨pLџDs@!>DMx#Da%~};r#gIr3k^Y7t26dyȟ* +YD*h){.}RieKʮliZ)op_@{4.Ѩ\VaHyJL {"+Tg~k})$w)0bDߥ^И8Z|54! ֡]WxXuQO^/?x*aQC{ŁwT)wHaC˸wk~/a‡ج*x?O׬`q׮?Ŋ|_6*s2 caE\()'W/BU_6qv ~0a1p ?q[bLpz [3$EkW yUƱl*0%݄YEUwP]-\F5*cA-|rK /3Dn8|(y*%ow#B+ ԰h@7aVnQs1pbH r拧{iC@Zeub:lyi -s&"mYtܓ4<T J3z&^ q .W 9p8+M;Be h>b]`-O8W]*4R&v/u1g~JmqY"J=%RK(jtL@J}-<aA)֤V k&4ϼ k!L[!'o|h2`N׫]_eW=ؠqe _wjp#^\ }t5G:r^sa?!ėꥁ+i/R״f+s9ϰvWlPxYP\aqRDn@ME4pj1pÂSIyBS+d jx/ن/1JJ" 6(@䥺ɘvth,$-hfUraM]Pƫȏlj6w@q/Y!੽qބ*\z/tz6[".TC/rS0C :lRM\,Wr5.AQCB~o-D JmqCD#ƨÅ?QMyʪ;cG}˂-ŪJbKcSʊ kfSz3U9֤).+ny$ yȤL"mm/>~HGլf,nw豆ȁ.ǥ,6KXzC;j1;?7=˳{wmVˋ2kE^ Q ;X]uքfp%,id@W)FNbxNUG}3z]RmvquDY7Xˮ#h2f@y_.Ҽq2! 0=v@ _g fژZB[ʴ 72Q}\-ITZ4+|@&[Mj~LK {i6OP8@W8?b(g-Y$1kF Q=&TE") 88Ô(]\:Jk=݄R>4]dYA=zn&[nȰ c5&n!+0۷q g'<>zÕ_{?eA dmȾQ,_\,q zHJcXGO;`±B8Oeeͻ&{w|Hdh^oG}%n.}K=1D٣YZ ₣lۚp;%a!v'}*QɭHx_j& J#kW$#rC xs ؘSxQ K 8QLA`p;]w$> qSM}*x!˅Dc=Ӧ)sس#J\݈CdomHԲˠj=CRnԚz,qO\ -ݸ?ڠjaG"MZ*,]ԣEːH8&jQQ8-G\v`R!<(I򙋾b Q/kKfSA_Yo8$w O`.ѳ?  BmukB1^Փj?)U:*Tu6OU5`ѡLlp˜l!3)XRz2Mq'|K`?{6 mZmQ,uTX)` U!nTz̺ٲeB˔$aZ4>h%* |܇[kX\pܺÛVʠC߲A+H3/S!.^zsj:܌YKu=2DT{z߸x ̛0!f*4&*e EϪ FBu,~FH4 w+eLX- FQY,H!%a|dgbD *G)P_LgvVL.zlf6ѡ8<+&=o[at=X5#$3W T )|}⺁jy& IxZ\rFw'ZZ -7B`Xn3e"dΖEL@a AÊ0bfPh X!d.Q2RմiAw 4#%l\ t>P7 QEKhA1)h5lG`'yїXTSW%ӜC^&*T]=DPQ9F$[3 Ls6;v|@65&w&+xv?fpZ-jEU6q)fe!fwc@WQb3<˘p p|Qs#tWY5nn;Q&{~B+q.`>PcrJED&Q;/1I amP#>оZKF0`pz&a.>w:J=#\Nlgu頨#5šѪA\{$X'9VZKN1TMX 9;"ˆ3R[bbji(W-ОuC(*K=Y\Js}TVTUboo>wg;ґܨ%|Ѹ/di幜%MTP\&ny?duB,sFbc~ `$3XWnkP_t< `G雜8%tBTA6EVDgZ}V?I@.ֵV(S%ÇͨLו M1r~J`zi''a00.Jx )h7=%Y^ ̃Ygj}˺ES6qf#4 P ZK ؙ7^tdžIKQ*A\9DcpcEٗOafOܮR&0g#e L+oIx0,>1T>ԉϼiQ,8HXuO̭Ν7{7e⼳I:LH׿ɛ6,M"h.ZP@2uRA1F"bka&Qv#XFڷdgQns(slQɬxV/mѼj@- -!8ULY IDATO9*6{{*rh!WP۳:l-X)r]US`IpN,-; /J; ߻.L10v$)y Bܭ[x'[|Cj`A;-Rq&D R6. uac%2tœKſ-ZCY6kE Sm]n TNo[ YdPgnP j&eѯ0FldayO &OF+ajZ e:`R|/!>fyH&aP%ّN#rOw@X9q D?43 J ”떚 ?uxp DYKcv6gZKUwZٕ0 2lRtT݄^F4M瘥C {;Jꂇ6sq"*54]co$8a-7yٮ`G t{vX\Y]jgVjFsEV^h묊VIgT}@Shbmqdɴw0#"*Jrw3MiZ}ۘ~;ᬹ̰8v.]3 o 8?:B`%Y+Jg4d́fdNar7';KnW$J$$opv[6&H4w5hQRwcƠ7Rf3 ^k\H#J.h? VrqKNP9@ZW4[K`_ᖄou^.C+ x*w0$.NS{s @;aW U #tP\@k]%$IK5gT7O4[ #x'eleE$OwS%ZZfA/3ONod4zޞD9H]/Apw+0s2ED6=`.]iFuvUX -EEQ I JC0b=V`.72./2{E}7<:r$SjL`63`T)b;f5Fo-8nڨ ͇Uw䍓b߰?Nb.Rh1NPޣAja w-B8_܋E+3כM wo.4oع&X5𔈽׏ms-R.0tXQ:KWe{'dz۫L!aeݹsgB֚k,+!R`ClB Ku%7ܘר6J j &? [{@vfR!>:8d7QjǬPõOۃ59%~X3:ЪAP"RGYLJont8UGS_GP!y~DCA:4}֢? pĬ zpj%b>YwU]XTOEkxtx(Zl(j"M儗JOp -dn *^3>#ާڝ𑄅NS!a=; zjn2P!t"jii}Vϒ?/d  M嚍kTyoLiwRcL2#mD_&f.lZ$3)(T>Ke!O*h*45TWc8iwtfM ,4 yr#@ 67w}8n۔`թʼng^ (~ վSD]B N/lmٷ'C|èZhJ4F_tH(v\\+`~CɖjM `.@m`B~Id9Slul $fa|Y#)c|s*/ xVop+:%jHnۅDYj`J_[7V)T*d=Oc"[fы%K׆qxn]uf{kf nbSS]<8݁< Ni6.Z3蒄 ~Ns]S;Se[[Y]zVkN-i0.?{My|FO35 _4E0XQq$]U;c5 Yve!T`9kGbRyY[7 ԨJ]! KU^:$02dڐ u;y~0s\aq "d:|j?6CZGWo_D95{q IY[$]'qX76/VEa󏪭5[-RX]m`,ũ7q}ꚹA7h{ֶuj\GPDX,2Ef"[:VqAW&L~R@!gwLc|/UزC bpFY.QFB5;nH0![GϲXo1)&b܈X9?+'LQ+CmX7̏` ,F5<@kL.A-fɋFu^"ɦV)uA*[eFnCK``7YΌ 3Q6"b$KxN5Iz$ TIy Fd;[ÂOidQLW9O<xBg!XES ȨoYN29;I6WD Dlwj/(] 0) ɰɐ8VІ-ixB-"Pd.\łRϰTK>]ٴPn5楑Li$qzO@*FϢHe8/1WEs&uq}f./vdq;q~aY _)s$x7fLX]MR"֝҃IE#nc*2Wㄬ+!zW[pC_$4tmq c(咭U=Ff52Q ~_qˡ" *_AY>SC-HZ~Gsx6 :)\V *vV3 Ju'UEj05Fu?g!AH^`"-IVl bAdT-U_OƵغ=5/,ڿ7ڏ*8׉D&z$ zL|mdlմDӡЕQH9!c߾$YnrLaKFx`S/PK(. 7hU7J/{q іNދri*8 $] 0%ƎkQ쵢y?t;WT嚔ߌP¤& l[cɸQ}@@i~eU]jr1-<mSȪ7fy GEJ/9 NeSm2SUj%Ph*V+_LSHt%;Ȟ 9&P9Iy-2Y :m0|V Gxulxk?WjkxS 2To ߲8ښ{tMRMhʅ2=b~ 5kŊ cZT TBJht7Ǘ$qUޮ\Uɀfr9K9mOp`!dAC #15C485esF;_OFC;F?Gs &fPݩuB+&Zh >h4"&;2 YmX. -T9%[D\iŷ ҍIo|XTDarE۷W,N=J$|^2}`QK(xPj~M+rHkYK  fԐ?Zw9l礗\ NeLi1zQ/@q1],=]T9Mr&v>-i4y#[#$-jldSPͩ[jR".ɹ h SuEsuRg8=T+f0\< v]nEB7mX8yeZ%CU'yƭ#H^x,YDtB].[mPEAbX$SjdjDX#[ϦNF)*q ƈ,~oeAr˳z)e Ŧl薎'IUͭ,<@{_>}0PxJ/c`Ӄ&]͙tlhdb  [a0)9?w&1xjSXj8Ҟ!jkzv$lM*eX k&$͢koR cHͻX&qNm9Hԗ<_V_C^c2HrДsUqb?˭y老$ -^J-Tj𐽙Ǥ"~VLj֧,p4+g6ERtE. zE|" vtZ] XAy*WoΠ*tEJ聣G+pD3@ӁMI) Ɵ ¹q]7/H͌T=pQs>ζCMap;{=u9k=Ciϩ緐f;fNޛtbnixMO*xPYAjSu*ClZRm.uq =|]ov282foZ0ɩ{n1rE\р|\*0:poQ6#HɋԖGH!+}њBj3W)}$d؂&.q/p>:m yzvɮJs9Le`fc2>tu.c掸HHL\"Zsdspc{U4]i9"\gY)95ZXYc4$FC2j?ɜ"ElÝ ɂ0&b55B.OKHjH1(kM$6f {O1 yuIWtZN[eP&-KdsL2 YƆJV6A 3x%* 'kcLi5uPj0wjfvYH!/QCK.rLXPf :25aӌ:Y^쩠J]!WٔC2ee43/H6xZ3AV爉ԜLQ.+p?/)il04j82"XɈJQYQDp*'nO3\C6;Vӄ3yӹIMT,JF\,V2K{h3~,MQЄʧbYa< ϋ6T0\28HgB>ur|Z` bu%vsR'; ?sIG1 1t3S[8A (6i%VCYh?TZw ° x9=.t0M'd14n!gTp䢣sK%<+Y+iDsÓ!S2(, slZ2YPfJݶ@d3WP RT3LRjM rzK:X@<KH]PMۘ'1?7WAYB. ,MfTSL.aDsKG11%J—%X(_n 'D4ؖIQC%;`JB J-LMeʉMe1#[T0ݛӋI04ӼHbfJiAQngT&5QQ;iF=YB,Qj:u 7 x"»xVZG`0DY%%ĹC2272h({X1ӇVSL*8߈1&}rЭ*\UI%1Rs0rx0򹆚9FUj2I+1f] ivkJˉ)ͱ$IwHcӽPT5ؘ]TS͆̐ zܨ$AT`BCEf$ Ь/V9M\hƚߘ JEh$eRc̢PdMȲe7s[Qstͫ rw108o d)Zeap KG9Y#dIEgX Ddcj IDAT5y`UjB[s 1 ܍9dJIIa@,&AC274XI/6L"9MyJ>!Ld$`lT 8i+sK  hJ${((<[b"ȁV`BSm2j^b'd! _MR̩s0$LfHha4%s!iͼO--J+'|8\̰3"i)θ۔gՌ銜OX%|qmD11#I5V3m a hg5#4T 'ZDMq3q^@s54-*fa.3\ҊBS06oTs:N#,YD d{w D$\dk,9фJXD9Y}DT^;fb4œ+Ō >' .2(hrЁ<*p[ gY+GεRn'+!|mt(ӭiH*N^ W|c&7aGА:j|D{LJ2W͇:`PaLp;N&E)O唛*pdŸȀdl&$hTR,@ 5YU`a,dԼ0j9fsT!WO ɇegF<Y[bsZ!X.`I$2 [E$4&cH:O'=U.l;sPYRzyU&taFTܒUEeN͇:{u9ÑqLUj%)ZJd|h.r`$DY2b,PvNYa^dY0jIY2( t5!Xly b"IBQdDW /8R#BJeIyuLC}F*9#@S2NUe$tG;)4s*HFafH8"KPYDWK6h)+caf&+9jcMI%R@LY,|l@EUn9FK=16"0l9I c;!DGM,+TͺW7 ЮS iɲlQT(e>P3Wk_5Qq\A9jj1PL-#/+z+s7srҀmd.CEY`lJKh?HC"R{V̡2|8D75{0ʮRzjX"6*m&aah@ ON`@%vZ6}i@؝u=.\䙸!c9}Yc {y˖jq-l.ٽv|d V;/ˤ q歷rw졀]ze{p }{u놯ǂ.?fۍ9:nr,=xZUwq=QZ<>(m Ƿ<\';rd]?z7/?y;;/}>?k^˭Wjye~{޷_N~xrsyho~>/W7xF|g?/݇Qvƿ+?_/wm@ܼv__tk#uhtĴK &/<X$a@wC &dFX $ٴ4 SCeu#9uX s^3yZeW W%Yޠ"] hEb$pq?H5YrrXgXPB^}ܺu;oN~{Qۦ$_{Fّ O~Okx=<'W yo?/2OyS|_ojOfd=_|~ҢNo^݆ӏ|m$?&ڗ?__߾=ǃm|'wۛwonOoݭ|]5=^x[C? | .׎5\{ uyrg/_}>|u_[DžO=QǷ+8Cyoì(۷O޼Gcq6ӵn߹;ND썄{?;wcsA8vIzt;J訵8jnoTQXí`M9_q7Y$ wRlWk!kLe J6rX(lXKvIőB˝*Ѕdwh*w UQ};g%}`-2 DΝje![Ʉwf!!*DcѬ$8?꯾Ow33_*/.P/pg߳ޯ.?7^{WnxBto~w{3O\\{Hʳ=ڛzs~iՑՋ{͟`_l?xe9o^oM};2Uw~ܝ%>o`qf}p1tnsG[ξ/ ݾ߽ҥ 5vp 'Kn;]\gʓCk~OFŔ̮}G i(^jهVVViʂj HgAaM*JbP=kHZaI1},DF&5װ2ǚ}}B/s"F1W "ύSU 9"Ȃ}v$U!=FU*mY1aj1/ѵޣ1WM&]$+ahŜ Kȹˣ g(G ?lM(ͭcD @ĂHF`@,fU@T1`MESgziMkc>&spX-1b 9(ea } Njq~QSs.vw]DؕNv''u^V7th]z5bn5ec0C*ha^O[T0f>9_"Ḿ kSc?](a1d aX[ZdUhUVc]ƢYB Q:m!Yiɧ2yf乓4]Ӳ8GB`<#Q'> 6_yt geHPK,>/G\8MdupᲚQ.8Ӕ,s+ac ';kXn^ӓIb?xνwL~WVn|MJ _='+Ŷl/_-榪oxnz^d._xʂ;zӌt Y0U@š =ȁ8T8rc@|e@ɤN bhѥF&V yܢ`V3h*o VVhtD!}TAct X"Z!c-"d=YVg *1#$\.N<=R4[ eVefܸJh5Λ4ْ'Bit5Ń[\Nfaqn.l#mcx@O .]ц[ތ.]O/˥ wyt7l'R/\σ*N3ݳQ'cҪNv7޺-{G\SU]6CÃ˒7{tѣO6KmZBqpދ=y᳟x$=`bDaTe)L: 9Y5).yd8uŒы(0j UBH\-JGs,3׆b -Kn2H9A+F泶dq╵3_Tn.Z|: Y+gdyA(bwO>ƭj;7{v+߾sgQqzefGg}7dpa[wS ]ǘhyǒ~>! i2 [G?[ƥvч$'}ϥK YC+;/UCӏWluoϥ˺ a_;=v˟F4v2}soogf&vnJVb%ږAA((S4?+O|>{þW~7VP}7` zǞ헿?:h>̧?e䵛Q|+מ}}^}W^|l.7nٛm'k3fYbd{!~BA(9A%j%ۧ3qqg4RNdUtP=ug+^I+r$2A{-3}u$ˬjH͚L4@#\%S"!cD| m\66ʤ1 f})P1״V> z/]$H&SJ5TMB=esיG~mC˓U܆dӌڏ^#_Yz{7G?~O~3w77 v/E cKfcM]kA'$ {fT ꭓo7=z>Ϗ֛ͯLi^2ӷF B=r+>x|)))Y덛Dvl*e뫬y 4).Qf&vuV!kUD$#8-6y,m# s?gKɧ+xU5bW8]4V4P+3̬8\n6|MK0 WT''oyYI0n9=˛+Gp~޾zG?npp@ٝg-~zez!J4luT.`[9c39}eML5*u.ԋ#CT Sfcfb4fƘݑl@=7c llB rV'ƙN"j:' b#(25-3ডF["[rWu!Tm[VR/25ZY [x[LRz8cd*i&N,JNN6es0GaP #/+Í-1Zҥ Xȝz$TEL5sFpΡ$*LXaQ#Vݍ+`"2-BHXT6RrP@RUV${KIT*Ǧ|T3F&D I3RV ffHh6cdP#mkR鮒9itJrp G4fj0 T2,~kKT5ʦD0?h2}YyvsԓHf88gl!,IW+/ՇaݲNϪ΢`߯.l.wKbLQ~≯r,yjپ g6*i2WkS.d#1m;c[}޸؜5ս4<>䇽m=kb7[YG?x?yzDaFTk~>x^7=>4(?G>ahM?42 (ǚR͔FXTpi6Q6ճUe%Ti6кKI/ قҁt֪]KD@)l-1v2R0Ž^KEVTFfR_kpty 0v!Σ*uvhV)5ΥUR ?8jbN<;IvPxUEKWI3o4FnH70T|mI]G>뗹VRߩ=yuzUWGz4sİ~ʞR?oEUm~6rۻ_|Mk^cO?~9wDR?x?ۉ$?/~xgKհU KjIVrk僯|GO|jOR$KMdoEы2Sc׻nןOx\k9c[ԁC5Ν0(^zu7؀겈, %ൣG}<#Ne)]Q\b:*IrӚj%l3coᆴYʦqP_ć$`;FΜ},&"5xhjڿŏήx|Wv@ImY¸A4VRXz;w;oR_=!;z}Goˏꏻw^}zKs8NƯ'o֯z<~TK͛L郕؏g|lgKQ" C4 9e k{z2ܱ]b؋i3-+>rC1`%*gz-lnQ}!i7ɩ,ZHPDPB\*H+L5j"~D4;ET5O G۳ͅ]"8 !o [wZVY9 &70mEF_G3p{EORf2p^D.<"x}Ȣv> :6CncwV[ 9fYD 8~p;w?@jf6 1lt)]u˿˝ |pkjßWWV`7|?z{wn`sHYŐ6uR4[s!ͬ#cǶ[/=qUUe1mfoȧuvfz<~/5DS}_?η/uLrs/Gnቋ_F!6 ʕoj]$lgՖJX:~n:x8Jyc.\N}.h/^u{ ^{Ï^m`hJe6ߜk'$[,IE'pX*KF˽HWȵܲ0ɐ X*1\idAD1 L'(lѸ2fEÂz8AjJSSV,Õ@Ƭ0Iֻ!h`xI]U$ 8!@J˦^vw~׿kG`ofGq7W_?zXO>z=}Ҧ9LxꝗqxUֻ_|3s+_zۖ?կR4c50Ҽ;?#no7}c.1heXbjS}$},ûW@+Q֝Q!GA əX¼KH_hk'̶UWn7ort ζ̅GFU3߾aVȱA;XYNζs]?Cgjs: <$n9op/! fH4/Т*Nmii[|o>zJtJBBXۡRϙ[fbYDSB+kh Ƒ>V(SZYP nh)0}vcw2S!w E MX.z :?Kp·0(HKhŹ JL:z&G粵s@7rG`> UӡQC\Q;fc-gԕ;1#QɚI3\ݚW]˯~jw̽vnGě֛ucr^]{sxZGiHHoǥӢj*QQ9Re8y ,HvNT̎>j[AYPw!됙/YȈӇ *U[e4(.D`3+QJ1}8+[d nֈ08qkc @HjDA,] dp{HdLj)6́)vtvb.Oδ 1wjJMe6|D[mKS3sɑf^At\Ԅ.]<#{ՒQ% TX ƠrT2B\2WgԂ<ζܦa驥ܳZglslfV1e/ѣ^6UtwQi[ `2dr/H3v%XTDG*v;wdrl6OӨ.]Vpy e/QccINZdF&5 \VMai &DwOҝ2S'4ĒٰjP0φr2ohU6S`RqPT"QP:& _m4VȃVjD3Xr+&sf>]n8ʠd:H[0h+`!rTsTsIPy8Y}ϻYkak6MɎ Qnlw].,m،qky.'Y[}kgfʡɕ&bYljd=XD_aKTN4jHo̮j y<%X.@Ɔ0 ZHͰEfW[\2 ,*dj19tYNhkʪd@$*> Ph>/ A!q#'L`ƅe&c+K%liq5)֒jj Y WkN5w)n]`RWG[m4&qA cZIXAVUsfƒe~-$3ht]F]?_CV2R_:3mUeϟf>~S&;\3>utxh%ٷfy>mRV{5-MdX0֬jmCgߎ5%5 (Mۋbۥ@\Ɲ/I!֝ʻfXJmUo|~ڗޣԪnʙ)QEKwPXEiDC喬4ef@QF͓h^,MmyFVEj\$M@P̀'UF+*:cФT",|T%#.?*k1 JªPc#3s1jq]$`+9wS{H|=tv"nM~dxm?{ϓ8Z]zd=?\i^ ;|y_n%@^D^~/S驧>zX%>rܷ!׎?›/ ɂjJz엾n7kⅷ?zGd(}G?W_oS+(C "5U+WyȒ8̟|㗾H\Bwa9C_JiEoxKo/^{;jGWXRnLyj4~_N{>gXZlw*M`.SldMƱHwnFd*6{7nM-W7!1g[n08lۦ%@>#ʠ*9FEIf,y˓Pnn`1 dKHFCGb2 VIC֙nQd >܍,zTj‚Ý;XYu P+0ٲK+e(ze í$OBjQugS:֐0O?rL𔟎o>6~p>#]g꯿p/|ѯﹹS'/~;-vM-避l zhݫ}J[_<׾a7^z{i\_=tz:}#h=HqG˯+v!sA[TƋؙ on6n[ Ruv=ἦђ4f^igmrlQND %ZZ&+CG5ӚQA@GGЕm/LZPBA13ܖǒ*ãd^fVFC X m52=jH,] ܊OL,[K`d /WǴ,uB,w5 ȀK ),O iFpQ!d9ݵ ӾɆwT!LRi (5mcNȱTcX0CV cQ9YpPMYd0JUDF2$EYrVì2mN{fa͹hV-0!YHHLйvLJ>p|/|y4l i>B۷f#bdmL,+K-7j5&D X1yHPVѷڪ.X>2U>ŋ7l %`҂.@ Kriț4:fKhf!T$9+䰚R(}uʒ6}KEDrl,ۭq)!a5ogK1 ^ | jG)>*aVÍ[1l`=7ߦiӫϛ0e\H{-}5kv=y*w_x&3&88{F] k 牬 &!+q'G';@+TJ6QEK2x"TaISd#ReRr+݊2D$ժulĪJTLDIȲeutVRF_©%W-i)&9GY-OfՠZSD^%3.}4p f jq# i9d60,M47OE,0#`A9{xFE۱2YM 1&efV-l? i+SKcGX^ Re\r"!U)cwͳRtpN(!D SU3},VA02"R% / p3;ip4<>u/<7zڭ_Kd^~=CVћl%V9sm\UgXDe0c??;uW!^wOo}sO~˾snzO~oXilw~j|V/>sO3Ze0S>L$s$ i.$0hLՌ>lUI^p7AUz +,w+ɍ960a(nѳn%\i[[zͼӦSdO &Tk Fn L+*g`bզ#>:dΙ3g7=k+Ua`!%ha;pٝj{+|cqZJٰ'qq5U]YL>SalC2L;<C}͞ލE1O#>pJœĺ讓%W^6a޻??| mҒ9 9ms06*) [lpJ}hB%jh*/+a@)3=}~bs|WLڣ}9[`ZM#"<NmZO-ј yU˶y-4;2o϶L40QEUV D9X&TנMN҂#>gм>-J61TJOQ}[l",paRE&Mh ehe>[aԽ"SNf(073`7؀f2[xd-Q!,tˬ{nU^ì9ASFa@^wɶMJh 'kQV)C(чBa, WR1 `2F2i2+Y#XJ',fJXx,<͈ -\.0Dh[D80[>a5j XljbKv)sWf&X2iv7LjoaMӻFcUn QBy-cBSʉ N{.{B  Y¬ Ak0N-9|8Jbp 9I Nf51, .Qjl1%hޭ4erœڠhfY& 8`gH[dba9@Z"E1T*TD7%&mpcU0F H %qYPX*YHy9 UUΈ8 J014`͋=9 bf`䖣l9%ي!^V)Atb2'c% &xdhp!&Xe\!+dfU#y O2(urSYe==G`2ǒ w5QBآ9 s}T=?ޙ]Ȫ{W9`;{&YGw*c{Ѝ{7ls1p|f7^{׿ !l?s1qǟx䱳(dֵo"?.kw˗m ׏ƭkGR`pᯞiqeW)UHʊBmamcr/TBJt̊}2rѩAVx%"'pj.F=&fՀ9-Bc3!6&`fdN{ IDATAjӝ5\4*#`uj m Y0Zܭf)ғ}Bu$j 2(Q^d-2؄ޚ}h g8s7Ì[c Xd)EzunaywfskĺM1FVQQ*nRdb8yk]]ԙz.>chacme;'B4pj瓟D=_E[ fv_ȅyDWqe o8ݯ_ײp~ި>lf6˚#~'t$`4Hov|5*GPLzpͱ }l a_zѿZ}Or|1߆o~^Q?|0O*R71LLC8;}OQ-idǼG{9D"%Yt &j-S$JWT#C֘ @Pښ@ZňkoZZP) @ubx 1iσAD7 ("T}vM F*kۚ/RGDKBQAZϪaf[ze&txq6+<`te 鍛GWvsw9G/; pf?xHT,kȢQ@X^ x?[ѳ/}=dhy.|KbVZ bvv Rx^X?/`ku3g_z;uZ4=}W߹PAm|ko\Mu'|YdK W;>y':s9c%8aUx?zW' ðK^~GػRU`=}2UB:l;o_yMV&*r;7#K `fPw%ouxxٔ3),WcgtyH6nHeK1ҋYN5zW  DIe3m94O;,z7&你1+d6%4Y9: f3Q i+(jV>ƊMcfFr2YEzwA1M,"XVV^7W/rZ",k]*OJ +[+" Pe1qQ2za$Յ7><>kKjڠ^~GV7ޣ]ww"4"T.owQTUP •o99 <~rnzI'!d@3^̕qo\y_[QNZ#Vn;5ƠӊlmXŮ*6 ѪΝ#ܽ}kP[iǧ fUCDJyN7+Hr:M5.%(D;2Pc T^%i\%-O #rX*&[|.#FQ} IiP Y06i$mj!FRX FaPNDj2jpƞj ]27cIAX*#W`Qd.VC%lag63@JfŔ<2ẇrzC2¶ZF #lP)+]%m7=Ѭ0P4X{KKVެK% ؘ(e,5lf5KUcdhvZT@L@Ί۶o(yx 6zIZq*THHplQ(0TdEC@m9 T0kPJJ0-bbxnnY+ YO./cuMKUCp|2!E[X ABsVfnp@A]\s>;Hi (,3JdH[Wo"7AZ=-3;jkQ5޸qy|@p uֿ/l}Po TxorsKtz'?p)֕'p4w.PWȴ@wuZDXەn/VSnj*fY *Y z.І@ #eͬffBT IQ5,[4AI7B h5FH.+@}UWo ƒjJVcˇg 1U*49fT.¨n1ll3czʁ5Z)`)e YU#=[)w?4-R5\=ͯ/up/Xe"Q~'d.dR&TtrwsH/ڿן=~0Wn~?^_hBw':g޺3w_|}s@{?}Ol+`z՗~"] Q۴7޹+:+7d&nݾ՚o90(*Ξ>੃9V޸yfݶeuf}̹>-F]GA%$)$2*J~pkOf6KN* %'#0DD@7"nuúz:g]9FwU PRB3źx PlpZrV劜Q `EebpQE 494vK1sl*%`ERbƂ$ 2è,H1#*y@cpDot2UA?z3qʢ8&CDQ-*-KT-5Y)l&q$= kA]h@Us 6܄dV@@ j$ј&֚ʰ5$[.\* Ysf3lx*`E <6tDOv%zUR5 %hlwyp)2כtT/2Yޫ߼f`oэgxKK7UhUՑ&10Ꭺs+sYd`øCcu\jՅj]"wyH8шW. UX9U@l1fb^V֔\EEC͡沵W%'$:#iB&291!` Mi! Zu6s?S(҉%J]p5֦ V *<;E/Na%-sڇ&"KX@sA%bla"pYF!ϫGKY ۭK'}hlM G:HDui[8D5(6c@&9ʀlVrtENc8rU(_f불j˵d -*Wc mUi\IHh5U]dy]Zp@`;pЅ.;!D¬`͹:Y(PHoYY4= ˦ØܨS& >F%ʤ:̜:`F7d +T%&-6*4!sִzPd1 > RT-Jl$=M1k†'7+=N\&lM6k/S;!1irȑ@P O= Ms7~z}b9'WJ>4>򬄙ڏ~/~).ߤT&4Bc$?صo/Ü~i{Ww{GWGǧƹ:nD1Faեs P*g\}s|WS]L3$\bV^͌$fAvϐ26e.`iAfo;pD=g^zP0PJaA7+RM cНbe9rAjD M\g0[Z(GrD&QTEܥb< #`gTسHWn˶A=5๴)Jht s"ִLf5V2ᣥ}/< P ?A&q腢|7q"i}]E8Tr|~r<&++V.H`*77jA(A'G+W]{G' 'cPNGcRURZ Y`z:.sy0W&6d9qd, +Pە,"V)[9ٚD3t*2M(8^\3ѦsC3zXz &4ZȚ!lҲfAi SL}(MmE5]EΥM䈌+VM"KKNcصzv /-˙轍fԜUDD!w! uC t9ܐ:HWd A#2z)3aL%+M\#r$щl ˘)0bղB#]xQ1CF=6TAj]> D (Gە,`dвՂ,^MFxYFyƫl c[, &7Ҽ4=fjV+*J#Ȗӄ .pS"2o@(bD#( )2˨-٭ .RG"ȉGUI0CjR=B2*dfR)G0m jXE⎣!EFGG'yR WA 0.<<٥9f故7]ѕY3pj,5-rN^[c7:k4TfۨuŠ}NWޫbi Neͫ؟#;c_;fx),ߺzNk軗WcEdWg~3GO\LRs 9fjϾO 44XxږKO}?J$~_׿Y 3{Z;+~R _O?;!~꽏>؟09Cf&dgeD5,mr*LӁTT,+gU `XYFȕQՠ.*4bJ5 F7RعIΨ=zN`QN(mt;p6V;Eқ@tWj(jeN.W/ߝӊV٠霓Nz_?e-J%0'Ang* /^^w"qu+z[Y4Al}fi/tM@WehBG ciQTlpE{ʇe{`#~Э}IX"\_}뫩a* ? {n>=ey'|SO~^lb^[Qmrm56HF%p`nww6p@Ll1/EF.H}q ylDoZݡg1\TN.V{@ Μ!#òGmqfrsE}a@0^ %j1[15"Om IDATYV8mN2`N R" TuDUfGzc V.U9#zmlYdX:[j)QZd <?xB+kGzQ{ 7#v+&"%%+w "^w_~,׮=w,7=+~\ ͘lsH ]2AmC;v*#Dc-޼WZP0 mbۊ1-"[7G;8 q0]}v΋(*f3Lw" \p[T82ԕn? K"1DQ&9qٲM DDjxF剠'^s>ƄSB*`'r1 5V!9\2 .96fT-pD\HVVΊԦlW&UXSg 5jZQ p\5S!խ&rө$5ǬRX6%nKd^u!M:'k([IdЙtU̩ND V20mZs#Qtl}aX9ՠbpZ&[0)aM3`߹83TIzE}L U[9 ԐahCp~Ƽ<@F󩖽;M# JJ툶rl޲ CgҢP=CmsRbBa92J[ T%3L[D Ҁ1Ql(04kHjLyR&]&)0S/D9:zb(EçX" (jTS#CFLn_Ewhx\/>bjiXvF.ϼ8h HͳsW..Ɉ+Wz* "bg:q?G;@z/<jx􉫱,4oHBsr whXc2L]V}Ԉ=]YBe2XrA0dZ⠃hb*U6w),bI涏"Tk$cp̩4r5pZk=ʣqVF6 T\LVB MbqzKu^IETRfR&VeZ`J GˋzI[;֮?rjb+7O_'4-\ed8A)Z?DZ{N)xNDեd 6mR~pS`21꯯_{[?W8fJ[@):f4ʭFvW`SKIs@hj2(d#gn/Da";mezUMe=h7*RٲcvLSJ t h`Fbs R)6occ8Ĕ&!W=1C 'h;n{?r^{_yPuϚ`"*<}~;O uڟg~?|wpzU;?|囯+}~DϿwzH'^GrSg?1GQv_}?:D!% k˿ww냟NMDiO߹߸*Qak? Uf.g7oP4ԧ7fE]={{$4F?vXNNƄ[''~MrSy~w;+ǹ\Vģ&n9`Grlgg:!zEBrN,ҘmyM}Frd1:ҫv"dyg^:u@8v]Q;Utv X F0][VH%REJE 04 L]J(*pvs㖙OXP,NلD,KĆ4b%bc±`Ƴt.y@ 9(Ԍ&BN-ZH'Ƭ-d)Ɯ,Pyco.x' ( sT tƕfw~yq$78j5xp#f%lPzpݻ<vb+Bҗq.!Ʌk 6e]$7jžJZ!7؀%3Ȝ·fq@8[HphY[˟ŇD# rrw%gEjM;Y]z!9.ɣ2Ws*7à[ V.VP-$m0gf'qNPlTaU[ "l2TkF(]ӓNsOjIfiy$ cS!E^dUf)+QCp8%-2V܌PRt2gZ:uxj*B5m?DZhg_\ ܰM9ůHX@m s&AClـp+GXGQhn;%eYSEPBAѲ3]P$EKnUI וKörY@Y$;}F9a! 6b:uZ"+P@21U0 X0,L1<57r=(L(&f^%9``X}ĤkΕ&0Z TIIܡ%fETBDӡ#GN޿z\A} PUk͠5< t,P9*j»O߼/Ù7~x¸;U|D\=*7vmZ؇KFս~{o< TLJؼE\]b #̕/'/>>A[g?nGoć?08a r<XڔG֚Ad4eYE%Uۨc7g (t.Ctf9e4$ dYàE@-vR2#;b\ FŜ7`Y3(Vx.3*F+΢aetta |zsZ?Zsq eرUΣOOOci*>_vN,G}f.gpP"I[ EJPe9Fs|_}PMdLNk>SeT<_ M)%nvǏgVNCVrK̖Z+%V-*XXceYmh$4!FpCTjcV)ei΃)rmrdn";jV+R7\L#{UWBHbfx "ѧ2aR@sh_v~ÜmsZ{}3/nCVyyxϿڿ<QOܼ+ץfopKDvy+2..z}" dL䂈F&\U ?@LVs[V &ME n^s36Vi*LRuo/J3,IJVWvW_|ٞ!NL4D,z(H.Zۻ]}ݷnGv{׮[]tp4xA޽<{\|9a7GM͜PcM!&a6DU@Cfp N@Tr&9XtGrz* X4ڂYmy+qj͊JC!44BcZ%nJd9;[jSX.S˥)ZQ;4F0< K bT +WAATrQXm8TT9YF4`T]HG%ȾwX(ϽO-Dv_~|vgMs,#*Wabd|zſ7q nȨV̬E 8Xb!e`,Uw(f[_$*6TErcߠƥ3564 % Z%K*f搵A K-4mWu9Ɯks2Cz]e 22`Se]'uD᪊r]ƍݦ0~⪒ X+>2?t~Έpv֜c~-a2j:"Q,H5 ɀ)\T3i?K\\1]\ٝId9Nq4۪}b%bYEFy8WM _X읾t;Ťcׯ]yH0d$qoy=\idiFVej8G>x=Sz*?]iv>!|9 \TgP lSE"KL(E yLmr:BSM0=eµra`K=ҰIn,Ʌ omYT4P( }N͗t(ff䤦!JIk)rL`KMآEf@BNomLF )w(:j3,t:mrOL0k52#\X<9A5#HŐbDtH8r'>wDZ k12Ei"{dkSx pIזh E3'EM/뒕0[t0RhVplRmъvI8*+(7I$1]R5vGW0$esz-91j8VhpÜ͜+mb@Y}HGs'Gzf}gcGGbs;^k8SS޿o#ո+DS}j{qX Gwa}UjN7IugSqX;]AF䭥oru  ׮|Jg9"ڄώ|/!ר$~0w߿X`Q "~x>w_@k?G>kց׿r]xgֳMLpPϘr} 0QrogG8:gj}?;9w/?K?gJ;wdWy1ު|>)Q 6pͻ?FF }׮_̃}"Zpc%82 npAIܵ#ڨ1Q gf.e'ݻ%]e32I{Ef2-* BytОǒQP- 笙ъE0 T0KI#jP1ˈ$aN<"v6Hs'ܠR`:.mf eF3'"ldqfCD iW)'CvkA@KjĜv#|wna1.޹Qo_yCO=Rgk_oT8"Mүqyp.h3f}2@t*;w~u+,ED^9~w񾇮}ϴ7ݢ3cfo?|~´І lkE@ *,xxZyν^stĺ7|;u<'f FeZ \gppeA]NʻS$E (Gklf91jCf86wfBW~./t8gP5 z7/1P>zdHqnhaEX"UӫWd$Iv3a%Fl!Č@ p!Hh06VLS}[Х$bDr-h+ADH5@GnxnrK̘Rk-UD VLiN+ab&M,jcV)gIbqL$kyy11]ՎXWr db[G:RfɎ!i "䢙::"ջfeO!AYF&gɄAypГf҈pcs6w [ W9ӹ\ f U-bt0D.&6*&CDpat IDATUIP"JL$f 6MJг[lISIۮluSXу0n Lk#=2 QVFY\і9ݓYl@ajW8FU%!ˣm B% *V$w]C#qzǪY+HRS9mwkIݕӖջgc޿ID's=v8=[ǼЕ~z]Qas mU٧ns hWm=SOdNޛ[/~![7w^z՗D匂X+G0`=̥k->޿il[]/|䙏}N޳a1D { Aܨ$Z[ʕm7ZDIX胊~cr q_`+m I4ғ}#^~.+5 G@rc6T dS!p37<ՇK1+4Ȱu|GEuH?_=|=/$_ϙ,GH~߸x=]~_>s_%$zĹ@ٿRb|Ou_]s?ԇ~ˑ7]?||wI/smh0=̿yc CFtf[BZ[,1~ty'lAa{|s=`*؁+&6-%r۩ IbP .!gǥ.βș`m MIh Ԥ@sulh@h ,mI ` l{圛 5BJvґ摽Gan .gХAY q(jj$^ƨh!,)icO>sݩ[v8<UV\YK͏~W_׳o's=룏?oPeʎ.R3nݽ_|N>}w߻WW(aeUDM+Xʵ\p-fFho|LF PeK͸0pD,hfvO~bw|Xo<{ö K6}ڳ{&9vļy+I={l7P>(^{?}_j\>CR"O{MlgwuS'2mylٵox~~ΚWޜJ[z#h Qj 99ha1#P'fg=fyJ[{?]qﯭ~?|yӫ~GXCo< EF"u .7"ƛo].'=tѫٯcyI1a@+\Xc&\\j3]Ą8(^"M~YZ..હt9#`2a27l#l|$4d"7tDH-Rp;D` "^1Fi3nRfZe S 1BnYE(lΩ-A1eK!+JPp=*?[LE'zk}`*G*(Lv-bis ]ْe;;'"#۾=!NN7hh`Qm`p6 XF 6'] kQlrE$) Yɐ%=M5ga"pml {hd"l[jBQیkz(eq4i89) G0j]1zQ<A9`~O]C{:m =颇n> WN[M7s^gG0I5Zi=%>—V,!odɐKo[)=o>ݯb3?W:8>3Ys!jKuoA)cR+HmbY[L/S7~?n+ Uw Y4`@-4hNJjqPM šbONvnh#wo{'qw'34ly,=9jVug_:̓}˿[r,'To]e2~_1_m<ܴW׍l?[ŠJi^m)=u2]1(2xtOl;nkۯu~CwgsW dv=KBܛ jh4FZКi %Ӟ$/06T8hJ 7j14tբ#asZv]d(/;w|w%P7o\o;\KP4lB!pq DBKi"f69Ճkk% op  CmrRDKىL(FQ\> ,$ 0{mW4C a.o|8HF`:{B IV;z;8˜AK}ڄIwqkBͱU7LAT=j&*IWS2GZcGd#2!{E5-ù,k*..fl` 6kl6#苇B4 œ9hE$Β%ؠr -6qjUj /4 aKY0V@$`kqn{CeEք!5Q0FEQQ& ([BA%ޢ+܊GkYqr*SiZ6lSc@VLC#Z6aA7; >&fԊen"4Fl-&rK]v4̌KVp<j Çi[sIŹQ!Vb]٬IIQY/Sw-4T )04H!c2g@e@Tw5}G8 ϶\Ym쇣^uL"7? LCYx틯_ɋ.4.&U ,oޒg~aq7Tooːc.{Cep$4GϋA5e܁FH*dABtd6[|[{IݞшiO^CT{/tdkؑpmyVA^{λgҳDR qW"ޣa7 8r;Bmj4tjB@`9ѻ#9Ba҆2yE2vp[اXÑJ<4o0EK:}V[ M6 "`#IDlǚ-#h#fN0JR̥¹߾3y8 i\.0,kZAu;w{"f^z}7:abhĘ59JU+% PCǍƟ~d\k37l1 pHV8S&ZKc8pX0 6rGVzC Ԍ"@fA-}r+@F%V!<ƒ(]nb C ›6csGlXvYt g:e0ayICte Bwt$8!~B]!fZp.~ӃPer!A̠^DxE- ɘEzhDE`iN9 )'|d.Xma=G)87~׳hEELNt\[-Ԅ-v' pxqD =h${eX 'AVs[$lY,*LIg(k3]x?hCPY uO.~bJ%S `ҥ:>;T".\b@؎Vs6&r"YoߘVKoEU8?ahuQ$T+ܢ!]iV@v9\dYF0Zzh!vEɬT̐͐adŠ. sVU SsQ@'ֶ3IH- ]XA kl@֍m]zĖB3'I#[&ť칅1/Re&ҲȂS@;H!l[y]{5u a C(,AeeEwa6TBAF@-=`dȪj%rZPٴyQ)̥MÌ 21&5`Y+m3VIt̥d^\259,H̙ 3s'0Jm# lVsˣ$ZI!9QpbKIogi6n/Yќ`HTY3憥´(=`Sv'9kbkd9`m.T,p00= )660F4:1,nNW2Ch:6{G4*ņ;,ِa $쥹@6!gHKm#EFqUm #egV:ݍ^ ,&nF8hj]&.jj-3 ÎSm ;@{kbhV @ȅlh۶5$sZn-}c!ɝ7K|j<y)L4 nZ;f;ERyJ ]W6C-j{Ƙ\MDn8=E@=e'V́XRPCd l㠉!0Ǧǥ94J->XP *cMj`b]h7c1T%bnF@]#Ba[ ;- aE $h@"(ZWe;َ $E9Y-*AK8&\@fCn `1DDwxjLKI'dZMO7Bp@ M>2<%IsC}Z2nE07,呮(̰D}|n1@ۖJX5A攣 ԉ玊zmem5~{.Fh/LF|.̭ReڧS]#k!_@-znP??>52ٰDpðq,28ތ)h|5!>Y< @uQ8UAu_J;L ezdëo%ph{*źYГ$ uGeCyQpڟk0|.q IDAT빢z=yŚޅQt_3 X@1<_?S0bJLJ|U TЯNhM1M9UH@`pueX 5ffEAq VXPVGi+|BFw,` S{G`W!83/j oG`(hxCr"G[KGAK/&rY.7@B1f1avNbNRۧ᫰^Gϔ o^_?/ 0^(a"VU.P1H;ubI/ÄP73yzM{h7uS]P4)kkpQ,:%=SʬMK97%$5br](] "(!/K*dzYV}V&A߸1[֍En֕Sq;^@@E|> ;kRM"]cK*9Ka3/5wQ0>Ni_FVr'DA&M&2wK$SU-@ қ{P z12}g?@ y|B.&GC@),)0HuN5)T_ z/DCmVmƊLL5n`zxIxjrk(Am [PMpTA$;-@׶2u J $Ͼ SE }Uy>{Tq3&B;eX6<*3AU,QRPF`l@9C O~be堛Y6]Ri&Q4lJCKf@x d `q Y,)Rc;kP 0 Wt͊H Ep*BAL9yqqrBİ1 tHRQCH.aEbn.Qp[V;`RS8TjB%v Ʒ0}?jjwxl1p1LsR\k(%]b8B?ˉo S A ]9I; nEkF#Zs.JSEC͗ ~/ŭds+6 ݪC5)굪LR<$+e.b^3S{]4}6Jr2^k#ʕ{6eؐAX{ܮ ?aRn )K+`gO(T <[x?>ATg!f[]h:k2Un6@UB&qgKUj^P`A0i~QST2x%0v1Ia'w^ ѫ #!(AEA5=6o,$?7kr !y9Su_t Pf)-Faq.nf]\"Fï!KkC\ 'O=6UP1,YKXfqwDAt+;Yyt0C1 Qde3\C"L=p\]rBoXRv}enoY䒏JYz D(j19txm3[ B w bCSZ)7bhL Yg8@=j<"]"S߶ V.˦. Wve&xyK\ASof,N?@ !ܠm?kQ}`<%@QsIDRqhANE]=1LT!pya4i{86CJ 1c fԐnʊA< *߇kFS2#j\di}r&mFp.`2C~H[gkz@T<;2תJ<}>LpCݽ6|~:gp# W_6(zT Y~O(w# йT7 媋4h:0˅cDcLC!2Tu!X?e1R6g4;E*ŸK5re+: +z`A @/ONƙbxQ@F^B\Kg,IYRa%7 ;dTYOEć%[2} QEl4RnyByxJ_(l8FF>kCl &Xrfpk B.ŭjB'{Gx(X[Tl&i):֩`X-|\IJqz7ꂲ%kF2]u}4gQ#5:?L@l]U(XSʄeSi|;A5RhdMM@ѽlzg`F/Fg; !<ش-]0(5{@y,gYsft:|s %߉hEQAРy&A\c& PG\Rt:VeaI1HXgȓ;|ƩKdjv{NPm mc,>dٞ*K7 אpx-Pev1૜0/uŌhѤJxDae.!m!q#WSÓ*{3]~ؑhn!q$Π-]竇au^ US;LcΣC}LNSSn{*L%A# rTh'ov[1VF"/JOr΄(VXiꭤ@o;/q& ]I`_35[.)WS@ē} H+A;5AUEqF&VjR0Y(G.{a*%}-?|z (`%S45-k7@U  _t3A} )m-ᚘߖK Ltu @9ḟ'di#V6Dx@k&E7[NXCעP~M(jByJ*@H #. $Ж P/^~[1q(A5'}9)EVE{Lw坭'K8]OHۄ$=* GkLY@b%ڮ7 %^Am^+L/D$Z$=`0 U!2 ?X(IIߍMgT~JWy OI"ך[{"ȫ*Y[-;IRS(6wyk$}U& % g[~KH%8_'!ht-4P?wi&%qNIW5â vTTeم6qrVlr^V3Q"eIXgգ ֘US$b6Tg.Z_[%ޛݴZ%0]۵@G6LT!U~&@`~lIDNz Y$ݣ7i!V!$ tzlk$msSαzcX^ NBqŹzƫKa16yر=^W5 VGZ>%J2E/jw/a:<1!jկHxԻ 1!r*Ś2HP'^#[#al1K `0łYˈ#T*s 4VkPV`Fj 2@ᦊƧt |f|rbUpUu}m$NÉ) ߔjQ% qdfPـ^!Py_eL@x`!#Cc'xpϭAUnJ㎾ טG Lt.*&x֪ea%ڹ 7i֝-פaޠ #`|8e[!Ԙӕ&9pJ|$1Խ~)}yE2WlC2Rni!ςȦI%Nh.@Qh~yagkw<)ܙM=gt>r~c/Ps0#uj(ˀMp:3[AqF'{GS~6< >Kћp14VcPp1Drp߳Ub3%]>Ϛ`\]-0ɻ+$:Zm~)Я]k8(!{]z 3ټ$(|ujXIdVͯ#`,J֢^=Cx0,b} umd"8A 1˶t:CR *$)7oS6L%5xA>|m&s/zр`X`mrd!`5F:So5`d\4/0qR10h톅yq)r l{:8-OXdn1/jQNMFL@K_};jt5@*4-'G彑Q)#z9 T.hLMeU/IBwdO&.߻MnZYDSA(TXCK'pápuO 󽔪T K?n9YG|X2m BZ3!>-k%Aԃ_+#NBqDqc gM[Z8k (^Un;F6@?e bK-.C@*(\[Jsd#"5Y"Q7 3uÇsf4Sj T`M)0AU%1Kz 4յ\`pߩ5ȷ72L1|r nYA$@ŚݤSfMW$;tV"r繻܌[H;|c5/2| aN2f.0^S+gZB,]XUaHb%-/)t|㒌[YFBd#5U1ު1L&t;)`dHq8Se=tXIz8ӸS Qn U*׉ IDAT?n_ԖṔϾ'jl~^`oa]IX2 bYLH/V dǿfNS@P؋-HJM(Is7o° 6϶:/vʞzfTЖB{#ǡR@!~7jZ%֮EhB[Û#t*yy cbe}ШUf.[w@,=KhDPoEk*A!fP~i8C 0NLyf!2|kih9+ _ ,qZ|VAwU;q5 хaH^ܥD#ԜWfԆ#ak@8ڱ QltݑKyz4IzXSU$g w\ #TE.T76Zd~(#;y+ң,w9+L=JH2ժ vЏ,9Oؤ1 3s!)v&T jല4)W |'N#?3+;J?VWMkLXve^hnf;D!3j6:qJaaP|5/$I.='kؾOͲRc7wu̺2wW6weҶTCPmHI|Xa= X3Ts= =x OxBӇ>lrYH m&/C h>D0n3 ni:Ld :pqP:͍jTҁD f<֮6$u _5#e0BAiƳj=\@?q\*OVo'ugMT@?_^=)[@D_0ғۨ$?:ƹ3Q@l9p #b_&&C*K#7}7K#*A^M!M$ſn X_UYٞ vi |IMΈąF1YȻ@cᠴvoW`*1RGc0X`L v.S۹"o76_N\hr"DTD<]»R͘"WX邌vbkDc G3zT" !*ܜ ჾdr=iE̩3 3 %.h/i {q@J"5Ua*^Ё+qqYPYx5[.q,T@ +ymt;ԂS  .{a:y&Dl 6zŇƪ qf2jC ٛoBh2o'Iqmߌu˰8.pZR$ݟ uNd1(!S!@=C5C4|`nWUʲLO*?*YrR<3ok0|l@  h:G<A (>e\t H+Y rzx\XR\L$o7y3u*osnlUN4>agBZ&OC΄JXQX3G݆΀y? Wb~vk c8A=ϋ|~a8I R7TfunT]L6fp"TT?E"l ;E@])͒I!@obC6*X#.d@Du3|CcԅIe&p;Ĕ]$ck;Yܣ:BQ5G`p9!eVLwGhPoFdFV7rVe^C}㪮wbi(;h3_]RM &<3G﫾z W,3Fzw[{939+[UL>0CR 57%&pLKd6aSڦ}ݏb+x*RRۤ8z xw0+`o[r$Tn-sE s}\h2l)BQYϿ8J`Շo=&@bOaP-SE $ :k=WRONV-r L6($'my-tyHFntb[G<Ȱ?,=hlPTE X@ cb;NXde I*qE$#ʀSoD*ÀI(6hfDe9n!Ft$R-R`;ԭ!\JF:2,B ݲᴐV2tľc!g"^0юq3z0$\nۚ05J@,|>)'t*ʴnb.X:5H(6E%j;\Kq̌K0^cRjćK⍁I鄨o%@ wׄB'( Zl>lmqR1z}aaWyt d\dpNUazp@=1u}x ,lC#պ>vA.݅6pW0a^PJx ҝYlBLzS@((JLݰ'F(j*܍~V_ GyӍgѬ}=R +{WӀHF$S[Tźݮ_^?E5qC{)(PcPǠI$ջnH-%~\+yOHoofbT dq`1"f#5MSL2KtA͍,wB ‰wtlv47U|IkȣpB1NYTϿӥvyA\w_"EiU,QY!;tkqZ҈d͈ u s$e>;^層 >.9 3bZȀ^O0M`HB*x [$B͈`u[LÃX;rX]MQ|l85F2rd2ń[eC׭((yҮGh,C>~ȭq X1g6fx A69cV*𘔧!C x*5+z `WC]@.'^[C Iz,M%^q㒾;ijÜwJ\LSvxl[N.v}nM2CVkֽdqBISdfՎ~ HA}ÏS"7.ӵ$*iBU$.^+|JB;t&@$ `{.4 z%+=DZCny|ܰ&1sAc7dc]R/~i7U/$}4Pgi4IP6㈄+devJwǨ͋*m @+jjjC(fM$.J'1oHp.{ 4>+ X[yy!0ly, qZSi[LJ&t) N+DL9J-Nlv^blXa.)SYc'O0OkI+G|kāG`D8>LylVBңRf"0]Y3q 6KQȌ e'@𶠇 ڹ/eTE>HG.Zٱ^_-;)ш,,ZM}gFm·dGb"f9421Vui'E:>7>v*g^n) hC4;558;T!o4 W_~7?4Wp4BbM#Ѫys.rQ{LhN 7KQ[F6  TM1\CǹUm8oGjp0_$O4q)0Qh1S*J J(!j{Fuc7)_sY>q'J@8VHiQK.nQa?b yOA#ڹ'^:O-uD9 urT]Չ6<}̣88ḫAS`AUVa8%-ͦXV8ŗyjZ~5źD w s[ۺ0$:ijNji8{b

JU-87x1$3 w]EQԕ^^D]G'dME$3.jY#'`#],ƹGA O|^w2AY:% r)?:Ȭwo,QOg8gyUswA_5ҒE yg[%AQ*>tUQJvX$7:c?r&m`DGXq •LQDKlƾ.$p`X^QLX,87 ;B? CEAm A Y0 h*D*5aYChL ~T U{.NIr D@ hmHft:gZ 3xS0ϠOIMbb,s-I_C &\ѕ%zUS*DkP=ׇn٩#53xTlɆ+ YlNr "wc/dɭ Ct0B_Rg(mV*|E7!k FW^;oDV,ۼ4esٞ΅ \}MLq'{n!)"^.oҔ~4~0l$gSdq7?FD}Ei,Wr\|4(z(8AW]< ǻ1 üD?l 8wuC\qw+ y|L,D=909.U85<(C.s( DuY)&9-g "-ΞepCy/K4SwiQ1@=,>hɩM8-#ti<THC*èt9LS! V e^Lio̳yT3gKJJ3Ě~@Q\+ǵ.hfغvHu T`=2X~ w1-W| @^Sb0Fqm*I)V!CιNJLH˘>c觙,TYb/+u<HP7\0"p 9hWU2gonY AhiCbHNmռ'? vN9.4!gBwo4'c.߃W *d}-ii4ˉzd(as |'8z}Y =,BV.3p )[5:jykaa@-0 0`b QD?qJ$F.&2h6>>ݣ'G2cjGeT&vS5mDf3yQm)\x=d[ ZzqfI-fd+f]N/v[,KxIg 4LMf[O~ IZs<),s+Z-hX; ?̔cQDO[D^E;I1kkf׫b[47ROYwJSUhf˲#äs>ː )lr',%ω6Y),hb]I޾#4rEz#]T,'쭛:j1b?/Vjlr#Fތ2+l- Ϫ,*Ikt[xP9" #UCl,=A؊AζU%TNgocG[DLN<,f e А cEd6_FR73+ͬͱbk?tiq ޱFSO$6Ե[M[- CJ OR1[lYd+ح3^ݷUDa dнP~A]tsk[Y`yJUY W Hi0:"8eZ9;̫oAl Qj0D$ϭ_Ʋ. K,bz,9S1[{9=|DgsM*F0?A_XZ. nQ3PjI{D\t}'c¶3MFɕнَ~0[S+8UTF++`NpqkbԗWȪ g8M3E[-n偓]x=/aDOPA{ lrg=6?7a=iw5 ӵCTBO8h) [Y?2V]6D|p!|vEGXu vB`GrP.-eӯe2NӯZ l=0bO}rml kӲn'0{,^Y0AE9 ʞZy iM֟`ego[v1'dޕE@tT^bT9ܧT~ڏVf hkr1zUGO l58a=Z݊j3&У̥ITVА#Ewp7t% BTfzruH w28bi(6!*a#cĝH['QEWlr*ra)@mPeBE Zi/ngi){†Xq4|$δ@q,`<ȁT“0HѠ!vqğ89&轿UWxNNrnk+$<鬎"bkw"9#[|T7Q-sK޵$Y a̪ڻ'xտb` %")^!G8ZtnY$In ݻ♩Wsk!'T~%+DFH0=)=rޙƱ}:`ۿ6:؄Y]?:ę^xD\Z=6B+L3*?,"GveSgqHlr6,dq=XhV:ܲPŅH? 7G[ Ϊ;42s Uo{}zAiT!"RUgm8 Dv@P~u:c1Ws__2Vsl \;D)MY)"<" հ~W@^L\guJ|˨\-}ﳪ pvCB1I^OU55D,ۨYsOI Glwřk6U<KDp@^JS2FUѲfmdu =-R S`Ġ,;_$"nH&O|uGci,h`lm¹k8Z\t8G-`=kS7ӟ`+̂ڦڐ.]RC+y'f{3vyU.% ]<* UЄ荡-!=ax@UT  H' ||74.j#\n()JvaخB`\ԡ1KMZ&Cvot /]wɽ %B[;hZoUtB11.\['rQ+vm\Vޙ[`1C FQQ 1{*϶車09)f2 m8ГnKaLT U'i%w|b|ɝ$;thg=@zA"a޽E$?ZA56l.]Ë]dU;V:ܨ6Y{8TO9Cъ\= ycl 5<-}a)OE[ONA,rUr ^T tP"U|Y`TD}1,dv{qƮY%p%#' OƭHD պ1b"tqIn6 W4N lD2/dKTPůTs,fdNڴ'KH蹬tpAP?}quUO,†:`7 `|g ^ `ޅ;ufzgN5iJ/ ] J2(9d?zsYyYwv,1NE{M0| ;bg`ݣ %<8qe a"0&0uwg!y >J,Sƫ5,:F {ũafie䤩EWd @A?K Slj+@>TCլsW:/b\ vڥ1,e"[M#n#$gk:#sJ?,[1 =MF7 u6?ĕsx`޺Tf8+R*9Q+eMzg!D88$O%<xR_D'o( 3SG *U y2yqn2~^% QPۅm^T5m:VSkKH֜?KS]"wHTξhp=u}hQdGI6[)V:SKw+h[\t&u~sXpi<\6k{W`Egyx2RޕhEln-CB a=L؀5?ejlߏuH_*>#p}QknЩd=QZq3]5a!WVwTBu }^a1b+03ڄ:pXyaeVpn!10tkol;U]r=JL3{w:~ y &q2Rk!x'Q ]QPs^ 6%MRxT(˙bMٲUQxm*7Iй@"!b@;W#w8Q@*ǜ%-hIڂ~1''^CA}v0ޫUЙ.hW՜Rb\ |ݔ'O32oThLD\kkGݼgH&g}{o@>[6 AxuRh,77'm8>,D!vJ}&^~d<7bk"],DJ!  P[bU.nuӢ+^p*扞*2+GfF\lw;"6RcoDwDqxRD`)B7#R$D#Dka< ZCz1m#T&ڭ7dQ׮aȻ A@)(,*[TkF#LoK%B^^޸kaZݐ}_!lu6ϼB̲[_e xwtߨ_nܳ1ڕlo)qHk@rc+ι!v[*;1M`)-lyeШ\ #I'ru2Nc'G*{`Efixuʫ@d1:%!U7y*:д1|ʶk8պgwM-oșݶܸ=z5ANܫ/KY"0*޹ @xYޤ?_n=![vtC˭[=ڬv|R #thX=҆j'7.W Xv;Sp^⧞7VJ=Ƴ"k>G8I[g6ywW)LV75XM1a&&#iIALFFXU T-QXe8$9_m&)CƩ)9څr";qR+ef;5Odlm=UQXx[;׎uuCwĮ5ilRLPV:fPx[nfC#(Dc凜U/<*=0IJBGT&:kZ|̏(L#%fstB<ܰ{#W3,-LqQA|ςR9`I*˛V4LY"k^Yꊕ 5vq4h7uq '0bt|g!{w).j%rv.;P(MoK񱼹sw}Êiͪ`a1dLř5b+oϝ8r:f.Co p$2gZ%Lԭk?c ;^+a4 U଩Kvzק^ݜ>]LDRca$w*0f-Ґ;1&ӛl{sF-)#1pC5Yf{ 6ec37=8j/2Z, ))9$yAp^>4)w:^K?(.Q[-Y/ :zV_A4 wF|]VkG beP0=cXJT7\\j&hyiC!2ޖzp"X>CYU-5bKɩ{(nmѨE@0{vT`FB @!aW -'^ Z*5*i oدC,-FOh78:#+/%/x$ YMOk vWQXwJh`E.'I Gy&SJgx薇LܷgFK\Qˉ Ց=JmWhxZz'AgA#SdRTF/#Tx6VHj|2Y`W. *aEF2ej#\hJ6һ/G@'Pt2"pnЕwFH }{ E3@BVPt-p~j1mCF] 1Sʺ{*/y}dz۠M:v}2" ]vhX":#K$c:_K[ rrVUAiH/qPI!YǡN` 8*% }}\|Ԕ_|r( IDATS+]a9l_T_Jilq2@ 9ypgtͽx+ެ Qo!7q^S2ތ4WV7t$Sun\u֍DB/*ڰlkA2V|!p0aҢg !Yք2Y[4pmˀxse!֒`4vץCT  #ͫbqG;e2LbAї &U:0KhyL5fLe&vnz(.)/! hI(z.9f2'o#Pw 1=mLqJ3v@iUec`wB uBbm!.ď/`wK3KI8'ݸYĶ_uq) @!9xúBƆi',] ŰU3U(ʼN4m-WUݛE ]{ waw"d([ߋW!1@G߸*^`īɰZ6OJzr"s~n4Oԋ+tKw8Qwt$*C51]2 Q"C)3Ϋp8aYBrZ4 >`N`ۤ vI,T u#PSAk[Ƃv8$¦^FSd.̫zD̢g hxYhRY$eR#VԿuL{PGt~AqPmp[ݥ$H|' l4OhvЗ,x ]1IrjK#YB.\HRjBI*)]GIAsz044TwlU8y6!cDγK8\^TkB=xkv 32B:@ ɓöݞ@ txyO/|7#vMT|N݈¯3iu 7^m­aW/먦A<0]%Nt{"=W|بC'ćX"$19ZvfaC1@ʘRncnu5tQgy>~kr<*ʻyǣs<ɉ\((λ~SS\>)"MN]Al d NX)uۗ}lʉ겕k x_1լBX|p!sCeBߋt9>HTۉQНxUL)CJul7枊ry!ݮ>w?@B_e?G}>N8TUSɼLB%l5I&ȜY_MN `N߰YYJ cy2d?*tN Du+ʖP{OW 9&gkR;ʜKTT*h ]pPղrGoȕ^]%aOwm6r6.ڻ6ndL(2ݰ&wdP9L BVl1K̯9 )Uf Ts o]wH6mNn#Gն] #ޛY1v4);K*,y-1u%{7Yȍ=TfUZ m2Y9iAgx bI`Ďo%!\?"SeTuF虠NF(?N&8 ߊ/TRv,Lh:nlnC ) ~H>RxSS3.;ê =Sf)~ 2h' V\Fy/\nEJA~(K&g 7IX>h.YTyQ3!s2@B\9(.T">dKS|@MZOb$e/Am';bmώIԆ/Мov% aǀ z[DˆT!$b&-ܘPE|C6[Gw0wtǰ՚=B/aqq&V0|k-MBocA G|MhoXH*c=̏5om6RNyfRk?wJ)ܔl @_313W3Ͽ {[MnHpe2)V*)әu3%:mE̓Rq;Պ˙˻HRQ tCvT BNS+EadB&ܸ<U4D^JXhԢW$"Fꬵn퇨D:oyNr/?# LOzCV6 uø\W5|*;~ؿ\md_xmEaR$H B /=Oqv՘0U٬y{r(uC{׺3S\Ԧ膦.y 6ܘ8VQfyH]mKwB9+ r sgP>`\ z<[cVx1Йa60&d2ʾ#kP lAr2:BۙOu׷Vٔέ??ȅGZAHݔ5\Ro,^KH UF ^33D3T:lgψJSbQ4U\H+` (z6+؀3cP#XeH>IHGVփbHP*gY-,b݌Yۅ:w>|jqNG!7=Wf3!ѳ¹Ã)^l52__$.ш` C1"#VŷqR;0G=eοbawL_.J9ƲT-U 2V m}[kA L@~:s]"vw!b3B(5nYh4F*dc .o/pF hV^rDM&:D=/R/xk&bŐ ߥ{hSUŶ884E;~VUŖSU,fj\96Z bV}iNޜjx޲+zj0.xH"yU}D t?[6.'/ ;Izc9Vj #)ζQ)'v}]u(CS"D۔hZ,#LJyiQke/@Z4Y>\<3RХ躘?.k,'5qE+4Ԇ `7Ѓ7٫ȋԡČ ]W/\OAwp^ܤ DG4s7`*X%'-Jx}"y85}%t9\ٽ *)]4Nwh[y8 c80< ơpʴs)8Xԗ._x[d#vN,«J87`f̷ػS6"s bxC!׆N%!z}D\sFU$EB gao{JYEBe,5l1D9/H^;lsټ{o{0n)y"gmSuuΥ:lĐE 妡l~ژB$שl PrY};Ź:1{]55yuT]7;oO|~{p9@PCt3e ["cA]*{!I"}Eٷ,Q}F30gay;TBC+4W)crkf *iL}[_H#m ,CEb3AUε!fM2Bި:*S[zܧ@Y@AUT{Q;)j IDATPAeLߌ ޓo+)阓b]^\<.MnEb']!|lz fï,׫Ys]k;NBV|K TyqccFU"cLgިDҼ{!c8b䠍((@qƍ:|t)CM ]KLV@qc ÕNwʸ5*Hĺqq Y%kC2a00hTD2 PV[kF=(/ jS//iu!ʝDQ -⊿Ai`LƎ j;2.+nޔh5iFu(LC ? k)P{Mr =uc>ftmà!9x*=cW|D B-öƈM]*%&qi ʇG$cпS,F.~51hǿ?H-}r sT7ya#'S<ƴ"ΌbUr4"'K%'Rwm & 8:r+HAؕ"ϔ&+Ál^}M:8(7/ӌނ8V;qϿ8 hZ&Pd>6utEidwg\}E7˅J_c],a%˲v3aVI=S+A&޽otӕd6T[XE򌭋EE:(F s$ۂo3+Xlh':Dg g5~$lv@sXUVB6ˁ#x3[%;4:aio5&A8TD_iu^EeWoOr'l̰'@;V nЦe$i2t!-yhⅵs /[9Ap o#+|1h.^ބ?_]t, 0k(Is4Z|H`a|!Q,f% *ldcON'?97ya{Bn4=͆I \# Xj:eB={쎌 x (%WNj`.R0JT\dD73 "d0w`+pqd]m/}w*K{i]gȤ/p9 4c$/jϚI !E5!?XEy=j|p  ֑1J] Rl$/Ua=ޢgz #b_?e)K#wW _hfhyLe՛eTO-g[eͮ'0w*t*?[ICMJ`.~c+~TP4SgՅ[-aS {kn¦0+(D(/sB*$9QBRMB IBgG4Pl(*!4tf5xݮ ׆`z#q JB/uK3% naok9pT{ %fjT¬qMƯWtw9`[qnYٖBQS]=Ԏ߅wZӑL6更 C?@VZB}$.X\L ƪ;UC a΁킠Эs2{j[uB̀kS%2EjȮ(E1v8,&L\H7/eAᆵ>M0on7 sJ~z#_X%! c?9[|5Zqq^t.s7Kq/}(ް޷<@Ww_n8+1eU<t3˳Gh˻<E ߤ4~ vd4~2=#f6L;$;j^9SQߨ7Kon؟_mfoT3EY#0rHޘ)Lbu4[];FX!0`xIx-"٫Z?^SK"f,3ԟ8`E+MXdGbAE? =ie4qj>L? BͿ2BS|}flz^HgЗllG9[1[?_Э (ݹ"i]F7O,'ݪh˳_ʗjeVlbIw'ʄ܉0o -zS&dV*lo{'ow[4an+ct)qS'qj׺kdݕV' &*_B3־σ ̢qεh(ib1^^AՌ͖8}{XUMx`5{h'֮(@1˟n0J*@%`حM5J- Heg&%0(DM,&k{pV\,/}m5Zx7%y]*F%b*X$S&"ϋ62rxYKϩ_sĐ#{%˜IHTc]ޤXXN͙ވGCuk |yo0M!տwL~{(\/15D)(UqdCؕ,r_֠'$,Fqx4|6SEWUaU8a烲0`q|j)萗`{RB BS|3b' e:|%ZM%Iʳ0Y}zTe_ridmRL2LC"2"tj]j;|G0hqP"^SvҮ沂=ݟ[[kEzO%yHy5 gh k4U'|<:hpHk+ _Ī^p)0*܁c+x@{PMy?+WҘو1W 8cbtnsS:Io2y ~KQ\Mn3mq~pP|wŭF!Ub0/ns* AaA6'o:oәlLe*;q)CneK=,zS PU[0%q_zsUid_h@B :ZğH%0*;prlL @pg$gW^e'BvIkGp`mwޒK+噣ᅂol(aW8R׾)^g{* s vu9OP' tmzn=.M`vIv~M&\i`dpC@J&tdk"N%t9 r3,UAsA$~@.0Pe1pkX ^} TwU5!y$Xr %\wnIXkz끹X\B?0XZfc Vr2@7 b' }v*2&/CG'Ϋ5X;DwP3DP~(+ eUZ!Ğ޳Bg\Q+qhc,3Stl7ϻ^/4׾}PapV'K8<$*P la*xuMYfWp_+`;Z3 )!9 @w+YMUŰʜ{ie[tۘ1}+aOY6L' Q-ö1 @ Zt7 q?Y * SU8c$`&=?-7sZbL{&PrfQ /P> }MԵ1K8*,o9pp& B{;ζ&Oz 6ܪKh0ר`Z&BȢ՘]J#v{PC:3 3[QʦuCF $MON XyQ Ӓ83넬<e&FCF_H *Dgzf  x3n\2 4O$݄͢vSp lWQތ6r̠:֯r,BNtnɒɭYII]lRٝX*w8_4w`-fpBgHΒ zOyU[(*xaٺ姘`SfcN8B.iq2,u6 .dJ*#$%k:s8:DKop"4*PSgbdt-O0%ҿ*I4Lzʤ2<#ᩢHJ\kq{R:W6/z~E/5Ych_S`_|ńGVHDS> ƬD?F{r{ijT$ V8k\KBj!_㞲ܑ~xC${-D;^Z:U0J06͓cAcߕQE#Nix/X-27nPdqkXfA !u ^+ TrJ0 kj%8T%/ p+כ W j[Ҁxc`J<34\5R` 3+j^jNINhPDQildv@`GLnC=eY4ś&k:o1pť^FðƲ/‘JZx]׹@pQ})ҧ2Lpc\iEz$b.a )ycAv+g*U4Nn`)$|+RrG=p V>ף73۱"z f7 ڜ8|Js̮TZW Չ`2YZFT)U\QAkn1Sδ40^Nk72ma6Ɂ(pdS`_1}@JE1ųokzh5T|+QT`%ES e nQU;πϲlh`"2[9`e<{8|o^".e"e|CvoHdtE8X3ל"DNkVkWAo]bsˇbVT7Y`qTUqQ#J{R䫑W@.pZ-d&* C8&J\G4 t:ͦFqL[2Oq Ĝ,W+_DED¾0^Ha3А46yHPoqk _YLx/GDlB!HOf0ڃ3z{$?Ż=Js8΄~{̤y/LS t{*%Tp‡S?Qp=jPr9֟c Bf;% \n̐H[=`ؚ'"w#J^^5G ۨY5M)DQzM?nNQ1`g =;){JX? ݕ`/uRz!j"=@`5 1A`Y*@!}ųx5U 9CvR|-"'[ 0^O4ę Ň-ڦB=Pt]L.`$?Bt\ugrEpjj\]{I0ܚ/h;o;JA>ziȄf.}ΛzJ,X~vY7g V| ~(O>}CZDv!˄Z\ kYA_f}/k&ܲS$cen5bi.V/.oAq]|L QE;z5ؓ9dw ' # 40BM]x@UfJPɫvSNHBh'F_)/ʳfLoMHWAb}VLiIeMyM-ލ{ab0ۣaOE. FP@·>шR=}؃Mm׭rn^::7>nT~W1. 6˃LZ3ƀnY̔)?@);]Dַ>ʿs*^vQ{7)+eq(?R>FTQ^Mu0= zgʝhL׳wx|,uvܮU)arC p.ى.!)Vi|hwR\iS!_fA78KCO> ^*t:kdtc6 k*]Vj"&Qƴ̑*~uŐ2cY-cA:;jCɈ,rA  #ytP0Jԅ{>:;Waܓvhd @ՐliR6nqĸj6n֭٭b'b&jfuy8 oFim#~`OG+ٖM a͹ag۬ _+@%JBS$ʞ:$ŞLHwacOP 3S'IҋCm`>̬cRP3B.(7HCU^!W-TtFt)sTJl*X.J5n;@zGV1 K4wFB=e8$0 ?9 /.xy'9־Qh-x7S/`.ZKY`4R /K`=XBʥOKփ2Ɩ` d1]r?5왥OMB%N&t::_:—:tfIy[qyп]GC+ȕrE4[D}=$6紊\` |#Zm 9R.R<}%lqNOCzn|+036JjI~gSU[؆!3>4bJצ蘮3& T7W7+%6,NFNA@WpVǤ+T1Avu cZ+@IWE(1 H}=j9U*)hRMma D.M&}ovQħ ;ȏqŀj?O yhI.OOx\E{ੇXNH6y1. s=K"}E{쐝TFU7+odT{C64OT"bp&*XU#8 ,id ?։FRꊿ^Qn(e$TW$~lsd-wyv/(5EA/ِd. 8#t_i!3޸|@L7ۆXAxsvp!>d.&\NM-"e%zZꯟVG~ŚU4'ǡ~Qpfk݉Q,^USF:x\Ȟ9¬H+v&W. [EkxvVEH.UE=٫jf7DZeѽCtB#gZF9 .v@q}}iǏwNhXٔ }@N0>P2Z}U*,.__+Ϭx;~}aK_Ar`fbff[тCPrsKKJޕd;͍dRJ٢;8DӁ#RTSMqηC4" ^5t 4g42C!9? ,>śipS[]/z=;-C~6tWdJ*\R'UncR"&N(n=ԧ!cc M]b<-Kl A+ip(4 9μ!FV"Y;,6}-e^A<|zU!fP}QN&FF>9_PP>-vN>w-mg!ΈT%g=56h)RYQf OC0?U -F;F&" FuF+W+7)95 NJW6%e^#rY*k`0נ/ C 4zJfW'&[.D:Tp}pbr{*3=ܥ*cijSa2- |d^|/r?Es 4[4̺m=S;%{GJQay?Y oQ6D@*;{~nĵgTNk0u,XP kPd';T5$ŏH?ڗ$A*]^!G\ht'!86{f#ͺc44k.IwiMy!,̠v@Ãunbtz Ҷ DJO ??4wpazw_U[/6VZOX|:{K{#;}͞oZL.Ny-8PS,ct-\=戮Qc/ټWy^Q(@%Hi Dae#XmBKw/hfJeE͵%rDaڝf̰pui~d..v_yfr[ ajy4{Ȭ]nléJ%oT .G?w-\8wVOk/̍>~nQCsB1N-+Vv.@vc P{F73l ʛ!Tj_R/T0Ju+fiEmcs`vmARڴAe*^ ]C<6rox؈µ6m|ۤ0K)Fxr~Gr :e\3ҕ}:I7 aGm蒔LJa/&8<~ǖyi wjߧW6nzW93JT'v(UCɭ J :T׃ėGEG%T3 8_5&GEfՃYpG_A̗-QpʚJX" ?8iFg[ .SFKaڠ"2y0LKx^9!>e\{+=hxFy%juU0v>2ZזPT,YpQ_Ы6"oҋ$5\4UxZMUx6* rkv[ʝl})a.`O,Yf#?& )2u7j┟I.AdkX 7XūgM皭I"-'=QJ%˩E-!:Jxj"lԵ:ۨ5>/f.ifOXrʆYТl:]H}" *]=M闢Cf"W(>cv΃tuܣ٨hﻆ\O'm?TŞ$1'9GYxs̾#ገ+fmenEocNM( - w) :6P_89Q5m5w?Ԉ.'LnSĬf&"*JY5yLL:NwkD\ LފAs(xLVySl*:4,wrEC.C}]l5fUNni#TP  ZkbJ*ج(i`֭]%@oX\?yШGX dfP0(H؏d}H{aAߓlȋ25*Q<"NX]i oGLJe~?]c3Б9i˯#!ՀK}ǯ>l-b*ި3&څ1]Hie63*J7nnٹ;LLw(0!2ҼJ|X d1&,J#L&Zȯ]ݛ`ؘzəc~y6HJ2Vg–.}r9aSEӋ"c3)C}RFi4,CcCjZB'pʌтthqxa4-;ӣ,fГ<A|-búnO{ SYٵ\[sF3JӞ2( m͖UK8# $RDP{onM4ְH]H%OJGfk* Qmn-oq&ΐ oC,iPHΊ)ͼĺ~GI*X|* #w}<휼r@@\A2ggޥ=- 0b&! rbDEdFďa^Rv@=aX9bPg| !lE" b0~"ͶQ?@ AA(D,2SԅUۭs[mPTY'>==Gp DE-*ѫJjH|[C$Ԭt^)z `/Zsg;\BAl DV9vWi]LǞr%ܓA:Q6OiQm4ƞi_T@ ,øHIHuE`4@zMõ?uX9;b 2UJ9e|9<>*=m)g"3bF =f Z_:P11E1D%=q\,堉3-]g2yZ֞ c>?]'['pSJ*] G,FXO53g\UWw?cP ?k \Qz( Rn"FyWLAvExUEy\N"? (Yv: &i]>A4)=K_q3$,~m8{G[SC@Oa &ʽ5ֺ C?ƫ ,eŜY}4m=fҽUL*%膵nT$x4r .PCJ E\ $G:SML_WZfh#32I@4@Ceoc`.FJ]M gOq ?%Bͣ:Ψ~KLtVL-Tڶ=;\X}a&U@Y0H0/:|T4(M0w#s\0~`R-?5rXgw PIb [p"&PN6 xROI!Sh߅-aisĻ}TOxb P+A>H{YP`eͣ,t35I}A L<䆱 IDAT]3Sp @z|̬\xR9~-k ; -f9td#u7G-U g&K*d5 lEBNf ْahSPP] օ A^4Q.9L:g;a",El5`|Ǫ$#h;QsYDBb'r6.ġ ¤׭#PS+Gx dx.'¸vH0 Yor5~8zrvp{NasH$E0N-.9Dְ4kb12="TK\iGZĦ}9`G2J2 HG83[0ffy 6}tQE>I/YUYbôتp y<>=M]a nQM78 XfAqWyf,=t|~0:|S:+yk'hqGv^-F秳lmK7pyτP03 S`A yWUMЯohfa2PuRt^h*f +]kw6gu^?NJyr^0r7} ?\{|I#WyO!5W\QƐuŭ;ݘۆg,WS1P^F(Rq8e-&\!EvRMC|cAFup}F:u8ςE!(ٻvaz3ߓ5qw2Tve0 jSʽ;"{ UqsKǮP9M/):*&< 3ITj'y/@%I xǑ3[ӡ޽ueYQ-UWa&@?Uu.rdTka^|Nj-ك2 5};bp_μ*g04˯4ȫ-ox>_j2,)c7 S= 9b`{N.,T|YLk`/ 6E)ӄ*>dXB{PB:_SwPrI\Z\e_iQE0'TM۷a7!MiԙOM,6o*Ѝ%"AK*v_L%Nô_&tQB=ʘ9R@D8=z2|_^} ~W^('`O Q~1޴ȇ!# ]5gjԄv/a }goTΓhb$A0KIj߆*u2pŅbebG!xxY*fm`1_a -˞4 5Un_yV^oH=T"N˝rw!fY ԝJgIۻ& j22+[.WR&( /tqEeلG4D}^[ D,T6 R^+ |cMp !)Wp8Gfz{MKUٓA`Vv𹎘zy0(xjlD * * \file rotate_test.c */ #include "rotate_test.h" #define MIN(x,y) ((x < y) ? x : y) #define MAX(x,y) ((x > y) ? x : y) GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0; GLfloat angle = 0.0; GLuint barcodeTexture; GLint barcodeList; DmtxImage *gImage = NULL; unsigned char *capturePxl = NULL; unsigned char *texturePxl = NULL; unsigned char *passOnePxl = NULL; unsigned char *passTwoPxl = NULL; char *gFilename[] = { "test_image18.png" , "test_image16.png" , "test_image17.png" , "test_image01.png" , "test_image05.png" , "test_image06.png" , "test_image07.png" , "test_image12.png" , "test_image13.png" , "test_image08.png" , "test_image09.png" , "test_image10.png" , "test_image04.png" , "test_image11.png" , "test_image02.png" , "test_image03.png" , "test_image14.png" , "test_image15.png" }; int gFileIdx = 0; int gFileCount = 18; /** * * */ int main(int argc, char *argv[]) { int i; int count; int done; int width, height; SDL_Event event; SDL_Surface *screen; unsigned char outputString[1024]; DmtxDecode *dec; DmtxRegion *reg; DmtxMessage *msg; DmtxTime timeout; /* Initialize display window */ screen = initDisplay(); /* Load input image to DmtxImage */ texturePxl = loadTextureImage(&width, &height); assert(texturePxl != NULL); capturePxl = (unsigned char *)malloc(width * height * 3); assert(capturePxl != NULL); passOnePxl = (unsigned char *)malloc(width * height * 3); assert(passOnePxl != NULL); passTwoPxl = (unsigned char *)malloc(width * height * 3); assert(passTwoPxl != NULL); done = 0; while(!done) { SDL_Delay(50); while(SDL_PollEvent(&event)) done = HandleEvent(&event, screen); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawGeneratedImage(screen); memset(passOnePxl, 0x00, width * height * 3); memset(passTwoPxl, 0x00, width * height * 3); /* Capture screenshot of generated image */ glReadPixels(2, 324, width, height, GL_RGB, GL_UNSIGNED_BYTE, capturePxl); gImage = dmtxImageCreate(capturePxl, width, height, DmtxPack24bppRGB); assert(gImage != NULL); /* Pixels from glReadPixels are Y-flipped according to libdmtx */ dmtxImageSetProp(gImage, DmtxPropImageFlip, DmtxFlipY); /* Start fresh scan */ dec = dmtxDecodeCreate(gImage, 1); assert(dec != NULL); for(;;) { timeout = dmtxTimeAdd(dmtxTimeNow(), 500); reg = dmtxRegionFindNext(dec, &timeout); if(reg != NULL) { msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined); if(msg != NULL) { fwrite(msg->output, sizeof(unsigned char), msg->outputIdx, stdout); fputc('\n', stdout); dmtxMessageDestroy(&msg); } dmtxRegionDestroy(®); } break; } dmtxDecodeDestroy(&dec); dmtxImageDestroy(&gImage); DrawBorders(screen); /* DrawPane2(screen, passOnePxl); */ /* DrawPane4(screen, passTwoPxl); */ SDL_GL_SwapBuffers(); } free(passTwoPxl); free(passOnePxl); free(capturePxl); free(texturePxl); exit(0); } libdmtx-0.7.7/test/rotate_test/rotate_test.h000066400000000000000000000020621423156660700212240ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file rotate_test.h */ #ifndef __SCANDEMO_H__ #define __SCANDEMO_H__ #include #include #include #include #include #include #include "../../dmtx.h" #include "image.h" #include "display.h" #include "callback.h" #define max(X,Y) (X > Y) ? X : Y #define min(X,Y) (X < Y) ? X : Y extern GLfloat view_rotx; extern GLfloat view_roty; extern GLfloat view_rotz; extern GLfloat angle; extern GLuint barcodeTexture; extern GLint barcodeList; extern DmtxImage *gImage; extern unsigned char *capturePxl; extern unsigned char *texturePxl; extern unsigned char *passOnePxl; extern unsigned char *passTwoPxl; extern char *gFilename[]; extern int gFileIdx; extern int gFileCount; #endif libdmtx-0.7.7/test/simple_test/000077500000000000000000000000001423156660700165115ustar00rootroot00000000000000libdmtx-0.7.7/test/simple_test/Makefile.am000066400000000000000000000002471423156660700205500ustar00rootroot00000000000000AM_CPPFLAGS = -Wshadow -Wall -pedantic -std=c99 check_PROGRAMS = simple_test simple_test_SOURCES = simple_test.c simple_test_LDFLAGS = -lm LDADD = ../../libdmtx.la libdmtx-0.7.7/test/simple_test/simple_test.c000066400000000000000000000067651423156660700212230ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2010-2016 Vadim A. Misbakh-Soloviov. All rights reserved. * Copyright 2016 Tim Zaman. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: * Vadim A. Misbakh-Soloviov * Mike Laughton * * \file simple_test.c */ #include #include #include #include #include #include "../../dmtx.h" int main(int argc, char *argv[]) { size_t width, height, bytesPerPixel; unsigned char str[] = "30Q324343430794image, DmtxPropWidth); height = dmtxImageGetProp(enc->image, DmtxPropHeight); bytesPerPixel = dmtxImageGetProp(enc->image, DmtxPropBytesPerPixel); pxl = (unsigned char *)malloc(width * height * bytesPerPixel); assert(pxl != NULL); memcpy(pxl, enc->image->pxl, width * height * bytesPerPixel); dmtxEncodeDestroy(&enc); fprintf(stdout, "width: \"%zd\"\n", width); fprintf(stdout, "height: \"%zd\"\n", height); fprintf(stdout, "bpp: \"%zd\"\n", bytesPerPixel); for (int i=0; iarraySize : \"%zd\"\n", msg->arraySize ); fprintf(stdout, "msg->codeSize : \"%zd\"\n", msg->codeSize ); fprintf(stdout, "msg->outputSize: \"%zd\"\n", msg->outputSize); int oned = sqrt(msg->arraySize); for (int i=0; iarraySize; i++){ fprintf(stdout, " %c.", msg->array[i]); if (i%oned==oned-1){ fprintf(stdout, "\n"); } } fprintf(stdout, "\n\n"); for (int j=0; jcodeSize; j++){ fprintf(stdout, " %c.", msg->code[j]); } fprintf(stdout, "\n\n"); for (int k=0; koutputSize; k++){ fprintf(stdout, " %c.", msg->output[k]); } fprintf(stdout, "\n\n"); if(msg != NULL) { fputs("output: \"", stdout); fwrite(msg->output, sizeof(unsigned char), msg->outputIdx, stdout); fputs("\"\n", stdout); dmtxMessageDestroy(&msg); } dmtxRegionDestroy(®); } dmtxDecodeDestroy(&dec); dmtxImageDestroy(&img); free(pxl); fprintf(stdout, "%d\n", getSizeIdxFromSymbolDimension(12, 12)); exit(0); } libdmtx-0.7.7/test/unit_test/000077500000000000000000000000001423156660700161775ustar00rootroot00000000000000libdmtx-0.7.7/test/unit_test/Makefile.am000066400000000000000000000003311423156660700202300ustar00rootroot00000000000000AM_CPPFLAGS = -Wshadow -Wall -pedantic -std=c99 check_PROGRAMS = unit_test unit_test_SOURCES = unit_test.c ../../util/common/dmtxutil.c ../../util/common/dmtxutil.h unit_test_LDFLAGS = -lm LDADD = ../../libdmtx.la libdmtx-0.7.7/test/unit_test/unit_test.c000066400000000000000000000040651423156660700203660ustar00rootroot00000000000000/** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2007, 2008, 2009 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton * * \file unit_test.c */ #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include "../../dmtx.h" char *programName; static void FatalError(int idx, char* msg) { fprintf(stdout, "FAIL: (%d) %s\n", idx, msg); exit(1); } static void timeAddTest(void); static void timePrint(DmtxTime t); int main(int argc, char *argv[]) { programName = argv[0]; timeAddTest(); exit(0); } /** * * */ static void timePrint(DmtxTime t) { #ifdef _MSC_VER fprintf(stdout, "t.sec: %llu\n", t.sec); #else fprintf(stdout, "t.sec: %lu\n", t.sec); #endif fprintf(stdout, "t.usec: %lu\n", t.usec); } /** * * */ static void timeAddTest(void) { DmtxTime t0, t1; t0 = dmtxTimeNow(); t0.usec = 999000; t1 = dmtxTimeAdd(t0, 0); if(memcmp(&t0, &t1, sizeof(DmtxTime)) != 0) FatalError(1, "timeAddTest\n"); t1 = dmtxTimeAdd(t0, 1); if(memcmp(&t0, &t1, sizeof(DmtxTime)) == 0) FatalError(2, "timeAddTest\n"); t1 = dmtxTimeAdd(t0, 1); if(t1.sec != t0.sec + 1 || t1.usec != 0) { timePrint(t0); timePrint(t1); FatalError(3, "timeAddTest\n"); } t1 = dmtxTimeAdd(t0, 1001); if(t1.sec != t0.sec + 2 || t1.usec != 0) { timePrint(t0); timePrint(t1); FatalError(4, "timeAddTest\n"); } t1 = dmtxTimeAdd(t0, 2002); if(t1.sec != t0.sec + 3 || t1.usec != 1000) { timePrint(t0); timePrint(t1); FatalError(5, "timeAddTest\n"); } } /** * * */ /** static void TestRGB(void) { unsigned char *pxl; FILE *fp; pxl = (unsigned char *)malloc(320 * 240 * 3); assert(pxl != NULL); fp = fopen("fruit_matrix.rgb", "rb"); assert(fp != NULL); fread(ptr, 3, 320 * 240, fp); fclose(fp); dmtxImageCreate(ptr, 320, 240, DmtxPack24bppRGB); } */