1 /* 2 * Fast QR Code generator library 3 * 4 * Copyright (c) Project Nayuki. (MIT License) 5 * https://www.nayuki.io/page/fast-qr-code-generator-library 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 * this software and associated documentation files (the "Software"), to deal in 9 * the Software without restriction, including without limitation the rights to 10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 * the Software, and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * - The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * - The Software is provided "as is", without warranty of any kind, express or 16 * implied, including but not limited to the warranties of merchantability, 17 * fitness for a particular purpose and noninfringement. In no event shall the 18 * authors or copyright holders be liable for any claim, damages or other 19 * liability, whether in an action of contract, tort or otherwise, arising from, 20 * out of or in connection with the Software or the use or other dealings in the 21 * Software. 22 */ 23 24 package io.nayuki.fastqrcodegen; 25 26 27 // Stores the parts of a QR Code that depend only on the version number, 28 // and does not depend on the data or error correction level or mask. 29 final class QrTemplate { 30 31 // Use this memoizer to get instances of this class. 32 public static final Memoizer<Integer,QrTemplate> MEMOIZER 33 = new Memoizer<>(QrTemplate::new); 34 35 36 private final int version; // In the range [1, 40]. 37 private final int size; // Derived from version. 38 39 final int[] template; // Length and values depend on version. 40 final int[][] masks; // masks.length == 8, and masks[i].length == template.length. 41 final int[] dataOutputBitIndexes; // Length and values depend on version. 42 43 // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. 44 // Otherwise when the constructor is running, isFunction.length == template.length. 45 private int[] isFunction; 46 47 48 // Creates a QR Code template for the given version number. QrTemplate(int ver)49 private QrTemplate(int ver) { 50 if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) 51 throw new IllegalArgumentException("Version out of range"); 52 version = ver; 53 size = version * 4 + 17; 54 template = new int[(size * size + 31) / 32]; 55 isFunction = new int[template.length]; 56 57 drawFunctionPatterns(); // Reads and writes fields 58 masks = generateMasks(); // Reads fields, returns array 59 dataOutputBitIndexes = generateZigzagScan(); // Reads fields, returns array 60 isFunction = null; 61 } 62 63 64 // Reads this object's version field, and draws and marks all function modules. drawFunctionPatterns()65 private void drawFunctionPatterns() { 66 // Draw horizontal and vertical timing patterns 67 for (int i = 0; i < size; i++) { 68 darkenFunctionModule(6, i, ~i & 1); 69 darkenFunctionModule(i, 6, ~i & 1); 70 } 71 72 // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 73 drawFinderPattern(3, 3); 74 drawFinderPattern(size - 4, 3); 75 drawFinderPattern(3, size - 4); 76 77 // Draw numerous alignment patterns 78 int[] alignPatPos = getAlignmentPatternPositions(); 79 int numAlign = alignPatPos.length; 80 for (int i = 0; i < numAlign; i++) { 81 for (int j = 0; j < numAlign; j++) { 82 // Don't draw on the three finder corners 83 if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) 84 drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); 85 } 86 } 87 88 // Draw configuration data 89 drawDummyFormatBits(); 90 drawVersion(); 91 } 92 93 94 // Draws two blank copies of the format bits. drawDummyFormatBits()95 private void drawDummyFormatBits() { 96 // Draw first copy 97 for (int i = 0; i <= 5; i++) 98 darkenFunctionModule(8, i, 0); 99 darkenFunctionModule(8, 7, 0); 100 darkenFunctionModule(8, 8, 0); 101 darkenFunctionModule(7, 8, 0); 102 for (int i = 9; i < 15; i++) 103 darkenFunctionModule(14 - i, 8, 0); 104 105 // Draw second copy 106 for (int i = 0; i < 8; i++) 107 darkenFunctionModule(size - 1 - i, 8, 0); 108 for (int i = 8; i < 15; i++) 109 darkenFunctionModule(8, size - 15 + i, 0); 110 darkenFunctionModule(8, size - 8, 1); // Always dark 111 } 112 113 114 // Draws two copies of the version bits (with its own error correction code), 115 // based on this object's version field, iff 7 <= version <= 40. drawVersion()116 private void drawVersion() { 117 if (version < 7) 118 return; 119 120 // Calculate error correction code and pack bits 121 int rem = version; // version is uint6, in the range [7, 40] 122 for (int i = 0; i < 12; i++) 123 rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); 124 int bits = version << 12 | rem; // uint18 125 assert bits >>> 18 == 0; 126 127 // Draw two copies 128 for (int i = 0; i < 18; i++) { 129 int bit = QrCode.getBit(bits, i); 130 int a = size - 11 + i % 3; 131 int b = i / 3; 132 darkenFunctionModule(a, b, bit); 133 darkenFunctionModule(b, a, bit); 134 } 135 } 136 137 138 // Draws a 9*9 finder pattern including the border separator, 139 // with the center module at (x, y). Modules can be out of bounds. drawFinderPattern(int x, int y)140 private void drawFinderPattern(int x, int y) { 141 for (int dy = -4; dy <= 4; dy++) { 142 for (int dx = -4; dx <= 4; dx++) { 143 int dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm 144 int xx = x + dx, yy = y + dy; 145 if (0 <= xx && xx < size && 0 <= yy && yy < size) 146 darkenFunctionModule(xx, yy, (dist != 2 && dist != 4) ? 1 : 0); 147 } 148 } 149 } 150 151 152 // Draws a 5*5 alignment pattern, with the center module 153 // at (x, y). All modules must be in bounds. drawAlignmentPattern(int x, int y)154 private void drawAlignmentPattern(int x, int y) { 155 for (int dy = -2; dy <= 2; dy++) { 156 for (int dx = -2; dx <= 2; dx++) 157 darkenFunctionModule(x + dx, y + dy, Math.abs(Math.max(Math.abs(dx), Math.abs(dy)) - 1)); 158 } 159 } 160 161 162 // Computes and returns a new array of masks, based on this object's various fields. generateMasks()163 private int[][] generateMasks() { 164 int[][] result = new int[8][template.length]; 165 for (int mask = 0; mask < result.length; mask++) { 166 int[] maskModules = result[mask]; 167 for (int y = 0, i = 0; y < size; y++) { 168 for (int x = 0; x < size; x++, i++) { 169 boolean invert; 170 switch (mask) { 171 case 0: invert = (x + y) % 2 == 0; break; 172 case 1: invert = y % 2 == 0; break; 173 case 2: invert = x % 3 == 0; break; 174 case 3: invert = (x + y) % 3 == 0; break; 175 case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 176 case 5: invert = x * y % 2 + x * y % 3 == 0; break; 177 case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 178 case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 179 default: throw new AssertionError(); 180 } 181 int bit = (invert ? 1 : 0) & ~getModule(isFunction, x, y); 182 maskModules[i >>> 5] |= bit << i; 183 } 184 } 185 } 186 return result; 187 } 188 189 190 // Computes and returns an array of bit indexes, based on this object's various fields. generateZigzagScan()191 private int[] generateZigzagScan() { 192 int[] result = new int[getNumRawDataModules(version) / 8 * 8]; 193 int i = 0; // Bit index into the data 194 for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 195 if (right == 6) 196 right = 5; 197 for (int vert = 0; vert < size; vert++) { // Vertical counter 198 for (int j = 0; j < 2; j++) { 199 int x = right - j; // Actual x coordinate 200 boolean upward = ((right + 1) & 2) == 0; 201 int y = upward ? size - 1 - vert : vert; // Actual y coordinate 202 if (getModule(isFunction, x, y) == 0 && i < result.length) { 203 result[i] = y * size + x; 204 i++; 205 } 206 } 207 } 208 } 209 assert i == result.length; 210 return result; 211 } 212 213 214 // Returns the value of the bit at the given coordinates in the given grid. getModule(int[] grid, int x, int y)215 private int getModule(int[] grid, int x, int y) { 216 assert 0 <= x && x < size; 217 assert 0 <= y && y < size; 218 int i = y * size + x; 219 return QrCode.getBit(grid[i >>> 5], i); 220 } 221 222 223 // Marks the module at the given coordinates as a function module. 224 // Also either sets that module dark or keeps its color unchanged. darkenFunctionModule(int x, int y, int enable)225 private void darkenFunctionModule(int x, int y, int enable) { 226 assert 0 <= x && x < size; 227 assert 0 <= y && y < size; 228 assert enable == 0 || enable == 1; 229 int i = y * size + x; 230 template[i >>> 5] |= enable << i; 231 isFunction[i >>> 5] |= 1 << i; 232 } 233 234 235 // Returns an ascending list of positions of alignment patterns for this version number. 236 // Each position is in the range [0,177), and are used on both the x and y axes. 237 // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. getAlignmentPatternPositions()238 private int[] getAlignmentPatternPositions() { 239 if (version == 1) 240 return new int[]{}; 241 else { 242 int numAlign = version / 7 + 2; 243 int step = (version == 32) ? 26 : 244 (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; 245 int[] result = new int[numAlign]; 246 result[0] = 6; 247 for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) 248 result[i] = pos; 249 return result; 250 } 251 } 252 253 254 // Returns the number of data bits that can be stored in a QR Code of the given version number, after 255 // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. 256 // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. getNumRawDataModules(int ver)257 static int getNumRawDataModules(int ver) { 258 if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) 259 throw new IllegalArgumentException("Version number out of range"); 260 int result = (16 * ver + 128) * ver + 64; 261 if (ver >= 2) { 262 int numAlign = ver / 7 + 2; 263 result -= (25 * numAlign - 10) * numAlign - 55; 264 if (ver >= 7) 265 result -= 36; 266 } 267 return result; 268 } 269 270 } 271