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