Unify handling of EC level between encoder and decoder
[zxing.git] / core / src / com / google / zxing / qrcode / encoder / QRCode.java
1 /*
2  * Copyright 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.qrcode.encoder;
18
19 import com.google.zxing.common.ByteMatrix;
20 import com.google.zxing.WriterException;
21 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
22
23 /**
24  * @author satorux@google.com (Satoru Takabayashi) - creator
25  * @author dswitkin@google.com (Daniel Switkin) - ported from C++
26  */
27 public final class QRCode {
28
29   // Magic numbers.
30   private static final int MIN_VERSION = 1;
31   private static final int MAX_VERSION = 40;
32   // For matrix width, see 7.3.1 of JISX0510:2004 (p.5).
33   private static final int MIN_MATRIX_WIDTH = 21;  // Version 1
34   private static final int MAX_MATRIX_WIDTH = 177;  // Version 40 (21 + 4 * (40 -1)).
35   public static final int NUM_MASK_PATTERNS = 8;
36
37   // See table 3 of JISX0510:2004 (p.16)
38   private static final int[][] NUM_BITS_TABLE = {
39       // NUMERIC  ALPHANUMERIC  8BIT_BYTE  KANJI
40       {       10,            9,         8,     8 },  // Version 1-9
41       {       12,           11,        16,    10 },  // Version 10-26
42       {       14,           13,        16,    12 },  // Version 27-40
43   };
44
45   private int mode;
46   private ErrorCorrectionLevel ecLevel;
47   private int version;
48   private int matrixWidth;
49   private int maskPattern;
50   private int numTotalBytes;
51   private int numDataBytes;
52   private int numECBytes;
53   private int numRSBlocks;
54   private ByteMatrix matrix;
55
56
57   // They call encoding "mode". The modes are defined in 8.3 of JISX0510:2004 (p.14). It's unlikely
58   // (probably we will not support complicated modes) but if you add an item to this, please also
59   // add it to modeToString(), getModeCode(), getNumBitsForLength(), Encoder.appendBytes(), and
60   // Encoder.chooseMode().
61   //
62   // JAVAPORT: These used to be C++ enums, but the code evaluates them as integers, and requires
63   // negative values. I don't want to take the ParsedResultType approach of a class full of statics
64   // of that class's type. The best compromise here is integer constants.
65   //
66   // Formerly enum Mode
67   public static final int MODE_UNDEFINED = -1;
68   public static final int MODE_NUMERIC = 0;
69   public static final int MODE_ALPHANUMERIC = 1;
70   public static final int MODE_8BIT_BYTE = 2;
71   public static final int MODE_KANJI = 3;  // Shift_JIS
72   // The following modes are unimplemented.
73   // MODE_ECI,
74   // MODE_MIXED,
75   // MODE_CONCATENATED,
76   // MODE_FNC1,
77   public static final int NUM_MODES = 4;
78
79   public QRCode() {
80     mode = MODE_UNDEFINED;
81     ecLevel = null;
82     version = -1;
83     matrixWidth = -1;
84     maskPattern = -1;
85     numTotalBytes = -1;
86     numDataBytes = -1;
87     numECBytes = -1;
88     numRSBlocks = -1;
89     matrix = null;
90   }
91
92   // Mode of the QR Code.
93   public int getMode() {
94     return mode;
95   }
96
97   // Error correction level of the QR Code.
98   public ErrorCorrectionLevel getECLevel() {
99     return ecLevel;
100   }
101
102   // Version of the QR Code.  The bigger size, the bigger version.
103   public int getVersion() {
104     return version;
105   }
106
107   // ByteMatrix width of the QR Code.
108   public int getMatrixWidth() {
109     return matrixWidth;
110   }
111
112   // Mask pattern of the QR Code.
113   public int getMaskPattern() {
114     return maskPattern;
115   }
116
117   // Number of total bytes in the QR Code.
118   public int getNumTotalBytes() {
119     return numTotalBytes;
120   }
121
122   // Number of data bytes in the QR Code.
123   public int getNumDataBytes() {
124     return numDataBytes;
125   }
126
127   // Number of error correction bytes in the QR Code.
128   public int getNumECBytes() {
129     return numECBytes;
130   }
131
132   // Number of Reedsolomon blocks in the QR Code.
133   public int getNumRSBlocks() {
134     return numRSBlocks;
135   }
136
137   // ByteMatrix data of the QR Code.
138   public final ByteMatrix getMatrix() {
139     return matrix;
140   }
141   
142
143   // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
144   // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
145   public int at(int x, int y) {
146     // The value must be zero or one.
147     int value = matrix.get(y, x);
148     if (!(value == 0 || value == 1)) {
149       // this is really like an assert... not sure what better exception to use?
150       throw new RuntimeException("Bad value");
151     }
152     return value;
153   }
154
155   // Checks all the member variables are set properly. Returns true on success. Otherwise, returns
156   // false.
157   public boolean isValid() {
158     return (
159         // First check if all version are not uninitialized.
160         mode != MODE_UNDEFINED &&
161             ecLevel != null &&
162             version != -1 &&
163             matrixWidth != -1 &&
164             maskPattern != -1 &&
165             numTotalBytes != -1 &&
166             numDataBytes != -1 &&
167             numECBytes != -1 &&
168             numRSBlocks != -1 &&
169             // Then check them in other ways..
170             isValidVersion(version) &&
171             isValidMode(mode) &&
172             isValidMatrixWidth(matrixWidth) &&
173             isValidMaskPattern(maskPattern) &&
174             numTotalBytes == numDataBytes + numECBytes &&
175             // ByteMatrix stuff.
176             matrix != null &&
177             matrixWidth == matrix.width() &&
178             // See 7.3.1 of JISX0510:2004 (p.5).
179             matrixWidth == MIN_MATRIX_WIDTH + (version - 1) * 4 &&
180             matrix.width() == matrix.height()); // Must be square.
181   }
182
183   // Return debug String.
184   public String toString() {
185     StringBuffer result = new StringBuffer();
186     result.append("<<\n");
187     result.append(" mode: ");
188     result.append(modeToString(mode));
189     result.append("\n ecLevel: ");
190     result.append(ecLevel);
191     result.append("\n version: ");
192     result.append(version);
193     result.append("\n matrixWidth: ");
194     result.append(matrixWidth);
195     result.append("\n maskPattern: ");
196     result.append(maskPattern);
197     result.append("\n numTotalBytes: ");
198     result.append(numTotalBytes);
199     result.append("\n numDataBytes: ");
200     result.append(numDataBytes);
201     result.append("\n numECBytes: ");
202     result.append(numECBytes);
203     result.append("\n numRSBlocks: ");
204     result.append(numRSBlocks);
205     if (matrix == null) {
206       result.append("\n matrix: null\n");
207     } else {
208       result.append("\n matrix:\n");
209       result.append(matrix.toString());
210     }
211     result.append(">>\n");
212     return result.toString();
213   }
214
215   public void setMode(int value) {
216     mode = value;
217   }
218
219   public void setECLevel(ErrorCorrectionLevel value) {
220     ecLevel = value;
221   }
222
223   public void setVersion(int value) {
224     version = value;
225   }
226
227   public void setMatrixWidth(int value) {
228     matrixWidth = value;
229   }
230
231   public void setMaskPattern(int value) {
232     maskPattern = value;
233   }
234
235   public void setNumTotalBytes(int value) {
236     numTotalBytes = value;
237   }
238
239   public void setNumDataBytes(int value) {
240     numDataBytes = value;
241   }
242
243   public void setNumECBytes(int value) {
244     numECBytes = value;
245   }
246
247   public void setNumRSBlocks(int value) {
248     numRSBlocks = value;
249   }
250
251   // This takes ownership of the 2D array.
252   public void setMatrix(ByteMatrix value) {
253     matrix = value;
254   }
255
256   // Check if "version" is valid.
257   public static boolean isValidVersion(final int version) {
258     return version >= MIN_VERSION && version <= MAX_VERSION;
259   }
260
261   // Check if "mode" is valid.
262   public static boolean isValidMode(final int mode) {
263     return mode >= 0 && mode < NUM_MODES;
264   }
265
266   // Check if "width" is valid.
267   public static boolean isValidMatrixWidth(int width) {
268     return width >= MIN_MATRIX_WIDTH && width <= MAX_MATRIX_WIDTH;
269   }
270
271   // Check if "mask_pattern" is valid.
272   public static boolean isValidMaskPattern(int maskPattern) {
273     return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
274   }
275
276   // Convert "mode" to String for debugging.
277   public static String modeToString(int mode) {
278     switch (mode) {
279       case QRCode.MODE_UNDEFINED:
280         return "UNDEFINED";
281       case QRCode.MODE_NUMERIC:
282         return "NUMERIC";
283       case QRCode.MODE_ALPHANUMERIC:
284         return "ALPHANUMERIC";
285       case QRCode.MODE_8BIT_BYTE:
286         return "8BIT_BYTE";
287       case QRCode.MODE_KANJI:
288         return "KANJI";
289       default:
290         break;
291     }
292     return "UNKNOWN";
293   }
294
295   // Return the code of mode. On error, return -1. The codes of modes are defined in the table 2 of
296   // JISX0510:2004 (p.16).
297   public static int getModeCode(final int mode) throws WriterException {
298     switch (mode) {
299       case QRCode.MODE_NUMERIC:
300         return 1;
301       case QRCode.MODE_ALPHANUMERIC:
302         return 2;
303       case QRCode.MODE_8BIT_BYTE:
304         return 4;
305       case QRCode.MODE_KANJI:
306         return 8;
307       default:
308         throw new WriterException("Unknown mode: " + mode);
309     }
310   }
311
312   // Return the number of bits needed for representing the length info of QR Code with "version" and
313   // "mode". On error, return -1.
314   static int getNumBitsForLength(int version, int mode) {
315     if (!isValidVersion(version)) {
316       throw new IllegalArgumentException("Invalid version: " + version);
317     }
318     if (!isValidMode(mode)) {
319       throw new IllegalArgumentException("Invalid mode: " + mode);
320     }
321     if (version >= 1 && version <= 9) {
322       return NUM_BITS_TABLE[0][mode];
323     } else if (version >= 10 && version <= 26) {
324       return NUM_BITS_TABLE[1][mode];
325     } else if (version >= 27 && version <= 40) {
326       return NUM_BITS_TABLE[2][mode];
327     }
328     throw new IllegalArgumentException("Bad version: " + version);
329   }
330
331   // Return true if the all values in the matrix are binary numbers.
332   //
333   // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
334   // production. I'm leaving it because it may be useful for testing. It should be removed entirely
335   // if ByteMatrix is changed never to contain a -1.
336   /*
337   private static boolean EverythingIsBinary(final ByteMatrix matrix) {
338     for (int y = 0; y < matrix.height(); ++y) {
339       for (int x = 0; x < matrix.width(); ++x) {
340         int value = matrix.get(y, x);
341         if (!(value == 0 || value == 1)) {
342           // Found non zero/one value.
343           return false;
344         }
345       }
346     }
347     return true;
348   }
349    */
350
351 }