Committed C# port from Mohamad
[zxing.git] / csharp / qrcode / decoder / FormatInformation.cs
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 using System;\r
17 using com.google.zxing;\r
18 using com.google.zxing.common;\r
19 \r
20 namespace com.google.zxing.qrcode.decoder\r
21 {\r
22     public sealed class FormatInformation\r
23     { \r
24           private static int FORMAT_INFO_MASK_QR = 0x5412;\r
25           /**\r
26            * See ISO 18004:2006, Annex C, Table C.1\r
27            */\r
28           private static readonly int[][] FORMAT_INFO_DECODE_LOOKUP = new int[][] { new int[] { 0x5412, 0x00 }, new int[] { 0x5125, 0x01 }, new int[] { 0x5E7C, 0x02 }, new int[] { 0x5B4B, 0x03 }, new int[] { 0x45F9, 0x04 }, new int[] { 0x40CE, 0x05 }, new int[] { 0x4F97, 0x06 }, new int[] { 0x4AA0, 0x07 }, new int[] { 0x77C4, 0x08 }, new int[] { 0x72F3, 0x09 }, new int[] { 0x7DAA, 0x0A }, new int[] { 0x789D, 0x0B }, new int[] { 0x662F, 0x0C }, new int[] { 0x6318, 0x0D }, new int[] { 0x6C41, 0x0E }, new int[] { 0x6976, 0x0F }, new int[] { 0x1689, 0x10 }, new int[] { 0x13BE, 0x11 }, new int[] { 0x1CE7, 0x12 }, new int[] { 0x19D0, 0x13 }, new int[] { 0x0762, 0x14 }, new int[] { 0x0255, 0x15 }, new int[] { 0x0D0C, 0x16 }, new int[] { 0x083B, 0x17 }, new int[] { 0x355F, 0x18 }, new int[] { 0x3068, 0x19 }, new int[] { 0x3F31, 0x1A }, new int[] { 0x3A06, 0x1B }, new int[] { 0x24B4, 0x1C }, new int[] { 0x2183, 0x1D }, new int[] { 0x2EDA, 0x1E }, new int[] { 0x2BED, 0x1F } };\r
29                 \r
30           /**\r
31            * Offset i holds the number of 1 bits in the binary representation of i\r
32            */\r
33           private static int[] BITS_SET_IN_HALF_BYTE =\r
34               {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};\r
35 \r
36           private ErrorCorrectionLevel errorCorrectionLevel;\r
37           private byte dataMask;\r
38 \r
39           private FormatInformation(int formatInfo) {\r
40             // Bits 3,4\r
41             errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);\r
42             // Bottom 3 bits\r
43             dataMask = (byte) (formatInfo & 0x07);\r
44           }\r
45 \r
46           public static int numBitsDiffering(int a, int b) {\r
47               a ^= b; // a now has a 1 bit exactly where its bit differs with b's\r
48               // Count bits set quickly with a series of lookups:\r
49               return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 4) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 8) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 12) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 16) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 20) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 24) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 28) & 0x0F)];\r
50           }\r
51 \r
52           /**\r
53            * @param rawFormatInfo\r
54            * @return\r
55            */\r
56           public static FormatInformation decodeFormatInformation(int rawFormatInfo){\r
57               try{\r
58                 FormatInformation formatInfo = doDecodeFormatInformation(rawFormatInfo);\r
59                 if (formatInfo != null) {\r
60                   return formatInfo;\r
61                 }\r
62                 // Should return null, but, some QR codes apparently\r
63                 // do not mask this info. Try again, first masking the raw bits so\r
64                 // the function will unmask\r
65                 return doDecodeFormatInformation(rawFormatInfo ^ FORMAT_INFO_MASK_QR);\r
66               }catch(Exception e){\r
67                   throw new ReaderException(e.Message);\r
68               }            \r
69           }\r
70 \r
71           private static FormatInformation doDecodeFormatInformation(int rawFormatInfo) {\r
72             // Unmask:\r
73             int unmaskedFormatInfo = rawFormatInfo ^ FORMAT_INFO_MASK_QR;\r
74             // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing\r
75             int bestDifference = int.MaxValue;\r
76             int bestFormatInfo = 0;\r
77             for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.Length; i++) {\r
78               int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];\r
79               int targetInfo = decodeInfo[0];\r
80               if (targetInfo == unmaskedFormatInfo) {\r
81                 // Found an exact match\r
82                 return new FormatInformation(decodeInfo[1]);\r
83               }\r
84               int bitsDifference = numBitsDiffering(unmaskedFormatInfo, targetInfo);\r
85               if (bitsDifference < bestDifference) {\r
86                 bestFormatInfo = decodeInfo[1];\r
87                 bestDifference = bitsDifference;\r
88               }\r
89             }\r
90             if (bestDifference <= 3) {\r
91               return new FormatInformation(bestFormatInfo);\r
92             }\r
93             return null;\r
94           }\r
95 \r
96           public ErrorCorrectionLevel getErrorCorrectionLevel() {\r
97             return errorCorrectionLevel;\r
98           }\r
99 \r
100           public byte getDataMask() {\r
101             return dataMask;\r
102           }\r
103 \r
104           public int hashCode() {\r
105             return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;\r
106           }\r
107 \r
108           public bool equals(Object o) {\r
109             if (!(o.GetType() == typeof(FormatInformation))){\r
110               return false;\r
111             }\r
112             FormatInformation other = (FormatInformation) o;\r
113             return this.errorCorrectionLevel == other.errorCorrectionLevel &&\r
114                 this.dataMask == other.dataMask;\r
115           }\r
116     \r
117     }\r
118 }