Another attack on integrating encoder and decoder: Version is done. Attempted to...
[zxing.git] / core / src / com / google / zxing / qrcode / decoder / DataBlock.java
1 /*\r
2  * Copyright 2007 ZXing authors\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 package com.google.zxing.qrcode.decoder;\r
18 \r
19 /**\r
20  * <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into\r
21  * multiple blocks, each of which is a unit of data and error-correction codewords. Each\r
22  * is represented by an instance of this class.</p>\r
23  *\r
24  * @author Sean Owen\r
25  */\r
26 final class DataBlock {\r
27 \r
28   private final int numDataCodewords;\r
29   private final byte[] codewords;\r
30 \r
31   private DataBlock(int numDataCodewords, byte[] codewords) {\r
32     this.numDataCodewords = numDataCodewords;\r
33     this.codewords = codewords;\r
34   }\r
35 \r
36   /**\r
37    * <p>When QR Codes use multiple data blocks, they are actually interleave the bytes of each of them.\r
38    * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This\r
39    * method will separate the data into original blocks.</p>\r
40    *\r
41    * @param rawCodewords bytes as read directly from the QR Code\r
42    * @param version version of the QR Code\r
43    * @param ecLevel error-correction level of the QR Code\r
44    * @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the\r
45    *         QR Code\r
46    */\r
47   static DataBlock[] getDataBlocks(byte[] rawCodewords,\r
48                                    Version version,\r
49                                    ErrorCorrectionLevel ecLevel) {\r
50     // Figure out the number and size of data blocks used by this version and\r
51     // error correction level\r
52     Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\r
53 \r
54     // First count the total number of data blocks\r
55     int totalBlocks = 0;\r
56     Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();\r
57     for (int i = 0; i < ecBlockArray.length; i++) {\r
58       totalBlocks += ecBlockArray[i].getCount();\r
59     }\r
60 \r
61     // Now establish DataBlocks of the appropriate size and number of data codewords\r
62     DataBlock[] result = new DataBlock[totalBlocks];\r
63     int numResultBlocks = 0;\r
64     for (int j = 0; j < ecBlockArray.length; j++) {\r
65       Version.ECB ecBlock = ecBlockArray[j];\r
66       for (int i = 0; i < ecBlock.getCount(); i++) {\r
67         int numDataCodewords = ecBlock.getDataCodewords();\r
68         int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;\r
69         result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);\r
70       }\r
71     }\r
72 \r
73     // All blocks have the same amount of data, except that the last n\r
74     // (where n may be 0) have 1 more byte. Figure out where these start.\r
75     int shorterBlocksTotalCodewords = result[0].codewords.length;\r
76     int longerBlocksStartAt = result.length - 1;\r
77     while (longerBlocksStartAt >= 0) {\r
78       int numCodewords = result[longerBlocksStartAt].codewords.length;\r
79       if (numCodewords == shorterBlocksTotalCodewords) {\r
80         break;\r
81       }\r
82       if (numCodewords != shorterBlocksTotalCodewords + 1) {\r
83         throw new IllegalArgumentException("Data block sizes differ by more than 1");\r
84       }\r
85       longerBlocksStartAt--;\r
86     }\r
87     longerBlocksStartAt++;\r
88 \r
89     int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();\r
90     // The last elements of result may be 1 element longer;\r
91     // first fill out as many elements as all of them have\r
92     int rawCodewordsOffset = 0;\r
93     for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {\r
94       for (int j = 0; j < numResultBlocks; j++) {\r
95         result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];\r
96       }\r
97     }\r
98     // Fill out the last data block in the longer ones\r
99     for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {\r
100       result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];\r
101     }\r
102     // Now add in error correction blocks\r
103     int max = result[0].codewords.length;\r
104     for (int i = shorterBlocksNumDataCodewords; i < max; i++) {\r
105       for (int j = 0; j < numResultBlocks; j++) {\r
106         int iOffset = j < longerBlocksStartAt ? i : i + 1;\r
107         result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];\r
108       }\r
109     }\r
110 \r
111     if (rawCodewordsOffset != rawCodewords.length) {\r
112       throw new IllegalArgumentException();\r
113     }\r
114 \r
115     return result;\r
116   }\r
117 \r
118   int getNumDataCodewords() {\r
119     return numDataCodewords;\r
120   }\r
121 \r
122   byte[] getCodewords() {\r
123     return codewords;\r
124   }\r
125 \r
126 }\r