From f50305895d5c52df36ad11a959d5e5950c945db7 Mon Sep 17 00:00:00 2001 From: dswitkin Date: Thu, 13 Nov 2008 16:14:44 +0000 Subject: [PATCH] Began porting the QR Code encoder from ChartServer to Java/ZXing. Some important comments are flagged with JAVAPORT. I've done the following so far: - Created Java files with our headers, packages, etc. - Converted includes to imports, or commented out the unresolved dependencies - Merged all the .h and .cpp contents into Java classes - Fixed most of the formatting - Did all the simple transformations (bool, NULL, const, struct, string, ::, ->) - Created a Debug class to handle all the asserts and logging - Fixed about half of the static arrays - Removed some pthread cruft IMPORTANT: - Please do not start hacking this code up as I'm going to keep making large changes to it. In particular, we need to leave the trailing underscores on member variables for now. Once everything is compiling with no errors, we can revisit stylistic issues. - There will be a number of similar classes within the encoder and in the rest of ZXing. We should refactor those later (BitVector and BitArray come to mind). In the mean time, I want to get everything working and the tests passing before we do that. git-svn-id: https://zxing.googlecode.com/svn/trunk@694 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../zxing/qrcode/encoder/BitVector.java | 133 +++ .../google/zxing/qrcode/encoder/Debug.java | 59 ++ .../google/zxing/qrcode/encoder/Encoder.java | 905 ++++++++++++++++++ .../google/zxing/qrcode/encoder/MaskUtil.java | 219 +++++ .../zxing/qrcode/encoder/MatrixUtil.java | 588 ++++++++++++ .../google/zxing/qrcode/encoder/QRCode.java | 350 +++++++ .../com/google/zxing/qrcode/encoder/README | 5 + .../google/zxing/qrcode/encoder/Renderer.java | 183 ++++ 8 files changed, 2442 insertions(+) create mode 100644 core/src/com/google/zxing/qrcode/encoder/BitVector.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/Debug.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/Encoder.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/MaskUtil.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/QRCode.java create mode 100644 core/src/com/google/zxing/qrcode/encoder/README create mode 100644 core/src/com/google/zxing/qrcode/encoder/Renderer.java diff --git a/core/src/com/google/zxing/qrcode/encoder/BitVector.java b/core/src/com/google/zxing/qrcode/encoder/BitVector.java new file mode 100644 index 000000000..a0885134d --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/BitVector.java @@ -0,0 +1,133 @@ +/* + * 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 satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class BitVector { + + private int size_; + private String bytes_; + + public BitVector() { + size_ = 0; + } + + // Return the bit value at "index". + public int at(final int index) { + Debug.DCHECK_LE(0, index); + Debug.DCHECK_LT(index, size_); + final uint8 byte = bytes_.at(index / 8); + return (byte >> (7 - (index % 8))) & 1; + } + + // Return the number of bits in the bit vector. + public int size() { + return size_; + } + + // Return the number of bytes in the bit vector. + public int num_bytes() { + return size_ / 8; + } + + // Append one bit to the bit vector. + public void AppendBit(final int bit) { + Debug.DCHECK(bit == 0 || bit == 1); + final int num_bits_in_last_byte = size_ % 8; + // We'll expand bytes_ if we don't have bits in the last byte. + if (num_bits_in_last_byte == 0) { + bytes_.push_back(0); + } + // Modify the last byte. + bytes_[bytes_.size() - 1] |= (bit << (7 - num_bits_in_last_byte)); + ++size_; + } + + // Append "num_bits" bits in "value" to the bit vector. + // REQUIRES: 0<= num_bits <= 32. + // + // Examples: + // - AppendBits(0x00, 1) adds 0. + // - AppendBits(0x00, 4) adds 0000. + // - AppendBits(0xff, 8) adds 11111111. + public void AppendBits(final uint32 value, final int num_bits) { + Debug.DCHECK(num_bits >= 0 && num_bits <= 32); + int num_bits_left = num_bits; + while (num_bits_left > 0) { + // Optimization for byte-oriented appending. + if (size_ % 8 == 0 && num_bits_left >= 8) { + final uint8 byte = (value >> (num_bits_left - 8)) & 0xff; + bytes_.push_back(byte); + size_ += 8; + num_bits_left -= 8; + } else { + final int bit = (value >> (num_bits_left - 1)) & 1; + AppendBit(bit); + --num_bits_left; + } + } + } + + // Append "bytes". + public void AppendBytes(final StringPiece &bytes) { + for (int i = 0; i < bytes.size(); ++i) { + AppendBits(bytes[i], 8); + } + } + + // Append "bits". + public void AppendBitVector(final BitVector &bits) { + for (int i = 0; i < bits.size(); ++i) { + AppendBit(bits.at(i)); + } + } + + // Modify the bit vector by XOR'ing with "other" + public void XOR(final BitVector &other) { + Debug.DCHECK_EQ(size_, other.size()); + for (int i = 0; i < bytes_.size(); ++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. + bytes_[i] ^= other.ToString()[i]; + } + } + + // Return the content of the bit vector as String. + public final String &ToString() { + return bytes_; + } + + // Return String like "01110111" for debugging. + public String ToASCII() { + String result; + result.reserve(size_); + for (int i = 0; i < size_; ++i) { + if (at(i) == 0) { + result.append("0"); + } else if (at(i) == 1) { + result.append("1"); + } else { + Debug.DCHECK(false); + } + } + return result; + } + +} diff --git a/core/src/com/google/zxing/qrcode/encoder/Debug.java b/core/src/com/google/zxing/qrcode/encoder/Debug.java new file mode 100644 index 000000000..d90354c9e --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/Debug.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.qrcode.encoder; + +/** + * JAVAPORT: Equivalent methods to the C++ versions. It's debateable whether these should throw or + * assert. We can revisit that decision, or remove these throughout the code later. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public class Debug { + + public static final void LOG_ERROR(String message) { + // TODO: Implement + } + + public static final void LOG_INFO(String message) { + // TODO: Implement + } + + public static final void DCHECK(boolean condition) { + assert(condition); + } + + public static final void DCHECK_LT(int a, int b) { + assert(a < b); + } + + public static final void DCHECK_LE(int a, int b) { + assert(a <= b); + } + + public static final void DCHECK_GT(int a, int b) { + assert(a > b); + } + + public static final void DCHECK_GE(int a, int b) { + assert(a >= b); + } + + public static final void DCHECK_EQ(int a, int b) { + assert(a == b); + } + +} diff --git a/core/src/com/google/zxing/qrcode/encoder/Encoder.java b/core/src/com/google/zxing/qrcode/encoder/Encoder.java new file mode 100644 index 000000000..0db9c152b --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/Encoder.java @@ -0,0 +1,905 @@ +/* + * 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; + +// class GF_Poly; +// #include "util/array/array2d-inl.h" +// #include "strings/stringpiece.h" +// #include "util/reedsolomon/galois_field.h" +// #include "util/reedsolomon/galois_poly.h" + +/** + * @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). + static final int kAlphanumericTable[] = { + -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 + }; + + class RSBlockInfo { + int num_bytes; + class { + int num_ec_bytes; + int num_rs_blocks; + } block_info[4]; + }; + + static final RSBlockInfo kRSBlockTable[] = { + // The table is from table 12 of JISX0510:2004 (p. 30) + // The "block_info" parts are ordered by L, M, Q, H. + // The table was doublechecked by komatsu. + { 26, { { 7, 1}, { 10, 1}, { 13, 1}, { 17, 1}}}, // Version 1 + { 44, { { 10, 1}, { 16, 1}, { 22, 1}, { 28, 1}}}, // Version 2 + { 70, { { 15, 1}, { 26, 1}, { 36, 2}, { 44, 2}}}, // Version 3 + { 100, { { 20, 1}, { 36, 2}, { 52, 2}, { 64, 4}}}, // Version 4 + { 134, { { 26, 1}, { 48, 2}, { 72, 4}, { 88, 4}}}, // Version 5 + { 172, { { 36, 2}, { 64, 4}, { 96, 4}, { 112, 4}}}, // Version 6 + { 196, { { 40, 2}, { 72, 4}, { 108, 6}, { 130, 5}}}, // Version 7 + { 242, { { 48, 2}, { 88, 4}, { 132, 6}, { 156, 6}}}, // Version 8 + { 292, { { 60, 2}, { 110, 5}, { 160, 8}, { 192, 8}}}, // Version 9 + { 346, { { 72, 4}, { 130, 5}, { 192, 8}, { 224, 8}}}, // Version 10 + { 404, { { 80, 4}, { 150, 5}, { 224, 8}, { 264, 11}}}, // Version 11 + { 466, { { 96, 4}, { 176, 8}, { 260, 10}, { 308, 11}}}, // Version 12 + { 532, { {104, 4}, { 198, 9}, { 288, 12}, { 352, 16}}}, // Version 13 + { 581, { {120, 4}, { 216, 9}, { 320, 16}, { 384, 16}}}, // Version 14 + { 655, { {132, 6}, { 240, 10}, { 360, 12}, { 432, 18}}}, // Version 15 + { 733, { {144, 6}, { 280, 10}, { 408, 17}, { 480, 16}}}, // Version 16 + { 815, { {168, 6}, { 308, 11}, { 448, 16}, { 532, 19}}}, // Version 17 + { 901, { {180, 6}, { 338, 13}, { 504, 18}, { 588, 21}}}, // Version 18 + { 991, { {196, 7}, { 364, 14}, { 546, 21}, { 650, 25}}}, // Version 19 + {1085, { {224, 8}, { 416, 16}, { 600, 20}, { 700, 25}}}, // Version 20 + {1156, { {224, 8}, { 442, 17}, { 644, 23}, { 750, 25}}}, // Version 21 + {1258, { {252, 9}, { 476, 17}, { 690, 23}, { 816, 34}}}, // Version 22 + {1364, { {270, 9}, { 504, 18}, { 750, 25}, { 900, 30}}}, // Version 23 + {1474, { {300, 10}, { 560, 20}, { 810, 27}, { 960, 32}}}, // Version 24 + {1588, { {312, 12}, { 588, 21}, { 870, 29}, {1050, 35}}}, // Version 25 + {1706, { {336, 12}, { 644, 23}, { 952, 34}, {1110, 37}}}, // Version 26 + {1828, { {360, 12}, { 700, 25}, {1020, 34}, {1200, 40}}}, // Version 27 + {1921, { {390, 13}, { 728, 26}, {1050, 35}, {1260, 42}}}, // Version 28 + {2051, { {420, 14}, { 784, 28}, {1140, 38}, {1350, 45}}}, // Version 29 + {2185, { {450, 15}, { 812, 29}, {1200, 40}, {1440, 48}}}, // Version 30 + {2323, { {480, 16}, { 868, 31}, {1290, 43}, {1530, 51}}}, // Version 31 + {2465, { {510, 17}, { 924, 33}, {1350, 45}, {1620, 54}}}, // Version 32 + {2611, { {540, 18}, { 980, 35}, {1440, 48}, {1710, 57}}}, // Version 33 + {2761, { {570, 19}, {1036, 37}, {1530, 51}, {1800, 60}}}, // Version 34 + {2876, { {570, 19}, {1064, 38}, {1590, 53}, {1890, 63}}}, // Version 35 + {3034, { {600, 20}, {1120, 40}, {1680, 56}, {1980, 66}}}, // Version 36 + {3196, { {630, 21}, {1204, 43}, {1770, 59}, {2100, 70}}}, // Version 37 + {3362, { {660, 22}, {1260, 45}, {1860, 62}, {2220, 74}}}, // Version 38 + {3532, { {720, 24}, {1316, 47}, {1950, 65}, {2310, 77}}}, // Version 39 + {3706, { {750, 25}, {1372, 49}, {2040, 68}, {2430, 81}}}, // Version 40 + }; + + static final int kMaxNumECBytes = 68; // See the table in Appendix A. + class ECPolyInfo { + int ec_length; + int coeffs[kMaxNumECBytes + 1]; + }; + +// The numbers were generated using the logic found in +// http://www.d-project.com/qrcode/. We use generated numbers instead +// of the logic itself (don't want to copy it). The numbers are +// supposed to be identical to the ones in the table is from the table +// in Appendix A of JISX0510:2004 (p. 30). However, there are some + // cases the spec seems to be wrong. + static final ECPolyInfo kECPolynomials[] = { + { 7, + { 0, 87, 229, 146, 149, 238, 102, 21 }}, + // The spec lacks the coefficient for x^5 (a^46 x^5). + // Tested a QR code of Version 1-M (uses 10 error correction bytes) + // with a cell phone and it worked. + { 10, + { 0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45 }}, + { 13, + { 0, 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, + 140, 78 }}, + { 15, + { 0, 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, + 124, 5, 99, 105 }}, + { 16, + { 0, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, + 169, 182, 194, 225, 120 }}, + { 17, + { 0, 43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, + 99, 150, 39, 243, 163, 136 }}, + { 18, + { 0, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, + 148, 252, 179, 5, 98, 96, 153 }}, + { 20, + { 0, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, + 225, 83, 239, 156, 164, 212, 212, 188, 190 }}, + { 22, + { 0, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, + 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231 }}, + { 24, + { 0, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, + 152, 192, 226, 228, 218, 111, 0, 117, 232, 87, 96, 227, + 21 }}, + { 26, + { 0, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, + 28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153, + 145, 218, 70 }}, + { 28, + { 0, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, + 147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212, + 119, 242, 37, 9, 123 }}, + { 30, + { 0, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, + 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, + 37, 251, 216, 238, 40, 192, 180 }}, + // In the spec, the coefficient for x^10 is a^60 but we use the + // generated number a^69 instead (probably it's typo in the spec). + // + // Anyway, there seems to be no way that error correction bytes + // bigger than 30 can be used in RS blocks, according to the table + // 12. It's weird why the spec has numbers for error correction + // bytes of 32 and bigger in this table here. + { 32, + { 0, 10, 6, 106, 190, 249, 167, 4, 67, 209, 138, 138, + 32, 242, 123, 89, 27, 120, 185, 80, 156, 38, 69, 171, + 60, 28, 222, 80, 52, 254, 185, 220, 241 }}, + { 34, + { 0, 111, 77, 146, 94, 26, 21, 108, 19, 105, 94, 113, + 193, 86, 140, 163, 125, 58, 158, 229, 239, 218, 103, 56, + 70, 114, 61, 183, 129, 167, 13, 98, 62, 129, 51 }}, + { 36, + { 0, 200, 183, 98, 16, 172, 31, 246, 234, 60, 152, 115, + 0, 167, 152, 113, 248, 238, 107, 18, 63, 218, 37, 87, + 210, 105, 177, 120, 74, 121, 196, 117, 251, 113, 233, 30, + 120 }}, + // The spec doesn't have a row for 38 but just in case. + { 38, + { 0, 159, 34, 38, 228, 230, 59, 243, 95, 49, 218, 176, + 164, 20, 65, 45, 111, 39, 81, 49, 118, 113, 222, 193, + 250, 242, 168, 217, 41, 164, 247, 177, 30, 238, 18, 120, + 153, 60, 193 }}, + { 40, + { 0, 59, 116, 79, 161, 252, 98, 128, 205, 128, 161, 247, + 57, 163, 56, 235, 106, 53, 26, 187, 174, 226, 104, 170, + 7, 175, 35, 181, 114, 88, 41, 47, 163, 125, 134, 72, + 20, 232, 53, 35, 15 }}, + { 42, + { 0, 250, 103, 221, 230, 25, 18, 137, 231, 0, 3, 58, + 242, 221, 191, 110, 84, 230, 8, 188, 106, 96, 147, 15, + 131, 139, 34, 101, 223, 39, 101, 213, 199, 237, 254, 201, + 123, 171, 162, 194, 117, 50, 96 }}, + { 44, + { 0, 190, 7, 61, 121, 71, 246, 69, 55, 168, 188, 89, + 243, 191, 25, 72, 123, 9, 145, 14, 247, 1, 238, 44, + 78, 143, 62, 224, 126, 118, 114, 68, 163, 52, 194, 217, + 147, 204, 169, 37, 130, 113, 102, 73, 181 }}, + { 46, + { 0, 112, 94, 88, 112, 253, 224, 202, 115, 187, 99, 89, + 5, 54, 113, 129, 44, 58, 16, 135, 216, 169, 211, 36, + 1, 4, 96, 60, 241, 73, 104, 234, 8, 249, 245, 119, + 174, 52, 25, 157, 224, 43, 202, 223, 19, 82, 15 }}, + { 48, + { 0, 228, 25, 196, 130, 211, 146, 60, 24, 251, 90, 39, + 102, 240, 61, 178, 63, 46, 123, 115, 18, 221, 111, 135, + 160, 182, 205, 107, 206, 95, 150, 120, 184, 91, 21, 247, + 156, 140, 238, 191, 11, 94, 227, 84, 50, 163, 39, 34, + 108 }}, + { 50, + { 0, 232, 125, 157, 161, 164, 9, 118, 46, 209, 99, 203, + 193, 35, 3, 209, 111, 195, 242, 203, 225, 46, 13, 32, + 160, 126, 209, 130, 160, 242, 215, 242, 75, 77, 42, 189, + 32, 113, 65, 124, 69, 228, 114, 235, 175, 124, 170, 215, + 232, 133, 205 }}, + { 52, + { 0, 116, 50, 86, 186, 50, 220, 251, 89, 192, 46, 86, + 127, 124, 19, 184, 233, 151, 215, 22, 14, 59, 145, 37, + 242, 203, 134, 254, 89, 190, 94, 59, 65, 124, 113, 100, + 233, 235, 121, 22, 76, 86, 97, 39, 242, 200, 220, 101, + 33, 239, 254, 116, 51 }}, + { 54, + { 0, 183, 26, 201, 87, 210, 221, 113, 21, 46, 65, 45, + 50, 238, 184, 249, 225, 102, 58, 209, 218, 109, 165, 26, + 95, 184, 192, 52, 245, 35, 254, 238, 175, 172, 79, 123, + 25, 122, 43, 120, 108, 215, 80, 128, 201, 235, 8, 153, + 59, 101, 31, 198, 76, 31, 156 }}, + { 56, + { 0, 106, 120, 107, 157, 164, 216, 112, 116, 2, 91, 248, + 163, 36, 201, 202, 229, 6, 144, 254, 155, 135, 208, 170, + 209, 12, 139, 127, 142, 182, 249, 177, 174, 190, 28, 10, + 85, 239, 184, 101, 124, 152, 206, 96, 23, 163, 61, 27, + 196, 247, 151, 154, 202, 207, 20, 61, 10 }}, + { 58, + { 0, 82, 116, 26, 247, 66, 27, 62, 107, 252, 182, 200, + 185, 235, 55, 251, 242, 210, 144, 154, 237, 176, 141, 192, + 248, 152, 249, 206, 85, 253, 142, 65, 165, 125, 23, 24, + 30, 122, 240, 214, 6, 129, 218, 29, 145, 127, 134, 206, + 245, 117, 29, 41, 63, 159, 142, 233, 125, 148, 123 }}, + { 60, + { 0, 107, 140, 26, 12, 9, 141, 243, 197, 226, 197, 219, + 45, 211, 101, 219, 120, 28, 181, 127, 6, 100, 247, 2, + 205, 198, 57, 115, 219, 101, 109, 160, 82, 37, 38, 238, + 49, 160, 209, 121, 86, 11, 124, 30, 181, 84, 25, 194, + 87, 65, 102, 190, 220, 70, 27, 209, 16, 89, 7, 33, + 240 }}, + // The spec lacks the coefficient for x^5 (a^127 x^5). + // Anyway the number will not be used. See the comment for 32. + { 62, + { 0, 65, 202, 113, 98, 71, 223, 248, 118, 214, 94, 0, + 122, 37, 23, 2, 228, 58, 121, 7, 105, 135, 78, 243, + 118, 70, 76, 223, 89, 72, 50, 70, 111, 194, 17, 212, + 126, 181, 35, 221, 117, 235, 11, 229, 149, 147, 123, 213, + 40, 115, 6, 200, 100, 26, 246, 182, 218, 127, 215, 36, + 186, 110, 106 }}, + { 64, + { 0, 45, 51, 175, 9, 7, 158, 159, 49, 68, 119, 92, + 123, 177, 204, 187, 254, 200, 78, 141, 149, 119, 26, 127, + 53, 160, 93, 199, 212, 29, 24, 145, 156, 208, 150, 218, + 209, 4, 216, 91, 47, 184, 146, 47, 140, 195, 195, 125, + 242, 238, 63, 99, 108, 140, 230, 242, 31, 204, 11, 178, + 243, 217, 156, 213, 231 }}, + { 66, + { 0, 5, 118, 222, 180, 136, 136, 162, 51, 46, 117, 13, + 215, 81, 17, 139, 247, 197, 171, 95, 173, 65, 137, 178, + 68, 111, 95, 101, 41, 72, 214, 169, 197, 95, 7, 44, + 154, 77, 111, 236, 40, 121, 143, 63, 87, 80, 253, 240, + 126, 217, 77, 34, 232, 106, 50, 168, 82, 76, 146, 67, + 106, 171, 25, 132, 93, 45, 105 }}, + { 68, + { 0, 247, 159, 223, 33, 224, 93, 77, 70, 90, 160, 32, + 254, 43, 150, 84, 101, 190, 205, 133, 52, 60, 202, 165, + 220, 203, 151, 93, 84, 15, 84, 253, 173, 160, 89, 227, + 52, 199, 97, 95, 231, 52, 177, 41, 125, 137, 241, 166, + 225, 118, 2, 54, 32, 82, 215, 175, 198, 43, 238, 235, + 27, 101, 184, 127, 3, 5, 8, 163, 238 }}, + }; + + private static final int kFieldSize = 8; + private static GF_Poly *g_ec_polynomials[kMaxNumECBytes + 1]; + + public: + // Encode "bytes" with the error correction level "ec_level". The + // encoding mode will be chosen internally by ChooseMode(). + // On success, store the result in "qr_code" and return true. On + // error, return false. We recommend you to use QRCode.EC_LEVEL_L + // (the lowest level) for "ec_level" since our primary use is to + // show QR code on desktop screens. We don't need very strong error + // correction for this purpose. + // + // Note that there is no way to encode bytes in MODE_KANJI. We might + // want to add EncodeWithMode() with which clients can specify the + // encoding mode. For now, we don't need the functionality. + static boolean Encode(final StringPiece& bytes, QRCode.ECLevel ec_level, + QRCode *qr_code) { + // Step 1: Choose the mode (encoding). + final QRCode.Mode mode = ChooseMode(bytes); + + // Step 2: Append "bytes" into "data_bits" in appropriate encoding. + BitVector data_bits; + if (!AppendBytes(bytes, mode, &data_bits)) { + return false; + } + // Step 3: Initialize QR code that can contain "data_bits". + final int num_input_bytes = data_bits.num_bytes(); + if (!InitQRCode(num_input_bytes, ec_level, mode, qr_code)) { + return false; + } + + // Step 4: Build another bit vector that contains header and data. + BitVector header_and_data_bits; + if (!AppendModeInfo(qr_code.mode(), &header_and_data_bits)) { + return false; + } + if (!AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), + &header_and_data_bits)) { + return false; + } + header_and_data_bits.AppendBitVector(data_bits); + + // Step 5: Terminate the bits properly. + if (!TerminateBits(qr_code.num_data_bytes(), &header_and_data_bits)) { + return false; + } + + // Step 6: Interleave data bits with error correction code. + BitVector final_bits; + InterleaveWithECBytes(header_and_data_bits, + qr_code.num_total_bytes(), + qr_code.num_data_bytes(), + qr_code.num_rs_blocks(), + &final_bits); + + // Step 7: Choose the mask pattern and set to "qr_code". + QRCodeMatrix* matrix = new QRCodeMatrix(qr_code.matrix_width(), + qr_code.matrix_width()); + qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, + qr_code.ec_level(), + qr_code.version(), + matrix)); + if (qr_code.mask_pattern() == -1) { + // There was an error. + delete matrix; + return false; + } + + // Step 8. Build the matrix and set it to "qr_code". + MatrixUtil.BuildMatrix(final_bits, + qr_code.ec_level(), + qr_code.version(), + qr_code.mask_pattern(), matrix); + qr_code.set_matrix(matrix); + // Step 9. Make sure we have a valid QR Code. + if (!qr_code.IsValid()) { + Debug.LOG_ERROR("Invalid QR code: " + qr_code.DebugString()); + return false; + } + return true; + } + + // The functions below are public but not intended to be used + // outside the library. We make them public for ease of unit + // testing with gUnit. + + // Return the code point of the table used in alphanumeric mode. + // Return -1 if there is no corresponding code in the table. + static int GetAlphanumericCode(int code) { + if (code < arraysize(kAlphanumericTable)) { + return kAlphanumericTable[code]; + } + return -1; + } + + // Choose the best mode from the content of "bytes". + // The function is guaranteed to return valid mode. + // + // Note that the function does not return MODE_KANJI, as we cannot + // distinguish Shift_JIS from other encodings such as ISO-8859-1, from + // data bytes alone. For example "\xE0\xE0" can be interpreted as one + // character in Shift_JIS, but also two characters in ISO-8859-1. + static QRCode.Mode ChooseMode(final StringPiece &bytes) { + boolean has_numeric = false; + boolean has_alphanumeric = false; + boolean has_other = false; + for (int i = 0; i < bytes.size(); ++i) { + final int byte = bytes[i]; + if (byte >= '0' && byte <= '9') { + has_numeric = true; + } else if (GetAlphanumericCode(byte) != -1) { + has_alphanumeric = true; + } else { + has_other = true; + } + } + if (has_other) { + return QRCode.MODE_8BIT_BYTE; + } else if (has_alphanumeric) { + return QRCode.MODE_ALPHANUMERIC; + } else if (has_numeric) { + return QRCode.MODE_NUMERIC; + } + // "bytes" must be empty to reach here. + Debug.DCHECK(bytes.empty()); + return QRCode.MODE_8BIT_BYTE; +} + + private static int ChooseMaskPattern(final BitVector &bits, + QRCode.ECLevel ec_level, + int version, + QRCodeMatrix *matrix) { + if (!QRCode.IsValidMatrixWidth(matrix.width())) { + Debug.LOG_ERROR("Invalid matrix width: " + matrix.width()); + return -1; + } + + int min_penalty = INT_MAX; // Lower penalty is better. + int best_mask_pattern = -1; + // We try all mask patterns to choose the best one. + for (int i = 0; i < kNumMaskPatterns; ++i) { + final int mask_pattern = i; + if (!MatrixUtil.BuildMatrix(bits, ec_level, version, + mask_pattern, matrix)) { + return -1; + } + final int penalty = MaskUtil.CalculateMaskPenalty(*matrix); + Debug.LOG_INFO("mask_pattern: " + mask_pattern + ", " + "penalty: " + penalty); + if (penalty < min_penalty) { + min_penalty = penalty; + best_mask_pattern = mask_pattern; + } + } + return best_mask_pattern; + } + + // Initialize "qr_code" according to "num_input_bytes", "ec_level", + // and "mode". On success, modify "qr_code" and return true. On + // error, return false. + static boolean InitQRCode(int num_input_bytes, QRCode.ECLevel ec_level, + QRCode.Mode mode, QRCode *qr_code) { + qr_code.set_ec_level(ec_level); + qr_code.set_mode(mode); + + if (!QRCode.IsValidECLevel(ec_level)) { + Debug.LOG_ERROR("Invalid EC level: " + ec_level); + return false; + } + + // In the following comments, we use numbers of Version 7-H. + for (int i = 0; i < arraysize(kRSBlockTable); ++i) { + final RSBlockInfo &row = kRSBlockTable[i]; + // num_bytes = 196 + final int num_bytes = row.num_bytes; + // num_ec_bytes = 130 + final int num_ec_bytes = row.block_info[ec_level].num_ec_bytes; + // num_rs_blocks = 5 + final int num_rs_blocks = row.block_info[ec_level].num_rs_blocks; + // num_data_bytes = 196 - 130 = 66 + final int num_data_bytes = num_bytes - num_ec_bytes; + // We want to choose the smallest version which can contain data + // of "num_input_bytes" + some extra bits for the header (mode + // info and length info). The header can be three bytes + // (precisely 4 + 16 bits) at most. Hence we do +3 here. + if (num_data_bytes >= num_input_bytes + 3) { + // Yay, we found the proper rs block info! + qr_code.set_version(i + 1); + qr_code.set_num_total_bytes(num_bytes); + qr_code.set_num_data_bytes(num_data_bytes); + qr_code.set_num_rs_blocks(num_rs_blocks); + // num_ec_bytes = 196 - 66 = 130 + qr_code.set_num_ec_bytes(num_bytes - num_data_bytes); + // num_matrix_width = 21 + 6 * 4 = 45 + qr_code.set_matrix_width(21 + i * 4); + return true; + } + } + Debug.LOG_ERROR("Cannot find proper rs block info (input data too big?)"); + return false; + } + + // Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 + // (p.24). + static boolean TerminateBits(int num_data_bytes, BitVector *bits) { + final int capacity = num_data_bytes * 8; + if (bits.size() > capacity) { + Debug.LOG_ERROR("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity); + return false; + } + // Append termination bits. + // See 8.4.8 of JISX0510:2004 (p.24) for details. + for (int i = 0; i < 4 && bits.size() < capacity; ++i) { + bits.AppendBit(0); + } + final int num_bits_in_last_byte = bits.size() % 8; + // If the last byte isn't 8-bit aligned, we'll add padding bits. + if (num_bits_in_last_byte > 0) { + final int num_padding_bits = 8 - num_bits_in_last_byte; + for (int i = 0; i < num_padding_bits; ++i) { + bits.AppendBit(0); + } + } + // Should be 8-bit aligned here. + Debug.DCHECK_EQ(0, bits.size() % 8); + // If we have more space, we'll fill the space with padding patterns + // defined in 8.4.9 (p.24). + final int num_padding_bytes = num_data_bytes - bits.num_bytes(); + for (int i = 0; i < num_padding_bytes; ++i) { + if (i % 2 == 0) { + bits.AppendBits(0xec, 8); + } else { + bits.AppendBits(0x11, 8); + } + } + Debug.DCHECK_EQ(bits.size(), capacity); // Should be same. + return bits.size() == capacity; + } + + // Get number of data bytes and number of error correction bytes for + // block id "block_id". Store the result in + // "num_data_bytes_in_block", and "num_ec_bytes_in_block". + // See table 12 in 8.5.1 of JISX0510:2004 (p.30) + static void GetNumDataBytesAndNumECBytesForBlockID( + int num_total_bytes, + int num_data_bytes, + int num_rs_blocks, + int block_id, + int *num_data_bytes_in_block, + int *num_ec_bytes_in_block) { + Debug.DCHECK_LT(block_id, num_rs_blocks); + // num_rs_blocks_in_group2 = 196 % 5 = 1 + final int num_rs_blocks_in_group2 = num_total_bytes % num_rs_blocks; + // num_rs_blocks_in_group1 = 5 - 1 = 4 + final int num_rs_blocks_in_group1 = num_rs_blocks - num_rs_blocks_in_group2; + // num_total_bytes_in_group1 = 196 / 5 = 39 + final int num_total_bytes_in_group1 = num_total_bytes / num_rs_blocks; + // num_total_bytes_in_group2 = 39 + 1 = 40 + final int num_total_bytes_in_group2 = num_total_bytes_in_group1 + 1; + // num_data_bytes_in_group1 = 66 / 5 = 13 + final int num_data_bytes_in_group1 = num_data_bytes / num_rs_blocks; + // num_data_bytes_in_group2 = 13 + 1 = 14 + final int num_data_bytes_in_group2 = num_data_bytes_in_group1 + 1; + // num_ec_bytes_in_group1 = 39 - 13 = 26 + final int num_ec_bytes_in_group1 = num_total_bytes_in_group1 - + num_data_bytes_in_group1; + // num_ec_bytes_in_group2 = 40 - 14 = 26 + final int num_ec_bytes_in_group2 = num_total_bytes_in_group2 - + num_data_bytes_in_group2; + // Sanity checks. + // 26 = 26 + Debug.DCHECK_EQ(num_ec_bytes_in_group1, num_ec_bytes_in_group2); + // 5 = 4 + 1. + Debug.DCHECK_EQ(num_rs_blocks, num_rs_blocks_in_group1 + num_rs_blocks_in_group2); + // 196 = (13 + 26) * 4 + (14 + 26) * 1 + Debug.DCHECK_EQ(num_total_bytes, + ((num_data_bytes_in_group1 + num_ec_bytes_in_group1) * + num_rs_blocks_in_group1) + + ((num_data_bytes_in_group2 + num_ec_bytes_in_group2) * + num_rs_blocks_in_group2)); + + if (block_id < num_rs_blocks_in_group1) { + *num_data_bytes_in_block = num_data_bytes_in_group1; + *num_ec_bytes_in_block = num_ec_bytes_in_group1; + } else { + *num_data_bytes_in_block = num_data_bytes_in_group2; + *num_ec_bytes_in_block = num_ec_bytes_in_group2; + } + } + + // Interleave "bits" with corresponding error correction bytes. On + // success, store the result in "result" and return true. On error, + // return false. + // The interleave rule is complicated. See 8.6 of JISX0510:2004 + // (p.37) for details. + static boolean InterleaveWithECBytes(final BitVector &bits, + int num_total_bytes, + int num_data_bytes, + int num_rc_blocks, + BitVector *result) { + // "bits" must have "num_data_bytes" bytes of data. + Debug.DCHECK(bits.num_bytes() == num_data_bytes); + + // 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". + typedef pair BlockPair; + int data_bytes_offset = 0; + final String &encoded_bytes = bits.ToString(); + int max_num_data_bytes = 0; // StringPiece's size is "int". + size_t max_num_ec_bytes = 0; // STL String's size is "size_t". + vector blocks; + // Since, we know the number of reedsolmon blocks, we can initialize + // the vector with the number. + blocks.resize(num_rs_blocks); + + for (int i = 0; i < num_rs_blocks; ++i) { + int num_data_bytes_in_block, num_ec_bytes_in_block; + GetNumDataBytesAndNumECBytesForBlockID( + num_total_bytes, num_data_bytes, num_rs_blocks, i, + &num_data_bytes_in_block, &num_ec_bytes_in_block); + // We modify the objects in the vector instead of copying new + // objects to the vector. In particular, we want to avoid String + // copies. + StringPiece *data_bytes = &(blocks[i].first); + String *ec_bytes = &(blocks[i].second); + + data_bytes.set(encoded_bytes.data() + data_bytes_offset, + num_data_bytes_in_block); + GenerateECBytes(*data_bytes, num_ec_bytes_in_block, ec_bytes); + + max_num_data_bytes = max(max_num_data_bytes, data_bytes.size()); + max_num_ec_bytes = max(max_num_ec_bytes, ec_bytes.size()); + data_bytes_offset += num_data_bytes_in_block; + } + Debug.DCHECK_EQ(num_data_bytes, data_bytes_offset); + + // First, place data blocks. + for (int i = 0; i < max_num_data_bytes; ++i) { + for (int j = 0; j < blocks.size(); ++j) { + final StringPiece &data_bytes = blocks[j].first; + if (i < data_bytes.size()) { + result.AppendBits(data_bytes[i], 8); + } + } + } + // Then, place error correction blocks. + for (int i = 0; i < max_num_ec_bytes; ++i) { + for (int j = 0; j < blocks.size(); ++j) { + final String &ec_bytes = blocks[j].second; + if (i < ec_bytes.size()) { + result.AppendBits(ec_bytes[i], 8); + } + } + } + if (num_total_bytes == result.num_bytes()) { // Should be same. + return true; + } + Debug.LOG_ERROR("Interleaving error: " + num_total_bytes + " and " + result.num_bytes() + + "differ."); + return false; + } + + // Append mode info. On success, store the result in "bits" and + // return true. On error, return false. + static boolean AppendModeInfo(QRCode.Mode mode, BitVector *bits) { + final int code = QRCode.GetModeCode(mode); + if (code == -1) { + Debug.LOG_ERROR("Invalid mode: " + mode); + return false; + } + bits.AppendBits(code, 4); + return true; + } + + + // Append length info. On success, store the result in "bits" and + // return true. On error, return false. + static boolean AppendLengthInfo(int num_bytes, + int version, + QRCode.Mode mode, + BitVector *bits) { + int num_letters = num_bytes; + // In Kanji mode, a letter is represented in two bytes. + if (mode == QRCode.MODE_KANJI) { + Debug.DCHECK_EQ(0, num_letters % 2); + num_letters /= 2; + } + + final int num_bits = QRCode.GetNumBitsForLength(version, mode); + if (num_bits == -1) { + Debug.LOG_ERROR("num_bits unset"); + return false; + } + if (num_letters > ((1 << num_bits) - 1)) { + Debug.LOG_ERROR(num_letters + "is bigger than" + ((1 << num_bits) - 1)); + return false; + } + bits.AppendBits(num_letters, num_bits); + return true; + } + + // Append "bytes" in "mode" mode (encoding) into "bits". On + // success, store the result in "bits" and return true. On error, + // return false. + static boolean AppendBytes(final StringPiece &bytes, + QRCode.Mode mode, BitVector *bits) { + switch (mode) { + case QRCode.MODE_NUMERIC: + return AppendNumericBytes(bytes, bits); + case QRCode.MODE_ALPHANUMERIC: + return AppendAlphanumericBytes(bytes, bits); + case QRCode.MODE_8BIT_BYTE: + return Append8BitBytes(bytes, bits); + case QRCode.MODE_KANJI: + return AppendKanjiBytes(bytes, bits); + default: + break; + } + Debug.LOG_ERROR("Invalid mode: " + mode); + return false; + } + + // Append "bytes" to "bits" using QRCode.MODE_NUMERIC mode. + // On success, store the result in "bits" and return true. On error, + // return false. + static boolean AppendNumericBytes(final StringPiece &bytes, BitVector *bits) { + // Validate all the bytes first. + for (int i = 0; i < bytes.size(); ++i) { + if (!isdigit(bytes[i])) { + return false; + } + } + for (int i = 0; i < bytes.size();) { + final int num1 = bytes[i] - '0'; + if (i + 2 < bytes.size()) { + // Encode three numeric letters in ten bits. + final int num2 = bytes[i + 1] - '0'; + final int num3 = bytes[i + 2] - '0'; + bits.AppendBits(num1 * 100 + num2 * 10 + num3, 10); + i += 3; + } else if (i + 1 < bytes.size()) { + // Encode two numeric letters in seven bits. + final int num2 = bytes[i + 1] - '0'; + bits.AppendBits(num1 * 10 + num2, 7); + i += 2; + } else { + // Encode one numeric letter in four bits. + bits.AppendBits(num1, 4); + ++i; + } + } + return true; + } + + // Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. + // On success, store the result in "bits" and return true. On error, + // return false. + static boolean AppendAlphanumericBytes(final StringPiece &bytes, + BitVector *bits) { + for (int i = 0; i < bytes.size();) { + final int code1 = GetAlphanumericCode(bytes[i]); + if (code1 == -1) { + return false; + } + if (i + 1 < bytes.size()) { + final int code2 = GetAlphanumericCode(bytes[i + 1]); + if (code2 == -1) { + return false; + } + // 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; + } + } + return true; + } + + // Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. + // On success, store the result in "bits" and return true. On error, + // return false. + static boolean Append8BitBytes(final StringPiece &bytes, BitVector *bits) { + for (int i = 0; i < bytes.size(); ++i) { + bits.AppendBits(bytes[i], 8); + } + return true; + } + + // Append "bytes" to "bits" using QRCode.MODE_KANJI mode. + // On success, store the result in "bits" and return true. On error, + // return false. + // See 8.4.5 of JISX0510:2004 (p.21) for how to encode Kanji bytes. + static boolean AppendKanjiBytes(final StringPiece &bytes, BitVector *bits) { + if (bytes.size() % 2 != 0) { + Debug.LOG_ERROR("Invalid byte sequence: " + bytes); + return false; + } + for (int i = 0; i < bytes.size(); i += 2) { + Debug.DCHECK(IsValidKanji(bytes[i], bytes[i + 1])); + final int code = (static_cast(bytes[i]) << 8 | bytes[i + 1]); + int subtracted = -1; + if (code >= 0x8140 && code <= 0x9ffc) { + subtracted = code - 0x8140; + } else if (code >= 0xe040 && code <= 0xebbf) { + subtracted = code - 0xc140; + } + if (subtracted == -1) { + Debug.LOG_ERROR("Invalid byte sequence: " + bytes); + return false; + } + final int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); + bits.AppendBits(encoded, 13); + } + return true; + } + + // Only call once + static { + InitECPolynomials(); + } + + // Initialize "g_ec_polynomials" with numbers in kECPolynomials. + private static void InitECPolynomials() { + final GaloisField &field = GaloisField.GetField(kFieldSize); + for (int i = 0; i < arraysize(kECPolynomials); ++i) { + final ECPolyInfo& ec_poly_info = kECPolynomials[i]; + final int ec_length = ec_poly_info.ec_length; + vector *coeffs = new vector; + // The number of coefficients is one more than "ec_length". + // That's why the termination condition is <= instead of <. + for (int j = 0; j <= ec_length; ++j) { + // We need exp'ed numbers for later use. + final int coeff = field.Exp(ec_poly_info.coeffs[j]); + coeffs.push_back(coeff); + } + // Reverse the coefficients since the numbers in kECPolynomials + // are ordered in reverse order to the order GF_Poly expects. + reverse(coeffs.begin(), coeffs.end()); + + GF_Poly *ec_poly = new GF_Poly(coeffs, GaloisField.GetField(kFieldSize)); + g_ec_polynomials[ec_length] = ec_poly; + } + } + + // Get error correction polynomials. The polynomials are + // defined in Appendix A of JISX0510 2004 (p. 59). In the appendix, + // they use exponential notations for the polynomials. We need to + // apply GaloisField.Log() to all coefficients generated by the + // function to compare numbers with the ones in the appendix. + // + // Example: + // - Input: 17 + // - Output (in reverse order) + // {119,66,83,120,119,22,197,83,249,41,143,134,85,53,125,99,79} + // - Log()'ed output (in reverse order) + // {0,43,139,206,78,43,239,123,206,214,147,24,99,150,39,243,163,136} + static final GF_Poly &GetECPoly(int ec_length) { + Debug.DCHECK_GE(kMaxNumECBytes, ec_length); + final GF_Poly *ec_poly = g_ec_polynomials[ec_length]; + Debug.DCHECK(ec_poly); + return *ec_poly; + } + + // Generate error correction bytes of "ec_length". + // + // Example: + // - Input: {32,65,205,69,41,220,46,128,236}, ec_length = 17 + // - Output: {42,159,74,221,244,169,239,150,138,70,237,85,224,96,74,219,61} + static void GenerateECBytes(final StringPiece &data_bytes, + int ec_length, + String *ec_bytes) { + // First, fill the vector with "ec_length" copies of 0. + // They are low-order zero coefficients. + vector *coeffs = new vector(ec_length, 0); + // Then copy data_bytes backward. + copy(data_bytes.rbegin(), data_bytes.rend(), back_inserter(*coeffs)); + // Now we have data polynomial. + GF_Poly data_poly(coeffs, GaloisField.GetField(kFieldSize)); + + // Get error correction polynomial. + final GF_Poly &ec_poly = GetECPoly(ec_length); + pair divrem = GF_Poly.DivRem(data_poly, ec_poly); + + // Basically, the coefficients in the remainder polynomial are the + // error correction bytes. + GF_Poly *remainder = divrem.second; + ec_bytes.reserve(ec_length); + // However, high-order zero cofficients in the remainder polynomial + // are ommited. We should add zero by ourselvs. + final int num_pruned_zero_coeffs = ec_length - (remainder.degree() + 1); + for (int i = 0; i < num_pruned_zero_coeffs; ++i) { + ec_bytes.push_back(0); + } + // Copy the remainder numbers to "ec_bytes". + for (int i = remainder.degree(); i >= 0; --i) { + ec_bytes.push_back(remainder.coeff(i)); + } + Debug.DCHECK_EQ(ec_length, ec_bytes.size()); + + // Deallocate quotient and reminder. + delete divrem.first; + delete divrem.second; + } + + // Check if "byte1" and "byte2" can compose a valid Kanji letter + // (2-byte Shift_JIS letter). + // The numbers are from http://ja.wikipedia.org/wiki/Shift_JIS. + static boolean IsValidKanji(final char byte1, final char byte2) { + return (byte2 != 0x7f && + ((byte1 >= 0x81 && byte1 <= 0x9f && + byte2 >= 0x40 && byte2 <= 0xfc) || + ((byte1 >= 0xe0 && byte1 <= 0xfc && + byte2 >= 0x40 && byte2 <= 0xfc)))); + } + + // Check if "bytes" is a valid Kanji sequence. + static boolean IsValidKanjiSequence(final StringPiece &bytes) { + if (bytes.size() % 2 != 0) { + return false; + } + int i = 0; + for (; i < bytes.size(); i += 2) { + if (!IsValidKanji(bytes[i], bytes[i + 1])) { + break; + } + } + return i == bytes.size(); // Consumed all bytes? + } + +} diff --git a/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java new file mode 100644 index 000000000..fbfaec82d --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java @@ -0,0 +1,219 @@ +/* + * 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; + +// #include "util/array/array2d-inl.h" + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class MaskUtil { + + // 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. + public static int CalculateMaskPenalty(final QRCodeMatrix &matrix) { + int penalty = 0; + penalty += ApplyMaskPenaltyRule1(matrix); + penalty += ApplyMaskPenaltyRule2(matrix); + penalty += ApplyMaskPenaltyRule3(matrix); + penalty += ApplyMaskPenaltyRule4(matrix); + return penalty; + } + + // 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. + public static int ApplyMaskPenaltyRule1(final QRCodeMatrix &matrix) { + final int penalty = (ApplyMaskPenaltyRule1Internal(matrix, true) + + ApplyMaskPenaltyRule1Internal(matrix, false)); + Debug.LOG_INFO("\tApplyMaskPenaltyRule1: " + penalty); + return penalty; + } + + // Apply mask penalty rule 2 and return the penalty. + // Find 2x2 blocks with the same color and give penalty to them. + public static int ApplyMaskPenaltyRule2(final QRCodeMatrix &matrix) { + int penalty = 0; + for (int y = 0; y < matrix.height() - 1; ++y) { + for (int x = 0; x < matrix.width() - 1; ++x) { + if (matrix(y, x) == matrix(y + 0, x + 1) && + matrix(y, x) == matrix(y + 1, x + 0) && + matrix(y, x) == matrix(y + 1, x + 1)) { + penalty += 3; + } + } + } + Debug.LOG_INFO("\tApplyMaskPenaltyRule2: " + penalty); + return penalty; + } + + // Apply mask penalty rule 3 and return the penalty. + // Find consecutive cells of 00001011101 or 10111010000, and give + // penalty to them. If we find patterns like 000010111010000, we give + // penalties twice (i.e. 40 * 2). + public static int ApplyMaskPenaltyRule3(final QRCodeMatrix &matrix) { + int penalty = 0; + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + // Tried to simplify following conditions but failed. + if (x + 6 < matrix.width() && + matrix(y, x + 0) == 1 && + matrix(y, x + 1) == 0 && + matrix(y, x + 2) == 1 && + matrix(y, x + 3) == 1 && + matrix(y, x + 4) == 1 && + matrix(y, x + 5) == 0 && + matrix(y, x + 6) == 1 && + ((x + 10 < matrix.width() && + matrix(y, x + 7) == 0 && + matrix(y, x + 8) == 0 && + matrix(y, x + 9) == 0 && + matrix(y, x + 10) == 0) || + (x - 4 >= 0 && + matrix(y, x - 1) == 0 && + matrix(y, x - 2) == 0 && + matrix(y, x - 3) == 0 && + matrix(y, x - 4) == 0))) { + penalty += 40; + } + if (y + 6 < matrix.height() && + matrix(y + 0, x) == 1 && + matrix(y + 1, x) == 0 && + matrix(y + 2, x) == 1 && + matrix(y + 3, x) == 1 && + matrix(y + 4, x) == 1 && + matrix(y + 5, x) == 0 && + matrix(y + 6, x) == 1 && + ((y + 10 < matrix.height() && + matrix(y + 7, x) == 0 && + matrix(y + 8, x) == 0 && + matrix(y + 9, x) == 0 && + matrix(y + 10, x) == 0) || + (y - 4 >= 0 && + matrix(y - 1, x) == 0 && + matrix(y - 2, x) == 0 && + matrix(y - 3, x) == 0 && + matrix(y - 4, x) == 0))) { + penalty += 40; + } + } + } + Debug.LOG_INFO("\tApplyMaskPenaltyRule3: " + penalty); + return penalty; + } + + // 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. + // Examples: + // - 0% => 100 + // - 40% => 20 + // - 45% => 10 + // - 50% => 0 + // - 55% => 10 + // - 55% => 20 + // - 100% => 100 + public static int ApplyMaskPenaltyRule4(final QRCodeMatrix &matrix) { + int num_dark_cells = 0; + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + if (matrix(y, x) == 1) { + num_dark_cells += 1; + } + } + } + final int num_total_cells = matrix.height() * matrix.width(); + double dark_ratio = static_cast(num_dark_cells) / num_total_cells; + final int penalty = abs(static_cast(dark_ratio * 100 - 50)) / 5 * 10; + Debug.LOG_INFO("\tApplyMaskPenaltyRule4: " + penalty); + return penalty; + } + + // Return the mask bit for "mask_pattern" at "x" and "y". + // See 8.8 of JISX0510:2004 for mask pattern conditions. + public static int GetDataMaskBit(final int mask_pattern, + final int x, final int y) { + Debug.DCHECK(QRCode.IsValidMaskPattern(mask_pattern)); + switch (mask_pattern) { + case 0: + return (y + x) % 2 == 0; + case 1: + return y % 2 == 0; + case 2: + return x % 3 == 0; + case 3: + return (y + x) % 3 == 0; + case 4: + return ((y / 2) + (x / 3)) % 2 == 0; + case 5: + return ((y * x) % 2) + ((y * x) % 3) == 0; + case 6: + return (((y * x) % 2) + ((y * x) % 3)) % 2 == 0; + case 7: + return (((y * x) % 3) + ((y + x) % 2)) % 2 == 0; + default: + ; + } + Debug.LOG_ERROR("invalid mask pattern: " + mask_pattern); + return -1; + } + + // Helper function for ApplyMaskPenaltyRule1. We need this for doing + // this calculation in both vertical and horizontal orders + // respectively. + private static int ApplyMaskPenaltyRule1Internal(final QRCodeMatrix &matrix, + boolean is_horizontal) { + int penalty = 0; + int num_same_bit_cells = 0; + int prev_bit = -1; + // Horizontal mode: + // for (int i = 0; i < matrix.height(); ++i) { + // for (int j = 0; j < matrix.width(); ++j) { + // int bit = matrix(i, j); + // Vertical mode: + // for (int i = 0; i < matrix.width(); ++i) { + // for (int j = 0; j < matrix.height(); ++j) { + // int bit = matrix(j, i); + final int i_limit = is_horizontal ? matrix.height() : matrix.width(); + final int j_limit = is_horizontal ? matrix.width() : matrix.height(); + for (int i = 0; i < i_limit; ++i) { + for (int j = 0; j < j_limit; ++j) { + final int bit = is_horizontal ? matrix(i, j) : matrix(j, i); + if (bit == prev_bit) { + num_same_bit_cells += 1; + // Found five repetitive cells with the same color (bit). + // We'll give penalty of 3. + if (num_same_bit_cells == 5) { + penalty += 3; + } else if (num_same_bit_cells > 5) { + // After five repetitive cells, we'll add the penalty one + // by one. + penalty += 1; + } + } else { + num_same_bit_cells = 1; // Include the cell itself. + prev_bit = bit; + } + } + num_same_bit_cells = 0; // Clear at each row/column. + } + return penalty; + } + +} diff --git a/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java new file mode 100644 index 000000000..6cdf66ab4 --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java @@ -0,0 +1,588 @@ +/* + * 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; + +// #include "util/array/array2d-inl.h" + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class MatrixUtil { + + private static final int kPositionDetectionPattern[][] = { + {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 kHorizontalSeparationPattern[][] = { + {0, 0, 0, 0, 0, 0, 0, 0}, + }; + + private static final int kVerticalSeparationPattern[][] = { + {0}, {0}, {0}, {0}, {0}, {0}, {0}, + }; + + private static final int kPositionAdjustmentPattern[][] = { + {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 kPositionAdjustmentPatternCoordinateTable[][] = { + {-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 int kTypeInfoCoordinates[][] = { + {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 uint32 kVersionInfoPoly = 0x1f25; // 1 1111 0010 0101 + + // From Appendix C in JISX0510:2004 (p.65). + private static final uint32 kTypeInfoPoly = 0x537; + private static final uint32 kTypeInfoMaskPattern = 0x5412; + + // Set all cells to -1. -1 means that the cell is empty (not set + // yet). + public static void ClearMatrix(QRCodeMatrix *matrix) { + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + (*matrix)(y, x) = -1; + } + } + } + + // Convert "matrix" to ASCII String for debugging. + public static String ToASCII(final QRCodeMatrix &matrix) { + String result; + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + switch (matrix(y, x)) { + case 0: + result.append(" 0"); + break; + case 1: + result.append(" 1"); + break; + default: + result.append(" "); + break; + } + } + result.append("\n"); + } + return result; + } + + // Build 2D matrix of QR Code from "data_bits" with "ec_level", + // "version" and "mask_pattern". On success, store the result in + // "matrix" and return true. On error, return false. + public static boolean BuildMatrix(final BitVector &data_bits, + QRCode.ECLevel ec_level, + int version, + int mask_pattern, + QRCodeMatrix *matrix) { + MatrixUtil.ClearMatrix(matrix); + if (!EmbedBasicPatterns(version, matrix)) { + return false; + } + // Type information appear with any version. + if (!EmbedTypeInfo(ec_level, mask_pattern, matrix)) { + return false; + } + // Version info appear if version >= 7. + if (!MaybeEmbedVersionInfo(version, matrix)) { + return false; + } + // Data should be embedded at end. + return EmbedDataBits(data_bits, mask_pattern, matrix); + } + + // Embed basic patterns. On success, modify the matrix and return + // true. On error, return false. The basic patterns are: + // - Position detection patterns + // - Timing patterns + // - Dark dot at the left bottom corner + // - Position adjustment patterns, if need be + public static boolean EmbedBasicPatterns(int version, + QRCodeMatrix *matrix) { + // 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); + return true; + } + + // Embed type information. On success, modify the matrix and return + // true. On error, return false. + public static boolean EmbedTypeInfo(QRCode.ECLevel ec_level, + int mask_pattern, + QRCodeMatrix *matrix) { + BitVector type_info_bits; + if (!MakeTypeInfoBits(ec_level, mask_pattern, &type_info_bits)) { + return false; + } + Debug.DCHECK_EQ(15, type_info_bits.size()); + + for (int i = 0; i < type_info_bits.size(); ++i) { + // Place bits in LSB to MSB order. LSB (least significant bit) + // is the last value in "type_info_bits". + final int bit = type_info_bits.at(type_info_bits.size() - 1 - i); + + // Type info bits at the left top corner. + // See 8.9 of JISX0510:2004 (p.46). + final int x1 = kTypeInfoCoordinates[i][0]; + final int y1 = kTypeInfoCoordinates[i][1]; + (*matrix)(y1, x1) = bit; + + if (i < 8) { + // Right top corner. + final int x2 = matrix.width() - i - 1; + final int y2 = 8; + (*matrix)(y2, x2) = bit; + } else { + // Left bottom corner. + final int x2 = 8; + final int y2 = matrix.height() - 7 + (i - 8); + (*matrix)(y2, x2) = bit; + } + } + return true; + } + + // Embed version information if need be. On success, modify the + // matrix and return true. On error, return false. + // See 8.10 of JISX0510:2004 (p.47) for how to embed version + // information. Return true on success. Return false otherwise. + public static boolean MaybeEmbedVersionInfo(int version, + QRCodeMatrix *matrix) { + if (version < 7) { // Version info is necessary if version >= 7. + return true; // Don't need version info. + } + BitVector version_info_bits; + if (!MakeVersionInfoBits(version, &version_info_bits)) { + return false; + } + + Debug.DCHECK_EQ(18, version_info_bits.size()); + int bit_index = 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. + final int bit = version_info_bits.at(bit_index--); + // Left bottom corner. + (*matrix)(matrix.height() - 11 + j, i) = bit; + // Right bottom corner. + (*matrix)(i, matrix.height() - 11 + j) = bit; + } + } + return true; + } + + // Embed "data_bits" using "mask_pattern". On success, modify the + // matrix and return true. On error, return false. For debugging + // purpose, it skips masking process if "mask_pattern" is -1. + // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. + public static boolean EmbedDataBits(final BitVector &data_bits, + int mask_pattern, + QRCodeMatrix *matrix) { + int bit_index = 0; + int direction = -1; + // Start from the right bottom cell. + int x = matrix.width() - 1; + int y = matrix.height() - 1; + while (x > 0) { + // Skip the vertical timing pattern. + if (x == 6) { + x -= 1; + } + while (y >= 0 && y < matrix.height()) { + for (int i = 0; i < 2; ++i) { + final int xx = x - i; + // Skip the cell if it's not empty. + if (!IsEmpty((*matrix)(y, xx))) { + continue; + } + int bit = -1; + if (bit_index < data_bits.size()) { + bit = data_bits.at(bit_index); + ++bit_index; + } 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 = 0; + } + Debug.DCHECK(IsValidValue(bit)); + + // Skip masking if mask_pattern is -1. + if (mask_pattern != -1) { + final int mask = MaskUtil.GetDataMaskBit(mask_pattern, xx, y); + Debug.DCHECK(mask == 0 || mask == 1); + bit ^= mask; + } + (*matrix)(y, xx) = bit; + } + y += direction; + } + direction = -direction; // Reverse the direction. + y += direction; + x -= 2; // Move to the left. + } + // All bits should be consumed. + if (bit_index < data_bits.size()) { + Debug.LOG_ERROR("Not all bits consumed: " + bit_index + "/" + data_bits.size()); + return false; + } + Debug.DCHECK_EQ(bit_index, data_bits.size()); + return true; + } + + // 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 + public static int FindMSBSet(uint32 value) { + int num_digits = 0; + while (value != 0) { + value >>= 1; + ++num_digits; + } + return num_digits; + } + + // 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^2 + x^1 + // 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 nagative. + public static uint32 CalculateBCHCode(uint32 value, uint32 poly) { + // If poly is "1 1111 0010 0101" (version info poly), + // msb_set_in_poly is 13. We'll subtract 1 from 13 to make it 12. + final int msb_set_in_poly = FindMSBSet(poly); + value <<= msb_set_in_poly - 1; + // Do the division business using exclusive-or operations. + while (FindMSBSet(value) >= msb_set_in_poly) { + value ^= poly << (FindMSBSet(value) - msb_set_in_poly); + } + // 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. On error, return false. + // Encode error correction level and mask pattern. See 8.9 of + // JISX0510:2004 (p.45) for details. + public static boolean MakeTypeInfoBits(QRCode.ECLevel ec_level, + final int mask_pattern, + BitVector *bits) { + final int ec_code = QRCode.GetECLevelCode(ec_level); + if (ec_code == -1) { + return false; + } + if (!QRCode.IsValidMaskPattern(mask_pattern)) { + return false; + } + final uint32 type_info = (ec_code << 3) | mask_pattern; + bits.AppendBits(type_info, 5); + + final uint32 bch_code = MatrixUtil.CalculateBCHCode(type_info, + kTypeInfoPoly); + bits.AppendBits(bch_code, 10); + + BitVector mask_bits; + mask_bits.AppendBits(kTypeInfoMaskPattern, 15); + bits.XOR(mask_bits); + + if (bits.size() != 15) { // Just in case. + Debug.LOG_ERROR("should not happen but we got: " + bits.size()); + return false; + } + return true; + } + + // Make bit vector of version information. On success, store the + // result in "bits" and return true. On error, return false. + // Encode version information. See 8.10 of JISX0510:2004 (p.45) for + // details. + public static boolean MakeVersionInfoBits(int version, BitVector *bits) { + bits.AppendBits(version, 6); + final uint32 bch_code = MatrixUtil.CalculateBCHCode(version, + kVersionInfoPoly); + bits.AppendBits(bch_code, 12); + if (bits.size() != 18) { // Just in case. + Debug.LOG_ERROR("should not happen but we got: " + bits.size()); + return false; + } + return true; + } + + // Check if "value" is empty. + private static boolean IsEmpty(final int value) { + return value == -1; + } + + // Check if "value" is valid. + private static boolean IsValidValue(final int value) { + return (value == -1 || // Empty. + value == 0 || // Light (white). + value == 1); // Dark (black). + } + + private static void EmbedTimingPatterns(QRCodeMatrix *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.width() - 8; ++i) { + final int bit = (i + 1) % 2; + // Horizontal line. + Debug.DCHECK(IsValidValue((*matrix)(6, i))); + if (IsEmpty((*matrix)(6, i))) { + (*matrix)(6, i) = bit; + } + // Vertical line. + Debug.DCHECK(IsValidValue((*matrix)(i, 6))); + if (IsEmpty((*matrix)(i, 6))) { + (*matrix)(i, 6) = bit; + } + } + } + + // Embed the lonely dark dot at left bottom corner. + // JISX0510:2004 (p.46) + private static void EmbedDarkDotAtLeftBottomCorner( + QRCodeMatrix *matrix) { + Debug.DCHECK((*matrix)(matrix.height() - 8, 8)); + (*matrix)(matrix.height() - 8, 8) = 1; + } + + private static void EmbedHorizontalSeparationPattern(final int x_start, + final int y_start, + QRCodeMatrix *matrix) { + // We know the width and height. + Debug.DCHECK_EQ(8, arraysize(kHorizontalSeparationPattern[0])); + Debug.DCHECK_EQ(1, arraysize(kHorizontalSeparationPattern)); + for (int x = 0; x < 8; ++x) { + Debug.DCHECK(IsEmpty((*matrix)(y_start, x_start + x))); + (*matrix)(y_start, x_start + x) = kHorizontalSeparationPattern[0][x]; + } + } + + private static void EmbedVerticalSeparationPattern(final int x_start, + final int y_start, + QRCodeMatrix *matrix) { + // We know the width and height. + Debug.DCHECK_EQ(1, arraysize(kVerticalSeparationPattern[0])); + Debug.DCHECK_EQ(7, arraysize(kVerticalSeparationPattern)); + for (int y = 0; y < 7; ++y) { + Debug.DCHECK(IsEmpty((*matrix)(y_start + y, x_start))); + (*matrix)(y_start + y, x_start) = kVerticalSeparationPattern[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(final int x_start, + final int y_start, + QRCodeMatrix *matrix) { + // We know the width and height. + Debug.DCHECK_EQ(5, arraysize(kPositionAdjustmentPattern[0])); + Debug.DCHECK_EQ(5, arraysize(kPositionAdjustmentPattern)); + for (int y = 0; y < 5; ++y) { + for (int x = 0; x < 5; ++x) { + Debug.DCHECK(IsEmpty((*matrix)(y_start + y, x_start + x))); + (*matrix)(y_start + y, x_start + x) = + kPositionAdjustmentPattern[y][x]; + } + } + } + + private static void EmbedPositionDetectionPattern(final int x_start, + final int y_start, + QRCodeMatrix *matrix) { + // We know the width and height. + Debug.DCHECK_EQ(7, arraysize(kPositionDetectionPattern[0])); + Debug.DCHECK_EQ(7, arraysize(kPositionDetectionPattern)); + for (int y = 0; y < 7; ++y) { + for (int x = 0; x < 7; ++x) { + Debug.DCHECK(IsEmpty((*matrix)(y_start + y, x_start + x))); + (*matrix)(y_start + y, x_start + x) = + kPositionDetectionPattern[y][x]; + } + } + } + + // Embed position detection patterns and surrounding + // vertical/horizontal separators. + private static void EmbedPositionDetectionPatternsAndSeparators(QRCodeMatrix *matrix) { + // Embed three big squares at corners. + final int pdp_width = arraysize(kPositionDetectionPattern[0]); + // Left top corner. + EmbedPositionDetectionPattern(0, 0, matrix); + // Right top corner. + EmbedPositionDetectionPattern(matrix.width() - pdp_width, 0, matrix); + // Left bottom corner. + EmbedPositionDetectionPattern(0, matrix.width() - pdp_width, matrix); + + // Embed horizontal separation patterns around the squares. + final int hsp_width = arraysize(kHorizontalSeparationPattern[0]); + // Left top corner. + EmbedHorizontalSeparationPattern(0, hsp_width - 1, matrix); + // Right top corner. + EmbedHorizontalSeparationPattern(matrix.width() - hsp_width, + hsp_width - 1, matrix); + // Left bottom corner. + EmbedHorizontalSeparationPattern(0, matrix.width() - hsp_width, matrix); + + // Embed vertical separation patterns around the squares. + final int vsp_height = arraysize(kVerticalSeparationPattern); + // Left top corner. + EmbedVerticalSeparationPattern(vsp_height, 0, matrix); + // Right top corner. + EmbedVerticalSeparationPattern(matrix.height() - vsp_height - 1, 0, matrix); + // Left bottom corner. + EmbedVerticalSeparationPattern(vsp_height, matrix.height() - vsp_height, + matrix); + } + + // Embed position adjustment patterns if need be. + private static void MaybeEmbedPositionAdjustmentPatterns(final int version, + QRCodeMatrix *matrix) { + if (version < 2) { // The patterns appear if version >= 2 + return; + } + final int index = version - 1; + final int *coordinates = + kPositionAdjustmentPatternCoordinateTable[index]; + final int num_coordinates = + arraysize(kPositionAdjustmentPatternCoordinateTable[index]); + for (int i = 0; i < num_coordinates; ++i) { + for (int j = 0; j < num_coordinates; ++j) { + final int y = coordinates[i]; + final 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)(y, x))) { + // -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/core/src/com/google/zxing/qrcode/encoder/QRCode.java b/core/src/com/google/zxing/qrcode/encoder/QRCode.java new file mode 100644 index 000000000..48aca0217 --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/QRCode.java @@ -0,0 +1,350 @@ +/* + * 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: QRCodeMatrix needs to be renamed Matrix and built as a new class. +// +// template class Array2D; +// typedef Array2D QRCodeMatrix; +// #include "util/array/array2d-inl.h" + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class QRCode { + + // Magic numbers. + public static final int kMinVersion = 1; + public static final int kMaxVersion = 40; + // For matrix width, see 7.3.1 of JISX0510:2004 (p.5). + public static final int kMinMatrixWidth = 21; // Version 1 + public static final int kMaxMatrixWidth = 177; // Version 40 (21 + 4 * (40 -1)). + public static final int kNumMaskPatterns = 8; + + // See table 3 of JISX0510:2004 (p.16) + private static final int kNumBitsTable[][] = { + // NUMERIC ALPHANUMERIC 8BIT_BYTE KANJI + { 10, 9, 8, 8 }, // Version 1-9 + { 12, 11, 16, 10 }, // Version 10-26 + { 14, 13, 16, 12 }, // Version 27-40 + }; + + // JAVAPORT: Do not remove trailing slashes yet. There are very likely conflicts with local + // variables and parameters which will introduce insidious bugs. + private Mode mode_; + private ECLevel ec_level_; + private int version_; + private int matrix_width_; + private int mask_pattern_; + private int num_total_bytes_; + private int num_data_bytes_; + private int num_ec_bytes_; + private int num_rs_blocks_; + private QRCodeMatrix *matrix_; + + + // They call encoding "mode". The modes are defined in 8.3 of + // JISX0510:2004 (p.14). It's unlikely (probably we will not + // support complicated modes) but if you add an item to this, please + // also add it to ModeToString(), GetModeCode(), + // GetNumBitsForLength(), Encoder.AppendBytes(), + // Encoder.ChooseMode(). + public enum Mode { + MODE_UNDEFINED = -1, + MODE_NUMERIC, + MODE_ALPHANUMERIC, + MODE_8BIT_BYTE, + MODE_KANJI, // Shift_JIS + // The following modes are unimplemented. + // MODE_ECI, + // MODE_MIXED, + // MODE_CONCATENATED, + // MODE_FNC1, + NUM_MODES, // Always keep this at the end. + }; + + // The error correction levels are defined in the table 22 of + // JISX0510:2004 (p.45). It's very unlikely (we've already covered + // all of them!) but if you add an item to this, please also add it + // to ECLevelToString() and GetECLevelCode(). + public enum ECLevel { + EC_LEVEL_UNDEFINED = -1, + // They don't have names in the standard! + EC_LEVEL_L, // 7% of corruption can be recovered. + EC_LEVEL_M, // 15% + EC_LEVEL_Q, // 25% + EC_LEVEL_H, // 30% + NUM_EC_LEVELS, // Always keep this at the end. + }; + + public QRCode() { + mode_ = MODE_UNDEFINED; + ec_level_ = EC_LEVEL_UNDEFINED; + version_ = -1; + matrix_width_ = -1; + mask_pattern_ = -1; + num_total_bytes_ = -1; + num_data_bytes_ = -1; + num_ec_bytes_ = -1; + num_rs_blocks_ = -1; + matrix_ = null; + } + + // Mode of the QR Code. + public Mode mode() { return mode_; } + // Error correction level of the QR Code. + public ECLevel ec_level() { return ec_level_; } + // Version of the QR Code. The bigger size, the bigger version. + public int version() { return version_; } + // Matrix width of the QR Code. + public int matrix_width() { return matrix_width_; } + // Mask pattern of the QR Code. + public int mask_pattern() { return mask_pattern_; } + // Number of total bytes in the QR Code. + public int num_total_bytes() { return num_total_bytes_; } + // Number of data bytes in the QR Code. + public int num_data_bytes() { return num_data_bytes_; } + // Number of error correction bytes in the QR Code. + public int num_ec_bytes() { return num_ec_bytes_; } + // Number of Reedsolomon blocks in the QR Code. + public int num_rs_blocks() { return num_rs_blocks_; } + // Matrix data of the QR Code. + public final QRCodeMatrix* matrix() { return matrix_; } + + // Return the value of the module (cell) pointed by "x" and "y" in + // the matrix of the QR Code. They call cells in the matrix + // "modules". 1 represents a black cell, and 0 represents a white + // cell. + // + // Note that the class internally used Array2D. You should access + // cells in row-major order for cache efficiency. Example: + // + // for (int y = 0; y < qrcode.matrix_width(); ++y) { + // for (int x = 0; x < qrcode.matrix_width(); ++x) { + // DoSomething(qrcode.at(x, y)); + // } + // } + // + public int at(int x, int y) { + // The value must be zero or one. + Debug.DCHECK((*matrix_)(y, x) == 0 || (*matrix_)(y, x) == 1); + return (*matrix_)(y, x); + } + + // Checks all the member variables are set properly. Returns true + // on success. Otherwise, returns false. + public boolean IsValid() { + return ( + // First check if all version are not uninitialized. + mode_ != MODE_UNDEFINED && + ec_level_ != EC_LEVEL_UNDEFINED && + version_ != -1 && + matrix_width_ != -1 && + mask_pattern_ != -1 && + num_total_bytes_ != -1 && + num_data_bytes_ != -1 && + num_ec_bytes_ != -1 && + num_rs_blocks_ != -1 && + // Then check them in other ways.. + IsValidVersion(version_) && + IsValidMode(mode_) && + IsValidECLevel(ec_level_) && + IsValidMatrixWidth(matrix_width_) && + IsValidMaskPattern(mask_pattern_) && + num_total_bytes_ == num_data_bytes_ + num_ec_bytes_ && + // Matrix stuff. + matrix_ != null && + matrix_width_ == matrix_.width() && + // See 7.3.1 of JISX0510:2004 (p.5). + matrix_width_ == kMinMatrixWidth + (version_ - 1) * 4 && + matrix_.width() == matrix_.height() && // Must be square. + EverythingIsBinary(*matrix_)); + } + + // Return debug String. + public String DebugString() { + String result; + StringAppendF(&result, "<>\n"); + return result; + } + + public void set_mode(Mode value) { mode_ = value; } + public void set_ec_level(ECLevel value) { ec_level_ = value; } + public void set_version(int value) { version_ = value; } + public void set_matrix_width(int value) { matrix_width_ = value; } + public void set_mask_pattern(int value) { mask_pattern_ = value; } + public void set_num_total_bytes(int value) { num_total_bytes_ = value; } + public void set_num_data_bytes(int value) { num_data_bytes_ = value; } + public void set_num_ec_bytes(int value) { num_ec_bytes_ = value; } + public void set_num_rs_blocks(int value) { num_rs_blocks_ = value; } + // This takes ownership of the 2D array. The 2D array will be + // deleted in the destructor of the class. + public void set_matrix(QRCodeMatrix *value) { matrix_ = value; } + + + // Check if "version" is valid. + public static boolean IsValidVersion(final int version) { + return version >= kMinVersion && version <= kMaxVersion; + } + // Check if "mask_pattern" is valid. + public static boolean IsValidECLevel(ECLevel ec_level) { + return ec_level >= 0 && ec_level < NUM_EC_LEVELS; + } + // Check if "mode" is valid. + public static boolean IsValidMode(final QRCode.Mode mode) { + return mode >= 0 && mode < NUM_MODES; + } + // Check if "width" is valid. + public static boolean IsValidMatrixWidth(int width) { + return width >= kMinMatrixWidth && width <= kMaxMatrixWidth; + } + // Check if "mask_pattern" is valid. + public static boolean IsValidMaskPattern(int mask_pattern) { + return mask_pattern >= 0 && mask_pattern < kNumMaskPatterns; + } + + // Convert "ec_level" to String for debugging. + public static final char *ECLevelToString(QRCode.ECLevel ec_level) { + switch (ec_level) { + case QRCode.EC_LEVEL_UNDEFINED: + return "UNDEFINED"; + case QRCode.EC_LEVEL_L: + return "L"; + case QRCode.EC_LEVEL_M: + return "M"; + case QRCode.EC_LEVEL_Q: + return "Q"; + case QRCode.EC_LEVEL_H: + return "H"; + default: + break; + } + return "UNKNOWN"; +} + + // Convert "mode" to String for debugging. + public static final char *ModeToString(QRCode.Mode mode) { + switch (mode) { + case QRCode.MODE_UNDEFINED: + return "UNDEFINED"; + case QRCode.MODE_NUMERIC: + return "NUMERIC"; + case QRCode.MODE_ALPHANUMERIC: + return "ALPHANUMERIC"; + case QRCode.MODE_8BIT_BYTE: + return "8BIT_BYTE"; + case QRCode.MODE_KANJI: + return "KANJI"; + default: + break; + } + return "UNKNOWN"; +} + + // Return the code of error correction level. On error, return -1. + // The codes of error correction levels are defined in the table 22 + // of JISX0510:2004 (p.45). + public static int GetECLevelCode(final QRCode.ECLevel ec_level) { + switch (ec_level) { + case QRCode.EC_LEVEL_L: + return 1; + case QRCode.EC_LEVEL_M: + return 0; + case QRCode.EC_LEVEL_Q: + return 3; + case QRCode.EC_LEVEL_H: + return 2; + default: + break; + } + return -1; // Unknown error correction level. + } + + // Return the code of mode. On error, return -1. + // The codes of modes are defined in the table 2 of JISX0510:2004 + // (p.16). + public static int GetModeCode(final QRCode.Mode mode) { + switch (mode) { + case QRCode.MODE_NUMERIC: + return 1; + case QRCode.MODE_ALPHANUMERIC: + return 2; + case QRCode.MODE_8BIT_BYTE: + return 4; + case QRCode.MODE_KANJI: + return 8; + default: + break; + } + return -1; // Unknown mode. + } + + // Return the number of bits needed for representing the length info + // of QR Code with "version" and "mode". On error, return -1. + public static int GetNumBitsForLength(int version, QRCode.Mode mode) { + if (!IsValidVersion(version)) { + Debug.LOG_ERROR("Invalid version: " + version); + return -1; + } + if (!IsValidMode(mode)) { + Debug.LOG_ERROR("Invalid mode: " + mode); + return -1; + } + if (version >= 1 && version <= 9) { + return kNumBitsTable[0][mode]; + } else if (version >= 10 && version <= 26) { + return kNumBitsTable[1][mode]; + } else if (version >= 27 && version <= 40) { + return kNumBitsTable[2][mode]; + } else { + Debug.LOG_ERROR("Should not reach"); + } + return -1; + } + + // Return true if the all values in the matrix are binary numbers. + // Otherwise, return false. + private static boolean EverythingIsBinary(final Array2D &matrix) { + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + if (!(matrix(y, x) == 0 || matrix(y, x) == 1)) { + // Found non zero/one value. + return false; + } + } + } + return true; + } + +} diff --git a/core/src/com/google/zxing/qrcode/encoder/README b/core/src/com/google/zxing/qrcode/encoder/README new file mode 100644 index 000000000..df85b8874 --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/README @@ -0,0 +1,5 @@ +This package is a work in progress, porting a C++ library to Java. At the moment, it does not +remotely compile or work. It is only checked in so we can collaborate on it. This README will be +removed when the port is finished. + +-- dswitkin 11/13/2008 \ No newline at end of file diff --git a/core/src/com/google/zxing/qrcode/encoder/Renderer.java b/core/src/com/google/zxing/qrcode/encoder/Renderer.java new file mode 100644 index 000000000..789df3e7d --- /dev/null +++ b/core/src/com/google/zxing/qrcode/encoder/Renderer.java @@ -0,0 +1,183 @@ +/* + * 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; + +//class StringPiece; +// #include "third_party/png/png.h" + +/** + * JAVAPORT: This class may get thrown out in the future, or it may turn into the object which + * returns a MonochromeBitmapSource. + * + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported from C++ + */ +public final class Renderer { + + // See 7.3.7 of JISX0510:2004 (p. 11). + private static final int kQuietZoneSize = 4; + + // Render QR Code as PNG image with "cell_size". On success, store + // the result in "result" and return true. On error, return false. + // The recommended cell size for desktop screens is 3. This + // setting generates 87x87 pixels PNG image for version 1 QR Code + // (21x21). 87 = (21 + 4 + 4) * 3. 4 is for surrounding white + // space (they call it quiet zone). + // Sorry for the long function but libpng's API is a bit complecated. +// See http://www.libpng.org/pub/png/libpng-1.2.5-manual.html for + // details. + public static boolean RenderAsPNG(final QRCode &qr_code, int cell_size, + String *result) { + // First, clear the result String. + result.clear(); + + // Create PNG class. + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + null, null, null); + if (png_ptr == null) { + Debug.LOG_ERROR("Unable to create png_strupctp"); + return false; + } + + // Create PNG info. + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == null) { + Debug.LOG_ERROR("Unable to create png_infop"); + png_destroy_write_struct(&png_ptr, (png_infopp)null); + return false; + } + + // Calculate the width of the resulting image. Note that the height + // is equal to the width (i.e. the resulting image is square). + final int image_width = (qr_code.matrix_width() + + kQuietZoneSize * 2) * cell_size; + // Since we use 1-bit color depth, we only need 1 bit per pixel. + final int num_bytes_in_row = image_width / 8 + + (image_width % 8 == 0 ? 0 : 1); + // We'll use this storage later but we should prepare this before + // setjmp() so that this will be deleted on error. Today's lesson + // is that RAII isn't reliable with setjmp/longjmp! + scoped_array row(new char[num_bytes_in_row]); + + // Erorr handling of libpng is a bit tricky. If something bad + // happens in libpng, they call longjmp() to get to here. + if (setjmp(png_ptr.jmpbuf)) { + Debug.LOG_ERROR("Something bad happened in libpng"); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + // Attach the pointer to the result String and the pointer to the + // writer function. + png_set_write_fn(png_ptr, static_cast(result), PNGWriter, null); + + // Set the image information. + png_set_IHDR(png_ptr, info_ptr, image_width, image_width, + 1, // The color depth is 1 (black and white). + PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + // Write the file header information. + png_write_info(png_ptr, info_ptr); + + // Quiet zone at the top. + FillRowWithWhite(num_bytes_in_row, row.get()); + WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size); + // Fill data. + for (int y = 0; y < qr_code.matrix_width(); ++y) { + FillRowWithData(num_bytes_in_row, qr_code, y, cell_size, row.get()); + WriteRowNumTimes(png_ptr, row.get(), cell_size); + } + // Quiet zone at the bottom. + FillRowWithWhite(num_bytes_in_row, row.get()); + WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size); + + // Cleanups for libpng stuff. + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Finally, it's all done! + return true; + } + + // Similar to RenderAsPNG but it renders QR code from data in + // "bytes" with error correction level "ec_level". This is the + // friendliest function in the QR code library. + public static boolean RenderAsPNGFromData(final StringPiece& bytes, + QRCode.ECLevel ec_level, + int cell_size, + String *result) { + QRCode qr_code; + if (!Encoder.Encode(bytes, ec_level, &qr_code)) { + return false; + } + return RenderAsPNG(qr_code, cell_size, result); + } + + // Callback function which gets called by png_write_row(). + private static void PNGWriter(png_structp png_ptr, png_bytep data, png_size_t length) { + String* out = static_cast(png_get_io_ptr(png_ptr)); + out.append(reinterpret_cast(data), length); + } + + // Fill all pixels in "row" with white. + private static void FillRowWithWhite(final int num_bytes_in_row, char *row) { + memset(row, 0xff, num_bytes_in_row); + } + + // Set the bit in "row" pointed by "index" to 1 (1 is for white). + private static void SetBit(final int index, char *row) { + final int byte_index = index / 8; + final int bit_index = index % 8; + row[byte_index] |= 0x80 >> bit_index; + } + + // Fill pixels in "row" with data in "qr_code". + private static void FillRowWithData(final int num_bytes_in_row, + final QRCode &qr_code, + final int y, + final int cell_size, + char *row) { + memset(row, 0, num_bytes_in_row); // Fill all pixels with black. + + int index = 0; + for (int i = 0; i < kQuietZoneSize * cell_size; ++i) { + SetBit(index++, row); // Cells in the quite zone should be white. + } + for (int x = 0; x < qr_code.matrix_width(); ++x) { + for (int i = 0; i < cell_size; ++i) { + if (qr_code.at(x, y) == 0) { // White cell. + SetBit(index, row); + } + ++index; + } + } + for (int i = 0; i < kQuietZoneSize * cell_size; ++i) { + SetBit(index++, row); + } + } + + // Write pixels in "row" to "png_ptr" "num" times. + private static void WriteRowNumTimes(png_structp png_ptr, char *row, final int num) { + for (int i = 0; i < num; ++i) { + png_write_row(png_ptr, reinterpret_cast(row)); + } + } + +}