mirror of
https://git.code.sf.net/p/zint/code
synced 2026-01-11 22:16:02 +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:
@@ -1,7 +1,7 @@
|
||||
/* channel.c - Handles Channel */
|
||||
/*
|
||||
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
|
||||
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, }, },
|
||||
|
||||
76
backend/qr.c
76
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 <rstuart114@gmail.com>
|
||||
Copyright (C) 2009-2026 Robin Stuart <rstuart114@gmail.com>
|
||||
|
||||
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" */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
backend/qr.h
39
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 <rstuart114@gmail.com>
|
||||
Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
|
||||
Copyright (C) 2006 Kentaro Fukuchi <fukuchi@megaui.net>
|
||||
|
||||
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 */
|
||||
|
||||
Binary file not shown.
80
backend/tools/gen_qr_masks.php
Normal file
80
backend/tools/gen_qr_masks.php
Normal 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 : */
|
||||
Reference in New Issue
Block a user