001/* 002 * Copyright 2008 ZXing authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.itextpdf.text.pdf.qrcode; 018 019/** 020 * @author satorux@google.com (Satoru Takabayashi) - creator 021 * @author dswitkin@google.com (Daniel Switkin) - ported from C++ 022 * @since 5.0.2 023 */ 024public final class MaskUtil { 025 026 private MaskUtil() { 027 // do nothing 028 } 029 030 // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and 031 // give penalty to them. Example: 00000 or 11111. 032 public static int applyMaskPenaltyRule1(ByteMatrix matrix) { 033 return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false); 034 } 035 036 // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give 037 // penalty to them. 038 public static int applyMaskPenaltyRule2(ByteMatrix matrix) { 039 int penalty = 0; 040 byte[][] array = matrix.getArray(); 041 int width = matrix.getWidth(); 042 int height = matrix.getHeight(); 043 for (int y = 0; y < height - 1; ++y) { 044 for (int x = 0; x < width - 1; ++x) { 045 int value = array[y][x]; 046 if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) { 047 penalty += 3; 048 } 049 } 050 } 051 return penalty; 052 } 053 054 // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or 055 // 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give 056 // penalties twice (i.e. 40 * 2). 057 public static int applyMaskPenaltyRule3(ByteMatrix matrix) { 058 int penalty = 0; 059 byte[][] array = matrix.getArray(); 060 int width = matrix.getWidth(); 061 int height = matrix.getHeight(); 062 for (int y = 0; y < height; ++y) { 063 for (int x = 0; x < width; ++x) { 064 // Tried to simplify following conditions but failed. 065 if (x + 6 < width && 066 array[y][x] == 1 && 067 array[y][x + 1] == 0 && 068 array[y][x + 2] == 1 && 069 array[y][x + 3] == 1 && 070 array[y][x + 4] == 1 && 071 array[y][x + 5] == 0 && 072 array[y][x + 6] == 1 && 073 ((x + 10 < width && 074 array[y][x + 7] == 0 && 075 array[y][x + 8] == 0 && 076 array[y][x + 9] == 0 && 077 array[y][x + 10] == 0) || 078 (x - 4 >= 0 && 079 array[y][x - 1] == 0 && 080 array[y][x - 2] == 0 && 081 array[y][x - 3] == 0 && 082 array[y][x - 4] == 0))) { 083 penalty += 40; 084 } 085 if (y + 6 < height && 086 array[y][x] == 1 && 087 array[y + 1][x] == 0 && 088 array[y + 2][x] == 1 && 089 array[y + 3][x] == 1 && 090 array[y + 4][x] == 1 && 091 array[y + 5][x] == 0 && 092 array[y + 6][x] == 1 && 093 ((y + 10 < height && 094 array[y + 7][x] == 0 && 095 array[y + 8][x] == 0 && 096 array[y + 9][x] == 0 && 097 array[y + 10][x] == 0) || 098 (y - 4 >= 0 && 099 array[y - 1][x] == 0 && 100 array[y - 2][x] == 0 && 101 array[y - 3][x] == 0 && 102 array[y - 4][x] == 0))) { 103 penalty += 40; 104 } 105 } 106 } 107 return penalty; 108 } 109 110 // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give 111 // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples: 112 // - 0% => 100 113 // - 40% => 20 114 // - 45% => 10 115 // - 50% => 0 116 // - 55% => 10 117 // - 55% => 20 118 // - 100% => 100 119 public static int applyMaskPenaltyRule4(ByteMatrix matrix) { 120 int numDarkCells = 0; 121 byte[][] array = matrix.getArray(); 122 int width = matrix.getWidth(); 123 int height = matrix.getHeight(); 124 for (int y = 0; y < height; ++y) { 125 for (int x = 0; x < width; ++x) { 126 if (array[y][x] == 1) { 127 numDarkCells += 1; 128 } 129 } 130 } 131 int numTotalCells = matrix.getHeight() * matrix.getWidth(); 132 double darkRatio = (double) numDarkCells / numTotalCells; 133 return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10; 134 } 135 136 // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask 137 // pattern conditions. 138 public static boolean getDataMaskBit(int maskPattern, int x, int y) { 139 if (!QRCode.isValidMaskPattern(maskPattern)) { 140 throw new IllegalArgumentException("Invalid mask pattern"); 141 } 142 int intermediate, temp; 143 switch (maskPattern) { 144 case 0: 145 intermediate = (y + x) & 0x1; 146 break; 147 case 1: 148 intermediate = y & 0x1; 149 break; 150 case 2: 151 intermediate = x % 3; 152 break; 153 case 3: 154 intermediate = (y + x) % 3; 155 break; 156 case 4: 157 intermediate = ((y >>> 1) + (x / 3)) & 0x1; 158 break; 159 case 5: 160 temp = y * x; 161 intermediate = (temp & 0x1) + (temp % 3); 162 break; 163 case 6: 164 temp = y * x; 165 intermediate = (((temp & 0x1) + (temp % 3)) & 0x1); 166 break; 167 case 7: 168 temp = y * x; 169 intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1); 170 break; 171 default: 172 throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern); 173 } 174 return intermediate == 0; 175 } 176 177 // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both 178 // vertical and horizontal orders respectively. 179 private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) { 180 int penalty = 0; 181 int numSameBitCells = 0; 182 int prevBit = -1; 183 // Horizontal mode: 184 // for (int i = 0; i < matrix.height(); ++i) { 185 // for (int j = 0; j < matrix.width(); ++j) { 186 // int bit = matrix.get(i, j); 187 // Vertical mode: 188 // for (int i = 0; i < matrix.width(); ++i) { 189 // for (int j = 0; j < matrix.height(); ++j) { 190 // int bit = matrix.get(j, i); 191 int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth(); 192 int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight(); 193 byte[][] array = matrix.getArray(); 194 for (int i = 0; i < iLimit; ++i) { 195 for (int j = 0; j < jLimit; ++j) { 196 int bit = isHorizontal ? array[i][j] : array[j][i]; 197 if (bit == prevBit) { 198 numSameBitCells += 1; 199 // Found five repetitive cells with the same color (bit). 200 // We'll give penalty of 3. 201 if (numSameBitCells == 5) { 202 penalty += 3; 203 } else if (numSameBitCells > 5) { 204 // After five repetitive cells, we'll add the penalty one 205 // by one. 206 penalty += 1; 207 } 208 } else { 209 numSameBitCells = 1; // Include the cell itself. 210 prevBit = bit; 211 } 212 } 213 numSameBitCells = 0; // Clear at each row/column. 214 } 215 return penalty; 216 } 217 218}