Another attack on integrating encoder and decoder: Version is done. Attempted to...
[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.width();
43     int height = matrix.height();
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.width();
62     int height = matrix.height();
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.width();
124     int height = matrix.height();
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.height() * matrix.width();
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 int getDataMaskBit(int maskPattern, int x, int y) {
140     if (!QRCode.isValidMaskPattern(maskPattern)) {
141       throw new IllegalArgumentException("Invalid mask pattern");
142     }
143     switch (maskPattern) {
144       case 0:
145         return ((y + x) % 2 == 0) ? 1 : 0;
146       case 1:
147         return (y % 2 == 0) ? 1 : 0;
148       case 2:
149         return (x % 3 == 0) ? 1 : 0;
150       case 3:
151         return ((y + x) % 3 == 0) ? 1 : 0;
152       case 4:
153         return (((y / 2) + (x / 3)) % 2 == 0) ? 1 : 0;
154       case 5:
155         return (((y * x) % 2) + ((y * x) % 3) == 0) ? 1 : 0;
156       case 6:
157         return ((((y * x) % 2) + ((y * x) % 3)) % 2 == 0) ? 1 : 0;
158       case 7:
159         return ((((y * x) % 3) + ((y + x) % 2)) % 2 == 0) ? 1 : 0;
160     }
161     throw new IllegalArgumentException("invalid mask pattern: " + maskPattern);
162   }
163
164   // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
165   // vertical and horizontal orders respectively.
166   private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
167     int penalty = 0;
168     int numSameBitCells = 0;
169     int prevBit = -1;
170     // Horizontal mode:
171     //   for (int i = 0; i < matrix.height(); ++i) {
172     //     for (int j = 0; j < matrix.width(); ++j) {
173     //       int bit = matrix.get(i, j);
174     // Vertical mode:
175     //   for (int i = 0; i < matrix.width(); ++i) {
176     //     for (int j = 0; j < matrix.height(); ++j) {
177     //       int bit = matrix.get(j, i);
178     int iLimit = isHorizontal ? matrix.height() : matrix.width();
179     int jLimit = isHorizontal ? matrix.width() : matrix.height();
180     byte[][] array = matrix.getArray();
181     for (int i = 0; i < iLimit; ++i) {
182       for (int j = 0; j < jLimit; ++j) {
183         int bit = isHorizontal ? array[i][j] : array[j][i];
184         if (bit == prevBit) {
185           numSameBitCells += 1;
186           // Found five repetitive cells with the same color (bit).
187           // We'll give penalty of 3.
188           if (numSameBitCells == 5) {
189             penalty += 3;
190           } else if (numSameBitCells > 5) {
191             // After five repetitive cells, we'll add the penalty one
192             // by one.
193             penalty += 1;
194           }
195         } else {
196           numSameBitCells = 1;  // Include the cell itself.
197           prevBit = bit;
198         }
199       }
200       numSameBitCells = 0;  // Clear at each row/column.
201     }
202     return penalty;
203   }
204
205 }