mirror of
https://github.com/zint/zint
synced 2024-11-16 20:57:25 +13:00
Improved data compression algorithm for QR Code
This commit is contained in:
parent
001a0a88d9
commit
653105b81d
325
backend/qr.c
325
backend/qr.c
@ -761,7 +761,7 @@ int write_log(char log[])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int evaluate(unsigned char *grid, int size, int pattern)
|
int evaluate(unsigned char *eval, int size, int pattern)
|
||||||
{
|
{
|
||||||
int x, y, block, weight;
|
int x, y, block, weight;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -791,15 +791,10 @@ int evaluate(unsigned char *grid, int size, int pattern)
|
|||||||
// all eight bitmask variants have been encoded in the 8 bits of the bytes that make up the grid array. select them for evaluation according to the desired pattern.
|
// all eight bitmask variants have been encoded in the 8 bits of the bytes that make up the grid array. select them for evaluation according to the desired pattern.
|
||||||
for(x = 0; x < size; x++) {
|
for(x = 0; x < size; x++) {
|
||||||
for(y = 0; y < size; y++) {
|
for(y = 0; y < size; y++) {
|
||||||
switch(pattern) {
|
if ((eval[(y * size) + x] & (0x01 << pattern)) != 0) {
|
||||||
case 0: if (grid[(y * size) + x] & 0x01) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
local[(y * size) + x] = '1';
|
||||||
case 1: if (grid[(y * size) + x] & 0x02) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
} else {
|
||||||
case 2: if (grid[(y * size) + x] & 0x04) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
local[(y * size) + x] = '0';
|
||||||
case 3: if (grid[(y * size) + x] & 0x08) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
|
||||||
case 4: if (grid[(y * size) + x] & 0x10) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
|
||||||
case 5: if (grid[(y * size) + x] & 0x20) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
|
||||||
case 6: if (grid[(y * size) + x] & 0x40) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
|
||||||
case 7: if (grid[(y * size) + x] & 0x80) { local[(y * size) + x] = '1'; } else { local[(y * size) + x] = '0'; } break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -819,7 +814,7 @@ int evaluate(unsigned char *grid, int size, int pattern)
|
|||||||
|
|
||||||
/* Test 1: Adjacent modules in row/column in same colour */
|
/* Test 1: Adjacent modules in row/column in same colour */
|
||||||
/* Vertical */
|
/* Vertical */
|
||||||
for(x = 0; x < size; x++) {
|
/*for(x = 0; x < size; x++) {
|
||||||
state = local[x];
|
state = local[x];
|
||||||
block = 0;
|
block = 0;
|
||||||
for(y = 0; y < size; y++) {
|
for(y = 0; y < size; y++) {
|
||||||
@ -834,10 +829,10 @@ int evaluate(unsigned char *grid, int size, int pattern)
|
|||||||
block=0;
|
block=0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* Horizontal */
|
/* Horizontal */
|
||||||
for(y = 0; y < size; y++) {
|
/*for(y = 0; y < size; y++) {
|
||||||
state = local[y * size];
|
state = local[y * size];
|
||||||
block = 0;
|
block = 0;
|
||||||
for(x = 0; x < size; x++) {
|
for(x = 0; x < size; x++) {
|
||||||
@ -852,6 +847,47 @@ int evaluate(unsigned char *grid, int size, int pattern)
|
|||||||
block=0;
|
block=0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/* Test 1: Adjacent modules in row/column in same colour */
|
||||||
|
/* Vertical */
|
||||||
|
for (x = 0; x < size; x++) {
|
||||||
|
state = local[x];
|
||||||
|
block = 0;
|
||||||
|
for (y = 0; y < size; y++) {
|
||||||
|
if (local[(y * size) + x] == state) {
|
||||||
|
block++;
|
||||||
|
} else {
|
||||||
|
if (block > 5) {
|
||||||
|
result += (3 + (block - 5));
|
||||||
|
}
|
||||||
|
block = 0;
|
||||||
|
state = local[(y * size) + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block > 5) {
|
||||||
|
result += (3 + (block - 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Horizontal */
|
||||||
|
for (y = 0; y < size; y++) {
|
||||||
|
state = local[y * size];
|
||||||
|
block = 0;
|
||||||
|
for (x = 0; x < size; x++) {
|
||||||
|
if (local[(y * size) + x] == state) {
|
||||||
|
block++;
|
||||||
|
} else {
|
||||||
|
if (block > 5) {
|
||||||
|
result += (3 + (block - 5));
|
||||||
|
}
|
||||||
|
block = 0;
|
||||||
|
state = local[(y * size) + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block > 5) {
|
||||||
|
result += (3 + (block - 5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ZINTLOG
|
#ifdef ZINTLOG
|
||||||
@ -1197,11 +1233,216 @@ void add_version_info(unsigned char *grid, int size, int version)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tribus(int version, int a, int b, int c) {
|
||||||
|
/* Choose from three numbers based on version */
|
||||||
|
int RetVal;
|
||||||
|
|
||||||
|
RetVal = c;
|
||||||
|
|
||||||
|
if (version < 10) {
|
||||||
|
RetVal = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((version >= 10) && (version <= 26)) {
|
||||||
|
RetVal = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RetVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyOptimisation(int version, char inputMode[], int inputLength) {
|
||||||
|
/* Implements a custom optimisation algorithm, more efficient than that
|
||||||
|
given in Annex J.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int blockCount = 0;
|
||||||
|
int i, j;
|
||||||
|
char currentMode = ' '; // Null
|
||||||
|
|
||||||
|
for (i = 0; i < inputLength; i++) {
|
||||||
|
if (inputMode[i] != currentMode) {
|
||||||
|
currentMode = inputMode[i];
|
||||||
|
blockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockLength[blockCount];
|
||||||
|
char blockMode[blockCount];
|
||||||
|
|
||||||
|
j = -1;
|
||||||
|
currentMode = ' '; // Null
|
||||||
|
for (i = 0; i < inputLength; i++) {
|
||||||
|
if (inputMode[i] != currentMode) {
|
||||||
|
j++;
|
||||||
|
blockLength[j] = 1;
|
||||||
|
blockMode[j] = inputMode[i];
|
||||||
|
currentMode = inputMode[i];
|
||||||
|
} else {
|
||||||
|
blockLength[j]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockCount > 1) {
|
||||||
|
// Search forward
|
||||||
|
for (i = 0; i <= (blockCount - 2); i++) {
|
||||||
|
if (blockMode[i] == 'B') {
|
||||||
|
switch (blockMode[i + 1]) {
|
||||||
|
case 'K':
|
||||||
|
if (blockLength[i + 1] < tribus(version, 4, 5, 6)) {
|
||||||
|
blockMode[i + 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
if (blockLength[i + 1] < tribus(version, 7, 8, 9)) {
|
||||||
|
blockMode[i + 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (blockLength[i + 1] < tribus(version, 3, 4, 5)) {
|
||||||
|
blockMode[i + 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((blockMode[i] == 'A')
|
||||||
|
&& (blockMode[i + 1] == 'N')) {
|
||||||
|
if (blockLength[i + 1] < tribus(version, 6, 8, 10)) {
|
||||||
|
blockMode[i + 1] = 'A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search backward
|
||||||
|
for (i = blockCount - 1; i > 0; i--) {
|
||||||
|
if (blockMode[i] == 'B') {
|
||||||
|
switch (blockMode[i - 1]) {
|
||||||
|
case 'K':
|
||||||
|
if (blockLength[i - 1] < tribus(version, 4, 5, 6)) {
|
||||||
|
blockMode[i - 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
if (blockLength[i - 1] < tribus(version, 7, 8, 9)) {
|
||||||
|
blockMode[i - 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (blockLength[i - 1] < tribus(version, 3, 4, 5)) {
|
||||||
|
blockMode[i - 1] = 'B';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((blockMode[i] == 'A')
|
||||||
|
&& (blockMode[i - 1] == 'N')) {
|
||||||
|
if (blockLength[i - 1] < tribus(version, 6, 8, 10)) {
|
||||||
|
blockMode[i - 1] = 'A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
for (int block = 0; block < blockCount; block++) {
|
||||||
|
currentMode = blockMode[block];
|
||||||
|
for (i = 0; i < blockLength[block]; i++) {
|
||||||
|
inputMode[j] = currentMode;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockLength(int start, char inputMode[], int inputLength) {
|
||||||
|
/* Find the length of the block starting from 'start' */
|
||||||
|
int i, count;
|
||||||
|
char mode = inputMode[start];
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
i = start;
|
||||||
|
|
||||||
|
do {
|
||||||
|
count++;
|
||||||
|
} while (((i + count) < inputLength) && (inputMode[i + count] == mode));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBinaryLength(int version, char inputMode[], int inputData[], int inputLength, int gs1) {
|
||||||
|
/* Calculate the actual bitlength of the proposed binary string */
|
||||||
|
char currentMode;
|
||||||
|
int i, j;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
applyOptimisation(version, inputMode, inputLength);
|
||||||
|
|
||||||
|
currentMode = ' '; // Null
|
||||||
|
|
||||||
|
if (gs1 == 1) {
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < inputLength; i++) {
|
||||||
|
if (inputMode[i] != currentMode) {
|
||||||
|
count += 4;
|
||||||
|
switch (inputMode[i]) {
|
||||||
|
case 'K':
|
||||||
|
count += tribus(version, 8, 10, 12);
|
||||||
|
count += (blockLength(i, inputMode, inputLength) * 13);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
count += tribus(version, 8, 16, 16);
|
||||||
|
for (j = i; j < (i + blockLength(i, inputMode, inputLength)); j++) {
|
||||||
|
if (inputData[j] > 0xff) {
|
||||||
|
count += 16;
|
||||||
|
} else {
|
||||||
|
count += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
count += tribus(version, 9, 11, 13);
|
||||||
|
switch (blockLength(i, inputMode, inputLength) % 2) {
|
||||||
|
case 0:
|
||||||
|
count += (blockLength(i, inputMode, inputLength) / 2) * 11;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
count += ((blockLength(i, inputMode, inputLength) - 1) / 2) * 11;
|
||||||
|
count += 6;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
count += tribus(version, 10, 12, 14);
|
||||||
|
switch (blockLength(i, inputMode, inputLength) % 3) {
|
||||||
|
case 0:
|
||||||
|
count += (blockLength(i, inputMode, inputLength) / 3) * 10;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
count += ((blockLength(i, inputMode, inputLength) - 1) / 3) * 10;
|
||||||
|
count += 4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
count += ((blockLength(i, inputMode, inputLength) - 2) / 3) * 10;
|
||||||
|
count += 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentMode = inputMode[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
int qr_code(struct zint_symbol *symbol, unsigned char source[], int length)
|
int qr_code(struct zint_symbol *symbol, unsigned char source[], int length)
|
||||||
{
|
{
|
||||||
int error_number, i, j, glyph, est_binlen;
|
int error_number, i, j, glyph, est_binlen;
|
||||||
int ecc_level, autosize, version, max_cw, target_binlen, blocks, size;
|
int ecc_level, autosize, version, max_cw, target_binlen, blocks, size;
|
||||||
int bitmask, gs1;
|
int bitmask, gs1;
|
||||||
|
int canShrink;
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
int utfdata[length + 1];
|
int utfdata[length + 1];
|
||||||
@ -1296,14 +1537,64 @@ int qr_code(struct zint_symbol *symbol, unsigned char source[], int length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((symbol->option_2 >= 1) && (symbol->option_2 <= 40)) {
|
// Now see if the optimised binary will fit in a smaller symbol.
|
||||||
if (symbol->option_2 > autosize) {
|
canShrink = 1;
|
||||||
version = symbol->option_2;
|
|
||||||
|
do {
|
||||||
|
if (autosize == 1) {
|
||||||
|
canShrink = 0;
|
||||||
} else {
|
} else {
|
||||||
version = autosize;
|
if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) {
|
||||||
|
// Length of binary needed to encode the data in the smaller symbol is different, recalculate
|
||||||
|
est_binlen = getBinaryLength(autosize - 1, mode, jisdata, length, gs1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (ecc_level) {
|
||||||
|
case LEVEL_L:
|
||||||
|
if ((8 * qr_data_codewords_L[autosize - 2]) < est_binlen) {
|
||||||
|
canShrink = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEVEL_M:
|
||||||
|
if ((8 * qr_data_codewords_M[autosize - 2]) < est_binlen) {
|
||||||
|
canShrink = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEVEL_Q:
|
||||||
|
if ((8 * qr_data_codewords_Q[autosize - 2]) < est_binlen) {
|
||||||
|
canShrink = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEVEL_H:
|
||||||
|
if ((8 * qr_data_codewords_H[autosize - 2]) < est_binlen) {
|
||||||
|
canShrink = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canShrink == 1) {
|
||||||
|
// Optimisation worked - data will fit in a smaller symbol
|
||||||
|
autosize--;
|
||||||
} else {
|
} else {
|
||||||
|
// Data did not fit in the smaller symbol, revert to original size
|
||||||
|
if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) {
|
||||||
|
est_binlen = getBinaryLength(autosize, mode, jisdata, length, gs1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (canShrink == 1);
|
||||||
|
|
||||||
version = autosize;
|
version = autosize;
|
||||||
|
|
||||||
|
if ((symbol->option_2 >= 1) && (symbol->option_2 <= 40)) {
|
||||||
|
/* If the user has selected a larger symbol than the smallest available,
|
||||||
|
then use the size the user has selected, and re-optimise for this
|
||||||
|
symbol size.
|
||||||
|
*/
|
||||||
|
if (symbol->option_2 > version) {
|
||||||
|
version = symbol->option_2;
|
||||||
|
est_binlen = getBinaryLength(symbol->option_2, mode, jisdata, length, gs1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure maxium error correction capacity */
|
/* Ensure maxium error correction capacity */
|
||||||
|
Loading…
Reference in New Issue
Block a user