diff --git a/backend/channel.c b/backend/channel.c index 06d93dee..785ab191 100644 --- a/backend/channel.c +++ b/backend/channel.c @@ -1,7 +1,7 @@ /* channel.c - Handles Channel */ /* libzint - the open source barcode library - Copyright (C) 2008-2025 Robin Stuart + Copyright (C) 2008-2026 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -87,7 +87,8 @@ static int channel_copy_precalc(channel_precalc *const precalc, int B[8], int S[ assume no liability for the use of this document." */ static void CHNCHR(int channels, int target_value, int B[8], int S[8]) { /* Use of initial pre-calculations taken from Barcode Writer in Pure PostScript (BWIPP) - * Copyright (c) 2004-2020 Terry Burton (MIT/X-Consortium license) */ + Copyright (c) 2004-2026 Terry Burton */ + /* SPDX-License-Identifier: MIT */ static channel_precalc initial_precalcs[6] = { { 0, { 1, 1, 1, 1, 1, 2, 1, 2, }, { 1, 1, 1, 1, 1, 1, 1, 3, }, { 1, 1, 1, 1, 1, 3, 2, }, { 1, 1, 1, 1, 1, 3, 3, }, }, diff --git a/backend/qr.c b/backend/qr.c index e328e6c2..dd61ce1f 100644 --- a/backend/qr.c +++ b/backend/qr.c @@ -1,7 +1,7 @@ /* qr.c Handles QR Code, Micro QR Code, UPNQR and rMQR */ /* libzint - the open source barcode library - Copyright (C) 2009-2025 Robin Stuart + Copyright (C) 2009-2026 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -1003,7 +1003,7 @@ static void qr_populate_grid(unsigned char *grid, const int h_size, const int v_ i = 0; while (i < n) { int x = x_start - (row * 2); - int r = y * h_size; + const int r = y * h_size; if (x < 6 && not_rmqr) x--; /* skip over vertical timing pattern */ @@ -1308,11 +1308,11 @@ static void qr_add_format_info(unsigned char *grid, const int size, const int ec static int qr_apply_bitmask(unsigned char *grid, const int size, const int ecc_level, const int user_mask, const int fast_encode, const int debug_print) { int x, y; - int r, k; + int k; int bit; int pattern, penalty[8]; int best_pattern; - int size_squared = size * size; + const int size_squared = size * size; unsigned char *mask = (unsigned char *) z_alloca(size_squared); unsigned char *local = (unsigned char *) z_alloca(size_squared); #ifdef ZINTLOG @@ -1322,41 +1322,13 @@ static int qr_apply_bitmask(unsigned char *grid, const int size, const int ecc_l /* Perform data masking */ memset(mask, 0, size_squared); for (y = 0; y < size; y++) { - r = y * size; + const int ymod = y % 12; + const int r = y * size; for (x = 0; x < size; x++) { - /* all eight bitmask variants are encoded in the 8 bits of the bytes that make up the mask array. */ - if (!(grid[r + x] & 0xF0)) { /* exclude areas not to be masked. */ - if (((y + x) & 1) == 0) { - mask[r + x] |= 0x01; - } - if (!fast_encode) { - if ((y & 1) == 0) { - mask[r + x] |= 0x02; - } - } - if (x % 3 == 0) { - mask[r + x] |= 0x04; - } - if (!fast_encode) { - if ((y + x) % 3 == 0) { - mask[r + x] |= 0x08; - } - } - if (((y / 2 + x / 3) & 1) == 0) { - mask[r + x] |= 0x10; - } - if (!fast_encode) { - if ((y * x) % 6 == 0) { /* Equivalent to (y * x) % 2 + (y * x) % 3 == 0 */ - mask[r + x] |= 0x20; - } - if (((((y * x) & 1) + (y * x) % 3) & 1) == 0) { - mask[r + x] |= 0x40; - } - } - if (((((y + x) & 1) + (y * x) % 3) & 1) == 0) { - mask[r + x] |= 0x80; - } + /* All eight bitmask variants are encoded in the 8 bits of the bytes that make up the mask array. */ + if (!(grid[r + x] & 0xF0)) { /* Exclude areas not to be masked. */ + mask[r + x] = qr_masks[ymod][x % 6]; /* Pre-calculated table ala BWIPP, see "qr.h" */ } } } @@ -1364,7 +1336,7 @@ static int qr_apply_bitmask(unsigned char *grid, const int size, const int ecc_l if (user_mask) { best_pattern = user_mask - 1; } else { - /* all eight bitmask variants have been encoded in the 8 bits of the bytes + /* All eight bitmask variants have been encoded in the 8 bits of the bytes * that make up the mask array. select them for evaluation according to the * desired pattern.*/ best_pattern = 0; @@ -1854,7 +1826,7 @@ INTERNAL int zint_qrcode(struct zint_symbol *symbol, struct zint_seg segs[], con symbol->rows = size; for (i = 0; i < size; i++) { - int r = i * size; + const int r = i * size; for (j = 0; j < size; j++) { if (grid[r + j] & 0x01) { z_set_module(symbol, i, j); @@ -1981,11 +1953,10 @@ static void microqr_setup_grid(unsigned char *grid, const int size) { /* Reserve space for format information */ - for (i = 0; i < 8; i++) { + for (i = 1; i <= 8; i++) { grid[(8 * size) + i] |= 0x20; grid[(i * size) + 8] |= 0x20; } - grid[(8 * size) + 8] |= 20; } static void microqr_populate_grid(unsigned char *grid, const int size, const char full_stream[], int bp) { @@ -2063,36 +2034,23 @@ static int microqr_evaluate(const unsigned char *grid, const int size, const int static int microqr_apply_bitmask(unsigned char *grid, const int size, const int user_mask, const int debug_print) { int x, y; - int r, k; + int k; int bit; int pattern, value[4]; int best_pattern; - int size_squared = size * size; + const int size_squared = size * size; unsigned char *mask = (unsigned char *) z_alloca(size_squared); unsigned char *eval = (unsigned char *) z_alloca(size_squared); /* Perform data masking */ memset(mask, 0, size_squared); for (y = 0; y < size; y++) { - r = y * size; + const int ymod = y % 12; + const int r = y * size; for (x = 0; x < size; x++) { if (!(grid[r + x] & 0xF0)) { - if ((y & 1) == 0) { - mask[r + x] |= 0x01; - } - - if (((y / 2 + x / 3) & 1) == 0) { - mask[r + x] |= 0x02; - } - - if (((((y * x) & 1) + (y * x) % 3) & 1) == 0) { - mask[r + x] |= 0x04; - } - - if (((((y + x) & 1) + (y * x) % 3) & 1) == 0) { - mask[r + x] |= 0x08; - } + mask[r + x] = microqr_masks[ymod][x % 6]; /* Pre-calculated table ala BWIPP, see "qr.h" */ } } } diff --git a/backend/qr.h b/backend/qr.h index 5e870b23..4fc3bb89 100644 --- a/backend/qr.h +++ b/backend/qr.h @@ -1,7 +1,7 @@ /* qr.h Data for QR Code, Micro QR Code and rMQR */ /* libzint - the open source barcode library - Copyright (C) 2008-2024 Robin Stuart + Copyright (C) 2008-2026 Robin Stuart Copyright (C) 2006 Kentaro Fukuchi Redistribution and use in source and binary forms, with or without @@ -335,5 +335,42 @@ static const unsigned int rmqr_format_info_right[64] = { 0x1CFB4, 0x1D091, 0x1EEDB, 0x1F1FE }; +/* Pre-calculated QR and MicroQR mask tables, generated by "backend/tools/gen_qr_masks.php", + based on lowest common periodicy of the masks (6x12): + QR: 000: 2x2, 001: 1x2, 010: 3x1, 011: 3x3, 100: 6x4, 101: 6x6, 110: 6x6, 111: 6x6 + MicroQR: 00 (001), 01 (100), 10 (110), 11 (111) + Taken from Barcode Writer in Pure PostScript (BWIPP) + Copyright (c) 2004-2026 Terry Burton */ +/* SPDX-License-Identifier: MIT */ +static const unsigned char qr_masks[12][6] = { + { 0xFF, 0x72, 0xF3, 0x6E, 0xE3, 0x62 }, + { 0x74, 0x51, 0x58, 0x85, 0x80, 0x89 }, + { 0xE7, 0x4A, 0x03, 0x76, 0xDB, 0x92 }, + { 0x6C, 0x81, 0x60, 0x9D, 0x70, 0x91 }, + { 0xF7, 0x92, 0xDB, 0x66, 0x03, 0x4A }, + { 0x74, 0x99, 0x90, 0x85, 0x48, 0x41 }, + { 0xEF, 0x62, 0xE3, 0x7E, 0xF3, 0x72 }, + { 0x64, 0x41, 0x48, 0x95, 0x90, 0x99 }, + { 0xF7, 0x5A, 0x13, 0x66, 0xCB, 0x82 }, + { 0x7C, 0x91, 0x70, 0x8D, 0x60, 0x81 }, + { 0xE7, 0x82, 0xCB, 0x76, 0x13, 0x5A }, + { 0x64, 0x89, 0x80, 0x95, 0x58, 0x51 } +}; + +static const unsigned char microqr_masks[12][6] = { + { 0x0F, 0x07, 0x0F, 0x05, 0x0D, 0x05 }, + { 0x06, 0x06, 0x06, 0x08, 0x08, 0x08 }, + { 0x0D, 0x05, 0x01, 0x07, 0x0F, 0x0B }, + { 0x04, 0x08, 0x04, 0x0A, 0x06, 0x0A }, + { 0x0F, 0x0B, 0x0F, 0x05, 0x01, 0x05 }, + { 0x06, 0x0A, 0x0A, 0x08, 0x04, 0x04 }, + { 0x0D, 0x05, 0x0D, 0x07, 0x0F, 0x07 }, + { 0x04, 0x04, 0x04, 0x0A, 0x0A, 0x0A }, + { 0x0F, 0x07, 0x03, 0x05, 0x0D, 0x09 }, + { 0x06, 0x0A, 0x06, 0x08, 0x04, 0x08 }, + { 0x0D, 0x09, 0x0D, 0x07, 0x03, 0x07 }, + { 0x04, 0x08, 0x08, 0x0A, 0x06, 0x06 } +}; + /* vim: set ts=4 sw=4 et : */ #endif /* Z_QR_H */ diff --git a/backend/tests/tools/bwipp_dump.ps.tar.xz b/backend/tests/tools/bwipp_dump.ps.tar.xz index eb406d8a..4a7b19c2 100644 Binary files a/backend/tests/tools/bwipp_dump.ps.tar.xz and b/backend/tests/tools/bwipp_dump.ps.tar.xz differ diff --git a/backend/tools/gen_qr_masks.php b/backend/tools/gen_qr_masks.php new file mode 100644 index 00000000..f0e2fb48 --- /dev/null +++ b/backend/tools/gen_qr_masks.php @@ -0,0 +1,80 @@ + +*/ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* Taken from Barcode Writer in Pure PostScript (BWIPP) + Copyright (c) 2004-2026 Terry Burton */ +/* SPDX-License-Identifier: MIT */ + +/* See https://sourceforge.net/p/zint/mailman/message/59278637/ + and https://github.com/bwipp/postscriptbarcode/commit/763fb4ffbfad7b379723dd3570183c769b28c786 + and "Pre-calculated QR and MicroQR tables" comment in "qr.h" + + Paste output after comment +*/ + +$qr_masks = array(); +$mqr_masks = array(); + +for ($y = 0; $y < 12; $y++) { + for ($x = 0; $x < 6; $x++) { + $bits = 0; + $mbits = 0; + if (($y + $x) % 2 === 0) { /* QR 000, period 2x2 */ + $bits |= 0x01; + } + if ($y % 2 === 0) { /* QR 001, MicroQR 00, period 1x2 */ + $bits |= 0x02; + $mbits |= 0x01; + } + if ($x % 3 === 0) { /* QR 010, period 3x1 */ + $bits |= 0x04; + } + if (($y + $x) % 3 === 0) { /* QR 011, period 3x3 */ + $bits |= 0x08; + } + if (((int)($y / 2) + (int)($x / 3)) % 2 === 0) { /* QR 100, MicroQR 01, period 6x4 */ + $bits |= 0x10; + $mbits |= 0x02; + } + if ((($y * $x) % 2 + ($y * $x) % 3) === 0) { /* QR 101, period 6x6 */ + $bits |= 0x20; + } + if ((($y * $x) % 2 + ($y * $x) % 3) % 2 === 0) { /* QR 110, MicroQR 10, period 6x6 */ + $bits |= 0x40; + $mbits |= 0x04; + } + if ((($y + $x) % 2 + ($y * $x) % 3) % 2 === 0) { /* QR 111, MicroQR 11, period 6x6 */ + $bits |= 0x80; + $mbits |= 0x08; + } + $qr_masks[$y][$x] = $bits; + $mqr_masks[$y][$x] = $mbits; + } +} + +printf("static const unsigned char qr_masks[12][6] = {\n"); +for ($y = 0; $y < 12; $y++) { + printf(" { "); + for ($x = 0; $x < 6; $x++) { + printf("0x%02X%s", $qr_masks[$y][$x], $x == 5 ? " " : ", "); + } + printf("}%s\n", $y == 11 ? "" : ","); +} +printf("};\n"); + +printf("\nstatic const unsigned char microqr_masks[12][6] = {\n"); +for ($y = 0; $y < 12; $y++) { + printf(" { "); + for ($x = 0; $x < 6; $x++) { + printf("0x%02X%s", $mqr_masks[$y][$x], $x == 5 ? " " : ", "); + } + printf("}%s\n", $y == 11 ? "" : ","); +} +printf("};\n"); + +/* vim: set ts=4 sw=4 et : */