summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2010-11-26 02:29:06 +0100
committerMarc-André Lureau <marcandre.lureau@redhat.com>2010-11-26 02:29:06 +0100
commitdb29073aa5b6ddc4c630e901ad4304160e22ad2f (patch)
tree60c9c9ae03918db18095defbe001cfb40349fa1b
parent6c603038b5910cd9c1b9697fbaeff429a56f8282 (diff)
gtk: add jpeg decoder
-rw-r--r--gtk/channel-display-priv.h1
-rw-r--r--gtk/channel-display.c72
-rw-r--r--gtk/decode-jpeg.c167
-rw-r--r--gtk/decode.h3
-rw-r--r--gtk/spice-channel-cache.h1
5 files changed, 240 insertions, 4 deletions
diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h
index ab8268c..b7aac2a 100644
--- a/gtk/channel-display-priv.h
+++ b/gtk/channel-display-priv.h
@@ -45,6 +45,7 @@ typedef struct display_surface {
SpiceCanvas *canvas;
SpiceGlzDecoder *glz_decoder;
SpiceZlibDecoder *zlib_decoder;
+ SpiceJpegDecoder *jpeg_decoder;
} display_surface;
typedef struct display_stream {
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 275d375..47225e4 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -152,9 +152,8 @@ static void image_put(SpiceImageCache *cache, uint64_t id, pixman_image_t *image
SPICE_CONTAINEROF(cache, spice_display_channel, image_cache);
display_cache_item *item;
-#if 1 /* temporary sanity check */
- item = cache_find(&c->images, id);
- g_return_if_fail(item == NULL);
+#if 1 /* TODO: temporary sanity check */
+ g_warn_if_fail(cache_find(&c->images, id) == NULL);
#endif
item = cache_add(&c->images, id);
@@ -261,9 +260,66 @@ static void palette_release(SpicePaletteCache *cache, SpicePalette *palette)
palette_remove(cache, palette->unique);
}
+#ifdef SW_CANVAS_CACHE
+static void image_put_lossy(SpiceImageCache *cache, uint64_t id,
+ pixman_image_t *surface)
+{
+ spice_display_channel *c =
+ SPICE_CONTAINEROF(cache, spice_display_channel, image_cache);
+ display_cache_item *item;
+
+#if 1 /* TODO: temporary sanity check */
+ g_warn_if_fail(cache_find(&c->images, id) == NULL);
+#endif
+
+ item = cache_add(&c->images, id);
+ item->ptr = pixman_image_ref(surface);
+ item->lossy = TRUE;
+}
+
+static void image_replace_lossy(SpiceImageCache *cache, uint64_t id,
+ pixman_image_t *surface)
+{
+ spice_display_channel *c =
+ SPICE_CONTAINEROF(cache, spice_display_channel, image_cache);
+ display_cache_item *item;
+
+ item = cache_find(&c->images, id);
+ g_return_if_fail(item != NULL);
+
+ pixman_image_unref(item->ptr);
+ item->ptr = pixman_image_ref(surface);
+ item->lossy = FALSE;
+}
+
+static pixman_image_t* image_get_lossless(SpiceImageCache *cache, uint64_t id)
+{
+ spice_display_channel *c =
+ SPICE_CONTAINEROF(cache, spice_display_channel, image_cache);
+ display_cache_item *item;
+
+ item = cache_find(&c->images, id);
+ if (!item)
+ return NULL;
+
+ /* TODO: shared_cache.hpp does wait until it is lossless..., is
+ that necessary? */
+ g_warn_if_fail(item->lossy == FALSE);
+
+ cache_used(&c->images, item);
+ return pixman_image_ref(item->ptr);
+}
+#endif
+
static SpiceImageCacheOps image_cache_ops = {
.put = image_put,
.get = image_get,
+
+#ifdef SW_CANVAS_CACHE
+ .put_lossy = image_put_lossy,
+ .replace_lossy = image_replace_lossy,
+ .get_lossless = image_get_lossless,
+#endif
};
static SpicePaletteCacheOps palette_cache_ops = {
@@ -312,8 +368,15 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
if (!c->glz_window) {
c->glz_window = glz_decoder_window_new();
}
+
+ g_warn_if_fail(surface->canvas == NULL);
+ g_warn_if_fail(surface->glz_decoder == NULL);
+ g_warn_if_fail(surface->zlib_decoder == NULL);
+ g_warn_if_fail(surface->jpeg_decoder == NULL);
+
surface->glz_decoder = glz_decoder_new(c->glz_window);
surface->zlib_decoder = zlib_decoder_new();
+ surface->jpeg_decoder = jpeg_decoder_new();
surface->canvas = canvas_create_for_data(surface->width,
surface->height,
@@ -326,7 +389,7 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
#endif
NULL, // &csurfaces.base,
surface->glz_decoder,
- NULL, // &jpeg_decoder(),
+ surface->jpeg_decoder,
surface->zlib_decoder);
g_return_val_if_fail(surface->canvas != NULL, 0);
@@ -340,6 +403,7 @@ static void destroy_canvas(display_surface *surface)
glz_decoder_destroy(surface->glz_decoder);
zlib_decoder_destroy(surface->zlib_decoder);
+ jpeg_decoder_destroy(surface->jpeg_decoder);
if (surface->shmid == -1) {
free(surface->data);
diff --git a/gtk/decode-jpeg.c b/gtk/decode-jpeg.c
index 8491541..a0caead 100644
--- a/gtk/decode-jpeg.c
+++ b/gtk/decode-jpeg.c
@@ -16,3 +16,170 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "decode.h"
+
+#ifdef WIN32
+/* We need some hacks to avoid warnings from the jpeg headers */
+#define XMD_H
+#undef FAR
+#endif
+
+#include <stdio.h>
+#include <jpeglib.h>
+
+typedef struct GlibJpegDecoder
+{
+ SpiceJpegDecoder base;
+ struct jpeg_decompress_struct _cinfo;
+ struct jpeg_error_mgr _jerr;
+ struct jpeg_source_mgr _jsrc;
+
+ uint8_t* _data;
+ int _data_size;
+ int _width;
+ int _height;
+} GlibJpegDecoder;
+
+
+static void begin_decode(SpiceJpegDecoder *decoder,
+ uint8_t* data, int data_size,
+ int* out_width, int* out_height)
+{
+ GlibJpegDecoder *d = SPICE_CONTAINEROF(decoder, GlibJpegDecoder, base);
+
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(data_size != 0);
+
+ if (d->_data)
+ jpeg_abort_decompress(&d->_cinfo);
+
+ d->_data = data;
+ d->_data_size = data_size;
+
+ d->_cinfo.src->next_input_byte = d->_data;
+ d->_cinfo.src->bytes_in_buffer = d->_data_size;
+
+ jpeg_read_header(&d->_cinfo, TRUE);
+
+ d->_cinfo.out_color_space = JCS_RGB;
+ d->_width = d->_cinfo.image_width;
+ d->_height = d->_cinfo.image_height;
+
+ *out_width = d->_width;
+ *out_height = d->_height;
+}
+
+/* TODO: move it elsewhere and reuse it in get_pixbuf(), optimize? */
+typedef void (*converter_rgb_t)(uint8_t* src, uint8_t* dest, int width);
+
+static void convert_rgb_to_bgr(uint8_t* src, uint8_t* dest, int width)
+{
+ int x;
+
+ for (x = 0; x < width; x++) {
+ *dest++ = src[2];
+ *dest++ = src[1];
+ *dest++ = src[0];
+ src += 3;
+ }
+}
+
+static void convert_rgb_to_bgrx(uint8_t* src, uint8_t* dest, int width)
+{
+ int x;
+
+ for (x = 0; x < width; x++) {
+ *dest++ = src[2];
+ *dest++ = src[1];
+ *dest++ = src[0];
+ *dest++ = 0;
+ src += 3;
+ }
+}
+
+static void decode(SpiceJpegDecoder *decoder,
+ uint8_t* dest, int stride, int format)
+{
+ GlibJpegDecoder *d = SPICE_CONTAINEROF(decoder, GlibJpegDecoder, base);
+ uint8_t* scan_line = g_alloca(d->_width * 3);
+ converter_rgb_t converter = NULL;
+ int row;
+
+ switch (format) {
+ case SPICE_BITMAP_FMT_24BIT:
+ converter = convert_rgb_to_bgr;
+ break;
+ case SPICE_BITMAP_FMT_32BIT:
+ converter = convert_rgb_to_bgrx;
+ break;
+ default:
+ g_warning("bad bitmap format, %d", format);
+ return;
+ }
+
+ g_return_if_fail(converter != NULL);
+
+ jpeg_start_decompress(&d->_cinfo);
+
+ for (row = 0; row < d->_height; row++) {
+ jpeg_read_scanlines(&d->_cinfo, &scan_line, 1);
+ converter(scan_line, dest, d->_width);
+ dest += stride;
+ }
+
+ jpeg_finish_decompress(&d->_cinfo);
+}
+
+static SpiceJpegDecoderOps jpeg_decoder_ops = {
+ .begin_decode = begin_decode,
+ .decode = decode,
+};
+
+static void jpeg_decoder_init_source(j_decompress_ptr cinfo)
+{
+}
+
+static boolean jpeg_decoder_fill_input_buffer(j_decompress_ptr cinfo)
+{
+ g_warning("no more data for jpeg");
+ return FALSE;
+}
+
+static void jpeg_decoder_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ g_return_if_fail(num_bytes < (long)cinfo->src->bytes_in_buffer);
+
+ cinfo->src->next_input_byte += num_bytes;
+ cinfo->src->bytes_in_buffer -= num_bytes;
+}
+
+static void jpeg_decoder_term_source (j_decompress_ptr cinfo)
+{
+ return;
+}
+
+SpiceJpegDecoder *jpeg_decoder_new(void)
+{
+ GlibJpegDecoder *d = spice_new0(GlibJpegDecoder, 1);
+
+ d->_cinfo.err = jpeg_std_error(&d->_jerr);
+ jpeg_create_decompress(&d->_cinfo);
+
+ d->_cinfo.src = &d->_jsrc;
+ d->_cinfo.src->init_source = jpeg_decoder_init_source;
+ d->_cinfo.src->fill_input_buffer = jpeg_decoder_fill_input_buffer;
+ d->_cinfo.src->skip_input_data = jpeg_decoder_skip_input_data;
+ d->_cinfo.src->resync_to_restart = jpeg_resync_to_restart;
+ d->_cinfo.src->term_source = jpeg_decoder_term_source;
+
+ d->base.ops = &jpeg_decoder_ops;
+
+ return &d->base;
+}
+
+void jpeg_decoder_destroy(SpiceJpegDecoder *decoder)
+{
+ GlibJpegDecoder *d = SPICE_CONTAINEROF(decoder, GlibJpegDecoder, base);
+
+ jpeg_destroy_decompress(&d->_cinfo);
+ free(d);
+}
diff --git a/gtk/decode.h b/gtk/decode.h
index c2e21d5..8bef3f9 100644
--- a/gtk/decode.h
+++ b/gtk/decode.h
@@ -34,6 +34,9 @@ void glz_decoder_destroy(SpiceGlzDecoder *d);
SpiceZlibDecoder *zlib_decoder_new(void);
void zlib_decoder_destroy(SpiceZlibDecoder *d);
+SpiceJpegDecoder *jpeg_decoder_new(void);
+void jpeg_decoder_destroy(SpiceJpegDecoder *d);
+
G_END_DECLS
#endif // SPICEGTK_DECODE_H_
diff --git a/gtk/spice-channel-cache.h b/gtk/spice-channel-cache.h
index 7e737b9..07acb5c 100644
--- a/gtk/spice-channel-cache.h
+++ b/gtk/spice-channel-cache.h
@@ -29,6 +29,7 @@ typedef struct display_cache_item {
uint64_t id;
uint32_t refcount;
void *ptr;
+ gboolean lossy;
} display_cache_item;
typedef struct display_cache {