authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-28 14:54:46 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-28 14:54:46 -08:00
logeb448ff91847e04a8d2bfb502a02b75cfd1080c7
treec8980151292603260bcc93153dfceaf913aa5771
parentdd0882b5489701e11832923537c2affcd5299b39

fix version 10


2 files changed, 169 insertions(+), 36 deletions(-)

qrcode.zig+157-36
...@@ -110,18 +110,7 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,...@@ -110,18 +110,7 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,
110 std.log.warn("{d}", .{&bitReverse(scratch.masks)});110 std.log.warn("{d}", .{&bitReverse(scratch.masks)});
111 std.log.warn("{x}", .{&bitReverse(scratch.masks)});111 std.log.warn("{x}", .{&bitReverse(scratch.masks)});
112112
113 const groups = if (groupTwoBlockCount) |_| 2 else 1;113 const generator_polynomial = reverseArray(generatorPolynomial(ecCodewordsPerBlock));
114 _ = &groups;
115 const blocks = groupOneBlockCount;
116 _ = &blocks;
117 const dc_per_block = groupOneDataCodewordsCountPerBlock;
118 _ = &dc_per_block;
119 const ecc_per_block = ecCodewordsPerBlock;
120
121 var message_polynomial = scratch.masks;
122 std.log.warn("message_polynomial: {d}", .{&bitReverse(message_polynomial)});
123
124 const generator_polynomial = reverseArray(generatorPolynomial(ecc_per_block));
125 std.log.warn("generator_polynomial: {d}", .{generator_polynomial});114 std.log.warn("generator_polynomial: {d}", .{generator_polynomial});
126115
127 const generator_polynomial_alpha = to_alpha(generator_polynomial);116 const generator_polynomial_alpha = to_alpha(generator_polynomial);
...@@ -135,24 +124,91 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,...@@ -135,24 +124,91 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,
135 // 3. XOR the result of Step 2 with the message polynomial and discard the lead 0 term.124 // 3. XOR the result of Step 2 with the message polynomial and discard the lead 0 term.
136 // 4. Repeat Steps 2-3 n-1 more times where n is the number of data codewords.125 // 4. Repeat Steps 2-3 n-1 more times where n is the number of data codewords.
137 }126 }
138 var mp: [scratch.masks.len + ecc_per_block]?u8 = @splat(null);
139 for (&message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0];
140127
141 for (0..scratch.masks.len) |i| {128 const group2_block_count = groupTwoBlockCount orelse 0;
142 const lead = mp[i].?;129 const group2_datacodewords_per_block = groupTwoDataCodewordsCountPerBlock orelse 0;
143 for (&generator_polynomial_alpha, 0..) |x, j| {130
144 var y = GF[(@as(usize, x) + lead) % 255];131 var group1_ec: [groupOneBlockCount][ecCodewordsPerBlock]u8 = @splat(@splat(0));
145 if (mp[i + j]) |z| y ^= GF[z];132 var group2_ec: [group2_block_count][ecCodewordsPerBlock]u8 = @splat(@splat(0));
146 mp[i + j] = if (y == 0) null else to_alpha(.{y})[0];133
134 for (&group1_ec, 0..) |*g, gn| {
135 const message_polynomial = scratch.masks[groupOneDataCodewordsCountPerBlock * gn ..][0..groupOneDataCodewordsCountPerBlock];
136 std.log.warn("message_polynomial: 1,{d},{d},{d}", .{ gn, message_polynomial.len, &bitReverse(message_polynomial.*) });
137
138 var mp: [groupOneDataCodewordsCountPerBlock + ecCodewordsPerBlock]?u8 = @splat(null);
139 for (message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0];
140
141 for (0..groupOneDataCodewordsCountPerBlock) |i| {
142 const lead = mp[i].?;
143 for (&generator_polynomial_alpha, 0..) |x, j| {
144 var y = GF[(@as(usize, x) + lead) % 255];
145 if (mp[i + j]) |z| y ^= GF[z];
146 mp[i + j] = if (y == 0) null else to_alpha(.{y})[0];
147 }
148 }
149 for (g, 0..) |_, i| {
150 g[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0;
151 }
152 }
153 for (&group2_ec, 0..) |*g, gn| {
154 const message_polynomial = scratch.masks[groupOneBlockCount * groupOneDataCodewordsCountPerBlock ..][group2_datacodewords_per_block * gn ..][0..group2_datacodewords_per_block];
155 std.log.warn("message_polynomial: 2,{d},{d},{d}", .{ gn, message_polynomial.len, &bitReverse(message_polynomial.*) });
156
157 var mp: [group2_datacodewords_per_block + ecCodewordsPerBlock]?u8 = @splat(null);
158 for (message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0];
159
160 for (0..group2_datacodewords_per_block) |i| {
161 const lead = mp[i].?;
162 for (&generator_polynomial_alpha, 0..) |x, j| {
163 var y = GF[(@as(usize, x) + lead) % 255];
164 if (mp[i + j]) |z| y ^= GF[z];
165 mp[i + j] = if (y == 0) null else to_alpha(.{y})[0];
166 }
167 }
168 for (g, 0..) |_, i| {
169 g[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0;
147 }170 }
148 }171 }
149172
150 var error_codewords: [ecc_per_block]u8 = @splat(0);173 comptime std.debug.assert(groupOneDataCodewordsCountPerBlock != group2_datacodewords_per_block);
151 for (0..ecc_per_block) |i| error_codewords[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0;174 const group1_allcodewords_count = groupOneBlockCount * groupOneDataCodewordsCountPerBlock + groupOneBlockCount * ecCodewordsPerBlock;
152 std.log.warn("error_codewords: {d}", .{error_codewords});175 const group2_allcodewords_count = group2_block_count * group2_datacodewords_per_block + group2_block_count * ecCodewordsPerBlock;
153176 var interleaved_message: [group1_allcodewords_count + group2_allcodewords_count]u8 = @splat(0);
154 const interleaved_message = scratch.masks ++ bitReverse(error_codewords);177 var imi: usize = 0;
178 for (0..@min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| {
179 for (0..groupOneBlockCount) |i| {
180 interleaved_message[imi] = scratch.masks[(i * groupOneDataCodewordsCountPerBlock) + j];
181 imi += 1;
182 }
183 for (0..group2_block_count) |i| {
184 interleaved_message[imi] = scratch.masks[(groupOneBlockCount * groupOneDataCodewordsCountPerBlock) + (i * group2_datacodewords_per_block) + j];
185 imi += 1;
186 }
187 }
188 for (0..groupOneDataCodewordsCountPerBlock - @min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| {
189 for (0..groupOneBlockCount) |i| {
190 interleaved_message[imi] = scratch.masks[(i * groupOneDataCodewordsCountPerBlock) + j];
191 imi += 1;
192 }
193 }
194 for (0..group2_datacodewords_per_block - @min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| {
195 for (0..group2_block_count) |i| {
196 interleaved_message[imi] = scratch.masks[(groupOneBlockCount * groupOneDataCodewordsCountPerBlock) + (i * group2_datacodewords_per_block) + j];
197 imi += 1;
198 }
199 }
200 for (0..ecCodewordsPerBlock) |j| {
201 for (0..groupOneBlockCount) |i| {
202 interleaved_message[imi] = @bitReverse(group1_ec[i][j]);
203 imi += 1;
204 }
205 for (0..group2_block_count) |i| {
206 interleaved_message[imi] = @bitReverse(group2_ec[i][j]);
207 imi += 1;
208 }
209 }
155 std.log.warn("interleaved_message: {b:0>8}", .{&bitReverse(interleaved_message)});210 std.log.warn("interleaved_message: {b:0>8}", .{&bitReverse(interleaved_message)});
211 std.debug.assert(imi == interleaved_message.len);
156212
157 // remainder bits213 // remainder bits
158 const remainder_bits_count = comptime remainderBitsCount(version);214 const remainder_bits_count = comptime remainderBitsCount(version);
...@@ -184,8 +240,6 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,...@@ -184,8 +240,6 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,
184 // dark module240 // dark module
185 grid.set(8, 4 * (version - 1) + 13);241 grid.set(8, 4 * (version - 1) + 13);
186242
187 // version block
188
189 // data mask (data cannot be placed where this is set)243 // data mask (data cannot be placed where this is set)
190 var data_mask: BitGrid(size) = .initEmpty();244 var data_mask: BitGrid(size) = .initEmpty();
191 for (0..9) |y| for (0..9) |x| data_mask.set(x, y);245 for (0..9) |y| for (0..9) |x| data_mask.set(x, y);
...@@ -196,13 +250,12 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,...@@ -196,13 +250,12 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,
196250
197 // alignment patterns251 // alignment patterns
198 const alignment_pattern_centers = comptime alignmentPatternCenters(version);252 const alignment_pattern_centers = comptime alignmentPatternCenters(version);
199 for (alignment_pattern_centers) |y| {253 for (alignment_pattern_centers, 0..) |y, i| {
200 for (alignment_pattern_centers) |x| blk: {254 for (alignment_pattern_centers, 0..) |x, j| blk: {
201 for (y - 2..y + 2 + 1) |y2| {255 if (i == 0 and j == 0) break :blk;
202 for (x - 2..x + 2 + 1) |x2| {256 if (i == 0 and j == alignment_pattern_centers.len - 1) break :blk;
203 if (grid.isSet(x2, y2)) break :blk;257 if (j == 0 and i == alignment_pattern_centers.len - 1) break :blk;
204 }258
205 }
206 for (y - 2..y + 2 + 1) |y2| for (x - 2..x + 2 + 1) |x2| {259 for (y - 2..y + 2 + 1) |y2| for (x - 2..x + 2 + 1) |x2| {
207 grid.setT(x2, y2);260 grid.setT(x2, y2);
208 data_mask.set(x2, y2);261 data_mask.set(x2, y2);
...@@ -216,6 +269,27 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,...@@ -216,6 +269,27 @@ pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16,
216 }269 }
217 }270 }
218271
272 // version block
273 if (version >= 7) {
274 var bititer: std.bit_set.IntegerBitSet(18) = .{ .mask = versionBits(version) };
275 std.debug.assert(bititer.mask == 0b001010010011010011);
276 std.debug.assert(bititer.isSet(0));
277 std.debug.assert(bititer.isSet(1));
278
279 for (0..3) |x| {
280 for (0..6) |y| {
281 grid.setV(size - 11 + x, y, bititer.isSet((y * 3) + x));
282 data_mask.set(size - 11 + x, y);
283 }
284 }
285 for (0..6) |x| {
286 for (0..3) |y| {
287 grid.setV(x, size - 11 + y, bititer.isSet((x * 3) + y));
288 data_mask.set(x, size - 11 + y);
289 }
290 }
291 }
292
219 // write data293 // write data
220 var data_iter: BufBiterator = .init(&bitReverse(interleaved_message));294 var data_iter: BufBiterator = .init(&bitReverse(interleaved_message));
221295
...@@ -567,7 +641,7 @@ fn isAlphanumeric(c: u8) bool {...@@ -567,7 +641,7 @@ fn isAlphanumeric(c: u8) bool {
567 return switch (c) {641 return switch (c) {
568 '0'...'9' => true,642 '0'...'9' => true,
569 'A'...'Z' => true,643 'A'...'Z' => true,
570 ' ', '$', '%', '*', '+', '-', '.', '/', ':', ',' => true,644 ' ', '$', '%', '*', '+', '-', '.', '/', ':' => true,
571 else => false,645 else => false,
572 };646 };
573}647}
...@@ -887,6 +961,52 @@ fn alignmentPatternCenters(version: u16) []const u8 {...@@ -887,6 +961,52 @@ fn alignmentPatternCenters(version: u16) []const u8 {
887 }[version];961 }[version];
888}962}
889963
964fn versionBits(version: u16) u18 {
965 return ([_]u18{
966 0,
967 0,
968 0,
969 0,
970 0,
971 0,
972 0,
973 0b000111110010010100,
974 0b001000010110111100,
975 0b001001101010011001,
976 0b001010010011010011,
977 0b001011101111110110,
978 0b001100011101100010,
979 0b001101100001000111,
980 0b001110011000001101,
981 0b001111100100101000,
982 0b010000101101111000,
983 0b010001010001011101,
984 0b010010101000010111,
985 0b010011010100110010,
986 0b010100100110100110,
987 0b010101011010000011,
988 0b010110100011001001,
989 0b010111011111101100,
990 0b011000111011000100,
991 0b011001000111100001,
992 0b011010111110101011,
993 0b011011000010001110,
994 0b011100110000011010,
995 0b011101001100111111,
996 0b011110110101110101,
997 0b011111001001010000,
998 0b100000100111010101,
999 0b100001011011110000,
1000 0b100010100010111010,
1001 0b100011011110011111,
1002 0b100100101100001011,
1003 0b100101010000101110,
1004 0b100110101001100100,
1005 0b100111010101000001,
1006 0b101000110001101001,
1007 })[version];
1008}
1009
890//1010//
891//1011//
892//1012//
...@@ -895,10 +1015,11 @@ fn alignmentPatternCenters(version: u16) []const u8 {...@@ -895,10 +1015,11 @@ fn alignmentPatternCenters(version: u16) []const u8 {
895const BufBiterator = struct {1015const BufBiterator = struct {
896 buf: []const u8,1016 buf: []const u8,
897 bits: std.bit_set.IntegerBitSet(8),1017 bits: std.bit_set.IntegerBitSet(8),
898 idx: u8, // buf index1018 idx: u32, // buf index
899 jdx: u8, // bits index1019 jdx: u8, // bits index
9001020
901 pub fn init(buf: []const u8) BufBiterator {1021 pub fn init(buf: []const u8) BufBiterator {
1022 std.debug.assert(buf.len > 0);
902 return .{1023 return .{
903 .buf = buf,1024 .buf = buf,
904 .bits = .{ .mask = buf[0] },1025 .bits = .{ .mask = buf[0] },
test.zig+12
...@@ -23,6 +23,18 @@ test {...@@ -23,6 +23,18 @@ test {
23 // f6 46 52 c2 07 57 02 07 46 f2 03 53 02 06 36 8623 // f6 46 52 c2 07 57 02 07 46 f2 03 53 02 06 36 86
24 // 17 20 ec 1124 // 17 20 ec 11
25 _ = &grid;25 _ = &grid;
26}
27test {
28 const grid = qrcode.encode(.latin1, .M, 10, "VERSION 10 QR CODE, UP TO 174 CHAR AT H LEVEL, WITH 57X57 MODULES AND PLENTY OF ERROR CORRECTION TO GO AROUND. NOTE THAT THERE ARE ADDITIONAL TRACKING BOXES");
29 // 20 25 60 66 db 42 85 e0 b7 37 4e 34 68 95 d0 00
30 // 04 b0 80 d6 72 91 34 c7 2a 93 fc c0 c1 e7 19 df
31 // cc b9 a5 4a a8 b4 00 01 2c 20 df 9d 1a 3b 21 1d
32 // 17 48 af bf 64 ce ef e8 81 d9 4d b1 e9 46 d3 bc
33 // d8 b1 d4 8c d7 9c 68 d2 6d 28 2a 57 13 f3 8c 5c
34 // 5d 19 7a 6b d5 d4 e7 9e 21 1d 27 ce 2c 1e 9e d2
35 // a5 22 a6 8e ea 9a 39 e9 6e 95 c4 f3 af 9c 66 4a
36 // 30 68 2b d1 03 dd b7 00 ec 11
37 _ = &grid;
2638
27 const bg_black = "\x1b[40m";39 const bg_black = "\x1b[40m";
28 const bg_white = "\x1b[47m";40 const bg_white = "\x1b[47m";