authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-27 03:10:52 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-27 03:10:52 -08:00
log80455a913d0be4e02b024e2d94ae4d3a7d69d359
treed6426ba8463ae71ddae3f2063cb66741a723ad91

initial commit


9 files changed, 1138 insertions(+), 0 deletions(-)

.gitattributes created+3
......@@ -0,0 +1,3 @@
1* text=auto
2*.zig text eol=lf
3zigmod.* text eol=lf
.gitignore created+6
......@@ -0,0 +1,6 @@
1.zig-cache
2zig-out
3.zigmod
4deps.zig
5files.zig
6zigmod.lock
LICENSE created+307
......@@ -0,0 +1,307 @@
1Mozilla Public License Version 2.0
2
31. Definitions
4
5 1.1. "Contributor" means each individual or legal entity that creates,
6contributes to the creation of, or owns Covered Software.
7
8 1.2. "Contributor Version" means the combination of the Contributions of
9others (if any) used by a Contributor and that particular Contributor's
10Contribution.
11
12 1.3. "Contribution" means Covered Software of a particular Contributor.
13
14 1.4. "Covered Software" means Source Code Form to which the initial
15Contributor has attached the notice in Exhibit A, the Executable Form of such
16Source Code Form, and Modifications of such Source Code Form, in each case
17including portions thereof.
18
19 1.5. "Incompatible With Secondary Licenses" means
20
21 (a) that the initial Contributor has attached the notice described
22in Exhibit B to the Covered Software; or
23
24 (b) that the Covered Software was made available under the terms of
25version 1.1 or earlier of the License, but not also under the terms of a
26Secondary License.
27
28 1.6. "Executable Form" means any form of the work other than Source Code
29Form.
30
31 1.7. "Larger Work" means a work that combines Covered Software with other
32material, in a separate file or files, that is not Covered Software.
33
34 1.8. "License" means this document.
35
36 1.9. "Licensable" means having the right to grant, to the maximum extent
37possible, whether at the time of the initial grant or subsequently, any and
38all of the rights conveyed by this License.
39
40 1.10. "Modifications" means any of the following:
41
42 (a) any file in Source Code Form that results from an addition to,
43deletion from, or modification of the contents of Covered Software; or
44
45 (b) any new file in Source Code Form that contains any Covered
46Software.
47
48 1.11. "Patent Claims" of a Contributor means any patent claim(s),
49including without limitation, method, process, and apparatus claims, in any
50patent Licensable by such Contributor that would be infringed, but for the
51grant of the License, by the making, using, selling, offering for sale, having
52made, import, or transfer of either its Contributions or its Contributor
53Version.
54
55 1.12. "Secondary License" means either the GNU General Public License,
56Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU
57Affero General Public License, Version 3.0, or any later versions of those
58licenses.
59
60 1.13. "Source Code Form" means the form of the work preferred for making
61modifications.
62
63 1.14. "You" (or "Your") means an individual or a legal entity exercising
64rights under this License. For legal entities, "You" includes any entity that
65controls, is controlled by, or is under common control with You. For purposes
66of this definition, "control" means (a) the power, direct or indirect, to
67cause the direction or management of such entity, whether by contract or
68otherwise, or (b) ownership of more than fifty percent (50%) of the
69outstanding shares or beneficial ownership of such entity.
70
712. License Grants and Conditions
72
73 2.1. Grants
74 Each Contributor hereby grants You a world-wide, royalty-free,
75non-exclusive license:
76
77 (a) under intellectual property rights (other than patent or
78trademark) Licensable by such Contributor to use, reproduce, make available,
79modify, display, perform, distribute, and otherwise exploit its Contributions,
80either on an unmodified basis, with Modifications, or as part of a Larger
81Work; and
82
83 (b) under Patent Claims of such Contributor to make, use, sell,
84offer for sale, have made, import, and otherwise transfer either its
85Contributions or its Contributor Version.
86
87 2.2. Effective Date
88 The licenses granted in Section 2.1 with respect to any Contribution
89become effective for each Contribution on the date the Contributor first
90distributes such Contribution.
91
92 2.3. Limitations on Grant Scope
93 The licenses granted in this Section 2 are the only rights granted under
94this License. No additional rights or licenses will be implied from the
95distribution or licensing of Covered Software under this License.
96Notwithstanding Section 2.1(b) above, no patent license is granted by a
97Contributor:
98
99 (a) for any code that a Contributor has removed from Covered
100Software; or
101
102 (b) for infringements caused by: (i) Your and any other third
103party's modifications of Covered Software, or (ii) the combination of its
104Contributions with other software (except as part of its Contributor Version);
105or
106
107 (c) under Patent Claims infringed by Covered Software in the absence
108of its Contributions.
109
110 This License does not grant any rights in the trademarks, service marks,
111or logos of any Contributor (except as may be necessary to comply with the
112notice requirements in Section 3.4).
113
114 2.4. Subsequent Licenses
115 No Contributor makes additional grants as a result of Your choice to
116distribute the Covered Software under a subsequent version of this License
117(see Section 10.2) or under the terms of a Secondary License (if permitted
118under the terms of Section 3.3).
119
120 2.5. Representation
121 Each Contributor represents that the Contributor believes its
122Contributions are its original creation(s) or it has sufficient rights to
123grant the rights to its Contributions conveyed by this License.
124
125 2.6. Fair Use
126 This License is not intended to limit any rights You have under
127applicable copyright doctrines of fair use, fair dealing, or other
128equivalents.
129
130 2.7. Conditions
131 Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
132Section 2.1.
133
1343. Responsibilities
135
136 3.1. Distribution of Source Form
137 All distribution of Covered Software in Source Code Form, including any
138Modifications that You create or to which You contribute, must be under the
139terms of this License. You must inform recipients that the Source Code Form of
140the Covered Software is governed by the terms of this License, and how they
141can obtain a copy of this License. You may not attempt to alter or restrict
142the recipients' rights in the Source Code Form.
143
144 3.2. Distribution of Executable Form
145 If You distribute Covered Software in Executable Form then:
146
147 (a) such Covered Software must also be made available in Source Code
148Form, as described in Section 3.1, and You must inform recipients of the
149Executable Form how they can obtain a copy of such Source Code Form by
150reasonable means in a timely manner, at a charge no more than the cost of
151distribution to the recipient; and
152
153 (b) You may distribute such Executable Form under the terms of this
154License, or sublicense it under different terms, provided that the license for
155the Executable Form does not attempt to limit or alter the recipients' rights
156in the Source Code Form under this License.
157
158 3.3. Distribution of a Larger Work
159 You may create and distribute a Larger Work under terms of Your choice,
160provided that You also comply with the requirements of this License for the
161Covered Software. If the Larger Work is a combination of Covered Software with
162a work governed by one or more Secondary Licenses, and the Covered Software is
163not Incompatible With Secondary Licenses, this License permits You to
164additionally distribute such Covered Software under the terms of such
165Secondary License(s), so that the recipient of the Larger Work may, at their
166option, further distribute the Covered Software under the terms of either this
167License or such Secondary License(s).
168
169 3.4. Notices
170 You may not remove or alter the substance of any license notices
171(including copyright notices, patent notices, disclaimers of warranty, or
172limitations of liability) contained within the Source Code Form of the Covered
173Software, except that You may alter any license notices to the extent required
174to remedy known factual inaccuracies.
175
176 3.5. Application of Additional Terms
177 You may choose to offer, and to charge a fee for, warranty, support,
178indemnity or liability obligations to one or more recipients of Covered
179Software. However, You may do so only on Your own behalf, and not on behalf of
180any Contributor. You must make it absolutely clear that any such warranty,
181support, indemnity, or liability obligation is offered by You alone, and You
182hereby agree to indemnify every Contributor for any liability incurred by such
183Contributor as a result of warranty, support, indemnity or liability terms You
184offer. You may include additional disclaimers of warranty and limitations of
185liability specific to any jurisdiction.
186
1874. Inability to Comply Due to Statute or Regulation
188If it is impossible for You to comply with any of the terms of this License
189with respect to some or all of the Covered Software due to statute, judicial
190order, or regulation then You must: (a) comply with the terms of this License
191to the maximum extent possible; and (b) describe the limitations and the code
192they affect. Such description must be placed in a text file included with all
193distributions of the Covered Software under this License. Except to the extent
194prohibited by statute or regulation, such description must be sufficiently
195detailed for a recipient of ordinary skill to be able to understand it.
196
1975. Termination
198
199 5.1. The rights granted under this License will terminate automatically
200if You fail to comply with any of its terms. However, if You become compliant,
201then the rights granted under this License from a particular Contributor are
202reinstated (a) provisionally, unless and until such Contributor explicitly and
203finally terminates Your grants, and (b) on an ongoing basis, if such
204Contributor fails to notify You of the non-compliance by some reasonable means
205prior to 60 days after You have come back into compliance. Moreover, Your
206grants from a particular Contributor are reinstated on an ongoing basis if
207such Contributor notifies You of the non-compliance by some reasonable means,
208this is the first time You have received notice of non-compliance with this
209License from such Contributor, and You become compliant prior to 30 days after
210Your receipt of the notice.
211
212 5.2. If You initiate litigation against any entity by asserting a patent
213infringement claim (excluding declaratory judgment actions, counter-claims,
214and cross-claims) alleging that a Contributor Version directly or indirectly
215infringes any patent, then the rights granted to You by any and all
216Contributors for the Covered Software under Section 2.1 of this License shall
217terminate.
218
219 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end
220user license agreements (excluding distributors and resellers) which have been
221validly granted by You or Your distributors under this License prior to
222termination shall survive termination.
223
2246. Disclaimer of Warranty
225Covered Software is provided under this License on an "as is" basis, without
226warranty of any kind, either expressed, implied, or statutory, including,
227without limitation, warranties that the Covered Software is free of defects,
228merchantable, fit for a particular purpose or non-infringing. The entire risk
229as to the quality and performance of the Covered Software is with You. Should
230any Covered Software prove defective in any respect, You (not any Contributor)
231assume the cost of any necessary servicing, repair, or correction. This
232disclaimer of warranty constitutes an essential part of this License. No use
233of any Covered Software is authorized under this License except under this
234disclaimer.
235
2367. Limitation of Liability
237Under no circumstances and under no legal theory, whether tort (including
238negligence), contract, or otherwise, shall any Contributor, or anyone who
239distributes Covered Software as permitted above, be liable to You for any
240direct, indirect, special, incidental, or consequential damages of any
241character including, without limitation, damages for lost profits, loss of
242goodwill, work stoppage, computer failure or malfunction, or any and all other
243commercial damages or losses, even if such party shall have been informed of
244the possibility of such damages. This limitation of liability shall not apply
245to liability for death or personal injury resulting from such party's
246negligence to the extent applicable law prohibits such limitation. Some
247jurisdictions do not allow the exclusion or limitation of incidental or
248consequential damages, so this exclusion and limitation may not apply to You.
249
2508. Litigation
251Any litigation relating to this License may be brought only in the courts of a
252jurisdiction where the defendant maintains its principal place of business and
253such litigation shall be governed by laws of that jurisdiction, without
254reference to its conflict-of-law provisions. Nothing in this Section shall
255prevent a party's ability to bring cross-claims or counter-claims.
256
2579. Miscellaneous
258This License represents the complete agreement concerning the subject matter
259hereof. If any provision of this License is held to be unenforceable, such
260provision shall be reformed only to the extent necessary to make it
261enforceable. Any law or regulation which provides that the language of a
262contract shall be construed against the drafter shall not be used to construe
263this License against a Contributor.
264
26510. Versions of the License
266
267 10.1. New Versions
268 Mozilla Foundation is the license steward. Except as provided in Section
26910.3, no one other than the license steward has the right to modify or publish
270new versions of this License. Each version will be given a distinguishing
271version number.
272
273 10.2. Effect of New Versions
274 You may distribute the Covered Software under the terms of the version of
275the License under which You originally received the Covered Software, or under
276the terms of any subsequent version published by the license steward.
277
278 10.3. Modified Versions
279 If you create software not governed by this License, and you want to
280create a new license for such software, you may create and use a modified
281version of this License if you rename the license and remove any references to
282the name of the license steward (except to note that such modified license
283differs from this License).
284
285 10.4. Distributing Source Code Form that is Incompatible With Secondary
286Licenses
287 If You choose to distribute Source Code Form that is Incompatible With
288Secondary Licenses under the terms of this version of the License, the notice
289described in Exhibit B of this License must be attached.
290
291Exhibit A - Source Code Form License Notice
292
293 This Source Code Form is subject to the terms of the Mozilla Public
294License, v. 2.0. If a copy of the MPL was not distributed with this file, You
295can obtain one at http://mozilla.org/MPL/2.0/.
296
297If it is not possible or desirable to put the notice in a particular file,
298then You may include the notice in a location (such as a LICENSE file in a
299relevant directory) where a recipient would be likely to look for such a
300notice.
301
302You may add additional accurate notices of copyright ownership.
303
304Exhibit B - "Incompatible With Secondary Licenses" Notice
305
306 This Source Code Form is "Incompatible With Secondary Licenses", as
307defined by the Mozilla Public License, v. 2.0.
README.md created+13
......@@ -0,0 +1,13 @@
1# zig-qrcode
2
3![loc](https://sloc.xyz/github/nektro/zig-qrcode)
4[![license](https://img.shields.io/github/license/nektro/zig-qrcode.svg)](https://github.com/nektro/zig-qrcode/blob/master/LICENSE)
5[![nektro @ github sponsors](https://img.shields.io/badge/sponsors-nektro-purple?logo=github)](https://github.com/sponsors/nektro)
6[![Zig](https://img.shields.io/badge/Zig-0.14-f7a41d)](https://ziglang.org/)
7[![Zigmod](https://img.shields.io/badge/Zigmod-latest-f7a41d)](https://github.com/nektro/zigmod)
8
9Zig generator for quick-response codes
10
11https://en.wikipedia.org/wiki/QR_code
12
13https://observablehq.com/@zavierhenry/encoding-qr-codes
build.zig created+20
......@@ -0,0 +1,20 @@
1const std = @import("std");
2const deps = @import("./deps.zig");
3
4pub fn build(b: *std.Build) void {
5 const target = b.standardTargetOptions(.{});
6 const mode = b.option(std.builtin.Mode, "mode", "") orelse .Debug;
7
8 const tests = b.addTest(.{
9 .root_source_file = b.path("test.zig"),
10 .target = target,
11 .optimize = mode,
12 });
13 deps.addAllTo(tests);
14
15 const test_step = b.step("test", "Run all library tests");
16 const tests_run = b.addRunArtifact(tests);
17 tests_run.setCwd(b.path("."));
18 tests_run.has_side_effects = true;
19 test_step.dependOn(&tests_run.step);
20}
licenses.txt created+7
......@@ -0,0 +1,7 @@
1MPL-2.0:
2= https://spdx.org/licenses/MPL-2.0
3- This
4
5MIT:
6= https://spdx.org/licenses/MIT
7- git https://github.com/nektro/zig-extras
qrcode.zig created+744
......@@ -0,0 +1,744 @@
1const std = @import("std");
2const extras = @import("extras");
3
4/// GF(256)[0...255]
5const GF = blk: {
6 var gf: [256]u8 = @splat(2);
7 for (0..256) |n| {
8 if (n == 0) {
9 gf[n] = 1;
10 continue;
11 }
12 if (n < 8) {
13 gf[n] <<= @intCast(n - 1);
14 continue;
15 }
16 gf[n] = gf[n - 1] *% 2;
17 if (gf[n - 1] > 127) gf[n] ^= 0b00011101;
18 }
19 const out = gf;
20 break :blk out;
21};
22
23pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16, input: []const u8) BitGrid(4 * version + 17) {
24 _ = &version;
25 _ = &mode;
26 _ = &level;
27 _ = &input;
28
29 std.log.warn("mode: {s}", .{@tagName(mode)});
30 std.log.warn("level: {s}", .{@tagName(level)});
31
32 if (mode == .numeric) std.debug.assert(extras.matchesAll(u8, input, isNumeric));
33 if (mode == .alphanumeric) std.debug.assert(extras.matchesAll(u8, input, isAlphanumeric));
34 // latin1 is a full 1-byte format, some readers can detect utf-8
35 // shift jis is a full 2-byte format
36
37 const max_lengths = comptime maxLenPerModeLevelVersion(mode, level);
38 const max_length = max_lengths[version];
39 std.log.warn("actual version: {d},{d},{d}", .{ version, max_length, input.len });
40 std.debug.assert(input.len <= max_length);
41 for (max_lengths, 0..) |l, i| if (l > input.len) break std.log.warn("reccom version: {d},{d}", .{ i, l });
42
43 const mode_indicator: u4 = switch (mode) {
44 .numeric => 0b0001,
45 .alphanumeric => 0b0010,
46 .latin1 => 0b0100,
47 .shiftjis => 0b1000,
48 };
49 std.log.warn("mode_indicator: {d}", .{mode_indicator});
50
51 const LenIndicator = switch (mode) {
52 .numeric => switch (version) {
53 1...9 => u10,
54 10...26 => u12,
55 27...40 => u14,
56 else => unreachable,
57 },
58 .alphanumeric => switch (version) {
59 1...9 => u9,
60 10...26 => u11,
61 27...40 => u13,
62 else => unreachable,
63 },
64 .latin1 => switch (version) {
65 1...9 => u8,
66 10...26 => u16,
67 27...40 => u16,
68 else => unreachable,
69 },
70 .shiftjis => switch (version) {
71 1...9 => u8,
72 10...26 => u10,
73 27...40 => u12,
74 else => unreachable,
75 },
76 };
77 const len_indicator: LenIndicator = @intCast(input.len);
78 std.log.warn("len_indicator: {d}", .{len_indicator});
79
80 const total_bits = (max_length + 2) * 8;
81 std.log.warn("total_bits: {d}", .{total_bits});
82
83 var scratch: std.bit_set.ArrayBitSet(u8, total_bits) = .initEmpty();
84 _ = &scratch;
85 var scratchw: BitsetWriter(u8, total_bits) = .{ .set = &scratch };
86 _ = &scratchw;
87
88 scratchw.writeInt(mode_indicator);
89 scratchw.writeInt(len_indicator);
90 for (input) |x| scratchw.writeInt(x);
91
92 const data_bits = input.len * 8 + @bitSizeOf(@TypeOf(mode_indicator)) + @bitSizeOf(@TypeOf(len_indicator));
93 std.log.warn("data_bits: {d}", .{data_bits});
94 std.debug.assert(scratchw.idx == data_bits);
95 std.debug.assert(scratchw.idx == 44); //temp
96
97 // write @max(4, padding_bits) 0s
98 for (0..@min(4, total_bits - scratchw.idx)) |_| scratchw.writeInt(@as(u1, 0));
99 std.debug.assert(scratchw.idx == 48); //temp
100
101 // add zero bits to make data_bits%8 == 0
102 for (0..-%scratchw.idx & 7) |_| scratchw.writeInt(@as(u1, 0));
103 std.debug.assert(scratchw.idx == 48); //temp
104
105 // add 236(11101100) 17(00010001) until we reach total_bits
106 while (scratchw.idx < total_bits) {
107 scratchw.writeInt(@as(u8, 0xec));
108 scratchw.writeInt(@as(u8, 0x11));
109 }
110
111 // std.log.warn("{b:0>8}", .{&bitReverse(scratch.masks)});
112 // std.log.warn("{d}", .{&bitReverse(scratch.masks)});
113 // std.log.warn("{x}", .{&bitReverse(scratch.masks)});
114
115 std.debug.assert(@bitReverse(scratch.masks[0]) == 0b01000000); //temp
116 std.debug.assert(!scratch.isSet(0)); //temp
117 std.debug.assert(scratch.isSet(1)); //temp
118 std.debug.assert(!scratch.isSet(2)); //temp
119 std.debug.assert(!scratch.isSet(3)); //temp
120 // 01000000 01000101 01100110 01010111 00100011 00010000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100
121 // 01000000 01000101 01100110 01010111 00100011 00010000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100
122
123 const groups = 1; // ?
124 _ = &groups;
125 const blocks = 1; // ?
126 _ = &blocks;
127 const dc_per_block = 19; //?
128 _ = &dc_per_block;
129 const ecc_per_block = 7; //?
130 _ = &ecc_per_block;
131
132 var message_polynomial = scratch.masks;
133 _ = &message_polynomial;
134 std.log.warn("message_polynomial: {d}", .{&bitReverse(message_polynomial)});
135
136 const generator_polynomial = reverseArray(generatorPolynomial(ecc_per_block));
137 _ = &generator_polynomial;
138 std.log.warn("generator_polynomial: {d}", .{generator_polynomial});
139
140 const generator_polynomial_alpha = to_alpha(generator_polynomial);
141 _ = &generator_polynomial_alpha;
142 std.log.warn("generator_polynomial_alpha: {d}", .{generator_polynomial_alpha});
143
144 // Now that the message and generator polynomials have been created, divide them to get the error codewords.
145 {
146 // 1. Multiply the message polynomial by x^n where n is the number of error correction codewords to be generated
147 // and multiply the generator polynomial by x^m where m is the leading exponent of the message polynomial.
148 // 2. Multiply the generator polynomial by the lead term of the message polynomial.
149 // 3. XOR the result of Step 2 with the message polynomial and discard the lead 0 term.
150 // 4. Repeat Steps 2-3 n-1 more times where n is the number of data codewords.
151 }
152 var mp: [scratch.masks.len + ecc_per_block]?u8 = @splat(null);
153 for (&message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0];
154
155 for (0..scratch.masks.len) |i| {
156 const lead = mp[i].?;
157 for (&generator_polynomial_alpha, 0..) |x, j| {
158 var y = GF[(@as(usize, x) + lead) % 255];
159 if (mp[i + j]) |z| y ^= GF[z];
160 mp[i + j] = if (y == 0) null else to_alpha(.{y})[0];
161 }
162 }
163
164 var error_codewords: [ecc_per_block]u8 = @splat(0);
165 for (0..ecc_per_block) |i| error_codewords[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0;
166 std.log.warn("error_codewords: {d}", .{error_codewords});
167
168 const interleaved_message = scratch.masks ++ bitReverse(error_codewords);
169 _ = &interleaved_message;
170 std.log.warn("interleaved_message: {b:0>8}", .{&bitReverse(interleaved_message)});
171
172 // remainder bits
173
174 //
175
176 const size = 4 * version + 17;
177 var grid: BitGrid(size) = .initEmpty();
178 _ = &grid;
179
180 // top left finder pattern
181 for (0..7) |i| for (0..7) |j| grid.setT(0 + i, 0 + j);
182 for (0..5) |i| for (0..5) |j| grid.setF(1 + i, 1 + j);
183 for (0..3) |i| for (0..3) |j| grid.setT(2 + i, 2 + j);
184
185 // top right finder pattern
186 for (0..7) |i| for (0..7) |j| grid.setT(size - 7 + 0 + i, 0 + j);
187 for (0..5) |i| for (0..5) |j| grid.setF(size - 7 + 1 + i, 1 + j);
188 for (0..3) |i| for (0..3) |j| grid.setT(size - 7 + 2 + i, 2 + j);
189
190 // bottom left finder pattern
191 for (0..7) |i| for (0..7) |j| grid.setT(0 + i, size - 7 + 0 + j);
192 for (0..5) |i| for (0..5) |j| grid.setF(1 + i, size - 7 + 1 + j);
193 for (0..3) |i| for (0..3) |j| grid.setT(2 + i, size - 7 + 2 + j);
194
195 // timing pattern
196 for (8..size) |i| if (i % 2 == 0) grid.set(i, 6);
197 for (8..size) |i| if (i % 2 == 0) grid.set(6, i);
198
199 // alignment patterns
200
201 // dark module
202 grid.set(8, 4 * (version - 1) + 13);
203
204 // version block
205
206 // data mask (data cannot be placed where this is set)
207 var data_mask: BitGrid(size) = .initEmpty();
208 for (0..9) |y| for (0..9) |x| data_mask.set(x, y);
209 for (0..9) |y| for (0..8) |x| data_mask.set(size - 8 + x, y);
210 for (0..8) |y| for (0..9) |x| data_mask.set(x, size - 8 + y);
211 for (0..size) |i| data_mask.set(i, 6);
212 for (0..size) |i| data_mask.set(6, i);
213
214 // write data
215 var data_iter: BufBiterator = .init(&bitReverse(interleaved_message));
216 _ = &data_iter;
217
218 var griditer: GridZigZag = .init(size, 6);
219 while (griditer.next()) |idx| {
220 const x = idx % size;
221 const y = (idx - x) / size;
222 // std.log.warn("idx: {d} {d},{d}", .{ idx, x, y });
223 if (data_mask.isSet(x, y)) continue;
224 grid.debug.set((y * size) + x);
225 if (@bitCast(data_iter.next() orelse break)) grid.set(x, y);
226 }
227
228 //
229
230 for (0..8) |y| for (0..8) |x| grid.debug.set((y * size) + x);
231 for (0..8) |y| for (0..8) |x| grid.debug.set((y * size) + size - 8 + x);
232 for (0..8) |y| for (0..8) |x| grid.debug.set(((size - 8 + y) * size) + x);
233 for (0..size) |i| grid.debug.set((6 * size) + i);
234 for (0..size) |i| grid.debug.set((i * size) + 6);
235
236 // format info
237 var masks = [_]std.bit_set.IntegerBitSet(15){
238 .{ .mask = @bitReverse(formatPattern(level, 0)) },
239 .{ .mask = @bitReverse(formatPattern(level, 1)) },
240 .{ .mask = @bitReverse(formatPattern(level, 2)) },
241 .{ .mask = @bitReverse(formatPattern(level, 3)) },
242 .{ .mask = @bitReverse(formatPattern(level, 4)) },
243 .{ .mask = @bitReverse(formatPattern(level, 5)) },
244 .{ .mask = @bitReverse(formatPattern(level, 6)) },
245 .{ .mask = @bitReverse(formatPattern(level, 7)) },
246 };
247 var grids = [_]BitGrid(size){
248 grid,
249 grid,
250 grid,
251 grid,
252 grid,
253 grid,
254 grid,
255 grid,
256 };
257 for (&grids, &masks) |*g, m| {
258 g.setV(0, 8, m.isSet(0));
259 g.setV(1, 8, m.isSet(1));
260 g.setV(2, 8, m.isSet(2));
261 g.setV(3, 8, m.isSet(3));
262 g.setV(4, 8, m.isSet(4));
263 g.setV(5, 8, m.isSet(5));
264 g.setV(7, 8, m.isSet(6));
265 g.setV(8, 8, m.isSet(7));
266 g.setV(8, 7, m.isSet(8));
267 g.setV(8, 5, m.isSet(9));
268 g.setV(8, 4, m.isSet(10));
269 g.setV(8, 3, m.isSet(11));
270 g.setV(8, 2, m.isSet(12));
271 g.setV(8, 1, m.isSet(13));
272 g.setV(8, 0, m.isSet(14));
273
274 g.setV(8, size - 1 - 0, m.isSet(0));
275 g.setV(8, size - 1 - 1, m.isSet(1));
276 g.setV(8, size - 1 - 2, m.isSet(2));
277 g.setV(8, size - 1 - 3, m.isSet(3));
278 g.setV(8, size - 1 - 4, m.isSet(4));
279 g.setV(8, size - 1 - 5, m.isSet(5));
280 g.setV(8, size - 1 - 6, m.isSet(6));
281 g.setV(size - 8 + 0, 8, m.isSet(7));
282 g.setV(size - 8 + 1, 8, m.isSet(8));
283 g.setV(size - 8 + 2, 8, m.isSet(9));
284 g.setV(size - 8 + 3, 8, m.isSet(10));
285 g.setV(size - 8 + 4, 8, m.isSet(11));
286 g.setV(size - 8 + 5, 8, m.isSet(12));
287 g.setV(size - 8 + 6, 8, m.isSet(13));
288 g.setV(size - 8 + 7, 8, m.isSet(14));
289 }
290
291 // masking patterns
292 // Pattern 0: (row + column) mod 2 == 0
293 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row + col) % 2 == 0) grids[0].flip(col, row);
294 // Pattern 1: row mod 2 == 0
295 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and row % 2 == 0) grids[1].flip(col, row);
296 // Pattern 2: column mod 3 == 0
297 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and col % 3 == 0) grids[2].flip(col, row);
298 // Pattern 3: (row + column) mod 3 == 0
299 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row + col) % 3 == 0) grids[3].flip(col, row);
300 // Pattern 4: (floor(row/2) + floor(column / 3)) mod 2 == 0
301 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row / 2 + col / 3) % 2 == 0) grids[4].flip(col, row);
302 // Pattern 5: ((row * column) mod 2) + ((row * column) mod 3) == 0
303 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row * col) % 2) + ((row * col) % 3)) == 0) grids[5].flip(col, row);
304 // Pattern 6: ( ((row * column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
305 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row * col) % 2) + ((row * col) % 3)) % 2 == 0) grids[6].flip(col, row);
306 // Pattern 7: ( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
307 for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row + col) % 2) + ((row * col) % 3)) % 2 == 0) grids[7].flip(col, row);
308
309 // penalty
310 var penaltys: [8][4]u32 = @splat(@splat(0));
311 for (&grids, 0..) |*g, i| {
312 var prev = false;
313 var run: u16 = 0;
314 for (0..size) |y| {
315 for (0..size) |x| {
316 if (x == 0) {
317 run = 0;
318 prev = g.isSet(0, y);
319 }
320 const this = g.isSet(x, y);
321 if (this == prev) {
322 run += 1;
323 penaltys[i][0] += if (run == 5) 3 else if (run > 5) 1 else 0;
324 } else {
325 run = 1;
326 }
327 prev = this;
328 }
329 }
330 for (0..size) |x| {
331 for (0..size) |y| {
332 if (y == 0) {
333 run = 0;
334 prev = g.isSet(x, 0);
335 }
336 const this = g.isSet(x, y);
337 if (this == prev) {
338 run += 1;
339 penaltys[i][0] += if (run == 5) 3 else if (run > 5) 1 else 0;
340 } else {
341 run = 1;
342 }
343 prev = this;
344 }
345 }
346 }
347 for (&grids, 0..) |*g, i| {
348 for (0..size - 1) |y| {
349 for (0..size - 1) |x| {
350 const p0 = g.isSet(x, y);
351 const p1 = g.isSet(x + 1, y);
352 const p2 = g.isSet(x, y + 1);
353 const p3 = g.isSet(x + 1, y + 1);
354 penaltys[i][1] += if (p0 == p1 and p1 == p2 and p2 == p3) 3 else 0;
355 }
356 }
357 }
358 for (&grids, 0..) |*g, i| {
359 for (0..size) |y| {
360 for (0..size - 10) |x| {
361 const sel = g.sliceX(y, x, 11);
362 penaltys[i][2] += if (sel.mask == 0b00001011101) 40 else 0;
363 penaltys[i][2] += if (sel.mask == 0b10111010000) 40 else 0;
364 }
365 }
366 for (0..size) |x| {
367 for (0..size - 10) |y| {
368 const sel = g.sliceY(x, y, 11);
369 penaltys[i][2] += if (sel.mask == 0b00001011101) 40 else 0;
370 penaltys[i][2] += if (sel.mask == 0b10111010000) 40 else 0;
371 }
372 }
373 }
374 for (&grids, 0..) |*g, i| {
375 const total_modules: comptime_float = size * size;
376 var dark_modules: f32 = 0;
377 for (0..size) |y| {
378 for (0..size) |x| {
379 if (g.isSet(x, y)) {
380 dark_modules += 1;
381 }
382 }
383 }
384 const percent: u32 = @intFromFloat(@ceil(dark_modules / total_modules * 100));
385 const round1 = percent + (-%percent & 4);
386 const round2 = round1 -| 5;
387 const sub = [_]u32{ @abs(@as(i32, @intCast(round1)) - 50), @abs(@as(i32, @intCast(round2)) - 50) };
388 const div = [_]u32{ @divFloor(sub[0], 5), @divFloor(sub[1], 5) };
389 penaltys[i][3] += std.mem.min(u32, &div) * 10;
390 }
391 for (&penaltys) |x| std.log.warn("penaltys: {d}", .{&x});
392
393 var penalty: [8]u32 = @splat(0);
394 for (0..8) |i| penalty[i] = extras.sum(u32, &penaltys[i]);
395
396 const min_penalty = std.mem.min(u32, &penalty);
397 std.log.warn("min_penalty: {d}", .{min_penalty});
398 const min_mask = std.mem.indexOfScalar(u32, &penalty, min_penalty).?;
399 std.log.warn("min_mask: {d}", .{min_mask});
400
401 std.log.warn("", .{});
402 return grids[min_mask];
403}
404
405fn BitGrid(comptime size: usize) type {
406 return struct {
407 bset: std.bit_set.ArrayBitSet(usize, size * size),
408 debug: std.bit_set.ArrayBitSet(usize, size * size),
409 comptime size: usize = size,
410
411 pub fn initEmpty() @This() {
412 return .{
413 .bset = .initEmpty(),
414 .debug = .initEmpty(),
415 };
416 }
417
418 pub fn isSet(self: *const @This(), x: usize, y: usize) bool {
419 return self.bset.isSet((y * size) + x);
420 }
421
422 pub fn set(self: *@This(), x: usize, y: usize) void {
423 self.bset.set((y * size) + x);
424 self.debug.set((y * size) + x);
425 }
426
427 pub fn setF(self: *@This(), x: usize, y: usize) void {
428 self.bset.setValue((y * size) + x, false);
429 self.debug.setValue((y * size) + x, true);
430 }
431
432 pub fn setT(self: *@This(), x: usize, y: usize) void {
433 self.bset.setValue((y * size) + x, true);
434 self.debug.setValue((y * size) + x, true);
435 }
436
437 pub fn setV(self: *@This(), x: usize, y: usize, value: bool) void {
438 self.bset.setValue((y * size) + x, value);
439 self.debug.setValue((y * size) + x, true);
440 }
441
442 pub fn hasSet(self: *const @This(), x: usize, y: usize) bool {
443 return self.debug.isSet((y * size) + x);
444 }
445
446 pub fn flip(self: *@This(), x: usize, y: usize) void {
447 std.debug.assert(self.debug.isSet((y * size) + x));
448 self.bset.toggle((y * size) + x);
449 }
450
451 pub fn sliceX(self: *@This(), y: usize, sx: usize, comptime len: usize) std.bit_set.IntegerBitSet(len) {
452 var result: std.bit_set.IntegerBitSet(len) = .initEmpty();
453 for (0..len) |i| result.setValue(i, self.isSet(sx + i, y));
454 return result;
455 }
456
457 pub fn sliceY(self: *@This(), x: usize, sy: usize, comptime len: usize) std.bit_set.IntegerBitSet(len) {
458 var result: std.bit_set.IntegerBitSet(len) = .initEmpty();
459 for (0..len) |i| result.setValue(i, self.isSet(x, sy + i));
460 return result;
461 }
462 };
463}
464
465fn BitsetWriter(MaskIntType: type, size: usize) type {
466 return struct {
467 set: *std.bit_set.ArrayBitSet(MaskIntType, size),
468 idx: usize = 0,
469
470 pub fn writeInt(self: *@This(), int: anytype) void {
471 return writeInt2(self, @bitReverse(int));
472 }
473 fn writeInt2(self: *@This(), int: anytype) void {
474 const T = @TypeOf(int);
475 const info = @typeInfo(T).int;
476 comptime std.debug.assert(info.signedness == .unsigned);
477 if (self.idx == size) {
478 return;
479 }
480 if (T == u1) {
481 self.set.setValue(self.idx, @bitCast(int));
482 self.idx += 1;
483 return;
484 }
485 self.set.setValue(self.idx, (int & 1) == 1);
486 self.idx += 1;
487 return writeInt2(self, @as(extras.OneSmallerInt(T), @intCast(int >> 1)));
488 }
489 };
490}
491
492fn bitReverse(array: anytype) [array.len]std.meta.Child(@TypeOf(array)) {
493 var result: [array.len]std.meta.Child(@TypeOf(array)) = @splat(0);
494 for (&array, &result) |x, *y| y.* = @bitReverse(x);
495 return result;
496}
497
498fn reverseArray(array: anytype) @TypeOf(array) {
499 var result: @TypeOf(array) = array;
500 for (0..result.len / 2) |i| {
501 std.mem.swap(std.meta.Child(@TypeOf(result)), &result[i], &result[result.len - 1 - i]);
502 }
503 return result;
504}
505
506//
507
508pub const Mode = enum {
509 numeric,
510 alphanumeric,
511 latin1,
512 shiftjis,
513};
514
515pub const Level = enum {
516 L,
517 M,
518 Q,
519 H,
520};
521
522// Sourced from https://gist.github.com/ZavierHenry/a4b3761212109d5598bb2db79c334782
523pub fn maxLenPerModeLevelVersion(mode: Mode, level: Level) *const [41]u16 {
524 return switch (mode) {
525 .numeric => switch (level) {
526 .L => &.{ 0, 41, 77, 127, 187, 255, 322, 370, 461, 552, 652, 772, 883, 1022, 1101, 1250, 1408, 1548, 1725, 1903, 2061, 2232, 2409, 2620, 2812, 3057, 2383, 3517, 3669, 3909, 4158, 4417, 4686, 4965, 5253, 5529, 5836, 6153, 6479, 6743, 7089 },
527 .M => &.{ 0, 34, 63, 101, 149, 202, 255, 293, 365, 432, 513, 694, 691, 796, 871, 991, 1082, 1212, 1346, 1500, 1600, 1708, 1872, 2059, 2188, 2395, 2544, 2701, 2857, 3035, 3289, 3486, 3693, 3909, 4134, 4343, 4588, 4775, 5039, 5313, 5596 },
528 .Q => &.{ 0, 27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427, 489, 580, 621, 703, 775, 876, 948, 1063, 1159, 1224, 1358, 1468, 1588, 1718, 1804, 1933, 2085, 2181, 2358, 2473, 2670, 2805, 2949, 3081, 3244, 3417, 3599, 3791, 3993 },
529 .H => &.{ 0, 17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331, 374, 427, 468, 530, 602, 674, 746, 813, 919, 969, 1056, 1108, 1228, 1286, 1425, 1501, 1581, 1677, 1782, 1897, 2022, 2157, 2301, 2361, 2524, 2625, 2735, 2927, 3057 },
530 },
531 .alphanumeric => switch (level) {
532 .L => &.{ 0, 25, 47, 77, 114, 154, 195, 224, 279, 335, 395, 468, 535, 619, 667, 758, 854, 938, 1046, 1153, 1249, 1352, 1460, 1588, 1704, 1853, 1990, 2132, 2223, 2369, 2520, 2677, 2840, 3009, 3183, 3351, 3537, 3729, 3927, 4087, 4296 },
533 .M => &.{ 0, 20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366, 419, 483, 528, 600, 656, 734, 816, 909, 970, 1035, 1134, 1248, 1326, 1451, 1542, 1637, 1732, 1839, 1994, 2113, 2238, 2369, 2506, 2632, 2780, 2894, 3054, 3220, 3391 },
534 .Q => &.{ 0, 16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259, 296, 352, 376, 426, 470, 531, 574, 644, 702, 742, 823, 890, 963, 1041, 1094, 1172, 1263, 1322, 1429, 1499, 1618, 1700, 1787, 1867, 1966, 2071, 2181, 2298, 2420 },
535 .H => &.{ 0, 10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200, 227, 259, 283, 321, 365, 408, 452, 493, 557, 587, 640, 672, 744, 779, 864, 910, 958, 1016, 1080, 1150, 1226, 1307, 1394, 1431, 1530, 1591, 1658, 1774, 1852 },
536 },
537 .latin1 => switch (level) {
538 .L => &.{ 0, 17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321, 367, 425, 458, 520, 586, 644, 718, 792, 858, 929, 1003, 1091, 1171, 1273, 1367, 1465, 1528, 1628, 1732, 1840, 1952, 2068, 2188, 2303, 2431, 2563, 2699, 2809, 2953 },
539 .M => &.{ 0, 14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251, 287, 331, 362, 412, 450, 504, 560, 624, 666, 711, 779, 857, 911, 997, 1059, 1125, 1190, 1264, 1370, 1452, 1538, 1628, 1722, 1809, 1911, 1989, 2099, 2213, 2331 },
540 .Q => &.{ 0, 11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177, 203, 241, 258, 292, 322, 364, 394, 442, 482, 509, 565, 611, 661, 715, 751, 805, 868, 908, 982, 1030, 1112, 1168, 1228, 1283, 1351, 1423, 1499, 1579, 1663 },
541 .H => &.{ 0, 7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137, 155, 177, 194, 220, 250, 280, 310, 338, 382, 403, 439, 461, 511, 535, 593, 625, 658, 698, 742, 790, 842, 898, 958, 983, 1051, 1093, 1139, 1219, 1273 },
542 },
543 .shiftjis => switch (level) {
544 .L => &.{ 0, 10, 20, 32, 48, 65, 82, 95, 118, 141, 167, 198, 226, 262, 282, 320, 361, 397, 442, 488, 528, 572, 618, 672, 721, 784, 842, 902, 940, 1002, 1066, 1132, 1201, 1273, 1347, 1417, 1496, 1577, 1661, 1729, 1817 },
545 .M => &.{ 0, 8, 16, 26, 38, 52, 65, 75, 93, 111, 131, 155, 177, 204, 223, 254, 277, 310, 345, 384, 410, 438, 480, 528, 561, 614, 652, 692, 732, 778, 843, 894, 947, 1002, 1060, 1113, 1176, 1224, 1292, 1362, 1435 },
546 .Q => &.{ 0, 7, 12, 20, 28, 37, 45, 53, 66, 80, 93, 109, 125, 149, 159, 180, 198, 224, 243, 272, 297, 314, 348, 376, 407, 440, 462, 496, 534, 559, 604, 634, 684, 719, 756, 790, 832, 876, 923, 972, 1024 },
547 .H => &.{ 0, 4, 8, 15, 21, 27, 36, 39, 52, 60, 74, 85, 96, 109, 120, 136, 154, 173, 191, 208, 235, 248, 270, 284, 315, 330, 365, 385, 405, 430, 457, 486, 518, 553, 590, 605, 647, 673, 701, 750, 784 },
548 },
549 };
550}
551
552fn isNumeric(c: u8) bool {
553 return switch (c) {
554 '0'...'9' => true,
555 else => false,
556 };
557}
558
559fn isAlphanumeric(c: u8) bool {
560 return switch (c) {
561 '0'...'9' => true,
562 'A'...'Z' => true,
563 ' ', '$', '%', '*', '+', '-', '.', '/', ':', ',' => true,
564 else => false,
565 };
566}
567
568fn generatorPolynomial(comptime n: usize) [n + 1]u8 {
569 if (n == 1) return .{ 1, 1 };
570
571 const prev = generatorPolynomial(n - 1);
572 const prev_alpha = to_alpha(prev);
573 const term_alpha: [2]u8 = .{ n - 1, 0 };
574
575 var multiply_alpha: [n * 2][2]u8 = @splat(@splat(0));
576 for (&prev_alpha, 0..) |a, i| {
577 for (&term_alpha, 0..) |b, j| {
578 multiply_alpha[(term_alpha.len * i) + j] = .{ a + b, @intCast(i + j) };
579 }
580 }
581 var combine_alpha: [n + 1]u8 = @splat(0);
582 for (0..n + 1) |i| {
583 for (&multiply_alpha) |el| {
584 if (el[1] == i) combine_alpha[i] ^= GF[el[0]];
585 }
586 }
587 return combine_alpha;
588}
589
590fn to_alpha(array: anytype) [array.len]u8 {
591 var result: [array.len]u8 = array;
592 for (&result) |*x| x.* = @intCast(std.mem.indexOfScalar(u8, &GF, x.*).?);
593 return result;
594}
595
596fn formatPattern(level: Level, mask: u3) u15 {
597 return switch (level) {
598 .L => switch (mask) {
599 0 => 0b111011111000100,
600 1 => 0b111001011110011,
601 2 => 0b111110110101010,
602 3 => 0b111100010011101,
603 4 => 0b110011000101111,
604 5 => 0b110001100011000,
605 6 => 0b110110001000001,
606 7 => 0b110100101110110,
607 },
608 .M => switch (mask) {
609 0 => 0b101010000010010,
610 1 => 0b101000100100101,
611 2 => 0b101111001111100,
612 3 => 0b101101101001011,
613 4 => 0b100010111111001,
614 5 => 0b100000011001110,
615 6 => 0b100111110010111,
616 7 => 0b100101010100000,
617 },
618 .Q => switch (mask) {
619 0 => 0b011010101011111,
620 1 => 0b011000001101000,
621 2 => 0b011111100110001,
622 3 => 0b011101000000110,
623 4 => 0b010010010110100,
624 5 => 0b010000110000011,
625 6 => 0b010111011011010,
626 7 => 0b010101111101101,
627 },
628 .H => switch (mask) {
629 0 => 0b001011010001001,
630 1 => 0b001001110111110,
631 2 => 0b001110011100111,
632 3 => 0b001100111010000,
633 4 => 0b000011101100010,
634 5 => 0b000001001010101,
635 6 => 0b000110100001100,
636 7 => 0b000100000111011,
637 },
638 };
639}
640
641//
642//
643//
644//
645
646const BufBiterator = struct {
647 buf: []const u8,
648 bits: std.bit_set.IntegerBitSet(8),
649 idx: u8, // buf index
650 jdx: u8, // bits index
651
652 pub fn init(buf: []const u8) BufBiterator {
653 return .{
654 .buf = buf,
655 .bits = .{ .mask = buf[0] },
656 .idx = 0,
657 .jdx = 0,
658 };
659 }
660
661 pub fn next(self: *BufBiterator) ?u1 {
662 if (self.jdx == 8) {
663 self.jdx = 0;
664 self.idx += 1;
665 if (self.idx == self.buf.len) return null;
666 self.bits.mask = self.buf[self.idx];
667 return self.next();
668 }
669 const result = self.bits.isSet(7 - self.jdx);
670 self.jdx += 1;
671 return @intFromBool(result);
672 }
673};
674
675//
676//
677//
678//
679//
680
681const GridZigZag = struct {
682 grid_size: usize,
683 col_skip: usize,
684 dir: u1,
685 phase: u1,
686 y: usize,
687 x: usize,
688
689 pub fn init(grid_size: usize, col_skip: usize) GridZigZag {
690 return .{
691 .grid_size = grid_size,
692 .col_skip = col_skip,
693 .dir = 0,
694 .phase = 0,
695 .y = grid_size,
696 .x = grid_size - 2,
697 };
698 }
699
700 pub fn next(self: *@This()) ?usize {
701 blk: switch (self.dir) {
702 0 => switch (self.phase) { //up
703 0 => { //prev left
704 if (self.y == 0) {
705 self.x -= 1;
706 self.dir = 1;
707 self.phase = 1;
708 if (self.x == self.col_skip and self.col_skip == 0) return null;
709 if (self.x == self.col_skip) self.x -= 1;
710 break :blk;
711 }
712 self.y -= 1;
713 self.x += 1;
714 self.phase +%= 1;
715 },
716 1 => { //prev right
717 self.x -= 1;
718 self.phase +%= 1;
719 },
720 },
721 1 => switch (self.phase) { //down
722 0 => { //prev left
723 if (self.y == self.grid_size - 1) {
724 if (self.x == 0) return null;
725 self.x -= 1;
726 if (self.x == self.col_skip and self.col_skip == 0) return null;
727 if (self.x == self.col_skip) self.x -= 1;
728 self.dir = 0;
729 self.phase = 1;
730 break :blk;
731 }
732 self.x += 1;
733 self.y += 1;
734 self.phase +%= 1;
735 },
736 1 => { //prev right
737 self.x -= 1;
738 self.phase +%= 1;
739 },
740 },
741 }
742 return (self.y * self.grid_size) + self.x;
743 }
744};
test.zig created+31
......@@ -0,0 +1,31 @@
1const std = @import("std");
2const qrcode = @import("qrcode");
3
4test {
5 const grid = qrcode.encode(.latin1, .L, 1, "Ver1");
6 // 40 45 66 57 23 10 ec 11 ec
7 _ = &grid;
8
9 const bg_black = "\x1b[40m";
10 const bg_white = "\x1b[47m";
11 const bg_blue = "\x1b[44m";
12 const bg_reset = "\x1b[49m";
13
14 const fg_black = "\x1b[30m";
15 const fg_white = "\x1b[37m";
16 const fg_reset = "\x1b[39m";
17
18 for (0..grid.size) |y| {
19 for (0..grid.size) |x| {
20 std.debug.print("{s}", .{if (grid.hasSet(x, y))
21 if (grid.isSet(x, y))
22 bg_black ++ fg_black ++ "██"
23 else
24 bg_white ++ fg_white ++ " "
25 else
26 bg_blue ++ fg_white ++ " -"});
27 }
28 std.debug.print("{s}", .{fg_reset ++ bg_reset});
29 std.debug.print("\n", .{});
30 }
31}
zigmod.yml created+7
......@@ -0,0 +1,7 @@
1id: nvs62rfbcub6qtnmc9v7753nokki6w1y5flyu6hfdkqvtn5x
2name: qrcode
3main: qrcode.zig
4license: MPL-2.0
5description: QR code (quick-response code) generator for Zig
6dependencies:
7 - src: git https://github.com/nektro/zig-extras