/*------------------------------------------------------------------------ * Copyright 2010 (c) Jeff Brown * * 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 #include #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); }