Files
OpenTXW81X/sdk/app/zbar/decoder/databar.c
T
2025-08-27 09:51:58 +01:00

1281 lines
34 KiB
C

/*------------------------------------------------------------------------
* Copyright 2010 (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 <zbar_config.h>
#include <zbar.h>
#ifdef DEBUG_DATABAR
# define DEBUG_LEVEL (DEBUG_DATABAR)
#endif
#include "debug.h"
#include "decoder.h"
#define GS ('\035')
enum { SCH_NUM, SCH_ALNUM, SCH_ISO646 };
static const signed char finder_hash[0x20] = {
0x16, 0x1f, 0x02, 0x00, 0x03, 0x00, 0x06, 0x0b,
0x1f, 0x0e, 0x17, 0x0c, 0x0b, 0x14, 0x11, 0x0c,
0x1f, 0x03, 0x13, 0x08, 0x00, 0x0a, -1, 0x16,
0x0c, 0x09, -1, 0x1a, 0x1f, 0x1c, 0x00, -1,
};
/* DataBar character encoding groups */
struct group_s {
unsigned short sum;
unsigned char wmax;
unsigned char todd;
unsigned char teven;
} groups[] = {
/* (17,4) DataBar Expanded character groups */
{ 0, 7, 87, 4 },
{ 348, 5, 52, 20 },
{ 1388, 4, 30, 52 },
{ 2948, 3, 10, 104 },
{ 3988, 1, 1, 204 },
/* (16,4) DataBar outer character groups */
{ 0, 8, 161, 1 },
{ 161, 6, 80, 10 },
{ 961, 4, 31, 34 },
{ 2015, 3, 10, 70 },
{ 2715, 1, 1, 126 },
/* (15,4) DataBar inner character groups */
{ 1516, 8, 81, 1 },
{ 1036, 6, 48, 10 },
{ 336, 4, 20, 35 },
{ 0, 2, 4, 84 },
};
static const unsigned char exp_sequences[] = {
/* sequence Group 1 */
0x01,
0x23,
0x25, 0x07,
0x29, 0x47,
0x29, 0x67, 0x0b,
0x29, 0x87, 0xab,
/* sequence Group 2 */
0x21, 0x43, 0x65, 0x07,
0x21, 0x43, 0x65, 0x89,
0x21, 0x43, 0x65, 0xa9, 0x0b,
0x21, 0x43, 0x67, 0x89, 0xab
};
/* DataBar expanded checksum multipliers */
static const unsigned char exp_checksums[] = {
1, 189, 62, 113, 46, 43, 109, 134, 6, 79, 161, 45
};
static inline void
append_check14 (unsigned char *buf)
{
unsigned char chk = 0, d;
int i;
for(i = 13; --i >= 0; ) {
d = *(buf++) - '0';
chk += d;
if(!(i & 1))
chk += d << 1;
}
chk %= 10;
if(chk)
chk = 10 - chk;
*buf = chk + '0';
}
static inline void
decode10 (unsigned char *buf,
unsigned long n,
int i)
{
buf += i;
while(--i >= 0) {
unsigned char d = n % 10;
n /= 10;
*--buf = '0' + d;
}
}
#define VAR_MAX(l, i) ((((l) * 12 + (i)) * 2 + 6) / 7)
#define FEED_BITS(b) \
while(i < (b) && len) { \
d = (d << 12) | (*(data++) & 0xfff); \
i += 12; \
len--; \
dbprintf(2, " %03lx", d & 0xfff); \
}
#define PUSH_CHAR(c) \
*(buf++) = (c)
#define PUSH_CHAR4(c0, c1, c2, c3) do { \
PUSH_CHAR(c0); \
PUSH_CHAR(c1); \
PUSH_CHAR(c2); \
PUSH_CHAR(c3); \
} while(0);
static inline int
databar_postprocess_exp (zbar_decoder_t *dcode,
int *data)
{
int i = 0, enc;
unsigned n;
unsigned char *buf;
unsigned long d = *(data++);
int len = d / 211 + 4, buflen;
/* grok encodation method */
d = *(data++);
dbprintf(2, "\n len=%d %03lx", len, d & 0xfff);
n = (d >> 4) & 0x7f;
if(n >= 0x40) {
i = 10;
enc = 1;
buflen = 2 + 14 + VAR_MAX(len, 10 - 2 - 44 + 6) + 2;
}
else if(n >= 0x38) {
i = 4;
enc = 6 + (n & 7);
buflen = 2 + 14 + 4 + 6 + 2 + 6 + 2;
}
else if(n >= 0x30) {
i = 6;
enc = 2 + ((n >> 2) & 1);
buflen = 2 + 14 + 4 + 3 + VAR_MAX(len, 6 - 2 - 44 - 2 - 10) + 2;
}
else if(n >= 0x20) {
i = 7;
enc = 4 + ((n >> 3) & 1);
buflen = 2 + 14 + 4 + 6;
}
else {
i = 9;
enc = 0;
buflen = VAR_MAX(len, 9 - 2) + 2;
}
dbprintf(2, " buflen=%d enc=%d", buflen, enc);
zassert(buflen > 2, -1, "buflen=%d\n", buflen);
if(enc < 4) {
/* grok variable length symbol bit field */
if((len ^ (d >> (--i))) & 1)
/* even/odd length mismatch */
return(-1);
if(((d >> (--i)) & 1) != (len > 14))
/* size group mismatch */
return(-1);
}
len -= 2;
dbprintf(2, " [%d+%d]", i, len);
if(size_buf(dcode, buflen))
return(-1);
buf = dcode->buf;
/* handle compressed fields */
if(enc) {
PUSH_CHAR('0');
PUSH_CHAR('1');
}
if(enc == 1) {
i -= 4;
n = (d >> i) & 0xf;
if(i >= 10)
return(-1);
PUSH_CHAR('0' + n);
}
else if(enc)
PUSH_CHAR('9');
if(enc) {
int j;
for(j = 0; j < 4; j++) {
FEED_BITS(10);
i -= 10;
n = (d >> i) & 0x3ff;
if(n >= 1000)
return(-1);
decode10(buf, n, 3);
buf += 3;
}
append_check14(buf - 13);
buf++;
}
switch(enc)
{
case 2: /* 01100: AI 392x */
FEED_BITS(2);
i -= 2;
n = (d >> i) & 0x3;
PUSH_CHAR4('3', '9', '2', '0' + n);
break;
case 3: /* 01101: AI 393x */
FEED_BITS(12);
i -= 2;
n = (d >> i) & 0x3;
PUSH_CHAR4('3', '9', '3', '0' + n);
i -= 10;
n = (d >> i) & 0x3ff;
if(n >= 1000)
return(-1);
decode10(buf, n, 3);
buf += 3;
break;
case 4: /* 0100: AI 3103 */
FEED_BITS(15);
i -= 15;
n = (d >> i) & 0x7fff;
PUSH_CHAR4('3', '1', '0', '3');
decode10(buf, n, 6);
buf += 6;
break;
case 5: /* 0101: AI 3202/3203 */
FEED_BITS(15);
i -= 15;
n = (d >> i) & 0x7fff;
dbprintf(2, " v=%d", n);
PUSH_CHAR4('3', '2', '0', (n >= 10000) ? '3' : '2' );
if(n >= 10000)
n -= 10000;
decode10(buf, n, 6);
buf += 6;
break;
}
if(enc >= 6) {
/* 0111000 - 0111111: AI 310x/320x + AI 11/13/15/17 */
PUSH_CHAR4('3', '1' + (enc & 1), '0', 'x');
FEED_BITS(20);
i -= 20;
n = (d >> i) & 0xfffff;
dbprintf(2, " [%d+%d] %d", i, len, n);
if(n >= 1000000)
return(-1);
decode10(buf, n, 6);
*(buf - 1) = *buf;
*buf = '0';
buf += 6;
FEED_BITS(16);
i -= 16;
n = (d >> i) & 0xffff;
if(n < 38400) {
int dd, mm, yy;
dd = n % 32;
n /= 32;
mm = n % 12 + 1;
n /= 12;
yy = n;
PUSH_CHAR('1');
PUSH_CHAR('0' + ((enc - 6) | 1));
decode10(buf, yy, 2);
buf += 2;
decode10(buf, mm, 2);
buf += 2;
decode10(buf, dd, 2);
buf += 2;
}
else if(n > 38400)
return(-1);
}
if(enc < 4) {
/* remainder is general-purpose data compaction */
int scheme = SCH_NUM;
while(i > 0 || len > 0) {
FEED_BITS(8);
dbprintf(2, " [%d+%d]", i, len);
if(scheme == SCH_NUM) {
int n1;
i -= 4;
if(i < 0)
break;
if(!((d >> i) & 0xf)) {
scheme = SCH_ALNUM;
dbprintf(2, ">A");
continue;
}
if(!len && i < 3) {
/* special case last digit */
n = ((d >> i) & 0xf) - 1;
if(n > 9)
return(-1);
*(buf++) = '0' + n;
break;
}
i -= 3;
zassert(i >= 0, -1, "\n");
n = ((d >> i) & 0x7f) - 8;
n1 = n % 11;
n = n / 11;
dbprintf(2, "N%d%d", n, n1);
*(buf++) = (n < 10) ? '0' + n : GS;
*(buf++) = (n1 < 10) ? '0' + n1 : GS;
}
else {
unsigned c = 0;
i -= 3;
if(i < 0)
break;
if(!((d >> i) & 0x7)) {
scheme = SCH_NUM;
continue;
}
i -= 2;
if(i < 0)
break;
n = (d >> i) & 0x1f;
if(n == 0x04) {
scheme ^= 0x3;
dbprintf(2, ">%d", scheme);
}
else if(n == 0x0f)
c = GS;
else if(n < 0x0f)
c = 43 + n;
else if(scheme == SCH_ALNUM) {
i--;
if(i < 0)
return(-1);
n = (d >> i) & 0x1f;
if(n < 0x1a)
c = 'A' + n;
else if(n == 0x1a)
c = '*';
else if(n < 0x1f)
c = ',' + n - 0x1b;
else
return(-1);
}
else if(scheme == SCH_ISO646 && n < 0x1d) {
i -= 2;
if(i < 0)
return(-1);
n = (d >> i) & 0x3f;
if(n < 0x1a)
c = 'A' + n;
else if(n < 0x34)
c = 'a' + n - 0x1a;
else
return(-1);
}
else if(scheme == SCH_ISO646) {
i -= 3;
if(i < 0)
return(-1);
n = ((d >> i) & 0x1f);
dbprintf(2, "(%02x)", n);
if(n < 0xa)
c = '!' + n - 8;
else if(n < 0x15)
c = '%' + n - 0xa;
else if(n < 0x1b)
c = ':' + n - 0x15;
else if(n == 0x1b)
c = '_';
else if(n == 0x1c)
c = ' ';
else
return(-1);
}
else
return(-1);
if(c) {
dbprintf(2, "%d%c", scheme, c);
*(buf++) = c;
}
}
}
/* FIXME check pad? */
}
i = buf - dcode->buf;
zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i,
_zbar_decoder_buf_dump(dcode->buf, i));
*buf = 0;
dcode->buflen = i;
if(i && *--buf == GS) {
*buf = 0;
dcode->buflen--;
}
dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, dcode->buflen));
return(0);
}
#undef FEED_BITS
/* convert from heterogeneous base {1597,2841}
* to base 10 character representation
*/
static inline void
databar_postprocess (zbar_decoder_t *dcode,
unsigned d[4])
{
databar_decoder_t *db = &dcode->databar;
int i;
unsigned c, chk = 0;
unsigned char *buf = dcode->buf;
*(buf++) = '0';
*(buf++) = '1';
buf += 15;
*--buf = '\0';
*--buf = '\0';
dbprintf(2, "\n d={%d,%d,%d,%d}", d[0], d[1], d[2], d[3]);
unsigned long r = d[0] * 1597 + d[1];
d[1] = r / 10000;
r %= 10000;
r = r * 2841 + d[2];
d[2] = r / 10000;
r %= 10000;
r = r * 1597 + d[3];
d[3] = r / 10000;
dbprintf(2, " r=%ld", r);
for(i = 4; --i >= 0; ) {
c = r % 10;
chk += c;
if(i & 1)
chk += c << 1;
*--buf = c + '0';
if(i)
r /= 10;
}
dbprintf(2, " d={%d,%d,%d}", d[1], d[2], d[3]);
r = d[1] * 2841 + d[2];
d[2] = r / 10000;
r %= 10000;
r = r * 1597 + d[3];
d[3] = r / 10000;
dbprintf(2, " r=%ld", r);
for(i = 4; --i >= 0; ) {
c = r % 10;
chk += c;
if(i & 1)
chk += c << 1;
*--buf = c + '0';
if(i)
r /= 10;
}
r = d[2] * 1597 + d[3];
dbprintf(2, " d={%d,%d} r=%ld", d[2], d[3], r);
for(i = 5; --i >= 0; ) {
c = r % 10;
chk += c;
if(!(i & 1))
chk += c << 1;
*--buf = c + '0';
if(i)
r /= 10;
}
/* NB linkage flag not supported */
if(TEST_CFG(db->config, ZBAR_CFG_EMIT_CHECK)) {
chk %= 10;
if(chk)
chk = 10 - chk;
buf[13] = chk + '0';
dcode->buflen = buf - dcode->buf + 14;
}
else
dcode->buflen = buf - dcode->buf + 13;
dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, 16));
}
static inline int
check_width (unsigned wf,
unsigned wd,
unsigned n)
{
unsigned dwf = wf * 3;
wd *= 14;
wf *= n;
return(wf - dwf <= wd && wd <= wf + dwf);
}
static inline void
merge_segment (databar_decoder_t *db,
databar_segment_t *seg)
{
unsigned csegs = db->csegs;
int i;
for(i = 0; i < csegs; i++) {
databar_segment_t *s = db->segs + i;
if(s != seg && s->finder == seg->finder && s->exp == seg->exp &&
s->color == seg->color && s->side == seg->side &&
s->data == seg->data && s->check == seg->check &&
check_width(seg->width, s->width, 14)) {
/* merge with existing segment */
unsigned cnt = s->count;
if(cnt < 0x7f)
cnt++;
seg->count = cnt;
seg->partial &= s->partial;
seg->width = (3 * seg->width + s->width + 2) / 4;
s->finder = -1;
dbprintf(2, " dup@%d(%d,%d)",
i, cnt, (db->epoch - seg->epoch) & 0xff);
}
else if(s->finder >= 0) {
unsigned age = (db->epoch - s->epoch) & 0xff;
if(age >= 248 || (age >= 128 && s->count < 2))
s->finder = -1;
}
}
}
static inline zbar_symbol_type_t
match_segment (zbar_decoder_t *dcode,
databar_segment_t *seg)
{
databar_decoder_t *db = &dcode->databar;
unsigned csegs = db->csegs, maxage = 0xfff;
int i0, i1, i2, maxcnt = 0;
databar_segment_t *smax[3] = { NULL, };
if(seg->partial && seg->count < 4)
return(ZBAR_PARTIAL);
for(i0 = 0; i0 < csegs; i0++) {
databar_segment_t *s0 = db->segs + i0;
if(s0 == seg || s0->finder != seg->finder || s0->exp ||
s0->color != seg->color || s0->side == seg->side ||
(s0->partial && s0->count < 4) ||
!check_width(seg->width, s0->width, 14))
continue;
for(i1 = 0; i1 < csegs; i1++) {
databar_segment_t *s1 = db->segs + i1;
int chkf, chks, chk;
unsigned age1;
if(i1 == i0 || s1->finder < 0 || s1->exp ||
s1->color == seg->color ||
(s1->partial && s1->count < 4) ||
!check_width(seg->width, s1->width, 14))
continue;
dbprintf(2, "\n\t[%d,%d] f=%d(0%xx)/%d(%x%x%x)",
i0, i1, seg->finder, seg->color,
s1->finder, s1->exp, s1->color, s1->side);
if(seg->color)
chkf = seg->finder + s1->finder * 9;
else
chkf = s1->finder + seg->finder * 9;
if(chkf > 72)
chkf--;
if(chkf > 8)
chkf--;
chks = (seg->check + s0->check + s1->check) % 79;
if(chkf >= chks)
chk = chkf - chks;
else
chk = 79 + chkf - chks;
dbprintf(2, " chk=(%d,%d) => %d", chkf, chks, chk);
age1 = (((db->epoch - s0->epoch) & 0xff) +
((db->epoch - s1->epoch) & 0xff));
for(i2 = i1 + 1; i2 < csegs; i2++) {
databar_segment_t *s2 = db->segs + i2;
unsigned cnt, age2, age;
if(i2 == i0 || s2->finder != s1->finder || s2->exp ||
s2->color != s1->color || s2->side == s1->side ||
s2->check != chk ||
(s2->partial && s2->count < 4) ||
!check_width(seg->width, s2->width, 14))
continue;
age2 = (db->epoch - s2->epoch) & 0xff;
age = age1 + age2;
cnt = s0->count + s1->count + s2->count;
dbprintf(2, " [%d] MATCH cnt=%d age=%d", i2, cnt, age);
if(maxcnt < cnt ||
(maxcnt == cnt && maxage > age)) {
maxcnt = cnt;
maxage = age;
smax[0] = s0;
smax[1] = s1;
smax[2] = s2;
}
}
}
}
if(!smax[0])
return(ZBAR_PARTIAL);
unsigned d[4];
d[(seg->color << 1) | seg->side] = seg->data;
for(i0 = 0; i0 < 3; i0++) {
d[(smax[i0]->color << 1) | smax[i0]->side] = smax[i0]->data;
if(!--(smax[i0]->count))
smax[i0]->finder = -1;
}
seg->finder = -1;
if(size_buf(dcode, 18))
return(ZBAR_PARTIAL);
if(acquire_lock(dcode, ZBAR_DATABAR))
return(ZBAR_PARTIAL);
databar_postprocess(dcode, d);
dcode->modifiers = MOD(ZBAR_MOD_GS1);
dcode->direction = 1 - 2 * (seg->side ^ seg->color ^ 1);
return(ZBAR_DATABAR);
}
static inline unsigned
lookup_sequence (databar_segment_t *seg,
int fixed,
int seq[22])
{
unsigned n = seg->data / 211, i;
const unsigned char *p;
i = (n + 1) / 2 + 1;
n += 4;
i = (i * i) / 4;
dbprintf(2, " {%d,%d:", i, n);
p = exp_sequences + i;
fixed >>= 1;
seq[0] = 0;
seq[1] = 1;
for(i = 2; i < n; ) {
int s = *p;
if(!(i & 2)) {
p++;
s >>= 4;
}
else
s &= 0xf;
if(s == fixed)
fixed = -1;
s <<= 1;
dbprintf(2, "%x", s);
seq[i++] = s++;
seq[i++] = s;
}
dbprintf(2, "}");
seq[n] = -1;
return(fixed < 1);
}
#define IDX(s) \
(((s)->finder << 2) | ((s)->color << 1) | ((s)->color ^ (s)->side))
static inline zbar_symbol_type_t
match_segment_exp (zbar_decoder_t *dcode,
databar_segment_t *seg,
int dir)
{
databar_decoder_t *db = &dcode->databar;
int bestsegs[22], i = 0, segs[22], seq[22];
int ifixed = seg - db->segs, fixed = IDX(seg), maxcnt = 0;
int iseg[DATABAR_MAX_SEGMENTS];
unsigned csegs = db->csegs, width = seg->width, maxage = 0x7fff;
bestsegs[0] = segs[0] = seq[1] = -1;
seq[0] = 0;
dbprintf(2, "\n fixed=%d@%d: ", fixed, ifixed);
for(i = csegs, seg = db->segs + csegs - 1; --i >= 0; seg--) {
if(seg->exp && seg->finder >= 0 &&
(!seg->partial || seg->count >= 4))
iseg[i] = IDX(seg);
else
iseg[i] = -1;
dbprintf(2, " %d", iseg[i]);
}
for(i = 0; ; i--) {
if(!i)
dbprintf(2, "\n ");
for(; i >= 0 && seq[i] >= 0; i--) {
int j;
dbprintf(2, " [%d]%d", i, seq[i]);
if(seq[i] == fixed) {
seg = db->segs + ifixed;
if(segs[i] < 0 && check_width(width, seg->width, 14)) {
dbprintf(2, "*");
j = ifixed;
}
else
continue;
}
else {
for(j = segs[i] + 1; j < csegs; j++) {
if(iseg[j] == seq[i] &&
(!i || check_width(width, db->segs[j].width, 14))) {
seg = db->segs + j;
break;
}
}
if(j == csegs)
continue;
}
if(!i) {
if(!lookup_sequence(seg, fixed, seq)) {
dbprintf(2, "[nf]");
continue;
}
width = seg->width;
dbprintf(2, " A00@%d", j);
}
else {
width = (width + seg->width) / 2;
dbprintf(2, " %c%x%x@%d",
'A' + seg->finder, seg->color, seg->side, j);
}
segs[i++] = j;
segs[i++] = -1;
}
if(i < 0)
break;
seg = db->segs + segs[0];
unsigned cnt = 0, chk = 0, age = (db->epoch - seg->epoch) & 0xff;
for(i = 1; segs[i] >= 0; i++) {
seg = db->segs + segs[i];
chk += seg->check;
cnt += seg->count;
age += (db->epoch - seg->epoch) & 0xff;
}
unsigned data0 = db->segs[segs[0]].data;
unsigned chk0 = data0 % 211;
chk %= 211;
dbprintf(2, " chk=%d ?= %d", chk, chk0);
if(chk != chk0)
continue;
dbprintf(2, " cnt=%d age=%d", cnt, age);
if(maxcnt > cnt || (maxcnt == cnt && maxage <= age))
continue;
dbprintf(2, " !");
maxcnt = cnt;
maxage = age;
for(i = 0; segs[i] >= 0; i++)
bestsegs[i] = segs[i];
bestsegs[i] = -1;
}
if(bestsegs[0] < 0)
return(ZBAR_PARTIAL);
if(acquire_lock(dcode, ZBAR_DATABAR_EXP))
return(ZBAR_PARTIAL);
for(i = 0; bestsegs[i] >= 0; i++)
segs[i] = db->segs[bestsegs[i]].data;
if(databar_postprocess_exp(dcode, segs)) {
release_lock(dcode, ZBAR_DATABAR_EXP);
return(ZBAR_PARTIAL);
}
for(i = 0; bestsegs[i] >= 0; i++)
if(bestsegs[i] != ifixed) {
seg = db->segs + bestsegs[i];
if(!--seg->count)
seg->finder = -1;
}
/* FIXME stacked rows are frequently reversed,
* so direction is impossible to determine at this level
*/
dcode->direction = (1 - 2 * (seg->side ^ seg->color)) * dir;
dcode->modifiers = MOD(ZBAR_MOD_GS1);
return(ZBAR_DATABAR_EXP);
}
#undef IDX
static inline unsigned
calc_check (unsigned sig0,
unsigned sig1,
unsigned side,
unsigned mod)
{
unsigned chk = 0;
int i;
for(i = 4; --i >= 0; ) {
chk = (chk * 3 + (sig1 & 0xf) + 1) * 3 + (sig0 & 0xf) + 1;
sig1 >>= 4;
sig0 >>= 4;
if(!(i & 1))
chk %= mod;
}
dbprintf(2, " chk=%d", chk);
if(side)
chk = (chk * (6561 % mod)) % mod;
return(chk);
}
static inline int
calc_value4 (unsigned sig,
unsigned n,
unsigned wmax,
unsigned nonarrow)
{
unsigned v = 0;
n--;
unsigned w0 = (sig >> 12) & 0xf;
if(w0 > 1) {
if(w0 > wmax)
return(-1);
unsigned n0 = n - w0;
unsigned sk20 = (n - 1) * n * (2 * n - 1);
unsigned sk21 = n0 * (n0 + 1) * (2 * n0 + 1);
v = sk20 - sk21 - 3 * (w0 - 1) * (2 * n - w0);
if(!nonarrow && w0 > 2 && n > 4) {
unsigned k = (n - 2) * (n - 1) * (2 * n - 3) - sk21;
k -= 3 * (w0 - 2) * (14 * n - 7 * w0 - 31);
v -= k;
}
if(n - 2 > wmax) {
unsigned wm20 = 2 * wmax * (wmax + 1);
unsigned wm21 = (2 * wmax + 1);
unsigned k = sk20;
if(n0 > wmax) {
k -= sk21;
k += 3 * (w0 - 1) * (wm20 - wm21 * (2 * n - w0));
}
else {
k -= (wmax + 1) * (wmax + 2) * (2 * wmax + 3);
k += 3 * (n - wmax - 2) * (wm20 - wm21 * (n + wmax + 1));
}
k *= 3;
v -= k;
}
v /= 12;
}
else
nonarrow = 1;
n -= w0;
unsigned w1 = (sig >> 8) & 0xf;
if(w1 > 1) {
if(w1 > wmax)
return(-1);
v += (2 * n - w1) * (w1 - 1) / 2;
if(!nonarrow && w1 > 2 && n > 3)
v -= (2 * n - w1 - 5) * (w1 - 2) / 2;
if(n - 1 > wmax) {
if(n - w1 > wmax)
v -= (w1 - 1) * (2 * n - w1 - 2 * wmax);
else
v -= (n - wmax) * (n - wmax - 1);
}
}
else
nonarrow = 1;
n -= w1;
unsigned w2 = (sig >> 4) & 0xf;
if(w2 > 1) {
if(w2 > wmax)
return(-1);
v += w2 - 1;
if(!nonarrow && w2 > 2 && n > 2)
v -= n - 2;
if(n > wmax)
v -= n - wmax;
}
else
nonarrow = 1;
unsigned w3 = sig & 0xf;
if(w3 == 1)
nonarrow = 1;
else if(w3 > wmax)
return(-1);
if(!nonarrow)
return(-1);
return(v);
}
static inline zbar_symbol_type_t
decode_char (zbar_decoder_t *dcode,
databar_segment_t *seg,
int off,
int dir)
{
databar_decoder_t *db = &dcode->databar;
unsigned s = calc_s(dcode, (dir > 0) ? off : off - 6, 8);
int n, i, emin[2] = { 0, }, sum = 0;
unsigned sig0 = 0, sig1 = 0;
if(seg->exp)
n = 17;
else if(seg->side)
n = 15;
else
n = 16;
emin[1] = -n;
dbprintf(2, "\n char[%c%d]: n=%d s=%d w=%d sig=",
(dir < 0) ? '>' : '<', off, n, s, seg->width);
if(s < 13 || !check_width(seg->width, s, n))
return(ZBAR_NONE);
for(i = 4; --i >= 0; ) {
int e = decode_e(pair_width(dcode, off), s, n);
if(e < 0)
return(ZBAR_NONE);
dbprintf(2, "%d", e);
sum = e - sum;
off += dir;
sig1 <<= 4;
if(emin[1] < -sum)
emin[1] = -sum;
sig1 += sum;
if(!i)
break;
e = decode_e(pair_width(dcode, off), s, n);
if(e < 0)
return(ZBAR_NONE);
dbprintf(2, "%d", e);
sum = e - sum;
off += dir;
sig0 <<= 4;
if(emin[0] > sum)
emin[0] = sum;
sig0 += sum;
}
int diff = emin[~n & 1];
diff = diff + (diff << 4);
diff = diff + (diff << 8);
sig0 -= diff;
sig1 += diff;
dbprintf(2, " emin=%d,%d el=%04x/%04x", emin[0], emin[1], sig0, sig1);
unsigned sum0 = sig0 + (sig0 >> 8);
unsigned sum1 = sig1 + (sig1 >> 8);
sum0 += sum0 >> 4;
sum1 += sum1 >> 4;
sum0 &= 0xf;
sum1 &= 0xf;
dbprintf(2, " sum=%d/%d", sum0, sum1);
if(sum0 + sum1 + 8 != n) {
dbprintf(2, " [SUM]");
return(ZBAR_NONE);
}
if(((sum0 ^ (n >> 1)) | (sum1 ^ (n >> 1) ^ n)) & 1) {
dbprintf(2, " [ODD]");
return(ZBAR_NONE);
}
i = ((n & 0x3) ^ 1) * 5 + (sum1 >> 1);
zassert(i < sizeof(groups) / sizeof(*groups), -1,
"n=%d sum=%d/%d sig=%04x/%04x g=%d",
n, sum0, sum1, sig0, sig1, i);
struct group_s *g = groups + i;
dbprintf(2, "\n g=%d(%d,%d,%d/%d)",
i, g->sum, g->wmax, g->todd, g->teven);
int vodd = calc_value4(sig0 + 0x1111, sum0 + 4, g->wmax, ~n & 1);
dbprintf(2, " v=%d", vodd);
if(vodd < 0 || vodd > g->todd)
return(ZBAR_NONE);
int veven = calc_value4(sig1 + 0x1111, sum1 + 4, 9 - g->wmax, n & 1);
dbprintf(2, "/%d", veven);
if(veven < 0 || veven > g->teven)
return(ZBAR_NONE);
int v = g->sum;
if(n & 2)
v += vodd + veven * g->todd;
else
v += veven + vodd * g->teven;
dbprintf(2, " f=%d(%x%x%x)", seg->finder, seg->exp, seg->color, seg->side);
unsigned chk = 0;
if(seg->exp) {
unsigned side = seg->color ^ seg->side ^ 1;
if(v >= 4096)
return(ZBAR_NONE);
/* skip A1 left */
chk = calc_check(sig0, sig1, side, 211);
if(seg->finder || seg->color || seg->side) {
i = (seg->finder << 1) - side + seg->color;
zassert(i >= 0 && i < 12, ZBAR_NONE,
"f=%d(%x%x%x) side=%d i=%d\n",
seg->finder, seg->exp, seg->color, seg->side, side, i);
chk = (chk * exp_checksums[i]) % 211;
}
else if(v >= 4009)
return(ZBAR_NONE);
else
chk = 0;
}
else {
chk = calc_check(sig0, sig1, seg->side, 79);
if(seg->color)
chk = (chk * 16) % 79;
}
dbprintf(2, " => %d val=%d", chk, v);
seg->check = chk;
seg->data = v;
merge_segment(db, seg);
if(seg->exp)
return(match_segment_exp(dcode, seg, dir));
else if(dir > 0)
return(match_segment(dcode, seg));
return(ZBAR_PARTIAL);
}
static inline int
alloc_segment (databar_decoder_t *db)
{
unsigned maxage = 0, csegs = db->csegs;
int i, old = -1;
for(i = 0; i < csegs; i++) {
databar_segment_t *seg = db->segs + i;
unsigned age;
if(seg->finder < 0) {
dbprintf(2, " free@%d", i);
return(i);
}
age = (db->epoch - seg->epoch) & 0xff;
if(age >= 128 && seg->count < 2) {
seg->finder = -1;
dbprintf(2, " stale@%d (%d - %d = %d)",
i, db->epoch, seg->epoch, age);
return(i);
}
/* score based on both age and count */
if(age > seg->count)
age = age - seg->count + 1;
else
age = 1;
if(maxage < age) {
maxage = age;
old = i;
dbprintf(2, " old@%d(%u)", i, age);
}
}
if(csegs < DATABAR_MAX_SEGMENTS) {
dbprintf(2, " new@%d", i);
i = csegs;
csegs *= 2;
if(csegs > DATABAR_MAX_SEGMENTS)
csegs = DATABAR_MAX_SEGMENTS;
if(csegs != db->csegs) {
databar_segment_t *seg;
db->segs = realloc(db->segs, csegs * sizeof(*db->segs));
db->csegs = csegs;
seg = db->segs + csegs;
while(--seg, --csegs >= i) {
seg->finder = -1;
seg->exp = 0;
seg->color = 0;
seg->side = 0;
seg->partial = 0;
seg->count = 0;
seg->epoch = 0;
seg->check = 0;
}
return(i);
}
}
zassert(old >= 0, -1, "\n");
db->segs[old].finder = -1;
return(old);
}
static inline zbar_symbol_type_t
decode_finder (zbar_decoder_t *dcode)
{
databar_decoder_t *db = &dcode->databar;
databar_segment_t *seg;
unsigned e0 = pair_width(dcode, 1);
unsigned e2 = pair_width(dcode, 3);
unsigned e1, e3, s, finder, dir;
int sig, iseg;
dbprintf(2, " databar: e0=%d e2=%d", e0, e2);
if(e0 < e2) {
unsigned e = e2 * 4;
if(e < 15 * e0 || e > 34 * e0)
return(ZBAR_NONE);
dir = 0;
e3 = pair_width(dcode, 4);
}
else {
unsigned e = e0 * 4;
if(e < 15 * e2 || e > 34 * e2)
return(ZBAR_NONE);
dir = 1;
e2 = e0;
e3 = pair_width(dcode, 0);
}
e1 = pair_width(dcode, 2);
s = e1 + e3;
dbprintf(2, " e1=%d e3=%d dir=%d s=%d", e1, e3, dir, s);
if(s < 12)
return(ZBAR_NONE);
sig = ((decode_e(e3, s, 14) << 8) | (decode_e(e2, s, 14) << 4) |
decode_e(e1, s, 14));
dbprintf(2, " sig=%04x", sig & 0xfff);
if(sig < 0 ||
((sig >> 4) & 0xf) < 8 ||
((sig >> 4) & 0xf) > 10 ||
(sig & 0xf) >= 10 ||
((sig >> 8) & 0xf) >= 10 ||
(((sig >> 8) + sig) & 0xf) != 10)
return(ZBAR_NONE);
finder = (finder_hash[(sig - (sig >> 5)) & 0x1f] +
finder_hash[(sig >> 1) & 0x1f]) & 0x1f;
dbprintf(2, " finder=%d", finder);
if(finder == 0x1f ||
!TEST_CFG((finder < 9) ? db->config : db->config_exp, ZBAR_CFG_ENABLE))
return(ZBAR_NONE);
zassert(finder >= 0, ZBAR_NONE, "dir=%d sig=%04x f=%d\n",
dir, sig & 0xfff, finder);
iseg = alloc_segment(db);
if(iseg < 0)
return(ZBAR_NONE);
seg = db->segs + iseg;
seg->finder = (finder >= 9) ? finder - 9 : finder;
seg->exp = (finder >= 9);
seg->color = get_color(dcode) ^ dir ^ 1;
seg->side = dir;
seg->partial = 0;
seg->count = 1;
seg->width = s;
seg->epoch = db->epoch;
int rc = decode_char(dcode, seg, 12 - dir, -1);
if(!rc)
seg->partial = 1;
else
db->epoch++;
int i = (dcode->idx + 8 + dir) & 0xf;
zassert(db->chars[i] == -1, ZBAR_NONE, "\n");
db->chars[i] = iseg;
return(rc);
}
zbar_symbol_type_t
_zbar_decode_databar (zbar_decoder_t *dcode)
{
databar_decoder_t *db = &dcode->databar;
databar_segment_t *seg, *pair;
zbar_symbol_type_t sym;
int iseg, i = dcode->idx & 0xf;
sym = decode_finder(dcode);
dbprintf(2, "\n");
iseg = db->chars[i];
if(iseg < 0)
return(sym);
db->chars[i] = -1;
seg = db->segs + iseg;
dbprintf(2, " databar: i=%d part=%d f=%d(%x%x%x)",
iseg, seg->partial, seg->finder, seg->exp, seg->color, seg->side);
zassert(seg->finder >= 0, ZBAR_NONE, "i=%d f=%d(%x%x%x) part=%x\n",
iseg, seg->finder, seg->exp, seg->color, seg->side, seg->partial);
if(seg->partial) {
pair = NULL;
seg->side = !seg->side;
}
else {
int jseg = alloc_segment(db);
pair = db->segs + iseg;
seg = db->segs + jseg;
seg->finder = pair->finder;
seg->exp = pair->exp;
seg->color = pair->color;
seg->side = !pair->side;
seg->partial = 0;
seg->count = 1;
seg->width = pair->width;
seg->epoch = db->epoch;
}
sym = decode_char(dcode, seg, 1, 1);
if(!sym) {
seg->finder = -1;
if(pair)
pair->partial = 1;
}
else
db->epoch++;
dbprintf(2, "\n");
return(sym);
}