aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/quirc/demo
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/lib/quirc/demo')
-rw-r--r--circuitpython/lib/quirc/demo/camera.c591
-rw-r--r--circuitpython/lib/quirc/demo/camera.h104
-rw-r--r--circuitpython/lib/quirc/demo/convert.c103
-rw-r--r--circuitpython/lib/quirc/demo/convert.h39
-rw-r--r--circuitpython/lib/quirc/demo/demo.c333
-rw-r--r--circuitpython/lib/quirc/demo/demo_opencv.cxx263
-rw-r--r--circuitpython/lib/quirc/demo/demoutil.c71
-rw-r--r--circuitpython/lib/quirc/demo/demoutil.h42
-rw-r--r--circuitpython/lib/quirc/demo/dthash.c139
-rw-r--r--circuitpython/lib/quirc/demo/dthash.h61
-rw-r--r--circuitpython/lib/quirc/demo/mjpeg.c284
-rw-r--r--circuitpython/lib/quirc/demo/mjpeg.h54
-rw-r--r--circuitpython/lib/quirc/demo/scanner.c214
13 files changed, 2298 insertions, 0 deletions
diff --git a/circuitpython/lib/quirc/demo/camera.c b/circuitpython/lib/quirc/demo/camera.c
new file mode 100644
index 0000000..8e8de6b
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/camera.c
@@ -0,0 +1,591 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <sys/videoio.h>
+#else
+#include <linux/videodev2.h>
+#endif
+#include "camera.h"
+
+/************************************************************************
+ * Fraction arithmetic
+ */
+
+static int gcd(int a, int b)
+{
+ if (a < 0)
+ a = -a;
+ if (b < 0)
+ b = -b;
+
+ for (;;) {
+ if (a < b) {
+ const int t = a;
+
+ a = b;
+ b = t;
+ }
+
+ if (!b)
+ break;
+
+ a %= b;
+ }
+
+ return a;
+}
+
+static void frac_reduce(const struct v4l2_fract *f, struct v4l2_fract *g)
+{
+ const int x = gcd(f->numerator, f->denominator);
+ int n = f->numerator;
+ int d = f->denominator;
+
+ if (d < 0) {
+ n = -n;
+ d = -d;
+ }
+
+ g->numerator = n / x;
+ g->denominator = d / x;
+}
+
+static void frac_add(const struct v4l2_fract *a, const struct v4l2_fract *b,
+ struct v4l2_fract *r)
+{
+ r->numerator = a->numerator * b->denominator +
+ b->numerator * b->denominator;
+ r->denominator = a->denominator * b->denominator;
+
+ frac_reduce(r, r);
+}
+
+static void frac_sub(const struct v4l2_fract *a, const struct v4l2_fract *b,
+ struct v4l2_fract *r)
+{
+ r->numerator = a->numerator * b->denominator -
+ b->numerator * b->denominator;
+ r->denominator = a->denominator * b->denominator;
+
+ frac_reduce(r, r);
+}
+
+static int frac_cmp(const struct v4l2_fract *a, const struct v4l2_fract *b)
+{
+ return a->numerator * b->denominator - b->numerator * b->denominator;
+}
+
+static void frac_mul(const struct v4l2_fract *a, const struct v4l2_fract *b,
+ struct v4l2_fract *r)
+{
+ r->numerator = a->numerator * b->numerator;
+ r->denominator = a->denominator * b->denominator;
+
+ frac_reduce(r, r);
+}
+
+static void frac_div(const struct v4l2_fract *a, const struct v4l2_fract *b,
+ struct v4l2_fract *r)
+{
+ r->numerator = a->numerator * b->denominator;
+ r->denominator = a->denominator * b->numerator;
+
+ frac_reduce(r, r);
+}
+
+static int frac_cmp_ref(const struct v4l2_fract *ref,
+ const struct v4l2_fract *a,
+ const struct v4l2_fract *b)
+{
+ struct v4l2_fract da;
+ struct v4l2_fract db;
+
+ frac_sub(a, ref, &da);
+ frac_sub(b, ref, &db);
+
+ if (da.numerator < 0)
+ da.numerator = -da.numerator;
+ if (db.numerator < 0)
+ db.numerator = -db.numerator;
+
+ return frac_cmp(&da, &db);
+}
+
+/************************************************************************
+ * Parameter searching and choosing
+ */
+
+static camera_format_t map_fmt(uint32_t pf)
+{
+ if (pf == V4L2_PIX_FMT_YUYV)
+ return CAMERA_FORMAT_YUYV;
+
+ if (pf == V4L2_PIX_FMT_MJPEG)
+ return CAMERA_FORMAT_MJPEG;
+
+ return CAMERA_FORMAT_UNKNOWN;
+}
+
+static int find_best_format(int fd, uint32_t *fmt_ret)
+{
+ struct v4l2_fmtdesc best;
+ int i = 1;
+
+ memset(&best, 0, sizeof(best));
+ best.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ best.index = 0;
+
+ if (ioctl(fd, VIDIOC_ENUM_FMT, &best) < 0)
+ return -1;
+
+ for (;;) {
+ struct v4l2_fmtdesc f;
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f.index = ++i;
+
+ if (ioctl(fd, VIDIOC_ENUM_FMT, &f) < 0)
+ break;
+
+ if (map_fmt(f.pixelformat) > map_fmt(best.pixelformat))
+ memcpy(&best, &f, sizeof(best));
+ }
+
+ if (fmt_ret)
+ *fmt_ret = best.pixelformat;
+
+ return 0;
+}
+
+static int step_to_discrete(int min, int max, int step, int target)
+{
+ int offset;
+
+ if (target < min)
+ return min;
+
+ if (target > max)
+ return max;
+
+ offset = (target - min) % step;
+ if ((offset * 2 > step) && (target + step <= max))
+ target += step;
+
+ return target - offset;
+}
+
+static int score_discrete(const struct v4l2_frmsizeenum *f, int w, int h)
+{
+ const int dw = f->discrete.width - w;
+ const int dh = f->discrete.height - h;
+
+ return dw * dw + dh * dh;
+}
+
+static int find_best_size(int fd, uint32_t pixel_format,
+ int target_w, int target_h,
+ int *ret_w, int *ret_h)
+{
+ struct v4l2_frmsizeenum best;
+ int i = 1;
+
+ memset(&best, 0, sizeof(best));
+ best.index = 0;
+ best.pixel_format = pixel_format;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &best) < 0)
+ return -1;
+
+ if (best.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
+ *ret_w = step_to_discrete(best.stepwise.min_width,
+ best.stepwise.max_width,
+ best.stepwise.step_width,
+ target_w);
+ *ret_h = step_to_discrete(best.stepwise.min_height,
+ best.stepwise.max_height,
+ best.stepwise.step_height,
+ target_h);
+ return 0;
+ }
+
+ for (;;) {
+ struct v4l2_frmsizeenum f;
+
+ memset(&f, 0, sizeof(f));
+ f.index = ++i;
+ f.pixel_format = pixel_format;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &f) < 0)
+ break;
+
+ if (score_discrete(&f, target_w, target_h) <
+ score_discrete(&best, target_w, target_h))
+ memcpy(&best, &f, sizeof(best));
+ }
+
+ *ret_w = best.discrete.width;
+ *ret_h = best.discrete.height;
+
+ return 0;
+}
+
+static int find_best_rate(int fd, uint32_t pixel_format,
+ int w, int h, int target_n, int target_d,
+ int *ret_n, int *ret_d)
+{
+ const struct v4l2_fract target = {
+ .numerator = target_n,
+ .denominator = target_d
+ };
+ struct v4l2_frmivalenum best;
+ int i = 1;
+
+ memset(&best, 0, sizeof(best));
+ best.index = 0;
+ best.pixel_format = pixel_format;
+ best.width = w;
+ best.height = h;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &best) < 0)
+ return -1;
+
+ if (best.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
+ struct v4l2_fract t;
+
+ if (frac_cmp(&target, &best.stepwise.min) < 0) {
+ *ret_n = best.stepwise.min.numerator;
+ *ret_d = best.stepwise.min.denominator;
+ }
+
+ if (frac_cmp(&target, &best.stepwise.max) > 0) {
+ *ret_n = best.stepwise.max.numerator;
+ *ret_d = best.stepwise.max.denominator;
+ }
+
+ frac_sub(&target, &best.stepwise.min, &t);
+ frac_div(&t, &best.stepwise.step, &t);
+ if (t.numerator * 2 >= t.denominator)
+ t.numerator += t.denominator;
+ t.numerator /= t.denominator;
+ t.denominator = 1;
+ frac_mul(&t, &best.stepwise.step, &t);
+ frac_add(&t, &best.stepwise.max, &t);
+
+ if (frac_cmp(&t, &best.stepwise.max) > 0) {
+ *ret_n = best.stepwise.max.numerator;
+ *ret_d = best.stepwise.max.denominator;
+ } else {
+ *ret_n = t.numerator;
+ *ret_d = t.denominator;
+ }
+
+ return 0;
+ }
+
+ for (;;) {
+ struct v4l2_frmivalenum f;
+
+ memset(&f, 0, sizeof(f));
+ f.index = ++i;
+ f.pixel_format = pixel_format;
+ f.width = w;
+ f.height = h;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &f) < 0)
+ break;
+
+ if (frac_cmp_ref(&target, &f.discrete, &best.discrete) < 0)
+ memcpy(&best, &f, sizeof(best));
+ }
+
+ *ret_n = best.discrete.numerator;
+ *ret_d = best.discrete.denominator;
+
+ return 0;
+}
+
+/************************************************************************
+ * Public interface
+ */
+
+void camera_init(struct camera *c)
+{
+ c->fd = -1;
+ c->buf_count = 0;
+ c->s_on = 0;
+}
+
+void camera_destroy(struct camera *c)
+{
+ camera_close(c);
+}
+
+int camera_open(struct camera *c, const char *path,
+ int target_w, int target_h,
+ int tr_n, int tr_d)
+{
+ struct v4l2_format fmt;
+ struct v4l2_streamparm parm;
+ uint32_t pf;
+ int w, h;
+ int n, d;
+
+ if (c->fd >= 0)
+ camera_close(c);
+
+ /* Open device and get basic properties */
+ c->fd = open(path, O_RDWR);
+ if (c->fd < 0)
+ return -1;
+
+ /* Find a pixel format from the list */
+ if (find_best_format(c->fd, &pf) < 0)
+ goto fail;
+
+ /* Find a frame size */
+ if (find_best_size(c->fd, pf, target_w, target_h, &w, &h) < 0)
+ goto fail;
+
+ /* Find a frame rate */
+ if (find_best_rate(c->fd, pf, w, h, tr_n, tr_d, &n, &d) < 0)
+ goto fail;
+
+ /* Set format */
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = w;
+ fmt.fmt.pix.height = h;
+ fmt.fmt.pix.pixelformat = pf;
+ if (ioctl(c->fd, VIDIOC_S_FMT, &fmt) < 0)
+ goto fail;
+
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl(c->fd, VIDIOC_G_FMT, &fmt) < 0)
+ goto fail;
+
+ /* Set frame interval */
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parm.parm.capture.timeperframe.numerator = n;
+ parm.parm.capture.timeperframe.denominator = d;
+ if (ioctl(c->fd, VIDIOC_S_PARM, &parm) < 0)
+ goto fail;
+
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl(c->fd, VIDIOC_G_PARM, &parm) < 0)
+ goto fail;
+
+ c->parms.format = map_fmt(fmt.fmt.pix.pixelformat);
+ c->parms.width = fmt.fmt.pix.width;
+ c->parms.height = fmt.fmt.pix.height;
+ c->parms.pitch_bytes = fmt.fmt.pix.bytesperline;
+ c->parms.interval_n = parm.parm.capture.timeperframe.numerator;
+ c->parms.interval_d = parm.parm.capture.timeperframe.denominator;
+
+ return 0;
+
+fail:
+ {
+ const int e = errno;
+
+ close(c->fd);
+ c->fd = -1;
+ errno = e;
+ }
+
+ return -1;
+}
+
+void camera_close(struct camera *c)
+{
+ camera_off(c);
+ camera_unmap(c);
+
+ if (c->fd < 0)
+ return;
+
+ close(c->fd);
+ c->fd = -1;
+}
+
+int camera_map(struct camera *c, int buf_count)
+{
+ struct v4l2_requestbuffers reqbuf;
+ int count;
+ int i;
+
+ if (buf_count > CAMERA_MAX_BUFFERS)
+ buf_count = CAMERA_MAX_BUFFERS;
+
+ if (buf_count <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (c->fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (c->buf_count)
+ camera_unmap(c);
+
+ memset(&reqbuf, 0, sizeof(reqbuf));
+ reqbuf.count = buf_count;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ if (ioctl(c->fd, VIDIOC_REQBUFS, &reqbuf) < 0)
+ return -1;
+
+ count = reqbuf.count;
+ if (count > CAMERA_MAX_BUFFERS)
+ count = CAMERA_MAX_BUFFERS;
+
+ /* Query all buffers */
+ for (i = 0; i < count; i++) {
+ struct v4l2_buffer buf;
+ struct camera_buffer *cb = &c->buf_desc[i];
+
+ memset(&buf, 0, sizeof(buf));
+ buf.index = i;
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ if (ioctl(c->fd, VIDIOC_QUERYBUF, &buf) < 0)
+ return -1;
+
+ cb->offset = buf.m.offset;
+ cb->size = buf.length;
+ cb->addr = mmap(NULL, cb->size, PROT_READ,
+ MAP_SHARED, c->fd, cb->offset);
+
+ if (cb->addr == MAP_FAILED) {
+ const int save = errno;
+
+ i--;
+ while (i >= 0) {
+ cb = &c->buf_desc[i--];
+ munmap(cb->addr, cb->size);
+ }
+
+ errno = save;
+ return -1;
+ }
+ }
+
+ c->buf_count = count;
+ return 0;
+}
+
+void camera_unmap(struct camera *c)
+{
+ int i;
+
+ for (i = 0; i < c->buf_count; i++) {
+ struct camera_buffer *cb = &c->buf_desc[i];
+
+ munmap(cb->addr, cb->size);
+ }
+
+ c->buf_count = 0;
+}
+
+int camera_on(struct camera *c)
+{
+ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (c->s_on)
+ return 0;
+
+ if (c->fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (ioctl(c->fd, VIDIOC_STREAMON, &type) < 0)
+ return -1;
+
+ c->s_on = 1;
+ c->s_qc = 0;
+ c->s_qhead = 0;
+ return 0;
+}
+
+void camera_off(struct camera *c)
+{
+ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (!c->s_on)
+ return;
+
+ ioctl(c->fd, VIDIOC_STREAMOFF, &type);
+ c->s_on = 0;
+}
+
+int camera_enqueue_all(struct camera *c)
+{
+ while (c->s_qc < c->buf_count) {
+ struct v4l2_buffer buf;
+
+ memset(&buf, 0, sizeof(buf));
+ buf.index = (c->s_qc + c->s_qhead) % c->buf_count;
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ if (ioctl(c->fd, VIDIOC_QBUF, &buf) < 0)
+ return -1;
+
+ c->s_qc++;
+ }
+
+ return 0;
+}
+
+int camera_dequeue_one(struct camera *c)
+{
+ struct v4l2_buffer buf;
+
+ if (!c->s_qc) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&buf, 0, sizeof(buf));
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (ioctl(c->fd, VIDIOC_DQBUF, &buf) < 0)
+ return -1;
+
+ c->s_qc--;
+ if (++c->s_qhead >= c->buf_count)
+ c->s_qhead = 0;
+
+ return 0;
+}
diff --git a/circuitpython/lib/quirc/demo/camera.h b/circuitpython/lib/quirc/demo/camera.h
new file mode 100644
index 0000000..511b5bd
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/camera.h
@@ -0,0 +1,104 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CAMERA_H_
+#define CAMERA_H_
+
+#include <stddef.h>
+
+#define CAMERA_MAX_BUFFERS 32
+
+typedef enum {
+ CAMERA_FORMAT_UNKNOWN = 0,
+ CAMERA_FORMAT_MJPEG,
+ CAMERA_FORMAT_YUYV
+} camera_format_t;
+
+struct camera_parms {
+ camera_format_t format;
+ int width;
+ int height;
+ int pitch_bytes;
+ int interval_n;
+ int interval_d;
+};
+
+struct camera_buffer {
+ void *addr;
+ size_t size;
+ unsigned long offset;
+};
+
+struct camera {
+ int fd;
+
+ struct camera_parms parms;
+
+ struct camera_buffer buf_desc[CAMERA_MAX_BUFFERS];
+ int buf_count;
+
+ /* Stream state */
+ int s_on;
+ int s_qc;
+ int s_qhead;
+};
+
+/* Initialize/destroy a camera. No resources are allocated. */
+void camera_init(struct camera *c);
+void camera_destroy(struct camera *c);
+
+/* Open/close the camera device */
+int camera_open(struct camera *c, const char *path,
+ int target_w, int target_h,
+ int tr_n, int tr_d);
+void camera_close(struct camera *c);
+
+static inline int camera_get_fd(const struct camera *c)
+{
+ return c->fd;
+}
+
+static inline const struct camera_parms *camera_get_parms
+ (const struct camera *c)
+{
+ return &c->parms;
+}
+
+/* Map buffers */
+int camera_map(struct camera *c, int buf_count);
+void camera_unmap(struct camera *c);
+
+static inline int camera_get_buf_count(const struct camera *c)
+{
+ return c->buf_count;
+}
+
+/* Switch streaming on/off */
+int camera_on(struct camera *c);
+void camera_off(struct camera *c);
+
+/* Enqueue/dequeue buffers (count = 0 means enqueue all) */
+int camera_enqueue_all(struct camera *c);
+int camera_dequeue_one(struct camera *c);
+
+/* Fetch the oldest dequeued buffer */
+static inline const struct camera_buffer *camera_get_head
+ (const struct camera *c)
+{
+ return &c->buf_desc[(c->s_qhead + c->buf_count - 1) % c->buf_count];
+}
+
+#endif
diff --git a/circuitpython/lib/quirc/demo/convert.c b/circuitpython/lib/quirc/demo/convert.c
new file mode 100644
index 0000000..9e6f458
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/convert.c
@@ -0,0 +1,103 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "convert.h"
+
+#define CHANNEL_CLAMP(dst, tmp, lum, chrom) \
+ (tmp) = ((lum) + (chrom)) >> 8; \
+ if ((tmp) < 0) \
+ (tmp) = 0; \
+ if ((tmp) > 255) \
+ (tmp) = 255; \
+ (dst) = (tmp);
+
+void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch)
+{
+ int y;
+
+ for (y = 0; y < h; y++) {
+ int x;
+ const uint8_t *srow = src + y * src_pitch;
+ uint8_t *drow = dst + y * dst_pitch;
+
+ for (x = 0; x < w; x += 2) {
+ /* ITU-R colorspace assumed */
+ int y0 = (int)srow[0] * 256;
+ int y1 = (int)srow[2] * 256;
+ int cr = (int)srow[3] - 128;
+ int cb = (int)srow[1] - 128;
+ int r = cr * 359;
+ int g = -cb * 88 - 128 * cr;
+ int b = 454 * cb;
+ int z;
+
+ CHANNEL_CLAMP(drow[0], z, y0, b);
+ CHANNEL_CLAMP(drow[1], z, y0, g);
+ CHANNEL_CLAMP(drow[2], z, y0, r);
+ CHANNEL_CLAMP(drow[4], z, y1, b);
+ CHANNEL_CLAMP(drow[5], z, y1, g);
+ CHANNEL_CLAMP(drow[6], z, y1, r);
+
+ srow += 4;
+ drow += 8;
+ }
+ }
+}
+
+void yuyv_to_luma(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch)
+{
+ int y;
+
+ for (y = 0; y < h; y++) {
+ int x;
+ const uint8_t *srow = src + y * src_pitch;
+ uint8_t *drow = dst + y * dst_pitch;
+
+ for (x = 0; x < w; x += 2) {
+ *(drow++) = srow[0];
+ *(drow++) = srow[2];
+ srow += 4;
+ }
+ }
+}
+
+void rgb32_to_luma(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch)
+{
+ int y;
+
+ for (y = 0; y < h; y++) {
+ const uint8_t *rgb32 = src + src_pitch * y;
+ uint8_t *gray = dst + y * dst_pitch;
+ int i;
+
+ for (i = 0; i < w; i++) {
+ /* ITU-R colorspace assumed */
+ int r = (int)rgb32[2];
+ int g = (int)rgb32[1];
+ int b = (int)rgb32[0];
+ int sum = r * 59 + g * 150 + b * 29;
+
+ *(gray++) = sum >> 8;
+ rgb32 += 4;
+ }
+ }
+}
diff --git a/circuitpython/lib/quirc/demo/convert.h b/circuitpython/lib/quirc/demo/convert.h
new file mode 100644
index 0000000..ec2a14e
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/convert.h
@@ -0,0 +1,39 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CONVERT_H_
+#define CONVERT_H_
+
+#include <stdint.h>
+
+/* Convert 4:2:2 YUYV format to RGB32 format. The source and destination
+ * frames are expected to be the same size.
+ */
+void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch);
+
+/* Extract the luma channel from a 4:2:2 YUYV image. */
+void yuyv_to_luma(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch);
+
+/* Extract the luma channel from an RGB32 image. */
+void rgb32_to_luma(const uint8_t *src, int src_pitch,
+ int w, int h,
+ uint8_t *dst, int dst_pitch);
+
+#endif
diff --git a/circuitpython/lib/quirc/demo/demo.c b/circuitpython/lib/quirc/demo/demo.c
new file mode 100644
index 0000000..178fde8
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/demo.c
@@ -0,0 +1,333 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <SDL.h>
+#include <SDL_gfxPrimitives.h>
+#include <quirc.h>
+#include <time.h>
+#include <getopt.h>
+
+#include "camera.h"
+#include "mjpeg.h"
+#include "convert.h"
+#include "dthash.h"
+#include "demoutil.h"
+
+/* Collected command-line arguments */
+static const char *camera_path = "/dev/video0";
+static int video_width = 640;
+static int video_height = 480;
+static int want_frame_rate = 0;
+static int want_verbose = 0;
+static int printer_timeout = 2;
+
+static void fat_text(SDL_Surface *screen, int x, int y, const char *text)
+{
+ int i, j;
+
+ for (i = -1; i <= 1; i++)
+ for (j = -1; j <= 1; j++)
+ stringColor(screen, x + i, y + j, text, 0xffffffff);
+ stringColor(screen, x, y, text, 0x008000ff);
+}
+
+static void fat_text_cent(SDL_Surface *screen, int x, int y, const char *text)
+{
+ x -= strlen(text) * 4;
+
+ fat_text(screen, x, y, text);
+}
+
+static void draw_qr(SDL_Surface *screen, struct quirc *q, struct dthash *dt)
+{
+ int count = quirc_count(q);
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct quirc_code code;
+ struct quirc_data data;
+ quirc_decode_error_t err;
+ int j;
+ int xc = 0;
+ int yc = 0;
+ char buf[128];
+
+ quirc_extract(q, i, &code);
+
+ for (j = 0; j < 4; j++) {
+ struct quirc_point *a = &code.corners[j];
+ struct quirc_point *b = &code.corners[(j + 1) % 4];
+
+ xc += a->x;
+ yc += a->y;
+ lineColor(screen, a->x, a->y, b->x, b->y, 0x008000ff);
+ }
+
+ xc /= 4;
+ yc /= 4;
+
+ if (want_verbose) {
+ snprintf(buf, sizeof(buf), "Code size: %d cells",
+ code.size);
+ fat_text_cent(screen, xc, yc - 20, buf);
+ }
+
+ err = quirc_decode(&code, &data);
+
+ if (err) {
+ if (want_verbose)
+ fat_text_cent(screen, xc, yc,
+ quirc_strerror(err));
+ } else {
+ fat_text_cent(screen, xc, yc, (char *)data.payload);
+ print_data(&data, dt, want_verbose);
+
+ if (want_verbose) {
+ snprintf(buf, sizeof(buf),
+ "Ver: %d, ECC: %c, Mask: %d, Type: %d",
+ data.version, "MLHQ"[data.ecc_level],
+ data.mask, data.data_type);
+ fat_text_cent(screen, xc, yc + 20, buf);
+ }
+ }
+ }
+}
+
+static int main_loop(struct camera *cam, SDL_Surface *screen,
+ struct quirc *q, struct mjpeg_decoder *mj)
+{
+ SDL_Event ev;
+ time_t last_rate = 0;
+ int frame_count = 0;
+ char rate_text[64];
+ struct dthash dt;
+
+ rate_text[0] = 0;
+ dthash_init(&dt, printer_timeout);
+
+ for (;;) {
+ time_t now = time(NULL);
+ const struct camera_buffer *head;
+ const struct camera_parms *parms = camera_get_parms(cam);
+
+ if (camera_dequeue_one(cam) < 0) {
+ perror("camera_dequeue_one");
+ return -1;
+ }
+
+ head = camera_get_head(cam);
+
+ SDL_LockSurface(screen);
+ switch (parms->format) {
+ case CAMERA_FORMAT_MJPEG:
+ mjpeg_decode_rgb32(mj, head->addr, head->size,
+ screen->pixels, screen->pitch,
+ screen->w, screen->h);
+ break;
+
+ case CAMERA_FORMAT_YUYV:
+ yuyv_to_rgb32(head->addr, parms->width * 2,
+ parms->width, parms->height,
+ screen->pixels, screen->pitch);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown frame format\n");
+ return -1;
+ }
+
+ if (camera_enqueue_all(cam) < 0) {
+ perror("camera_enqueue_all");
+ return -1;
+ }
+
+ rgb32_to_luma(screen->pixels, screen->pitch,
+ screen->w, screen->h,
+ quirc_begin(q, NULL, NULL),
+ screen->w);
+ quirc_end(q);
+ SDL_UnlockSurface(screen);
+
+ draw_qr(screen, q, &dt);
+ if (want_frame_rate)
+ fat_text(screen, 5, 5, rate_text);
+ SDL_Flip(screen);
+
+ while (SDL_PollEvent(&ev) > 0) {
+ if (ev.type == SDL_QUIT)
+ return 0;
+
+ if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == 'q')
+ return 0;
+ }
+
+ if (now != last_rate) {
+ snprintf(rate_text, sizeof(rate_text),
+ "Frame rate: %d fps", frame_count);
+ frame_count = 0;
+ last_rate = now;
+ }
+
+ frame_count++;
+ }
+}
+
+static int run_demo(void)
+{
+ struct quirc *qr;
+ struct camera cam;
+ struct mjpeg_decoder mj;
+ const struct camera_parms *parms;
+ SDL_Surface *screen;
+
+ camera_init(&cam);
+ if (camera_open(&cam, camera_path, video_width, video_height,
+ 25, 1) < 0) {
+ perror("camera_open");
+ goto fail_qr;
+ }
+
+ if (camera_map(&cam, 8) < 0) {
+ perror("camera_map");
+ goto fail_qr;
+ }
+
+ if (camera_on(&cam) < 0) {
+ perror("camera_on");
+ goto fail_qr;
+ }
+
+ if (camera_enqueue_all(&cam) < 0) {
+ perror("camera_enqueue_all");
+ goto fail_qr;
+ }
+
+ parms = camera_get_parms(&cam);
+
+ qr = quirc_new();
+ if (!qr) {
+ perror("couldn't allocate QR decoder");
+ goto fail_qr;
+ }
+
+ if (quirc_resize(qr, parms->width, parms->height) < 0) {
+ perror("couldn't allocate QR buffer");
+ goto fail_qr_resize;
+ }
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ perror("couldn't init SDL");
+ goto fail_sdl_init;
+ }
+
+ screen = SDL_SetVideoMode(parms->width, parms->height, 32,
+ SDL_SWSURFACE | SDL_DOUBLEBUF);
+ if (!screen) {
+ perror("couldn't init video mode");
+ goto fail_video_mode;
+ }
+
+ mjpeg_init(&mj);
+ if (main_loop(&cam, screen, qr, &mj) < 0)
+ goto fail_main_loop;
+ mjpeg_free(&mj);
+
+ SDL_Quit();
+ quirc_destroy(qr);
+ camera_destroy(&cam);
+
+ return 0;
+
+fail_main_loop:
+ mjpeg_free(&mj);
+fail_video_mode:
+ SDL_Quit();
+fail_qr_resize:
+fail_sdl_init:
+ quirc_destroy(qr);
+fail_qr:
+ camera_destroy(&cam);
+
+ return -1;
+}
+
+static void usage(const char *progname)
+{
+ printf("Usage: %s [options]\n\n"
+"Valid options are:\n\n"
+" -f Show frame rate on screen.\n"
+" -v Show extra data for detected codes.\n"
+" -d <device> Specify camera device path.\n"
+" -s <WxH> Specify video dimensions.\n"
+" -p <timeout> Set printer timeout (seconds).\n"
+" --help Show this information.\n"
+" --version Show library version information.\n",
+ progname);
+}
+
+int main(int argc, char **argv)
+{
+ static const struct option longopts[] = {
+ {"help", 0, 0, 'H'},
+ {"version", 0, 0, 'V'},
+ {NULL, 0, 0, 0}
+ };
+ int opt;
+
+ printf("quirc demo\n");
+ printf("Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>\n");
+ printf("\n");
+
+ while ((opt = getopt_long(argc, argv, "d:s:fvg:p:",
+ longopts, NULL)) >= 0)
+ switch (opt) {
+ case 'V':
+ printf("Library version: %s\n", quirc_version());
+ return 0;
+
+ case 'H':
+ usage(argv[0]);
+ return 0;
+
+ case 'v':
+ want_verbose = 1;
+ break;
+
+ case 'f':
+ want_frame_rate = 1;
+ break;
+
+ case 's':
+ if (parse_size(optarg, &video_width, &video_height) < 0)
+ return -1;
+ break;
+
+ case 'p':
+ printer_timeout = atoi(optarg);
+ break;
+
+ case 'd':
+ camera_path = optarg;
+ break;
+
+ case '?':
+ fprintf(stderr, "Try --help for usage information\n");
+ return -1;
+ }
+
+ return run_demo();
+}
diff --git a/circuitpython/lib/quirc/demo/demo_opencv.cxx b/circuitpython/lib/quirc/demo/demo_opencv.cxx
new file mode 100644
index 0000000..d6dbe8b
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/demo_opencv.cxx
@@ -0,0 +1,263 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#include <quirc.h>
+#include <time.h>
+#include <getopt.h>
+
+#include <opencv2/videoio.hpp>
+#include <opencv2/imgproc.hpp>
+#include <opencv2/highgui.hpp>
+
+using namespace cv;
+
+#include "camera.h"
+#include "convert.h"
+#include "dthash.h"
+#include "demoutil.h"
+
+/* Collected command-line arguments */
+static int want_frame_rate = 0;
+static int want_verbose = 0;
+static int printer_timeout = 2;
+
+static const int font = FONT_HERSHEY_PLAIN;
+static const int thickness = 2;
+static const double font_scale = 1.5;
+static Scalar blue = Scalar(255, 0, 8);
+
+static void fat_text(Mat &screen, int x, int y, const char *text)
+{
+ putText(screen, text, Point(x, y), font, font_scale, blue, thickness);
+}
+
+static void fat_text_cent(Mat &screen, int x, int y, const char *text)
+{
+ int baseline;
+
+ Size size = getTextSize(text, font, font_scale, thickness, &baseline);
+ putText(screen, text, Point(x - size.width / 2, y - size.height /2),
+ font, font_scale, blue, thickness);
+}
+
+static void draw_qr(Mat &screen, struct quirc *q, struct dthash *dt)
+{
+ int count = quirc_count(q);
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct quirc_code code;
+ struct quirc_data data;
+ quirc_decode_error_t err;
+ int j;
+ int xc = 0;
+ int yc = 0;
+ char buf[128];
+
+ quirc_extract(q, i, &code);
+
+ for (j = 0; j < 4; j++) {
+ struct quirc_point *a = &code.corners[j];
+ struct quirc_point *b = &code.corners[(j + 1) % 4];
+
+ xc += a->x;
+ yc += a->y;
+ line(screen, Point(a->x, a->y), Point(b->x, b->y), blue, 8);
+ }
+
+ xc /= 4;
+ yc /= 4;
+
+ if (want_verbose) {
+ snprintf(buf, sizeof(buf), "Code size: %d cells",
+ code.size);
+ fat_text_cent(screen, xc, yc - 20, buf);
+ }
+
+ err = quirc_decode(&code, &data);
+
+ if (err) {
+ if (want_verbose)
+ fat_text_cent(screen, xc, yc, quirc_strerror(err));
+ } else {
+ fat_text_cent(screen, xc, yc, (const char *)data.payload);
+ print_data(&data, dt, want_verbose);
+
+ if (want_verbose) {
+ snprintf(buf, sizeof(buf),
+ "Ver: %d, ECC: %c, Mask: %d, Type: %d",
+ data.version, "MLHQ"[data.ecc_level],
+ data.mask, data.data_type);
+ fat_text_cent(screen, xc, yc + 20, buf);
+ }
+ }
+ }
+}
+
+static int main_loop(VideoCapture &cap, struct quirc *q)
+{
+ time_t last_rate = 0;
+ int frame_count = 0;
+ char rate_text[64];
+ struct dthash dt;
+
+ rate_text[0] = 0;
+ dthash_init(&dt, printer_timeout);
+
+ Mat frame;
+ for (;;) {
+ time_t now = time(NULL);
+
+ cap.read(frame);
+ if (frame.empty()) {
+ perror("empty frame");
+ return 0;
+ }
+
+ int w;
+ int h;
+ uint8_t *buf = quirc_begin(q, &w, &h);
+
+ /* convert frame into buf */
+ assert(frame.cols == w);
+ assert(frame.rows == h);
+ Mat gray;
+ cvtColor(frame, gray, COLOR_BGR2GRAY, 0);
+ for (int y = 0; y < gray.rows; y++) {
+ for (int x = 0; x < gray.cols; x++) {
+ buf[(y * w + x)] = gray.at<uint8_t>(y, x);
+ }
+ }
+
+ quirc_end(q);
+
+ draw_qr(frame, q, &dt);
+ if (want_frame_rate)
+ fat_text(frame, 20, 20, rate_text);
+
+ imshow("quirc-demo-opencv", frame);
+ waitKey(5);
+
+ if (now != last_rate) {
+ snprintf(rate_text, sizeof(rate_text),
+ "Frame rate: %d fps", frame_count);
+ frame_count = 0;
+ last_rate = now;
+ }
+
+ frame_count++;
+ }
+}
+
+static int run_demo(void)
+{
+ struct quirc *qr;
+ VideoCapture cap(0);
+ unsigned int width;
+ unsigned int height;
+
+ if (!cap.isOpened()) {
+ perror("camera_open");
+ goto fail_qr;
+ }
+
+ width = cap.get(CAP_PROP_FRAME_WIDTH);
+ height = cap.get(CAP_PROP_FRAME_HEIGHT);
+
+ qr = quirc_new();
+ if (!qr) {
+ perror("couldn't allocate QR decoder");
+ goto fail_qr;
+ }
+
+ if (quirc_resize(qr, width, height) < 0) {
+ perror("couldn't allocate QR buffer");
+ goto fail_qr_resize;
+ }
+
+ if (main_loop(cap, qr) < 0) {
+ goto fail_main_loop;
+ }
+
+ quirc_destroy(qr);
+
+ return 0;
+
+fail_main_loop:
+fail_qr_resize:
+ quirc_destroy(qr);
+fail_qr:
+
+ return -1;
+}
+
+static void usage(const char *progname)
+{
+ printf("Usage: %s [options]\n\n"
+"Valid options are:\n\n"
+" -f Show frame rate on screen.\n"
+" -v Show extra data for detected codes.\n"
+" -p <timeout> Set printer timeout (seconds).\n"
+" --help Show this information.\n"
+" --version Show library version information.\n",
+ progname);
+}
+
+int main(int argc, char **argv)
+{
+ static const struct option longopts[] = {
+ {"help", 0, 0, 'H'},
+ {"version", 0, 0, 'V'},
+ {NULL, 0, 0, 0}
+ };
+ int opt;
+
+ printf("quirc demo with OpenCV\n");
+ printf("Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>\n");
+ printf("\n");
+
+ while ((opt = getopt_long(argc, argv, "fvg:p:",
+ longopts, NULL)) >= 0)
+ switch (opt) {
+ case 'V':
+ printf("Library version: %s\n", quirc_version());
+ return 0;
+
+ case 'H':
+ usage(argv[0]);
+ return 0;
+
+ case 'v':
+ want_verbose = 1;
+ break;
+
+ case 'f':
+ want_frame_rate = 1;
+ break;
+
+ case 'p':
+ printer_timeout = atoi(optarg);
+ break;
+
+ case '?':
+ fprintf(stderr, "Try --help for usage information\n");
+ return -1;
+ }
+
+ return run_demo();
+}
diff --git a/circuitpython/lib/quirc/demo/demoutil.c b/circuitpython/lib/quirc/demo/demoutil.c
new file mode 100644
index 0000000..857247a
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/demoutil.c
@@ -0,0 +1,71 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "demoutil.h"
+
+void print_data(const struct quirc_data *data, struct dthash *dt,
+ int want_verbose)
+{
+ if (dthash_seen(dt, data))
+ return;
+
+ printf("==> %s\n", data->payload);
+
+ if (want_verbose)
+ printf(" Version: %d, ECC: %c, Mask: %d, Type: %d\n\n",
+ data->version, "MLHQ"[data->ecc_level],
+ data->mask, data->data_type);
+}
+
+int parse_size(const char *text, int *video_width, int *video_height)
+{
+ int state = 0;
+ int w = 0, h = 0;
+ int i;
+
+ for (i = 0; text[i]; i++) {
+ if (text[i] == 'x' || text[i] == 'X') {
+ if (state == 0) {
+ state = 1;
+ } else {
+ fprintf(stderr, "parse_size: expected WxH\n");
+ return -1;
+ }
+ } else if (isdigit(text[i])) {
+ if (state == 0)
+ w = w * 10 + text[i] - '0';
+ else
+ h = h * 10 + text[i] - '0';
+ } else {
+ fprintf(stderr, "Invalid character in size: %c\n",
+ text[i]);
+ return -1;
+ }
+ }
+
+ if (w <= 0 || w >= 10000 || h <= 0 || h >= 10000) {
+ fprintf(stderr, "Invalid size: %dx%d\n", w, h);
+ return -1;
+ }
+
+ *video_width = w;
+ *video_height = h;
+
+ return 0;
+}
diff --git a/circuitpython/lib/quirc/demo/demoutil.h b/circuitpython/lib/quirc/demo/demoutil.h
new file mode 100644
index 0000000..888eddd
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/demoutil.h
@@ -0,0 +1,42 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DEMOUTIL_H_
+#define DEMOUTIL_H_
+
+#include "dthash.h"
+#include "quirc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Check if we've seen the given code, and if not, print it on stdout.
+ * Include version info if requested.
+ */
+void print_data(const struct quirc_data *data, struct dthash *dt,
+ int want_verbose);
+
+/* Parse a string of the form "WxH" and return width and height as
+ * integers. Returns 0 on success or -1 if a parser error occurs.
+ */
+int parse_size(const char *text, int *video_width, int *video_height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/circuitpython/lib/quirc/demo/dthash.c b/circuitpython/lib/quirc/demo/dthash.c
new file mode 100644
index 0000000..888dc1f
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/dthash.c
@@ -0,0 +1,139 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include "dthash.h"
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static uint32_t calc_crc(uint32_t crc, const uint8_t *buf, int len)
+{
+ while (len--) {
+ crc = crc32_tab[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+ buf++;
+ }
+
+ return crc;
+}
+
+static uint32_t code_hash(const struct quirc_data *data)
+{
+ uint8_t extra[4] = {data->version, data->ecc_level,
+ data->mask, data->data_type};
+ uint32_t crc = calc_crc(0xffffffff, extra, 4);
+
+ return calc_crc(crc, data->payload, data->payload_len);
+}
+
+static void flush_old(struct dthash *d, time_t now)
+{
+ int i;
+
+ for (i = 0; i < d->count; i++) {
+ struct dthash_code *c = &d->codes[i];
+
+ if (c->when + d->timeout <= now) {
+ if (i + 1 < d->count)
+ memcpy(c, &d->codes[d->count - 1], sizeof(*c));
+ d->count--;
+ }
+ }
+}
+
+void dthash_init(struct dthash *d, int timeout)
+{
+ d->count = 0;
+ d->timeout = timeout;
+}
+
+int dthash_seen(struct dthash *d, const struct quirc_data *data)
+{
+ time_t now = time(NULL);
+ uint32_t hash = code_hash(data);
+ struct dthash_code *c;
+ int i;
+
+ flush_old(d, now);
+
+ /* If the code is already seen, update its timestamp */
+ for (i = 0; i < d->count; i++) {
+ c = &d->codes[i];
+ if (c->hash == hash) {
+ c->when = now;
+ return 1;
+ }
+ }
+
+ /* Otherwise, find a place to put it. If necessary, push the
+ * oldset code out of the table.
+ */
+ if (d->count + 1 < DTHASH_MAX_CODES) {
+ c = &d->codes[d->count++];
+ } else {
+ c = &d->codes[0];
+ for (i = 1; i < d->count; i++)
+ if (d->codes[i].when < c->when)
+ c = &d->codes[i];
+ }
+
+ c->hash = hash;
+ c->when = now;
+ return 0;
+}
diff --git a/circuitpython/lib/quirc/demo/dthash.h b/circuitpython/lib/quirc/demo/dthash.h
new file mode 100644
index 0000000..431db1e
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/dthash.h
@@ -0,0 +1,61 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DTHASH_H_
+#define DTHASH_H_
+
+#include <stdint.h>
+#include <time.h>
+#include "quirc.h"
+
+/* Detector hash.
+ *
+ * This structure keeps track of codes that have been seen within the
+ * last N seconds, and allows us to print out codes at a reasonable
+ * rate as we see them.
+ */
+#define DTHASH_MAX_CODES 32
+
+struct dthash_code {
+ uint32_t hash;
+ time_t when;
+};
+
+struct dthash {
+ struct dthash_code codes[DTHASH_MAX_CODES];
+ int count;
+ int timeout;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initialise a detector hash with the given timeout. */
+void dthash_init(struct dthash *d, int timeout);
+
+/* When a code is discovered, this function should be called to see if
+ * it should be printed. The hash will record having seen the code, and
+ * return non-zero if it's the first time we've seen it within the
+ * configured timeout period.
+ */
+int dthash_seen(struct dthash *d, const struct quirc_data *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/circuitpython/lib/quirc/demo/mjpeg.c b/circuitpython/lib/quirc/demo/mjpeg.c
new file mode 100644
index 0000000..09d2197
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/mjpeg.c
@@ -0,0 +1,284 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+#include <assert.h>
+#include "mjpeg.h"
+
+struct huffman_table {
+ uint8_t bits[17];
+ uint8_t huffval[256];
+};
+
+static const struct huffman_table dc_lum = {
+ .bits = {
+ 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ },
+ .huffval = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b
+ }
+};
+
+static const struct huffman_table ac_lum = {
+ .bits = {
+ 0x00, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,
+ 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d
+ },
+ .huffval = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+ }
+};
+
+static const struct huffman_table dc_chroma = {
+ .bits = {
+ 0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ },
+ .huffval = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b
+ }
+};
+
+static const struct huffman_table ac_chroma = {
+ .bits = {
+ 0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+ 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
+ 0x77
+ },
+ .huffval = {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+ 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+ 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+ 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+ 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+ }
+};
+
+static void init_source(j_decompress_ptr cinfo)
+{
+}
+
+static boolean fill_input_buffer(j_decompress_ptr cinfo)
+{
+ static const uint8_t eoi_marker[] = {0xff, 0xd9};
+
+ cinfo->src->next_input_byte = eoi_marker;
+ cinfo->src->bytes_in_buffer = 2;
+
+ return TRUE;
+}
+
+static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0)
+ return;
+ if (num_bytes > cinfo->src->bytes_in_buffer)
+ num_bytes = cinfo->src->bytes_in_buffer;
+
+ cinfo->src->bytes_in_buffer -= num_bytes;
+ cinfo->src->next_input_byte += num_bytes;
+}
+
+static void term_source(j_decompress_ptr cinfo)
+{
+}
+
+struct my_jpeg_error {
+ struct jpeg_error_mgr base;
+ jmp_buf env;
+};
+
+static void my_output_message(struct jpeg_common_struct *com)
+{
+ struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
+ char buf[JMSG_LENGTH_MAX];
+
+ mj->err.format_message(com, buf);
+ fprintf(stderr, "MJPEG error: %s\n", buf);
+}
+
+static void my_error_exit(struct jpeg_common_struct *com)
+{
+ struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
+
+ my_output_message(com);
+ longjmp(mj->env, 0);
+}
+
+static void setup_table(struct jpeg_decompress_struct *jpeg,
+ JHUFF_TBL **tbl_ptr, const struct huffman_table *tab)
+{
+ assert (*tbl_ptr == NULL);
+
+ *tbl_ptr = jpeg_alloc_huff_table((j_common_ptr)jpeg);
+ memcpy((*tbl_ptr)->bits, tab->bits, 17);
+ memcpy((*tbl_ptr)->huffval, tab->huffval, 256);
+}
+
+void mjpeg_init(struct mjpeg_decoder *mj)
+{
+ memset(mj, 0, sizeof(*mj));
+
+ /* Set up error management */
+ mj->dinfo.err = jpeg_std_error(&mj->err);
+ mj->err.error_exit = my_error_exit;
+ mj->err.output_message = my_output_message;
+
+ mj->src.init_source = init_source;
+ mj->src.fill_input_buffer = fill_input_buffer;
+ mj->src.skip_input_data = skip_input_data;
+ mj->src.resync_to_restart = jpeg_resync_to_restart;
+ mj->src.term_source = term_source;
+
+ jpeg_create_decompress(&mj->dinfo);
+ mj->dinfo.src = &mj->src;
+ mj->dinfo.err = &mj->err;
+
+ setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[0], &dc_lum);
+ setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[0], &ac_lum);
+ setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[1], &dc_chroma);
+ setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[1], &ac_chroma);
+}
+
+void mjpeg_free(struct mjpeg_decoder *mj)
+{
+ jpeg_destroy_decompress(&mj->dinfo);
+}
+
+int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
+ const uint8_t *data, int datalen,
+ uint8_t *out, int pitch, int max_w, int max_h)
+{
+ if (setjmp(mj->env))
+ return -1;
+
+ mj->dinfo.src->bytes_in_buffer = datalen;
+ mj->dinfo.src->next_input_byte = data;
+
+ jpeg_read_header(&mj->dinfo, TRUE);
+ mj->dinfo.output_components = 3;
+ mj->dinfo.out_color_space = JCS_RGB;
+ jpeg_start_decompress(&mj->dinfo);
+
+ if (mj->dinfo.image_height > max_h ||
+ mj->dinfo.image_width > max_w) {
+ fprintf(stderr, "MJPEG: frame too big\n");
+ return -1;
+ }
+
+ uint8_t *rgb = calloc(mj->dinfo.image_width, 3);
+ if (!rgb) {
+ fprintf(stderr, "memory allocation failed\n");
+ return -1;
+ }
+ while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
+ uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
+ uint8_t *output = rgb;
+ int i;
+
+ jpeg_read_scanlines(&mj->dinfo, &output, 1);
+ for (i = 0; i < mj->dinfo.image_width; i++) {
+ scr[0] = output[2];
+ scr[1] = output[1];
+ scr[2] = output[0];
+ scr += 4;
+ output += 3;
+ }
+ }
+ free(rgb);
+
+ jpeg_finish_decompress(&mj->dinfo);
+
+ return 0;
+}
+
+int mjpeg_decode_gray(struct mjpeg_decoder *mj,
+ const uint8_t *data, int datalen,
+ uint8_t *out, int pitch, int max_w, int max_h)
+{
+ if (setjmp(mj->env))
+ return -1;
+
+ mj->dinfo.src->bytes_in_buffer = datalen;
+ mj->dinfo.src->next_input_byte = data;
+
+ jpeg_read_header(&mj->dinfo, TRUE);
+ mj->dinfo.output_components = 1;
+ mj->dinfo.out_color_space = JCS_GRAYSCALE;
+ jpeg_start_decompress(&mj->dinfo);
+
+ if (mj->dinfo.image_height > max_h ||
+ mj->dinfo.image_width > max_w) {
+ fprintf(stderr, "MJPEG: frame too big\n");
+ return -1;
+ }
+
+ while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
+ uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
+
+ jpeg_read_scanlines(&mj->dinfo, &scr, 1);
+ }
+
+ jpeg_finish_decompress(&mj->dinfo);
+
+ return 0;
+}
diff --git a/circuitpython/lib/quirc/demo/mjpeg.h b/circuitpython/lib/quirc/demo/mjpeg.h
new file mode 100644
index 0000000..caac855
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/mjpeg.h
@@ -0,0 +1,54 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MJPEG_H_
+#define MJPEG_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+
+struct mjpeg_decoder {
+ /* The error manager must be the first item in this struct */
+ struct jpeg_error_mgr err;
+ struct jpeg_decompress_struct dinfo;
+ struct jpeg_source_mgr src;
+ jmp_buf env;
+};
+
+/* Construct an MJPEG decoder. */
+void mjpeg_init(struct mjpeg_decoder *mj);
+
+/* Free any memory allocated while decoding MJPEG frames. */
+void mjpeg_free(struct mjpeg_decoder *mj);
+
+/* Decode a single MJPEG image to the buffer given, in RGB format.
+ * Returns 0 on success, -1 if an error occurs (bad data, or image too
+ * big for buffer).
+ */
+int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
+ const uint8_t *data, int datalen,
+ uint8_t *out, int pitch, int max_w, int max_h);
+
+/* Decode a single MJPEG image to the buffer given in 8-bit grayscale.
+ * Returns 0 on success, -1 if an error occurs.
+ */
+int mjpeg_decode_gray(struct mjpeg_decoder *mj,
+ const uint8_t *data, int datalen,
+ uint8_t *out, int pitch, int max_w, int max_h);
+
+#endif
diff --git a/circuitpython/lib/quirc/demo/scanner.c b/circuitpython/lib/quirc/demo/scanner.c
new file mode 100644
index 0000000..7c6c38e
--- /dev/null
+++ b/circuitpython/lib/quirc/demo/scanner.c
@@ -0,0 +1,214 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <quirc.h>
+#include <time.h>
+#include <getopt.h>
+
+#include "camera.h"
+#include "mjpeg.h"
+#include "convert.h"
+#include "dthash.h"
+#include "demoutil.h"
+
+/* Collected command-line arguments */
+static const char *camera_path = "/dev/video0";
+static int video_width = 640;
+static int video_height = 480;
+static int want_verbose = 0;
+static int printer_timeout = 2;
+
+static int main_loop(struct camera *cam,
+ struct quirc *q, struct mjpeg_decoder *mj)
+{
+ struct dthash dt;
+
+ dthash_init(&dt, printer_timeout);
+
+ for (;;) {
+ int w, h;
+ int i, count;
+ uint8_t *buf = quirc_begin(q, &w, &h);
+ const struct camera_buffer *head;
+ const struct camera_parms *parms = camera_get_parms(cam);
+
+ if (camera_dequeue_one(cam) < 0) {
+ perror("camera_dequeue_one");
+ return -1;
+ }
+
+ head = camera_get_head(cam);
+
+ switch (parms->format) {
+ case CAMERA_FORMAT_MJPEG:
+ mjpeg_decode_gray(mj, head->addr, head->size,
+ buf, w, w, h);
+ break;
+
+ case CAMERA_FORMAT_YUYV:
+ yuyv_to_luma(head->addr, w * 2, w, h, buf, w);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown frame format\n");
+ return -1;
+ }
+
+ if (camera_enqueue_all(cam) < 0) {
+ perror("camera_enqueue_all");
+ return -1;
+ }
+
+ quirc_end(q);
+
+ count = quirc_count(q);
+ for (i = 0; i < count; i++) {
+ struct quirc_code code;
+ struct quirc_data data;
+
+ quirc_extract(q, i, &code);
+ if (!quirc_decode(&code, &data))
+ print_data(&data, &dt, want_verbose);
+ }
+ }
+}
+
+static int run_scanner(void)
+{
+ struct quirc *qr;
+ struct camera cam;
+ struct mjpeg_decoder mj;
+ const struct camera_parms *parms;
+
+ camera_init(&cam);
+ if (camera_open(&cam, camera_path, video_width, video_height,
+ 25, 1) < 0) {
+ perror("camera_open");
+ goto fail_qr;
+ }
+
+ if (camera_map(&cam, 8) < 0) {
+ perror("camera_map");
+ goto fail_qr;
+ }
+
+ if (camera_on(&cam) < 0) {
+ perror("camera_on");
+ goto fail_qr;
+ }
+
+ if (camera_enqueue_all(&cam) < 0) {
+ perror("camera_enqueue_all");
+ goto fail_qr;
+ }
+
+ parms = camera_get_parms(&cam);
+
+ qr = quirc_new();
+ if (!qr) {
+ perror("couldn't allocate QR decoder");
+ goto fail_qr;
+ }
+
+ if (quirc_resize(qr, parms->width, parms->height) < 0) {
+ perror("couldn't allocate QR buffer");
+ goto fail_qr_resize;
+ }
+
+ mjpeg_init(&mj);
+ if (main_loop(&cam, qr, &mj) < 0)
+ goto fail_main_loop;
+ mjpeg_free(&mj);
+
+ quirc_destroy(qr);
+ camera_destroy(&cam);
+
+ return 0;
+
+fail_main_loop:
+ mjpeg_free(&mj);
+fail_qr_resize:
+ quirc_destroy(qr);
+fail_qr:
+ camera_destroy(&cam);
+
+ return -1;
+}
+
+static void usage(const char *progname)
+{
+ printf("Usage: %s [options]\n\n"
+"Valid options are:\n\n"
+" -v Show extra data for detected codes.\n"
+" -d <device> Specify camera device path.\n"
+" -s <WxH> Specify video dimensions.\n"
+" -p <timeout> Set printer timeout (seconds).\n"
+" --help Show this information.\n"
+" --version Show library version information.\n",
+ progname);
+}
+
+int main(int argc, char **argv)
+{
+ static const struct option longopts[] = {
+ {"help", 0, 0, 'H'},
+ {"version", 0, 0, 'V'},
+ {NULL, 0, 0, 0}
+ };
+ int opt;
+
+ printf("quirc scanner demo\n");
+ printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
+ printf("\n");
+
+ while ((opt = getopt_long(argc, argv, "d:s:vg:p:",
+ longopts, NULL)) >= 0)
+ switch (opt) {
+ case 'V':
+ printf("Library version: %s\n", quirc_version());
+ return 0;
+
+ case 'H':
+ usage(argv[0]);
+ return 0;
+
+ case 'v':
+ want_verbose = 1;
+ break;
+
+ case 's':
+ if (parse_size(optarg, &video_width, &video_height) < 0)
+ return -1;
+ break;
+
+ case 'p':
+ printer_timeout = atoi(optarg);
+ break;
+
+ case 'd':
+ camera_path = optarg;
+ break;
+
+ case '?':
+ fprintf(stderr, "Try --help for usage information\n");
+ return -1;
+ }
+
+ return run_scanner();
+}