From bdd6066fc353289e78cb510c8f330d4328de5a12 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 22 Jan 2016 19:03:06 +0000 Subject: [PATCH] Partial zxing package This is a small portion of zxing, including only what's required to generate QR codes. Pulled from https://github.com/zxing/zxing on Jan. 4, 2016, rev 4e3abafe3008e02695f894eccf05f8257fca4ee9 dated Dec. 9, 2015. Contains only the files we need. Unmodified. License: Apache 2.0 --- apps/imagegen/zxing/AUTHORS | 134 ++++ apps/imagegen/zxing/CHANGES | 357 +++++++++++ apps/imagegen/zxing/LICENSE | 245 ++++++++ apps/imagegen/zxing/README.md | 83 +++ .../java/com/google/zxing/BarcodeFormat.java | 77 +++ .../com/google/zxing/ChecksumException.java | 47 ++ .../java/com/google/zxing/EncodeHintType.java | 98 +++ .../com/google/zxing/FormatException.java | 47 ++ .../com/google/zxing/ReaderException.java | 49 ++ .../main/java/com/google/zxing/Writer.java | 59 ++ .../com/google/zxing/WriterException.java | 38 ++ .../com/google/zxing/common/BitArray.java | 375 ++++++++++++ .../com/google/zxing/common/BitMatrix.java | 449 ++++++++++++++ .../google/zxing/common/CharacterSetECI.java | 118 ++++ .../zxing/common/reedsolomon/GenericGF.java | 166 +++++ .../common/reedsolomon/GenericGFPoly.java | 264 ++++++++ .../reedsolomon/ReedSolomonEncoder.java | 74 +++ .../com/google/zxing/qrcode/QRCodeWriter.java | 118 ++++ .../qrcode/decoder/ErrorCorrectionLevel.java | 60 ++ .../qrcode/decoder/FormatInformation.java | 172 ++++++ .../com/google/zxing/qrcode/decoder/Mode.java | 102 ++++ .../google/zxing/qrcode/decoder/Version.java | 578 ++++++++++++++++++ .../zxing/qrcode/encoder/BlockPair.java | 37 ++ .../zxing/qrcode/encoder/ByteMatrix.java | 98 +++ .../google/zxing/qrcode/encoder/Encoder.java | 578 ++++++++++++++++++ .../google/zxing/qrcode/encoder/MaskUtil.java | 217 +++++++ .../zxing/qrcode/encoder/MatrixUtil.java | 485 +++++++++++++++ .../google/zxing/qrcode/encoder/QRCode.java | 108 ++++ .../client/j2se/MatrixToImageConfig.java | 74 +++ .../client/j2se/MatrixToImageWriter.java | 162 +++++ 30 files changed, 5469 insertions(+) create mode 100644 apps/imagegen/zxing/AUTHORS create mode 100644 apps/imagegen/zxing/CHANGES create mode 100644 apps/imagegen/zxing/LICENSE create mode 100644 apps/imagegen/zxing/README.md create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java create mode 100644 apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java create mode 100644 apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java diff --git a/apps/imagegen/zxing/AUTHORS b/apps/imagegen/zxing/AUTHORS new file mode 100644 index 0000000000..d6aeafeef2 --- /dev/null +++ b/apps/imagegen/zxing/AUTHORS @@ -0,0 +1,134 @@ +This project consists of contributions from several people, recognized here for convenience, +in alphabetical order. + +AgustÃn Delgado (Servinform S.A.) +Aitor Almeida (University of Deusto) +Alasdair Mackintosh (Google) +Alex Dupre +Alexander Martin (Haase & Martin GmbH) +Alexander Schmidt +Anders Borg +Andreas Pillath +Andrew Walbran (Google) +Andrey Sitnik +Androida.hu / http://www.androida.hu/ +Antonio Manuel Benjumea (Servinform S.A.) +Asier Iturralde +Asmuri Anwar +atereshchuk +Betaminos +Brian Brown (Google) +Bruce Allen +Chang Hyun Park +Christian Brunschen (Google) +Christoph Schulz (creatale GmbH) +crowdin.net +Daniel Switkin (Google) +Dave MacLachlan (Google) +David Phillip Oster (Google) +David Albert (Bug Labs) +David Olivier +David Walker (Google) +Diego Pierotto +Dion Hardy +drejc83 +Eduardo Castillejo (University of Deusto) +Emanuele Aina +Eric Kobrin (Velocitude) +evansepdx +Erik Barbara +Facundo Farias +fakhri626 +fics.danmark +Francois B. (Google) +Frank Yellin +Fred Lin (Anobiit) +gcstang +Guenther Grau +Guillaume Cottenceau +Guillaume Le Biller +Hannes Erven +Hartmut Neubauer (Schweers Informationstechnologie GmbH) +hosigumayuugi +hypest (Barcorama project) +Ian W. Davis +Isaac Potoczny-Jones +Ivan Poliakov +Jacob Haynes (Google) +Jeff Breidenbach (Google) +jjYBdx4IL +Joan Montané (Softcatalà .cat) +John Connolly (Bug Labs) +Jonas Petersson (Prisjakt) +Joseph Wain (Google) +Juha Kuitunen +Juho Mikkonen +Jukka Nurmi +jwicks +Kamil Kaczmarczyk +Kazuki Nishiura +Kevin O'Sullivan (SITA) +Kevin Xue (NetDragon Websoft Inc., China) +Lachezar Dobrev +Loránt Gerencsér +Luiz Silva +Luka Finžgar +Åukasz Stefaniak +Malte Starostik +Manuel Kasten +Marcelo +Marcin Górny +Markus Helm +Mateusz JÄ™drasik +Matthew Schulkind (Google) +Matt York (LifeMarks) +MicheleMas +mike32767 +mikej06 / micjahn +Mike Kicinski +Mohamad Fairol +Morgan Courbet +Nikolaos Ftylitakis +Nikolaus Huber +Olexandr Nesterenko +Olli +Pablo Orduña (University of Deusto) +Paul Hackenberger +perennialmind +Radu Silaghi +Ralf Kistner +Randy Shen (Acer) +Rasmus Schrøder Sørensen +Richard HÅ™ivňák +Romain Pechayre +Roman Nurik (Google) +Rustam Abdullaev +Ryan Alford +Sanford Squires +Satoshi Kametaya +Shachar Shemesh +Sean Owen (Google) +Shiyuan Guo / éƒä¸–å…ƒ +ShumovichY +Simon Flannery (Ericsson) +slepmog +Stephen Furlani +Steven Parkes +stoty74 +Suraj Supekar +Sven Klinkhamer +taku0 +techinstantdx +tgibson11 +Thanapon Namjittrong +Thomas Gerbet +Thomas Weißschuh +Tim Gernat +v.anestis +Vince Francis (LifeMarks) +Wolfgang Jung +Yahoe001 +Yakov Okshtein (Google) +Yao Wei +Younes CHTIOUI +Yunhao Tian diff --git a/apps/imagegen/zxing/CHANGES b/apps/imagegen/zxing/CHANGES new file mode 100644 index 0000000000..0fe754a4cc --- /dev/null +++ b/apps/imagegen/zxing/CHANGES @@ -0,0 +1,357 @@ +0.1 (09 Nov 2007) +- Initial release + +0.1.1 (11 Nov 2007) +- Fixed bug decoding version 0 QR Codes +- Now default zoom is 2.5x in Java ME client + +0.1.2 (28 Nov 2007) +- Issue 11 fixed: Build problems on Windows +- Can now build "ZXingReaderBasic" which does not require JSR-234 +- Issue 14 fixed: release .zip builds into one directory + +0.1.3 (7 Dec 2007) +- Unit test for QR Code decoding +- Added EAN-13 support +- Now builds with class file format version 1.2 -- may solve some compatibility issues? +- Fixed obfuscation step bug causing NoClassDefFoundError + +0.1.4 (13 Dec 2007) +- Added Blackberry client build script -- does not yet work +- Big change to handling of content of barcodes: + - com.google.zxing.client.result moved from core-ext to core + - Rewritten for J2ME + - core-ext removed + - J2ME client now uses this code for better parsing/handling of results + +0.2 (07 Jan 2008) + - Few small bug fixes in AlignmentPatternFinder, MultiFormatReader + (thank K. Kakima, Andreas) + - LCDUIImageMonochromeBitmapSource now public + - Notably improved decoding for Basic version -- Regular version still + your best bet if you can run it though + +0.2.1 (16 Jan 2008) + - Slight tweaks and enhancements to decode and runtime performance of UPCDecoder + - Added black-box test suites against a known set of images + (Thanks to Enrique G. S. for additional images) + +0.2.2 (22 Jan 2008) + - Now includes junit locally to build tests + - Fixed "MIDlet" name in MANIFEST.MF, which may solve some problems + - Friendlier error message when barcode can't be found + +0.3 (04 Feb 2008) + - Major refactoring of 1D barcode decoding + - Added support for UPC-E, EAN-8 + - Also added Code 39, Code 128 support -- may need refinement + - Now any "game" key triggers photo capture + - Workaround implemented for Nokias that use "capture://image" in MMAPI + - UPC codes now trigger lookup to upcdatabase.com + +0.4 (15 Feb 2008) + - First release of Android client -- only guaranteed to work with "M3" + builds so far. + - Major reorganization of current test cases and data + - Worked around ProGuard issue with paths with spaces on Windows + - Phones that don't like FocusControl.AUTO_LOCK won't cause an error now + - Fixed ArrayIndexOutOfBoundsException in 1D barcode decoders + - Worked around "java.lang.Error 136" from Nokia N70 (?) + - Fixed bug in Shift_JIS detection and implemented basic UTF-8 detection + for nonstandard QR Codes that don't use ISO-8859-1 + +0.4.5 (22 Feb 2008) + - Checked in first minimal working Blackberry client. Still quite crude. + - Fixed UTF-8 detection again + - Refactored Reed-Solomon to allow different GF(256) primitive polynomials, + in preparation for Data Matrix + - Slight improvement to 1D decoding accuracy, most noticeable in + improvements to Code 128 decoding + - Bug fix in Code39Reader -- typo in encoding of letter "I" and "0" + - Bug fix in 1D readers -- error in counting black/white pixel counts + in certain situations + - Now supports Extended Code 39 + - Tiny bug fix to QRCodeReader for images that extend all the way + to top or left + - Android client now builds with ProGuard optimization + +0.5 (05 Mar 2008) + - Improvement in QR Code decoding accuracy on non-JSR-234 phones + - Supports "URLTO" data in barcodes. + - Restructured ParsedReaderResult parsing code to not use so many + exceptions, for more efficiency. + - "TRY_HARDER" decoding hint now supported. Will do more work in some cases + to find an image, such as scan for 1D barcodes at a right angle and + backwards. Only recommended for non-mobile applications. + - Result now indicates the format of the decoded barcode with a + BarcodeFormat instance + - More intelligent handling of POSSIBLE_FORMATS decoding hint + - Fixed sub-optimal handling of EAN-13 barcodes which encode a string + starting with "0". These are now returned as a full 13-digit string + from EAN13Reader, but will continue to be construed as 12-digit UPC-A + codes from the MultiFormatReader. + - A host of small bug fixes, including bug in Version 25 QR Code support + - A round of code cleanup and refactoring from IntelliJ inspections + +0.5.5 (12 Mar 2008) + - Revert change that rejected BufferedImage of type TYPE_CUSTOM in javase + - Now supports KDDI/AU / Softbank address book format + - Now can read Android "Intent" URIs (Android only) + - Modest improvements to efficiency of J2ME client: shorter autofocus, + better use of threads + - First checkin of Data Matrix decoder code. Not yet enabled in reader + applications. + - "Result" object now returns raw bytes decoded; maybe useful in some cases + - More unit tests for common code, EAN-8, UPC-E + +0.6 (28 Mar 2008) + - Updated android client to work on latest M5 SDK; M3-compatiable version + preserved in android-m3 + - Added tel: URL support to clients + - Added geo: URL support; only enabled for Android client so far + - Added initial support for short NDEF messages, but not yet enabled + - Bug fix to corner-case of perspective transformation code + - Now attempts to configure camera's exposure controls if available + - Fixed rotation transform used in "try harder" mode; result did not + contain the whole image + - "Try harder" mode tries 2D formats first to avoid false positives + in detailed search for 1D codes, in some cases + - More aggressive inlining by ProGuard, notable performance boost + - Small Reed-Solomon optimizations for 0- and 1-error case + - Refactored GridSampler out into common package for use by Data Matrix + - GridSampler now smarter about bounds checking and point nudging; + should avoid some ArrayIndexOutOfBoundsException issues + - More unit tests + +0.7 (5 May 2008) + - Added support for MobileTag message formats + - Added source to zxing.org + - Significant work on Android clients, including UI overhaul and + continuous scan mode + - Bug fix: ResultPoints from 1D barcodes were wrong + - SKIP_N_BARCODES hint now gone, and associated logic has been transferred + to individual project users, as it is fairly app-specific at this point + and is beginning to complicate the core library. We can help other users + who need this functionality on a case-by-case basis. + - Bug fix in AddressBookAUParsedResult parsing + - Possible workaround for NoSuchMethodError on Nokias + - New shell of an iPhone client committed + - ... and other assorted small improvements and fixes + +0.8 (16 Jun 2008) + - FinderPatternFinder now isn't fooled by false-positive finder patterns + in some rare cases + - Added SMSTO: support, expanded mailto: support to include subject and body + params + - Now should properly unescape URLs in appropriate cases; better handling of + mailto: and tel: URIs (won't get modified by the library) + - Optimized 1D decoding to use integer math + - Small bug fix / improvement in BlackPointEstimator's estimates + - 1D decoding now applies a light sharpening filter + - Refactored and simplified MonochromeBitmapSource implementations + - Removed android-m3/ directory, no longer in active development. The current + Android client remains in android/ (M5 SDK) + - Restored ability to read upside-down 1D codes without try-harder mode + - Added a crude GUI-based decoder to javase/, for the interested + - core/ is now independent of MIDP; only requires CLDC 1.1 + - Added optimized build of core library + - Unit tests now use local copies of images + +0.9 (11 July 2008) + - First draft of iPhone client committed + - core module now built in a way that makes it function as an OSGi bundle, + and also supports deployment in BugLabs's BUG platform. + - Added client for BUG platform courtesy of buglabs.net + - Big refactoring of com.google.zxing.client.result to separate idea of result + parser from result value objects + - Added basic vCard 2.1 support + - Added basic BIZCARD format support + - Added basic iCal format support + - Added new calendar event parsed result type + - Fewer false positives in 1D detectors, notably Code 39 and Code 128; + new unit tests for false positives + - Now parses and tries to act on ECI designators in QR Codes + - Merged "basic" and "regular" J2ME readers into one unified app that should + work on platforms with and without JSR-234 support + - Removed dependence on Ant 1.7 (1.6 needed now) + - Web application at zxing.org now shows raw bytes + - Far more black box tests + - Assorted small bug fixes + +1.0 (12 September 2008) + - All new RIM client from LifeMarks + - Initial Data Matrix detector included + - New and much improved Android 0.9-SDK compatible Android client + - Better 1D detection, especially UPC-E, due to loosened quite zone checks + - URIParsedResult now tries to warn about malicious URIs + - New parser for ISBNs + - New QR code blackbox images + +1.1 (22 October 2008) + - First public release of iPhone client called Barcodes + - Completely new multi-action Android client UI, released as Barcode Scanner v2.3 + - Code 128 parsing fixes + - Dozens of new blackbox test images + - Many small core library parsing improvements and bug fixes + - Improved quiet zone enforcement which reduces false positives significantly + - Better calendar event and MECARD support + - Compatibility with the Android 1.0 SDK + - Added androidtest app for testing intents and capturing blackbox test images + +1.2 (23 December 2008) + - New Android benchmark application to time decoding performance + - Dramatic performance improvements for worst case images (up to 2x faster) + - Expanded blackbox unit tests with many new test images + - Many small decoding improvements and bug fixes + - New Share functionality in the Android client which encodes contacts, bookmarks, or the + clipboard contents as a QR Code for a friend to scan with their phone + - Locale-specific Android client which picks the right TLD when connecting to Google + - Support for Furigana in the Android client, and better parsing of Kanji in core + - Added the ability to import and export multiple phone numbers and emails for a single contact + - Native QR Encoding added to the ZXing library - no need to send request to ChartServer + - New ITF barcode format support from Kevin O'Sullivan at SITA + - New HTML help system for the Android client + - New web-based QR Code encoder at zxing.appspot.com based on GWT + +1.3 (11 March 2009) + - New C# port from Mohamad Fairol + - ITF format scanning now on by default + - Many new blackbox test images + - Better QR Code detection + - Better Android Intent handling + - Improved email parsing + - New Android Integration module to make life easier for third party ZXing users + - More robust URI parsing + - Many other small improvements and bug fixes + +1.4 (26 Oct 2009) + - Wider viewfinder in Android app to account for better close-range focus + - More Android app translations: Spanish, Italian, Russian, French + - Support for more devices, Cupcake, Donut + - Worked around two important, related callback issues in Donut/Cupcake that stopped scanning + - New custom search in Android app + - New framework for evaluating the black point of an image + - Many many small bug fixes, refactorings, small improvements, new versions of dependencies + +1.5 (5 March 2010) + - Android app: + - Shopper integration + - Tries to activate front light on some devices + - Xperia et al. support with new screen sizes + - Translations: Dutch, Swedish, Czech, Finnish + - "Thinking" visualization + - Updated C++ port, including new Data Matrix port + - Updated C# port + - Preliminary RSS-14 support + - Reconceived support for various image sources and thresholding + - Other small bug fixes and performance improvements + +1.6 (20 Sept 2010) + - Many, many updates to C++, Symbian, iPhone ports + - RSS 14, Expanded support + - Code 93 support + - Codabar support + - Data Matrix support reaches release quality + - UPC/EAN +5 extension support + - Android app: + - Share history support + - Bulk scan mode + - Wi-fi configuration support + - Format available in callback for web-based integration + - Updated or new translations for: + Hungarian, Danish, Chinese, Arabic, Dutch, Slovenian, Turkish + - UI beautification + - Added ability to detect multiple barcodes per image + - Many small bug fixes and enhancements + +1.7 (8 June 2011) + - Initial support for Aztec codes + - Added the ability to read rectangular DataMatrix codes + - Added new lengths for ITF + - Dozens of bug fixes to core decoding routines + - New C# port + - Android app: + - Tablet support + - Many translations added and updated + - Support for new Intent parameters + - Many bug fixes and small features + - Mostly rewritten iPhone port + - New ZXingWidget library to include scanning in any iOS app + - Made CommandLineRunner multithreaded and recursive + +2.0 (3 Feb 2012) + + - core/ has been rewritten to work with Java 6 + - core/ has new functionality: + - PDF 417 encoding (from Barcode4J) + - Codabar encoding + - partial Maxicode decoding support + - android/ and androidtest/ have been rewritten to work with Android 2.1+ + - Uses new camera APIs + - Uses new contact APIs too + - (See separate Barcode Scanner release notes for version 4.0) + - zxing.appspot.com/ has been rewritten to work with GWT 2.4 + - cpp/, objc/ and iphone/ have received many ports of fixes and other functionality from Java, and changes to work with more recent iOS + - bug/, rim/, and javame/ have been removed + - Many, many small bug fixes and optimizations, mostly to core/ and android/ + +2.1 (22 Oct 2012) + + - Many small fixes to handling of QR code data formats, like exotic URLs, and contact info + - Many small fixes to decoding and detection common barcode formats, like PDF417, Aztec. Small improvements + too like partial error correction for PDF417 + - Many small updates to the Android app, Barcode Scanner, including rare bug fixes and translation updates. + The 2.1 release corresponds substantially to v4.3.1. + - Many ports of Java updates to C++ port + +2.2 (14 May 2013) + + - Retire Symbian port + - Retire C# port + - Improved PDF417 decoding, including macro PDF417 + - Added Aztec and Data Matrix encoders + - Added RSS Expanded decoder + - Java, C++ and iPhone build system updates, including migration to Maven for most Java modules + - Many small fixes and improvements + - Many small updates to the Android app, Barcode Scanner, including rare bug fixes and translation updates. + The 2.2 release corresponds substantially to v4.3.2. + - Update zxing.org web-based decoder for Amazon Elastic Beanstalk (and redeployed there) + - Many ports of Java updates to C++ and C# ports + - Standardize test system image processing and image format for reproducibility + +2.3.0 (1 Dec 2013) + + - Added clone of Google Chart Server QR code encoder API to zxingorg/, at endpoint /chart + - Updated zxing.appspot.com generator to remove deprecated API usage, use latest GWT / App Engine + - Added skeleton Google Glass Mirror API app + - Improve and standardize Maven build, standardize directories and version naming + - Barcode Scanner Android up requires Android 4 and is updated to use Android 4+ APIs + - javase/ and zxingorg/ modules now use Java 7 + - Many small fixes and improvements + - (Last release including cpp/ and iphone/ code) + +3.0.0 (3 March 2014) + + - iphone/, cpp/, objc/, and glass-mirror/ are retired + - Java 7 now used for all Java-based modules + - Project moved to github.com/zxing/zxing + - Many small bug fixes and improvements + +3.0.1 (18 April 2014) + + - Several small bug fixes + +3.1.0 (22 May 2014) + + - Retire actionscript port + - Small bug fixes + +3.2.0 (15 Feb 2015) + + - Several bug fixes, especially for Data Matrix and PDF417 parsing + - Retired jruby binding + - Barcode Scanner can now disable history + +3.2.1 (20 Aug 2015) + + - Small bug fixes diff --git a/apps/imagegen/zxing/LICENSE b/apps/imagegen/zxing/LICENSE new file mode 100644 index 0000000000..510991edab --- /dev/null +++ b/apps/imagegen/zxing/LICENSE @@ -0,0 +1,245 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================== +jai-imageio +======================================================================== + +Copyright (c) 2005 Sun Microsystems, Inc. +Copyright © 2010-2014 University of Manchester +Copyright © 2010-2015 Stian Soiland-Reyes +Copyright © 2015 Peter Hull +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistribution of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistribution 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 name of Sun Microsystems, Inc. or the names of +contributors may be used to endorse or promote products derived +from this software without specific prior written permission. + +This software is provided "AS IS," without a warranty of any +kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND +WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY +EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL +NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF +USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS +DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR +ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND +REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR +INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +You acknowledge that this software is not designed or intended for +use in the design, construction, operation or maintenance of any +nuclear facility. \ No newline at end of file diff --git a/apps/imagegen/zxing/README.md b/apps/imagegen/zxing/README.md new file mode 100644 index 0000000000..b0036f7e88 --- /dev/null +++ b/apps/imagegen/zxing/README.md @@ -0,0 +1,83 @@ +<img align="right" src="https://raw.github.com/wiki/zxing/zxing/zxing-logo.png"/> + +ZXing ("zebra crossing") is an open-source, multi-format 1D/2D barcode image processing +library implemented in Java, with ports to other languages. + +## Supported Formats + +| 1D product | 1D industrial | 2D +| ---------- | ------------- | -------------- +| UPC-A | Code 39 | QR Code +| UPC-E | Code 93 | Data Matrix +| EAN-8 | Code 128 | Aztec (beta) +| EAN-13 | Codabar | PDF 417 (beta) +| | ITF | +| | RSS-14 | +| | RSS-Expanded | + +## Components + +### Active + +| Module | Description +| ------------------- | ----------- +| core | The core image decoding library, and test code +| javase | JavaSE-specific client code +| android | Android client Barcode Scanner [](https://play.google.com/store/apps/details?id=com.google.zxing.client.android) +| androidtest | Android test app, ZXing Test +| android-integration | Supports integration with Barcode Scanner via `Intent` +| android-core | Android-related code shared among `android`, `androidtest`, `glass` +| glass | Simple Google Glass application +| zxingorg | The source behind `zxing.org` +| zxing.appspot.com | The source behind web-based barcode generator at `zxing.appspot.com` + +### Available in previous releases + +| Module | Description +| ------ | ----------- +| [cpp](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/cpp) | C++ port +| [iphone](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/iphone) | iPhone client +| [objc](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/objc) | Objective C port +| [actionscript](https://github.com/zxing/zxing/tree/c1df162b95e07928afbd4830798cc1408af1ac67/actionscript) | Partial ActionScript port +| [jruby](https://github.com/zxing/zxing/tree/a95a8fee842f67fb43799a8e0e70e4c68b509c43/jruby) | JRuby wrapper + +### ZXing-based third-party open source projects + +| Module | Description +| --------------------------------------------------------------- | ----------- +| [QZXing](https://sourceforge.net/projects/qzxing) | port to Qt framework +| [zxing-cpp](https://github.com/glassechidna/zxing-cpp) | port to C++ (forked from the [deprecated official C++ port](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/cpp)) +| [zxing_cpp.rb](https://github.com/glassechidna/zxing_cpp.rb) | bindings for Ruby (not just JRuby), powered by [zxing-cpp](https://github.com/glassechidna/zxing-cpp) +| [python-zxing](https://github.com/oostendo/python-zxing) | bindings for Python +| [ZXing .NET](http://zxingnet.codeplex.com/) | port to .NET and C#, and related Windows platform + +### Other related third-party open source projects + +| Module | Description +| ---------------------------------------------- | ----------- +| [Barcode4J](http://barcode4j.sourceforge.net/) | Generator library in Java +| [ZBar](http://zbar.sourceforge.net/) | Reader library in C99 +| [OkapiBarcode](https://github.com/woo-j/OkapiBarcode) | + +## Links + +* [Online Decoder](http://zxing.org/w/decode.jspx) +* [QR Code Generator](http://zxing.appspot.com/generator) +* [Javadoc](http://zxing.github.io/zxing/apidocs/) +* [Documentation Site](http://zxing.github.io/zxing/) +* [Google+](https://plus.google.com/u/0/b/105889184633382354358/105889184633382354358/posts) + +## Contacting + +Post to the [discussion forum](https://groups.google.com/group/zxing) or tag a question with [`zxing` +on StackOverflow](http://stackoverflow.com/questions/tagged/zxing). + +## Etcetera + +[](https://travis-ci.org/zxing/zxing) +[](https://scan.coverity.com/projects/1924) +[](https://codecov.io/github/zxing/zxing?branch=master) + +QR code is trademarked by Denso Wave, inc. Thanks to Haase & Martin OHG for contributing the logo. + +Optimized with [](http://www.ej-technologies.com/products/jprofiler/overview.html) diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java new file mode 100644 index 0000000000..7f6a0ef586 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java @@ -0,0 +1,77 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * Enumerates barcode formats known to this package. Please keep alphabetized. + * + * @author Sean Owen + */ +public enum BarcodeFormat { + + /** Aztec 2D barcode format. */ + AZTEC, + + /** CODABAR 1D format. */ + CODABAR, + + /** Code 39 1D format. */ + CODE_39, + + /** Code 93 1D format. */ + CODE_93, + + /** Code 128 1D format. */ + CODE_128, + + /** Data Matrix 2D barcode format. */ + DATA_MATRIX, + + /** EAN-8 1D format. */ + EAN_8, + + /** EAN-13 1D format. */ + EAN_13, + + /** ITF (Interleaved Two of Five) 1D format. */ + ITF, + + /** MaxiCode 2D barcode format. */ + MAXICODE, + + /** PDF417 format. */ + PDF_417, + + /** QR Code 2D barcode format. */ + QR_CODE, + + /** RSS 14 */ + RSS_14, + + /** RSS EXPANDED */ + RSS_EXPANDED, + + /** UPC-A 1D format. */ + UPC_A, + + /** UPC-E 1D format. */ + UPC_E, + + /** UPC/EAN extension format. Not a stand-alone format. */ + UPC_EAN_EXTENSION + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java new file mode 100644 index 0000000000..c5acbe3ee8 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java @@ -0,0 +1,47 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * Thrown when a barcode was successfully detected and decoded, but + * was not returned because its checksum feature failed. + * + * @author Sean Owen + */ +public final class ChecksumException extends ReaderException { + + private static final ChecksumException INSTANCE = new ChecksumException(); + static { + INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless + } + + private ChecksumException() { + // do nothing + } + + private ChecksumException(Throwable cause) { + super(cause); + } + + public static ChecksumException getChecksumInstance() { + return isStackTrace ? new ChecksumException() : INSTANCE; + } + + public static ChecksumException getChecksumInstance(Throwable cause) { + return isStackTrace ? new ChecksumException(cause) : INSTANCE; + } +} \ No newline at end of file diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java new file mode 100644 index 0000000000..eb7938e422 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java @@ -0,0 +1,98 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * These are a set of hints that you may pass to Writers to specify their behavior. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public enum EncodeHintType { + + /** + * Specifies what degree of error correction to use, for example in QR Codes. + * Type depends on the encoder. For example for QR codes it's type + * {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}. + * For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words. + * For PDF417 it is of type {@link Integer}, valid values being 0 to 8. + * In all cases, it can also be a {@link String} representation of the desired value as well. + * Note: an Aztec symbol should have a minimum of 25% EC words. + */ + ERROR_CORRECTION, + + /** + * Specifies what character encoding to use where applicable (type {@link String}) + */ + CHARACTER_SET, + + /** + * Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint}) + */ + DATA_MATRIX_SHAPE, + + /** + * Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now. + * + * @deprecated use width/height params in + * {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)} + */ + @Deprecated + MIN_SIZE, + + /** + * Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now. + * + * @deprecated without replacement + */ + @Deprecated + MAX_SIZE, + + /** + * Specifies margin, in pixels, to use when generating the barcode. The meaning can vary + * by format; for example it controls margin before and after the barcode horizontally for + * most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value). + */ + MARGIN, + + /** + * Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false" + * {@link String} value). + */ + PDF417_COMPACT, + + /** + * Specifies what compaction mode to use for PDF417 (type + * {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its + * enum values). + */ + PDF417_COMPACTION, + + /** + * Specifies the minimum and maximum number of rows and columns for PDF417 (type + * {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}). + */ + PDF417_DIMENSIONS, + + /** + * Specifies the required number of layers for an Aztec code. + * A negative number (-1, -2, -3, -4) specifies a compact Aztec code. + * 0 indicates to use the minimum number of layers (the default). + * A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code. + * (Type {@link Integer}, or {@link String} representation of the integer value). + */ + AZTEC_LAYERS, +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java new file mode 100644 index 0000000000..b046b822d3 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java @@ -0,0 +1,47 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * Thrown when a barcode was successfully detected, but some aspect of + * the content did not conform to the barcode's format rules. This could have + * been due to a mis-detection. + * + * @author Sean Owen + */ +public final class FormatException extends ReaderException { + + private static final FormatException INSTANCE = new FormatException(); + static { + INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless + } + + private FormatException() { + } + + private FormatException(Throwable cause) { + super(cause); + } + + public static FormatException getFormatInstance() { + return isStackTrace ? new FormatException() : INSTANCE; + } + + public static FormatException getFormatInstance(Throwable cause) { + return isStackTrace ? new FormatException(cause) : INSTANCE; + } +} \ No newline at end of file diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java new file mode 100644 index 0000000000..5f2c12e432 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * The general exception class throw when something goes wrong during decoding of a barcode. + * This includes, but is not limited to, failing checksums / error correction algorithms, being + * unable to locate finder timing patterns, and so on. + * + * @author Sean Owen + */ +public abstract class ReaderException extends Exception { + + // disable stack traces when not running inside test units + protected static final boolean isStackTrace = + System.getProperty("surefire.test.class.path") != null; + protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0]; + + ReaderException() { + // do nothing + } + + ReaderException(Throwable cause) { + super(cause); + } + + // Prevent stack traces from being taken + // srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden? + // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow. + @Override + public final Throwable fillInStackTrace() { + return null; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java new file mode 100644 index 0000000000..f405fd844f --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +import com.google.zxing.common.BitMatrix; + +import java.util.Map; + +/** + * The base class for all objects which encode/generate a barcode image. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public interface Writer { + + /** + * Encode a barcode using the default settings. + * + * @param contents The contents to encode in the barcode + * @param format The barcode format to generate + * @param width The preferred width in pixels + * @param height The preferred height in pixels + * @return {@link BitMatrix} representing encoded barcode image + * @throws WriterException if contents cannot be encoded legally in a format + */ + BitMatrix encode(String contents, BarcodeFormat format, int width, int height) + throws WriterException; + + /** + * @param contents The contents to encode in the barcode + * @param format The barcode format to generate + * @param width The preferred width in pixels + * @param height The preferred height in pixels + * @param hints Additional parameters to supply to the encoder + * @return {@link BitMatrix} representing encoded barcode image + * @throws WriterException if contents cannot be encoded legally in a format + */ + BitMatrix encode(String contents, + BarcodeFormat format, + int width, + int height, + Map<EncodeHintType,?> hints) + throws WriterException; + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java new file mode 100644 index 0000000000..c61800b93a --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing; + +/** + * A base class which covers the range of exceptions which may occur when encoding a barcode using + * the Writer framework. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class WriterException extends Exception { + + public WriterException() { + } + + public WriterException(String message) { + super(message); + } + + public WriterException(Throwable cause) { + super(cause); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java new file mode 100644 index 0000000000..0af7dd72ce --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java @@ -0,0 +1,375 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common; + +import java.util.Arrays; + +/** + * <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p> + * + * @author Sean Owen + */ +public final class BitArray implements Cloneable { + + private int[] bits; + private int size; + + public BitArray() { + this.size = 0; + this.bits = new int[1]; + } + + public BitArray(int size) { + this.size = size; + this.bits = makeArray(size); + } + + // For testing only + BitArray(int[] bits, int size) { + this.bits = bits; + this.size = size; + } + + public int getSize() { + return size; + } + + public int getSizeInBytes() { + return (size + 7) / 8; + } + + private void ensureCapacity(int size) { + if (size > bits.length * 32) { + int[] newBits = makeArray(size); + System.arraycopy(bits, 0, newBits, 0, bits.length); + this.bits = newBits; + } + } + + /** + * @param i bit to get + * @return true iff bit i is set + */ + public boolean get(int i) { + return (bits[i / 32] & (1 << (i & 0x1F))) != 0; + } + + /** + * Sets bit i. + * + * @param i bit to set + */ + public void set(int i) { + bits[i / 32] |= 1 << (i & 0x1F); + } + + /** + * Flips bit i. + * + * @param i bit to set + */ + public void flip(int i) { + bits[i / 32] ^= 1 << (i & 0x1F); + } + + /** + * @param from first bit to check + * @return index of first bit that is set, starting from the given index, or size if none are set + * at or beyond this given index + * @see #getNextUnset(int) + */ + public int getNextSet(int from) { + if (from >= size) { + return size; + } + int bitsOffset = from / 32; + int currentBits = bits[bitsOffset]; + // mask off lesser bits first + currentBits &= ~((1 << (from & 0x1F)) - 1); + while (currentBits == 0) { + if (++bitsOffset == bits.length) { + return size; + } + currentBits = bits[bitsOffset]; + } + int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); + return result > size ? size : result; + } + + /** + * @param from index to start looking for unset bit + * @return index of next unset bit, or {@code size} if none are unset until the end + * @see #getNextSet(int) + */ + public int getNextUnset(int from) { + if (from >= size) { + return size; + } + int bitsOffset = from / 32; + int currentBits = ~bits[bitsOffset]; + // mask off lesser bits first + currentBits &= ~((1 << (from & 0x1F)) - 1); + while (currentBits == 0) { + if (++bitsOffset == bits.length) { + return size; + } + currentBits = ~bits[bitsOffset]; + } + int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); + return result > size ? size : result; + } + + /** + * Sets a block of 32 bits, starting at bit i. + * + * @param i first bit to set + * @param newBits the new value of the next 32 bits. Note again that the least-significant bit + * corresponds to bit i, the next-least-significant to i+1, and so on. + */ + public void setBulk(int i, int newBits) { + bits[i / 32] = newBits; + } + + /** + * Sets a range of bits. + * + * @param start start of range, inclusive. + * @param end end of range, exclusive + */ + public void setRange(int start, int end) { + if (end < start) { + throw new IllegalArgumentException(); + } + if (end == start) { + return; + } + end--; // will be easier to treat this as the last actually set bit -- inclusive + int firstInt = start / 32; + int lastInt = end / 32; + for (int i = firstInt; i <= lastInt; i++) { + int firstBit = i > firstInt ? 0 : start & 0x1F; + int lastBit = i < lastInt ? 31 : end & 0x1F; + int mask; + if (firstBit == 0 && lastBit == 31) { + mask = -1; + } else { + mask = 0; + for (int j = firstBit; j <= lastBit; j++) { + mask |= 1 << j; + } + } + bits[i] |= mask; + } + } + + /** + * Clears all bits (sets to false). + */ + public void clear() { + int max = bits.length; + for (int i = 0; i < max; i++) { + bits[i] = 0; + } + } + + /** + * Efficient method to check if a range of bits is set, or not set. + * + * @param start start of range, inclusive. + * @param end end of range, exclusive + * @param value if true, checks that bits in range are set, otherwise checks that they are not set + * @return true iff all bits are set or not set in range, according to value argument + * @throws IllegalArgumentException if end is less than or equal to start + */ + public boolean isRange(int start, int end, boolean value) { + if (end < start) { + throw new IllegalArgumentException(); + } + if (end == start) { + return true; // empty range matches + } + end--; // will be easier to treat this as the last actually set bit -- inclusive + int firstInt = start / 32; + int lastInt = end / 32; + for (int i = firstInt; i <= lastInt; i++) { + int firstBit = i > firstInt ? 0 : start & 0x1F; + int lastBit = i < lastInt ? 31 : end & 0x1F; + int mask; + if (firstBit == 0 && lastBit == 31) { + mask = -1; + } else { + mask = 0; + for (int j = firstBit; j <= lastBit; j++) { + mask |= 1 << j; + } + } + + // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, + // equals the mask, or we're looking for 0s and the masked portion is not all 0s + if ((bits[i] & mask) != (value ? mask : 0)) { + return false; + } + } + return true; + } + + public void appendBit(boolean bit) { + ensureCapacity(size + 1); + if (bit) { + bits[size / 32] |= 1 << (size & 0x1F); + } + size++; + } + + /** + * Appends the least-significant bits, from value, in order from most-significant to + * least-significant. For example, appending 6 bits from 0x000001E will append the bits + * 0, 1, 1, 1, 1, 0 in that order. + * + * @param value {@code int} containing bits to append + * @param numBits bits from value to append + */ + public void appendBits(int value, int numBits) { + if (numBits < 0 || numBits > 32) { + throw new IllegalArgumentException("Num bits must be between 0 and 32"); + } + ensureCapacity(size + numBits); + for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) { + appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1); + } + } + + public void appendBitArray(BitArray other) { + int otherSize = other.size; + ensureCapacity(size + otherSize); + for (int i = 0; i < otherSize; i++) { + appendBit(other.get(i)); + } + } + + public void xor(BitArray other) { + if (bits.length != other.bits.length) { + throw new IllegalArgumentException("Sizes don't match"); + } + for (int i = 0; i < bits.length; i++) { + // The last byte could be incomplete (i.e. not have 8 bits in + // it) but there is no problem since 0 XOR 0 == 0. + bits[i] ^= other.bits[i]; + } + } + + /** + * + * @param bitOffset first bit to start writing + * @param array array to write into. Bytes are written most-significant byte first. This is the opposite + * of the internal representation, which is exposed by {@link #getBitArray()} + * @param offset position in array to start writing + * @param numBytes how many bytes to write + */ + public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) { + for (int i = 0; i < numBytes; i++) { + int theByte = 0; + for (int j = 0; j < 8; j++) { + if (get(bitOffset)) { + theByte |= 1 << (7 - j); + } + bitOffset++; + } + array[offset + i] = (byte) theByte; + } + } + + /** + * @return underlying array of ints. The first element holds the first 32 bits, and the least + * significant bit is bit 0. + */ + public int[] getBitArray() { + return bits; + } + + /** + * Reverses all bits in the array. + */ + public void reverse() { + int[] newBits = new int[bits.length]; + // reverse all int's first + int len = ((size-1) / 32); + int oldBitsLen = len + 1; + for (int i = 0; i < oldBitsLen; i++) { + long x = (long) bits[i]; + x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1); + x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2); + x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4); + x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8); + x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16); + newBits[len - i] = (int) x; + } + // now correct the int's if the bit size isn't a multiple of 32 + if (size != oldBitsLen * 32) { + int leftOffset = oldBitsLen * 32 - size; + int mask = 1; + for (int i = 0; i < 31 - leftOffset; i++) { + mask = (mask << 1) | 1; + } + int currentInt = (newBits[0] >> leftOffset) & mask; + for (int i = 1; i < oldBitsLen; i++) { + int nextInt = newBits[i]; + currentInt |= nextInt << (32 - leftOffset); + newBits[i - 1] = currentInt; + currentInt = (nextInt >> leftOffset) & mask; + } + newBits[oldBitsLen - 1] = currentInt; + } + bits = newBits; + } + + private static int[] makeArray(int size) { + return new int[(size + 31) / 32]; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BitArray)) { + return false; + } + BitArray other = (BitArray) o; + return size == other.size && Arrays.equals(bits, other.bits); + } + + @Override + public int hashCode() { + return 31 * size + Arrays.hashCode(bits); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(size); + for (int i = 0; i < size; i++) { + if ((i & 0x07) == 0) { + result.append(' '); + } + result.append(get(i) ? 'X' : '.'); + } + return result.toString(); + } + + @Override + public BitArray clone() { + return new BitArray(bits.clone(), size); + } + +} \ No newline at end of file diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java new file mode 100644 index 0000000000..30e2358cb0 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java @@ -0,0 +1,449 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common; + +import java.util.Arrays; + +/** + * <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common + * module, x is the column position, and y is the row position. The ordering is always x, y. + * The origin is at the top-left.</p> + * + * <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins + * with a new int. This is done intentionally so that we can copy out a row into a BitArray very + * efficiently.</p> + * + * <p>The ordering of bits is row-major. Within each int, the least significant bits are used first, + * meaning they represent lower x values. This is compatible with BitArray's implementation.</p> + * + * @author Sean Owen + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class BitMatrix implements Cloneable { + + private final int width; + private final int height; + private final int rowSize; + private final int[] bits; + + // A helper to construct a square matrix. + public BitMatrix(int dimension) { + this(dimension, dimension); + } + + public BitMatrix(int width, int height) { + if (width < 1 || height < 1) { + throw new IllegalArgumentException("Both dimensions must be greater than 0"); + } + this.width = width; + this.height = height; + this.rowSize = (width + 31) / 32; + bits = new int[rowSize * height]; + } + + private BitMatrix(int width, int height, int rowSize, int[] bits) { + this.width = width; + this.height = height; + this.rowSize = rowSize; + this.bits = bits; + } + + public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) { + if (stringRepresentation == null) { + throw new IllegalArgumentException(); + } + + boolean[] bits = new boolean[stringRepresentation.length()]; + int bitsPos = 0; + int rowStartPos = 0; + int rowLength = -1; + int nRows = 0; + int pos = 0; + while (pos < stringRepresentation.length()) { + if (stringRepresentation.charAt(pos) == '\n' || + stringRepresentation.charAt(pos) == '\r') { + if (bitsPos > rowStartPos) { + if (rowLength == -1) { + rowLength = bitsPos - rowStartPos; + } else if (bitsPos - rowStartPos != rowLength) { + throw new IllegalArgumentException("row lengths do not match"); + } + rowStartPos = bitsPos; + nRows++; + } + pos++; + } else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) { + pos += setString.length(); + bits[bitsPos] = true; + bitsPos++; + } else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) { + pos += unsetString.length(); + bits[bitsPos] = false; + bitsPos++; + } else { + throw new IllegalArgumentException( + "illegal character encountered: " + stringRepresentation.substring(pos)); + } + } + + // no EOL at end? + if (bitsPos > rowStartPos) { + if(rowLength == -1) { + rowLength = bitsPos - rowStartPos; + } else if (bitsPos - rowStartPos != rowLength) { + throw new IllegalArgumentException("row lengths do not match"); + } + nRows++; + } + + BitMatrix matrix = new BitMatrix(rowLength, nRows); + for (int i = 0; i < bitsPos; i++) { + if (bits[i]) { + matrix.set(i % rowLength, i / rowLength); + } + } + return matrix; + } + + /** + * <p>Gets the requested bit, where true means black.</p> + * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + * @return value of given bit in matrix + */ + public boolean get(int x, int y) { + int offset = y * rowSize + (x / 32); + return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; + } + + /** + * <p>Sets the given bit to true.</p> + * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + */ + public void set(int x, int y) { + int offset = y * rowSize + (x / 32); + bits[offset] |= 1 << (x & 0x1f); + } + + public void unset(int x, int y) { + int offset = y * rowSize + (x / 32); + bits[offset] &= ~(1 << (x & 0x1f)); + } + + /** + * <p>Flips the given bit.</p> + * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + */ + public void flip(int x, int y) { + int offset = y * rowSize + (x / 32); + bits[offset] ^= 1 << (x & 0x1f); + } + + /** + * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding + * mask bit is set. + * + * @param mask XOR mask + */ + public void xor(BitMatrix mask) { + if (width != mask.getWidth() || height != mask.getHeight() + || rowSize != mask.getRowSize()) { + throw new IllegalArgumentException("input matrix dimensions do not match"); + } + BitArray rowArray = new BitArray(width / 32 + 1); + for (int y = 0; y < height; y++) { + int offset = y * rowSize; + int[] row = mask.getRow(y, rowArray).getBitArray(); + for (int x = 0; x < rowSize; x++) { + bits[offset + x] ^= row[x]; + } + } + } + + /** + * Clears all bits (sets to false). + */ + public void clear() { + int max = bits.length; + for (int i = 0; i < max; i++) { + bits[i] = 0; + } + } + + /** + * <p>Sets a square region of the bit matrix to true.</p> + * + * @param left The horizontal position to begin at (inclusive) + * @param top The vertical position to begin at (inclusive) + * @param width The width of the region + * @param height The height of the region + */ + public void setRegion(int left, int top, int width, int height) { + if (top < 0 || left < 0) { + throw new IllegalArgumentException("Left and top must be nonnegative"); + } + if (height < 1 || width < 1) { + throw new IllegalArgumentException("Height and width must be at least 1"); + } + int right = left + width; + int bottom = top + height; + if (bottom > this.height || right > this.width) { + throw new IllegalArgumentException("The region must fit inside the matrix"); + } + for (int y = top; y < bottom; y++) { + int offset = y * rowSize; + for (int x = left; x < right; x++) { + bits[offset + (x / 32)] |= 1 << (x & 0x1f); + } + } + } + + /** + * A fast method to retrieve one row of data from the matrix as a BitArray. + * + * @param y The row to retrieve + * @param row An optional caller-allocated BitArray, will be allocated if null or too small + * @return The resulting BitArray - this reference should always be used even when passing + * your own row + */ + public BitArray getRow(int y, BitArray row) { + if (row == null || row.getSize() < width) { + row = new BitArray(width); + } else { + row.clear(); + } + int offset = y * rowSize; + for (int x = 0; x < rowSize; x++) { + row.setBulk(x * 32, bits[offset + x]); + } + return row; + } + + /** + * @param y row to set + * @param row {@link BitArray} to copy from + */ + public void setRow(int y, BitArray row) { + System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize); + } + + /** + * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees + */ + public void rotate180() { + int width = getWidth(); + int height = getHeight(); + BitArray topRow = new BitArray(width); + BitArray bottomRow = new BitArray(width); + for (int i = 0; i < (height+1) / 2; i++) { + topRow = getRow(i, topRow); + bottomRow = getRow(height - 1 - i, bottomRow); + topRow.reverse(); + bottomRow.reverse(); + setRow(i, bottomRow); + setRow(height - 1 - i, topRow); + } + } + + /** + * This is useful in detecting the enclosing rectangle of a 'pure' barcode. + * + * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white + */ + public int[] getEnclosingRectangle() { + int left = width; + int top = height; + int right = -1; + int bottom = -1; + + for (int y = 0; y < height; y++) { + for (int x32 = 0; x32 < rowSize; x32++) { + int theBits = bits[y * rowSize + x32]; + if (theBits != 0) { + if (y < top) { + top = y; + } + if (y > bottom) { + bottom = y; + } + if (x32 * 32 < left) { + int bit = 0; + while ((theBits << (31 - bit)) == 0) { + bit++; + } + if ((x32 * 32 + bit) < left) { + left = x32 * 32 + bit; + } + } + if (x32 * 32 + 31 > right) { + int bit = 31; + while ((theBits >>> bit) == 0) { + bit--; + } + if ((x32 * 32 + bit) > right) { + right = x32 * 32 + bit; + } + } + } + } + } + + int width = right - left; + int height = bottom - top; + + if (width < 0 || height < 0) { + return null; + } + + return new int[] {left, top, width, height}; + } + + /** + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white + */ + public int[] getTopLeftOnBit() { + int bitsOffset = 0; + while (bitsOffset < bits.length && bits[bitsOffset] == 0) { + bitsOffset++; + } + if (bitsOffset == bits.length) { + return null; + } + int y = bitsOffset / rowSize; + int x = (bitsOffset % rowSize) * 32; + + int theBits = bits[bitsOffset]; + int bit = 0; + while ((theBits << (31-bit)) == 0) { + bit++; + } + x += bit; + return new int[] {x, y}; + } + + public int[] getBottomRightOnBit() { + int bitsOffset = bits.length - 1; + while (bitsOffset >= 0 && bits[bitsOffset] == 0) { + bitsOffset--; + } + if (bitsOffset < 0) { + return null; + } + + int y = bitsOffset / rowSize; + int x = (bitsOffset % rowSize) * 32; + + int theBits = bits[bitsOffset]; + int bit = 31; + while ((theBits >>> bit) == 0) { + bit--; + } + x += bit; + + return new int[] {x, y}; + } + + /** + * @return The width of the matrix + */ + public int getWidth() { + return width; + } + + /** + * @return The height of the matrix + */ + public int getHeight() { + return height; + } + + /** + * @return The row size of the matrix + */ + public int getRowSize() { + return rowSize; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BitMatrix)) { + return false; + } + BitMatrix other = (BitMatrix) o; + return width == other.width && height == other.height && rowSize == other.rowSize && + Arrays.equals(bits, other.bits); + } + + @Override + public int hashCode() { + int hash = width; + hash = 31 * hash + width; + hash = 31 * hash + height; + hash = 31 * hash + rowSize; + hash = 31 * hash + Arrays.hashCode(bits); + return hash; + } + + /** + * @return string representation using "X" for set and " " for unset bits + */ + @Override + public String toString() { + return toString("X ", " "); + } + + /** + * @param setString representation of a set bit + * @param unsetString representation of an unset bit + * @return string representation of entire matrix utilizing given strings + */ + public String toString(String setString, String unsetString) { + return toString(setString, unsetString, "\n"); + } + + /** + * @param setString representation of a set bit + * @param unsetString representation of an unset bit + * @param lineSeparator newline character in string representation + * @return string representation of entire matrix utilizing given strings and line separator + * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always + */ + @Deprecated + public String toString(String setString, String unsetString, String lineSeparator) { + StringBuilder result = new StringBuilder(height * (width + 1)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + result.append(get(x, y) ? setString : unsetString); + } + result.append(lineSeparator); + } + return result.toString(); + } + + @Override + public BitMatrix clone() { + return new BitMatrix(width, height, rowSize, bits.clone()); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java new file mode 100644 index 0000000000..7c3853ca89 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java @@ -0,0 +1,118 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common; + +import com.google.zxing.FormatException; + +import java.util.HashMap; +import java.util.Map; + +/** + * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 + * of ISO 18004. + * + * @author Sean Owen + */ +public enum CharacterSetECI { + + // Enum name is a Java encoding valid for java.lang and java.io + Cp437(new int[]{0,2}), + ISO8859_1(new int[]{1,3}, "ISO-8859-1"), + ISO8859_2(4, "ISO-8859-2"), + ISO8859_3(5, "ISO-8859-3"), + ISO8859_4(6, "ISO-8859-4"), + ISO8859_5(7, "ISO-8859-5"), + ISO8859_6(8, "ISO-8859-6"), + ISO8859_7(9, "ISO-8859-7"), + ISO8859_8(10, "ISO-8859-8"), + ISO8859_9(11, "ISO-8859-9"), + ISO8859_10(12, "ISO-8859-10"), + ISO8859_11(13, "ISO-8859-11"), + ISO8859_13(15, "ISO-8859-13"), + ISO8859_14(16, "ISO-8859-14"), + ISO8859_15(17, "ISO-8859-15"), + ISO8859_16(18, "ISO-8859-16"), + SJIS(20, "Shift_JIS"), + Cp1250(21, "windows-1250"), + Cp1251(22, "windows-1251"), + Cp1252(23, "windows-1252"), + Cp1256(24, "windows-1256"), + UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"), + UTF8(26, "UTF-8"), + ASCII(new int[] {27, 170}, "US-ASCII"), + Big5(28), + GB18030(29, "GB2312", "EUC_CN", "GBK"), + EUC_KR(30, "EUC-KR"); + + private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>(); + private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>(); + static { + for (CharacterSetECI eci : values()) { + for (int value : eci.values) { + VALUE_TO_ECI.put(value, eci); + } + NAME_TO_ECI.put(eci.name(), eci); + for (String name : eci.otherEncodingNames) { + NAME_TO_ECI.put(name, eci); + } + } + } + + private final int[] values; + private final String[] otherEncodingNames; + + CharacterSetECI(int value) { + this(new int[] {value}); + } + + CharacterSetECI(int value, String... otherEncodingNames) { + this.values = new int[] {value}; + this.otherEncodingNames = otherEncodingNames; + } + + CharacterSetECI(int[] values, String... otherEncodingNames) { + this.values = values; + this.otherEncodingNames = otherEncodingNames; + } + + public int getValue() { + return values[0]; + } + + /** + * @param value character set ECI value + * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but + * unsupported + * @throws FormatException if ECI value is invalid + */ + public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException { + if (value < 0 || value >= 900) { + throw FormatException.getFormatInstance(); + } + return VALUE_TO_ECI.get(value); + } + + /** + * @param name character set ECI encoding name + * @return CharacterSetECI representing ECI for character encoding, or null if it is legal + * but unsupported + */ + public static CharacterSetECI getCharacterSetECIByName(String name) { + return NAME_TO_ECI.get(name); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java new file mode 100644 index 0000000000..9ede5d80a6 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java @@ -0,0 +1,166 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common.reedsolomon; + +/** + * <p>This class contains utility methods for performing mathematical operations over + * the Galois Fields. Operations use a given primitive polynomial in calculations.</p> + * + * <p>Throughout this package, elements of the GF are represented as an {@code int} + * for convenience and speed (but at the cost of memory). + * </p> + * + * @author Sean Owen + * @author David Olivier + */ +public final class GenericGF { + + public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 + public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 + public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 + public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 + public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 + public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 + public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256; + public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6; + + private final int[] expTable; + private final int[] logTable; + private final GenericGFPoly zero; + private final GenericGFPoly one; + private final int size; + private final int primitive; + private final int generatorBase; + + /** + * Create a representation of GF(size) using the given primitive polynomial. + * + * @param primitive irreducible polynomial whose coefficients are represented by + * the bits of an int, where the least-significant bit represents the constant + * coefficient + * @param size the size of the field + * @param b the factor b in the generator polynomial can be 0- or 1-based + * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))). + * In most cases it should be 1, but for QR code it is 0. + */ + public GenericGF(int primitive, int size, int b) { + this.primitive = primitive; + this.size = size; + this.generatorBase = b; + + expTable = new int[size]; + logTable = new int[size]; + int x = 1; + for (int i = 0; i < size; i++) { + expTable[i] = x; + x *= 2; // we're assuming the generator alpha is 2 + if (x >= size) { + x ^= primitive; + x &= size-1; + } + } + for (int i = 0; i < size-1; i++) { + logTable[expTable[i]] = i; + } + // logTable[0] == 0 but this should never be used + zero = new GenericGFPoly(this, new int[]{0}); + one = new GenericGFPoly(this, new int[]{1}); + } + + GenericGFPoly getZero() { + return zero; + } + + GenericGFPoly getOne() { + return one; + } + + /** + * @return the monomial representing coefficient * x^degree + */ + GenericGFPoly buildMonomial(int degree, int coefficient) { + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return zero; + } + int[] coefficients = new int[degree + 1]; + coefficients[0] = coefficient; + return new GenericGFPoly(this, coefficients); + } + + /** + * Implements both addition and subtraction -- they are the same in GF(size). + * + * @return sum/difference of a and b + */ + static int addOrSubtract(int a, int b) { + return a ^ b; + } + + /** + * @return 2 to the power of a in GF(size) + */ + int exp(int a) { + return expTable[a]; + } + + /** + * @return base 2 log of a in GF(size) + */ + int log(int a) { + if (a == 0) { + throw new IllegalArgumentException(); + } + return logTable[a]; + } + + /** + * @return multiplicative inverse of a + */ + int inverse(int a) { + if (a == 0) { + throw new ArithmeticException(); + } + return expTable[size - logTable[a] - 1]; + } + + /** + * @return product of a and b in GF(size) + */ + int multiply(int a, int b) { + if (a == 0 || b == 0) { + return 0; + } + return expTable[(logTable[a] + logTable[b]) % (size - 1)]; + } + + public int getSize() { + return size; + } + + public int getGeneratorBase() { + return generatorBase; + } + + @Override + public String toString() { + return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')'; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java new file mode 100644 index 0000000000..8887e0ee12 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java @@ -0,0 +1,264 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common.reedsolomon; + +/** + * <p>Represents a polynomial whose coefficients are elements of a GF. + * Instances of this class are immutable.</p> + * + * <p>Much credit is due to William Rucklidge since portions of this code are an indirect + * port of his C++ Reed-Solomon implementation.</p> + * + * @author Sean Owen + */ +final class GenericGFPoly { + + private final GenericGF field; + private final int[] coefficients; + + /** + * @param field the {@link GenericGF} instance representing the field to use + * to perform computations + * @param coefficients coefficients as ints representing elements of GF(size), arranged + * from most significant (highest-power term) coefficient to least significant + * @throws IllegalArgumentException if argument is null or empty, + * or if leading coefficient is 0 and this is not a + * constant polynomial (that is, it is not the monomial "0") + */ + GenericGFPoly(GenericGF field, int[] coefficients) { + if (coefficients.length == 0) { + throw new IllegalArgumentException(); + } + this.field = field; + int coefficientsLength = coefficients.length; + if (coefficientsLength > 1 && coefficients[0] == 0) { + // Leading term must be non-zero for anything except the constant polynomial "0" + int firstNonZero = 1; + while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { + firstNonZero++; + } + if (firstNonZero == coefficientsLength) { + this.coefficients = new int[]{0}; + } else { + this.coefficients = new int[coefficientsLength - firstNonZero]; + System.arraycopy(coefficients, + firstNonZero, + this.coefficients, + 0, + this.coefficients.length); + } + } else { + this.coefficients = coefficients; + } + } + + int[] getCoefficients() { + return coefficients; + } + + /** + * @return degree of this polynomial + */ + int getDegree() { + return coefficients.length - 1; + } + + /** + * @return true iff this polynomial is the monomial "0" + */ + boolean isZero() { + return coefficients[0] == 0; + } + + /** + * @return coefficient of x^degree term in this polynomial + */ + int getCoefficient(int degree) { + return coefficients[coefficients.length - 1 - degree]; + } + + /** + * @return evaluation of this polynomial at a given point + */ + int evaluateAt(int a) { + if (a == 0) { + // Just return the x^0 coefficient + return getCoefficient(0); + } + int size = coefficients.length; + if (a == 1) { + // Just the sum of the coefficients + int result = 0; + for (int coefficient : coefficients) { + result = GenericGF.addOrSubtract(result, coefficient); + } + return result; + } + int result = coefficients[0]; + for (int i = 1; i < size; i++) { + result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); + } + return result; + } + + GenericGFPoly addOrSubtract(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero()) { + return other; + } + if (other.isZero()) { + return this; + } + + int[] smallerCoefficients = this.coefficients; + int[] largerCoefficients = other.coefficients; + if (smallerCoefficients.length > largerCoefficients.length) { + int[] temp = smallerCoefficients; + smallerCoefficients = largerCoefficients; + largerCoefficients = temp; + } + int[] sumDiff = new int[largerCoefficients.length]; + int lengthDiff = largerCoefficients.length - smallerCoefficients.length; + // Copy high-order terms only found in higher-degree polynomial's coefficients + System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); + + for (int i = lengthDiff; i < largerCoefficients.length; i++) { + sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } + + return new GenericGFPoly(field, sumDiff); + } + + GenericGFPoly multiply(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero() || other.isZero()) { + return field.getZero(); + } + int[] aCoefficients = this.coefficients; + int aLength = aCoefficients.length; + int[] bCoefficients = other.coefficients; + int bLength = bCoefficients.length; + int[] product = new int[aLength + bLength - 1]; + for (int i = 0; i < aLength; i++) { + int aCoeff = aCoefficients[i]; + for (int j = 0; j < bLength; j++) { + product[i + j] = GenericGF.addOrSubtract(product[i + j], + field.multiply(aCoeff, bCoefficients[j])); + } + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly multiply(int scalar) { + if (scalar == 0) { + return field.getZero(); + } + if (scalar == 1) { + return this; + } + int size = coefficients.length; + int[] product = new int[size]; + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], scalar); + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly multiplyByMonomial(int degree, int coefficient) { + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return field.getZero(); + } + int size = coefficients.length; + int[] product = new int[size + degree]; + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], coefficient); + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly[] divide(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (other.isZero()) { + throw new IllegalArgumentException("Divide by 0"); + } + + GenericGFPoly quotient = field.getZero(); + GenericGFPoly remainder = this; + + int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); + int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); + + while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { + int degreeDifference = remainder.getDegree() - other.getDegree(); + int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); + GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale); + GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale); + quotient = quotient.addOrSubtract(iterationQuotient); + remainder = remainder.addOrSubtract(term); + } + + return new GenericGFPoly[] { quotient, remainder }; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(8 * getDegree()); + for (int degree = getDegree(); degree >= 0; degree--) { + int coefficient = getCoefficient(degree); + if (coefficient != 0) { + if (coefficient < 0) { + result.append(" - "); + coefficient = -coefficient; + } else { + if (result.length() > 0) { + result.append(" + "); + } + } + if (degree == 0 || coefficient != 1) { + int alphaPower = field.log(coefficient); + if (alphaPower == 0) { + result.append('1'); + } else if (alphaPower == 1) { + result.append('a'); + } else { + result.append("a^"); + result.append(alphaPower); + } + } + if (degree != 0) { + if (degree == 1) { + result.append('x'); + } else { + result.append("x^"); + result.append(degree); + } + } + } + } + return result.toString(); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java new file mode 100644 index 0000000000..ff813cb1ff --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java @@ -0,0 +1,74 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common.reedsolomon; + +import java.util.ArrayList; +import java.util.List; + +/** + * <p>Implements Reed-Solomon enbcoding, as the name implies.</p> + * + * @author Sean Owen + * @author William Rucklidge + */ +public final class ReedSolomonEncoder { + + private final GenericGF field; + private final List<GenericGFPoly> cachedGenerators; + + public ReedSolomonEncoder(GenericGF field) { + this.field = field; + this.cachedGenerators = new ArrayList<>(); + cachedGenerators.add(new GenericGFPoly(field, new int[]{1})); + } + + private GenericGFPoly buildGenerator(int degree) { + if (degree >= cachedGenerators.size()) { + GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1); + for (int d = cachedGenerators.size(); d <= degree; d++) { + GenericGFPoly nextGenerator = lastGenerator.multiply( + new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) })); + cachedGenerators.add(nextGenerator); + lastGenerator = nextGenerator; + } + } + return cachedGenerators.get(degree); + } + + public void encode(int[] toEncode, int ecBytes) { + if (ecBytes == 0) { + throw new IllegalArgumentException("No error correction bytes"); + } + int dataBytes = toEncode.length - ecBytes; + if (dataBytes <= 0) { + throw new IllegalArgumentException("No data bytes provided"); + } + GenericGFPoly generator = buildGenerator(ecBytes); + int[] infoCoefficients = new int[dataBytes]; + System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); + GenericGFPoly info = new GenericGFPoly(field, infoCoefficients); + info = info.multiplyByMonomial(ecBytes, 1); + GenericGFPoly remainder = info.divide(generator)[1]; + int[] coefficients = remainder.getCoefficients(); + int numZeroCoefficients = ecBytes - coefficients.length; + for (int i = 0; i < numZeroCoefficients; i++) { + toEncode[dataBytes + i] = 0; + } + System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java new file mode 100644 index 0000000000..ce7ee0736b --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java @@ -0,0 +1,118 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.Writer; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.encoder.ByteMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.google.zxing.qrcode.encoder.Encoder; +import com.google.zxing.qrcode.encoder.QRCode; + +import java.util.Map; + +/** + * This object renders a QR Code as a BitMatrix 2D array of greyscale values. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class QRCodeWriter implements Writer { + + private static final int QUIET_ZONE_SIZE = 4; + + @Override + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) + throws WriterException { + + return encode(contents, format, width, height, null); + } + + @Override + public BitMatrix encode(String contents, + BarcodeFormat format, + int width, + int height, + Map<EncodeHintType,?> hints) throws WriterException { + + if (contents.isEmpty()) { + throw new IllegalArgumentException("Found empty contents"); + } + + if (format != BarcodeFormat.QR_CODE) { + throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format); + } + + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' + + height); + } + + ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L; + int quietZone = QUIET_ZONE_SIZE; + if (hints != null) { + if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) { + errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString()); + } + if (hints.containsKey(EncodeHintType.MARGIN)) { + quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString()); + } + } + + QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints); + return renderResult(code, width, height, quietZone); + } + + // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses + // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap). + private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) { + ByteMatrix input = code.getMatrix(); + if (input == null) { + throw new IllegalStateException(); + } + int inputWidth = input.getWidth(); + int inputHeight = input.getHeight(); + int qrWidth = inputWidth + (quietZone * 2); + int qrHeight = inputHeight + (quietZone * 2); + int outputWidth = Math.max(width, qrWidth); + int outputHeight = Math.max(height, qrHeight); + + int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight); + // Padding includes both the quiet zone and the extra white pixels to accommodate the requested + // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone. + // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will + // handle all the padding from 100x100 (the actual QR) up to 200x160. + int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; + int topPadding = (outputHeight - (inputHeight * multiple)) / 2; + + BitMatrix output = new BitMatrix(outputWidth, outputHeight); + + for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) { + // Write the contents of this row of the barcode + for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) { + if (input.get(inputX, inputY) == 1) { + output.setRegion(outputX, outputY, multiple, multiple); + } + } + } + + return output; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java new file mode 100644 index 0000000000..0f1e113877 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java @@ -0,0 +1,60 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.decoder; + +/** + * <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels + * defined by the QR code standard.</p> + * + * @author Sean Owen + */ +public enum ErrorCorrectionLevel { + + /** L = ~7% correction */ + L(0x01), + /** M = ~15% correction */ + M(0x00), + /** Q = ~25% correction */ + Q(0x03), + /** H = ~30% correction */ + H(0x02); + + private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q}; + + private final int bits; + + ErrorCorrectionLevel(int bits) { + this.bits = bits; + } + + public int getBits() { + return bits; + } + + /** + * @param bits int containing the two bits encoding a QR Code's error correction level + * @return ErrorCorrectionLevel representing the encoded error correction level + */ + public static ErrorCorrectionLevel forBits(int bits) { + if (bits < 0 || bits >= FOR_BITS.length) { + throw new IllegalArgumentException(); + } + return FOR_BITS[bits]; + } + + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java new file mode 100644 index 0000000000..894e321754 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java @@ -0,0 +1,172 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.decoder; + +/** + * <p>Encapsulates a QR Code's format information, including the data mask used and + * error correction level.</p> + * + * @author Sean Owen + * @see DataMask + * @see ErrorCorrectionLevel + */ +final class FormatInformation { + + private static final int FORMAT_INFO_MASK_QR = 0x5412; + + /** + * See ISO 18004:2006, Annex C, Table C.1 + */ + private static final int[][] FORMAT_INFO_DECODE_LOOKUP = { + {0x5412, 0x00}, + {0x5125, 0x01}, + {0x5E7C, 0x02}, + {0x5B4B, 0x03}, + {0x45F9, 0x04}, + {0x40CE, 0x05}, + {0x4F97, 0x06}, + {0x4AA0, 0x07}, + {0x77C4, 0x08}, + {0x72F3, 0x09}, + {0x7DAA, 0x0A}, + {0x789D, 0x0B}, + {0x662F, 0x0C}, + {0x6318, 0x0D}, + {0x6C41, 0x0E}, + {0x6976, 0x0F}, + {0x1689, 0x10}, + {0x13BE, 0x11}, + {0x1CE7, 0x12}, + {0x19D0, 0x13}, + {0x0762, 0x14}, + {0x0255, 0x15}, + {0x0D0C, 0x16}, + {0x083B, 0x17}, + {0x355F, 0x18}, + {0x3068, 0x19}, + {0x3F31, 0x1A}, + {0x3A06, 0x1B}, + {0x24B4, 0x1C}, + {0x2183, 0x1D}, + {0x2EDA, 0x1E}, + {0x2BED, 0x1F}, + }; + + /** + * Offset i holds the number of 1 bits in the binary representation of i + */ + private static final int[] BITS_SET_IN_HALF_BYTE = + {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + + private final ErrorCorrectionLevel errorCorrectionLevel; + private final byte dataMask; + + private FormatInformation(int formatInfo) { + // Bits 3,4 + errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); + // Bottom 3 bits + dataMask = (byte) (formatInfo & 0x07); + } + + static int numBitsDiffering(int a, int b) { + a ^= b; // a now has a 1 bit exactly where its bit differs with b's + // Count bits set quickly with a series of lookups: + return BITS_SET_IN_HALF_BYTE[a & 0x0F] + + BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)]; + } + + /** + * @param maskedFormatInfo1 format info indicator, with mask still applied + * @param maskedFormatInfo2 second copy of same info; both are checked at the same time + * to establish best match + * @return information about the format it specifies, or {@code null} + * if doesn't seem to match any known pattern + */ + static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { + FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2); + if (formatInfo != null) { + return formatInfo; + } + // Should return null, but, some QR codes apparently + // do not mask this info. Try again by actually masking the pattern + // first + return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR, + maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR); + } + + private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { + // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing + int bestDifference = Integer.MAX_VALUE; + int bestFormatInfo = 0; + for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) { + int targetInfo = decodeInfo[0]; + if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) { + // Found an exact match + return new FormatInformation(decodeInfo[1]); + } + int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo); + if (bitsDifference < bestDifference) { + bestFormatInfo = decodeInfo[1]; + bestDifference = bitsDifference; + } + if (maskedFormatInfo1 != maskedFormatInfo2) { + // also try the other option + bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo); + if (bitsDifference < bestDifference) { + bestFormatInfo = decodeInfo[1]; + bestDifference = bitsDifference; + } + } + } + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits + // differing means we found a match + if (bestDifference <= 3) { + return new FormatInformation(bestFormatInfo); + } + return null; + } + + ErrorCorrectionLevel getErrorCorrectionLevel() { + return errorCorrectionLevel; + } + + byte getDataMask() { + return dataMask; + } + + @Override + public int hashCode() { + return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof FormatInformation)) { + return false; + } + FormatInformation other = (FormatInformation) o; + return this.errorCorrectionLevel == other.errorCorrectionLevel && + this.dataMask == other.dataMask; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java new file mode 100644 index 0000000000..b7e9ab3a91 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java @@ -0,0 +1,102 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.decoder; + +/** + * <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which + * data can be encoded to bits in the QR code standard.</p> + * + * @author Sean Owen + */ +public enum Mode { + + TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode... + NUMERIC(new int[]{10, 12, 14}, 0x01), + ALPHANUMERIC(new int[]{9, 11, 13}, 0x02), + STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported + BYTE(new int[]{8, 16, 16}, 0x04), + ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply + KANJI(new int[]{8, 10, 12}, 0x08), + FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05), + FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09), + /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ + HANZI(new int[]{8, 10, 12}, 0x0D); + + private final int[] characterCountBitsForVersions; + private final int bits; + + Mode(int[] characterCountBitsForVersions, int bits) { + this.characterCountBitsForVersions = characterCountBitsForVersions; + this.bits = bits; + } + + /** + * @param bits four bits encoding a QR Code data mode + * @return Mode encoded by these bits + * @throws IllegalArgumentException if bits do not correspond to a known mode + */ + public static Mode forBits(int bits) { + switch (bits) { + case 0x0: + return TERMINATOR; + case 0x1: + return NUMERIC; + case 0x2: + return ALPHANUMERIC; + case 0x3: + return STRUCTURED_APPEND; + case 0x4: + return BYTE; + case 0x5: + return FNC1_FIRST_POSITION; + case 0x7: + return ECI; + case 0x8: + return KANJI; + case 0x9: + return FNC1_SECOND_POSITION; + case 0xD: + // 0xD is defined in GBT 18284-2000, may not be supported in foreign country + return HANZI; + default: + throw new IllegalArgumentException(); + } + } + + /** + * @param version version in question + * @return number of bits used, in this QR Code symbol {@link Version}, to encode the + * count of characters that will follow encoded in this Mode + */ + public int getCharacterCountBits(Version version) { + int number = version.getVersionNumber(); + int offset; + if (number <= 9) { + offset = 0; + } else if (number <= 26) { + offset = 1; + } else { + offset = 2; + } + return characterCountBitsForVersions[offset]; + } + + public int getBits() { + return bits; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java new file mode 100644 index 0000000000..cc4052508c --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java @@ -0,0 +1,578 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.decoder; + +import com.google.zxing.FormatException; +import com.google.zxing.common.BitMatrix; + +/** + * See ISO 18004:2006 Annex D + * + * @author Sean Owen + */ +public final class Version { + + /** + * See ISO 18004:2006 Annex D. + * Element i represents the raw version bits that specify version i + 7 + */ + private static final int[] VERSION_DECODE_INFO = { + 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, + 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, + 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, + 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, + 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, + 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, + 0x2542E, 0x26A64, 0x27541, 0x28C69 + }; + + private static final Version[] VERSIONS = buildVersions(); + + private final int versionNumber; + private final int[] alignmentPatternCenters; + private final ECBlocks[] ecBlocks; + private final int totalCodewords; + + private Version(int versionNumber, + int[] alignmentPatternCenters, + ECBlocks... ecBlocks) { + this.versionNumber = versionNumber; + this.alignmentPatternCenters = alignmentPatternCenters; + this.ecBlocks = ecBlocks; + int total = 0; + int ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); + ECB[] ecbArray = ecBlocks[0].getECBlocks(); + for (ECB ecBlock : ecbArray) { + total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); + } + this.totalCodewords = total; + } + + public int getVersionNumber() { + return versionNumber; + } + + public int[] getAlignmentPatternCenters() { + return alignmentPatternCenters; + } + + public int getTotalCodewords() { + return totalCodewords; + } + + public int getDimensionForVersion() { + return 17 + 4 * versionNumber; + } + + public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) { + return ecBlocks[ecLevel.ordinal()]; + } + + /** + * <p>Deduces version information purely from QR Code dimensions.</p> + * + * @param dimension dimension in modules + * @return Version for a QR Code of that dimension + * @throws FormatException if dimension is not 1 mod 4 + */ + public static Version getProvisionalVersionForDimension(int dimension) throws FormatException { + if (dimension % 4 != 1) { + throw FormatException.getFormatInstance(); + } + try { + return getVersionForNumber((dimension - 17) / 4); + } catch (IllegalArgumentException ignored) { + throw FormatException.getFormatInstance(); + } + } + + public static Version getVersionForNumber(int versionNumber) { + if (versionNumber < 1 || versionNumber > 40) { + throw new IllegalArgumentException(); + } + return VERSIONS[versionNumber - 1]; + } + + static Version decodeVersionInformation(int versionBits) { + int bestDifference = Integer.MAX_VALUE; + int bestVersion = 0; + for (int i = 0; i < VERSION_DECODE_INFO.length; i++) { + int targetVersion = VERSION_DECODE_INFO[i]; + // Do the version info bits match exactly? done. + if (targetVersion == versionBits) { + return getVersionForNumber(i + 7); + } + // Otherwise see if this is the closest to a real version info bit string + // we have seen so far + int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); + if (bitsDifference < bestDifference) { + bestVersion = i + 7; + bestDifference = bitsDifference; + } + } + // We can tolerate up to 3 bits of error since no two version info codewords will + // differ in less than 8 bits. + if (bestDifference <= 3) { + return getVersionForNumber(bestVersion); + } + // If we didn't find a close enough match, fail + return null; + } + + /** + * See ISO 18004:2006 Annex E + */ + BitMatrix buildFunctionPattern() { + int dimension = getDimensionForVersion(); + BitMatrix bitMatrix = new BitMatrix(dimension); + + // Top left finder pattern + separator + format + bitMatrix.setRegion(0, 0, 9, 9); + // Top right finder pattern + separator + format + bitMatrix.setRegion(dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + bitMatrix.setRegion(0, dimension - 8, 9, 8); + + // Alignment patterns + int max = alignmentPatternCenters.length; + for (int x = 0; x < max; x++) { + int i = alignmentPatternCenters[x] - 2; + for (int y = 0; y < max; y++) { + if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { + // No alignment patterns near the three finder paterns + continue; + } + bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); + } + } + + // Vertical timing pattern + bitMatrix.setRegion(6, 9, 1, dimension - 17); + // Horizontal timing pattern + bitMatrix.setRegion(9, 6, dimension - 17, 1); + + if (versionNumber > 6) { + // Version info, top right + bitMatrix.setRegion(dimension - 11, 0, 3, 6); + // Version info, bottom left + bitMatrix.setRegion(0, dimension - 11, 6, 3); + } + + return bitMatrix; + } + + /** + * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will + * use blocks of differing sizes within one version, so, this encapsulates the parameters for + * each set of blocks. It also holds the number of error-correction codewords per block since it + * will be the same across all blocks within one version.</p> + */ + public static final class ECBlocks { + private final int ecCodewordsPerBlock; + private final ECB[] ecBlocks; + + ECBlocks(int ecCodewordsPerBlock, ECB... ecBlocks) { + this.ecCodewordsPerBlock = ecCodewordsPerBlock; + this.ecBlocks = ecBlocks; + } + + public int getECCodewordsPerBlock() { + return ecCodewordsPerBlock; + } + + public int getNumBlocks() { + int total = 0; + for (ECB ecBlock : ecBlocks) { + total += ecBlock.getCount(); + } + return total; + } + + public int getTotalECCodewords() { + return ecCodewordsPerBlock * getNumBlocks(); + } + + public ECB[] getECBlocks() { + return ecBlocks; + } + } + + /** + * <p>Encapsualtes the parameters for one error-correction block in one symbol version. + * This includes the number of data codewords, and the number of times a block with these + * parameters is used consecutively in the QR code version's format.</p> + */ + public static final class ECB { + private final int count; + private final int dataCodewords; + + ECB(int count, int dataCodewords) { + this.count = count; + this.dataCodewords = dataCodewords; + } + + public int getCount() { + return count; + } + + public int getDataCodewords() { + return dataCodewords; + } + } + + @Override + public String toString() { + return String.valueOf(versionNumber); + } + + /** + * See ISO 18004:2006 6.5.1 Table 9 + */ + private static Version[] buildVersions() { + return new Version[]{ + new Version(1, new int[]{}, + new ECBlocks(7, new ECB(1, 19)), + new ECBlocks(10, new ECB(1, 16)), + new ECBlocks(13, new ECB(1, 13)), + new ECBlocks(17, new ECB(1, 9))), + new Version(2, new int[]{6, 18}, + new ECBlocks(10, new ECB(1, 34)), + new ECBlocks(16, new ECB(1, 28)), + new ECBlocks(22, new ECB(1, 22)), + new ECBlocks(28, new ECB(1, 16))), + new Version(3, new int[]{6, 22}, + new ECBlocks(15, new ECB(1, 55)), + new ECBlocks(26, new ECB(1, 44)), + new ECBlocks(18, new ECB(2, 17)), + new ECBlocks(22, new ECB(2, 13))), + new Version(4, new int[]{6, 26}, + new ECBlocks(20, new ECB(1, 80)), + new ECBlocks(18, new ECB(2, 32)), + new ECBlocks(26, new ECB(2, 24)), + new ECBlocks(16, new ECB(4, 9))), + new Version(5, new int[]{6, 30}, + new ECBlocks(26, new ECB(1, 108)), + new ECBlocks(24, new ECB(2, 43)), + new ECBlocks(18, new ECB(2, 15), + new ECB(2, 16)), + new ECBlocks(22, new ECB(2, 11), + new ECB(2, 12))), + new Version(6, new int[]{6, 34}, + new ECBlocks(18, new ECB(2, 68)), + new ECBlocks(16, new ECB(4, 27)), + new ECBlocks(24, new ECB(4, 19)), + new ECBlocks(28, new ECB(4, 15))), + new Version(7, new int[]{6, 22, 38}, + new ECBlocks(20, new ECB(2, 78)), + new ECBlocks(18, new ECB(4, 31)), + new ECBlocks(18, new ECB(2, 14), + new ECB(4, 15)), + new ECBlocks(26, new ECB(4, 13), + new ECB(1, 14))), + new Version(8, new int[]{6, 24, 42}, + new ECBlocks(24, new ECB(2, 97)), + new ECBlocks(22, new ECB(2, 38), + new ECB(2, 39)), + new ECBlocks(22, new ECB(4, 18), + new ECB(2, 19)), + new ECBlocks(26, new ECB(4, 14), + new ECB(2, 15))), + new Version(9, new int[]{6, 26, 46}, + new ECBlocks(30, new ECB(2, 116)), + new ECBlocks(22, new ECB(3, 36), + new ECB(2, 37)), + new ECBlocks(20, new ECB(4, 16), + new ECB(4, 17)), + new ECBlocks(24, new ECB(4, 12), + new ECB(4, 13))), + new Version(10, new int[]{6, 28, 50}, + new ECBlocks(18, new ECB(2, 68), + new ECB(2, 69)), + new ECBlocks(26, new ECB(4, 43), + new ECB(1, 44)), + new ECBlocks(24, new ECB(6, 19), + new ECB(2, 20)), + new ECBlocks(28, new ECB(6, 15), + new ECB(2, 16))), + new Version(11, new int[]{6, 30, 54}, + new ECBlocks(20, new ECB(4, 81)), + new ECBlocks(30, new ECB(1, 50), + new ECB(4, 51)), + new ECBlocks(28, new ECB(4, 22), + new ECB(4, 23)), + new ECBlocks(24, new ECB(3, 12), + new ECB(8, 13))), + new Version(12, new int[]{6, 32, 58}, + new ECBlocks(24, new ECB(2, 92), + new ECB(2, 93)), + new ECBlocks(22, new ECB(6, 36), + new ECB(2, 37)), + new ECBlocks(26, new ECB(4, 20), + new ECB(6, 21)), + new ECBlocks(28, new ECB(7, 14), + new ECB(4, 15))), + new Version(13, new int[]{6, 34, 62}, + new ECBlocks(26, new ECB(4, 107)), + new ECBlocks(22, new ECB(8, 37), + new ECB(1, 38)), + new ECBlocks(24, new ECB(8, 20), + new ECB(4, 21)), + new ECBlocks(22, new ECB(12, 11), + new ECB(4, 12))), + new Version(14, new int[]{6, 26, 46, 66}, + new ECBlocks(30, new ECB(3, 115), + new ECB(1, 116)), + new ECBlocks(24, new ECB(4, 40), + new ECB(5, 41)), + new ECBlocks(20, new ECB(11, 16), + new ECB(5, 17)), + new ECBlocks(24, new ECB(11, 12), + new ECB(5, 13))), + new Version(15, new int[]{6, 26, 48, 70}, + new ECBlocks(22, new ECB(5, 87), + new ECB(1, 88)), + new ECBlocks(24, new ECB(5, 41), + new ECB(5, 42)), + new ECBlocks(30, new ECB(5, 24), + new ECB(7, 25)), + new ECBlocks(24, new ECB(11, 12), + new ECB(7, 13))), + new Version(16, new int[]{6, 26, 50, 74}, + new ECBlocks(24, new ECB(5, 98), + new ECB(1, 99)), + new ECBlocks(28, new ECB(7, 45), + new ECB(3, 46)), + new ECBlocks(24, new ECB(15, 19), + new ECB(2, 20)), + new ECBlocks(30, new ECB(3, 15), + new ECB(13, 16))), + new Version(17, new int[]{6, 30, 54, 78}, + new ECBlocks(28, new ECB(1, 107), + new ECB(5, 108)), + new ECBlocks(28, new ECB(10, 46), + new ECB(1, 47)), + new ECBlocks(28, new ECB(1, 22), + new ECB(15, 23)), + new ECBlocks(28, new ECB(2, 14), + new ECB(17, 15))), + new Version(18, new int[]{6, 30, 56, 82}, + new ECBlocks(30, new ECB(5, 120), + new ECB(1, 121)), + new ECBlocks(26, new ECB(9, 43), + new ECB(4, 44)), + new ECBlocks(28, new ECB(17, 22), + new ECB(1, 23)), + new ECBlocks(28, new ECB(2, 14), + new ECB(19, 15))), + new Version(19, new int[]{6, 30, 58, 86}, + new ECBlocks(28, new ECB(3, 113), + new ECB(4, 114)), + new ECBlocks(26, new ECB(3, 44), + new ECB(11, 45)), + new ECBlocks(26, new ECB(17, 21), + new ECB(4, 22)), + new ECBlocks(26, new ECB(9, 13), + new ECB(16, 14))), + new Version(20, new int[]{6, 34, 62, 90}, + new ECBlocks(28, new ECB(3, 107), + new ECB(5, 108)), + new ECBlocks(26, new ECB(3, 41), + new ECB(13, 42)), + new ECBlocks(30, new ECB(15, 24), + new ECB(5, 25)), + new ECBlocks(28, new ECB(15, 15), + new ECB(10, 16))), + new Version(21, new int[]{6, 28, 50, 72, 94}, + new ECBlocks(28, new ECB(4, 116), + new ECB(4, 117)), + new ECBlocks(26, new ECB(17, 42)), + new ECBlocks(28, new ECB(17, 22), + new ECB(6, 23)), + new ECBlocks(30, new ECB(19, 16), + new ECB(6, 17))), + new Version(22, new int[]{6, 26, 50, 74, 98}, + new ECBlocks(28, new ECB(2, 111), + new ECB(7, 112)), + new ECBlocks(28, new ECB(17, 46)), + new ECBlocks(30, new ECB(7, 24), + new ECB(16, 25)), + new ECBlocks(24, new ECB(34, 13))), + new Version(23, new int[]{6, 30, 54, 78, 102}, + new ECBlocks(30, new ECB(4, 121), + new ECB(5, 122)), + new ECBlocks(28, new ECB(4, 47), + new ECB(14, 48)), + new ECBlocks(30, new ECB(11, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(16, 15), + new ECB(14, 16))), + new Version(24, new int[]{6, 28, 54, 80, 106}, + new ECBlocks(30, new ECB(6, 117), + new ECB(4, 118)), + new ECBlocks(28, new ECB(6, 45), + new ECB(14, 46)), + new ECBlocks(30, new ECB(11, 24), + new ECB(16, 25)), + new ECBlocks(30, new ECB(30, 16), + new ECB(2, 17))), + new Version(25, new int[]{6, 32, 58, 84, 110}, + new ECBlocks(26, new ECB(8, 106), + new ECB(4, 107)), + new ECBlocks(28, new ECB(8, 47), + new ECB(13, 48)), + new ECBlocks(30, new ECB(7, 24), + new ECB(22, 25)), + new ECBlocks(30, new ECB(22, 15), + new ECB(13, 16))), + new Version(26, new int[]{6, 30, 58, 86, 114}, + new ECBlocks(28, new ECB(10, 114), + new ECB(2, 115)), + new ECBlocks(28, new ECB(19, 46), + new ECB(4, 47)), + new ECBlocks(28, new ECB(28, 22), + new ECB(6, 23)), + new ECBlocks(30, new ECB(33, 16), + new ECB(4, 17))), + new Version(27, new int[]{6, 34, 62, 90, 118}, + new ECBlocks(30, new ECB(8, 122), + new ECB(4, 123)), + new ECBlocks(28, new ECB(22, 45), + new ECB(3, 46)), + new ECBlocks(30, new ECB(8, 23), + new ECB(26, 24)), + new ECBlocks(30, new ECB(12, 15), + new ECB(28, 16))), + new Version(28, new int[]{6, 26, 50, 74, 98, 122}, + new ECBlocks(30, new ECB(3, 117), + new ECB(10, 118)), + new ECBlocks(28, new ECB(3, 45), + new ECB(23, 46)), + new ECBlocks(30, new ECB(4, 24), + new ECB(31, 25)), + new ECBlocks(30, new ECB(11, 15), + new ECB(31, 16))), + new Version(29, new int[]{6, 30, 54, 78, 102, 126}, + new ECBlocks(30, new ECB(7, 116), + new ECB(7, 117)), + new ECBlocks(28, new ECB(21, 45), + new ECB(7, 46)), + new ECBlocks(30, new ECB(1, 23), + new ECB(37, 24)), + new ECBlocks(30, new ECB(19, 15), + new ECB(26, 16))), + new Version(30, new int[]{6, 26, 52, 78, 104, 130}, + new ECBlocks(30, new ECB(5, 115), + new ECB(10, 116)), + new ECBlocks(28, new ECB(19, 47), + new ECB(10, 48)), + new ECBlocks(30, new ECB(15, 24), + new ECB(25, 25)), + new ECBlocks(30, new ECB(23, 15), + new ECB(25, 16))), + new Version(31, new int[]{6, 30, 56, 82, 108, 134}, + new ECBlocks(30, new ECB(13, 115), + new ECB(3, 116)), + new ECBlocks(28, new ECB(2, 46), + new ECB(29, 47)), + new ECBlocks(30, new ECB(42, 24), + new ECB(1, 25)), + new ECBlocks(30, new ECB(23, 15), + new ECB(28, 16))), + new Version(32, new int[]{6, 34, 60, 86, 112, 138}, + new ECBlocks(30, new ECB(17, 115)), + new ECBlocks(28, new ECB(10, 46), + new ECB(23, 47)), + new ECBlocks(30, new ECB(10, 24), + new ECB(35, 25)), + new ECBlocks(30, new ECB(19, 15), + new ECB(35, 16))), + new Version(33, new int[]{6, 30, 58, 86, 114, 142}, + new ECBlocks(30, new ECB(17, 115), + new ECB(1, 116)), + new ECBlocks(28, new ECB(14, 46), + new ECB(21, 47)), + new ECBlocks(30, new ECB(29, 24), + new ECB(19, 25)), + new ECBlocks(30, new ECB(11, 15), + new ECB(46, 16))), + new Version(34, new int[]{6, 34, 62, 90, 118, 146}, + new ECBlocks(30, new ECB(13, 115), + new ECB(6, 116)), + new ECBlocks(28, new ECB(14, 46), + new ECB(23, 47)), + new ECBlocks(30, new ECB(44, 24), + new ECB(7, 25)), + new ECBlocks(30, new ECB(59, 16), + new ECB(1, 17))), + new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150}, + new ECBlocks(30, new ECB(12, 121), + new ECB(7, 122)), + new ECBlocks(28, new ECB(12, 47), + new ECB(26, 48)), + new ECBlocks(30, new ECB(39, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(22, 15), + new ECB(41, 16))), + new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154}, + new ECBlocks(30, new ECB(6, 121), + new ECB(14, 122)), + new ECBlocks(28, new ECB(6, 47), + new ECB(34, 48)), + new ECBlocks(30, new ECB(46, 24), + new ECB(10, 25)), + new ECBlocks(30, new ECB(2, 15), + new ECB(64, 16))), + new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158}, + new ECBlocks(30, new ECB(17, 122), + new ECB(4, 123)), + new ECBlocks(28, new ECB(29, 46), + new ECB(14, 47)), + new ECBlocks(30, new ECB(49, 24), + new ECB(10, 25)), + new ECBlocks(30, new ECB(24, 15), + new ECB(46, 16))), + new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162}, + new ECBlocks(30, new ECB(4, 122), + new ECB(18, 123)), + new ECBlocks(28, new ECB(13, 46), + new ECB(32, 47)), + new ECBlocks(30, new ECB(48, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(42, 15), + new ECB(32, 16))), + new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166}, + new ECBlocks(30, new ECB(20, 117), + new ECB(4, 118)), + new ECBlocks(28, new ECB(40, 47), + new ECB(7, 48)), + new ECBlocks(30, new ECB(43, 24), + new ECB(22, 25)), + new ECBlocks(30, new ECB(10, 15), + new ECB(67, 16))), + new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170}, + new ECBlocks(30, new ECB(19, 118), + new ECB(6, 119)), + new ECBlocks(28, new ECB(18, 47), + new ECB(31, 48)), + new ECBlocks(30, new ECB(34, 24), + new ECB(34, 25)), + new ECBlocks(30, new ECB(20, 15), + new ECB(61, 16))) + }; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java new file mode 100644 index 0000000000..5714d9c3a4 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java @@ -0,0 +1,37 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +final class BlockPair { + + private final byte[] dataBytes; + private final byte[] errorCorrectionBytes; + + BlockPair(byte[] data, byte[] errorCorrection) { + dataBytes = data; + errorCorrectionBytes = errorCorrection; + } + + public byte[] getDataBytes() { + return dataBytes; + } + + public byte[] getErrorCorrectionBytes() { + return errorCorrectionBytes; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java new file mode 100644 index 0000000000..d7fef0601a --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java @@ -0,0 +1,98 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +/** + * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned + * -1, 0, and 1, I'm going to use less memory and go with bytes. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class ByteMatrix { + + private final byte[][] bytes; + private final int width; + private final int height; + + public ByteMatrix(int width, int height) { + bytes = new byte[height][width]; + this.width = width; + this.height = height; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public byte get(int x, int y) { + return bytes[y][x]; + } + + /** + * @return an internal representation as bytes, in row-major order. array[y][x] represents point (x,y) + */ + public byte[][] getArray() { + return bytes; + } + + public void set(int x, int y, byte value) { + bytes[y][x] = value; + } + + public void set(int x, int y, int value) { + bytes[y][x] = (byte) value; + } + + public void set(int x, int y, boolean value) { + bytes[y][x] = (byte) (value ? 1 : 0); + } + + public void clear(byte value) { + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + bytes[y][x] = value; + } + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(2 * width * height + 2); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + switch (bytes[y][x]) { + case 0: + result.append(" 0"); + break; + case 1: + result.append(" 1"); + break; + default: + result.append(" "); + break; + } + } + result.append('\n'); + } + return result.toString(); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java new file mode 100644 index 0000000000..013b1fa2d4 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java @@ -0,0 +1,578 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitArray; +import com.google.zxing.common.CharacterSetECI; +import com.google.zxing.common.reedsolomon.GenericGF; +import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.google.zxing.qrcode.decoder.Mode; +import com.google.zxing.qrcode.decoder.Version; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class Encoder { + + // The original table is defined in the table 5 of JISX0510:2004 (p.19). + private static final int[] ALPHANUMERIC_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f + }; + + static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; + + private Encoder() { + } + + // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. + // Basically it applies four rules and summate all penalties. + private static int calculateMaskPenalty(ByteMatrix matrix) { + return MaskUtil.applyMaskPenaltyRule1(matrix) + + MaskUtil.applyMaskPenaltyRule2(matrix) + + MaskUtil.applyMaskPenaltyRule3(matrix) + + MaskUtil.applyMaskPenaltyRule4(matrix); + } + + /** + * @param content text to encode + * @param ecLevel error correction level to use + * @return {@link QRCode} representing the encoded QR code + * @throws WriterException if encoding can't succeed, because of for example invalid content + * or configuration + */ + public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException { + return encode(content, ecLevel, null); + } + + public static QRCode encode(String content, + ErrorCorrectionLevel ecLevel, + Map<EncodeHintType,?> hints) throws WriterException { + + // Determine what character encoding has been specified by the caller, if any + String encoding = DEFAULT_BYTE_MODE_ENCODING; + if (hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET)) { + encoding = hints.get(EncodeHintType.CHARACTER_SET).toString(); + } + + // Pick an encoding mode appropriate for the content. Note that this will not attempt to use + // multiple modes / segments even if that were more efficient. Twould be nice. + Mode mode = chooseMode(content, encoding); + + // This will store the header information, like mode and + // length, as well as "header" segments like an ECI segment. + BitArray headerBits = new BitArray(); + + // Append ECI segment if applicable + if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) { + CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); + if (eci != null) { + appendECI(eci, headerBits); + } + } + + // (With ECI in place,) Write the mode marker + appendModeInfo(mode, headerBits); + + // Collect data within the main segment, separately, to count its size if needed. Don't add it to + // main payload yet. + BitArray dataBits = new BitArray(); + appendBytes(content, mode, dataBits, encoding); + + // Hard part: need to know version to know how many bits length takes. But need to know how many + // bits it takes to know version. First we take a guess at version by assuming version will be + // the minimum, 1: + + int provisionalBitsNeeded = headerBits.getSize() + + mode.getCharacterCountBits(Version.getVersionForNumber(1)) + + dataBits.getSize(); + Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); + + // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. + + int bitsNeeded = headerBits.getSize() + + mode.getCharacterCountBits(provisionalVersion) + + dataBits.getSize(); + Version version = chooseVersion(bitsNeeded, ecLevel); + + BitArray headerAndDataBits = new BitArray(); + headerAndDataBits.appendBitArray(headerBits); + // Find "length" of main segment and write it + int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); + appendLengthInfo(numLetters, version, mode, headerAndDataBits); + // Put data together into the overall payload + headerAndDataBits.appendBitArray(dataBits); + + Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); + int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); + + // Terminate the bits properly. + terminateBits(numDataBytes, headerAndDataBits); + + // Interleave data bits with error correction code. + BitArray finalBits = interleaveWithECBytes(headerAndDataBits, + version.getTotalCodewords(), + numDataBytes, + ecBlocks.getNumBlocks()); + + QRCode qrCode = new QRCode(); + + qrCode.setECLevel(ecLevel); + qrCode.setMode(mode); + qrCode.setVersion(version); + + // Choose the mask pattern and set to "qrCode". + int dimension = version.getDimensionForVersion(); + ByteMatrix matrix = new ByteMatrix(dimension, dimension); + int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); + qrCode.setMaskPattern(maskPattern); + + // Build the matrix and set it to "qrCode". + MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); + qrCode.setMatrix(matrix); + + return qrCode; + } + + /** + * @return the code point of the table used in alphanumeric mode or + * -1 if there is no corresponding code in the table. + */ + static int getAlphanumericCode(int code) { + if (code < ALPHANUMERIC_TABLE.length) { + return ALPHANUMERIC_TABLE[code]; + } + return -1; + } + + public static Mode chooseMode(String content) { + return chooseMode(content, null); + } + + /** + * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; + * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. + */ + private static Mode chooseMode(String content, String encoding) { + if ("Shift_JIS".equals(encoding) && isOnlyDoubleByteKanji(content)) { + // Choose Kanji mode if all input are double-byte characters + return Mode.KANJI; + } + boolean hasNumeric = false; + boolean hasAlphanumeric = false; + for (int i = 0; i < content.length(); ++i) { + char c = content.charAt(i); + if (c >= '0' && c <= '9') { + hasNumeric = true; + } else if (getAlphanumericCode(c) != -1) { + hasAlphanumeric = true; + } else { + return Mode.BYTE; + } + } + if (hasAlphanumeric) { + return Mode.ALPHANUMERIC; + } + if (hasNumeric) { + return Mode.NUMERIC; + } + return Mode.BYTE; + } + + private static boolean isOnlyDoubleByteKanji(String content) { + byte[] bytes; + try { + bytes = content.getBytes("Shift_JIS"); + } catch (UnsupportedEncodingException ignored) { + return false; + } + int length = bytes.length; + if (length % 2 != 0) { + return false; + } + for (int i = 0; i < length; i += 2) { + int byte1 = bytes[i] & 0xFF; + if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) { + return false; + } + } + return true; + } + + private static int chooseMaskPattern(BitArray bits, + ErrorCorrectionLevel ecLevel, + Version version, + ByteMatrix matrix) throws WriterException { + + int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. + int bestMaskPattern = -1; + // We try all mask patterns to choose the best one. + for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { + MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); + int penalty = calculateMaskPenalty(matrix); + if (penalty < minPenalty) { + minPenalty = penalty; + bestMaskPattern = maskPattern; + } + } + return bestMaskPattern; + } + + private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException { + // In the following comments, we use numbers of Version 7-H. + for (int versionNum = 1; versionNum <= 40; versionNum++) { + Version version = Version.getVersionForNumber(versionNum); + // numBytes = 196 + int numBytes = version.getTotalCodewords(); + // getNumECBytes = 130 + Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); + int numEcBytes = ecBlocks.getTotalECCodewords(); + // getNumDataBytes = 196 - 130 = 66 + int numDataBytes = numBytes - numEcBytes; + int totalInputBytes = (numInputBits + 7) / 8; + if (numDataBytes >= totalInputBytes) { + return version; + } + } + throw new WriterException("Data too big"); + } + + /** + * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). + */ + static void terminateBits(int numDataBytes, BitArray bits) throws WriterException { + int capacity = numDataBytes * 8; + if (bits.getSize() > capacity) { + throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + + capacity); + } + for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { + bits.appendBit(false); + } + // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. + // If the last byte isn't 8-bit aligned, we'll add padding bits. + int numBitsInLastByte = bits.getSize() & 0x07; + if (numBitsInLastByte > 0) { + for (int i = numBitsInLastByte; i < 8; i++) { + bits.appendBit(false); + } + } + // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). + int numPaddingBytes = numDataBytes - bits.getSizeInBytes(); + for (int i = 0; i < numPaddingBytes; ++i) { + bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8); + } + if (bits.getSize() != capacity) { + throw new WriterException("Bits size does not equal capacity"); + } + } + + /** + * Get number of data bytes and number of error correction bytes for block id "blockID". Store + * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of + * JISX0510:2004 (p.30) + */ + static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, + int numDataBytes, + int numRSBlocks, + int blockID, + int[] numDataBytesInBlock, + int[] numECBytesInBlock) throws WriterException { + if (blockID >= numRSBlocks) { + throw new WriterException("Block ID too large"); + } + // numRsBlocksInGroup2 = 196 % 5 = 1 + int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; + // numRsBlocksInGroup1 = 5 - 1 = 4 + int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; + // numTotalBytesInGroup1 = 196 / 5 = 39 + int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; + // numTotalBytesInGroup2 = 39 + 1 = 40 + int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; + // numDataBytesInGroup1 = 66 / 5 = 13 + int numDataBytesInGroup1 = numDataBytes / numRSBlocks; + // numDataBytesInGroup2 = 13 + 1 = 14 + int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; + // numEcBytesInGroup1 = 39 - 13 = 26 + int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; + // numEcBytesInGroup2 = 40 - 14 = 26 + int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; + // Sanity checks. + // 26 = 26 + if (numEcBytesInGroup1 != numEcBytesInGroup2) { + throw new WriterException("EC bytes mismatch"); + } + // 5 = 4 + 1. + if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) { + throw new WriterException("RS blocks mismatch"); + } + // 196 = (13 + 26) * 4 + (14 + 26) * 1 + if (numTotalBytes != + ((numDataBytesInGroup1 + numEcBytesInGroup1) * + numRsBlocksInGroup1) + + ((numDataBytesInGroup2 + numEcBytesInGroup2) * + numRsBlocksInGroup2)) { + throw new WriterException("Total bytes mismatch"); + } + + if (blockID < numRsBlocksInGroup1) { + numDataBytesInBlock[0] = numDataBytesInGroup1; + numECBytesInBlock[0] = numEcBytesInGroup1; + } else { + numDataBytesInBlock[0] = numDataBytesInGroup2; + numECBytesInBlock[0] = numEcBytesInGroup2; + } + } + + /** + * Interleave "bits" with corresponding error correction bytes. On success, store the result in + * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. + */ + static BitArray interleaveWithECBytes(BitArray bits, + int numTotalBytes, + int numDataBytes, + int numRSBlocks) throws WriterException { + + // "bits" must have "getNumDataBytes" bytes of data. + if (bits.getSizeInBytes() != numDataBytes) { + throw new WriterException("Number of bits and data bytes does not match"); + } + + // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll + // store the divided data bytes blocks and error correction bytes blocks into "blocks". + int dataBytesOffset = 0; + int maxNumDataBytes = 0; + int maxNumEcBytes = 0; + + // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. + Collection<BlockPair> blocks = new ArrayList<>(numRSBlocks); + + for (int i = 0; i < numRSBlocks; ++i) { + int[] numDataBytesInBlock = new int[1]; + int[] numEcBytesInBlock = new int[1]; + getNumDataBytesAndNumECBytesForBlockID( + numTotalBytes, numDataBytes, numRSBlocks, i, + numDataBytesInBlock, numEcBytesInBlock); + + int size = numDataBytesInBlock[0]; + byte[] dataBytes = new byte[size]; + bits.toBytes(8*dataBytesOffset, dataBytes, 0, size); + byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); + blocks.add(new BlockPair(dataBytes, ecBytes)); + + maxNumDataBytes = Math.max(maxNumDataBytes, size); + maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length); + dataBytesOffset += numDataBytesInBlock[0]; + } + if (numDataBytes != dataBytesOffset) { + throw new WriterException("Data bytes does not match offset"); + } + + BitArray result = new BitArray(); + + // First, place data blocks. + for (int i = 0; i < maxNumDataBytes; ++i) { + for (BlockPair block : blocks) { + byte[] dataBytes = block.getDataBytes(); + if (i < dataBytes.length) { + result.appendBits(dataBytes[i], 8); + } + } + } + // Then, place error correction blocks. + for (int i = 0; i < maxNumEcBytes; ++i) { + for (BlockPair block : blocks) { + byte[] ecBytes = block.getErrorCorrectionBytes(); + if (i < ecBytes.length) { + result.appendBits(ecBytes[i], 8); + } + } + } + if (numTotalBytes != result.getSizeInBytes()) { // Should be same. + throw new WriterException("Interleaving error: " + numTotalBytes + " and " + + result.getSizeInBytes() + " differ."); + } + + return result; + } + + static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) { + int numDataBytes = dataBytes.length; + int[] toEncode = new int[numDataBytes + numEcBytesInBlock]; + for (int i = 0; i < numDataBytes; i++) { + toEncode[i] = dataBytes[i] & 0xFF; + } + new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock); + + byte[] ecBytes = new byte[numEcBytesInBlock]; + for (int i = 0; i < numEcBytesInBlock; i++) { + ecBytes[i] = (byte) toEncode[numDataBytes + i]; + } + return ecBytes; + } + + /** + * Append mode info. On success, store the result in "bits". + */ + static void appendModeInfo(Mode mode, BitArray bits) { + bits.appendBits(mode.getBits(), 4); + } + + + /** + * Append length info. On success, store the result in "bits". + */ + static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException { + int numBits = mode.getCharacterCountBits(version); + if (numLetters >= (1 << numBits)) { + throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1)); + } + bits.appendBits(numLetters, numBits); + } + + /** + * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". + */ + static void appendBytes(String content, + Mode mode, + BitArray bits, + String encoding) throws WriterException { + switch (mode) { + case NUMERIC: + appendNumericBytes(content, bits); + break; + case ALPHANUMERIC: + appendAlphanumericBytes(content, bits); + break; + case BYTE: + append8BitBytes(content, bits, encoding); + break; + case KANJI: + appendKanjiBytes(content, bits); + break; + default: + throw new WriterException("Invalid mode: " + mode); + } + } + + static void appendNumericBytes(CharSequence content, BitArray bits) { + int length = content.length(); + int i = 0; + while (i < length) { + int num1 = content.charAt(i) - '0'; + if (i + 2 < length) { + // Encode three numeric letters in ten bits. + int num2 = content.charAt(i + 1) - '0'; + int num3 = content.charAt(i + 2) - '0'; + bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); + i += 3; + } else if (i + 1 < length) { + // Encode two numeric letters in seven bits. + int num2 = content.charAt(i + 1) - '0'; + bits.appendBits(num1 * 10 + num2, 7); + i += 2; + } else { + // Encode one numeric letter in four bits. + bits.appendBits(num1, 4); + i++; + } + } + } + + static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException { + int length = content.length(); + int i = 0; + while (i < length) { + int code1 = getAlphanumericCode(content.charAt(i)); + if (code1 == -1) { + throw new WriterException(); + } + if (i + 1 < length) { + int code2 = getAlphanumericCode(content.charAt(i + 1)); + if (code2 == -1) { + throw new WriterException(); + } + // Encode two alphanumeric letters in 11 bits. + bits.appendBits(code1 * 45 + code2, 11); + i += 2; + } else { + // Encode one alphanumeric letter in six bits. + bits.appendBits(code1, 6); + i++; + } + } + } + + static void append8BitBytes(String content, BitArray bits, String encoding) + throws WriterException { + byte[] bytes; + try { + bytes = content.getBytes(encoding); + } catch (UnsupportedEncodingException uee) { + throw new WriterException(uee); + } + for (byte b : bytes) { + bits.appendBits(b, 8); + } + } + + static void appendKanjiBytes(String content, BitArray bits) throws WriterException { + byte[] bytes; + try { + bytes = content.getBytes("Shift_JIS"); + } catch (UnsupportedEncodingException uee) { + throw new WriterException(uee); + } + int length = bytes.length; + for (int i = 0; i < length; i += 2) { + int byte1 = bytes[i] & 0xFF; + int byte2 = bytes[i + 1] & 0xFF; + int code = (byte1 << 8) | byte2; + int subtracted = -1; + if (code >= 0x8140 && code <= 0x9ffc) { + subtracted = code - 0x8140; + } else if (code >= 0xe040 && code <= 0xebbf) { + subtracted = code - 0xc140; + } + if (subtracted == -1) { + throw new WriterException("Invalid byte sequence"); + } + int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); + bits.appendBits(encoded, 13); + } + } + + private static void appendECI(CharacterSetECI eci, BitArray bits) { + bits.appendBits(Mode.ECI.getBits(), 4); + // This is correct for values up to 127, which is all we need now. + bits.appendBits(eci.getValue(), 8); + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java new file mode 100644 index 0000000000..b2204d4a97 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java @@ -0,0 +1,217 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +/** + * @author Satoru Takabayashi + * @author Daniel Switkin + * @author Sean Owen + */ +final class MaskUtil { + + // Penalty weights from section 6.8.2.1 + private static final int N1 = 3; + private static final int N2 = 3; + private static final int N3 = 40; + private static final int N4 = 10; + + private MaskUtil() { + // do nothing + } + + /** + * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and + * give penalty to them. Example: 00000 or 11111. + */ + static int applyMaskPenaltyRule1(ByteMatrix matrix) { + return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false); + } + + /** + * Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give + * penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a + * penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block. + */ + static int applyMaskPenaltyRule2(ByteMatrix matrix) { + int penalty = 0; + byte[][] array = matrix.getArray(); + int width = matrix.getWidth(); + int height = matrix.getHeight(); + for (int y = 0; y < height - 1; y++) { + for (int x = 0; x < width - 1; x++) { + int value = array[y][x]; + if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) { + penalty++; + } + } + } + return N2 * penalty; + } + + /** + * Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4 + * starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we + * find patterns like 000010111010000, we give penalty once. + */ + static int applyMaskPenaltyRule3(ByteMatrix matrix) { + int numPenalties = 0; + byte[][] array = matrix.getArray(); + int width = matrix.getWidth(); + int height = matrix.getHeight(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + byte[] arrayY = array[y]; // We can at least optimize this access + if (x + 6 < width && + arrayY[x] == 1 && + arrayY[x + 1] == 0 && + arrayY[x + 2] == 1 && + arrayY[x + 3] == 1 && + arrayY[x + 4] == 1 && + arrayY[x + 5] == 0 && + arrayY[x + 6] == 1 && + (isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) { + numPenalties++; + } + if (y + 6 < height && + array[y][x] == 1 && + array[y + 1][x] == 0 && + array[y + 2][x] == 1 && + array[y + 3][x] == 1 && + array[y + 4][x] == 1 && + array[y + 5][x] == 0 && + array[y + 6][x] == 1 && + (isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) { + numPenalties++; + } + } + } + return numPenalties * N3; + } + + private static boolean isWhiteHorizontal(byte[] rowArray, int from, int to) { + for (int i = from; i < to; i++) { + if (i >= 0 && i < rowArray.length && rowArray[i] == 1) { + return false; + } + } + return true; + } + + private static boolean isWhiteVertical(byte[][] array, int col, int from, int to) { + for (int i = from; i < to; i++) { + if (i >= 0 && i < array.length && array[i][col] == 1) { + return false; + } + } + return true; + } + + /** + * Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give + * penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. + */ + static int applyMaskPenaltyRule4(ByteMatrix matrix) { + int numDarkCells = 0; + byte[][] array = matrix.getArray(); + int width = matrix.getWidth(); + int height = matrix.getHeight(); + for (int y = 0; y < height; y++) { + byte[] arrayY = array[y]; + for (int x = 0; x < width; x++) { + if (arrayY[x] == 1) { + numDarkCells++; + } + } + } + int numTotalCells = matrix.getHeight() * matrix.getWidth(); + int fivePercentVariances = Math.abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells; + return fivePercentVariances * N4; + } + + /** + * Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask + * pattern conditions. + */ + static boolean getDataMaskBit(int maskPattern, int x, int y) { + int intermediate; + int temp; + switch (maskPattern) { + case 0: + intermediate = (y + x) & 0x1; + break; + case 1: + intermediate = y & 0x1; + break; + case 2: + intermediate = x % 3; + break; + case 3: + intermediate = (y + x) % 3; + break; + case 4: + intermediate = ((y / 2) + (x / 3)) & 0x1; + break; + case 5: + temp = y * x; + intermediate = (temp & 0x1) + (temp % 3); + break; + case 6: + temp = y * x; + intermediate = ((temp & 0x1) + (temp % 3)) & 0x1; + break; + case 7: + temp = y * x; + intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1; + break; + default: + throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern); + } + return intermediate == 0; + } + + /** + * Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both + * vertical and horizontal orders respectively. + */ + private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) { + int penalty = 0; + int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth(); + int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight(); + byte[][] array = matrix.getArray(); + for (int i = 0; i < iLimit; i++) { + int numSameBitCells = 0; + int prevBit = -1; + for (int j = 0; j < jLimit; j++) { + int bit = isHorizontal ? array[i][j] : array[j][i]; + if (bit == prevBit) { + numSameBitCells++; + } else { + if (numSameBitCells >= 5) { + penalty += N1 + (numSameBitCells - 5); + } + numSameBitCells = 1; // Include the cell itself. + prevBit = bit; + } + } + if (numSameBitCells >= 5) { + penalty += N1 + (numSameBitCells - 5); + } + } + return penalty; + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java new file mode 100644 index 0000000000..dac25f8de0 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java @@ -0,0 +1,485 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +import com.google.zxing.WriterException; +import com.google.zxing.common.BitArray; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.google.zxing.qrcode.decoder.Version; + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +final class MatrixUtil { + + private MatrixUtil() { + // do nothing + } + + private static final int[][] POSITION_DETECTION_PATTERN = { + {1, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 0, 1}, + {1, 0, 1, 1, 1, 0, 1}, + {1, 0, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 1}, + }; + + private static final int[][] POSITION_ADJUSTMENT_PATTERN = { + {1, 1, 1, 1, 1}, + {1, 0, 0, 0, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 0, 0, 1}, + {1, 1, 1, 1, 1}, + }; + + // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu. + private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = { + {-1, -1, -1, -1, -1, -1, -1}, // Version 1 + { 6, 18, -1, -1, -1, -1, -1}, // Version 2 + { 6, 22, -1, -1, -1, -1, -1}, // Version 3 + { 6, 26, -1, -1, -1, -1, -1}, // Version 4 + { 6, 30, -1, -1, -1, -1, -1}, // Version 5 + { 6, 34, -1, -1, -1, -1, -1}, // Version 6 + { 6, 22, 38, -1, -1, -1, -1}, // Version 7 + { 6, 24, 42, -1, -1, -1, -1}, // Version 8 + { 6, 26, 46, -1, -1, -1, -1}, // Version 9 + { 6, 28, 50, -1, -1, -1, -1}, // Version 10 + { 6, 30, 54, -1, -1, -1, -1}, // Version 11 + { 6, 32, 58, -1, -1, -1, -1}, // Version 12 + { 6, 34, 62, -1, -1, -1, -1}, // Version 13 + { 6, 26, 46, 66, -1, -1, -1}, // Version 14 + { 6, 26, 48, 70, -1, -1, -1}, // Version 15 + { 6, 26, 50, 74, -1, -1, -1}, // Version 16 + { 6, 30, 54, 78, -1, -1, -1}, // Version 17 + { 6, 30, 56, 82, -1, -1, -1}, // Version 18 + { 6, 30, 58, 86, -1, -1, -1}, // Version 19 + { 6, 34, 62, 90, -1, -1, -1}, // Version 20 + { 6, 28, 50, 72, 94, -1, -1}, // Version 21 + { 6, 26, 50, 74, 98, -1, -1}, // Version 22 + { 6, 30, 54, 78, 102, -1, -1}, // Version 23 + { 6, 28, 54, 80, 106, -1, -1}, // Version 24 + { 6, 32, 58, 84, 110, -1, -1}, // Version 25 + { 6, 30, 58, 86, 114, -1, -1}, // Version 26 + { 6, 34, 62, 90, 118, -1, -1}, // Version 27 + { 6, 26, 50, 74, 98, 122, -1}, // Version 28 + { 6, 30, 54, 78, 102, 126, -1}, // Version 29 + { 6, 26, 52, 78, 104, 130, -1}, // Version 30 + { 6, 30, 56, 82, 108, 134, -1}, // Version 31 + { 6, 34, 60, 86, 112, 138, -1}, // Version 32 + { 6, 30, 58, 86, 114, 142, -1}, // Version 33 + { 6, 34, 62, 90, 118, 146, -1}, // Version 34 + { 6, 30, 54, 78, 102, 126, 150}, // Version 35 + { 6, 24, 50, 76, 102, 128, 154}, // Version 36 + { 6, 28, 54, 80, 106, 132, 158}, // Version 37 + { 6, 32, 58, 84, 110, 136, 162}, // Version 38 + { 6, 26, 54, 82, 110, 138, 166}, // Version 39 + { 6, 30, 58, 86, 114, 142, 170}, // Version 40 + }; + + // Type info cells at the left top corner. + private static final int[][] TYPE_INFO_COORDINATES = { + {8, 0}, + {8, 1}, + {8, 2}, + {8, 3}, + {8, 4}, + {8, 5}, + {8, 7}, + {8, 8}, + {7, 8}, + {5, 8}, + {4, 8}, + {3, 8}, + {2, 8}, + {1, 8}, + {0, 8}, + }; + + // From Appendix D in JISX0510:2004 (p. 67) + private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101 + + // From Appendix C in JISX0510:2004 (p.65). + private static final int TYPE_INFO_POLY = 0x537; + private static final int TYPE_INFO_MASK_PATTERN = 0x5412; + + // Set all cells to -1. -1 means that the cell is empty (not set yet). + // + // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding + // with the ByteMatrix initialized all to zero. + static void clearMatrix(ByteMatrix matrix) { + matrix.clear((byte) -1); + } + + // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On + // success, store the result in "matrix" and return true. + static void buildMatrix(BitArray dataBits, + ErrorCorrectionLevel ecLevel, + Version version, + int maskPattern, + ByteMatrix matrix) throws WriterException { + clearMatrix(matrix); + embedBasicPatterns(version, matrix); + // Type information appear with any version. + embedTypeInfo(ecLevel, maskPattern, matrix); + // Version info appear if version >= 7. + maybeEmbedVersionInfo(version, matrix); + // Data should be embedded at end. + embedDataBits(dataBits, maskPattern, matrix); + } + + // Embed basic patterns. On success, modify the matrix and return true. + // The basic patterns are: + // - Position detection patterns + // - Timing patterns + // - Dark dot at the left bottom corner + // - Position adjustment patterns, if need be + static void embedBasicPatterns(Version version, ByteMatrix matrix) throws WriterException { + // Let's get started with embedding big squares at corners. + embedPositionDetectionPatternsAndSeparators(matrix); + // Then, embed the dark dot at the left bottom corner. + embedDarkDotAtLeftBottomCorner(matrix); + + // Position adjustment patterns appear if version >= 2. + maybeEmbedPositionAdjustmentPatterns(version, matrix); + // Timing patterns should be embedded after position adj. patterns. + embedTimingPatterns(matrix); + } + + // Embed type information. On success, modify the matrix. + static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix) + throws WriterException { + BitArray typeInfoBits = new BitArray(); + makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits); + + for (int i = 0; i < typeInfoBits.getSize(); ++i) { + // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in + // "typeInfoBits". + boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i); + + // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46). + int x1 = TYPE_INFO_COORDINATES[i][0]; + int y1 = TYPE_INFO_COORDINATES[i][1]; + matrix.set(x1, y1, bit); + + if (i < 8) { + // Right top corner. + int x2 = matrix.getWidth() - i - 1; + int y2 = 8; + matrix.set(x2, y2, bit); + } else { + // Left bottom corner. + int x2 = 8; + int y2 = matrix.getHeight() - 7 + (i - 8); + matrix.set(x2, y2, bit); + } + } + } + + // Embed version information if need be. On success, modify the matrix and return true. + // See 8.10 of JISX0510:2004 (p.47) for how to embed version information. + static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException { + if (version.getVersionNumber() < 7) { // Version info is necessary if version >= 7. + return; // Don't need version info. + } + BitArray versionInfoBits = new BitArray(); + makeVersionInfoBits(version, versionInfoBits); + + int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0. + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 3; ++j) { + // Place bits in LSB (least significant bit) to MSB order. + boolean bit = versionInfoBits.get(bitIndex); + bitIndex--; + // Left bottom corner. + matrix.set(i, matrix.getHeight() - 11 + j, bit); + // Right bottom corner. + matrix.set(matrix.getHeight() - 11 + j, i, bit); + } + } + } + + // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true. + // For debugging purposes, it skips masking process if "getMaskPattern" is -1. + // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. + static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix) + throws WriterException { + int bitIndex = 0; + int direction = -1; + // Start from the right bottom cell. + int x = matrix.getWidth() - 1; + int y = matrix.getHeight() - 1; + while (x > 0) { + // Skip the vertical timing pattern. + if (x == 6) { + x -= 1; + } + while (y >= 0 && y < matrix.getHeight()) { + for (int i = 0; i < 2; ++i) { + int xx = x - i; + // Skip the cell if it's not empty. + if (!isEmpty(matrix.get(xx, y))) { + continue; + } + boolean bit; + if (bitIndex < dataBits.getSize()) { + bit = dataBits.get(bitIndex); + ++bitIndex; + } else { + // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described + // in 8.4.9 of JISX0510:2004 (p. 24). + bit = false; + } + + // Skip masking if mask_pattern is -1. + if (maskPattern != -1 && MaskUtil.getDataMaskBit(maskPattern, xx, y)) { + bit = !bit; + } + matrix.set(xx, y, bit); + } + y += direction; + } + direction = -direction; // Reverse the direction. + y += direction; + x -= 2; // Move to the left. + } + // All bits should be consumed. + if (bitIndex != dataBits.getSize()) { + throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize()); + } + } + + // Return the position of the most significant bit set (to one) in the "value". The most + // significant bit is position 32. If there is no bit set, return 0. Examples: + // - findMSBSet(0) => 0 + // - findMSBSet(1) => 1 + // - findMSBSet(255) => 8 + static int findMSBSet(int value) { + int numDigits = 0; + while (value != 0) { + value >>>= 1; + ++numDigits; + } + return numDigits; + } + + // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH + // code is used for encoding type information and version information. + // Example: Calculation of version information of 7. + // f(x) is created from 7. + // - 7 = 000111 in 6 bits + // - f(x) = x^2 + x^1 + x^0 + // g(x) is given by the standard (p. 67) + // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1 + // Multiply f(x) by x^(18 - 6) + // - f'(x) = f(x) * x^(18 - 6) + // - f'(x) = x^14 + x^13 + x^12 + // Calculate the remainder of f'(x) / g(x) + // x^2 + // __________________________________________________ + // g(x) )x^14 + x^13 + x^12 + // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2 + // -------------------------------------------------- + // x^11 + x^10 + x^7 + x^4 + x^2 + // + // The remainder is x^11 + x^10 + x^7 + x^4 + x^2 + // Encode it in binary: 110010010100 + // The return value is 0xc94 (1100 1001 0100) + // + // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit + // operations. We don't care if cofficients are positive or negative. + static int calculateBCHCode(int value, int poly) { + if (poly == 0) { + throw new IllegalArgumentException("0 polynomial"); + } + // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1 + // from 13 to make it 12. + int msbSetInPoly = findMSBSet(poly); + value <<= msbSetInPoly - 1; + // Do the division business using exclusive-or operations. + while (findMSBSet(value) >= msbSetInPoly) { + value ^= poly << (findMSBSet(value) - msbSetInPoly); + } + // Now the "value" is the remainder (i.e. the BCH code) + return value; + } + + // Make bit vector of type information. On success, store the result in "bits" and return true. + // Encode error correction level and mask pattern. See 8.9 of + // JISX0510:2004 (p.45) for details. + static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits) + throws WriterException { + if (!QRCode.isValidMaskPattern(maskPattern)) { + throw new WriterException("Invalid mask pattern"); + } + int typeInfo = (ecLevel.getBits() << 3) | maskPattern; + bits.appendBits(typeInfo, 5); + + int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY); + bits.appendBits(bchCode, 10); + + BitArray maskBits = new BitArray(); + maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15); + bits.xor(maskBits); + + if (bits.getSize() != 15) { // Just in case. + throw new WriterException("should not happen but we got: " + bits.getSize()); + } + } + + // Make bit vector of version information. On success, store the result in "bits" and return true. + // See 8.10 of JISX0510:2004 (p.45) for details. + static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException { + bits.appendBits(version.getVersionNumber(), 6); + int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY); + bits.appendBits(bchCode, 12); + + if (bits.getSize() != 18) { // Just in case. + throw new WriterException("should not happen but we got: " + bits.getSize()); + } + } + + // Check if "value" is empty. + private static boolean isEmpty(int value) { + return value == -1; + } + + private static void embedTimingPatterns(ByteMatrix matrix) { + // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical + // separation patterns (size 1). Thus, 8 = 7 + 1. + for (int i = 8; i < matrix.getWidth() - 8; ++i) { + int bit = (i + 1) % 2; + // Horizontal line. + if (isEmpty(matrix.get(i, 6))) { + matrix.set(i, 6, bit); + } + // Vertical line. + if (isEmpty(matrix.get(6, i))) { + matrix.set(6, i, bit); + } + } + } + + // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46) + private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException { + if (matrix.get(8, matrix.getHeight() - 8) == 0) { + throw new WriterException(); + } + matrix.set(8, matrix.getHeight() - 8, 1); + } + + private static void embedHorizontalSeparationPattern(int xStart, + int yStart, + ByteMatrix matrix) throws WriterException { + for (int x = 0; x < 8; ++x) { + if (!isEmpty(matrix.get(xStart + x, yStart))) { + throw new WriterException(); + } + matrix.set(xStart + x, yStart, 0); + } + } + + private static void embedVerticalSeparationPattern(int xStart, + int yStart, + ByteMatrix matrix) throws WriterException { + for (int y = 0; y < 7; ++y) { + if (!isEmpty(matrix.get(xStart, yStart + y))) { + throw new WriterException(); + } + matrix.set(xStart, yStart + y, 0); + } + } + + // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are + // almost identical, since we cannot write a function that takes 2D arrays in different sizes in + // C/C++. We should live with the fact. + private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) { + for (int y = 0; y < 5; ++y) { + for (int x = 0; x < 5; ++x) { + matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]); + } + } + } + + private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) { + for (int y = 0; y < 7; ++y) { + for (int x = 0; x < 7; ++x) { + matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]); + } + } + } + + // Embed position detection patterns and surrounding vertical/horizontal separators. + private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException { + // Embed three big squares at corners. + int pdpWidth = POSITION_DETECTION_PATTERN[0].length; + // Left top corner. + embedPositionDetectionPattern(0, 0, matrix); + // Right top corner. + embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix); + // Left bottom corner. + embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix); + + // Embed horizontal separation patterns around the squares. + int hspWidth = 8; + // Left top corner. + embedHorizontalSeparationPattern(0, hspWidth - 1, matrix); + // Right top corner. + embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth, + hspWidth - 1, matrix); + // Left bottom corner. + embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix); + + // Embed vertical separation patterns around the squares. + int vspSize = 7; + // Left top corner. + embedVerticalSeparationPattern(vspSize, 0, matrix); + // Right top corner. + embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix); + // Left bottom corner. + embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize, + matrix); + } + + // Embed position adjustment patterns if need be. + private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) { + if (version.getVersionNumber() < 2) { // The patterns appear if version >= 2 + return; + } + int index = version.getVersionNumber() - 1; + int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index]; + int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length; + for (int i = 0; i < numCoordinates; ++i) { + for (int j = 0; j < numCoordinates; ++j) { + int y = coordinates[i]; + int x = coordinates[j]; + if (x == -1 || y == -1) { + continue; + } + // If the cell is unset, we embed the position adjustment pattern here. + if (isEmpty(matrix.get(x, y))) { + // -2 is necessary since the x/y coordinates point to the center of the pattern, not the + // left top corner. + embedPositionAdjustmentPattern(x - 2, y - 2, matrix); + } + } + } + } + +} diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java new file mode 100644 index 0000000000..b560de92f3 --- /dev/null +++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java @@ -0,0 +1,108 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.qrcode.encoder; + +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.google.zxing.qrcode.decoder.Mode; +import com.google.zxing.qrcode.decoder.Version; + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class QRCode { + + public static final int NUM_MASK_PATTERNS = 8; + + private Mode mode; + private ErrorCorrectionLevel ecLevel; + private Version version; + private int maskPattern; + private ByteMatrix matrix; + + public QRCode() { + maskPattern = -1; + } + + public Mode getMode() { + return mode; + } + + public ErrorCorrectionLevel getECLevel() { + return ecLevel; + } + + public Version getVersion() { + return version; + } + + public int getMaskPattern() { + return maskPattern; + } + + public ByteMatrix getMatrix() { + return matrix; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(200); + result.append("<<\n"); + result.append(" mode: "); + result.append(mode); + result.append("\n ecLevel: "); + result.append(ecLevel); + result.append("\n version: "); + result.append(version); + result.append("\n maskPattern: "); + result.append(maskPattern); + if (matrix == null) { + result.append("\n matrix: null\n"); + } else { + result.append("\n matrix:\n"); + result.append(matrix); + } + result.append(">>\n"); + return result.toString(); + } + + public void setMode(Mode value) { + mode = value; + } + + public void setECLevel(ErrorCorrectionLevel value) { + ecLevel = value; + } + + public void setVersion(Version version) { + this.version = version; + } + + public void setMaskPattern(int value) { + maskPattern = value; + } + + public void setMatrix(ByteMatrix value) { + matrix = value; + } + + // Check if "mask_pattern" is valid. + public static boolean isValidMaskPattern(int maskPattern) { + return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS; + } + +} diff --git a/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java new file mode 100644 index 0000000000..d4a563da38 --- /dev/null +++ b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.j2se; + +import java.awt.image.BufferedImage; + +/** + * Encapsulates custom configuration used in methods of {@link MatrixToImageWriter}. + */ +public final class MatrixToImageConfig { + + public static final int BLACK = 0xFF000000; + public static final int WHITE = 0xFFFFFFFF; + + private final int onColor; + private final int offColor; + + /** + * Creates a default config with on color {@link #BLACK} and off color {@link #WHITE}, generating normal + * black-on-white barcodes. + */ + public MatrixToImageConfig() { + this(BLACK, WHITE); + } + + /** + * @param onColor pixel on color, specified as an ARGB value as an int + * @param offColor pixel off color, specified as an ARGB value as an int + */ + public MatrixToImageConfig(int onColor, int offColor) { + this.onColor = onColor; + this.offColor = offColor; + } + + public int getPixelOnColor() { + return onColor; + } + + public int getPixelOffColor() { + return offColor; + } + + int getBufferedImageColorModel() { + if (onColor == BLACK && offColor == WHITE) { + // Use faster BINARY if colors match default + return BufferedImage.TYPE_BYTE_BINARY; + } + if (hasTransparency(onColor) || hasTransparency(offColor)) { + // Use ARGB representation if colors specify non-opaque alpha + return BufferedImage.TYPE_INT_ARGB; + } + // Default otherwise to RGB representation with ignored alpha channel + return BufferedImage.TYPE_INT_RGB; + } + + private static boolean hasTransparency(int argb) { + return (argb & 0xFF000000) != 0xFF000000; + } + +} diff --git a/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java new file mode 100644 index 0000000000..07bdb81492 --- /dev/null +++ b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java @@ -0,0 +1,162 @@ +/* + * Copyright 2009 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.j2se; + +import com.google.zxing.common.BitMatrix; + +import javax.imageio.ImageIO; +import java.io.File; +import java.io.OutputStream; +import java.io.IOException; +import java.awt.image.BufferedImage; +import java.nio.file.Path; + +/** + * Writes a {@link BitMatrix} to {@link BufferedImage}, + * file or stream. Provided here instead of core since it depends on + * Java SE libraries. + * + * @author Sean Owen + */ +public final class MatrixToImageWriter { + + private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig(); + + private MatrixToImageWriter() {} + + /** + * Renders a {@link BitMatrix} as an image, where "false" bits are rendered + * as white, and "true" bits are rendered as black. Uses default configuration. + * + * @param matrix {@link BitMatrix} to write + * @return {@link BufferedImage} representation of the input + */ + public static BufferedImage toBufferedImage(BitMatrix matrix) { + return toBufferedImage(matrix, DEFAULT_CONFIG); + } + + /** + * As {@link #toBufferedImage(BitMatrix)}, but allows customization of the output. + * + * @param matrix {@link BitMatrix} to write + * @param config output configuration + * @return {@link BufferedImage} representation of the input + */ + public static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel()); + int onColor = config.getPixelOnColor(); + int offColor = config.getPixelOffColor(); + int[] pixels = new int[width * height]; + int index = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + pixels[index++] = matrix.get(x, y) ? onColor : offColor; + } + } + image.setRGB(0, 0, width, height, pixels, 0, width); + return image; + } + + /** + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param file file {@link File} to write image to + * @throws IOException if writes to the file fail + * @deprecated use {@link #writeToPath(BitMatrix, String, Path)} + */ + @Deprecated + public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException { + writeToPath(matrix, format, file.toPath()); + } + + /** + * Writes a {@link BitMatrix} to a file with default configuration. + * + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param file file {@link Path} to write image to + * @throws IOException if writes to the stream fail + * @see #toBufferedImage(BitMatrix) + */ + public static void writeToPath(BitMatrix matrix, String format, Path file) throws IOException { + writeToPath(matrix, format, file, DEFAULT_CONFIG); + } + + /** + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param file file {@link File} to write image to + * @param config output configuration + * @throws IOException if writes to the file fail + * @deprecated use {@link #writeToPath(BitMatrix, String, Path, MatrixToImageConfig)} + */ + @Deprecated + public static void writeToFile(BitMatrix matrix, String format, File file, MatrixToImageConfig config) + throws IOException { + writeToPath(matrix, format, file.toPath(), config); + } + + /** + * As {@link #writeToFile(BitMatrix, String, File)}, but allows customization of the output. + * + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param file file {@link Path} to write image to + * @param config output configuration + * @throws IOException if writes to the file fail + */ + public static void writeToPath(BitMatrix matrix, String format, Path file, MatrixToImageConfig config) + throws IOException { + BufferedImage image = toBufferedImage(matrix, config); + if (!ImageIO.write(image, format, file.toFile())) { + throw new IOException("Could not write an image of format " + format + " to " + file); + } + } + + /** + * Writes a {@link BitMatrix} to a stream with default configuration. + * + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param stream {@link OutputStream} to write image to + * @throws IOException if writes to the stream fail + * @see #toBufferedImage(BitMatrix) + */ + public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException { + writeToStream(matrix, format, stream, DEFAULT_CONFIG); + } + + /** + * As {@link #writeToStream(BitMatrix, String, OutputStream)}, but allows customization of the output. + * + * @param matrix {@link BitMatrix} to write + * @param format image format + * @param stream {@link OutputStream} to write image to + * @param config output configuration + * @throws IOException if writes to the stream fail + */ + public static void writeToStream(BitMatrix matrix, String format, OutputStream stream, MatrixToImageConfig config) + throws IOException { + BufferedImage image = toBufferedImage(matrix, config); + if (!ImageIO.write(image, format, stream)) { + throw new IOException("Could not write an image of format " + format); + } + } + +} -- GitLab