summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2019-11-15 23:52:13 +0100
committerNicolas Schodet2019-11-15 23:52:13 +0100
commit7ce1b8358a8c41d2f00e93cfd614dd445c2d1a5c (patch)
tree85b1f7b760f2ecd4e5a04d040a1edef15702976a
parentde86953c464ef457bfd9fefb38f6492035c34698 (diff)
New image API
This allows to remove conversion responsibility from devices so that image treatments can be made on raw data. Now clients must unref images when they are not used, the driver can check this.
-rw-r--r--cli.c30
-rw-r--r--device.c2
-rw-r--r--device.h18
-rw-r--r--gui_app_window.c46
-rw-r--r--image.c88
-rw-r--r--image.h55
-rw-r--r--moticam.c102
7 files changed, 261 insertions, 80 deletions
diff --git a/cli.c b/cli.c
index 33edcee..b0d3a1b 100644
--- a/cli.c
+++ b/cli.c
@@ -34,23 +34,31 @@ cli_read_images(libusb_context *usb, struct device *device, int count,
{
device_start(device);
for (int i = 0; i < count;) {
- const struct device_image *device_image = device_read(device);
- if (device_image) {
+ struct image *image = device_read(device);
+ if (image) {
+ if (image->format != IMAGE_FORMAT_XBGR32) {
+ struct image *cimage = image_new(image->width, image->height,
+ image->width * 4, IMAGE_FORMAT_XBGR32);
+ image_convert(cimage, image);
+ image_unref(image);
+ image = cimage;
+ }
char *name = NULL;
if (asprintf(&name, out, i) < 0)
utils_fatal("can not prepare file name");
utils_info("write %s", name);
- png_image image;
- memset(&image, 0, sizeof(image));
- image.version = PNG_IMAGE_VERSION;
- image.width = device_image->width;
- image.height = device_image->height;
- image.format = PNG_FORMAT_BGRA;
- int r = png_image_write_to_file(&image, name, 0,
- device_image->pixels, device_image->stride, NULL);
+ png_image pimage;
+ memset(&pimage, 0, sizeof(pimage));
+ pimage.version = PNG_IMAGE_VERSION;
+ pimage.width = image->width;
+ pimage.height = image->height;
+ pimage.format = PNG_FORMAT_BGRA;
+ int r = png_image_write_to_file(&pimage, name, 0,
+ image->pixels, image->stride, NULL);
if (r == 0)
- utils_fatal("can not write image: %s", image.message);
+ utils_fatal("can not write image: %s", pimage.message);
free(name);
+ image_unref(image);
i++;
} else {
libusb_handle_events(usb);
diff --git a/device.c b/device.c
index 549cce8..cfc8783 100644
--- a/device.c
+++ b/device.c
@@ -104,7 +104,7 @@ device_start(struct device *device)
device->start(device);
}
-const struct device_image *
+struct image *
device_read(struct device *device)
{
return device->read(device);
diff --git a/device.h b/device.h
index 86e4b4f..01c6336 100644
--- a/device.h
+++ b/device.h
@@ -27,6 +27,8 @@
#include <libusb.h>
#include <stdint.h>
+#include "image.h"
+
struct device;
/* USB poll function. If the device is handled by this driver, it should
@@ -98,20 +100,8 @@ typedef void (*device_set_resolution_f)(
typedef void (*device_start_f)(
struct device *device);
-/* Image with attached information. */
-struct device_image {
- /* Image width. */
- int width;
- /* Image height. */
- int height;
- /* Byte offset between a line and the next line. */
- int stride;
- /* Frame buffer, ARGB format. */
- uint32_t *pixels;
-};
-
/* Return an image if one is available, else, return NULL. */
-typedef const struct device_image *(*device_read_f)(
+typedef struct image *(*device_read_f)(
struct device *device);
/* Stop streaming. */
@@ -168,7 +158,7 @@ void
device_start(struct device *device);
/* Read an image. */
-const struct device_image *
+struct image *
device_read(struct device *device);
/* Stop streaming. */
diff --git a/gui_app_window.c b/gui_app_window.c
index 4ec9e6d..2e4b7f0 100644
--- a/gui_app_window.c
+++ b/gui_app_window.c
@@ -49,6 +49,8 @@ struct _GuiAppWindowPrivate {
GdkRectangle allocation;
cairo_matrix_t transform;
cairo_surface_t *surface;
+ struct image *image;
+ struct image *image_converted;
bool rotate;
int width;
int height;
@@ -100,11 +102,43 @@ video_ready_cb(GuiApp *app, gpointer user_data)
{
GuiAppWindowPrivate *priv = user_data;
if (priv->started) {
- const struct device_image *image = device_read(priv->device);
+ struct image *image = device_read(priv->device);
if (image) {
+ /* Release old data. */
if (priv->surface) {
cairo_surface_destroy(priv->surface);
+ priv->surface = NULL;
}
+ if (priv->image) {
+ image_unref(priv->image);
+ priv->image = NULL;
+ }
+ /* Different format? Convert. */
+ if (image->format != IMAGE_FORMAT_XBGR32) {
+ if (priv->image_converted &&
+ (priv->image_converted->width != image->width
+ || priv->image_converted->height != image->height)) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
+ if (!priv->image_converted) {
+ int stride =
+ cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
+ image->width);
+ priv->image_converted = image_new(image->width,
+ image->height, stride, IMAGE_FORMAT_XBGR32);
+ }
+ image_convert(priv->image_converted, image);
+ image_unref(image);
+ image = priv->image_converted;
+ } else {
+ if (priv->image_converted) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
+ priv->image = image;
+ }
+ /* Make a surface. */
priv->surface = cairo_image_surface_create_for_data(
(unsigned char *) image->pixels, CAIRO_FORMAT_RGB24, image->width,
image->height, image->stride);
@@ -139,6 +173,14 @@ video_stop(GuiAppWindowPrivate *priv)
cairo_surface_destroy(priv->surface);
priv->surface = NULL;
}
+ if (priv->image) {
+ image_unref(priv->image);
+ priv->image = NULL;
+ }
+ if (priv->image_converted) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
g_signal_handler_disconnect(priv->app, priv->video_ready_handler_id);
device_stop(priv->device);
priv->started = false;
@@ -318,6 +360,8 @@ gui_app_window_init(GuiAppWindow *win)
priv->allocation.width = -1;
cairo_matrix_init_identity(&priv->transform);
priv->surface = NULL;
+ priv->image = NULL;
+ priv->image_converted = NULL;
priv->rotate = true;
priv->width = -1;
priv->height = -1;
diff --git a/image.c b/image.c
index 373010b..73ac957 100644
--- a/image.c
+++ b/image.c
@@ -20,13 +20,58 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "image.h"
+#include "utils.h"
-#include <string.h>
+static void
+image_free(struct image *image, void *user_data)
+{
+ free(image);
+}
+
+struct image *
+image_new(int width, int height, int stride, enum image_format format)
+{
+ size_t struct_size_aligned = (sizeof(struct image) + 15) / 16 * 16;
+ size_t image_size_aligned = ((height * stride) + 15) / 16 * 16;
+ uint8_t *m = aligned_alloc(16,
+ struct_size_aligned + image_size_aligned);
+ if (!m) {
+ utils_fatal("memory exhausted");
+ }
+ struct image *image = (struct image *) m;
+ image->width = width;
+ image->height = height;
+ image->stride = stride;
+ image->format = format;
+ image->pixels = m + struct_size_aligned;
+ image->refs = 1;
+ image->release = image_free;
+ image->release_user_data = NULL;
+ return image;
+}
+
+void
+image_ref(struct image *image)
+{
+ image->refs++;
+}
void
-image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
- int stride)
+image_unref(struct image *image)
+{
+ image->refs--;
+ if (image->refs == 0 && image->release)
+ image->release(image, image->release_user_data);
+}
+
+static void
+image_convert_to_xbgr32_from_sgrbg8(struct image *dst,
+ const struct image *src)
{
/*
* Compute missing value using an average of its neighbours.
@@ -36,11 +81,15 @@ image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
* G R G R G R
* B G B G B G
*/
- const int in_stride = width;
- const int out_stride = stride;
+ assert(dst->width == src->width
+ && dst->height == src->height);
+ const int in_stride = src->stride;
+ const int out_stride = dst->stride;
+ int width = src->width;
+ int height = src->height;
/* Skip first line and column. */
- const uint8_t *in = bayer + in_stride + 1;
- uint8_t *out = (uint8_t *) rgb + out_stride + 4;
+ const uint8_t *in = src->pixels + in_stride + 1;
+ uint8_t *out = dst->pixels + out_stride + 4;
/* Loop over lines. */
for (int i = 1; i < height - 1; i += 2) {
/* Even lines. */
@@ -105,3 +154,28 @@ image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
out -= (height - 1) * out_stride;
memcpy(out, out + out_stride, width * 4);
}
+
+void
+image_convert(struct image *dst, const struct image *src)
+{
+ assert(dst->width == src->width
+ && dst->height == src->height);
+ if (dst->format == src->format) {
+ assert(dst->stride == src->stride);
+ memcpy(dst->pixels, src->pixels, src->stride * src->height);
+ } else {
+ switch (src->format) {
+ case IMAGE_FORMAT_SGRBG8:
+ switch (dst->format) {
+ case IMAGE_FORMAT_XBGR32:
+ image_convert_to_xbgr32_from_sgrbg8(dst, src);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
diff --git a/image.h b/image.h
index 4d04413..9e20859 100644
--- a/image.h
+++ b/image.h
@@ -24,8 +24,59 @@
*/
#include <stdint.h>
+/* Image format. */
+enum image_format
+{
+#define IMAGE_FORMAT_FOURCC(a, b, c, d) \
+ (((uint32_t)(a)) | ((uint32_t)(b) << 8) \
+ | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+ /* Bayer image format:
+ * G R G R
+ * B G B G */
+ IMAGE_FORMAT_SGRBG8 = IMAGE_FORMAT_FOURCC('B', 'R', 'B', 'G'),
+ /* RGB 32 bits, B in LSB. */
+ IMAGE_FORMAT_XBGR32 = IMAGE_FORMAT_FOURCC('X', 'R', '2', '4'),
+};
+
+struct image;
+
+/* Image release callback. */
+typedef void (*image_release_f)(struct image *image, void *user_data);
+
+/* Image with attached information. */
+struct image {
+ /* Image width. */
+ int width;
+ /* Image height. */
+ int height;
+ /* Byte offset between a line and the next line. */
+ int stride;
+ /* Image format. */
+ enum image_format format;
+ /* Frame buffer. */
+ uint8_t *pixels;
+ /* Number of reference to this image. */
+ int refs;
+ /* Release callback. */
+ image_release_f release;
+ /* Release callback user data. */
+ void *release_user_data;
+};
+
+/* Allocate an image using system allocator. */
+struct image *
+image_new(int width, int height, int stride, enum image_format format);
+
+/* Add a reference to an image. */
+void
+image_ref(struct image *image);
+
+/* Release a reference to an image. */
+void
+image_unref(struct image *image);
+
+/* Copy an image to another one, converting format. */
void
-image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
- int stride);
+image_convert(struct image *dst, const struct image *src);
#endif /* image_h */
diff --git a/moticam.c b/moticam.c
index 6e487ea..4f46478 100644
--- a/moticam.c
+++ b/moticam.c
@@ -40,12 +40,15 @@ struct moticam_device {
libusb_context *usb;
libusb_device_handle *handle;
double exposure_ms;
+ int width;
+ int height;
bool exposure_pending;
double gain;
bool gain_pending;
size_t rawbuffer_size;
- uint8_t *rawbuffer;
- struct device_image image;
+ struct image image[2];
+ struct image *current_image;
+ struct image *other_image;
struct libusb_transfer *usb_transfer;
bool usb_transfer_done;
};
@@ -212,15 +215,10 @@ moticam_set_resolution(struct device *device, int width, int height,
int stride)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->image.pixels);
- /* Choose stride. */
- if (!stride)
- stride = width * sizeof(uint32_t);
- assert(stride >= width * sizeof(uint32_t));
- /* Store parameters. */
- mdev->image.width = width;
- mdev->image.height = height;
- mdev->image.stride = stride;
+ assert(!mdev->current_image);
+ /* Ignore stride, store parameters. */
+ mdev->width = width;
+ mdev->height = height;
}
static void
@@ -234,53 +232,62 @@ static void
moticam_start(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->image.pixels);
+ assert(!mdev->current_image);
/* Configure camera. */
moticam_control_exposure(&mdev->device, 30.0);
moticam_control_gain(&mdev->device, mdev->gain);
- moticam_control_resolution(&mdev->device, mdev->image.width,
- mdev->image.height);
+ moticam_control_resolution(&mdev->device, mdev->width, mdev->height);
moticam_control_exposure(&mdev->device, mdev->exposure_ms);
mdev->exposure_pending = false;
mdev->gain_pending = false;
utils_delay_us(100000);
/* Prepare sizes and buffers, request an extra frame to read the zero
* length packet. */
- int image_size = mdev->image.width * mdev->image.height;
+ int image_size = mdev->width * mdev->height;
int frame_size = 16384;
mdev->rawbuffer_size = (image_size + frame_size)
/ frame_size * frame_size;
- mdev->rawbuffer = libusb_dev_mem_alloc(mdev->handle,
- mdev->rawbuffer_size);
- if (!mdev->rawbuffer)
- utils_fatal("can not allocate raw buffer");
- mdev->image.pixels = utils_malloc(
- mdev->image.stride * mdev->image.height);
+ for (int i = 0; i < 2; i++) {
+ struct image *image = &mdev->image[i];
+ image->width = mdev->width;
+ image->height = mdev->height;
+ image->stride = mdev->width;
+ image->format = IMAGE_FORMAT_SGRBG8;
+ image->pixels = libusb_dev_mem_alloc(mdev->handle,
+ mdev->rawbuffer_size);
+ if (!image->pixels)
+ utils_fatal("can not allocate raw buffer");
+ assert(image->refs == 0);
+ image->release = NULL;
+ image->release_user_data = NULL;
+ }
+ mdev->current_image = &mdev->image[0];
+ mdev->other_image = &mdev->image[1];
/* Prepare and submit transfer. */
mdev->usb_transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(mdev->usb_transfer, mdev->handle, 0x83,
- mdev->rawbuffer, mdev->rawbuffer_size, moticam_transfer_cb, mdev,
- 30000);
+ mdev->current_image->pixels, mdev->rawbuffer_size,
+ moticam_transfer_cb, mdev, 30000);
int r = libusb_submit_transfer(mdev->usb_transfer);
if (r)
utils_fatal("can not submit transfer: %s", libusb_strerror(r));
}
-static const struct device_image *
+static struct image *
moticam_read(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->image.pixels);
+ assert(mdev->current_image);
if (mdev->usb_transfer_done) {
if (mdev->usb_transfer->status == LIBUSB_TRANSFER_COMPLETED) {
- const int image_size = mdev->image.width * mdev->image.height;
- const struct device_image *image = NULL;
- /* Image received? Convert, else drop. */
+ const int image_size = mdev->width * mdev->height;
+ struct image *image = NULL;
+ /* Image received? Return it, else drop. */
if (mdev->usb_transfer->actual_length == image_size) {
- image_bayer2argb(mdev->rawbuffer, mdev->image.pixels,
- mdev->image.width, mdev->image.height,
- mdev->image.stride);
- image = &mdev->image;
+ image = mdev->current_image;
+ image->refs = 1;
+ mdev->current_image = mdev->other_image;
+ mdev->other_image = image;
}
/* Pending updates? */
if (mdev->exposure_pending) {
@@ -292,12 +299,14 @@ moticam_read(struct device *device)
mdev->gain_pending = false;
}
/* Start a new transfer. */
+ assert(mdev->current_image->refs == 0);
mdev->usb_transfer_done = false;
+ mdev->usb_transfer->buffer = mdev->current_image->pixels;
int r = libusb_submit_transfer(mdev->usb_transfer);
if (r)
utils_fatal("can not submit transfer: %s",
libusb_strerror(r));
- /* Return converted image or NULL. */
+ /* Return image or NULL. */
return image;
} else {
utils_fatal("transfer stopped: TODO");
@@ -311,7 +320,7 @@ static void
moticam_stop(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->image.pixels);
+ assert(mdev->current_image);
/* Cancel transfer if running. */
if (!mdev->usb_transfer_done) {
libusb_cancel_transfer(mdev->usb_transfer);
@@ -321,10 +330,13 @@ moticam_stop(struct device *device)
/* Release. */
libusb_free_transfer(mdev->usb_transfer);
mdev->usb_transfer = NULL;
- libusb_dev_mem_free(mdev->handle, mdev->rawbuffer, mdev->rawbuffer_size);
- mdev->rawbuffer = NULL;
- free(mdev->image.pixels);
- mdev->image.pixels = NULL;
+ for (int i = 0; i < 2; i++) {
+ libusb_dev_mem_free(mdev->handle, mdev->image[i].pixels,
+ mdev->rawbuffer_size);
+ mdev->image[i].pixels = NULL;
+ }
+ mdev->current_image = NULL;
+ mdev->other_image = NULL;
/* Stop camera. */
moticam_control_exposure(device, 0.0);
moticam_control_exposure(device, 0.0);
@@ -335,7 +347,7 @@ static void
moticam_close(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- if (mdev->image.pixels)
+ if (mdev->current_image)
moticam_stop(device);
libusb_close(mdev->handle);
free(device);
@@ -368,16 +380,18 @@ moticam_usb_open(libusb_context *usb, libusb_device *usb_device)
mdev->device.close = &moticam_close;
mdev->usb = usb;
mdev->handle = NULL;
+ mdev->width = 0;
+ mdev->height = 0;
mdev->exposure_ms = moticam_device_info.exposure_default_ms;
mdev->exposure_pending = false;
mdev->gain = moticam_device_info.gain_default;
mdev->gain_pending = false;
mdev->rawbuffer_size = 0;
- mdev->rawbuffer = NULL;
- mdev->image.width = 0;
- mdev->image.height = 0;
- mdev->image.stride = 0;
- mdev->image.pixels = NULL;
+ for (int i = 0; i < 2; i++) {
+ mdev->image[i].refs = 0;
+ }
+ mdev->current_image = NULL;
+ mdev->other_image = NULL;
mdev->usb_transfer = NULL;
mdev->usb_transfer_done = false;
/* Open USB device. */