diff options
Diffstat (limited to 'lpg/libqr')
-rw-r--r-- | lpg/libqr/.gitignore | 6 | ||||
-rw-r--r-- | lpg/libqr/COPYING | 15 | ||||
-rw-r--r-- | lpg/libqr/Makefile | 33 | ||||
-rw-r--r-- | lpg/libqr/TODO | 11 | ||||
-rw-r--r-- | lpg/libqr/bitmap.c | 216 | ||||
-rw-r--r-- | lpg/libqr/bitstream.c | 243 | ||||
-rw-r--r-- | lpg/libqr/code-common.c | 109 | ||||
-rw-r--r-- | lpg/libqr/code-create.c | 565 | ||||
-rw-r--r-- | lpg/libqr/code-layout.c | 188 | ||||
-rw-r--r-- | lpg/libqr/code-parse.c | 348 | ||||
-rw-r--r-- | lpg/libqr/constants.c | 151 | ||||
-rw-r--r-- | lpg/libqr/constants.h | 29 | ||||
-rw-r--r-- | lpg/libqr/data-common.c | 38 | ||||
-rw-r--r-- | lpg/libqr/data-create.c | 273 | ||||
-rw-r--r-- | lpg/libqr/data-parse.c | 240 | ||||
-rw-r--r-- | lpg/libqr/galois.c | 125 | ||||
-rw-r--r-- | lpg/libqr/galois.h | 11 | ||||
-rw-r--r-- | lpg/libqr/qr/bitmap.h | 37 | ||||
-rw-r--r-- | lpg/libqr/qr/bitstream.h | 57 | ||||
-rw-r--r-- | lpg/libqr/qr/code.h | 25 | ||||
-rw-r--r-- | lpg/libqr/qr/common.h | 32 | ||||
-rw-r--r-- | lpg/libqr/qr/data.h | 41 | ||||
-rw-r--r-- | lpg/libqr/qr/layout.h | 22 | ||||
-rw-r--r-- | lpg/libqr/qr/parse.h | 24 | ||||
-rw-r--r-- | lpg/libqr/qr/types.h | 34 | ||||
-rw-r--r-- | lpg/libqr/qr/version.h | 18 | ||||
-rw-r--r-- | lpg/libqr/qrgen.c | 402 | ||||
-rw-r--r-- | lpg/libqr/qrparse.c | 59 | ||||
-rw-r--r-- | lpg/libqr/testqr.xbm | 19 |
29 files changed, 3371 insertions, 0 deletions
diff --git a/lpg/libqr/.gitignore b/lpg/libqr/.gitignore new file mode 100644 index 0000000..33bcf18 --- /dev/null +++ b/lpg/libqr/.gitignore @@ -0,0 +1,6 @@ +*~ +*.o +*.a +*.so +qrgen +qrparse diff --git a/lpg/libqr/COPYING b/lpg/libqr/COPYING new file mode 100644 index 0000000..ff2f585 --- /dev/null +++ b/lpg/libqr/COPYING @@ -0,0 +1,15 @@ +I, the author, hereby release this work into the public domain. + +Where the above is not possible, I release this work as "Copyright +Free", or (at your option) under the terms of the Creative Commons CC0 +license: + + http://creativecommons.org/publicdomain/zero/1.0/ + +The author waives all rights concerning the modification, reproduction +and/or distribution of this work, or derivatives thereof, in whole or +in part, and in any form. Attribution is not required. + + +Leo Howell, September 2009 + diff --git a/lpg/libqr/Makefile b/lpg/libqr/Makefile new file mode 100644 index 0000000..176c30c --- /dev/null +++ b/lpg/libqr/Makefile @@ -0,0 +1,33 @@ +# XXX: This Makefile only works single-threaded, -j1 is required. +OBJECTS := bitmap.o \ + bitstream.o \ + constants.o \ + code-common.o \ + code-create.o \ + code-layout.o \ + code-parse.o \ + data-common.o \ + data-create.o \ + data-parse.o \ + galois.o + +CFLAGS := -std=c89 -pedantic -I. -Wall +CFLAGS += -g +#CFLAGS += -O3 -DNDEBUG + +all : libqr qrgen qrparse + +$(OBJECTS) : $(wildcard *.h qr/*.h) + +libqr : libqr.a($(OBJECTS)) + +qrgen : libqr qrgen.c + $(CC) $(CFLAGS) -o qrgen qrgen.c libqr.a $(shell pkg-config libpng --cflags --libs) + +qrparse : libqr qrparse.c + $(CC) $(CFLAGS) -o qrparse qrparse.c libqr.a + +.PHONY : clean +clean: + $(RM) qr/*~ *~ *.o *.a *.so qrgen + diff --git a/lpg/libqr/TODO b/lpg/libqr/TODO new file mode 100644 index 0000000..1b9671b --- /dev/null +++ b/lpg/libqr/TODO @@ -0,0 +1,11 @@ +* Parsing + +* Detection + +* Test suite +* No-malloc +* Optimize +* Documentation + +* Other data types + diff --git a/lpg/libqr/bitmap.c b/lpg/libqr/bitmap.c new file mode 100644 index 0000000..246b9bd --- /dev/null +++ b/lpg/libqr/bitmap.c @@ -0,0 +1,216 @@ +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <qr/bitmap.h> + +struct qr_bitmap * qr_bitmap_create(size_t width, size_t height, int masked) +{ + struct qr_bitmap * out; + size_t size; + + out = malloc(sizeof(*out)); + if (!out) + goto fail; + + out->width = width; + out->height = height; + out->stride = (width / CHAR_BIT) + (width % CHAR_BIT ? 1 : 0); + + size = out->stride * height; + + out->mask = 0; + out->bits = malloc(size); + if (!out->bits) + goto fail; + memset(out->bits, 0, size); + + if (masked) { + out->mask = malloc(out->stride * width); + if (!out->mask) + goto fail; + memset(out->mask, 0xFF, size); + } + + return out; + +fail: + qr_bitmap_destroy(out); + return 0; +} + +void qr_bitmap_destroy(struct qr_bitmap * bmp) +{ + if (bmp) { + free(bmp->bits); + free(bmp->mask); + free(bmp); + } +} + +int qr_bitmap_add_mask(struct qr_bitmap * bmp) +{ + size_t size = bmp->stride * bmp->width; + bmp->mask = malloc(size); + if (!bmp->mask) + return -1; + memset(bmp->mask, 0xFF, size); + return 0; +} + +struct qr_bitmap * qr_bitmap_clone(const struct qr_bitmap * src) +{ + struct qr_bitmap * bmp; + size_t size; + + bmp = qr_bitmap_create(src->width, src->height, !!src->mask); + if (!bmp) + return 0; + + assert(bmp->stride == src->stride); + + size = bmp->width * bmp->stride; + memcpy(bmp->bits, src->bits, size); + if (bmp->mask) + memcpy(bmp->mask, src->mask, size); + + return bmp; +} + +void qr_bitmap_merge(struct qr_bitmap * dest, const struct qr_bitmap * src) +{ + unsigned char * d, * s, * m; + size_t n; + + assert(dest->stride == src->stride); + assert(dest->height == src->height); + assert(src->mask); + + n = dest->stride * dest->height; + d = dest->bits; + s = src->bits; + m = src->mask; + + while (n--) { + *d &= ~*m; + *d++ |= *s++ & *m++; + } +} + +/* CHAR_BIT | mod_bits (multi-byte) */ +static void render_line_1(unsigned char * out, + const unsigned char * in, + size_t mod_bits, + size_t dim, + unsigned long mark, + unsigned long space) +{ + unsigned char in_mask; + size_t n, b; + + in_mask = 1; + n = dim; + + while (n-- > 0) { + unsigned long v = (*in & in_mask) ? mark : space; + + if ((in_mask <<= 1) == 0) { + in_mask = 1; + ++in; + } + + b = mod_bits / CHAR_BIT; + while (b-- > 0) { + *out++ = (unsigned char) v; + v >>= CHAR_BIT; + } + } +} + +/* mod_bits | CHAR_BIT (packed) */ +static void render_line_2(unsigned char * out, + const unsigned char * in, + size_t mod_bits, + size_t dim, + unsigned long mark, + unsigned long space) +{ + unsigned char in_mask; + size_t n, b, step; + + in_mask = 1; + step = CHAR_BIT / mod_bits; + n = dim; + + while (n > 0) { + unsigned char tmp = 0; + + b = step; + while (b-- > 0) { + unsigned long v = (*in & in_mask) ? mark : space; + + if ((in_mask <<= 1) == 0) { + in_mask = 1; + ++in; + } + + tmp |= v << (b * mod_bits); + if (--n == 0) + break; + }; + + *out++ = tmp; + } +} + +void qr_bitmap_render(const struct qr_bitmap * bmp, + void * buffer, + int mod_bits, + long line_stride, + int line_repeat, + unsigned long mark, + unsigned long space) +{ + unsigned char * out; + const unsigned char * in; + size_t n, dim; + int pack; + + pack = (mod_bits < CHAR_BIT); + assert(!pack || (CHAR_BIT % mod_bits == 0)); + assert( pack || (mod_bits % CHAR_BIT == 0)); + + in = bmp->bits; + out = buffer; + dim = bmp->width; + + if (pack) { + mark &= (1 << mod_bits) - 1; + space &= (1 << mod_bits) - 1; + } + + n = dim; + while (n-- > 0) { + size_t rpt; + unsigned char * next; + + if (pack) + render_line_2(out, in, mod_bits, dim, mark, space); + else + render_line_1(out, in, mod_bits, dim, mark, space); + + rpt = line_repeat; + next = out + line_stride; + while (--rpt > 0) { + size_t bits = dim * mod_bits; + size_t bytes = bits / CHAR_BIT + !!(bits % CHAR_BIT); + memcpy(next, out, bytes); + next += line_stride; + } + + in += bmp->stride; + out = next; + } +} + diff --git a/lpg/libqr/bitstream.c b/lpg/libqr/bitstream.c new file mode 100644 index 0000000..cc8a1ae --- /dev/null +++ b/lpg/libqr/bitstream.c @@ -0,0 +1,243 @@ +/** + * It would perhaps be more sensible just to store the bits + * as an array of char or similar, but this way is more fun. + * This is a pretty inefficient implementation, althought I + * suspect that won't be a problem. + */ + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <assert.h> + +#include <qr/bitstream.h> + +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +struct qr_bitstream { + size_t pos; /* bits */ + size_t count; /* bits */ + size_t bufsiz; /* bytes */ + unsigned char * buffer; +}; + +static size_t bits_to_bytes(size_t bits) +{ + return (bits / CHAR_BIT) + (bits % CHAR_BIT != 0); +} + +static int ensure_available(struct qr_bitstream * stream, size_t bits) +{ + size_t need_bits = stream->pos + bits; + size_t newsize; + + if (stream->bufsiz * CHAR_BIT >= need_bits) + return 0; + + newsize = MAX(stream->bufsiz, 100) * CHAR_BIT; + while (newsize < need_bits) + newsize *= 2; + + return qr_bitstream_resize(stream, newsize); +} + +struct qr_bitstream * qr_bitstream_create(void) +{ + struct qr_bitstream * obj; + + obj = malloc(sizeof(*obj)); + + if (obj) { + obj->pos = 0; + obj->count = 0; + obj->bufsiz = 0; + obj->buffer = 0; + } + + return obj; +} + +int qr_bitstream_resize(struct qr_bitstream * stream, size_t bits) +{ + size_t newsize; + void * newbuf; + + newsize = bits_to_bytes(bits); + newbuf = realloc(stream->buffer, newsize); + + if (newbuf) { + stream->bufsiz = newsize; + stream->buffer = newbuf; + } + + return newbuf ? 0 : -1; +} + +void qr_bitstream_destroy(struct qr_bitstream * stream) +{ + free(stream->buffer); + free(stream); +} + +struct qr_bitstream * qr_bitstream_dup(const struct qr_bitstream * src) +{ + struct qr_bitstream * ret; + + ret = qr_bitstream_create(); + if (!ret) + return 0; + + if (qr_bitstream_resize(ret, src->count) != 0) { + free(ret); + return 0; + } + + ret->pos = src->pos; + ret->count = src->count; + memcpy(ret->buffer, src->buffer, src->bufsiz); + + return ret; +} + +void qr_bitstream_seek(struct qr_bitstream * stream, size_t pos) +{ + assert(pos <= stream->count); + stream->pos = pos; +} + +size_t qr_bitstream_tell(const struct qr_bitstream * stream) +{ + return stream->pos; +} + +size_t qr_bitstream_remaining(const struct qr_bitstream * stream) +{ + return stream->count - stream->pos; +} + +size_t qr_bitstream_size(const struct qr_bitstream * stream) +{ + return stream->count; +} + +unsigned long qr_bitstream_read(struct qr_bitstream * stream, int bits) +{ + unsigned long result = 0; + unsigned char * byte; + size_t bitnum; + + assert(qr_bitstream_remaining(stream) >= (size_t) bits); + + byte = stream->buffer + (stream->pos / CHAR_BIT); + bitnum = stream->pos % CHAR_BIT; + + stream->pos += bits; + + while (bits-- > 0) { + int bit = (*byte >> bitnum++) & 0x1; + result = (result << 1) | bit; + if (bitnum == CHAR_BIT) { + bitnum = 0; + ++byte; + } + } + + return result; +} + +void qr_bitstream_unpack(struct qr_bitstream * stream, + unsigned int * result, + size_t count, + int bitsize) +{ + assert(qr_bitstream_remaining(stream) >= (count * bitsize)); + + while (count--) + *(result++) = qr_bitstream_read(stream, bitsize); +} + +int qr_bitstream_write(struct qr_bitstream * stream, + unsigned long value, + int bits) +{ + unsigned char * byte; + size_t bitnum; + + if (ensure_available(stream, bits) != 0) + return -1; + + byte = stream->buffer + (stream->pos / CHAR_BIT); + bitnum = stream->pos % CHAR_BIT; + + stream->pos += bits; + stream->count = stream->pos; /* truncate */ + + while (bits-- > 0) { + int bit = (value >> bits) & 0x1; + unsigned char mask = 1 << bitnum++; + *byte = (*byte & ~mask) | (bit ? mask : 0); + if (bitnum == CHAR_BIT) { + bitnum = 0; + ++byte; + } + } + + return 0; +} + +int qr_bitstream_pack(struct qr_bitstream * stream, + const unsigned int * values, + size_t count, + int bitsize) +{ + if (ensure_available(stream, count * bitsize) != 0) + return -1; + + while (count--) + qr_bitstream_write(stream, *(values++), bitsize); + + return 0; +} + +int qr_bitstream_cat(struct qr_bitstream * dest, const struct qr_bitstream * src) +{ + size_t count = qr_bitstream_size(src); + size_t srcpos; + + if (ensure_available(dest, count) != 0) + return -1; + + srcpos = qr_bitstream_tell(src); + qr_bitstream_seek((struct qr_bitstream *)src, 0); + qr_bitstream_copy(dest, (struct qr_bitstream *)src, count); + qr_bitstream_seek((struct qr_bitstream *)src, srcpos); + + return 0; +} + +int qr_bitstream_copy(struct qr_bitstream * dest, + struct qr_bitstream * src, + size_t count) +{ + if (qr_bitstream_remaining(src) < count) + return -1; + if (ensure_available(dest, count) != 0) + return -1; + + /* uint must be at least 16 bits */ + for (; count >= 16; count -= 16) + qr_bitstream_write( + dest, + qr_bitstream_read((struct qr_bitstream *)src, 16), + 16); + + if (count > 0) + qr_bitstream_write( + dest, + qr_bitstream_read((struct qr_bitstream *)src, count), + count); + + return 0; +} + diff --git a/lpg/libqr/code-common.c b/lpg/libqr/code-common.c new file mode 100644 index 0000000..babaf86 --- /dev/null +++ b/lpg/libqr/code-common.c @@ -0,0 +1,109 @@ +#include <assert.h> +#include <limits.h> +#include <stdlib.h> + +#include <qr/bitmap.h> +#include <qr/bitstream.h> +#include <qr/code.h> +#include <qr/common.h> +#include "constants.h" + +void qr_code_destroy(struct qr_code * code) +{ + if (code) { + qr_bitmap_destroy(code->modules); + free(code); + } +} + +int qr_code_width(const struct qr_code * code) +{ + return code->version * 4 + 17; +} + +size_t qr_code_total_capacity(int version) +{ + int side = version * 4 + 17; + + int alignment_side = version > 1 ? (version / 7) + 2 : 0; + + int alignment_count = alignment_side >= 2 ? + alignment_side * alignment_side - 3 : 0; + + int locator_bits = 8*8*3; + + int format_bits = 8*4 - 1 + (version >= 7 ? 6*3*2 : 0); + + int timing_bits = 2 * (side - 8*2 - + (alignment_side > 2 ? (alignment_side - 2) * 5 : 0)); + + int function_bits = timing_bits + format_bits + locator_bits + + alignment_count * 5*5; + + return side * side - function_bits; +} + +void qr_get_rs_block_sizes(int version, + enum qr_ec_level ec, + int block_count[2], + int data_length[2], + int ec_length[2]) +{ + /* FIXME: rather than using a big static table, better to + * calculate these values. + */ + int total_words = qr_code_total_capacity(version) / 8; + int data_words = QR_DATA_WORD_COUNT[version - 1][ec ^ 0x1]; + int ec_words = total_words - data_words; + int total_blocks; + + block_count[0] = QR_RS_BLOCK_COUNT[version - 1][ec ^ 0x1][0]; + block_count[1] = QR_RS_BLOCK_COUNT[version - 1][ec ^ 0x1][1]; + total_blocks = block_count[0] + block_count[1]; + + data_length[0] = data_words / total_blocks; + data_length[1] = data_length[0] + 1; + + assert(data_length[0] * block_count[0] + + data_length[1] * block_count[1] == data_words); + + ec_length[0] = ec_length[1] = ec_words / total_blocks; + + assert(ec_length[0] * block_count[0] + + ec_length[1] * block_count[1] == ec_words); + assert(data_words + ec_words == total_words); +} + +void qr_mask_apply(struct qr_bitmap * bmp, int mask) +{ + size_t i, j; + + assert((mask & 0x7) == mask); + mask &= 0x7; + + /* Slow version for now; we can optimize later */ + + for (i = 0; i < bmp->height; ++i) { + unsigned char * p = bmp->bits + i * bmp->stride; + + for (j = 0; j < bmp->width; ++j) { + int bit = j % CHAR_BIT; + size_t off = j / CHAR_BIT; + int t; + + switch (mask) { + case 0: t = (i + j) % 2; break; + case 1: t = i % 2; break; + case 2: t = j % 3; break; + case 3: t = (i + j) % 3; break; + case 4: t = (i/2 + j/3) % 2; break; + case 5: t = ((i*j) % 2) + ((i*j) % 3); break; + case 6: t = (((i*j) % 2) + ((i*j) % 3)) % 2; break; + case 7: t = (((i*j) % 3) + ((i+j) % 2)) % 2; break; + } + + p[off] ^= (t == 0) << bit; + } + } +} + diff --git a/lpg/libqr/code-create.c b/lpg/libqr/code-create.c new file mode 100644 index 0000000..f64e42d --- /dev/null +++ b/lpg/libqr/code-create.c @@ -0,0 +1,565 @@ +#include <stdlib.h> +#include <limits.h> +#include <assert.h> + +#include <qr/bitmap.h> +#include <qr/bitstream.h> +#include <qr/code.h> +#include <qr/common.h> +#include <qr/data.h> +#include <qr/layout.h> +#include "constants.h" +#include "galois.h" + +#define MIN(a, b) ((b) < (a) ? (b) : (a)) + +static int mask_data(struct qr_code * code); +static int score_mask(const struct qr_bitmap * bmp); +static int score_runs(const struct qr_bitmap * bmp, int base); +static int count_2blocks(const struct qr_bitmap * bmp); +static int count_locators(const struct qr_bitmap * bmp); +static int calc_bw_balance(const struct qr_bitmap * bmp); +static int get_px(const struct qr_bitmap * bmp, int x, int y); +static int get_mask(const struct qr_bitmap * bmp, int x, int y); +static int draw_format(struct qr_bitmap * bmp, + struct qr_code * code, + enum qr_ec_level ec, + int mask); +static unsigned int calc_format_bits(enum qr_ec_level ec, int mask); +static unsigned long calc_version_bits(int version); + +static void setpx(struct qr_bitmap * bmp, int x, int y) +{ + size_t off = y * bmp->stride + x / CHAR_BIT; + unsigned char bit = 1 << (x % CHAR_BIT); + + bmp->bits[off] |= bit; +} + +static void draw_locator(struct qr_bitmap * bmp, int x, int y) +{ + int i; + for (i = 0; i < 6; ++i) { + setpx(bmp, x + i, y + 0); + setpx(bmp, x + 6, y + i); + setpx(bmp, x + i + 1, y + 6); + setpx(bmp, x, y + i + 1); + } + for (i = 0; i < 9; ++i) + setpx(bmp, x + 2 + i % 3, y + 2 + i / 3); +} + +static int draw_functional(struct qr_code * code, + enum qr_ec_level ec, + unsigned int mask) +{ + struct qr_bitmap * bmp; + int dim = qr_code_width(code); + int i; + int x, y; + int am_side; + + bmp = qr_bitmap_create(dim, dim, 0); + if (!bmp) + return -1; + + /* Locator pattern */ + draw_locator(bmp, 0, 0); + draw_locator(bmp, 0, dim - 7); + draw_locator(bmp, dim - 7, 0); + + /* Timing pattern */ + for (i = 8; i < dim - 8; i += 2) { + setpx(bmp, i, 6); + setpx(bmp, 6, i); + } + + /* Alignment pattern */ + am_side = code->version > 1 ? (code->version / 7) + 2 : 0; + for (y = 0; y < am_side; ++y) { + const int * am_pos = QR_ALIGNMENT_LOCATION[code->version - 1]; + + for (x = 0; x < am_side; ++x) { + if ((x == 0 && y == 0) || + (x == 0 && y == am_side - 1) || + (x == am_side - 1 && y == 0)) + continue; + + for (i = -2; i < 2; ++i) { + setpx(bmp, am_pos[x] + i, am_pos[y] - 2); + setpx(bmp, am_pos[x] + 2, am_pos[y] + i); + setpx(bmp, am_pos[x] - i, am_pos[y] + 2); + setpx(bmp, am_pos[x] - 2, am_pos[y] - i); + } + setpx(bmp, am_pos[x], am_pos[y]); + } + } + + /* Format info */ + setpx(bmp, 8, dim - 8); + if (draw_format(bmp, code, ec, mask) != 0) + return -1; + + /* Merge data */ + qr_bitmap_merge(bmp, code->modules); + qr_bitmap_destroy(code->modules); + code->modules = bmp; + + return 0; +} + +static int pad_data(struct qr_bitstream * bits, size_t limit) +{ + /* This function is not very nice. Sorry. */ + + size_t count, n; + + assert(qr_bitstream_size(bits) <= limit); + + if (qr_bitstream_resize(bits, limit) != 0) + return -1; + + n = qr_bitstream_size(bits); + qr_bitstream_seek(bits, n); + count = limit - n; + + /* First append the terminator (0000) if possible, + * and pad with zeros up to an 8-bit boundary + */ + n = (n + 4) % 8; + if (n != 0) + n = 8 - n; + n = MIN(count, n + 4); + qr_bitstream_write(bits, 0, n); + count -= n; + + assert(count % 8 == 0); /* since data codewords are 8 bits */ + + /* Finally pad with the repeating sequence 11101100 00010001 */ + while (count >= 16) { + qr_bitstream_write(bits, 0xEC11, 16); + count -= 16; + } + if (count > 0) { + assert(count == 8); + qr_bitstream_write(bits, 0xEC, 8); + } + + return 0; +} + +static struct qr_bitstream * make_data(int version, + enum qr_ec_level ec, + struct qr_bitstream * data) +{ + const size_t total_bits = qr_code_total_capacity(version); + const size_t total_data = QR_DATA_WORD_COUNT[version - 1][ec ^ 0x1]; + int block_count[2], data_length[2], ec_length[2]; + int total_blocks; + int i, w; + struct qr_bitstream * dcopy = 0; + struct qr_bitstream * out = 0; + struct qr_bitstream ** blocks = 0; + + /* Set up the output stream */ + out = qr_bitstream_create(); + if (!out) + return 0; + + if (qr_bitstream_resize(out, total_bits) != 0) + goto fail; + + qr_get_rs_block_sizes(version, ec, block_count, data_length, ec_length); + total_blocks = block_count[0] + block_count[1]; + + /* Make a copy of the data and pad it */ + dcopy = qr_bitstream_dup(data); + if (!dcopy) + goto fail; + + if (pad_data(dcopy, total_data * QR_WORD_BITS) != 0) + goto fail; + + /* Make space for the RS blocks */ + blocks = malloc(total_blocks * sizeof(*blocks)); + if (!blocks) + goto fail; + for (i = 0; i < total_blocks; ++i) + blocks[i] = NULL; + + /* Generate RS codewords */ + qr_bitstream_seek(dcopy, 0); + for (i = 0; i < total_blocks; ++i) { + int type = (i >= block_count[0]); + blocks[i] = rs_generate_words(dcopy, + data_length[type], + ec_length[type]); + if (!blocks[i]) { + while (i--) + qr_bitstream_destroy(blocks[i]); + free(blocks); + blocks = 0; + goto fail; + } + } + + /* Finally, write everything out in the correct order */ + assert(block_count[1] == 0 || data_length[1] >= data_length[0]); + for (w = 0; w < data_length[block_count[1] ? 1 : 0]; ++w) { + for (i = (w >= data_length[0] ? block_count[0] : 0); i < total_blocks; ++i) { + long di = w + i * data_length[0] + + (i > block_count[0] ? + (i - block_count[0]) * (data_length[1] - data_length[0]) + : 0); + + qr_bitstream_seek(dcopy, di * QR_WORD_BITS); + qr_bitstream_copy(out, dcopy, QR_WORD_BITS); + } + } + for (i = 0; i < total_blocks; ++i) + qr_bitstream_seek(blocks[i], 0); + assert(block_count[1] == 0 || ec_length[1] == ec_length[0]); + for (w = 0; w < ec_length[0]; ++w) + for (i = 0; i < total_blocks; ++i) + qr_bitstream_copy(out, blocks[i], QR_WORD_BITS); + + qr_bitstream_write(out, 0, total_bits % QR_WORD_BITS); + +exit: + if (blocks) { + while (total_blocks--) + qr_bitstream_destroy(blocks[total_blocks]); + free(blocks); + } + if (dcopy) + qr_bitstream_destroy(dcopy); + + return out; + +fail: + qr_bitstream_destroy(out); + out = 0; + goto exit; +} + +struct qr_code * qr_code_create(const struct qr_data * data) +{ + struct qr_code * code; + struct qr_bitstream * bits = 0; + struct qr_iterator * layout; + int mask; + size_t dim; + + code = malloc(sizeof(*code)); + if (!code) + return 0; + + code->version = data->version; + dim = qr_code_width(code); + code->modules = qr_bitmap_create(dim, dim, 1); + + if (!code->modules) + goto fail; + + bits = make_data(data->version, data->ec, data->bits); + if (!bits) + goto fail; + + qr_layout_init_mask(code); + + layout = qr_layout_begin(code); + if (!layout) + goto fail; + + qr_bitstream_seek(bits, 0); + while (qr_bitstream_remaining(bits) >= (size_t) QR_WORD_BITS) + qr_layout_write(layout, qr_bitstream_read(bits, QR_WORD_BITS)); + qr_layout_end(layout); + + mask = mask_data(code); + if (mask < 0) + goto fail; + + if (draw_functional(code, data->ec, mask) != 0) + goto fail; + +exit: + if (bits) + qr_bitstream_destroy(bits); + + return code; + +fail: + qr_code_destroy(code); + code = 0; + goto exit; +} + +static int mask_data(struct qr_code * code) +{ + struct qr_bitmap * mask, * test; + int selected, score; + int i, best; + + mask = 0; + + /* Generate bitmap for each mask and evaluate */ + for (i = 0; i < 8; ++i) { + test = qr_bitmap_clone(code->modules); + if (!test) { + qr_bitmap_destroy(mask); + return -1; + } + qr_mask_apply(test, i); + score = score_mask(test); + if (!mask || score < best) { + best = score; + selected = i; + qr_bitmap_destroy(mask); + mask = test; + } else { + qr_bitmap_destroy(test); + } + } + + qr_bitmap_destroy(code->modules); + code->modules = mask; + + return selected; +} + +static int score_mask(const struct qr_bitmap * bmp) +{ + const int N[4] = { 3, 3, 40, 10 }; + int score = 0; + + score += score_runs(bmp, N[0]); + score += N[1] * count_2blocks(bmp); + score += N[2] * count_locators(bmp); + score += N[3] * ((abs(calc_bw_balance(bmp) - 50) + 4) / 5); + + return score; +} + +static int score_runs(const struct qr_bitmap * bmp, int base) +{ + /* Runs of 5+n bits -> N[0] + i */ + size_t x, y; + int flip; + int score = 0; + int count, last; + + for (flip = 0; flip <= 1; ++flip) { + for (y = 0; y < bmp->height; ++y) { + count = 0; + for (x = 0; x < bmp->width; ++x) { + int mask, bit; + if (flip) { + mask = get_mask(bmp, y, x); + bit = get_mask(bmp, y, x); + } else { + mask = get_mask(bmp, x, y); + bit = get_px(bmp, x, y); + } + + if (mask && + (count == 0 || !!bit == !!last)) { + ++count; + last = bit; + } else { + if (count >= 5) + score += base + count - 5; + count = 0; + } + } + } + } + + return score; +} + +static int count_2blocks(const struct qr_bitmap * bmp) +{ + /* Count the number of 2x2 blocks (on or off) */ + size_t x, y; + int count = 0; + + /* Slow and stupid */ + for (y = 0; y < bmp->height - 1; ++y) { + for (x = 0; x < bmp->width - 1; ++x) { + if (get_mask(bmp, x, y) && + get_mask(bmp, x+1, y) && + get_mask(bmp, x, y+1) && + get_mask(bmp, x+1, y+1)) { + int v[4]; + v[0] = get_px(bmp, x, y); + v[1] = get_px(bmp, x+1, y); + v[2] = get_px(bmp, x, y+1); + v[3] = get_px(bmp, x+1, y+1); + if (!(v[0] || v[1] || v[2] || v[3]) || + (v[0] && v[1] && v[2] && v[3])) { + ++count; + } + } + } + } + + return count; +} + +static int count_locators(const struct qr_bitmap * bmp) +{ + /* 1:1:3:1:1 patterns -> N[2] */ + size_t x, y; + int flip; + int count = 0; + + for (flip = 0; flip <= 1; ++flip) { + for (y = 0; y < bmp->height - 7; ++y) { + for (x = 0; x < bmp->width - 7; ++x) { + int bits[7]; + int i; + + for (i = 0; i < 7; ++i) + if (!(flip ? get_mask(bmp, y, x+i) : get_mask(bmp, x+i, y))) + continue; + + for (i = 0; i < 7; ++i) + bits[i] = flip ? get_px(bmp, y, x+i) : get_px(bmp, x+i, y); + + if (!bits[0]) + for (i = 0; i < 7; ++i) + bits[i] = !bits[i]; + + if ( bits[0] && !bits[1] && bits[2] && + bits[3] && bits[4] && !bits[5] && + bits[6]) + ++count; + } + } + } + + return count; +} + +static int calc_bw_balance(const struct qr_bitmap * bmp) +{ + /* Calculate the proportion (in percent) of "on" bits */ + size_t x, y; + unsigned char bit; + long on, total; + + assert(bmp->mask); /* we only need this case */ + + on = total = 0; + for (y = 0; y < bmp->height; ++y) { + size_t off = y * bmp->stride; + unsigned char * bits = bmp->bits + off; + unsigned char * mask = bmp->mask + off; + + for (x = 0; x < bmp->width / CHAR_BIT; ++x, ++bits, ++mask) { + for (bit = 1; bit; bit <<= 1) { + int m = *mask & bit; + + total += !!m; + on += !!(*bits & m); + } + } + } + + return (on * 100) / total; +} + +static int get_px(const struct qr_bitmap * bmp, int x, int y) +{ + size_t off = y * bmp->stride + x / CHAR_BIT; + unsigned char bit = 1 << (x % CHAR_BIT); + + return bmp->bits[off] & bit; +} + +static int get_mask(const struct qr_bitmap * bmp, int x, int y) +{ + size_t off = y * bmp->stride + x / CHAR_BIT; + unsigned char bit = 1 << (x % CHAR_BIT); + + return bmp->mask[off] & bit; +} + +static int draw_format(struct qr_bitmap * bmp, + struct qr_code * code, + enum qr_ec_level ec, + int mask) +{ + int i; + size_t dim; + long bits; + + dim = bmp->width; + + bits = calc_format_bits(ec, mask); + if (bits < 0) + return -1; + + for (i = 0; i < 8; ++i) { + if (bits & 0x1) { + setpx(bmp, 8, i + (i > 5)); + setpx(bmp, dim - 1 - i, 8); + } + bits >>= 1; + } + + for (i = 0; i < 7; ++i) { + if (bits & 0x1) { + setpx(bmp, 8, dim - 7 + i); + setpx(bmp, 6 - i + (i == 0), 8); + } + bits >>= 1; + } + + if (code->version >= 7) { + bits = calc_version_bits(code->version); + + for (i = 0; i < 18; ++i) { + if (bits & 0x1) { + int a = i % 3, b = i / 3; + setpx(bmp, dim - 11 + a, b); + setpx(bmp, b, dim - 11 + a); + } + bits >>= 1; + } + } + + return 0; +} + +static unsigned int calc_format_bits(enum qr_ec_level ec, int mask) +{ + unsigned int bits; + + bits = (ec & 0x3) << 3 | (mask & 0x7); + + /* Compute (15, 5) BCH code */ + + bits <<= 15 - 5; + bits |= (unsigned int)gf_residue(bits, QR_FORMAT_POLY); + + bits ^= QR_FORMAT_MASK; + + return bits; +} + +static unsigned long calc_version_bits(int version) +{ + unsigned long bits; + + bits = version & 0x3F; + + /* (18, 6) BCH code */ + + bits <<= 18 - 6; + bits |= gf_residue(bits, QR_VERSION_POLY); + + return bits; +} + diff --git a/lpg/libqr/code-layout.c b/lpg/libqr/code-layout.c new file mode 100644 index 0000000..fe8caa7 --- /dev/null +++ b/lpg/libqr/code-layout.c @@ -0,0 +1,188 @@ +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <qr/bitmap.h> +#include <qr/code.h> +#include <qr/common.h> +#include <qr/layout.h> +#include "constants.h" + +struct qr_iterator { + struct qr_code * code; + int dim; + int column; + int row; + int up; + int mask; + unsigned char * p; +}; + +void qr_layout_init_mask(struct qr_code * code) +{ + size_t x, y; + size_t dim = qr_code_width(code); + struct qr_bitmap * bmp = code->modules; + const int * am_pos = QR_ALIGNMENT_LOCATION[code->version - 1]; + size_t am_side; + + if (!bmp->mask) + qr_bitmap_add_mask(bmp); + + assert(bmp->mask); + + memset(bmp->mask, 0, bmp->height * bmp->stride); + + /* slow and stupid, but I'm sleepy */ + for (y = 0; y < bmp->height; ++y) { + unsigned char * row = bmp->mask + y * bmp->stride; + for (x = 0; x < bmp->width; ++x) { + unsigned char bit = 1 << (x % CHAR_BIT); + int off = x / CHAR_BIT; + + if (x == 6 || y == 6) /* timing */ + continue; + + if (x < 9 && y < 9) /* top-left */ + continue; + + if (x >= dim - 8 && y < 9) /* top-right */ + continue; + + if (x < 9 && y >= dim - 8) /* bottom-left */ + continue; + + /* version info */ + if (code->version >= 7) { + if (y < 6 && x >= dim - 11) + continue; + if (x < 6 && y >= dim - 11) + continue; + } + + row[off] |= bit; + } + } + + /* Alignment pattern */ + am_side = code->version > 1 ? (code->version / 7) + 2 : 0; + for (y = 0; y < am_side; ++y) { + for (x = 0; x < am_side; ++x) { + int i, j; + + if ((x == 0 && y == 0) || + (x == 0 && y == am_side - 1) || + (x == am_side - 1 && y == 0)) + continue; + + for (j = -2; j <= 2; ++j) { + unsigned char * row = bmp->mask + (am_pos[y]+j) * bmp->stride; + for (i = -2; i <= 2; ++i) { + unsigned char bit = 1 << ((am_pos[x]+i) % CHAR_BIT); + int off = (am_pos[x]+i) / CHAR_BIT; + + row[off] &= ~bit; + } + } + } + } +} + +static int is_data_bit(const struct qr_iterator * i) +{ + unsigned char bit = 1 << (i->column % CHAR_BIT); + int off = (i->row * i->code->modules->stride) + (i->column / CHAR_BIT); + + return i->code->modules->mask[off] & bit; +} + +static void set_pointer(struct qr_iterator * i) +{ + i->mask = 1 << (i->column % CHAR_BIT); + i->p = i->code->modules->bits + + i->code->modules->stride * i->row + + i->column / CHAR_BIT; +} + +static void advance(struct qr_iterator * i) +{ + do { + /* This XOR is to account for the vertical strip of + * timing bits in column 6 which displaces everything. + */ + if ((i->column < 6) ^ !(i->column % 2)) { + /* Right-hand part or at left edge */ + i->column -= 1; + } else { + /* Left-hand part */ + i->column += 1; + + if (( i->up && i->row == 0) || + (!i->up && i->row == i->dim - 1)) { + /* Hit the top / bottom */ + i->column -= 2; + i->up = !i->up; + } else { + i->row += i->up ? -1 : 1; + } + } + + if (i->column < 0) + continue; /* don't go off left edge */ + + /* Check for one-past-end */ + if (i->column == 0 && i->row >= i->dim - 8) + break; + + } while (!is_data_bit(i)); + + set_pointer(i); +} + +struct qr_iterator * qr_layout_begin(struct qr_code * code) +{ + struct qr_iterator * i; + + i = malloc(sizeof(*i)); + if (i) { + i->dim = qr_code_width(code); + i->code = code; + i->column = i->dim - 1; + i->row = i->dim - 1; + i->up = 1; + set_pointer(i); + } + + return i; +} + +void qr_layout_end(struct qr_iterator * i) +{ + free(i); +} + +unsigned int qr_layout_read(struct qr_iterator * i) +{ + unsigned int x = 0; + int b; + + for (b = 0; b < QR_WORD_BITS; ++b) { + x = (x << 1) | ((*i->p & i->mask) ? 1 : 0); + advance(i); + } + + return x; +} + +void qr_layout_write(struct qr_iterator * i, unsigned int x) +{ + int b; + + for (b = 0; b < QR_WORD_BITS; ++b) { + *i->p |= (x & 0x80) ? i->mask : 0; + advance(i); + x <<= 1; + } +} + diff --git a/lpg/libqr/code-parse.c b/lpg/libqr/code-parse.c new file mode 100644 index 0000000..a84df72 --- /dev/null +++ b/lpg/libqr/code-parse.c @@ -0,0 +1,348 @@ +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <qr/bitmap.h> +#include <qr/bitstream.h> +#include <qr/code.h> +#include <qr/common.h> +#include <qr/layout.h> +#include <qr/parse.h> + +#include "constants.h" +#include "galois.h" + +/* XXX: duplicated */ +static int get_px(const struct qr_bitmap * bmp, int x, int y) +{ + size_t off = y * bmp->stride + x / CHAR_BIT; + unsigned char bit = 1 << (x % CHAR_BIT); + + return bmp->bits[off] & bit; +} + +static int unpack_bits(int version, + enum qr_ec_level ec, + struct qr_bitstream * raw_bits, + struct qr_bitstream * bits_out) +{ + /* FIXME: more comments to explain the algorithm */ + + int total_words = qr_code_total_capacity(version) / QR_WORD_BITS; + int block_count[2], data_length[2], ec_length[2]; + int total_blocks; + int i, w, block; + struct qr_bitstream ** blocks = 0; + int status = -1; + + qr_get_rs_block_sizes(version, ec, block_count, data_length, ec_length); + total_blocks = block_count[0] + block_count[1]; + + status = qr_bitstream_resize(bits_out, + (block_count[0] * data_length[0] + + block_count[1] * data_length[1]) * QR_WORD_BITS); + if (status != 0) + goto cleanup; + + blocks = malloc(total_blocks * sizeof(*blocks)); + if (blocks == NULL) + goto cleanup; + + for (i = 0; i < total_blocks; ++i) + blocks[i] = NULL; + + /* Allocate temporary space for the blocks */ + for (i = 0; i < total_blocks; ++i) { + int type = (i >= block_count[0]); + blocks[i] = qr_bitstream_create(); + if (blocks[i] == NULL) + goto cleanup; + status = qr_bitstream_resize(blocks[i], + (data_length[type] + ec_length[type]) * QR_WORD_BITS); + if (status != 0) + goto cleanup; + } + + /* Read in the data & EC (see spec table 19) */ + + qr_bitstream_seek(raw_bits, 0); + + fprintf(stderr, "block counts %d and %d\n", block_count[0], block_count[1]); + fprintf(stderr, "row lengths %d and %d\n", data_length[0], data_length[1]); + + block = 0; + for (w = 0; w < total_words; ++w) { + if (block == 0 && w / total_blocks >= data_length[0] && block_count[1] != 0) { + /* Skip the short blocks, if there are any */ + block += block_count[0]; + } + qr_bitstream_copy(blocks[block], raw_bits, QR_WORD_BITS); + block = (block + 1) % total_blocks; + } + + /* XXX: apply ec */ + + for (block = 0; block < total_blocks; ++block) { + int type = (block >= block_count[0]); + struct qr_bitstream * stream = blocks[block]; + qr_bitstream_seek(stream, 0); + qr_bitstream_copy(bits_out, stream, data_length[type] * QR_WORD_BITS); + } + status = 0; + +cleanup: + if (blocks != NULL) { + for (block = 0; block < total_blocks; ++block) + if (blocks[block] != NULL) + qr_bitstream_destroy(blocks[block]); + free(blocks); + } + + return status; +} + +static int read_bits(const struct qr_code * code, + enum qr_ec_level ec, + struct qr_bitstream * data_bits) +{ + const size_t total_bits = qr_code_total_capacity(code->version); + const size_t total_words = total_bits / QR_WORD_BITS; + struct qr_bitstream * raw_bits; + struct qr_iterator * layout; + size_t w; + int ret = -1; + + raw_bits = qr_bitstream_create(); + if (raw_bits == NULL) + goto cleanup; + + ret = qr_bitstream_resize(raw_bits, total_words * QR_WORD_BITS); + if (ret != 0) + goto cleanup; + + layout = qr_layout_begin((struct qr_code *) code); /* dropping const! */ + if (layout == NULL) + goto cleanup; + for (w = 0; w < total_words; ++w) + qr_bitstream_write(raw_bits, qr_layout_read(layout), QR_WORD_BITS); + qr_layout_end(layout); + + ret = unpack_bits(code->version, ec, raw_bits, data_bits); + +cleanup: + if (raw_bits != NULL) + qr_bitstream_destroy(raw_bits); + + return ret; +} + +static int read_format(const struct qr_bitmap * bmp, enum qr_ec_level * ec, int * mask) +{ + int dim; + int i; + unsigned bits1, bits2; + enum qr_ec_level ec1, ec2; + int mask1, mask2; + int err1, err2; + + dim = bmp->width; + bits1 = bits2 = 0; + + for (i = 14; i >= 8; --i) { + bits1 = (bits1 << 1) | !!get_px(bmp, 14 - i + (i <= 8), 8); + bits2 = (bits2 << 1) | !!get_px(bmp, 8, dim - 1 - (14 - i)); + } + + for (; i >= 0; --i) { + bits1 = (bits1 << 1) | !!get_px(bmp, 8, i + (i >= 6)); + bits2 = (bits2 << 1) | !!get_px(bmp, dim - 1 - i, 8); + } + + fprintf(stderr, "read format bits %x / %x\n", bits1, bits2); + + err1 = qr_decode_format(bits1, &ec1, &mask1); + err2 = qr_decode_format(bits2, &ec2, &mask2); + + if (err1 < 0 && err2 < 0) + return -1; + + if (err1 < err2) + *ec = ec1, *mask = mask1; + else + *ec = ec2, *mask = mask2; + + return 0; +} + +static int read_version(const struct qr_bitmap * bmp) +{ + int dim; + int i; + unsigned long bits1, bits2; + int ver1, ver2; + int err1, err2; + + dim = bmp->width; + bits1 = bits2 = 0; + + for (i = 17; i >= 0; --i) { + bits1 = (bits1 << 1) | !!get_px(bmp, i / 3, dim - 11 + (i % 3)); + bits2 = (bits2 << 1) | !!get_px(bmp, dim - 11 + (i % 3), i / 3); + } + + fprintf(stderr, "read version bits %lx / %lx\n", bits1, bits2); + + err1 = qr_decode_version(bits1, &ver1); + err2 = qr_decode_version(bits2, &ver2); + + fprintf(stderr, "got versions %d[%d] / %d[%d]\n", ver1, err1, ver2, err2); + + if (err1 < 0 && err2 < 0) + return -1; + + return err1 < err2 ? ver1 : ver2; +} + +int qr_code_parse(const void * buffer, + size_t line_bits, + size_t line_stride, + size_t line_count, + struct qr_data ** data) +{ + /* TODO: more informative return values for errors */ + struct qr_bitmap src_bmp; + struct qr_code code; + enum qr_ec_level ec; + int mask; + struct qr_bitstream * data_bits = NULL; + int status; + + fprintf(stderr, "parsing code bitmap %lux%lu\n", (unsigned long) line_bits, (unsigned long) line_count); + + if (line_bits != line_count + || line_bits < 21 + || (line_bits - 17) % 4 != 0) { + /* Invalid size */ + fprintf(stderr, "Invalid size\n"); + return -1; + } + + code.version = (line_bits - 17) / 4; + fprintf(stderr, "assuming version %d\n", code.version); + + src_bmp.bits = (unsigned char *) buffer; /* dropping const! */ + src_bmp.mask = NULL; + src_bmp.stride = line_stride; + src_bmp.width = line_bits; + src_bmp.height = line_count; + + if (code.version >= 7 && read_version(&src_bmp) != code.version) { + fprintf(stderr, "Invalid version info\n"); + return -1; + } + + if (read_format(&src_bmp, &ec, &mask) != 0) { + fprintf(stderr, "Failed to read format\n"); + return -1; + } + + fprintf(stderr, "detected ec type %d; mask %d\n", ec, mask); + + code.modules = qr_bitmap_clone(&src_bmp); + if (code.modules == NULL) { + status = -1; + goto cleanup; + } + qr_mask_apply(code.modules, mask); + + qr_layout_init_mask(&code); + + data_bits = qr_bitstream_create(); + if (data_bits == NULL) { + status = -1; + goto cleanup; + } + + status = read_bits(&code, ec, data_bits); + if (status != 0) + goto cleanup; + + *data = malloc(sizeof(**data)); + if (*data == NULL) { + status = -1; + goto cleanup; + } + + (*data)->version = code.version; + (*data)->ec = ec; + (*data)->bits = data_bits; + (*data)->offset = 0; + + data_bits = 0; + status = 0; + +cleanup: + if (data_bits) + qr_bitstream_destroy(data_bits); + if (code.modules) + qr_bitmap_destroy(code.modules); + + return status; +} + +int qr_decode_format(unsigned long bits, enum qr_ec_level * ec, int * mask) +{ + bits ^= QR_FORMAT_MASK; + + /* TODO: check and fix errors */ + + bits >>= 10; + *mask = bits & 7; + *ec = bits >> 3; + + return 0; +} + +int qr_decode_version(unsigned long bits, int * version) +{ + int v, errors; + unsigned long version_bits; + int best_err, best_version; + + if (bits != (bits & 0x3FFFF)) + fprintf(stderr, "WARNING: excess version bits"); + + best_err = 18; + + for (v = 7; v <= 40; ++v) { + /* FIXME: cache these values */ + /* see calc_version_bits() */ + version_bits = v; + version_bits <<= 12; + version_bits |= gf_residue(version_bits, QR_VERSION_POLY); + + /* count errors */ + errors = 0; + version_bits ^= bits; + while (version_bits != 0) { + if ((version_bits & 1) == 1) + ++errors; + version_bits >>= 1; + } + + if (errors < best_err) { + best_version = v; + best_err = errors; + } + + if (errors == 0) /* can we do better than this? */ + break; + } + + *version = best_version; + + return best_err; +} + diff --git a/lpg/libqr/constants.c b/lpg/libqr/constants.c new file mode 100644 index 0000000..18c37e6 --- /dev/null +++ b/lpg/libqr/constants.c @@ -0,0 +1,151 @@ +#include "constants.h" + +/* FIXME: don't like big tables of data */ + +const int QR_ALIGNMENT_LOCATION[40][7] = { + { 0, 0, 0, 0, 0, 0, 0 }, /* 1 */ + { 6, 18, 0, 0, 0, 0, 0 }, /* 2 */ + { 6, 22, 0, 0, 0, 0, 0 }, /* 3 */ + { 6, 26, 0, 0, 0, 0, 0 }, /* 4 */ + { 6, 30, 0, 0, 0, 0, 0 }, /* 5 */ + { 6, 34, 0, 0, 0, 0, 0 }, /* 6 */ + { 6, 22, 38, 0, 0, 0, 0 }, /* 7 */ + { 6, 24, 42, 0, 0, 0, 0 }, /* 8 */ + { 6, 26, 46, 0, 0, 0, 0 }, /* 9 */ + { 6, 28, 50, 0, 0, 0, 0 }, /* 10 */ + { 6, 30, 54, 0, 0, 0, 0 }, /* 11 */ + { 6, 32, 58, 0, 0, 0, 0 }, /* 12 */ + { 6, 34, 62, 0, 0, 0, 0 }, /* 13 */ + { 6, 26, 46, 66, 0, 0, 0 }, /* 14 */ + { 6, 26, 48, 70, 0, 0, 0 }, /* 15 */ + { 6, 26, 50, 74, 0, 0, 0 }, /* 16 */ + { 6, 30, 54, 78, 0, 0, 0 }, /* 17 */ + { 6, 30, 56, 82, 0, 0, 0 }, /* 18 */ + { 6, 30, 58, 86, 0, 0, 0 }, /* 19 */ + { 6, 34, 62, 90, 0, 0, 0 }, /* 20 */ + { 6, 28, 50, 72, 94, 0, 0 }, /* 21 */ + { 6, 26, 50, 74, 98, 0, 0 }, /* 22 */ + { 6, 30, 54, 78,102, 0, 0 }, /* 23 */ + { 6, 28, 54, 80,106, 0, 0 }, /* 24 */ + { 6, 32, 58, 84,110, 0, 0 }, /* 25 */ + { 6, 30, 58, 86,114, 0, 0 }, /* 26 */ + { 6, 34, 62, 90,118, 0, 0 }, /* 27 */ + { 6, 26, 50, 74, 98,122, 0 }, /* 28 */ + { 6, 30, 54, 78,102,126, 0 }, /* 29 */ + { 6, 26, 52, 78,104,130, 0 }, /* 30 */ + { 6, 30, 56, 82,108,134, 0 }, /* 31 */ + { 6, 34, 60, 86,112,138, 0 }, /* 32 */ + { 6, 30, 58, 86,114,142, 0 }, /* 33 */ + { 6, 34, 62, 90,118,146, 0 }, /* 34 */ + { 6, 30, 54, 78,102,126,150 }, /* 35 */ + { 6, 24, 50, 76,102,128,154 }, /* 36 */ + { 6, 28, 54, 80,106,132,158 }, /* 37 */ + { 6, 32, 58, 84,110,136,162 }, /* 38 */ + { 6, 26, 54, 82,110,138,166 }, /* 39 */ + { 6, 30, 58, 86,114,142,170 }, /* 40 */ +}; + +const int QR_DATA_WORD_COUNT[40][4] = { + { 19, 16, 13, 9 }, + { 34, 28, 22, 16 }, + { 55, 44, 34, 26 }, + { 80, 64, 48, 36 }, + { 108, 86, 62, 46 }, + { 136, 108, 76, 60 }, + { 156, 124, 88, 66 }, + { 194, 154, 110, 86 }, + { 232, 182, 132, 100 }, + { 274, 216, 154, 122 }, + { 324, 254, 180, 140 }, + { 370, 290, 206, 158 }, + { 428, 334, 244, 180 }, + { 461, 365, 261, 197 }, + { 523, 415, 295, 223 }, + { 589, 453, 325, 253 }, + { 647, 507, 367, 283 }, + { 721, 563, 397, 313 }, + { 795, 627, 445, 341 }, + { 861, 669, 485, 385 }, + { 932, 714, 512, 406 }, + { 1006, 782, 568, 442 }, + { 1094, 860, 614, 464 }, + { 1174, 914, 664, 514 }, + { 1276, 1000, 718, 538 }, + { 1370, 1062, 754, 596 }, + { 1468, 1128, 808, 628 }, + { 1531, 1193, 871, 661 }, + { 1631, 1267, 911, 701 }, + { 1735, 1373, 985, 745 }, + { 1845, 1455, 1033, 793 }, + { 1955, 1541, 1115, 845 }, + { 2071, 1631, 1171, 901 }, + { 2191, 1725, 1231, 961 }, + { 2306, 1812, 1286, 986 }, + { 2434, 1914, 1351, 1054 }, + { 2566, 1992, 1426, 1096 }, + { 2812, 2216, 1582, 1222 }, + { 2956, 2334, 1666, 1276 } +}; + +const int QR_RS_BLOCK_COUNT[40][4][2] = { + { { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 0 } }, /* 1 */ + { { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 0 } }, /* 2 */ + { { 1, 0 }, { 1, 0 }, { 2, 0 }, { 2, 0 } }, /* 3 */ + { { 1, 0 }, { 2, 0 }, { 2, 0 }, { 4, 0 } }, /* 4 */ + { { 1, 0 }, { 2, 0 }, { 2, 2 }, { 2, 2 } }, /* 5 */ + { { 2, 0 }, { 4, 0 }, { 4, 0 }, { 4, 0 } }, /* 6 */ + { { 2, 0 }, { 4, 0 }, { 2, 4 }, { 4, 1 } }, /* 7 */ + { { 2, 0 }, { 2, 2 }, { 4, 2 }, { 4, 2 } }, /* 8 */ + { { 2, 0 }, { 3, 2 }, { 4, 4 }, { 4, 4 } }, /* 9 */ + { { 2, 2 }, { 4, 1 }, { 6, 2 }, { 6, 2 } }, /* 10 */ + { { 4, 0 }, { 1, 4 }, { 4, 4 }, { 3, 8 } }, /* 11 */ + { { 2, 2 }, { 6, 2 }, { 4, 6 }, { 7, 4 } }, /* 12 */ + { { 4, 0 }, { 8, 1 }, { 8, 4 }, { 12, 4 } }, /* 13 */ + { { 3, 1 }, { 4, 5 }, { 11, 5 }, { 11, 5 } }, /* 14 */ + { { 5, 1 }, { 5, 5 }, { 5, 7 }, { 11, 7 } }, /* 15 */ + { { 5, 1 }, { 7, 3 }, { 15, 2 }, { 3, 13 } }, /* 16 */ + { { 1, 5 }, { 10, 1 }, { 1, 15 }, { 2, 17 } }, /* 17 */ + { { 5, 1 }, { 9, 4 }, { 17, 1 }, { 2, 19 } }, /* 18 */ + { { 3, 4 }, { 3, 11 }, { 17, 4 }, { 9, 16 } }, /* 19 */ + { { 3, 5 }, { 3, 13 }, { 15, 5 }, { 15, 10 } }, /* 20 */ + { { 4, 4 }, { 17, 0 }, { 17, 6 }, { 19, 6 } }, /* 21 */ + { { 2, 7 }, { 17, 0 }, { 7, 16 }, { 34, 0 } }, /* 22 */ + { { 4, 5 }, { 4, 14 }, { 11, 14 }, { 16, 14 } }, /* 23 */ + { { 6, 4 }, { 6, 14 }, { 11, 16 }, { 30, 2 } }, /* 24 */ + { { 8, 4 }, { 8, 13 }, { 7, 22 }, { 22, 13 } }, /* 25 */ + { { 10, 2 }, { 19, 4 }, { 28, 6 }, { 33, 4 } }, /* 26 */ + { { 8, 4 }, { 22, 3 }, { 8, 26 }, { 12, 28 } }, /* 27 */ + { { 3, 10 }, { 3, 23 }, { 4, 31 }, { 11, 31 } }, /* 28 */ + { { 7, 7 }, { 21, 7 }, { 1, 37 }, { 19, 26 } }, /* 29 */ + { { 5, 10 }, { 19, 10 }, { 15, 25 }, { 23, 25 } }, /* 30 */ + { { 13, 3 }, { 2, 29 }, { 42, 1 }, { 23, 28 } }, /* 31 */ + { { 17, 0 }, { 10, 23 }, { 10, 35 }, { 19, 35 } }, /* 32 */ + { { 17, 1 }, { 14, 21 }, { 29, 19 }, { 11, 46 } }, /* 33 */ + { { 13, 6 }, { 14, 23 }, { 44, 7 }, { 59, 1 } }, /* 34 */ + { { 12, 7 }, { 12, 26 }, { 39, 14 }, { 22, 41 } }, /* 35 */ + { { 6, 14 }, { 6, 34 }, { 46, 10 }, { 2, 64 } }, /* 36 */ + { { 17, 4 }, { 29, 14 }, { 49, 10 }, { 24, 46 } }, /* 37 */ + { { 4, 18 }, { 13, 32 }, { 48, 14 }, { 42, 32 } }, /* 38 */ + { { 20, 4 }, { 40, 7 }, { 43, 22 }, { 10, 67 } }, /* 39 */ + { { 19, 6 }, { 18, 31 }, { 34, 34 }, { 20, 61 } } /* 40 */ +}; + +const enum qr_data_type QR_TYPE_CODES[16] = { + QR_DATA_INVALID, /* 0000 */ + QR_DATA_NUMERIC, /* 0001 */ + QR_DATA_ALPHA, /* 0010 */ + QR_DATA_MIXED, /* 0011 */ + QR_DATA_8BIT, /* 0100 */ + QR_DATA_FNC1, /* 0101 */ + QR_DATA_INVALID, /* 0110 */ + QR_DATA_ECI, /* 0111 */ + QR_DATA_KANJI, /* 1000 */ + QR_DATA_FNC1, /* 1001 */ + QR_DATA_INVALID, /* 1010 */ + QR_DATA_INVALID, /* 1011 */ + QR_DATA_INVALID, /* 1100 */ + QR_DATA_INVALID, /* 1101 */ + QR_DATA_INVALID, /* 1110 */ + QR_DATA_INVALID, /* 1111 */ +}; + diff --git a/lpg/libqr/constants.h b/lpg/libqr/constants.h new file mode 100644 index 0000000..b1f7493 --- /dev/null +++ b/lpg/libqr/constants.h @@ -0,0 +1,29 @@ +#ifndef QR_CONSTANTS_H +#define QR_CONSTANTS_H + +#include <qr/types.h> + +/* XOR mask for format data: 101 0100 0001 0010 */ +static const unsigned int QR_FORMAT_MASK = 0x5412; + +/* Format info EC polynomial + * G(x) = x^10 + x^8 + x^5 + x^4 + x^2 + x + 1 + */ +static const unsigned int QR_FORMAT_POLY = 0x537; + +/* Version info EC polynomial + * G(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1 + */ +static const unsigned int QR_VERSION_POLY = 0x1F25; + +/* A QR-code word is always 8 bits, but CHAR_BIT might not be */ +static const int QR_WORD_BITS = 8; + +extern const int QR_ALIGNMENT_LOCATION[40][7]; +extern const int QR_DATA_WORD_COUNT[40][4]; +/* See qr_get_rs_block_sizes() */ +extern const int QR_RS_BLOCK_COUNT[40][4][2]; +extern const enum qr_data_type QR_TYPE_CODES[16]; + +#endif + diff --git a/lpg/libqr/data-common.c b/lpg/libqr/data-common.c new file mode 100644 index 0000000..af3a766 --- /dev/null +++ b/lpg/libqr/data-common.c @@ -0,0 +1,38 @@ +#include <stdlib.h> + +#include <qr/bitstream.h> +#include <qr/data.h> + +void qr_data_destroy(struct qr_data * data) +{ + qr_bitstream_destroy(data->bits); + free(data); +} + +size_t qr_data_size_field_length(int version, enum qr_data_type type) +{ + static const size_t QR_SIZE_LENGTHS[3][4] = { + { 10, 9, 8, 8 }, + { 12, 11, 16, 10 }, + { 14, 13, 16, 12 } + }; + int row, col; + + switch (type) { + case QR_DATA_NUMERIC: col = 0; break; + case QR_DATA_ALPHA: col = 1; break; + case QR_DATA_8BIT: col = 2; break; + case QR_DATA_KANJI: col = 3; break; + default: return 0; + } + + if (version < 10) + row = 0; + else if (version < 27) + row = 1; + else + row = 2; + + return QR_SIZE_LENGTHS[row][col]; +} + diff --git a/lpg/libqr/data-create.c b/lpg/libqr/data-create.c new file mode 100644 index 0000000..09d6653 --- /dev/null +++ b/lpg/libqr/data-create.c @@ -0,0 +1,273 @@ +/** + * Not "pure" C - only works with ASCII + */ + +#include <ctype.h> +#include <stdlib.h> + +#include <qr/bitstream.h> +#include <qr/data.h> +#include "constants.h" + +static void write_type_and_length(struct qr_data * data, + enum qr_data_type type, + size_t length) +{ + (void)qr_bitstream_write(data->bits, QR_TYPE_CODES[type], 4); + (void)qr_bitstream_write(data->bits, length, + qr_data_size_field_length(data->version, type)); +} + +static struct qr_data * encode_numeric(struct qr_data * data, + const unsigned char * input, + size_t length) +{ + struct qr_bitstream * stream = data->bits; + size_t bits; + + bits = 4 + qr_data_size_field_length(data->version, QR_DATA_NUMERIC) + + qr_data_dpart_length(QR_DATA_NUMERIC, length); + + stream = data->bits; + if (qr_bitstream_resize(stream, + qr_bitstream_size(stream) + bits) != 0) + return 0; + + write_type_and_length(data, QR_DATA_NUMERIC, length); + + for (; length >= 3; length -= 3) { + unsigned int x; + + if (!isdigit(input[0]) || + !isdigit(input[1]) || + !isdigit(input[2])) + return 0; + + x = (input[0] - '0') * 100 + + (input[1] - '0') * 10 + + (input[2] - '0'); + qr_bitstream_write(stream, x, 10); + input += 3; + } + + if (length > 0) { + unsigned int x; + + if (!isdigit(*input)) + return 0; + + x = *input++ - '0'; + + if (length == 2) { + if (!isdigit(*input)) + return 0; + x = x * 10 + (*input - '0'); + } + + qr_bitstream_write(stream, x, length == 2 ? 7 : 4); + } + + return data; +} + +static int get_alpha_code(char c) +{ + if (isdigit(c)) + return c - '0'; + else if (isalpha(c)) + return toupper(c) - 'A' + 10; + + switch (c) { + case ' ': return 36; + case '$': return 37; + case '%': return 38; + case '*': return 39; + case '+': return 40; + case '-': return 41; + case '.': return 42; + case '/': return 43; + case ':': return 44; + default: return -1; + } +} + +static struct qr_data * encode_alpha(struct qr_data * data, + const unsigned char * input, + size_t length) +{ + struct qr_bitstream * stream = data->bits; + size_t bits; + + bits = 4 + qr_data_size_field_length(data->version, QR_DATA_ALPHA) + + qr_data_dpart_length(QR_DATA_ALPHA, length); + + stream = data->bits; + if (qr_bitstream_resize(stream, + qr_bitstream_size(stream) + bits) != 0) + return 0; + + write_type_and_length(data, QR_DATA_ALPHA, length); + + for (; length >= 2; length -= 2) { + unsigned int x; + int c1, c2; + + c1 = get_alpha_code(*input++); + c2 = get_alpha_code(*input++); + + if (c1 < 0 || c2 < 0) + return 0; + + x = c1 * 45 + c2; + qr_bitstream_write(stream, x, 11); + } + + if (length > 0) { + int c = get_alpha_code(*input); + + if (c < 0) + return 0; + + qr_bitstream_write(stream, c, 6); + } + + return data; +} + +static struct qr_data * encode_8bit(struct qr_data * data, + const unsigned char * input, + size_t length) +{ + struct qr_bitstream * stream = data->bits; + size_t bits; + + bits = 4 + qr_data_size_field_length(data->version, QR_DATA_8BIT) + + qr_data_dpart_length(QR_DATA_8BIT, length); + + stream = data->bits; + if (qr_bitstream_resize(stream, + qr_bitstream_size(stream) + bits) != 0) + return 0; + + write_type_and_length(data, QR_DATA_8BIT, length); + + while (length--) + qr_bitstream_write(stream, *input++, 8); + + return data; +} + +static struct qr_data * encode_kanji(struct qr_data * data, + const unsigned char * input, + size_t length) +{ + return 0; +} + +static int calc_min_version(enum qr_data_type type, + enum qr_ec_level ec, + size_t length) +{ + size_t dbits; + int version; + + dbits = qr_data_dpart_length(type, length); + + for (version = 1; version <= 40; ++version) { + if (4 + dbits + qr_data_size_field_length(version, type) + < 8 * (size_t) QR_DATA_WORD_COUNT[version - 1][ec ^ 0x1]) + return version; + } + + return -1; +} + +struct qr_data * qr_data_create(int version, + enum qr_ec_level ec, + enum qr_data_type type, + const char * input, + size_t length) +{ + struct qr_data * data; + int minver; + + minver = calc_min_version(type, ec, length); + + if (version == 0) + version = minver; + + if (minver < 0 || version < minver) + return 0; + + data = malloc(sizeof(*data)); + if (!data) + return 0; + + data->version = version; + data->ec = ec; + data->bits = qr_bitstream_create(); + data->offset = 0; + + if (data->bits) { + struct qr_data * ret; + + switch (type) { + case QR_DATA_NUMERIC: + ret = encode_numeric(data, (const unsigned char *) input, length); + break; + case QR_DATA_ALPHA: + ret = encode_alpha(data, (const unsigned char *) input, length); + break; + case QR_DATA_8BIT: + ret = encode_8bit(data, (const unsigned char *) input, length); + break; + case QR_DATA_KANJI: + ret = encode_kanji(data, (const unsigned char *) input, length); + break; + default: + /* unsupported / invalid */ + ret = 0; + break; + } + + if (!ret) { + qr_bitstream_destroy(data->bits); + free(data); + } + + return ret; + } else { + free(data); + return 0; + } +} + +size_t qr_data_dpart_length(enum qr_data_type type, size_t length) +{ + size_t bits; + + switch (type) { + case QR_DATA_NUMERIC: + bits = 10 * (length / 3); + if (length % 3 == 1) + bits += 4; + else if (length % 3 == 2) + bits += 7; + break; + case QR_DATA_ALPHA: + bits = 11 * (length / 2) + + 6 * (length % 2); + break; + case QR_DATA_8BIT: + bits = 8 * length; + break; + case QR_DATA_KANJI: + /* unsupported */ + default: + /* unsupported; will be ignored */ + bits = 0; + } + + return bits; +} + diff --git a/lpg/libqr/data-parse.c b/lpg/libqr/data-parse.c new file mode 100644 index 0000000..d641c47 --- /dev/null +++ b/lpg/libqr/data-parse.c @@ -0,0 +1,240 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include <qr/bitstream.h> +#include <qr/data.h> +#include "constants.h" + +static enum qr_data_type read_data_type(struct qr_bitstream * stream) +{ + const size_t length = 4; + unsigned int type; + + if (qr_bitstream_remaining(stream) < length) + return QR_DATA_INVALID; + + type = qr_bitstream_read(stream, length); + assert(type < 16); + + return QR_TYPE_CODES[type]; +} + +static enum qr_data_type parse_numeric(const struct qr_data * data, + char ** output, + size_t * length) +{ + struct qr_bitstream * stream; + size_t field_len, bits; + unsigned int digits; + unsigned int chunk; + char * p, * buffer; + + stream = data->bits; + buffer = 0; + + field_len = qr_data_size_field_length(data->version, QR_DATA_NUMERIC); + if (qr_bitstream_remaining(stream) < field_len) + goto invalid; + + digits = qr_bitstream_read(stream, field_len); + + bits = (digits / 3) * 10; + if (digits % 3 == 1) + bits += 4; + else if (digits % 3 == 2) + bits += 7; + + if (qr_bitstream_remaining(stream) < bits) + goto invalid; + + buffer = malloc(digits + 1); + if (!buffer) + goto invalid; + + p = buffer; + + for (; bits >= 10; bits -= 10) { + chunk = qr_bitstream_read(stream, 10); + if (chunk >= 1000) + goto invalid; + sprintf(p, "%03u", chunk); + p += 3; + } + + if (bits > 0) { + chunk = qr_bitstream_read(stream, bits); + if (chunk >= (bits >= 7 ? 100 : 10)) + goto invalid; + sprintf(p, "%0*u", bits >= 7 ? 2 : 1, chunk); + } + + *output = buffer; + *length = digits; + + return QR_DATA_NUMERIC; +invalid: + free(buffer); + return QR_DATA_INVALID; +} + +static enum qr_data_type parse_alpha(const struct qr_data * data, + char ** output, + size_t * length) +{ + static const char charset[45] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + struct qr_bitstream * stream; + size_t field_len, bits; + unsigned int chars; + unsigned int chunk; + char * p, * buffer; + + stream = data->bits; + buffer = 0; + + field_len = qr_data_size_field_length(data->version, QR_DATA_ALPHA); + if (qr_bitstream_remaining(stream) < field_len) + goto invalid; + + chars = qr_bitstream_read(stream, field_len); + + bits = (chars / 2) * 11; + if (chars % 2 == 1) + bits += 6; + + if (qr_bitstream_remaining(stream) < bits) + goto invalid; + + buffer = malloc(chars + 1); + if (!buffer) + goto invalid; + + p = buffer; + + for (; bits >= 11; bits -= 11) { + unsigned int c1, c2; + chunk = qr_bitstream_read(stream, 11); + c1 = chunk / 45; + c2 = chunk % 45; + if (c1 >= 45) + goto invalid; + *p++ = charset[c1]; + *p++ = charset[c2]; + } + + if (bits > 0) { + chunk = qr_bitstream_read(stream, bits); + if (chunk >= 45) + goto invalid; + *p = charset[chunk]; + } + + *output = buffer; + *length = chars; + + return QR_DATA_ALPHA; +invalid: + free(buffer); + return QR_DATA_INVALID; +} + +static enum qr_data_type parse_8bit(const struct qr_data * data, + char ** output, + size_t * length) +{ + struct qr_bitstream * stream; + size_t field_len; + unsigned int bytes; + char * p; + + stream = data->bits; + + field_len = qr_data_size_field_length(data->version, QR_DATA_8BIT); + if (qr_bitstream_remaining(stream) < field_len) + return QR_DATA_INVALID; + + bytes = qr_bitstream_read(stream, field_len); + + if (qr_bitstream_remaining(stream) < bytes * 8) + return QR_DATA_INVALID; + + *output = malloc(bytes + 1); + if (!*output) + return QR_DATA_INVALID; + + p = *output; + *length = bytes; + + while (bytes-- > 0) + *p++ = qr_bitstream_read(stream, 8); + + *p = '\0'; + + return QR_DATA_8BIT; +} + +static enum qr_data_type parse_kanji(const struct qr_data * data, + char ** output, + size_t * length) +{ + return QR_DATA_INVALID; +} + +enum qr_data_type qr_data_type(const struct qr_data * data) +{ + qr_bitstream_seek(data->bits, data->offset); + return read_data_type(data->bits); +} + +int qr_get_data_length(const struct qr_data * data) +{ + size_t field_len; + enum qr_data_type type; + + qr_bitstream_seek(data->bits, data->offset); + + type = read_data_type(data->bits); + + switch (type) { + case QR_DATA_NUMERIC: + case QR_DATA_ALPHA: + case QR_DATA_8BIT: + case QR_DATA_KANJI: + field_len = qr_data_size_field_length(data->version, type); + break; + default: + /* unsupported / invalid */ + return -1; + } + + if (qr_bitstream_remaining(data->bits) < field_len) + return -1; + + return (int) qr_bitstream_read(data->bits, field_len); +} + +enum qr_data_type qr_parse_data(const struct qr_data * input, + char ** output, + size_t * length) +{ + qr_bitstream_seek(input->bits, input->offset); + + *output = NULL; + *length = 0; + + switch (read_data_type(input->bits)) { + case QR_DATA_NUMERIC: + return parse_numeric(input, output, length); + case QR_DATA_ALPHA: + return parse_alpha(input, output, length); + case QR_DATA_8BIT: + return parse_8bit(input, output, length); + case QR_DATA_KANJI: + return parse_kanji(input, output, length); + default: + /* unsupported / invalid */ + return QR_DATA_INVALID; + } +} + diff --git a/lpg/libqr/galois.c b/lpg/libqr/galois.c new file mode 100644 index 0000000..decefb0 --- /dev/null +++ b/lpg/libqr/galois.c @@ -0,0 +1,125 @@ +#include <assert.h> +#include <stdlib.h> + +#include <qr/bitstream.h> +#include "galois.h" + +/* Calculate the residue of a modulo m */ +unsigned long gf_residue(unsigned long a, unsigned long m) +{ + unsigned long o = 1; + int n = 1; + + /* Find one past the highest bit of the modulus */ + while (m & ~(o - 1)) + o <<= 1; + + /* Find the highest n such that O(m * x^n) <= O(a) */ + while (a & ~(o - 1)) { + o <<= 1; + ++n; + } + + /* For each n, try to reduce a by (m * x^n) */ + while (n--) { + o >>= 1; + + /* o is the highest bit of (m * x^n) */ + if (a & o) + a ^= m << n; + } + + return a; +} + +static unsigned int gf_mult(unsigned int a, unsigned int b) +{ + /* Reduce modulo x^8 + x^4 + x^3 + x^2 + 1 + * using the peasant's algorithm + */ + const unsigned int m = 0x11D; + unsigned int x = 0; + int i; + + for (i = 0; i < 8; ++i) { + x ^= (b & 0x1) ? a : 0; + a = (a << 1) ^ ((a & 0x80) ? m : 0); + b >>= 1; + } + + return x & 0xFF; +} + +static unsigned int * make_generator(int k) +{ + unsigned int * g; + unsigned int a; + int i, j; + + g = calloc(k, sizeof(*g)); + if (!g) + return 0; + + g[0] = 1; /* Start with g(x) = 1 */ + a = 1; /* 2^0 = 1 */ + + for (i = 0; i < k; ++i) { + /* Multiply our poly g(x) by (x + 2^i) */ + for (j = k - 1; j > 0; --j) + g[j] = gf_mult(g[j], a) ^ g[j-1]; + g[0] = gf_mult(g[0], a); + + a = gf_mult(a, 2); + } + + return g; +} + +struct qr_bitstream * rs_generate_words(struct qr_bitstream * data, + size_t data_words, + size_t rs_words) +{ + struct qr_bitstream * ec = 0; + unsigned int * b = 0; + unsigned int * g; + size_t n = rs_words; + size_t i, r; + + assert(qr_bitstream_remaining(data) >= data_words * 8); + + ec = qr_bitstream_create(); + if (!ec) + return 0; + + if (qr_bitstream_resize(ec, n * 8) != 0) + goto fail; + + b = calloc(n, sizeof(*b)); + if (!b) + goto fail; + + g = make_generator(n); + if (!g) + goto fail; + + /* First, prepare the registers (b) with data bits */ + for (i = 0; i < data_words; ++i) { + unsigned int x = b[n-1] ^ qr_bitstream_read(data, 8); + for (r = n-1; r > 0; --r) + b[r] = b[r-1] ^ gf_mult(g[r], x); + b[0] = gf_mult(g[0], x); + } + + /* Read off the registers */ + for (r = 0; r < n; ++r) + qr_bitstream_write(ec, b[(n-1)-r], 8); + + free(g); + free(b); + return ec; +fail: + free(b); + qr_bitstream_destroy(ec); + return 0; +} + diff --git a/lpg/libqr/galois.h b/lpg/libqr/galois.h new file mode 100644 index 0000000..4ca0c93 --- /dev/null +++ b/lpg/libqr/galois.h @@ -0,0 +1,11 @@ +#ifndef QR_GALOIS_H +#define QR_GALOIS_H + +unsigned long gf_residue(unsigned long a, unsigned long m); + +struct qr_bitstream * rs_generate_words(struct qr_bitstream * data, + size_t data_words, + size_t rs_words); + +#endif + diff --git a/lpg/libqr/qr/bitmap.h b/lpg/libqr/qr/bitmap.h new file mode 100644 index 0000000..098d9c3 --- /dev/null +++ b/lpg/libqr/qr/bitmap.h @@ -0,0 +1,37 @@ +#ifndef QR_BITMAP_H +#define QR_BITMAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct qr_bitmap { + unsigned char * bits; + unsigned char * mask; + size_t stride; + size_t width, height; +}; + +struct qr_bitmap * qr_bitmap_create(size_t width, size_t height, int masked); +void qr_bitmap_destroy(struct qr_bitmap *); + +int qr_bitmap_add_mask(struct qr_bitmap *); + +struct qr_bitmap * qr_bitmap_clone(const struct qr_bitmap *); + +void qr_bitmap_merge(struct qr_bitmap * dest, const struct qr_bitmap * src); + +void qr_bitmap_render(const struct qr_bitmap * bmp, + void * buffer, + int mod_bits, + long line_stride, + int line_repeat, + unsigned long mark, + unsigned long space); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/bitstream.h b/lpg/libqr/qr/bitstream.h new file mode 100644 index 0000000..aa431e8 --- /dev/null +++ b/lpg/libqr/qr/bitstream.h @@ -0,0 +1,57 @@ +#ifndef QR_BITSTREAM_H +#define QR_BITSTREAM_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Note: when writing / reading multiple bits, the + * _most_ significant bits come first in the stream. + * (That is, the order you would naturally write the + * number in binary) + */ + +struct qr_bitstream; + +struct qr_bitstream * qr_bitstream_create(void); +int qr_bitstream_resize(struct qr_bitstream *, size_t bits); +void qr_bitstream_destroy(struct qr_bitstream *); +struct qr_bitstream * qr_bitstream_dup(const struct qr_bitstream *); + +void qr_bitstream_seek(struct qr_bitstream *, size_t pos); +size_t qr_bitstream_tell(const struct qr_bitstream *); +size_t qr_bitstream_remaining(const struct qr_bitstream *); +size_t qr_bitstream_size(const struct qr_bitstream *); + +unsigned long qr_bitstream_read(struct qr_bitstream *, int bits); + +void qr_bitstream_unpack(struct qr_bitstream *, + unsigned int * result, + size_t count, + int bitsize); + +int qr_bitstream_write(struct qr_bitstream *, + unsigned long value, + int bits); + +int qr_bitstream_pack(struct qr_bitstream *, + const unsigned int * values, + size_t count, + int bitsize); + +int qr_bitstream_cat(struct qr_bitstream *, + const struct qr_bitstream * src); + +int qr_bitstream_copy(struct qr_bitstream * dest, + struct qr_bitstream * src, + size_t count); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/code.h b/lpg/libqr/qr/code.h new file mode 100644 index 0000000..0f5d49c --- /dev/null +++ b/lpg/libqr/qr/code.h @@ -0,0 +1,25 @@ +#ifndef QR_CODE_H +#define QR_CODE_H + +#include <stddef.h> +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct qr_code { + int version; + struct qr_bitmap * modules; +}; + +struct qr_code * qr_code_create(const struct qr_data * data); + +void qr_code_destroy(struct qr_code *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/common.h b/lpg/libqr/qr/common.h new file mode 100644 index 0000000..b7052ad --- /dev/null +++ b/lpg/libqr/qr/common.h @@ -0,0 +1,32 @@ +#ifndef QR_COMMON_H +#define QR_COMMON_H + +#include <qr/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void qr_mask_apply(struct qr_bitmap * bmp, int mask); + +size_t qr_code_total_capacity(int version); + +int qr_code_width(const struct qr_code *); + +/* See table 19 of the spec for the layout of EC data. There are at + * most two different block lengths, so the total number of data+ec + * blocks is the sum of block_count[]. The total number of 8-bit + * words in each kind of block is data_length + ec_length. + */ +void qr_get_rs_block_sizes(int version, + enum qr_ec_level ec, + int block_count[2], + int data_length[2], + int ec_length[2]); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/data.h b/lpg/libqr/qr/data.h new file mode 100644 index 0000000..06600ab --- /dev/null +++ b/lpg/libqr/qr/data.h @@ -0,0 +1,41 @@ +#ifndef QR_DATA_H +#define QR_DATA_H + +#include <stddef.h> +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct qr_data { + int version; /* 1 ~ 40 */ + enum qr_ec_level ec; + struct qr_bitstream * bits; + size_t offset; +}; + +struct qr_data * qr_data_create(int format, /* 1 ~ 40; 0=auto */ + enum qr_ec_level ec, + enum qr_data_type type, + const char * input, + size_t length); + +void qr_data_destroy(struct qr_data *); + +enum qr_data_type qr_data_type(const struct qr_data *); + +size_t qr_data_length(const struct qr_data *); +size_t qr_data_size_field_length(int version, enum qr_data_type); +size_t qr_data_dpart_length(enum qr_data_type type, size_t nchars); + +enum qr_data_type qr_parse_data(const struct qr_data * input, + char ** output, + size_t * length); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/layout.h b/lpg/libqr/qr/layout.h new file mode 100644 index 0000000..e691bdb --- /dev/null +++ b/lpg/libqr/qr/layout.h @@ -0,0 +1,22 @@ +#ifndef QR_CODE_LAYOUT_H +#define QR_CODE_LAYOUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct qr_iterator; + +void qr_layout_init_mask(struct qr_code *); + +struct qr_iterator * qr_layout_begin(struct qr_code * code); +unsigned int qr_layout_read(struct qr_iterator *); +void qr_layout_write(struct qr_iterator *, unsigned int); +void qr_layout_end(struct qr_iterator *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/parse.h b/lpg/libqr/qr/parse.h new file mode 100644 index 0000000..0e08354 --- /dev/null +++ b/lpg/libqr/qr/parse.h @@ -0,0 +1,24 @@ +#ifndef QR_PARSE_H +#define QR_PARSE_H + +#include "data.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int qr_code_parse(const void * buffer, + size_t line_bits, + size_t line_stride, + size_t line_count, + struct qr_data ** data); + +int qr_decode_format(unsigned long bits, enum qr_ec_level * ec, int * mask); +int qr_decode_version(unsigned long bits, int * version); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/types.h b/lpg/libqr/qr/types.h new file mode 100644 index 0000000..ae760ab --- /dev/null +++ b/lpg/libqr/qr/types.h @@ -0,0 +1,34 @@ +#ifndef QR_TYPES_H +#define QR_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct qr_data; +struct qr_code; + +enum qr_data_type { + QR_DATA_INVALID = -1, + QR_DATA_ECI = 7, + QR_DATA_NUMERIC = 1, + QR_DATA_ALPHA = 2, + QR_DATA_8BIT = 4, + QR_DATA_KANJI = 8, /* JIS X 0208 */ + QR_DATA_MIXED = 3, + QR_DATA_FNC1 = 9 +}; + +enum qr_ec_level { + QR_EC_LEVEL_L = 0x1, + QR_EC_LEVEL_M = 0x0, + QR_EC_LEVEL_Q = 0x3, + QR_EC_LEVEL_H = 0x2 +}; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qr/version.h b/lpg/libqr/qr/version.h new file mode 100644 index 0000000..ce540f4 --- /dev/null +++ b/lpg/libqr/qr/version.h @@ -0,0 +1,18 @@ +#ifndef QR_VERSION_H +#define QR_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define QR_VERSION_MAJOR 0 +#define QR_VERSION_MINOR 3 + +#define QR_VERSION "0.3" + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lpg/libqr/qrgen.c b/lpg/libqr/qrgen.c new file mode 100644 index 0000000..cf518e5 --- /dev/null +++ b/lpg/libqr/qrgen.c @@ -0,0 +1,402 @@ +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <limits.h> +#include <png.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <qr/bitmap.h> +#include <qr/bitstream.h> +#include <qr/code.h> +#include <qr/data.h> +#include <qr/version.h> + +struct config { + int version; + enum qr_ec_level ec; + enum qr_data_type dtype; + enum { + FORMAT_ANSI, + FORMAT_PBM, + FORMAT_PNG + } format; + const char * file; + const char * outfile; + const char * input; +}; + +struct qr_code * create(int version, + enum qr_ec_level ec, + enum qr_data_type dtype, + const char * input, + size_t len) +{ + struct qr_data * data; + struct qr_code * code; + + data = qr_data_create(version, ec, dtype, input, len); + + if (!data) { + /* BUG: this could also indicate OOM or + * some other error. + */ + fprintf(stderr, "Invalid data\n"); + exit(1); + } + + code = qr_code_create(data); + qr_data_destroy(data); + + if (!code) { + perror("Failed to create code"); + exit(2); + } + + return code; +} + +void output_pbm(FILE * file, const struct qr_bitmap * bmp, const char * comment) +{ + unsigned char * row; + int x, y; + + fputs("P1\n", file); + + if (comment) + fprintf(file, "# %s\n", comment); + + fprintf(file, "%u %u\n", + (unsigned)bmp->width + 8, + (unsigned)bmp->height + 8); + + row = bmp->bits; + + for (y = -4; y < (int)bmp->height + 4; ++y) { + + if (y < 0 || y >= (int)bmp->height) { + for (x = 0; x < (int)bmp->width + 8; ++x) + fputs("0 ", file); + fputc('\n', file); + continue; + } + + fputs("0 0 0 0 ", file); + + for (x = 0; x < (int)bmp->width; ++x) { + + int mask = 1 << x % CHAR_BIT; + int byte = row[x / CHAR_BIT]; + + fprintf(file, "%c ", (byte & mask) ? '1' : '0'); + } + + fputs("0 0 0 0\n", file); + row += bmp->stride; + } +} + +void output_ansi(FILE * file, const struct qr_bitmap * bmp) +{ + const char * out[2] = { + " ", + "\033[7m \033[0m", + }; + + unsigned char * line; + size_t x, y; + + line = bmp->bits; + + for (y = 0; y < bmp->height; ++y) { + + for (x = 0; x < bmp->width; ++x) { + + int mask = 1 << (x % CHAR_BIT); + int byte = line[x / CHAR_BIT]; + + fprintf(file, "%s", out[!!(byte & mask)]); + } + + fputc('\n', file); + + line += bmp->stride; + } +} + +void output_png(FILE * file, const struct qr_bitmap * bmp, const char * comment) +{ + const int px_size = 4; + png_structp png_ptr; + png_infop info_ptr; + png_text text; + int x, y, p; + unsigned char * row; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto err; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + goto err; + + if (setjmp(png_jmpbuf(png_ptr))) + goto err; + + png_init_io(png_ptr, file); + png_set_IHDR(png_ptr, info_ptr, + (bmp->width + 8) * px_size, + (bmp->height + 8) * px_size, + 1, + PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + text.compression = PNG_TEXT_COMPRESSION_NONE; + text.key = "Software"; + text.text = (char *) comment; + text.text_length = strlen(comment); + png_set_text(png_ptr, info_ptr, &text, 1); + + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + + row = malloc((bmp->width + 8) * px_size); + memset(row, 1, (bmp->width + 8) * px_size); + for (y = 0; y < 4; ++y) + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + + for (y = 0; y < bmp->height; ++y) { + const unsigned char * bmp_row = bmp->bits + y * bmp->stride; + unsigned char * out = row + 4 * px_size; + for (x = 0; x < bmp->width; ++x) { + int px = bmp_row[x / CHAR_BIT] & (1 << (x % CHAR_BIT)); + for (p = 0; p < px_size; ++p) + *out++ = !px; + } + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + } + + memset(row, 1, (bmp->width + 8) * px_size); + for (y = 0; y < 4; ++y) + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + + free(row); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, NULL); + return; + +err: + fprintf(stderr, "error writing PNG\n"); + exit(2); +} + +void show_help() { + fprintf(stderr, + "Usage:\n\t%s [options] <data>\n\n" + "\t-h Display this help message\n" + "\t-f <file> File containing data to encode (- for stdin)\n" + "\t-v <n> Specify QR version (size) 1 <= n <= 40\n" + "\t-e <type> Specify EC type: L, M, Q, H\n" + "\t-a Output as ANSI graphics (default)\n" + "\t-p Output as PBM\n" + "\t-g Output as PNG\n" + "\t-o <file> File to write (- for stdout)\n\n", + "qrgen"); +} + +void set_default_config(struct config * conf) +{ + conf->version = 0; + conf->ec = QR_EC_LEVEL_M; + conf->dtype = QR_DATA_8BIT; + conf->format = FORMAT_ANSI; + conf->file = NULL; + conf->outfile = NULL; + conf->input = NULL; +} + +void parse_options(int argc, char ** argv, struct config * conf) +{ + int c; + + for (;;) { + c = getopt(argc, argv, ":hf:v:e:t:apgo:"); + + if (c == -1) /* no more options */ + break; + + switch (c) { + case 'h': /* help */ + show_help(); + exit(0); + break; + case 'f': /* file */ + conf->file = optarg; + break; + case 'v': /* version */ + conf->version = atoi(optarg); + if (conf->version < 1 || conf->version > 40) { + fprintf(stderr, + "Version must be between 1 and 40\n"); + exit(1); + } + break; + case 'e': /* ec */ + switch (tolower(optarg[0])) { + case 'l': conf->ec = QR_EC_LEVEL_L; break; + case 'm': conf->ec = QR_EC_LEVEL_M; break; + case 'q': conf->ec = QR_EC_LEVEL_Q; break; + case 'h': conf->ec = QR_EC_LEVEL_H; break; + default: + fprintf(stderr, + "Invalid EC type (%c). Choose from" + " L, M, Q or H.\n", optarg[0]); + } + break; + case 't': /* type */ + fprintf(stderr, "XXX: ignored \"type\"\n"); + break; + case 'a': /* ansi */ + conf->format = FORMAT_ANSI; break; + case 'p': /* pnm */ + conf->format = FORMAT_PBM; break; + case 'g': /* png */ + conf->format = FORMAT_PNG; break; + case 'o': /* output file */ + conf->outfile = optarg; break; + case ':': + fprintf(stderr, + "Argument \"%s\" missing parameter\n", + argv[optind-1]); + exit(1); + break; + case '?': default: + fprintf(stderr, + "Invalid argument: \"%s\"\n" + "Try -h for help\n", + argv[optind-1]); + exit(1); + break; + } + } + + + if (optind < argc) + conf->input = argv[optind++]; + + if (!conf->file && !conf->input) { + fprintf(stderr, "No data (try -h for help)\n"); + exit(1); + } +} + +void slurp_file(const char * path, char ** data, size_t * len) +{ + const size_t chunk_size = 65536; + FILE * file; + char * tmpbuf; + size_t count; + + if (strcmp(path, "-") == 0) + file = stdin; + else + file = fopen(path, "rb"); + + if (!file) { + fprintf(stderr, "Failed to open %s\n", path); + exit(2); + } + + *data = NULL; + *len = 0; + + do { + tmpbuf = realloc(*data, *len + chunk_size); + if (!tmpbuf) { + perror("realloc"); + exit(2); + } + *data = tmpbuf; + count = fread(*data + *len, 1, chunk_size, file); + if (count == 0 && !feof(file)) { + perror("fread"); + exit(2); + } + *len += count; + } while (*len == chunk_size); + + fclose(file); +} + +int main(int argc, char ** argv) { + + struct config conf; + struct qr_code * code; + char * file_data; + size_t len; + FILE * outfile; + + set_default_config(&conf); + parse_options(argc, argv, &conf); + + if (conf.file) + slurp_file(conf.file, &file_data, &len); + else + len = strlen(conf.input); + + if (!conf.outfile) { + switch (conf.format) { + case FORMAT_ANSI: + conf.outfile = "-"; break; + case FORMAT_PBM: + conf.outfile = "qr.pbm"; break; + case FORMAT_PNG: + conf.outfile = "qr.png"; break; + } + } + + if (strcmp(conf.outfile, "-") == 0) { + outfile = stdout; + } else { + outfile = fopen(conf.outfile, "wb"); + if (!outfile) { + perror("fopen"); + exit(2); + } + } + + code = create(conf.version, + conf.ec, + conf.dtype, + conf.file ? file_data : conf.input, + len); + + if (conf.file) + free(file_data); + + switch (conf.format) { + case FORMAT_ANSI: + output_ansi(outfile, code->modules); + break; + case FORMAT_PBM: + output_pbm(outfile, code->modules, "libqr v" QR_VERSION); + break; + case FORMAT_PNG: + output_png(outfile, code->modules, "libqr v" QR_VERSION); + break; + } + + fclose(outfile); + qr_code_destroy(code); + + return 0; +} + diff --git a/lpg/libqr/qrparse.c b/lpg/libqr/qrparse.c new file mode 100644 index 0000000..1b5c80e --- /dev/null +++ b/lpg/libqr/qrparse.c @@ -0,0 +1,59 @@ +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> + +#include <qr/code.h> +#include <qr/data.h> +#include <qr/parse.h> + +#include "testqr.xbm" + +int main(int argc, char ** argv) +{ + struct qr_data * data; + int ret; + + ret = qr_code_parse(testqr_bits, + testqr_width, + (testqr_width + CHAR_BIT - 1) / CHAR_BIT, + testqr_height, + &data); + + fprintf(stderr, "qr_code_parse returned %d\n", ret); + + if (ret == 0) { + const char * ec_str, * type_str; + char * data_str; + size_t data_len; + enum qr_data_type data_type; + + fprintf(stderr, "version = %d\n", data->version); + switch (data->ec) { + case QR_EC_LEVEL_L: ec_str = "L"; break; + case QR_EC_LEVEL_M: ec_str = "M"; break; + case QR_EC_LEVEL_Q: ec_str = "Q"; break; + case QR_EC_LEVEL_H: ec_str = "H"; break; + default: ec_str = "(unknown)"; break; + } + fprintf(stderr, "EC level %s\n", ec_str); + + data_type = qr_parse_data(data, &data_str, &data_len); + switch (data_type) { + case QR_DATA_INVALID: type_str = "(invalid)"; break; + case QR_DATA_ECI: type_str = "ECI"; break; + case QR_DATA_NUMERIC: type_str = "numeric"; break; + case QR_DATA_ALPHA: type_str = "alpha"; break; + case QR_DATA_8BIT: type_str = "8-bit"; break; + case QR_DATA_KANJI: type_str = "kanji"; break; + case QR_DATA_MIXED: type_str = "mixed"; break; + case QR_DATA_FNC1: type_str = "FNC1"; break; + default: type_str = "(unknown)"; break; + } + fprintf(stderr, "Data type: %s; %lu bytes\nContent: %s\n", type_str, (unsigned long) data_len, data_str); + free(data_str); + qr_data_destroy(data); + } + + return 0; +} + diff --git a/lpg/libqr/testqr.xbm b/lpg/libqr/testqr.xbm new file mode 100644 index 0000000..ca2966f --- /dev/null +++ b/lpg/libqr/testqr.xbm @@ -0,0 +1,19 @@ +#define testqr_width 37 +#define testqr_height 37 +static unsigned char testqr_bits[] = { + 0x7F, 0x63, 0xFB, 0xDA, 0x1F, 0x41, 0xD7, 0xAD, 0x55, 0x10, 0x5D, 0x3D, + 0xE9, 0x5E, 0x17, 0x5D, 0xC6, 0xB5, 0x43, 0x17, 0x5D, 0x41, 0x61, 0x52, + 0x17, 0x41, 0x76, 0xC9, 0x46, 0x10, 0x7F, 0x55, 0x55, 0xD5, 0x1F, 0x00, + 0x98, 0x47, 0x05, 0x00, 0xF9, 0x99, 0xE0, 0x36, 0x1D, 0x24, 0x01, 0x07, + 0xC7, 0x0A, 0x60, 0xBC, 0xB9, 0x44, 0x16, 0x81, 0x21, 0x9D, 0x8D, 0x1B, + 0xCF, 0x8E, 0x83, 0x51, 0x16, 0xA2, 0x1B, 0xFD, 0x3A, 0x1B, 0xEB, 0x3B, + 0x1D, 0x5B, 0x17, 0xA9, 0xF6, 0x70, 0xF8, 0x1F, 0xC1, 0x68, 0x66, 0xFD, + 0x1D, 0xB3, 0x07, 0xC2, 0x54, 0x04, 0xD8, 0x0C, 0x4F, 0x4D, 0x15, 0x11, + 0xA1, 0x99, 0xA2, 0x11, 0x60, 0x65, 0xCC, 0x49, 0x05, 0x1D, 0x1F, 0xC3, + 0x8E, 0x15, 0x64, 0xED, 0xB9, 0x9A, 0x02, 0x90, 0x03, 0x1D, 0xAA, 0x0D, + 0x45, 0xD8, 0x72, 0x92, 0x01, 0x83, 0x53, 0x6E, 0x55, 0x06, 0xC7, 0xC4, + 0x02, 0xB4, 0x1B, 0x19, 0x24, 0x0F, 0x8D, 0x12, 0xE5, 0xAA, 0xAA, 0xF3, + 0x03, 0x00, 0x5F, 0x66, 0x11, 0x11, 0x7F, 0x73, 0x8F, 0x57, 0x1B, 0x41, + 0x8D, 0x39, 0x1B, 0x19, 0x5D, 0x3B, 0x3D, 0xFB, 0x13, 0x5D, 0x03, 0x70, + 0x62, 0x1C, 0x5D, 0x6A, 0x46, 0x37, 0x19, 0x41, 0xDA, 0x62, 0xF8, 0x1D, + 0x7F, 0x7B, 0x3E, 0x58, 0x14, }; |