Performance improvements for linear encoding and raster output

- use fixed-length string tables (mostly) instead of (char *) pointer ones
   (saves ~40K)
 - re-use C128Table for CODABLOCKF and CODE16K
   (required removal of Stop character and extra CODE16K-only entry)
 - use pointer to destination and copy (memcpy/strcpy(), bin_append_posn())
   instead of concatenating (strcat()) (mostly)
 - replace last remaining bin_append()s with bin_append_posn();
   bin_append() removed
 - add length arg to toupper() and expand() (avoids strlen())
 - change is_sane() to use table-based flags (avoids an iteration)
 - rename lookup() to is_sane_lookup() and change to check and return posns
   and use in pointer to destination loops (avoids strcat()s)
 - remove special case PHARMA in expand() (dealt with in pharma())
 - make #define SILVER/CALCIUM/TECHNETIUM/KRSET etc static strings
 - replace strchr() -> posn()
 - CODE128: populate destination once in checksum loop; re-use and export
   some more routines (c128_set_a/b/c(), c128_put_in_set()) for sharing;
   prefix defines (SHIFTA -> C128_SHIFTA etc) and existing exported routines
 - use factor XOR toggle trick in checksum calcs (avoids branch)
 - raster.c: fill out single 1-pixel row and copy using new draw_bar_line(),
   copy_bar_line() routines; similarly in buffer_plot compare previous line &
   copy if same (same technique as used to improve non-half-integer scaling,
   significant performance increase, (c) codemonkey82);
   also done for PNG (BMP/GIF/PCX/TIFF not done)
 - raster/vector/output.c: shorten "output_" prefix -> "out_";
   sync vector to other raster changes to try to keep source files similar
 - 2of5.c: prefix "c25_"
JAPANPOST: return error if input data truncated (backward incompatible)
DAFT: max chars 50 -> 100
common.c: istwodigit() -> is_twodigit()
common.c/emf.c/output.c: use some further stripf()s (MSVC6 float variations)
library.c: new check_output_args() helper
zint.h: add BARCODE_LAST marker and use in library.c
QRCODE: remove a NOLINT (requires clang-tidy-13), one remaining
CMake: separate no-optimize from ZINT_DEBUG into new ZINT_NOOPT option
This commit is contained in:
gitlost 2021-10-20 23:05:30 +01:00
parent e8b59aa696
commit fab7435fac
72 changed files with 3501 additions and 2380 deletions

View file

