"Split" ReaderException into subclasses to enable more useful error reporting
[zxing.git] / core / src / com / google / zxing / qrcode / encoder / MaskUtil.java
1 /*
2  * Copyright 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.qrcode.encoder;
18
19 import com.google.zxing.common.ByteMatrix;
20
21 /**
22  * @author satorux@google.com (Satoru Takabayashi) - creator
23  * @author dswitkin@google.com (Daniel Switkin) - ported from C++
24  */
25 public final class MaskUtil {
26
27   private MaskUtil() {
28     // do nothing
29   }
30
31   // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
32   // give penalty to them. Example: 00000 or 11111.
33   public static int applyMaskPenaltyRule1(ByteMatrix matrix) {
34     return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
35   }
36
37   // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
38   // penalty to them.
39   public static int applyMaskPenaltyRule2(ByteMatrix matrix) {
40     int penalty = 0;
41     byte[][] array = matrix.getArray();
42     int width = matrix.getWidth();
43     int height = matrix.getHeight();
44     for (int y = 0; y < height - 1; ++y) {
45       for (int x = 0; x < width - 1; ++x) {
46         int value = array[y][x];
47         if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
48           penalty += 3;
49         }
50       }
51     }
52     return penalty;
53   }
54
55   // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
56   // 10111010000, and give penalty to them.  If we find patterns like 000010111010000, we give
57   // penalties twice (i.e. 40 * 2).
58   public static int applyMaskPenaltyRule3(ByteMatrix matrix) {
59     int penalty = 0;
60     byte[][] array = matrix.getArray();
61     int width = matrix.getWidth();
62     int height = matrix.getHeight();
63     for (int y = 0; y < height; ++y) {
64       for (int x = 0; x < width; ++x) {
65         // Tried to simplify following conditions but failed.
66         if (x + 6 < width &&
67             array[y][x] == 1 &&
68             array[y][x +  1] == 0 &&
69             array[y][x +  2] == 1 &&
70             array[y][x +  3] == 1 &&
71             array[y][x +  4] == 1 &&
72             array[y][x +  5] == 0 &&
73             array[y][x +  6] == 1 &&
74             ((x + 10 < width &&
75                 array[y][x +  7] == 0 &&
76                 array[y][x +  8] == 0 &&
77                 array[y][x +  9] == 0 &&
78                 array[y][x + 10] == 0) ||
79                 (x - 4 >= 0 &&
80                     array[y][x -  1] == 0 &&
81                     array[y][x -  2] == 0 &&
82                     array[y][x -  3] == 0 &&
83                     array[y][x -  4] == 0))) {
84           penalty += 40;
85         }
86         if (y + 6 < height &&
87             array[y][x] == 1  &&
88             array[y +  1][x] == 0  &&
89             array[y +  2][x] == 1  &&
90             array[y +  3][x] == 1  &&
91             array[y +  4][x] == 1  &&
92             array[y +  5][x] == 0  &&
93             array[y +  6][x] == 1 &&
94             ((y + 10 < height &&
95                 array[y +  7][x] == 0 &&
96                 array[y +  8][x] == 0 &&
97                 array[y +  9][x] == 0 &&
98                 array[y + 10][x] == 0) ||
99                 (y - 4 >= 0 &&
100                     array[y -  1][x] == 0 &&
101                     array[y -  2][x] == 0 &&
102                     array[y -  3][x] == 0 &&
103                     array[y -  4][x] == 0))) {
104           penalty += 40;
105         }
106       }
107     }
108     return penalty;
109   }
110
111   // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
112   // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
113   // -   0% => 100
114   // -  40% =>  20
115   // -  45% =>  10
116   // -  50% =>   0
117   // -  55% =>  10
118   // -  55% =>  20
119   // - 100% => 100
120   public static int applyMaskPenaltyRule4(ByteMatrix matrix) {
121     int numDarkCells = 0;
122     byte[][] array = matrix.getArray();
123     int width = matrix.getWidth();
124     int height = matrix.getHeight();
125     for (int y = 0; y < height; ++y) {
126       for (int x = 0; x < width; ++x) {
127         if (array[y][x] == 1) {
128           numDarkCells += 1;
129         }
130       }
131     }
132     int numTotalCells = matrix.getHeight() * matrix.getWidth();
133     double darkRatio = (double) numDarkCells / numTotalCells;
134     return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10;
135   }
136
137   // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
138   // pattern conditions.
139   public static boolean getDataMaskBit(int maskPattern, int x, int y) {
140     if (!QRCode.isValidMaskPattern(maskPattern)) {
141       throw new IllegalArgumentException("Invalid mask pattern");
142     }
143     int intermediate, temp;
144     switch (maskPattern) {
145       case 0:
146         intermediate = (y + x) & 0x1;
147         break;
148       case 1:
149         intermediate = y & 0x1;
150         break;
151       case 2:
152         intermediate = x % 3;
153         break;
154       case 3:
155         intermediate = (y + x) % 3;
156         break;
157       case 4:
158         intermediate = ((y >>> 1) + (x / 3)) & 0x1;
159         break;
160       case 5:
161         temp = y * x;
162         intermediate = (temp & 0x1) + (temp % 3);
163         break;
164       case 6:
165         temp = y * x;
166         intermediate = (((temp & 0x1) + (temp % 3)) & 0x1);
167         break;
168       case 7:
169         temp = y * x;
170         intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
171         break;
172       default:
173         throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
174     }
175     return intermediate == 0;
176   }
177
178   // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
179   // vertical and horizontal orders respectively.
180   private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
181     int penalty = 0;
182     int numSameBitCells = 0;
183     int prevBit = -1;
184     // Horizontal mode:
185     //   for (int i = 0; i < matrix.height(); ++i) {
186     //     for (int j = 0; j < matrix.width(); ++j) {
187     //       int bit = matrix.get(i, j);
188     // Vertical mode:
189     //   for (int i = 0; i < matrix.width(); ++i) {
190     //     for (int j = 0; j < matrix.height(); ++j) {
191     //       int bit = matrix.get(j, i);
192     int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
193     int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
194     byte[][] array = matrix.getArray();
195     for (int i = 0; i < iLimit; ++i) {
196       for (int j = 0; j < jLimit; ++j) {
197         int bit = isHorizontal ? array[i][j] : array[j][i];
198         if (bit == prevBit) {
199           numSameBitCells += 1;
200           // Found five repetitive cells with the same color (bit).
201           // We'll give penalty of 3.
202           if (numSameBitCells == 5) {
203             penalty += 3;
204           } else if (numSameBitCells > 5) {
205             // After five repetitive cells, we'll add the penalty one
206             // by one.
207             penalty += 1;
208           }
209         } else {
210           numSameBitCells = 1;  // Include the cell itself.
211           prevBit = bit;
212         }
213       }
214       numSameBitCells = 0;  // Clear at each row/column.
215     }
216     return penalty;
217   }
218
219 }