zbar-windows/test/test_decode.c

1356 lines
38 KiB
C

/*------------------------------------------------------------------------
* Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
*
* This file is part of the ZBar Bar Code Reader.
*
* The ZBar Bar Code Reader is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* The ZBar Bar Code Reader is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser Public License
* along with the ZBar Bar Code Reader; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* http://sourceforge.net/projects/zbar
*------------------------------------------------------------------------*/
#include <argp.h>
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <zbar.h>
zbar_decoder_t *decoder;
zbar_symbol_type_t expect_sym;
char *expect_data = NULL;
int rnd_size = 9; /* NB should be odd */
int wrong = 0, spurious = 0, missing = 0;
#define zprintf(level, format, ...) \
do { \
if (verbosity >= (level)) { \
fprintf(stderr, format, ##__VA_ARGS__); \
} \
} while (0)
#define PROGRAM_NAME "test_video"
static const char doc[] =
"\nGenerate barcodes and decode them with ZBar decoding logic\n";
static const struct argp_option options[] = {
{ "quiet", 'q', 0, 0, "Don't be verbose", 0 },
{ "verbose", 'v', 0, 0, "Increases verbosity level", 0 },
{ "random", 'r', 0, 0, "use a random seed", 0 },
{ "seed", 's', "seed", 0, "sets the random seed", 0 },
{ "number", 'n', "count", 0, "sets the number of interactions", 0 },
{ "help", '?', 0, 0, "Give this help list", -1 },
{ "usage", -3, 0, 0, "Give a short usage message", 0 },
{ 0 }
};
unsigned seed = 0, rand_seed = 0;
int verbosity = 1;
int iter = 0, num_iter = 0; /* test iteration */
static error_t parse_opt(int k, char *optarg, struct argp_state *state)
{
switch (k) {
case 'q':
verbosity = 0;
break;
case 'v':
verbosity++;
break;
case 'r':
rand_seed = 1;
break;
case 's':
seed = strtol(optarg, NULL, 0);
break;
case 'n':
num_iter = strtol(optarg, NULL, 0);
break;
case '?':
argp_state_help(state, state->out_stream,
ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC);
exit(0);
case -3:
argp_state_help(state, state->out_stream, ARGP_HELP_USAGE);
exit(0);
default:
return ARGP_ERR_UNKNOWN;
};
return 0;
}
static const struct argp argp = {
.options = options,
.parser = parse_opt,
.doc = doc,
};
static inline void print_sep(int level)
{
zprintf(level,
"----------------------------------------------------------\n");
}
static void symbol_handler(zbar_decoder_t *decoder)
{
zbar_symbol_type_t sym = zbar_decoder_get_type(decoder);
if (sym <= ZBAR_PARTIAL || sym == ZBAR_QRCODE)
return;
const char *data = zbar_decoder_get_data(decoder);
if (sym != expect_sym) {
zprintf(0, "[%d] SEED=%d: warning: expecting %s, got spurious %s\n",
iter, seed, zbar_get_symbol_name(expect_sym),
zbar_get_symbol_name(sym));
spurious++;
return;
}
int pass = (sym == expect_sym) && !strcmp(data, expect_data) &&
zbar_decoder_get_data_length(decoder) == strlen(data);
pass *= 3;
zprintf(pass, "decode %s:%s\n", zbar_get_symbol_name(sym), data);
if (!expect_sym)
zprintf(0, "UNEXPECTED!\n");
else
zprintf(pass, "expect %s:%s\n", zbar_get_symbol_name(expect_sym),
expect_data);
if (!pass) {
zprintf(0, "[%d] SEED=%d: ERROR: expecting %s (%s), got %s (%s)\n",
iter, seed, expect_data, zbar_get_symbol_name(expect_sym), data,
zbar_get_symbol_name(sym));
wrong++;
}
expect_sym = ZBAR_NONE;
free(expect_data);
expect_data = NULL;
}
static void expect(zbar_symbol_type_t sym, const char *data)
{
if (expect_sym) {
zprintf(0, "[%d] SEED=%d: missing decode: %s (%s)\n", iter, seed,
zbar_get_symbol_name(expect_sym), expect_data);
missing++;
}
expect_sym = sym;
expect_data = (data) ? strdup(data) : NULL;
}
static void encode_junk(int n)
{
if (n > 1)
zprintf(3, "encode random junk...\n");
int i;
for (i = 0; i < n; i++)
zbar_decode_width(decoder, 20. * (rand() / (RAND_MAX + 1.)) + 1);
}
#define FWD 1
#define REV 0
static void encode(uint64_t units, int fwd)
{
zprintf(3, " raw=%x%x%c\n", (unsigned)(units >> 32),
(unsigned)(units & 0xffffffff), (fwd) ? '<' : '>');
if (!fwd)
while (units && !(units >> 0x3c))
units <<= 4;
while (units) {
unsigned char w = (fwd) ? units & 0xf : units >> 0x3c;
zbar_decode_width(decoder, w);
if (fwd)
units >>= 4;
else
units <<= 4;
}
}
/*------------------------------------------------------------*/
/* Code 128 encoding */
typedef enum code128_char_e {
FNC3 = 0x60,
FNC2 = 0x61,
SHIFT = 0x62,
CODE_C = 0x63,
CODE_B = 0x64,
CODE_A = 0x65,
FNC1 = 0x66,
START_A = 0x67,
START_B = 0x68,
START_C = 0x69,
STOP = 0x6a,
} code128_char_t;
static const unsigned int
code128[107] = {
0x212222, 0x222122, 0x222221, 0x121223, /* 00 */
0x121322, 0x131222, 0x122213, 0x122312,
0x132212, 0x221213, 0x221312, 0x231212, /* 08 */
0x112232, 0x122132, 0x122231, 0x113222,
0x123122, 0x123221, 0x223211, 0x221132, /* 10 */
0x221231, 0x213212, 0x223112, 0x312131,
0x311222, 0x321122, 0x321221, 0x312212, /* 18 */
0x322112, 0x322211, 0x212123, 0x212321,
0x232121, 0x111323, 0x131123, 0x131321, /* 20 */
0x112313, 0x132113, 0x132311, 0x211313,
0x231113, 0x231311, 0x112133, 0x112331, /* 28 */
0x132131, 0x113123, 0x113321, 0x133121,
0x313121, 0x211331, 0x231131, 0x213113, /* 30 */
0x213311, 0x213131, 0x311123, 0x311321,
0x331121, 0x312113, 0x312311, 0x332111, /* 38 */
0x314111, 0x221411, 0x431111, 0x111224,
0x111422, 0x121124, 0x121421, 0x141122, /* 40 */
0x141221, 0x112214, 0x112412, 0x122114,
0x122411, 0x142112, 0x142211, 0x241211, /* 48 */
0x221114, 0x413111, 0x241112, 0x134111,
0x111242, 0x121142, 0x121241, 0x114212, /* 50 */
0x124112, 0x124211, 0x411212, 0x421112,
0x421211, 0x212141, 0x214121, 0x412121, /* 58 */
0x111143, 0x111341, 0x131141, 0x114113,
0x114311, 0x411113, 0x411311, 0x113141, /* 60 */
0x114131, 0x311141, 0x411131, 0xa211412,
0xa211214, 0xa211232, /* START_A-START_C (67-69) */
0x2331112a, /* STOP (6a) */
};
static void encode_code128b(char *data)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "CODE-128(B): %s\n", data);
zprintf(3, " encode START_B: %02x", START_B);
encode(code128[START_B], 0);
int i, chk = START_B;
for (i = 0; data[i]; i++) {
zprintf(3, " encode '%c': %02x", data[i], data[i] - 0x20);
encode(code128[data[i] - 0x20], 0);
chk += (i + 1) * (data[i] - 0x20);
}
chk %= 103;
zprintf(3, " encode checksum: %02x", chk);
encode(code128[chk], 0);
zprintf(3, " encode STOP: %02x", STOP);
encode(code128[STOP], 0);
print_sep(3);
}
static void encode_code128c(char *data)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "CODE-128(C): %s\n", data);
zprintf(3, " encode START_C: %02x", START_C);
encode(code128[START_C], 0);
int i, chk = START_C;
for (i = 0; data[i]; i += 2) {
assert(data[i] >= '0');
assert(data[i + 1] >= '0');
unsigned char c = (data[i] - '0') * 10 + (data[i + 1] - '0');
zprintf(3, " encode '%c%c': %02d", data[i], data[i + 1], c);
encode(code128[c], 0);
chk += (i / 2 + 1) * c;
}
chk %= 103;
zprintf(3, " encode checksum: %02x", chk);
encode(code128[chk], 0);
zprintf(3, " encode STOP: %02x", STOP);
encode(code128[STOP], 0);
print_sep(3);
}
/*------------------------------------------------------------*/
/* Code 93 encoding */
#define CODE93_START_STOP 0x2f
static const unsigned int code93[47 + 1] = {
0x131112, 0x111213, 0x111312, 0x111411, /* 00 */
0x121113, 0x121212, 0x121311, 0x111114,
0x131211, 0x141111, 0x211113, 0x211212, /* 08 */
0x211311, 0x221112, 0x221211, 0x231111,
0x112113, 0x112212, 0x112311, 0x122112, /* 10 */
0x132111, 0x111123, 0x111222, 0x111321,
0x121122, 0x131121, 0x212112, 0x212211, /* 18 */
0x211122, 0x211221, 0x221121, 0x222111,
0x112122, 0x112221, 0x122121, 0x123111, /* 20 */
0x121131, 0x311112, 0x311211, 0x321111,
0x112131, 0x113121, 0x211131, 0x121221, /* 28 */
0x312111, 0x311121, 0x122211, 0x111141, /* START/STOP (2f) */
};
#define S1 0x2b00 |
#define S2 0x2c00 |
#define S3 0x2d00 |
#define S4 0x2e00 |
static const unsigned short code93_ext[0x80] = {
S2 'U', S1 'A', S1 'B', S1 'C', S1 'D', S1 'E', S1 'F', S1 'G', S1 'H',
S1 'I', S1 'J', S1 'K', S1 'L', S1 'M', S1 'N', S1 'O', S1 'P', S1 'Q',
S1 'R', S1 'S', S1 'T', S1 'U', S1 'V', S1 'W', S1 'X', S1 'Y', S1 'Z',
S2 'A', S2 'B', S2 'C', S2 'D', S2 'E', 0x26, S3 'A', S3 'B', S3 'C',
0x27, 0x2a, S3 'F', S3 'G', S3 'H', S3 'I', S3 'J', 0x29, S3 'L',
0x24, 0x25, 0x28, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, S3 'Z', S2 'F', S2 'G', S2 'H', S2 'I',
S2 'J', S2 'V', 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
0x23, S2 'K', S2 'L', S2 'M', S2 'N', S2 'O', S2 'W', S4 'A', S4 'B',
S4 'C', S4 'D', S4 'E', S4 'F', S4 'G', S4 'H', S4 'I', S4 'J', S4 'K',
S4 'L', S4 'M', S4 'N', S4 'O', S4 'P', S4 'Q', S4 'R', S4 'S', S4 'T',
S4 'U', S4 'V', S4 'W', S4 'X', S4 'Y', S4 'Z', S2 'P', S2 'Q', S2 'R',
S2 'S', S2 'T',
};
#undef S1
#undef S2
#undef S3
#undef S4
static void encode_char93(unsigned char c, int dir)
{
unsigned ext = code93_ext[c];
unsigned shift = ext >> 8;
assert(shift < 0x30);
c = ext & 0xff;
if (shift) {
assert(c < 0x80);
c = code93_ext[c];
}
assert(c < 0x30);
if (shift) {
encode(code93[(dir) ? shift : c], dir ^ 1);
encode(code93[(dir) ? c : shift], dir ^ 1);
} else
encode(code93[c], dir ^ 1);
}
static void encode_code93(char *data, int dir)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
/* calculate checksums */
int i, j, chk_c = 0, chk_k = 0, n = 0;
for (i = 0; data[i]; i++, n++) {
unsigned c = data[i], ext;
assert(c < 0x80);
ext = code93_ext[c];
n += ext >> 13;
}
for (i = 0, j = 0; data[i]; i++, j++) {
unsigned ext = code93_ext[(unsigned)data[i]];
unsigned shift = ext >> 8;
unsigned c = ext & 0xff;
if (shift) {
chk_c += shift * (((n - 1 - j) % 20) + 1);
chk_k += shift * (((n - j) % 15) + 1);
j++;
c = code93_ext[c];
}
chk_c += c * (((n - 1 - j) % 20) + 1);
chk_k += c * (((n - j) % 15) + 1);
}
chk_c %= 47;
chk_k += chk_c;
chk_k %= 47;
zprintf(2, "CODE-93: %s (n=%x C=%02x K=%02x)\n", data, n, chk_c, chk_k);
encode(0xa, 0); /* leading quiet */
zprintf(3, " encode %s:", (dir) ? "START" : "STOP");
if (!dir)
encode(0x1, REV);
encode(code93[CODE93_START_STOP], dir ^ 1);
if (!dir) {
zprintf(3, " encode checksum (K): %02x", chk_k);
encode(code93[chk_k], REV ^ 1);
zprintf(3, " encode checksum (C): %02x", chk_c);
encode(code93[chk_c], REV ^ 1);
}
n = strlen(data);
for (i = 0; i < n; i++) {
unsigned char c = data[(dir) ? i : (n - i - 1)];
zprintf(3, " encode '%c':", c);
encode_char93(c, dir);
}
if (dir) {
zprintf(3, " encode checksum (C): %02x", chk_c);
encode(code93[chk_c], FWD ^ 1);
zprintf(3, " encode checksum (K): %02x", chk_k);
encode(code93[chk_k], FWD ^ 1);
}
zprintf(3, " encode %s:", (dir) ? "STOP" : "START");
encode(code93[CODE93_START_STOP], dir ^ 1);
if (dir)
encode(0x1, FWD);
encode(0xa, 0); /* trailing quiet */
print_sep(3);
}
/*------------------------------------------------------------*/
/* Code 39 encoding */
static const unsigned int code39[91 - 32] = {
0x0c4, 0x000, 0x000, 0x000, 0x0a8, 0x02a, 0x000, 0x000, /* 20 */
0x000, 0x000, 0x094, 0x08a, 0x000, 0x085, 0x184, 0x0a2, /* 28 */
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, /* 30 */
0x124, 0x064, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, /* 38 */
0x000, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00d, /* 40 */
0x10c, 0x04c, 0x01c, 0x103, 0x043, 0x142, 0x013, 0x112, /* 48 */
0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0c1, 0x1c0, /* 50 */
0x091, 0x190, 0x0d0, /* 58 */
};
/* FIXME configurable/randomized ratio, ics */
/* FIXME check digit option, ASCII escapes */
static void convert_code39(char *data)
{
char *src, *dst;
for (src = data, dst = data; *src; src++) {
char c = *src;
if (c >= 'a' && c <= 'z')
*(dst++) = c - ('a' - 'A');
else if (c == ' ' || c == '$' || c == '%' || c == '+' || c == '-' ||
(c >= '.' && c <= '9') || (c >= 'A' && c <= 'Z'))
*(dst++) = c;
else
/* skip (FIXME) */;
}
*dst = 0;
}
static void encode_char39(unsigned char c, unsigned ics)
{
assert(0x20 <= c && c <= 0x5a);
unsigned int raw = code39[c - 0x20];
if (!raw)
return; /* skip (FIXME) */
uint64_t enc = 0;
int j;
for (j = 0; j < 9; j++) {
enc = (enc << 4) | ((raw & 0x100) ? 2 : 1);
raw <<= 1;
}
enc = (enc << 4) | ics;
zprintf(3, " encode '%c': %02x%08x: ", c, (unsigned)(enc >> 32),
(unsigned)(enc & 0xffffffff));
encode(enc, REV);
}
static void encode_code39(char *data)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "CODE-39: %s\n", data);
encode(0xa, 0); /* leading quiet */
encode_char39('*', 1);
int i;
for (i = 0; data[i]; i++)
if (data[i] != '*') /* skip (FIXME) */
encode_char39(data[i], 1);
encode_char39('*', 0xa); /* w/trailing quiet */
print_sep(3);
}
#if 0
/*------------------------------------------------------------*/
/* PDF417 encoding */
/* hardcoded test message: "hello world" */
#define PDF417_ROWS 3
#define PDF417_COLS 3
static const unsigned pdf417_msg[PDF417_ROWS][PDF417_COLS] = {
{ 007, 817, 131 },
{ 344, 802, 437 },
{ 333, 739, 194 },
};
#define PDF417_START UINT64_C(0x81111113)
#define PDF417_STOP UINT64_C(0x711311121)
#include "pdf417_encode.h"
static int calc_ind417 (int mod,
int r,
int cols)
{
mod = (mod + 3) % 3;
int cw = 30 * (r / 3);
if(!mod)
return(cw + cols - 1);
else if(mod == 1)
return(cw + (PDF417_ROWS - 1) % 3);
assert(mod == 2);
return(cw + (PDF417_ROWS - 1) / 3);
}
static void encode_row417 (int r,
const unsigned *cws,
int cols,
int dir)
{
int k = r % 3;
zprintf(3, " [%d] encode %s:", r, (dir) ? "stop" : "start");
encode((dir) ? PDF417_STOP : PDF417_START, dir);
int cw = calc_ind417(k + !dir, r, cols);
zprintf(3, " [%d,%c] encode %03d(%d): ", r, (dir) ? 'R' : 'L', cw, k);
encode(pdf417_encode[cw][k], dir);
int c;
for(c = 0; c < cols; c++) {
cw = cws[c];
zprintf(3, " [%d,%d] encode %03d(%d): ", r, c, cw, k);
encode(pdf417_encode[cw][k], dir);
}
cw = calc_ind417(k + dir, r, cols);
zprintf(3, " [%d,%c] encode %03d(%d): ", r, (dir) ? 'L' : 'R', cw, k);
encode(pdf417_encode[cw][k], dir);
zprintf(3, " [%d] encode %s:", r, (dir) ? "start" : "stop");
encode((dir) ? PDF417_START : PDF417_STOP, dir);
}
static void encode_pdf417 (char *data)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "PDF417: hello world\n");
encode(0xa, 0);
int r;
for(r = 0; r < PDF417_ROWS; r++) {
encode_row417(r, pdf417_msg[r], PDF417_COLS, r & 1);
encode(0xa, 0);
}
print_sep(3);
}
#endif
/*------------------------------------------------------------*/
/* Codabar encoding */
static const unsigned int codabar[20] = {
0x03, 0x06, 0x09, 0x60, 0x12, 0x42, 0x21, 0x24, 0x30, 0x48,
0x0c, 0x18, 0x45, 0x51, 0x54, 0x15, 0x1a, 0x29, 0x0b, 0x0e,
};
static const char codabar_char[0x14] = "0123456789-$:/.+ABCD";
/* FIXME configurable/randomized ratio, ics */
/* FIXME check digit option */
static char *convert_codabar(char *src)
{
unsigned len = strlen(src);
char tmp[4] = {
0,
};
if (len < 2) {
unsigned delim = rand() >> 8;
tmp[0] = delim & 3;
if (len)
tmp[1] = src[0];
tmp[len + 1] = (delim >> 2) & 3;
len += 2;
src = tmp;
}
char *result = malloc(len + 1);
char *dst = result;
*(dst++) = ((*(src++) - 1) & 0x3) + 'A';
for (len--; len > 1; len--) {
char c = *(src++);
if (c >= '0' && c <= '9')
*(dst++) = c;
else if (c == '-' || c == '$' || c == ':' || c == '/' || c == '.' ||
c == '+')
*(dst++) = c;
else
*(dst++) = codabar_char[c % 0x10];
}
*(dst++) = ((*(src++) - 1) & 0x3) + 'A';
*dst = 0;
return (result);
}
static void encode_codachar(unsigned char c, unsigned ics, int dir)
{
unsigned int idx;
if (c >= '0' && c <= '9')
idx = c - '0';
else if (c >= 'A' && c <= 'D')
idx = c - 'A' + 0x10;
else
switch (c) {
case '-':
idx = 0xa;
break;
case '$':
idx = 0xb;
break;
case ':':
idx = 0xc;
break;
case '/':
idx = 0xd;
break;
case '.':
idx = 0xe;
break;
case '+':
idx = 0xf;
break;
default:
assert(0);
}
assert(idx < 0x14);
unsigned int raw = codabar[idx];
uint32_t enc = 0;
int j;
for (j = 0; j < 7; j++, raw <<= 1)
enc = (enc << 4) | ((raw & 0x40) ? 3 : 1);
zprintf(3, " encode '%c': %07x: ", c, enc);
if (dir)
enc = (enc << 4) | ics;
else
enc |= ics << 28;
encode(enc, 1 - dir);
}
static void encode_codabar(char *data, int dir)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "CODABAR: %s\n", data);
encode(0xa, 0); /* leading quiet */
int i, n = strlen(data);
for (i = 0; i < n; i++) {
int j = (dir) ? i : n - i - 1;
encode_codachar(data[j], (i < n - 1) ? 1 : 0xa, dir);
}
print_sep(3);
}
/*------------------------------------------------------------*/
/* Interleaved 2 of 5 encoding */
static const unsigned char i25[10] = {
0x06, 0x11, 0x09, 0x18, 0x05, 0x14, 0x0c, 0x03, 0x12, 0x0a,
};
static void encode_i25(char *data, int dir)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "Interleaved 2 of 5: %s\n", data);
zprintf(3, " encode start:");
encode((dir) ? 0xa1111 : 0xa112, 0);
/* FIXME rev case data reversal */
int i;
for (i = (strlen(data) & 1) ? -1 : 0; i < 0 || data[i]; i += 2) {
/* encode 2 digits */
unsigned char c0 = (i < 0) ? 0 : data[i] - '0';
unsigned char c1 = data[i + 1] - '0';
zprintf(3, " encode '%d%d':", c0, c1);
assert(c0 < 10);
assert(c1 < 10);
c0 = i25[c0];
c1 = i25[c1];
/* interleave */
uint64_t enc = 0;
int j;
for (j = 0; j < 5; j++) {
enc <<= 8;
enc |= (c0 & 1) ? 0x02 : 0x01;
enc |= (c1 & 1) ? 0x20 : 0x10;
c0 >>= 1;
c1 >>= 1;
}
encode(enc, dir);
}
zprintf(3, " encode end:");
encode((dir) ? 0x211a : 0x1111a, 0);
print_sep(3);
}
/*------------------------------------------------------------*/
/* DataBar encoding */
/* character encoder reference algorithm from ISO/IEC 24724:2009 */
struct rss_group {
int T_odd, T_even, n_odd, w_max;
};
static const struct rss_group databar_groups_outside[] = { { 161, 1, 12, 8 },
{ 80, 10, 10, 6 },
{ 31, 34, 8, 4 },
{ 10, 70, 6, 3 },
{ 1, 126, 4, 1 },
{
0,
} };
static const struct rss_group databar_groups_inside[] = { { 4, 84, 5, 2 },
{ 20, 35, 7, 4 },
{ 48, 10, 9, 6 },
{ 81, 1, 11, 8 },
{
0,
} };
static const uint32_t databar_finders[9] = {
0x38211, 0x35511, 0x33711, 0x31911, 0x27411,
0x25611, 0x23811, 0x15711, 0x13911,
};
int combins(int n, int r)
{
int i, j;
int maxDenom, minDenom;
int val;
if (n - r > r) {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
val = 1;
j = 1;
for (i = n; i > maxDenom; i--) {
val *= i;
if (j <= minDenom) {
val /= j;
j++;
}
}
for (; j <= minDenom; j++)
val /= j;
return (val);
}
void getRSSWidths(int val, int n, int elements, int maxWidth, int noNarrow,
int *widths)
{
int narrowMask = 0;
int bar;
for (bar = 0; bar < elements - 1; bar++) {
int elmWidth, subVal;
for (elmWidth = 1, narrowMask |= (1 << bar);;
elmWidth++, narrowMask &= ~(1 << bar)) {
subVal = combins(n - elmWidth - 1, elements - bar - 2);
if ((!noNarrow) && !narrowMask &&
(n - elmWidth - (elements - bar - 1) >= elements - bar - 1))
subVal -= combins(n - elmWidth - (elements - bar),
elements - bar - 2);
if (elements - bar - 1 > 1) {
int mxwElement, lessVal = 0;
for (mxwElement = n - elmWidth - (elements - bar - 2);
mxwElement > maxWidth; mxwElement--)
lessVal += combins(n - elmWidth - mxwElement - 1,
elements - bar - 3);
subVal -= lessVal * (elements - 1 - bar);
} else if (n - elmWidth > maxWidth)
subVal--;
val -= subVal;
if (val < 0)
break;
}
val += subVal;
n -= elmWidth;
widths[bar] = elmWidth;
}
widths[bar] = n;
}
static uint64_t encode_databar_char(unsigned val, const struct rss_group *grp,
int nmodules, int nelems, int dir)
{
int G_sum = 0;
while (1) {
assert(grp->T_odd);
int sum = G_sum + grp->T_odd * grp->T_even;
if (val >= sum)
G_sum = sum;
else
break;
grp++;
}
zprintf(3, "char=%d", val);
int V_grp = val - G_sum;
int V_odd, V_even;
if (!dir) {
V_odd = V_grp / grp->T_even;
V_even = V_grp % grp->T_even;
} else {
V_even = V_grp / grp->T_odd;
V_odd = V_grp % grp->T_odd;
}
zprintf(3, " G_sum=%d T_odd=%d T_even=%d n_odd=%d w_max=%d V_grp=%d\n",
G_sum, grp->T_odd, grp->T_even, grp->n_odd, grp->w_max, V_grp);
int odd[16];
getRSSWidths(V_odd, grp->n_odd, nelems, grp->w_max, !dir, odd);
zprintf(3, " V_odd=%d odd=%d%d%d%d", V_odd, odd[0], odd[1], odd[2],
odd[3]);
int even[16];
getRSSWidths(V_even, nmodules - grp->n_odd, nelems, 9 - grp->w_max, dir,
even);
zprintf(3, " V_even=%d even=%d%d%d%d", V_even, even[0], even[1], even[2],
even[3]);
uint64_t units = 0;
int i;
for (i = 0; i < nelems; i++)
units = (units << 8) | (odd[i] << 4) | even[i];
zprintf(3, " raw=%" PRIx64 "\n", units);
return (units);
}
#define SWAP(a, b) \
do { \
uint32_t tmp = (a); \
(a) = (b); \
(b) = tmp; \
} while (0);
static void encode_databar(char *data, int dir)
{
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "DataBar: %s\n", data);
uint32_t v[4] = {
0,
};
int i, j;
for (i = 0; i < 14; i++) {
for (j = 0; j < 4; j++)
v[j] *= 10;
assert(data[i]);
v[0] += data[i] - '0';
v[1] += v[0] / 1597;
v[0] %= 1597;
v[2] += v[1] / 2841;
v[1] %= 2841;
v[3] += v[2] / 1597;
v[2] %= 1597;
/*printf(" [%d] %c (%d,%d,%d,%d)\n",
i, data[i], v[0], v[1], v[2], v[3]);*/
}
zprintf(3, "chars=(%d,%d,%d,%d)\n", v[3], v[2], v[1], v[0]);
uint32_t c[4] = {
encode_databar_char(v[3], databar_groups_outside, 16, 4, 0),
encode_databar_char(v[2], databar_groups_inside, 15, 4, 1),
encode_databar_char(v[1], databar_groups_outside, 16, 4, 0),
encode_databar_char(v[0], databar_groups_inside, 15, 4, 1),
};
int chk = 0, w = 1;
for (i = 0; i < 4; i++, chk %= 79, w %= 79)
for (j = 0; j < 8; j++, w *= 3)
chk += ((c[i] >> (28 - j * 4)) & 0xf) * w;
zprintf(3, "chk=%d\n", chk);
if (chk >= 8)
chk++;
if (chk >= 72)
chk++;
int C_left = chk / 9;
int C_right = chk % 9;
if (dir == REV) {
SWAP(C_left, C_right);
SWAP(c[0], c[2]);
SWAP(c[1], c[3]);
SWAP(v[0], v[2]);
SWAP(v[1], v[3]);
}
zprintf(3, " encode start guard:");
encode_junk(dir);
encode(0x1, FWD);
zprintf(3, "encode char[0]=%d", v[3]);
encode(c[0], REV);
zprintf(3, "encode left finder=%d", C_left);
encode(databar_finders[C_left], REV);
zprintf(3, "encode char[1]=%d", v[2]);
encode(c[1], FWD);
zprintf(3, "encode char[3]=%d", v[0]);
encode(c[3], REV);
zprintf(3, "encode right finder=%d", C_right);
encode(databar_finders[C_right], FWD);
zprintf(3, "encode char[2]=%d", v[1]);
encode(c[2], FWD);
zprintf(3, " encode end guard:");
encode(0x1, FWD);
encode_junk(!dir);
print_sep(3);
}
/*------------------------------------------------------------*/
/* EAN/UPC encoding */
static const unsigned int ean_digits[10] = {
0x1123, 0x1222, 0x2212, 0x1141, 0x2311,
0x1321, 0x4111, 0x2131, 0x3121, 0x2113,
};
static const unsigned int ean_guard[] = {
0, 0, 0x11, /* [2] add-on delineator */
0x1117, /* [3] normal guard bars */
0x2117, /* [4] add-on guard bars */
0x11111, /* [5] center guard bars */
0x111111 /* [6] "special" guard bars */
};
static const unsigned char ean_parity_encode[] = {
0x3f, /* AAAAAA = 0 */
0x34, /* AABABB = 1 */
0x32, /* AABBAB = 2 */
0x31, /* AABBBA = 3 */
0x2c, /* ABAABB = 4 */
0x26, /* ABBAAB = 5 */
0x23, /* ABBBAA = 6 */
0x2a, /* ABABAB = 7 */
0x29, /* ABABBA = 8 */
0x25, /* ABBABA = 9 */
};
static const unsigned char addon_parity_encode[] = {
0x07, /* BBAAA = 0 */
0x0b, /* BABAA = 1 */
0x0d, /* BAABA = 2 */
0x0e, /* BAAAB = 3 */
0x13, /* ABBAA = 4 */
0x19, /* AABBA = 5 */
0x1c, /* AAABB = 6 */
0x15, /* ABABA = 7 */
0x16, /* ABAAB = 8 */
0x1a, /* AABAB = 9 */
};
static void calc_ean_parity(char *data, int n)
{
int i, chk = 0;
for (i = 0; i < n; i++) {
unsigned char c = data[i] - '0';
chk += ((i ^ n) & 1) ? c * 3 : c;
}
chk %= 10;
if (chk)
chk = 10 - chk;
data[i++] = '0' + chk;
data[i] = 0;
}
static void encode_ean13(char *data)
{
int i;
unsigned char par = ean_parity_encode[data[0] - '0'];
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "EAN-13: %s (%02x)\n", data, par);
zprintf(3, " encode start guard:");
encode(ean_guard[3], FWD);
for (i = 1; i < 7; i++, par <<= 1) {
zprintf(3, " encode %x%c:", (par >> 5) & 1, data[i]);
encode(ean_digits[data[i] - '0'], (par >> 5) & 1);
}
zprintf(3, " encode center guard:");
encode(ean_guard[5], FWD);
for (; i < 13; i++) {
zprintf(3, " encode %x%c:", 0, data[i]);
encode(ean_digits[data[i] - '0'], FWD);
}
zprintf(3, " encode end guard:");
encode(ean_guard[3], REV);
print_sep(3);
}
static void encode_ean8(char *data)
{
int i;
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "EAN-8: %s\n", data);
zprintf(3, " encode start guard:");
encode(ean_guard[3], FWD);
for (i = 0; i < 4; i++) {
zprintf(3, " encode %c:", data[i]);
encode(ean_digits[data[i] - '0'], FWD);
}
zprintf(3, " encode center guard:");
encode(ean_guard[5], FWD);
for (; i < 8; i++) {
zprintf(3, " encode %c:", data[i]);
encode(ean_digits[data[i] - '0'], FWD);
}
zprintf(3, " encode end guard:");
encode(ean_guard[3], REV);
print_sep(3);
}
static void encode_addon(char *data, unsigned par, int n)
{
int i;
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
print_sep(3);
zprintf(2, "EAN-%d: %s (par=%02x)\n", n, data, par);
zprintf(3, " encode start guard:");
encode(ean_guard[4], FWD);
for (i = 0; i < n; i++, par <<= 1) {
zprintf(3, " encode %x%c:", (par >> (n - 1)) & 1, data[i]);
encode(ean_digits[data[i] - '0'], (par >> (n - 1)) & 1);
if (i < n - 1) {
zprintf(3, " encode delineator:");
encode(ean_guard[2], FWD);
}
}
zprintf(3, " encode trailing qz:");
encode(0x7, FWD);
print_sep(3);
}
static void encode_ean5(char *data)
{
unsigned chk = ((data[0] - '0' + data[2] - '0' + data[4] - '0') * 3 +
(data[1] - '0' + data[3] - '0') * 9) %
10;
encode_addon(data, addon_parity_encode[chk], 5);
}
static void encode_ean2(char *data)
{
unsigned par = (~(10 * (data[0] - '0') + data[1] - '0')) & 3;
encode_addon(data, par, 2);
}
/*------------------------------------------------------------*/
/* main test flow */
int test_databar_F_1()
{
expect(ZBAR_DATABAR, "0124012345678905");
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
encode(0x11, 0);
encode(0x31111333, 0);
encode(0x13911, 0);
encode(0x31131231, 0);
encode(0x11214222, 0);
encode(0x11553, 0);
encode(0x21231313, 0);
encode(0x1, 0);
encode_junk(rnd_size);
return (0);
}
int test_databar_F_3()
{
expect(ZBAR_DATABAR_EXP, "1012A");
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
encode(0x11, 0);
encode(0x11521151, 0);
encode(0x18411, 0);
encode(0x13171121, 0);
encode(0x11521232, 0);
encode(0x11481, 0);
encode(0x23171111, 0);
encode(0x1, 0);
encode_junk(rnd_size);
return (0);
}
int test_orange()
{
char data[32] = "0100845963000052";
expect(ZBAR_DATABAR, data);
assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE);
encode(0x1, 0);
encode(0x23212321, 0); // data[0]
encode(0x31911, 0); // finder[?] = 3
encode(0x21121215, 1); // data[1]
encode(0x41111133, 0); // data[3]
encode(0x23811, 1); // finder[?] = 6
encode(0x11215141, 1); // data[2]
encode(0x11, 0);
encode_junk(rnd_size);
expect(ZBAR_DATABAR, data);
data[1] = '0';
encode_databar(data + 1, FWD);
encode_junk(rnd_size);
return (0);
}
int test_numeric(char *data)
{
char tmp[32] = "01";
strncpy(tmp + 2, data + 1, 13);
tmp[15] = '\0';
calc_ean_parity(tmp + 2, 13);
expect(ZBAR_DATABAR, tmp);
tmp[1] = data[0] & '1';
encode_databar(tmp + 1, (rand() >> 8) & 1);
encode_junk(rnd_size);
data[strlen(data) & ~1] = 0;
expect(ZBAR_CODE128, data);
encode_code128c(data);
encode_junk(rnd_size);
expect(ZBAR_I25, data);
encode_i25(data, FWD);
encode_junk(rnd_size);
#if 0 /* FIXME encoding broken */
encode_i25(data, REV);
encode_junk(rnd_size);
#endif
char *cdb = convert_codabar(data);
expect(ZBAR_CODABAR, cdb);
encode_codabar(cdb, FWD);
encode_junk(rnd_size);
expect(ZBAR_CODABAR, cdb);
encode_codabar(cdb, REV);
encode_junk(rnd_size);
free(cdb);
calc_ean_parity(data + 2, 12);
expect(ZBAR_EAN13, data + 2);
encode_ean13(data + 2);
encode_junk(rnd_size);
calc_ean_parity(data + 7, 7);
expect(ZBAR_EAN8, data + 7);
encode_ean8(data + 7);
encode_junk(rnd_size);
data[5] = 0;
expect(ZBAR_EAN5, data);
encode_ean5(data);
encode_junk(rnd_size);
data[2] = 0;
expect(ZBAR_EAN2, data);
encode_ean2(data);
encode_junk(rnd_size);
expect(ZBAR_NONE, NULL);
return (0);
}
int test_alpha(char *data)
{
expect(ZBAR_CODE128, data);
encode_code128b(data);
encode_junk(rnd_size);
expect(ZBAR_CODE93, data);
encode_code93(data, FWD);
encode_junk(rnd_size);
expect(ZBAR_CODE93, data);
encode_code93(data, REV);
encode_junk(rnd_size);
char *cdb = convert_codabar(data);
expect(ZBAR_CODABAR, cdb);
encode_codabar(cdb, FWD);
encode_junk(rnd_size);
expect(ZBAR_CODABAR, cdb);
encode_codabar(cdb, REV);
encode_junk(rnd_size);
free(cdb);
convert_code39(data);
expect(ZBAR_CODE39, data);
encode_code39(data);
encode_junk(rnd_size);
#if 0 /* FIXME decoder unfinished */
encode_pdf417(data);
encode_junk(rnd_size);
#endif
expect(ZBAR_NONE, NULL);
return (0);
}
int test1()
{
print_sep(2);
if (!seed)
seed = 0xbabeface;
zprintf(1, "[%d] SEED=%d\n", iter, seed);
srand(seed);
int i;
char data[32];
for (i = 0; i < 14; i++) {
data[i] = (rand() % 10) + '0';
}
data[i] = 0;
zprintf(1, "testing data: %s\n", data);
test_numeric(data);
for (i = 0; i < 10; i++)
data[i] = (rand() % 0x5f) + 0x20;
data[i] = 0;
zprintf(1, "testing alpha: %s\n", data);
test_alpha(data);
return (0);
}
/* FIXME TBD:
* - random module width (!= 1.0)
* - simulate scan speed variance
* - simulate dark "swelling" and light "blooming"
* - inject parity errors
*/
float percent(int count, int iter)
{
if (iter <= 1) {
if (count)
return 100.0;
else
return 0.0;
}
return (count * 100.0) / iter;
}
int main(int argc, char *argv[])
{
if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) {
argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME);
return -1;
}
if (rand_seed) {
seed = time(NULL);
srand(seed);
seed = (rand() << 8) ^ rand();
zprintf(0, "Random SEED=%d\n", seed);
}
decoder = zbar_decoder_create();
/* allow empty CODE39 symbologies */
zbar_decoder_set_config(decoder, ZBAR_CODE39, ZBAR_CFG_MIN_LEN, 0);
/* enable addons */
zbar_decoder_set_config(decoder, ZBAR_EAN2, ZBAR_CFG_ENABLE, 1);
zbar_decoder_set_config(decoder, ZBAR_EAN5, ZBAR_CFG_ENABLE, 1);
zbar_decoder_set_handler(decoder, symbol_handler);
encode_junk(rnd_size + 1);
if (num_iter) {
for (iter = 0; iter < num_iter; iter++) {
test1();
seed = (rand() << 8) ^ rand();
}
} else {
test_databar_F_1();
test_databar_F_3();
test_orange();
test1();
}
zbar_decoder_destroy(decoder);
if (!wrong && percent(spurious, num_iter) <= 0.01 &&
percent(missing, num_iter) <= 0.01) {
if (spurious || missing)
printf(
"decoder PASSED with %d spurious (%02.4f%%) and %d missing(%02.4f%%).\n",
spurious, percent(spurious, num_iter), missing,
percent(missing, num_iter));
else
printf("decoder PASSED.\n");
} else {
printf(
"decoder FAILED with %d wrong decoding(%02.4f%%), %d spurious (%02.4f%%) and %d missing(%02.4f%%).\n",
wrong, percent(wrong, num_iter), spurious,
percent(spurious, num_iter), missing, percent(missing, num_iter));
return 1;
}
return (0);
}