Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | added beginning port of libdmtx library with tcl binding |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
f086cc43cf3144ad167daa9f29d95ecb |
User & Date: | chw 2015-08-11 16:57:17.269 |
2015-08-12
| ||
10:15 | added upstream changes to tablelist check-in: a5e4e5f7eb user: chw tags: trunk | |
2015-08-11
| ||
16:57 | added beginning port of libdmtx library with tcl binding check-in: f086cc43cf user: chw tags: trunk | |
06:40 | use after-idle handler for sound/music finish processing check-in: e6d02b004b user: chw tags: trunk | |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | # Demo: DMC scanner using dmtx in AndroWish # August 2015 <chw@ch-werner.de> package require borg package require dmtx . configure -bg black wm attributes . -fullscreen 1 sdltk screensaver off sdltk touchtranslate 0 borg screenorientation landscape bind all <Key-Break> exit bind all <<DidEnterBackground>> do_pause if {![borg camera open 0]} { label .nocam -text "Sorry, no camera found." -fg red -bg black -bd 0 pack .nocam -side top -fill both -expand 1 return } borg camera parameters preview-size 640x480 scan [dict get [borg camera parameters] preview-size] "%dx%d" width height # scale used for dmtx decoder if {$width > 1280} { set img_scale 3 } elseif {$width > 640} { set img_scale 2 } else { set img_scale 1 } canvas .c -width $width -height $height -bg black -bd 0 -highlightthickness 0 sdltk root $width $height pack .c -side top image create photo cam_img image create photo old_img cam_img configure -width 640 -height 480 .c create image 0 0 -anchor nw -image cam_img .c create text [expr {$width / 2}] 25 -fill #FFFFFF -tags data -anchor n \ -font TkFixedFont bind .c <1> start_stop bind . <<ImageCapture>> {do_capture %x} proc do_capture {flag} { if {$flag} { borg camera greyimage cam_img if {![catch {dmtx::async_decode cam_img dec_done $::img_scale} err]} { old_img copy cam_img -compositingrule set } } } proc dec_done {flag time data} { if {$flag && ([borg camera state] eq "capture")} { borg camera stop cam_img copy old_img -compositingrule set set prdata $data regsub -all {[[:cntrl:]]} $prdata "\n" prdata .c itemconfigure data -text $prdata .c create rectangle {*}[.c bbox data] -fill #666666 -outline #FFFFFF \ -width 2 -tags databg .c lower databg data borg vibrate 100 borg beep } } proc start_stop {} { if {[borg camera state] ne "capture"} { borg camera start .c itemconfigure data -text "" .c delete databg } else { borg camera stop } } proc do_pause {} { borg camera stop dmtx::async_decode stop } borg camera start |
> > | 1 2 | package ifneeded dmtx 0.7.5 \ [list load libdmtx[info sharedlibextension] Dmtx] |
> > | 1 2 | Mike Laughton Mackenzie Straight |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | LOCAL_PATH := $(call my-dir) ########################## # # dmtx shared library # ########################## include $(CLEAR_VARS) LOCAL_MODULE := dmtx tcl_path := $(LOCAL_PATH)/../tcl include $(tcl_path)/tcl-config.mk tk_path := $(LOCAL_PATH)/../sdl2tk include $(tk_path)/tk-config.mk LOCAL_C_INCLUDES := $(tcl_includes) $(tk_includes) $(LOCAL_PATH) LOCAL_CFLAGS := $(tcl_cflags) $(tk_cflags) \ -DHAVE_SYS_TIME_H=1 \ -DHAVE_GETTIMEOFDAY=1 \ -DDMTX_VERSION="\"0.7.5\"" \ -O2 LOCAL_SRC_FILES := dmtx.c tcldmtx.c LOCAL_LDLIBS := LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := tcl tk LOCAL_EXPORT_C_INCLUDES += $(LOCAL_C_INCLUDES) include $(BUILD_SHARED_LIBRARY) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | Changes for libdmtx ----------------------------------------------------------------- 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | Bugs 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Copyright 2005-2011 Mike Laughton 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | # 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 -ansi 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 26-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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | ================================================================= 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 ----------------------------------------------------------------- Project website: www.libdmtx.org Documentation wiki: libdmtx.wikidot.com SourceForge.net page: www.sourceforge.net/projects/libdmtx OhLoh.net page: www.ohloh.net/projects/libdmtx Open mailing list: libdmtx-open_discussion@lists.sourceforge.net Professional support: www.dragonflylogic.com/products 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ================================================================= 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ================================================================= 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ================================================================= 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. Go to http://www.dragonflylogic.com/downloads for a list of all download options available on your system. 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ================================================================= 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | ================================================================= 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ================================================================= 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. Users may wish to evaluate the trial binaries available at: http://www.dragonflylogic.com/downloads 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | TODO ----------------------------------------------------------------- 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 |
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | #!/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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | AC_INIT([libdmtx], [0.7.5], [mike@dragonflylogic.com]) 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \file dmtx.c * \brief Main libdmtx source file */ #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <ctype.h> #include <limits.h> #include <float.h> #include <string.h> #include <errno.h> #include <assert.h> #include <math.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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 <time.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #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, /* 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 */ 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; 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; 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; 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 *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); /* 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /** * 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 <mike@dragonflylogic.com> * * \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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2008, 2009 Mike Laughton. All rights reserved. * Copyright 2009 Mackenzie Straight. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \file dmtxdecode.c * \brief Decode regions */ /** * \brief Initialize decode struct with default values * \param img * \return Initialized DmtxDecode struct */ 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->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 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 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) { 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; } /* maybe place remaining logic into new dmtxDecodePopulatedArray() function so other people can pass in their own arrays */ ModulePlacementEcc200(msg->array, msg->code, reg->sizeIdx, DmtxModuleOnRed | DmtxModuleOnGreen | DmtxModuleOnBlue); if(RsDecode(msg->code, reg->sizeIdx, fix) == DmtxFail) { dmtxMessageDestroy(&msg); return NULL; } 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); DecodeDataStream(msg, reg->sizeIdx, 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; #ifdef _VISUALC_ 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) { 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); /* 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; 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(mapCol = 0; mapCol < mapWidth; mapCol++) { rowTmp = (yRegionCount * mapHeight) + mapRow; rowTmp = yRegionTotal * mapHeight - rowTmp - 1; colTmp = (xRegionCount * mapWidth) + mapCol; idx = (rowTmp * xRegionTotal * mapWidth) + colTmp; if(tally[mapRow][mapCol]/(double)weightFactor >= 0.5) msg->array[idx] = DmtxModuleOnRGB; else msg->array[idx] = DmtxModuleOff; msg->array[idx] |= DmtxModuleAssigned; } } } } return DmtxPass; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \file dmtxdecodescheme.c */ /** * \brief Translate encoded data stream into final output * \param msg * \param sizeIdx * \param outputStart * \return void */ static void DecodeDataStream(DmtxMessage *msg, int sizeIdx, unsigned char *outputStart) { 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; } } /* Print macro trailer if required */ if(macro == DmtxTrue) PushOutputMacroTrailer(msg); } /** * \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 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 */ static unsigned char * DecodeSchemeAscii(DmtxMessage *msg, unsigned char *ptr, unsigned char *dataEnd) { int upperShift; int codeword, digits; upperShift = DmtxFalse; while(ptr < dataEnd) { codeword = (int)(*ptr); if(GetEncodationScheme(*ptr) != DmtxSchemeAscii) return ptr; else ptr++; if(upperShift == DmtxTrue) { PushOutputWord(msg, codeword + 127); 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 <= 128) { PushOutputWord(msg, codeword - 1); } else if(codeword <= 229) { digits = codeword - 130; PushOutputWord(msg, digits/10 + '0'); PushOutputWord(msg, digits - (digits/10)*10 + '0'); } } 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) { PushOutputC40TextWord(msg, &state, 0x1d); /* FNC1 -- XXX depends on position? */ } 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 */ 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) exit(40); /* XXX needs cleaner error handling */ while(ptr < ptrEnd) PushOutputWord(msg, UnRandomize255State(*(ptr++), idx++)); return ptr; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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; /* 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; /* 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; 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); 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 * height * (bitsPerPixel/8) + enc->rowPadBytes); 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 sizeIdx; /* Encode input string into data codewords */ switch(scheme) { case DmtxSchemeAutoBest: sizeIdx = EncodeOptimizeBest(input, output, sizeIdxRequest); break; case DmtxSchemeAutoFast: sizeIdx = DmtxUndefined; /* EncodeAutoFast(input, output, sizeIdxRequest, passFail); */ break; default: sizeIdx = EncodeSingleScheme(input, output, sizeIdxRequest, scheme); 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); 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]); } } } } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \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; 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(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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \file dmtxencodebase256.c * \brief Base 256 encoding rules */ /** * * */ static void EncodeNextChunkBase256(DmtxEncodeStream *stream) { DmtxByte value; if(StreamInputHasNext(stream)) { 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \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) { DmtxPassFail passFail; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); while(StreamInputHasNext(stream)) { inputValue = StreamInputAdvanceNext(stream); CHKERR; /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, stream->currentScheme, &passFail); 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); 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); 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) { assert(valueList->length <= 2); /* Handle extended ASCII with Upper Shift character */ if(inputValue > 127) { 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 */ 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \file dmtxencodeedifact.c * \brief Edifact encoding rules */ /** * * */ static void EncodeNextChunkEdifact(DmtxEncodeStream *stream) { DmtxByte value; if(StreamInputHasNext(stream)) { value = StreamInputAdvanceNext(stream); CHKERR; AppendValueEdifact(stream, value); CHKERR; } } /** * * */ static void AppendValueEdifact(DmtxEncodeStream *stream, DmtxByte value) { DmtxByte edifactValue, previousOutput; CHKSCHEME(DmtxSchemeEdifact); 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); } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \file dmtxencodeoptimize.c * \brief Logic for optimized (multiple scheme) encoding */ 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 }; /** temporary 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); } } */ /** * * */ static int EncodeOptimizeBest(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest) { 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])); } 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); c40ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeText, &passFail); textValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeX12, &passFail); x12ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); /* DumpStreams(streamsBest); */ } /* 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \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) { DmtxEncodeStream stream; stream = StreamInit(input, output); /* 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. All rights reserved. * * See LICENSE file in the main project directory for full * terms of use and distribution. * * Contact: Mike Laughton <mike@dragonflylogic.com> * * \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; 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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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: err = dmtxImageSetChannel(img, 0, 1); return NULL; /* unsupported packing order */ /* break; */ case DmtxPack8bppK: err = dmtxImageSetChannel(img, 0, 8); break; case DmtxPack16bppRGB: case DmtxPack16bppBGR: case DmtxPack16bppYCbCr: err = dmtxImageSetChannel(img, 0, 5); err = dmtxImageSetChannel(img, 5, 5); err = dmtxImageSetChannel(img, 10, 5); break; case DmtxPack24bppRGB: case DmtxPack24bppBGR: case DmtxPack24bppYCbCr: case DmtxPack32bppRGBX: case DmtxPack32bppBGRX: err = dmtxImageSetChannel(img, 0, 8); err = dmtxImageSetChannel(img, 8, 8); err = dmtxImageSetChannel(img, 16, 8); break; case DmtxPack16bppRGBX: case DmtxPack16bppBGRX: err = dmtxImageSetChannel(img, 0, 5); err = dmtxImageSetChannel(img, 5, 5); err = dmtxImageSetChannel(img, 10, 5); break; case DmtxPack16bppXRGB: case DmtxPack16bppXBGR: err = dmtxImageSetChannel(img, 1, 5); err = dmtxImageSetChannel(img, 6, 5); err = dmtxImageSetChannel(img, 11, 5); break; case DmtxPack32bppXRGB: case DmtxPack32bppXBGR: err = dmtxImageSetChannel(img, 8, 8); err = dmtxImageSetChannel(img, 16, 8); err = dmtxImageSetChannel(img, 24, 8); break; case DmtxPack32bppCMYK: err = dmtxImageSetChannel(img, 0, 8); err = dmtxImageSetChannel(img, 8, 8); err = dmtxImageSetChannel(img, 16, 8); err = 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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"); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 2011 Mike Laughton. 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: Mike Laughton <mike@dragonflylogic.com> * * \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, 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] = 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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 err; 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); err = dmtxDecodeGetPixelValue(dec, (int)(p.X + 0.5), (int)(p.Y + 0.5), colorPlane, &colorTmp); color += colorTmp; } 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(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; /* 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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 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 void 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); /* 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); 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); 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); 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \file dmtxsymbol.c * \brief Data Matrix symbol attributes */ /** * \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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \file dmtxtime.c * \brief Time handling */ #define DMTX_USEC_PER_SEC 1000000 #if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY) #include <sys/time.h> #include <time.h> #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 <Windows.h> #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 <time.h> #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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \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; } |
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | prefix=@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} |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | .\" 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 <dmtx.h>\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 <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <dmtx.h> int main(int argc, char *argv[]) { size_t width, height, bytesPerPixel; unsigned char str[] = "30Q324343430794<OQQ"; unsigned char *pxl; DmtxEncode *enc; DmtxImage *img; DmtxDecode *dec; DmtxRegion *reg; DmtxMessage *msg; fprintf(stdout, "input: \\"%s\\"\\n", str); /* 1) ENCODE a new Data Matrix barcode image (in memory only) */ enc = dmtxEncodeCreate(); assert(enc != NULL); dmtxEncodeDataMatrix(enc, strlen(str), str); /* 2) COPY the new image data before releasing encoding memory */ width = dmtxImageGetProp(enc->image, 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 Email bug reports to mike@dragonflylogic.com .SH AUTHOR Copyright (C) 2008, 2009 Mike Laughton .\" end of man page |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #!/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 |
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/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 |
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | #!/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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #!/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); |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/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="^ \* Contact: Mike Laughton <mike@dragonflylogic.com>\$" 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #!/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 |
> > > > > > | 1 2 3 4 5 6 | #!/bin/sh #splint -linelen 999 -Disgreater -Disless dmtx.c splint -linelen 999 dmtx.c exit $? |
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | #!/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 |
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | #!/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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #!/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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 | /* * tcldmtx.c -- * * This file contains the implementation of the "dmtx" Tcl * command. This command wraps the libdmtx library to scan * dot-matrix codes. * * Copyright (c) 2015 Christian Werner <chw@ch-werner.de> * * See the file "LICENSE" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include <tcl.h> #include <tk.h> #include <dmtx.h> #include <string.h> /* * Structure used for asynchronous decoding carried out by a * dedicated thread. */ typedef struct { int run; /* Controls thread loop */ Tcl_Mutex mutex; /* Lock for this struct */ Tcl_Condition cond; /* For waking up thread */ Tcl_ThreadId tid; /* Thread identifier */ Tcl_Interp *interp; /* Interpreter using DMTX decoder */ Tcl_AsyncHandler async; /* For signalling result */ unsigned char *pixPtr; /* Thread input: pixel array */ DmtxImage *imgPtr; /* Thread input: DMTX image struct */ int scale; /* Thread input: scale */ int maxTime; /* Thread input: time limit */ int time; /* Thread output: time consumed */ DmtxMessage *msgPtr; /* Thread output: result */ int nCmdObjs; /* Callback, number items */ Tcl_Obj **cmdObjs; /* Callback, items plus 3 for result */ } AsyncDecode; /* * Decoder thread, waits per condition for a decode request. * Reports the result back by an asynchronous event which * triggers an do-when-idle handler in the requesting thread. */ static Tcl_ThreadCreateType DmtxThread(ClientData clientData) { AsyncDecode *aPtr = (AsyncDecode *) clientData; DmtxDecode *decPtr; DmtxRegion *regPtr; DmtxMessage *msgPtr; DmtxTime timeout; Tcl_WideInt tw[2]; Tcl_MutexLock(&aPtr->mutex); while (aPtr->run) { Tcl_ConditionWait(&aPtr->cond, &aPtr->mutex, NULL); if (aPtr->imgPtr == NULL) { continue; } Tcl_MutexUnlock(&aPtr->mutex); timeout = dmtxTimeNow(); tw[0] = (Tcl_WideInt) timeout.sec * 1000 + timeout.usec / 1000; decPtr = dmtxDecodeCreate(aPtr->imgPtr, aPtr->scale); if (decPtr == NULL) { decErr: timeout = dmtxTimeNow(); tw[1] = (Tcl_WideInt) timeout.sec * 1000 + timeout.usec / 1000; Tcl_MutexLock(&aPtr->mutex); dmtxImageDestroy(&aPtr->imgPtr); Tcl_Free((char *) aPtr->pixPtr); aPtr->pixPtr = NULL; aPtr->time = tw[1] - tw[0]; if (aPtr->time < 0) { aPtr->time = -1; } Tcl_AsyncMark(aPtr->async); continue; } timeout = dmtxTimeAdd(timeout, aPtr->maxTime); regPtr = dmtxRegionFindNext(decPtr, &timeout); if (regPtr == NULL) { goto decErr; } msgPtr = dmtxDecodeMatrixRegion(decPtr, regPtr, DmtxUndefined); dmtxDecodeDestroy(&decPtr); timeout = dmtxTimeNow(); tw[1] = (Tcl_WideInt) timeout.sec * 1000 + timeout.usec / 1000; Tcl_MutexLock(&aPtr->mutex); dmtxImageDestroy(&aPtr->imgPtr); Tcl_Free((char *) aPtr->pixPtr); aPtr->pixPtr = NULL; aPtr->time = tw[1] - tw[0]; if (aPtr->time < 0) { aPtr->time = -1; } if (msgPtr != NULL) { aPtr->msgPtr = msgPtr; Tcl_AsyncMark(aPtr->async); } } Tcl_MutexUnlock(&aPtr->mutex); TCL_THREAD_CREATE_RETURN; } /* * Process asynchronous result callback. Function executes * as a do-when-idle handler. */ static void DmtxDecodeDone(ClientData clientData) { AsyncDecode *aPtr = (AsyncDecode *) clientData; int ret, i, nCmdObjs = 0; Tcl_Obj **cmdObjs; Tcl_Preserve(aPtr); Tcl_Preserve(aPtr->interp); Tcl_MutexLock(&aPtr->mutex); cmdObjs = aPtr->cmdObjs; if (cmdObjs != NULL) { nCmdObjs = aPtr->nCmdObjs; if ((aPtr->msgPtr == NULL) || (aPtr->msgPtr->outputIdx <= 0)) { cmdObjs[nCmdObjs] = Tcl_NewIntObj(0); cmdObjs[nCmdObjs + 1] = Tcl_NewIntObj(aPtr->time); cmdObjs[nCmdObjs + 2] = Tcl_NewObj(); } else { cmdObjs[nCmdObjs] = Tcl_NewIntObj(1); cmdObjs[nCmdObjs + 1] = Tcl_NewIntObj(aPtr->time); cmdObjs[nCmdObjs + 2] = Tcl_NewByteArrayObj(aPtr->msgPtr->output, aPtr->msgPtr->outputIdx); } } aPtr->nCmdObjs = 0; aPtr->cmdObjs = NULL; if (aPtr->msgPtr != NULL) { dmtxMessageDestroy(&aPtr->msgPtr); } Tcl_MutexUnlock(&aPtr->mutex); if (cmdObjs != NULL) { Tcl_IncrRefCount(cmdObjs[nCmdObjs]); Tcl_IncrRefCount(cmdObjs[nCmdObjs + 1]); Tcl_IncrRefCount(cmdObjs[nCmdObjs + 2]); ret = Tcl_EvalObjv(aPtr->interp, nCmdObjs + 3, cmdObjs, TCL_GLOBAL_ONLY); for (i = 0; i < nCmdObjs + 3; i++) { if (cmdObjs[i] != NULL) { Tcl_DecrRefCount(cmdObjs[i]); cmdObjs[i] = NULL; } } Tcl_Free((char *) cmdObjs); if (ret == TCL_ERROR) { Tcl_AddErrorInfo(aPtr->interp, "\n (dmtx event handler)"); Tcl_BackgroundException(aPtr->interp, ret); } } Tcl_Release(aPtr->interp); Tcl_Release(aPtr); } /* * Function triggered by asynchronous event from decoder thread * dispatching do-when-idle handler to invoke Tcl callback. */ static int DmtxDecodeHandler(ClientData clientData, Tcl_Interp *interp, int code) { Tcl_DoWhenIdle(DmtxDecodeDone, clientData); return code; } /* * Stop the decoder thread, if any. */ static void DmtxAsyncStop(AsyncDecode *aPtr) { int i; Tcl_CancelIdleCall(DmtxDecodeDone, (ClientData) aPtr); Tcl_MutexLock(&aPtr->mutex); if (aPtr->msgPtr != NULL) { dmtxMessageDestroy(&aPtr->msgPtr); } if (aPtr->run) { int dummy; aPtr->run = 0; Tcl_ConditionNotify(&aPtr->cond); Tcl_MutexUnlock(&aPtr->mutex); Tcl_JoinThread(aPtr->tid, &dummy); aPtr->tid = NULL; Tcl_MutexLock(&aPtr->mutex); } Tcl_MutexUnlock(&aPtr->mutex); Tcl_ConditionFinalize(&aPtr->cond); Tcl_MutexFinalize(&aPtr->mutex); if (aPtr->async != NULL) { Tcl_AsyncDelete(aPtr->async); aPtr->async = NULL; } if (aPtr->cmdObjs != NULL) { for (i = 0; i < aPtr->nCmdObjs + 3; i++) { if (aPtr->cmdObjs[i] != NULL) { Tcl_DecrRefCount(aPtr->cmdObjs[i]); aPtr->cmdObjs[i] = NULL; } } Tcl_Free((char *) aPtr->cmdObjs); } aPtr->nCmdObjs = 0; aPtr->cmdObjs = NULL; } /* * Check/start the decoder thread. Error cases: * - thread could not be started * - thread is already started but still processing a request */ static int DmtxAsyncStart(Tcl_Interp *interp, AsyncDecode *aPtr) { int success = 0; Tcl_MutexLock(&aPtr->mutex); if (!aPtr->run) { if (Tcl_CreateThread(&aPtr->tid, DmtxThread, (ClientData) aPtr, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) == TCL_OK) { aPtr->async = Tcl_AsyncCreate(DmtxDecodeHandler, (ClientData) aPtr); aPtr->interp = interp; aPtr->run = success = 1; } } else if ((aPtr->pixPtr != NULL) || (aPtr->imgPtr != NULL) || (aPtr->cmdObjs != NULL) || (aPtr->msgPtr != NULL)) { success = -1; } else { success = 1; } Tcl_MutexUnlock(&aPtr->mutex); if (success < 0) { Tcl_SetResult(interp, "decode process still running", TCL_STATIC); return TCL_ERROR; } if (success == 0) { Tcl_SetResult(interp, "decode process not started", TCL_STATIC); return TCL_ERROR; } return TCL_OK; } /* * Free dmtx::async_decode command client data structure. */ static void DmtxAsyncFree(char *clientData) { AsyncDecode *aPtr = (AsyncDecode *) clientData; DmtxAsyncStop(aPtr); Tcl_Free((char *) aPtr); } /* * Callback for deletion of dmtx::async_decode command. */ static void DmtxAsyncCmdDeleted(ClientData clientData) { Tcl_EventuallyFree(clientData, DmtxAsyncFree); } /* * dmtx::async_decode Tcl command, asynchronous decoding. * * Command formats/arguments are * * stop decoder thread * * dmtx::async_decode stop * * start decoding an image * * dmtx::async_decode photo callback ?scale timeout? * * photo photo image name * callback procedure to invoke on end of decoding request * with these arguments * * flag 1 when dot matrix code decoded, else 0 * time decode/processing time in milliseconds * data decoded dot matrix code as byte array, * if decode failed, this is empty * * scale optional: scale down image for dmtx library, * default is 1 * timeout optional: maximum decode time in milliseconds, * default is 1000 ms */ static int DmtxAsyncDecodeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { AsyncDecode *aPtr = (AsyncDecode *) clientData; Tk_PhotoHandle handle; Tk_PhotoImageBlock block; int scale = 1, bpp, x, y, i, millis = 1000, nCmdObjs; unsigned char *pixPtr; DmtxImage *imgPtr; Tcl_Obj **cmdObjs; if (objc > 5) { Tcl_WrongNumArgs(interp, 1, objv, "stop|photo ?callback? ?scale timeout?"); return TCL_ERROR; } if ((objc == 2) && (strcmp(Tcl_GetString(objv[1]), "stop") == 0)) { DmtxAsyncStop(aPtr); return TCL_OK; } if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "photo callback ?scale timeout?"); return TCL_ERROR; } if ((objc > 3) && (Tcl_GetIntFromObj(interp, objv[3], &scale) != TCL_OK)) { return TCL_ERROR; } if (scale < 1) { scale = 1; } else if (scale > 32) { scale = 32; } if ((objc > 4) && (Tcl_GetIntFromObj(interp, objv[4], &millis) != TCL_OK)) { return TCL_ERROR; } if (millis <= 0) { millis = 1000; } handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1])); if (handle == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found", Tcl_GetString(objv[1]))); return TCL_ERROR; } if (Tk_PhotoGetImage(handle, &block) != 1) { Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC); return TCL_ERROR; } if (block.pixelSize == 1) { bpp = 1; } else if ((block.pixelSize == 3) || (block.pixelSize == 4)) { bpp = 3; } else { Tcl_SetResult(interp, "unsupported photo image depth", TCL_STATIC); return TCL_ERROR; } if (DmtxAsyncStart(interp, aPtr) != TCL_OK) { return TCL_ERROR; } pixPtr = (unsigned char *) Tcl_AttemptAlloc(block.width * block.height * bpp); if (pixPtr == NULL) { Tcl_SetResult(interp, "out of memory", TCL_STATIC); return TCL_ERROR; } for (y = 0; y < block.height; y++) { unsigned char *srcPtr, *dstPtr; srcPtr = block.pixelPtr + y * block.pitch; dstPtr = pixPtr + y * block.width * bpp; for (x = 0; x < block.width; x++) { dstPtr[0] = srcPtr[block.offset[0]]; if (bpp > 1) { dstPtr[1] = srcPtr[block.offset[1]]; dstPtr[2] = srcPtr[block.offset[2]]; } dstPtr += bpp; srcPtr += block.pixelSize; } } imgPtr = dmtxImageCreate(pixPtr, block.width, block.height, (bpp > 1) ? DmtxPack24bppRGB : DmtxPack8bppK); if (imgPtr == NULL) { Tcl_Free((char *) pixPtr); Tcl_SetResult(interp, "error creating internal image", TCL_STATIC); return TCL_ERROR; } if (Tcl_ListObjGetElements(interp, objv[2], &nCmdObjs, &cmdObjs) != TCL_OK) { dmtxImageDestroy(&imgPtr); Tcl_Free((char *) pixPtr); return TCL_ERROR; } if (nCmdObjs <= 0) { Tcl_SetResult(interp, "empty callback", TCL_STATIC); dmtxImageDestroy(&imgPtr); Tcl_Free((char *) pixPtr); return TCL_ERROR; } Tcl_MutexLock(&aPtr->mutex); aPtr->pixPtr = pixPtr; aPtr->imgPtr = imgPtr; aPtr->scale = scale; aPtr->maxTime = millis; aPtr->nCmdObjs = nCmdObjs; aPtr->cmdObjs = (Tcl_Obj **) Tcl_Alloc((nCmdObjs + 3) * sizeof (Tcl_Obj *)); for (i = 0; i < nCmdObjs; i++) { aPtr->cmdObjs[i] = cmdObjs[i]; Tcl_IncrRefCount(aPtr->cmdObjs[i]); } aPtr->cmdObjs[i++] = NULL; aPtr->cmdObjs[i++] = NULL; aPtr->cmdObjs[i++] = NULL; Tcl_ConditionNotify(&aPtr->cond); Tcl_MutexUnlock(&aPtr->mutex); return TCL_OK; } /* * dmtx::decode Tcl command, synchronous decoding. * * Command arguments are * * dmtx::decode photo ?scale timeout? * * photo photo image name * scale optional: scale down image for dmtx library, * default is 1 * timeout optional: maximum decode time in milliseconds, * default is unlimited * * Result is a three element list made up of * * flag 1 when dot matrix code decoded, else 0 * time decode/processing time in milliseconds * data decoded dot matrix code as byte array * if decode failed, this is empty */ static int DmtxDecodeObjCmd(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tk_PhotoHandle handle; Tk_PhotoImageBlock block; int scale = 1, bpp, x, y; unsigned char *pixPtr; DmtxImage *imgPtr; DmtxDecode *decPtr; DmtxRegion *regPtr; DmtxTime timeout, now, *timeoutPtr = NULL; Tcl_WideInt tw[2]; Tcl_Obj *list[3]; if ((objc < 2) || (objc > 4)) { Tcl_WrongNumArgs(interp, 1, objv, "photo ?scale timeout?"); return TCL_ERROR; } if ((objc > 2) && (Tcl_GetIntFromObj(interp, objv[2], &scale) != TCL_OK)) { return TCL_ERROR; } if (scale < 1) { scale = 1; } else if (scale > 32) { scale = 32; } now = dmtxTimeNow(); tw[0] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000; if (objc > 3) { int millis; if (Tcl_GetIntFromObj(interp, objv[3], &millis) != TCL_OK) { return TCL_ERROR; } if (millis > 0) { timeout = now; timeout = dmtxTimeAdd(timeout, millis); timeoutPtr = &timeout; } } handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1])); if (handle == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found", Tcl_GetString(objv[1]))); return TCL_ERROR; } if (Tk_PhotoGetImage(handle, &block) != 1) { Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC); return TCL_ERROR; } if (block.pixelSize == 1) { bpp = 1; } else if ((block.pixelSize == 3) || (block.pixelSize == 4)) { bpp = 3; } else { Tcl_SetResult(interp, "unsupported photo image depth", TCL_STATIC); return TCL_ERROR; } pixPtr = (unsigned char *) Tcl_AttemptAlloc(block.width * block.height * bpp); if (pixPtr == NULL) { Tcl_SetResult(interp, "out of memory", TCL_STATIC); return TCL_ERROR; } for (y = 0; y < block.height; y++) { unsigned char *srcPtr, *dstPtr; srcPtr = block.pixelPtr + y * block.pitch; dstPtr = pixPtr + y * block.width * bpp; for (x = 0; x < block.width; x++) { dstPtr[0] = srcPtr[block.offset[0]]; if (bpp > 1) { dstPtr[1] = srcPtr[block.offset[1]]; dstPtr[2] = srcPtr[block.offset[2]]; } dstPtr += bpp; srcPtr += block.pixelSize; } } imgPtr = dmtxImageCreate(pixPtr, block.width, block.height, (bpp > 1) ? DmtxPack24bppRGB : DmtxPack8bppK); if (imgPtr == NULL) { Tcl_Free((char *) pixPtr); Tcl_SetResult(interp, "error creating internal image", TCL_STATIC); return TCL_ERROR; } decPtr = dmtxDecodeCreate(imgPtr, scale); if (decPtr == NULL) { dmtxImageDestroy(&imgPtr); Tcl_Free((char *) pixPtr); Tcl_SetResult(interp, "error creating decoder", TCL_STATIC); return TCL_ERROR; } regPtr = dmtxRegionFindNext(decPtr, timeoutPtr); if (regPtr != NULL) { DmtxMessage *msgPtr; msgPtr = dmtxDecodeMatrixRegion(decPtr, regPtr, DmtxUndefined); now = dmtxTimeNow(); tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000; if ((msgPtr != NULL) && (msgPtr->outputIdx > 0)) { list[0] = Tcl_NewIntObj(1); list[2] = Tcl_NewByteArrayObj(msgPtr->output, msgPtr->outputIdx); } else { list[0] = Tcl_NewIntObj(0); list[2] = Tcl_NewObj(); } if (msgPtr != NULL) { dmtxMessageDestroy(&msgPtr); } dmtxRegionDestroy(®Ptr); } else { now = dmtxTimeNow(); tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000; list[0] = Tcl_NewIntObj(0); list[2] = Tcl_NewObj(); } x = tw[1] - tw[0]; if (x < 0) { x = -1; } list[1] = Tcl_NewIntObj(x); Tcl_SetObjResult(interp, Tcl_NewListObj(3, list)); dmtxDecodeDestroy(&decPtr); dmtxImageDestroy(&imgPtr); Tcl_Free((char *) pixPtr); return TCL_OK; } /* * Module initializer. */ int Dmtx_Init(Tcl_Interp *interp) { AsyncDecode *aPtr; #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, "8.4", 0) == NULL) #else if (Tcl_PkgRequire(interp, "Tcl", "8.4", 0) == NULL) #endif { return TCL_ERROR; } #ifdef USE_TK_STUBS if (Tk_InitStubs(interp, "8.4", 0) == NULL) #else if (Tcl_PkgRequire(interp, "Tk", "8.4", 0) == NULL) #endif { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "dmtx::decode", DmtxDecodeObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); aPtr = (AsyncDecode *) Tcl_Alloc(sizeof (AsyncDecode)); memset(aPtr, 0, sizeof (AsyncDecode)); Tcl_CreateObjCommand(interp, "dmtx::async_decode", DmtxAsyncDecodeObjCmd, (ClientData) aPtr, DmtxAsyncCmdDeleted); Tcl_PkgProvide(interp, "dmtx", DMTX_VERSION); return TCL_OK; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * tab-width: 8 * End: */ |
> > | 1 2 | SUBDIRS = simple_test #SUBDIRS = multi_test rotate_test simple_test unit_test |
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | test: @./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 |
> > > | 1 2 3 | o 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) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #!/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 |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #!/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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/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 |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> | 1 | abcdefghijklmnopqrsABCDEFGHIJKLMNOPQRS |
> | 1 | abcdefghijklmABCDEFGHIJKLM0123456789 |
> > | 1 2 | libdmtx 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. |
> > > | 1 2 3 | This test case contains newline charactes, including in the middle and at the end of the message. |
> | 1 | 123456 |
> | 1 | 123456789012345678901234567890123456789012345678901234567890 |
> | 1 | 30Q324343430794<OQQ |
> | 1 | between subtle shading and the absence of light |
> | 1 | as the raindrops penetrate the silence all around |
> | 1 | bathroom mirror casts confusing looks from me to me |
> | 1 | AND WHEN I SQUINTED THE WORLD SEEMED ROSE TINTED |
> | 1 | http://www.libdmtx.org |
> | 1 | and when i squinted the WORLD SEEMED ROSE TINTED |
> | 1 | 123456789 |
> | 1 | A1B2C3D4E5F6G7H8I9J0K1L2 |
> | 1 | 1234567 |
> | 1 | 12345678 |
> | 1 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus vitae diam ac ante tristique faucibus. In ac velit. Pellentesque enim ligula, accumsan vitae, convallis non, molestie quis, neque. Nullam in lacus. Praesent convallis. Suspendisse ut est non augue sollicitudin semper. Donec dui arcu, blandit ac, fermentum nec, tempor non, nibh. Nunc ut purus vel lorem dignissim consequat. Donec felis. Suspendisse vel dolor ac lacus ullamcorper nonummy. In hac habitasse platea dictumst. Ut dolor nunc, tincidunt id, cursus a, vehicula adipiscing, urna. Integer eget enim eu nunc elementum sollicitudin. Etiam sodales condimentum nulla. Nam blandit molestie felis. Ut faucibus sollicitudin sem. |
> | 1 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus vitae diam ac ante tristique faucibus. In ac velit. Pellentesque enim ligula, accumsan vitae, convallis non, molestie quis, neque. Nullam in lacus. Praesent convallis. Suspendisse ut est non augue sollicitudin semper. Donec dui arcu, blandit ac, fermentum nec, tempor non, nibh. Nunc ut purus vel lorem dignissim consequat. Donec felis. Suspendisse vel dolor ac lacus ullamcorper nonummy. In hac habitasse platea dictumst. Ut dolor nunc, tincidunt id, cursus a, vehicula adipiscing, urna. Integer eget enim eu nunc elementum sollicitudin. Etiam sodales condimentum nulla. Nam blandit molestie felis. |
> | 1 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus vitae diam ac ante tristique faucibus. In ac velit. Pellentesque enim ligula, accumsan vitae, convallis non, molestie quis, neque. Nullam in lacus. Praesent convallis. Suspendisse ut est non augue sollicitudin semper. Donec dui arcu, blandit ac, fermentum nec, tempor non, nibh. Nunc ut purus vel lorem dignissim consequat. Donec felis. Suspendisse vel dolor ac lacus ullamcorper nonummy. In hac habitasse platea dictumst. Ut dolor nunc, tincidunt id, cursus a, vehicula adipiscing, urna. Integer eget enim eu nunc elementum sollicitudin. |
> | 1 | HTTP://WWW.LIBDMTX.ORG |
> | 1 | http://www.mungewell.org/simon.asc |
> | 1 | ABC123!@#$abcdefghijkl |
> | 1 | ABCÈEFG |
> | 1 | Some sample text with an umläut in the middle |
> | 1 | דיה ×דומה |
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Milvus milvus Red Kite Rotmilan Milan royal Milano Real Luňák Äervený Rød Glente Rode Wouw Isohaarahaukka Aquila rossa Nibbio reale Glente Glada Vörös kanya КраÑный коршун Kania ruda Haja Äervená SarkanÄ klija ΨαλιδιάÏης Milhafre-real Руда шуліка Kızıl Çaylak アカトビ דיה ×דומה |
> | 1 | アカトビアカトビアカトビアカトビ |
> | 1 | アカトビアカトビアカトビアカト |
> | 1 | アカトビアカトビアカトビアカ |
> | 1 | 1234567890 |
> | 1 | 12345678901 |
> | 1 | 123456789012 |
> | 1 | 1234567890123 |
> | 1 | 12345678901234 |
> | 1 | 123456789012345 |
> | 1 | 1234567890123456 |
> | 1 | 12345678901234567 |
> | 1 | 123456789012345678 |
> | 1 | 1234567890123456789 |
> | 1 | Lorem ipsum dolor si |
> | 1 | Lorem ipsum dolor sit |
> | 1 | Lorem ipsum dolor sit |
> | 1 | Lorem ipsum dolor sit a |
> | 1 | Lorem ipsum dolor sit am |
> | 1 | Lorem ipsum dolor sit ame |
> | 1 | Lorem ipsum dolor sit amet |
> | 1 | Lorem ipsum dolor sit amet, |
> | 1 | Lorem ipsum dolor sit amet, |
> | 1 | Lorem ipsum dolor sit amet, c |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> | 1 | AB |
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | AM_CPPFLAGS = -Wshadow -Wall -pedantic -ansi 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* 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 <limits.h> #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 <alloca.h> #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 |
> | 1 | #include "../../dmtx.c" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxaccel.c */ #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <assert.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxdecode2.c */ #include <math.h> #include <assert.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxhough.c */ #include <string.h> #include <assert.h> #include <math.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxregion2.c */ #include <string.h> #include <math.h> #include <assert.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxsobel.c */ #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <assert.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | /** * 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 <mike@dragonflylogic.com> * * \file dmtxvaluegrid.c */ #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <assert.h> #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]; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | /* 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; u<m; ++u ) { C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5); scratch[0] = *Fout0; C_MUL(scratch[1] ,*Fout1, tw[u*fstride]); C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]); C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]); C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]); C_ADD( scratch[7],scratch[1],scratch[4]); C_SUB( scratch[10],scratch[1],scratch[4]); C_ADD( scratch[8],scratch[2],scratch[3]); C_SUB( scratch[9],scratch[2],scratch[3]); Fout0->r += 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<m; ++u ) { k=u; for ( q1=0 ; q1<p ; ++q1 ) { scratch[q1] = Fout[ k ]; C_FIXDIV(scratch[q1],p); k += m; } k=u; for ( q1=0 ; q1<p ; ++q1 ) { int twidx=0; Fout[ k ] = scratch[0]; for (q=1;q<p;++q ) { twidx += fstride * k; if (twidx>=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<p;++k) kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st); /* // all threads have joined by this point */ switch (p) { case 2: kf_bfly2(Fout,fstride,st,m); break; case 3: kf_bfly3(Fout,fstride,st,m); break; case 4: kf_bfly4(Fout,fstride,st,m); break; case 5: kf_bfly5(Fout,fstride,st,m); break; default: kf_bfly_generic(Fout,fstride,st,m,p); break; } return; } #endif if (m==1) { do{ *Fout = *f; f += fstride*in_stride; }while(++Fout != Fout_end ); }else{ do{ /* // recursive call: // DFT of size m*p performed by doing // p instances of smaller DFTs of size m, // each one takes a decimated version of the input */ kf_work( Fout , f, fstride*p, in_stride, factors,st); f += fstride*in_stride; }while( (Fout += m) != Fout_end ); } Fout=Fout_beg; /* // recombine the p smaller DFTs */ switch (p) { case 2: kf_bfly2(Fout,fstride,st,m); break; case 3: kf_bfly3(Fout,fstride,st,m); break; case 4: kf_bfly4(Fout,fstride,st,m); break; case 5: kf_bfly5(Fout,fstride,st,m); break; default: kf_bfly_generic(Fout,fstride,st,m,p); break; } } /* facbuf is populated by p1,m1,p2,m2, ... where p[i] * m[i] = m[i-1] m0 = n */ static void kf_factor(int n,int * facbuf) { int p=4; double floor_sqrt; floor_sqrt = floor( sqrt((double)n) ); /*factor out powers of 4, powers of 2, then any remaining primes */ do { while (n % p) { switch (p) { case 4: p = 2; break; case 2: p = 3; break; default: p += 2; break; } if (p > 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;i<nfft;++i) { const double pi=3.141592653589793238462643383279502884197169399375105820974944; double phase = -2*pi*i / nfft; if (st->inverse) 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | #ifndef KISS_FFT_H #define KISS_FFT_H #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h> #include <malloc.h> #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 <xmmintrin.h> # 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 <sys/types.h> # 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | /* 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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | /** * 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 <mike@dragonflylogic.com> * * \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 <stdlib.h> #include <stdio.h> #include <assert.h> #include <SDL/SDL.h> #include <SDL/SDL_image.h> #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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | /** * 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 <mike@dragonflylogic.com> * * \file multi_test.h */ #include <SDL/SDL.h> #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; |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | /** * 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 <mike@dragonflylogic.com> * * \file visualize.c */ #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <SDL/SDL_gfxPrimitives.h> #include <SDL/SDL_rotozoom.h> #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); } |
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | AM_CPPFLAGS = -Wshadow -Wall -pedantic -ansi 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 |
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | This 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. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | /** * 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 <mike@dragonflylogic.com> * * \file callback.c */ #include <stdlib.h> #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); } } } |
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * 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 <mike@dragonflylogic.com> * * \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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | /** * 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 <mike@dragonflylogic.com> * * \file display.c */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /** * 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 <mike@dragonflylogic.com> * * \file display.h */ #include <GL/gl.h> #include <GL/glu.h> #include <SDL/SDL.h> 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); |
> > > > > > > | 1 2 3 4 5 6 7 | #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" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | /** * 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 <mike@dragonflylogic.com> * * \file image.c */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #include <png.h> #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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /** * 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 <mike@dragonflylogic.com> * * \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 |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | /** * 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 <mike@dragonflylogic.com> * * \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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /** * 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 <mike@dragonflylogic.com> * * \file rotate_test.h */ #ifndef __SCANDEMO_H__ #define __SCANDEMO_H__ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #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 |
> > > > > > > > | 1 2 3 4 5 6 7 8 | AM_CPPFLAGS = -Wshadow -Wall -pedantic -ansi check_PROGRAMS = simple_test simple_test_SOURCES = simple_test.c simple_test_LDFLAGS = -lm LDADD = ../../libdmtx.la |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | /** * libdmtx - Data Matrix Encoding/Decoding Library * Copyright 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 <mike@dragonflylogic.com> * * \file simple_test.c */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <dmtx.h> int main(int argc, char *argv[]) { size_t width, height, bytesPerPixel; unsigned char str[] = "30Q324343430794<OQQ"; unsigned char *pxl; DmtxEncode *enc; DmtxImage *img; DmtxDecode *dec; DmtxRegion *reg; DmtxMessage *msg; fprintf(stdout, "input: \"%s\"\n", str); /* 1) ENCODE a new Data Matrix barcode image (in memory only) */ enc = dmtxEncodeCreate(); assert(enc != NULL); dmtxEncodeDataMatrix(enc, strlen((const char *)str), str); /* 2) COPY the new image data before releasing encoding memory */ width = dmtxImageGetProp(enc->image, 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); } |
> > > > > > > > | 1 2 3 4 5 6 7 8 | AM_CPPFLAGS = -Wshadow -Wall -pedantic -ansi 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /** * 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 <mike@dragonflylogic.com> * * \file unit_test.c */ #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <stdlib.h> #include <stdio.h> #include <string.h> #include "../../dmtx.h" #include "../../util/common/dmtxutil.h" char *programName; 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); } */ |
︙ | ︙ |
︙ | ︙ |