New C# port from Suraj Supekar
[zxing.git] / csharp / qrcode / encoder / MaskUtil.cs
1 /*\r
2 * Copyright 2008 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 ByteMatrix = com.google.zxing.common.ByteMatrix;\r
18 namespace com.google.zxing.qrcode.encoder\r
19 {\r
20         \r
21         /// <author>  satorux@google.com (Satoru Takabayashi) - creator\r
22         /// </author>\r
23         /// <author>  dswitkin@google.com (Daniel Switkin) - ported from C++\r
24         /// </author>\r
25         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
26         /// </author>\r
27         public sealed class MaskUtil\r
28         {\r
29                 \r
30                 private MaskUtil()\r
31                 {\r
32                         // do nothing\r
33                 }\r
34                 \r
35                 // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and\r
36                 // give penalty to them. Example: 00000 or 11111.\r
37                 public static int applyMaskPenaltyRule1(ByteMatrix matrix)\r
38                 {\r
39                         return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);\r
40                 }\r
41                 \r
42                 // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give\r
43                 // penalty to them.\r
44                 public static int applyMaskPenaltyRule2(ByteMatrix matrix)\r
45                 {\r
46                         int penalty = 0;\r
47                         sbyte[][] array = matrix.Array;\r
48                         int width = matrix.Width;\r
49                         int height = matrix.Height;\r
50                         for (int y = 0; y < height - 1; ++y)\r
51                         {\r
52                                 for (int x = 0; x < width - 1; ++x)\r
53                                 {\r
54                                         int value_Renamed = array[y][x];\r
55                                         if (value_Renamed == array[y][x + 1] && value_Renamed == array[y + 1][x] && value_Renamed == array[y + 1][x + 1])\r
56                                         {\r
57                                                 penalty += 3;\r
58                                         }\r
59                                 }\r
60                         }\r
61                         return penalty;\r
62                 }\r
63                 \r
64                 // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or\r
65                 // 10111010000, and give penalty to them.  If we find patterns like 000010111010000, we give\r
66                 // penalties twice (i.e. 40 * 2).\r
67                 public static int applyMaskPenaltyRule3(ByteMatrix matrix)\r
68                 {\r
69                         int penalty = 0;\r
70                         sbyte[][] array = matrix.Array;\r
71                         int width = matrix.Width;\r
72                         int height = matrix.Height;\r
73                         for (int y = 0; y < height; ++y)\r
74                         {\r
75                                 for (int x = 0; x < width; ++x)\r
76                                 {\r
77                                         // Tried to simplify following conditions but failed.\r
78                                         if (x + 6 < width && array[y][x] == 1 && array[y][x + 1] == 0 && array[y][x + 2] == 1 && array[y][x + 3] == 1 && array[y][x + 4] == 1 && array[y][x + 5] == 0 && array[y][x + 6] == 1 && ((x + 10 < width && array[y][x + 7] == 0 && array[y][x + 8] == 0 && array[y][x + 9] == 0 && array[y][x + 10] == 0) || (x - 4 >= 0 && array[y][x - 1] == 0 && array[y][x - 2] == 0 && array[y][x - 3] == 0 && array[y][x - 4] == 0)))\r
79                                         {\r
80                                                 penalty += 40;\r
81                                         }\r
82                                         if (y + 6 < height && array[y][x] == 1 && array[y + 1][x] == 0 && array[y + 2][x] == 1 && array[y + 3][x] == 1 && array[y + 4][x] == 1 && array[y + 5][x] == 0 && array[y + 6][x] == 1 && ((y + 10 < height && array[y + 7][x] == 0 && array[y + 8][x] == 0 && array[y + 9][x] == 0 && array[y + 10][x] == 0) || (y - 4 >= 0 && array[y - 1][x] == 0 && array[y - 2][x] == 0 && array[y - 3][x] == 0 && array[y - 4][x] == 0)))\r
83                                         {\r
84                                                 penalty += 40;\r
85                                         }\r
86                                 }\r
87                         }\r
88                         return penalty;\r
89                 }\r
90                 \r
91                 // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give\r
92                 // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:\r
93                 // -   0% => 100\r
94                 // -  40% =>  20\r
95                 // -  45% =>  10\r
96                 // -  50% =>   0\r
97                 // -  55% =>  10\r
98                 // -  55% =>  20\r
99                 // - 100% => 100\r
100                 public static int applyMaskPenaltyRule4(ByteMatrix matrix)\r
101                 {\r
102                         int numDarkCells = 0;\r
103                         sbyte[][] array = matrix.Array;\r
104                         int width = matrix.Width;\r
105                         int height = matrix.Height;\r
106                         for (int y = 0; y < height; ++y)\r
107                         {\r
108                                 for (int x = 0; x < width; ++x)\r
109                                 {\r
110                                         if (array[y][x] == 1)\r
111                                         {\r
112                                                 numDarkCells += 1;\r
113                                         }\r
114                                 }\r
115                         }\r
116                         int numTotalCells = matrix.Height * matrix.Width;\r
117                         double darkRatio = (double) numDarkCells / numTotalCells;\r
118                         //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
119                         return System.Math.Abs((int) (darkRatio * 100 - 50)) / 5 * 10;\r
120                 }\r
121                 \r
122                 // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask\r
123                 // pattern conditions.\r
124                 public static bool getDataMaskBit(int maskPattern, int x, int y)\r
125                 {\r
126                         if (!QRCode.isValidMaskPattern(maskPattern))\r
127                         {\r
128                                 throw new System.ArgumentException("Invalid mask pattern");\r
129                         }\r
130                         int intermediate, temp;\r
131                         switch (maskPattern)\r
132                         {\r
133                                 \r
134                                 case 0: \r
135                                         intermediate = (y + x) & 0x1;\r
136                                         break;\r
137                                 \r
138                                 case 1: \r
139                                         intermediate = y & 0x1;\r
140                                         break;\r
141                                 \r
142                                 case 2: \r
143                                         intermediate = x % 3;\r
144                                         break;\r
145                                 \r
146                                 case 3: \r
147                                         intermediate = (y + x) % 3;\r
148                                         break;\r
149                                 \r
150                                 case 4: \r
151                                         intermediate = ((SupportClass.URShift(y, 1)) + (x / 3)) & 0x1;\r
152                                         break;\r
153                                 \r
154                                 case 5: \r
155                                         temp = y * x;\r
156                                         intermediate = (temp & 0x1) + (temp % 3);\r
157                                         break;\r
158                                 \r
159                                 case 6: \r
160                                         temp = y * x;\r
161                                         intermediate = (((temp & 0x1) + (temp % 3)) & 0x1);\r
162                                         break;\r
163                                 \r
164                                 case 7: \r
165                                         temp = y * x;\r
166                                         intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);\r
167                                         break;\r
168                                 \r
169                                 default: \r
170                                         throw new System.ArgumentException("Invalid mask pattern: " + maskPattern);\r
171                                 \r
172                         }\r
173                         return intermediate == 0;\r
174                 }\r
175                 \r
176                 // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both\r
177                 // vertical and horizontal orders respectively.\r
178                 private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, bool isHorizontal)\r
179                 {\r
180                         int penalty = 0;\r
181                         int numSameBitCells = 0;\r
182                         int prevBit = - 1;\r
183                         // Horizontal mode:\r
184                         //   for (int i = 0; i < matrix.height(); ++i) {\r
185                         //     for (int j = 0; j < matrix.width(); ++j) {\r
186                         //       int bit = matrix.get(i, j);\r
187                         // Vertical mode:\r
188                         //   for (int i = 0; i < matrix.width(); ++i) {\r
189                         //     for (int j = 0; j < matrix.height(); ++j) {\r
190                         //       int bit = matrix.get(j, i);\r
191                         int iLimit = isHorizontal?matrix.Height:matrix.Width;\r
192                         int jLimit = isHorizontal?matrix.Width:matrix.Height;\r
193                         sbyte[][] array = matrix.Array;\r
194                         for (int i = 0; i < iLimit; ++i)\r
195                         {\r
196                                 for (int j = 0; j < jLimit; ++j)\r
197                                 {\r
198                                         int bit = isHorizontal?array[i][j]:array[j][i];\r
199                                         if (bit == prevBit)\r
200                                         {\r
201                                                 numSameBitCells += 1;\r
202                                                 // Found five repetitive cells with the same color (bit).\r
203                                                 // We'll give penalty of 3.\r
204                                                 if (numSameBitCells == 5)\r
205                                                 {\r
206                                                         penalty += 3;\r
207                                                 }\r
208                                                 else if (numSameBitCells > 5)\r
209                                                 {\r
210                                                         // After five repetitive cells, we'll add the penalty one\r
211                                                         // by one.\r
212                                                         penalty += 1;\r
213                                                 }\r
214                                         }\r
215                                         else\r
216                                         {\r
217                                                 numSameBitCells = 1; // Include the cell itself.\r
218                                                 prevBit = bit;\r
219                                         }\r
220                                 }\r
221                                 numSameBitCells = 0; // Clear at each row/column.\r
222                         }\r
223                         return penalty;\r
224                 }\r
225         }\r
226 }