• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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