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)); + } + } + +}