diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2010-11-26 02:29:06 +0100 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@redhat.com> | 2010-11-26 02:29:06 +0100 |
commit | db29073aa5b6ddc4c630e901ad4304160e22ad2f (patch) | |
tree | 60c9c9ae03918db18095defbe001cfb40349fa1b | |
parent | 6c603038b5910cd9c1b9697fbaeff429a56f8282 (diff) |
gtk: add jpeg decoder
-rw-r--r-- | gtk/channel-display-priv.h | 1 | ||||
-rw-r--r-- | gtk/channel-display.c | 72 | ||||
-rw-r--r-- | gtk/decode-jpeg.c | 167 | ||||
-rw-r--r-- | gtk/decode.h | 3 | ||||
-rw-r--r-- | gtk/spice-channel-cache.h | 1 |
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 { |