Committed C# port from Mohamad
[zxing.git] / csharp / qrcode / decoder / DataBlock.cs
1 /*\r
2 * Licensed under the Apache License, Version 2.0 (the "License");\r
3 * you may not use this file except in compliance with the License.\r
4 * You may obtain a copy of the License at\r
5 *\r
6 *      http://www.apache.org/licenses/LICENSE-2.0\r
7 *\r
8 * Unless required by applicable law or agreed to in writing, software\r
9 * distributed under the License is distributed on an "AS IS" BASIS,\r
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
11 * See the License for the specific language governing permissions and\r
12 * limitations under the License.\r
13 */\r
14 \r
15 using System;\r
16 namespace com.google.zxing.qrcode.decoder\r
17 {\r
18 \r
19     /// <summary> <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into\r
20     /// multiple blocks, each of which is a unit of data and error-correction codewords. Each\r
21     /// is represented by an instance of this class.</p>\r
22     /// \r
23     /// </summary>\r
24     /// <author>  srowen@google.com (Sean Owen)\r
25     /// </author>\r
26     public sealed class DataBlock\r
27     {\r
28         internal int NumDataCodewords\r
29         {\r
30             get\r
31             {\r
32                 return numDataCodewords;\r
33             }\r
34 \r
35         }\r
36         internal sbyte[] Codewords\r
37         {\r
38             get\r
39             {\r
40                 return codewords;\r
41             }\r
42 \r
43         }\r
44 \r
45         //UPGRADE_NOTE: Final was removed from the declaration of 'numDataCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
46         private int numDataCodewords;\r
47         //UPGRADE_NOTE: Final was removed from the declaration of 'codewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
48         private sbyte[] codewords;\r
49 \r
50         public DataBlock(int numDataCodewords, sbyte[] codewords)\r
51         {\r
52             this.numDataCodewords = numDataCodewords;\r
53             this.codewords = codewords;\r
54         }\r
55 \r
56         /// <summary> <p>When QR Codes use multiple data blocks, they are actually interleave the bytes of each of them.\r
57         /// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This\r
58         /// method will separate the data into original blocks.</p>\r
59         /// \r
60         /// </summary>\r
61         /// <param name="rawCodewords">bytes as read directly from the QR Code\r
62         /// </param>\r
63         /// <param name="version">version of the QR Code\r
64         /// </param>\r
65         /// <param name="ecLevel">error-correction level of the QR Code\r
66         /// </param>\r
67         /// <returns> {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the\r
68         /// QR Code\r
69         /// </returns>\r
70         internal static DataBlock[] getDataBlocks(sbyte[] rawCodewords, Version version, ErrorCorrectionLevel ecLevel)\r
71         {\r
72             // Figure out the number and size of data blocks used by this version and\r
73             // error correction level\r
74             Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\r
75 \r
76             // First count the total number of data blocks\r
77             int totalBlocks = 0;\r
78             Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();\r
79             for (int i = 0; i < ecBlockArray.Length; i++)\r
80             {\r
81                 totalBlocks += ecBlockArray[i].getCount();\r
82             }\r
83 \r
84             // Now establish DataBlocks of the appropriate size and number of data codewords\r
85             DataBlock[] result = new DataBlock[totalBlocks];\r
86             int numResultBlocks = 0;\r
87             for (int j = 0; j < ecBlockArray.Length; j++)\r
88             {\r
89                 Version.ECB ecBlock = ecBlockArray[j];\r
90                 for (int i = 0; i < ecBlock.getCount(); i++)\r
91                 {\r
92                     int numDataCodewords = ecBlock.getDataCodewords();\r
93                     int numBlockCodewords = ecBlocks.getTotalECCodewords() + numDataCodewords;\r
94                     result[numResultBlocks++] = new DataBlock(numDataCodewords, new sbyte[numBlockCodewords]);\r
95                 }\r
96             }\r
97 \r
98             // All blocks have the same amount of data, except that the last n\r
99             // (where n may be 0) have 1 more byte. Figure out where these start.\r
100             int shorterBlocksTotalCodewords = result[0].codewords.Length;\r
101             int longerBlocksStartAt = result.Length - 1;\r
102             while (longerBlocksStartAt >= 0)\r
103             {\r
104                 int numCodewords = result[longerBlocksStartAt].codewords.Length;\r
105                 if (numCodewords == shorterBlocksTotalCodewords)\r
106                 {\r
107                     break;\r
108                 }\r
109                 if (numCodewords != shorterBlocksTotalCodewords + 1)\r
110                 {\r
111                     throw new System.SystemException("Data block sizes differ by more than 1");\r
112                 }\r
113                 longerBlocksStartAt--;\r
114             }\r
115             longerBlocksStartAt++;\r
116 \r
117             int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getTotalECCodewords();\r
118             // The last elements of result may be 1 element longer;\r
119             // first fill out as many elements as all of them have\r
120             int rawCodewordsOffset = 0;\r
121             for (int i = 0; i < shorterBlocksNumDataCodewords; i++)\r
122             {\r
123                 for (int j = 0; j < numResultBlocks; j++)\r
124                 {\r
125                     result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];\r
126                 }\r
127             }\r
128             // Fill out the last data block in the longer ones\r
129             for (int j = longerBlocksStartAt; j < numResultBlocks; j++)\r
130             {\r
131                 result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];\r
132             }\r
133             // Now add in error correction blocks\r
134             int max = result[0].codewords.Length;\r
135             for (int i = shorterBlocksNumDataCodewords; i < max; i++)\r
136             {\r
137                 for (int j = 0; j < numResultBlocks; j++)\r
138                 {\r
139                     int iOffset = j < longerBlocksStartAt ? i : i + 1;\r
140                     result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];\r
141                 }\r
142             }\r
143 \r
144             if (rawCodewordsOffset != rawCodewords.Length)\r
145             {\r
146                 throw new System.SystemException();\r
147             }\r
148 \r
149             return result;\r
150         }\r
151     }\r
152 }