@ -36,15 +36,18 @@
INTERNAL int code39(struct zint_symbol *symbol, unsigned char source[], int length);
static const char CALCIUM[] = "0123456789-$:/.+ABCD";
#define CALCIUM_INNER_F (IS_NUM_F | IS_MNS_F | IS_CLI_F | IS_PLS_F) /* CALCIUM_INNER "0123456789-$:/.+" */
/* Codabar table checked against EN 798:1995 */
#define CALCIUM "0123456789-$:/.+ABCD"
#define CALCIUM_INNER "0123456789-$:/.+"
static const char *CodaTable[20] = {
"11111221", "11112211", "11121121", "22111111", "11211211", "21111211",
"12111121", "12112111", "12211111", "21121111", "11122111", "11221111", "21112121", "21211121",
"21212111", "11212121", "11221211", "12121121", "11121221", "11122211"
static const char CodaTable[20][8] = {
{'1','1','1','1','1','2','2','1'}, {'1','1','1','1','2','2','1','1'}, {'1','1','1','2','1','1','2','1'},
{'2','2','1','1','1','1','1','1'}, {'1','1','2','1','1','2','1','1'}, {'2','1','1','1','1','2','1','1'},
{'1','2','1','1','1','1','2','1'}, {'1','2','1','1','2','1','1','1'}, {'1','2','2','1','1','1','1','1'},
{'2','1','1','2','1','1','1','1'}, {'1','1','1','2','2','1','1','1'}, {'1','1','2','2','1','1','1','1'},
{'2','1','1','1','2','1','2','1'}, {'2','1','2','1','1','1','2','1'}, {'2','1','2','1','2','1','1','1'},
{'1','1','2','1','2','1','2','1'}, {'1','1','2','2','1','2','1','1'}, {'1','2','1','2','1','1','2','1'},
{'1','1','1','2','1','2','2','1'}, {'1','1','1','2','2','2','1','1'}
};
INTERNAL int pharma(struct zint_symbol *symbol, unsigned char source[], int length) {
@ -61,22 +64,23 @@ INTERNAL int pharma(struct zint_symbol *symbol, unsigned char source[], int leng
the specification at http://www.laetus.com/laetus.php?request=file&id=69
(http://www.gomaro.ch/ftproot/Laetus_PHARMA-CODE.pdf) */
unsigned long int tester;
int tester;
int counter, error_number = 0, h;
char inter[18] = {0}; /* 131070 -> 17 bits */
char *in = inter;
char dest[64]; /* 17 * 2 + 1 */
char *d = dest;
if (length > 6) {
strcpy(symbol->errtxt, "350: Input too long (6 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
if (is_sane(NEON, source, length) != 0) {
tester = to_int(source, length);
if (tester == -1) {
strcpy(symbol->errtxt, "351: Invalid character in data (digits only)");
return ZINT_ERROR_INVALID_DATA;
}
tester = atoi((char *) source);
if ((tester < 3) || (tester > 131070)) {
strcpy(symbol->errtxt, "352: Data out of range (3 to 131070)");
return ZINT_ERROR_INVALID_DATA;
@ -84,25 +88,22 @@ INTERNAL int pharma(struct zint_symbol *symbol, unsigned char source[], int leng
do {
if (!(tester & 1)) {
strcat(inter, "W");
*in++ = 'W';
tester = (tester - 2) / 2;
} else {
strcat(inter, "N");
*in++ = 'N';
tester = (tester - 1) / 2;
}
} while (tester != 0);
h = (int) strlen(inter) - 1;
*dest = '\0';
for (counter = h; counter >= 0; counter--) {
if (inter[counter] == 'W') {
strcat(dest, "32");
} else {
strcat(dest, "12");
}
h = in - inter;
for (counter = h - 1; counter >= 0; counter--) {
*d++ = inter[counter] == 'W' ? '3' : '1';
*d++ = '2';
}
*--d = '\0'; /* Chop off final bar */
expand(symbol, dest);
expand(symbol, dest, d - dest);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* Laetus Pharmacode Guide 1.2 Standard one-track height 8mm / 0.5mm (X) */
@ -114,75 +115,66 @@ INTERNAL int pharma(struct zint_symbol *symbol, unsigned char source[], int leng
return error_number;
}
static int pharma_two_calc(struct zint_symbol *symbol, unsigned char source[], char dest[]) {
static int pharma_two_calc(int tester, char *d) {
/* This code uses the Two Track Pharamacode defined in the document at
http://www.laetus.com/laetus.php?request=file&id=69 and using a modified
algorithm from the One Track system. This standard accepts integet values
from 4 to 64570080. */
unsigned long int tester;
int counter, h;
char inter[17];
int error_number;
char *in = inter;
tester = atoi((char *) source);
if ((tester < 4) || (tester > 64570080)) {
strcpy(symbol->errtxt, "353: Data out of range (4 to 64570080)");
return ZINT_ERROR_INVALID_DATA;
}
error_number = 0;
strcpy(inter, "");
do {
switch (tester % 3) {
case 0:
strcat(inter, "3");
*in++ = '3';
tester = (tester - 3) / 3;
break;
case 1:
strcat(inter, "1");
*in++ = '1';
tester = (tester - 1) / 3;
break;
case 2:
strcat(inter, "2");
*in++ = '2';
tester = (tester - 2) / 3;
break;
}
} while (tester != 0);
h = (int) strlen(inter) - 1;
for (counter = h; counter >= 0; counter--) {
dest[h - counter] = inter[counter];
h = in - inter;
for (counter = h - 1; counter >= 0; counter--) {
*d++ = inter[counter];
}
dest[h + 1] = '\0';
*d = '\0';
return error_number;
return h;
}
INTERNAL int pharma_two(struct zint_symbol *symbol, unsigned char source[], int length) {
/* Draws the patterns for two track pharmacode */
int tester;
char height_pattern[200];
unsigned int loopey, h;
int writer;
int error_number;
strcpy(height_pattern, "");
int error_number = 0;
if (length > 8) {
strcpy(symbol->errtxt, "354: Input too long (8 character maximum");
return ZINT_ERROR_TOO_LONG;
}
if (is_sane(NEON, source, length) != 0) {
tester = to_int(source, length);
if (tester == -1) {
strcpy(symbol->errtxt, "355: Invalid character in data (digits only)");
return ZINT_ERROR_INVALID_DATA;
}
error_number = pharma_two_calc(symbol, source, height_pattern);
if (error_number != 0) {
return error_number;
if ((tester < 4) || (tester > 64570080)) {
strcpy(symbol->errtxt, "353: Data out of range (4 to 64570080)");
return ZINT_ERROR_INVALID_DATA;
}
h = pharma_two_calc(tester, height_pattern);
writer = 0;
h = (int) strlen(height_pattern);
for (loopey = 0; loopey < h; loopey++) {
if ((height_pattern[loopey] == '2') || (height_pattern[loopey] == '3')) {
set_module(symbol, 0, writer);
@ -209,14 +201,13 @@ INTERNAL int pharma_two(struct zint_symbol *symbol, unsigned char source[], int
/* The Codabar system consisting of simple substitution */
INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int length) {
static const char calcium[] = CALCIUM;
int i, error_number = 0;
int posns[60];
char dest[512];
char *d = dest;
int add_checksum, count = 0, checksum = 0;
int d_chars = 0;
strcpy(dest, "");
if (length > 60) { /* No stack smashing please */
strcpy(symbol->errtxt, "356: Input too long (60 character maximum)");
return ZINT_ERROR_TOO_LONG;
@ -227,7 +218,7 @@ INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int len
strcpy(symbol->errtxt, "362: Input too short (3 character minimum)");
return ZINT_ERROR_TOO_LONG;
}
to_upper(source);
to_upper(source, length);
/* Codabar must begin and end with the characters A, B, C or D */
if ((source[0] != 'A') && (source[0] != 'B') && (source[0] != 'C')
@ -240,14 +231,13 @@ INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int len
strcpy(symbol->errtxt, "359: Does not end with \"A\", \"B\", \"C\" or \"D\"");
return ZINT_ERROR_INVALID_DATA;
}
if (!is_sane_lookup(CALCIUM, sizeof(CALCIUM) - 1, source, length, posns)) {
sprintf(symbol->errtxt, "357: Invalid character in data (\"%s\" only)", CALCIUM);
return ZINT_ERROR_INVALID_DATA;
}
/* And must not use A, B, C or D otherwise (BS EN 798:1995 4.3.2) */
if (is_sane(CALCIUM_INNER, source + 1, length - 2) != 0) {
if (is_sane(calcium, source + 1, length - 2) == 0) {
strcpy(symbol->errtxt, "363: Cannot contain \"A\", \"B\", \"C\" or \"D\"");
} else {
sprintf(symbol->errtxt, "357: Invalid character in data (\"%s\" only)", calcium);
}
if (!is_sane(CALCIUM_INNER_F, source + 1, length - 2)) {
strcpy(symbol->errtxt, "363: Cannot contain \"A\", \"B\", \"C\" or \"D\"");
return ZINT_ERROR_INVALID_DATA;
}
@ -255,29 +245,30 @@ INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int len
(unfortunately to maintain back-compatibility, this is reverse of C25) */
add_checksum = symbol->option_2 == 1 || symbol->option_2 == 2;
for (i = 0; i < length; i++) {
for (i = 0; i < length; i++, d += 8) {
if (add_checksum) {
/* BS EN 798:1995 A.3 suggests using ISO 7064 algorithm but leaves it application defined.
Following BWIPP and TEC-IT, use this simple mod-16 algorithm (not in ISO 7064) */
count += strchr(calcium, source[i]) - calcium;
count += posns[i];
if (i + 1 == length) {
checksum = count % 16;
if (checksum) {
checksum = 16 - checksum;
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("Codabar: %s, count %d, checksum %d (%c)\n", source, count, checksum, calcium[checksum]);
printf("Codabar: %s, count %d, checksum %d (%c)\n", source, count, checksum, CALCIUM[checksum]);
}
strcat(dest, CodaTable[checksum]);
memcpy(d, CodaTable[checksum], 8);
d += 8;
}
}
lookup(calcium, CodaTable, source[i], dest);
memcpy(d, CodaTable[posns[i]], 8);
if (source[i] == '/' || source[i] == ':' || source[i] == '.' || source[i] == '+') { /* Wide data characters */
d_chars++;
}
}
expand(symbol, dest);
expand(symbol, dest, d - dest);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* BS EN 798:1995 4.4.1 (d) max of 5mm / 0.191mm (X) ~ 26.178 or 15% of width where (taking N = narrow/wide
@ -297,7 +288,7 @@ INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int len
ustrcpy(symbol->text, source);
if (symbol->option_2 == 2) {
symbol->text[length - 1] = calcium[checksum]; /* Place before final A/B/C/D character (BS EN 798:1995 A.3) */
symbol->text[length - 1] = CALCIUM[checksum]; /* Place before final A/B/C/D character (BS EN 798:1995 A.3) */
symbol->text[length] = source[length - 1];
symbol->text[length + 1] = '\0';
}
@ -307,18 +298,18 @@ INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int len
/* Italian Pharmacode */
INTERNAL int code32(struct zint_symbol *symbol, unsigned char source[], int length) {
static const char TABELLA[] = "0123456789BCDFGHJKLMNPQRSTUVWXYZ";
int i, zeroes, error_number = 0, checksum, checkpart, checkdigit;
char localstr[10], risultante[7];
long int pharmacode, devisor;
int codeword[6];
char tabella[34];
/* Validate the input */
if (length > 8) {
strcpy(symbol->errtxt, "360: Input too long (8 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
if (is_sane(NEON, source, length) != 0) {
if (!is_sane(NEON_F, source, length)) {
strcpy(symbol->errtxt, "361: Invalid character in data (digits only)");
return ZINT_ERROR_INVALID_DATA;
}
@ -360,13 +351,12 @@ INTERNAL int code32(struct zint_symbol *symbol, unsigned char source[], int leng
}
/* Look up values in 'Tabella di conversione' */
strcpy(tabella, "0123456789BCDFGHJKLMNPQRSTUVWXYZ");
for (i = 5; i >= 0; i--) {
risultante[5 - i] = tabella[codeword[i]];
risultante[5 - i] = TABELLA[codeword[i]];
}
risultante[6] = '\0';
/* Plot the barcode using Code 39 */
error_number = code39(symbol, (unsigned char *) risultante, (int) strlen(risultante));
error_number = code39(symbol, (unsigned char *) risultante, 6);
if (error_number != 0) { /* Should never happen */
return error_number; /* Not reached */
}