1
0
mirror of https://git.code.sf.net/p/zint/code synced 2026-01-24 12:26:00 +00:00

QRCODE: implement pre-calculated QR/MICROQR masks ala BWIPP for a

slight performance gain (2-3%), see
  https://sourceforge.net/p/zint/mailman/message/59278637/
  - generated by "backend/tools/gen_qr_masks.php"
test suite: update BWIPP to latest
This commit is contained in:
gitlost
2026-01-06 11:50:06 +00:00
parent 973594a624
commit 64aa8e654c
5 changed files with 138 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
/* channel.c - Handles Channel */ /* channel.c - Handles Channel */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2008-2025 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions 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." */ assume no liability for the use of this document." */
static void CHNCHR(int channels, int target_value, int B[8], int S[8]) { 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) /* 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] = { 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, }, { 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, }, }, { 1, 1, 1, 1, 1, 3, 3, }, },

View File

@@ -1,7 +1,7 @@
/* qr.c Handles QR Code, Micro QR Code, UPNQR and rMQR */ /* qr.c Handles QR Code, Micro QR Code, UPNQR and rMQR */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2009-2025 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2009-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions 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; i = 0;
while (i < n) { while (i < n) {
int x = x_start - (row * 2); int x = x_start - (row * 2);
int r = y * h_size; const int r = y * h_size;
if (x < 6 && not_rmqr) if (x < 6 && not_rmqr)
x--; /* skip over vertical timing pattern */ 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, 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) { const int fast_encode, const int debug_print) {
int x, y; int x, y;
int r, k; int k;
int bit; int bit;
int pattern, penalty[8]; int pattern, penalty[8];
int best_pattern; 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 *mask = (unsigned char *) z_alloca(size_squared);
unsigned char *local = (unsigned char *) z_alloca(size_squared); unsigned char *local = (unsigned char *) z_alloca(size_squared);
#ifdef ZINTLOG #ifdef ZINTLOG
@@ -1322,41 +1322,13 @@ static int qr_apply_bitmask(unsigned char *grid, const int size, const int ecc_l
/* Perform data masking */ /* Perform data masking */
memset(mask, 0, size_squared); memset(mask, 0, size_squared);
for (y = 0; y < size; y++) { 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++) { 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. */ /* 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 (!(grid[r + x] & 0xF0)) { /* Exclude areas not to be masked. */
if (((y + x) & 1) == 0) { mask[r + x] = qr_masks[ymod][x % 6]; /* Pre-calculated table ala BWIPP, see "qr.h" */
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;
}
} }
} }
} }
@@ -1364,7 +1336,7 @@ static int qr_apply_bitmask(unsigned char *grid, const int size, const int ecc_l
if (user_mask) { if (user_mask) {
best_pattern = user_mask - 1; best_pattern = user_mask - 1;
} else { } 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 * that make up the mask array. select them for evaluation according to the
* desired pattern.*/ * desired pattern.*/
best_pattern = 0; best_pattern = 0;
@@ -1854,7 +1826,7 @@ INTERNAL int zint_qrcode(struct zint_symbol *symbol, struct zint_seg segs[], con
symbol->rows = size; symbol->rows = size;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
int r = i * size; const int r = i * size;
for (j = 0; j < size; j++) { for (j = 0; j < size; j++) {
if (grid[r + j] & 0x01) { if (grid[r + j] & 0x01) {
z_set_module(symbol, i, j); 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 */ /* Reserve space for format information */
for (i = 0; i < 8; i++) { for (i = 1; i <= 8; i++) {
grid[(8 * size) + i] |= 0x20; grid[(8 * size) + i] |= 0x20;
grid[(i * size) + 8] |= 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) { 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) { static int microqr_apply_bitmask(unsigned char *grid, const int size, const int user_mask, const int debug_print) {
int x, y; int x, y;
int r, k; int k;
int bit; int bit;
int pattern, value[4]; int pattern, value[4];
int best_pattern; 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 *mask = (unsigned char *) z_alloca(size_squared);
unsigned char *eval = (unsigned char *) z_alloca(size_squared); unsigned char *eval = (unsigned char *) z_alloca(size_squared);
/* Perform data masking */ /* Perform data masking */
memset(mask, 0, size_squared); memset(mask, 0, size_squared);
for (y = 0; y < size; y++) { 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++) { for (x = 0; x < size; x++) {
if (!(grid[r + x] & 0xF0)) { if (!(grid[r + x] & 0xF0)) {
if ((y & 1) == 0) { mask[r + x] = microqr_masks[ymod][x % 6]; /* Pre-calculated table ala BWIPP, see "qr.h" */
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;
}
} }
} }
} }

View File

@@ -1,7 +1,7 @@
/* qr.h Data for QR Code, Micro QR Code and rMQR */ /* qr.h Data for QR Code, Micro QR Code and rMQR */
/* /*
libzint - the open source barcode library libzint - the open source barcode library
Copyright (C) 2008-2024 Robin Stuart <rstuart114@gmail.com> Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2006 Kentaro Fukuchi <fukuchi@megaui.net> Copyright (C) 2006 Kentaro Fukuchi <fukuchi@megaui.net>
Redistribution and use in source and binary forms, with or without 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 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 : */ /* vim: set ts=4 sw=4 et : */
#endif /* Z_QR_H */ #endif /* Z_QR_H */

View File

@@ -0,0 +1,80 @@
<?php
/* Generate pre-calculated `qr_masks[]` for "qr.h" */
/*
libzint - the open source barcode library
Copyright (C) 2026 Robin Stuart <rstuart114@gmail.com>
*/
/* 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 : */