File: QrTemplate.java

package info (click to toggle)
qr-code-generator 1.8.0-1.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 984 kB
  • sloc: java: 2,309; ansic: 2,035; cpp: 932; python: 664; makefile: 159; xml: 123; sh: 6
file content (270 lines) | stat: -rw-r--r-- 9,782 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/* 
 * Fast QR Code generator library
 * 
 * Copyright (c) Project Nayuki. (MIT License)
 * https://www.nayuki.io/page/fast-qr-code-generator-library
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 * - The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 * - The Software is provided "as is", without warranty of any kind, express or
 *   implied, including but not limited to the warranties of merchantability,
 *   fitness for a particular purpose and noninfringement. In no event shall the
 *   authors or copyright holders be liable for any claim, damages or other
 *   liability, whether in an action of contract, tort or otherwise, arising from,
 *   out of or in connection with the Software or the use or other dealings in the
 *   Software.
 */

package io.nayuki.fastqrcodegen;


// Stores the parts of a QR Code that depend only on the version number,
// and does not depend on the data or error correction level or mask.
final class QrTemplate {
	
	// Use this memoizer to get instances of this class.
	public static final Memoizer<Integer,QrTemplate> MEMOIZER
		= new Memoizer<>(QrTemplate::new);
	
	
	private final int version;  // In the range [1, 40].
	private final int size;  // Derived from version.
	
	final int[] template;  // Length and values depend on version.
	final int[][] masks;  // masks.length == 8, and masks[i].length == template.length.
	final int[] dataOutputBitIndexes;  // Length and values depend on version.
	
	// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
	// Otherwise when the constructor is running, isFunction.length == template.length.
	private int[] isFunction;
	
	
	// Creates a QR Code template for the given version number.
	private QrTemplate(int ver) {
		if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION)
			throw new IllegalArgumentException("Version out of range");
		version = ver;
		size = version * 4 + 17;
		template = new int[(size * size + 31) / 32];
		isFunction = new int[template.length];
		
