summaryrefslogtreecommitdiffstats
path: root/zbar/window/xv.c
diff options
context:
space:
mode:
Diffstat (limited to 'zbar/window/xv.c')
-rw-r--r--zbar/window/xv.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/zbar/window/xv.c b/zbar/window/xv.c
new file mode 100644
index 0000000..f332800
--- /dev/null
+++ b/zbar/window/xv.c
@@ -0,0 +1,264 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include <string.h> /* strcmp */
+#include "image.h"
+#include "window.h"
+#include "x.h"
+
+static int xv_cleanup(zbar_window_t *w)
+{
+ window_state_t *x = w->state;
+ if (x->img.xv) {
+ XFree(x->img.xv);
+ x->img.xv = NULL;
+ }
+ int i;
+ for (i = 0; i < x->num_xv_adaptors; i++)
+ if (x->xv_adaptors[i]) {
+ XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime);
+ x->xv_adaptors[i] = 0;
+ }
+ free(x->xv_ports);
+ free(x->xv_adaptors);
+ x->xv_ports = NULL;
+ x->num_xv_adaptors = 0;
+ x->xv_adaptors = NULL;
+ return (0);
+}
+
+static inline int xv_init(zbar_window_t *w, zbar_image_t *img,
+ int format_change)
+{
+ window_state_t *x = w->state;
+ if (x->img.xv) {
+ XFree(x->img.xv);
+ x->img.xv = NULL;
+ }
+ if (format_change) {
+ /* lookup port for format */
+ x->img_port = 0;
+ int i;
+ for (i = 0; w->formats[i]; i++)
+ if (w->formats[i] == w->format) {
+ x->img_port = x->xv_ports[i];
+ break;
+ }
+ assert(x->img_port > 0);
+ }
+
+ XvImage *xvimg = XvCreateImage(w->display, x->img_port, w->format, NULL,
+ img->width, img->height);
+ zprintf(3,
+ "new XvImage %.4s(%08" PRIx32 ") %dx%d(%d)"
+ " from %.4s(%08" PRIx32 ") %dx%d\n",
+ (char *)&w->format, w->format, xvimg->width, xvimg->height,
+ xvimg->pitches[0], (char *)&img->format, img->format, img->width,
+ img->height);
+
+ /* FIXME not sure this simple check is always correct
+ * should lookup format to decode/sanitize target width from pitch & bpp
+ */
+ w->dst_width =
+ ((xvimg->num_planes <= 1) ? xvimg->width : xvimg->pitches[0]);
+ w->dst_height = xvimg->height;
+
+ /* FIXME datalen check */
+ if (w->dst_width < img->width || xvimg->height < img->height) {
+ XFree(xvimg);
+ /* FIXME fallback to XImage... */
+ return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__,
+ "output image size mismatch (XvCreateImage)"));
+ }
+ x->img.xv = xvimg;
+
+ return (0);
+}
+
+static int xv_draw(zbar_window_t *w, zbar_image_t *img)
+{
+ window_state_t *x = w->state;
+ XvImage *xvimg = x->img.xv;
+ assert(xvimg);
+ xvimg->data = (void *)img->data;
+ zprintf(24, "XvPutImage(%dx%d -> %dx%d (%08lx))\n", w->src_width,
+ w->src_height, w->scaled_size.x, w->scaled_size.y, img->datalen);
+ XvPutImage(w->display, x->img_port, w->xwin, x->gc, xvimg, 0, 0,
+ w->src_width, w->src_height, w->scaled_offset.x,
+ w->scaled_offset.y, w->scaled_size.x, w->scaled_size.y);
+ xvimg->data = NULL; /* FIXME hold shm image until completion */
+ return (0);
+}
+
+static inline int xv_add_format(zbar_window_t *w, uint32_t fmt, XvPortID port)
+{
+ int i = _zbar_window_add_format(w, fmt);
+
+ window_state_t *x = w->state;
+ if (!w->formats[i + 1])
+ x->xv_ports = realloc(x->xv_ports, (i + 1) * sizeof(*x->xv_ports));
+
+ /* FIXME could prioritize by something (rate? size?) */
+ x->xv_ports[i] = port;
+ return (i);
+}
+
+static int xv_probe_port(zbar_window_t *w, XvPortID port)
+{
+ unsigned n;
+ XvEncodingInfo *encodings = NULL;
+ if (XvQueryEncodings(w->display, port, &n, &encodings))
+ return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__,
+ "querying XVideo encodings"));
+
+ zprintf(1, "probing port %u with %d encodings:\n", (unsigned)port, n);
+ unsigned width = 0, height = 0;
+ int i;
+ for (i = 0; i < n; i++) {
+ XvEncodingInfo *enc = &encodings[i];
+ zprintf(2, " [%d] %lu x %lu rate=%d/%d : %s\n", i, enc->width,
+ enc->height, enc->rate.numerator, enc->rate.denominator,
+ enc->name);
+ if (!strcmp(enc->name, "XV_IMAGE")) {
+ if (width < enc->width)
+ width = enc->width;
+ if (height < enc->height)
+ height = enc->height;
+ }
+ }
+ XvFreeEncodingInfo(encodings);
+ encodings = NULL;
+
+ if (!width || !height)
+ return (err_capture(w, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
+ "no XV_IMAGE encodings found"));
+ zprintf(1, "max XV_IMAGE size %dx%d\n", width, height);
+ if (w->max_width > width)
+ w->max_width = width;
+ if (w->max_height > height)
+ w->max_height = height;
+
+ XvImageFormatValues *formats =
+ XvListImageFormats(w->display, port, (int *)&n);
+ if (!formats)
+ return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__,
+ "querying XVideo image formats"));
+
+ zprintf(1, "%d image formats\n", n);
+ for (i = 0; i < n; i++) {
+ XvImageFormatValues *fmt = &formats[i];
+ zprintf(2, " [%d] %.4s(%08x) %s %s %s planes=%d bpp=%d : %.16s\n", i,
+ (char *)&fmt->id, fmt->id, (fmt->type == XvRGB) ? "RGB" : "YUV",
+ (fmt->byte_order == LSBFirst) ? "LSBFirst" : "MSBFirst",
+ (fmt->format == XvPacked) ? "packed" : "planar",
+ fmt->num_planes, fmt->bits_per_pixel, fmt->guid);
+ xv_add_format(w, fmt->id, port);
+ }
+ XFree(formats);
+ return (0);
+}
+
+int _zbar_window_probe_xv(zbar_window_t *w)
+{
+ unsigned xv_major, xv_minor, xv_req, xv_ev, xv_err;
+ if (XvQueryExtension(w->display, &xv_major, &xv_minor, &xv_req, &xv_ev,
+ &xv_err)) {
+ zprintf(1, "XVideo extension not present\n");
+ return (-1);
+ }
+ zprintf(1, "XVideo extension version %u.%u\n", xv_major, xv_minor);
+
+ unsigned n;
+ XvAdaptorInfo *adaptors = NULL;
+ if (XvQueryAdaptors(w->display, w->xwin, &n, &adaptors))
+ return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__,
+ "unable to query XVideo adaptors"));
+
+ window_state_t *x = w->state;
+ x->num_xv_adaptors = 0;
+ x->xv_adaptors = calloc(n, sizeof(*x->xv_adaptors));
+ int i;
+ for (i = 0; i < n; i++) {
+ XvAdaptorInfo *adapt = &adaptors[i];
+ zprintf(2, " adaptor[%d] %lu ports %u-%u type=0x%x fmts=%lu : %s\n",
+ i, adapt->num_ports, (unsigned)adapt->base_id,
+ (unsigned)(adapt->base_id + adapt->num_ports - 1), adapt->type,
+ adapt->num_formats, adapt->name);
+ if (!(adapt->type & XvImageMask))
+ continue;
+
+ int j;
+ for (j = 0; j < adapt->num_ports; j++)
+ if (!XvGrabPort(w->display, adapt->base_id + j, CurrentTime)) {
+ zprintf(3, " grabbed port %u\n",
+ (unsigned)(adapt->base_id + j));
+ x->xv_adaptors[x->num_xv_adaptors++] = adapt->base_id + j;
+ break;
+ }
+
+ if (j == adapt->num_ports)
+ zprintf(3, " no available XVideo image port\n");
+ }
+ XvFreeAdaptorInfo(adaptors);
+ adaptors = NULL;
+
+ if (!x->num_xv_adaptors) {
+ zprintf(1, "WARNING: no XVideo adaptor supporting XvImages found\n");
+ free(x->xv_adaptors);
+ x->xv_adaptors = NULL;
+ return (-1);
+ }
+ if (x->num_xv_adaptors < n)
+ x->xv_adaptors =
+ realloc(x->xv_adaptors, x->num_xv_adaptors * sizeof(int));
+
+ w->max_width = w->max_height = 65536;
+ w->formats = realloc(w->formats, sizeof(uint32_t));
+ w->formats[0] = 0;
+ for (i = 0; i < x->num_xv_adaptors; i++)
+ if (xv_probe_port(w, x->xv_adaptors[i])) {
+ XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime);
+ x->xv_adaptors[i] = 0;
+ }
+ if (!w->formats[0] || w->max_width == 65536 || w->max_height == 65536) {
+ xv_cleanup(w);
+ return (-1);
+ }
+
+ /* clean out any unused adaptors */
+ for (i = 0; i < x->num_xv_adaptors; i++) {
+ int j;
+ for (j = 0; w->formats[j]; j++)
+ if (x->xv_ports[j] == x->xv_adaptors[i])
+ break;
+ if (!w->formats[j]) {
+ XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime);
+ x->xv_adaptors[i] = 0;
+ }
+ }
+
+ w->init = xv_init;
+ w->draw_image = xv_draw;
+ w->cleanup = xv_cleanup;
+ return (0);
+}