// QR encoder. See include/gfx/qr.h for why it exists and why it is this small. // // Freestanding on purpose: no allocation, no floating point, no libc beyond // memset/memmove. It has to run in panic context, where the heap may be exactly // what is broken. Every buffer here is either on the caller's struct or a // bounded local. // // The structure follows ISO/IEC 18004 in the order the standard describes it, // which is also the only order in which any of it can be checked: // // 1. encode the payload into data codewords // 2. append Reed-Solomon error correction, per block // 3. interleave the blocks // 4. draw the function patterns (finders, timing, alignment, format area) // 5. lay the codeword bits into the remaining modules in a zigzag // 6. try all 8 masks, score each, keep the best // // Verified by scanning the output with a phone, which is the only test that // matters and is also literally the feature. #ifdef QR_HOST #include #else #include "core/types.h" #include "core/defs.h" #endif #include "gfx/qr.h" // --------------------------------------------------------------------------- // Version table // --------------------------------------------------------------------------- // // Per version, at error correction level L: total codewords, error correction // codewords per block, and number of blocks. These are from the standard's // tables and cannot be derived, so they are the one part of this file that has // to be right by transcription rather than by reasoning. // // A useful check on them: total - ecc*blocks must be divisible by blocks for // every row below, because levels 1..6 at L happen to have equally sized // blocks. qr_encode() asserts exactly that at runtime, which is what turns a // mistyped number here into a visible failure instead of an unscannable symbol. static const struct { short total; // total codewords in the symbol unsigned char ecc; // EC codewords per block unsigned char blocks; // number of RS blocks } vinfo[QR_MAX_VERSION + 1] = { { 0, 0, 0 }, // version 0 does not exist; keeps indexing natural { 26, 7, 1 }, { 44, 10, 1 }, { 70, 15, 1 }, { 100, 20, 1 }, { 134, 26, 1 }, { 172, 18, 2 }, }; int qr_capacity(int version) { if (version < 1 || version > QR_MAX_VERSION) return 0; // Data codewords, less the two bytes of framing: a 4-bit mode indicator, an // 8-bit character count (versions 1-9), and a 4-bit terminator come to // exactly two codewords' worth of overhead. int data = vinfo[version].total - vinfo[version].ecc * vinfo[version].blocks; return data - 2; } // --------------------------------------------------------------------------- // GF(256) and Reed-Solomon // --------------------------------------------------------------------------- // Multiply in GF(256) modulo x^8 + x^4 + x^3 + x^2 + 1 (0x11D). // // Done by shift-and-add rather than log/exp tables. Two 256-byte tables plus // their initialisation is more code than this loop, and this needs no init step // at all -- which matters when the caller is a panic handler that cannot assume // any particular earlier function ran. static unsigned char gmul(unsigned char x, unsigned char y) { unsigned char z = 0; for (int i = 7; i >= 0; i--) { z = (unsigned char)((z << 1) ^ ((z >> 7) * 0x1D)); z ^= (unsigned char)(((y >> i) & 1) * x); } return z; } // Coefficients of the degree-`degree` generator polynomial, highest power // first, with the leading 1 left implicit. static void rs_divisor(int degree, unsigned char *result) { memset(result, 0, degree); result[degree - 1] = 1; // start at the monomial x^0 // Multiply by (x - r) for r = 1, a, a^2, ... Each pass shifts the polynomial // up one degree and folds in the new root. unsigned char root = 1; for (int i = 0; i < degree; i++) { for (int j = 0; j < degree; j++) { result[j] = gmul(result[j], root); if (j + 1 < degree) result[j] ^= result[j + 1]; } root = gmul(root, 0x02); } } // The remainder of data[] divided by the generator: the EC codewords. static void rs_remainder(const unsigned char *data, int len, const unsigned char *gen, int degree, unsigned char *result) { memset(result, 0, degree); for (int i = 0; i < len; i++) { unsigned char factor = data[i] ^ result[0]; memmove(&result[0], &result[1], degree - 1); result[degree - 1] = 0; for (int j = 0; j < degree; j++) result[j] ^= gmul(gen[j], factor); } } // --------------------------------------------------------------------------- // Module access // --------------------------------------------------------------------------- static void set_fn(struct qr *q, int x, int y, int dark) { if (x < 0 || y < 0 || x >= q->size || y >= q->size) return; q->mod[y][x] = (unsigned char)(dark != 0); q->fn[y][x] = 1; } static int absi(int v) { return v < 0 ? -v : v; } static int cheb(int dx, int dy) { int ax = absi(dx), ay = absi(dy); return ax > ay ? ax : ay; } // --------------------------------------------------------------------------- // Function patterns // --------------------------------------------------------------------------- // A finder plus its separator, as one 9x9 stamp centred on (cx, cy). // // Writing it by Chebyshev distance rather than as a literal bitmap is not // cleverness for its own sake: the concentric rings *are* the definition -- dark // 3x3 core, light ring, dark ring, light separator -- and the edge stamps run // off the symbol, which set_fn() clips. static void draw_finder(struct qr *q, int cx, int cy) { for (int dy = -4; dy <= 4; dy++) for (int dx = -4; dx <= 4; dx++) { int d = cheb(dx, dy); set_fn(q, cx + dx, cy + dy, d != 2 && d != 4); } } // The 5x5 alignment pattern: dark 3x3 outline with a light ring inside. static void draw_alignment(struct qr *q, int cx, int cy) { for (int dy = -2; dy <= 2; dy++) for (int dx = -2; dx <= 2; dx++) set_fn(q, cx + dx, cy + dy, cheb(dx, dy) != 1); } // The 15-bit format information, twice, plus the always-dark module. // // Called once with the real mask at the end, and once early with mask 0 purely // to *reserve* these modules -- the zigzag below skips anything marked as a // function pattern, so the reservation has to exist before data is placed. static void draw_format(struct qr *q, int mask) { // 0b01 is error correction level L. Five data bits, then a BCH(15,5) check // computed with generator 0x537, then XOR 0x5412 so an all-zero format never // produces an all-zero pattern. int data = (1 << 3) | mask; int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); int bits = ((data << 10) | rem) ^ 0x5412; int n = q->size; // First copy: down the left of the top-right finder, then left along the top // of the bottom-left one. The 6/7 and 7/8 jogs skip the timing pattern. for (int i = 0; i <= 5; i++) set_fn(q, 8, i, (bits >> i) & 1); set_fn(q, 8, 7, (bits >> 6) & 1); set_fn(q, 8, 8, (bits >> 7) & 1); set_fn(q, 7, 8, (bits >> 8) & 1); for (int i = 9; i < 15; i++) set_fn(q, 14 - i, 8, (bits >> i) & 1); // Second copy, so the format survives damage to either corner. for (int i = 0; i < 8; i++) set_fn(q, n - 1 - i, 8, (bits >> i) & 1); for (int i = 8; i < 15; i++) set_fn(q, 8, n - 15 + i, (bits >> i) & 1); set_fn(q, 8, n - 8, 1); // the dark module, always set, always here } static void draw_function_patterns(struct qr *q) { int n = q->size; // Timing patterns first: the finders overwrite their ends, which is correct. for (int i = 0; i < n; i++) { set_fn(q, 6, i, i % 2 == 0); set_fn(q, i, 6, i % 2 == 0); } draw_finder(q, 3, 3); draw_finder(q, n - 4, 3); draw_finder(q, 3, n - 4); // For versions 2-6 the alignment centres are {6, n-7}, and three of those // four combinations collide with a finder. Only the bottom-right survives, // which is why there is no position table here. Version 7 would need one. if (q->size > 21) draw_alignment(q, n - 7, n - 7); draw_format(q, 0); // reserve; overwritten once a mask is chosen } // --------------------------------------------------------------------------- // Masking // --------------------------------------------------------------------------- static int mask_bit(int mask, int x, int y) { switch (mask) { case 0: return (x + y) % 2 == 0; case 1: return y % 2 == 0; case 2: return x % 3 == 0; case 3: return (x + y) % 3 == 0; case 4: return (x / 3 + y / 2) % 2 == 0; case 5: return x * y % 2 + x * y % 3 == 0; case 6: return (x * y % 2 + x * y % 3) % 2 == 0; case 7: return ((x + y) % 2 + x * y % 3) % 2 == 0; } return 0; } static void apply_mask(struct qr *q, int mask) { for (int y = 0; y < q->size; y++) for (int x = 0; x < q->size; x++) if (!q->fn[y][x]) q->mod[y][x] ^= (unsigned char)mask_bit(mask, x, y); } // The standard's four penalty rules. Lower is better. // // A decoder will read any legal mask, so this is not correctness -- it is scan // reliability, which is the whole point when the reader is a handheld camera // pointed at a screen. Rule 3 in particular exists to stop the data accidentally // containing something that looks like a finder pattern. static int penalty(const struct qr *q) { int n = q->size, score = 0, dark = 0; for (int y = 0; y < n; y++) { for (int x = 0; x < n; x++) dark += q->mod[y][x]; // Rule 1, both directions: runs of five or more identical modules. for (int dir = 0; dir < 2; dir++) { int run = 1; int prev = dir ? q->mod[0][y] : q->mod[y][0]; for (int i = 1; i < n; i++) { int cur = dir ? q->mod[i][y] : q->mod[y][i]; if (cur == prev) { run++; } else { if (run >= 5) score += 3 + (run - 5); run = 1; prev = cur; } } if (run >= 5) score += 3 + (run - 5); } } // Rule 2: every 2x2 block of one colour. for (int y = 0; y + 1 < n; y++) for (int x = 0; x + 1 < n; x++) { int c = q->mod[y][x]; if (c == q->mod[y][x + 1] && c == q->mod[y + 1][x] && c == q->mod[y + 1][x + 1]) score += 3; } // Rule 3: the finder-lookalike 1011101 with four light modules on either // side, in either direction. for (int dir = 0; dir < 2; dir++) for (int a = 0; a < n; a++) { unsigned bits = 0; for (int b = 0; b < n; b++) { int m = dir ? q->mod[b][a] : q->mod[a][b]; bits = ((bits << 1) | (unsigned)m) & 0x7FF; if (b >= 10 && (bits == 0x05D || bits == 0x5D0)) score += 40; } } // Rule 4: deviation from an even split of dark and light, in 5% steps. int total = n * n; int k = (absi(dark * 20 - total * 10) + total - 1) / total; score += k * 10; return score; } // --------------------------------------------------------------------------- // Encode // --------------------------------------------------------------------------- static void put_bits(unsigned char *buf, int *pos, unsigned val, int n) { for (int i = n - 1; i >= 0; i--) { buf[*pos >> 3] |= (unsigned char)(((val >> i) & 1) << (7 - (*pos & 7))); (*pos)++; } } int qr_encode(struct qr *q, const unsigned char *data, int len) { int v; if (len < 0) return -1; for (v = 1; v <= QR_MAX_VERSION; v++) if (len <= qr_capacity(v)) break; if (v > QR_MAX_VERSION) return -1; int total = vinfo[v].total; int ecclen = vinfo[v].ecc; int blocks = vinfo[v].blocks; int datacw = total - ecclen * blocks; // The table claims equal-sized blocks for every version this file supports. // If that is ever untrue the interleave below is silently wrong, so check it // rather than trust it. if (datacw % blocks != 0) return -1; int blocklen = datacw / blocks; // --- 1. payload -> data codewords --------------------------------------- unsigned char cw[172]; // >= max total codewords memset(cw, 0, sizeof(cw)); int pos = 0; put_bits(cw, &pos, 0x4, 4); // byte mode put_bits(cw, &pos, (unsigned)len, 8); for (int i = 0; i < len; i++) put_bits(cw, &pos, data[i], 8); // Terminator, up to four zero bits, then round up to a whole codeword. int spare = datacw * 8 - pos; put_bits(cw, &pos, 0, spare < 4 ? spare : 4); if (pos & 7) put_bits(cw, &pos, 0, 8 - (pos & 7)); // Fill the rest with the standard alternating pad bytes. They are specified // values, not arbitrary filler: a decoder uses the character count to know // where the data ends, but the pad pattern keeps the symbol from having large // uniform regions that mask selection would then have to fight. for (int i = pos / 8; i < datacw; i++) cw[i] = (i - pos / 8) % 2 == 0 ? 0xEC : 0x11; // --- 2 and 3. error correction, then interleave ------------------------- unsigned char gen[30]; unsigned char ec[4][30]; rs_divisor(ecclen, gen); for (int b = 0; b < blocks; b++) rs_remainder(&cw[b * blocklen], blocklen, gen, ecclen, ec[b]); unsigned char out[172]; int o = 0; for (int i = 0; i < blocklen; i++) for (int b = 0; b < blocks; b++) out[o++] = cw[b * blocklen + i]; for (int i = 0; i < ecclen; i++) for (int b = 0; b < blocks; b++) out[o++] = ec[b][i]; // --- 4. function patterns ------------------------------------------------ memset(q, 0, sizeof(*q)); q->size = 17 + 4 * v; draw_function_patterns(q); // --- 5. zigzag placement ------------------------------------------------- // // Two-module-wide columns walked from the right edge leftwards, alternating // upward and downward. Column 6 is skipped entirely because it is the vertical // timing pattern -- without that step every subsequent column lands one place // out and the symbol is quietly unreadable. int bit = 0; int nbits = o * 8; for (int right = q->size - 1; right >= 1; right -= 2) { if (right == 6) right = 5; for (int vert = 0; vert < q->size; vert++) { for (int j = 0; j < 2; j++) { int x = right - j; int upward = ((right + 1) & 2) == 0; int y = upward ? q->size - 1 - vert : vert; if (!q->fn[y][x] && bit < nbits) { q->mod[y][x] = (unsigned char)((out[bit >> 3] >> (7 - (bit & 7))) & 1); bit++; } } } } // --- 6. pick a mask ------------------------------------------------------ int best = 0, bestscore = 0; for (int m = 0; m < 8; m++) { apply_mask(q, m); draw_format(q, m); int s = penalty(q); if (m == 0 || s < bestscore) { bestscore = s; best = m; } apply_mask(q, m); // XOR again to undo; masking is involutive } apply_mask(q, best); draw_format(q, best); return 0; }