		drawFunctionPatterns();  // Reads and writes fields
		masks = generateMasks();  // Reads fields, returns array
		dataOutputBitIndexes = generateZigzagScan();  // Reads fields, returns array
		isFunction = null;
	}
	
	
	// Reads this object's version field, and draws and marks all function modules.
	private void drawFunctionPatterns() {
		// Draw horizontal and vertical timing patterns
		for (int i = 0; i < size; i++) {
			darkenFunctionModule(6, i, ~i & 1);
			darkenFunctionModule(i, 6, ~i & 1);
		}
		
		// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
		drawFinderPattern(3, 3);
		drawFinderPattern(size - 4, 3);
		drawFinderPattern(3, size - 4);
		
		// Draw numerous alignment patterns
		int[] alignPatPos = getAlignmentPatternPositions();
		int numAlign = alignPatPos.length;
		for (int i = 0; i < numAlign; i++) {
			for (int j = 0; j < numAlign; j++) {
				// Don't draw on the three finder corners
				if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0))
					drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
			}
		}
		
		// Draw configuration data
		drawDummyFormatBits();
		drawVersion();
	}
	
	
	// Draws two blank copies of the format bits.
	private void drawDummyFormatBits() {
		// Draw first copy
		for (int i = 0; i <= 5; i++)
			darkenFunctionModule(8, i, 0);
		darkenFunctionModule(8, 7, 0);
		darkenFunctionModule(8, 8, 0);
		darkenFunctionModule(7, 8, 0);
		for (int i = 9; i < 15; i++)
			darkenFunctionModule(14 - i, 8, 0);
		
		// Draw second copy
		for (int i = 0; i < 8; i++)
			darkenFunctionModule(size - 1 - i, 8, 0);
		for (int i = 8; i < 15; i++)
			darkenFunctionModule(8, size - 15 + i, 0);
		darkenFunctionModule(8, size - 8, 1);  // Always dark
	}
	
	
	// Draws two copies of the version bits (with its own error correction code),
	// based on this object's version field, iff 7 <= version <= 40.
	private void drawVersion() {
		if (version < 7)
			return;
		
		// Calculate error correction code and pack bits
		int rem = version;  // version is uint6, in the range [7, 40]
		for (int i = 0; i < 12; i++)
			rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);
		int bits = version << 12 | rem;  // uint18
		assert bits >>> 18 == 0;
		
		// Draw two copies
		for (int i = 0; i < 18; i++) {
			int bit = QrCode.getBit(bits, i);
			int a = size - 11 + i % 3;
			int b = i / 3;
			darkenFunctionModule(a, b, bit);
			darkenFunctionModule(b, a, bit);
		}
	}
	
	
	// Draws a 9*9 finder pattern including the border separator,
	// with the center module at (x, y). Modules can be out of bounds.
	private void drawFinderPattern(int x, int y) {
		for (int dy = -4; dy <= 4; dy++) {
			for (int dx = -4; dx <= 4; dx++) {
				int dist = Math.max(Math.abs(dx), Math.abs(dy));  // Chebyshev/infinity norm
				int xx = x + dx, yy = y + dy;
				if (0 <= xx && xx < size && 0 <= yy && yy < size)
					darkenFunctionModule(xx, yy, (dist != 2 && dist != 4) ? 1 : 0);
			}
		}
	}
	
	
	// Draws a 5*5 alignment pattern, with the center module
	// at (x, y). All modules must be in bounds.
	private void drawAlignmentPattern(int x, int y) {
		for (int dy = -2; dy <= 2; dy++) {
			for (int dx = -2; dx <= 2; dx++)
				darkenFunctionModule(x + dx, y + dy, Math.abs(Math.max(Math.abs(dx), Math.abs(dy)) - 1));
		}
	}
	
	
	// Computes and returns a new array of masks, based on this object's various fields.
	private int[][] generateMasks() {
		int[][] result = new int[8][template.length];
		for (int mask = 0; mask < result.length; mask++) {
			int[] maskModules = result[mask];
			for (int y = 0, i = 0; y < size; y++) {
				for (int x = 0; x < size; x++, i++) {
					boolean invert;
					switch (mask) {
						case 0:  invert = (x + y) % 2 == 0;                    break;
						case 1:  invert = y % 2 == 0;                          break;
						case 2:  invert = x % 3 == 0;                          break;
						case 3:  invert = (x + y) % 3 == 0;                    break;
						case 4:  invert = (x / 3 + y / 2) % 2 == 0;            break;
						case 5:  invert = x * y % 2 + x * y % 3 == 0;          break;
						case 6:  invert = (x * y % 2 + x * y % 3) % 2 == 0;    break;
						case 7:  invert = ((x + y) % 2 + x * y % 3) % 2 == 0;  break;
						default:  throw new AssertionError();
					}
					int bit = (invert ? 1 : 0) & ~getModule(isFunction, x, y);
					maskModules[i >>> 5] |= bit << i;
				}
			}
		}
		return result;
	}
	
	
	// Computes and returns an array of bit indexes, based on this object's various fields.
	private int[] generateZigzagScan() {
		int[] result = new int[getNumRawDataModules(version) / 8 * 8];
		int i = 0;  // Bit index into the data
		for (int right = size - 1; right >= 1; right -= 2) {  // Index of right column in each column pair
			if (right == 6)
				right = 5;
			for (int vert = 0; vert < size; vert++) {  // Vertical counter
				for (int j = 0; j < 2; j++) {
					int x = right - j;  // Actual x coordinate
					boolean upward = ((right + 1) & 2) == 0;
					int y = upward ? size - 1 - vert : vert;  // Actual y coordinate
					if (getModule(isFunction, x, y) == 0 && i < result.length) {
						result[i] = y * size + x;
						i++;
					}
				}
			}
		}
		assert i == result.length;
		return result;
	}
	
	
	// Returns the value of the bit at the given coordinates in the given grid.
	private int getModule(int[] grid, int x, int y) {
		assert 0 <= x && x < size;
		assert 0 <= y && y < size;
		int i = y * size + x;
		return QrCode.getBit(grid[i >>> 5], i);
	}
	
	
	// Marks the module at the given coordinates as a function module.
	// Also either sets that module dark or keeps its color unchanged.
	private void darkenFunctionModule(int x, int y, int enable) {
		assert 0 <= x && x < size;
		assert 0 <= y && y < size;
		assert enable == 0 || enable == 1;
		int i = y * size + x;
		template[i >>> 5] |= enable << i;
		isFunction[i >>> 5] |= 1 << i;
	}
	
	
	// Returns an ascending list of positions of alignment patterns for this version number.
	// Each position is in the range [0,177), and are used on both the x and y axes.
	// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
	private int[] getAlignmentPatternPositions() {
		if (version == 1)
			return new int[]{};
		else {
			int numAlign = version / 7 + 2;
			int step = (version == 32) ? 26 :
				(version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2;
			int[] result = new int[numAlign];
			result[0] = 6;
			for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step)
				result[i] = pos;
			return result;
		}
	}
	
	
	// Returns the number of data bits that can be stored in a QR Code of the given version number, after
	// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
	// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
	static int getNumRawDataModules(int ver) {
		if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION)
			throw new IllegalArgumentException("Version number out of range");
		int result = (16 * ver + 128) * ver + 64;
		if (ver >= 2) {
			int numAlign = ver / 7 + 2;
			result -= (25 * numAlign - 10) * numAlign - 55;
			if (ver >= 7)
				result -= 36;
		}
		return result;
	}
	
}