[Spice-commits] 3 commits - client/canvas.h client/Makefile.am client/red_gdi_canvas.cpp client/red_gl_canvas.cpp client/red_sw_canvas.cpp client/windows client/x11 client/zlib_decoder.cpp client/zlib_decoder.h common/canvas_base.c common/canvas_base.h common/gdi_canvas.c common/gdi_canvas.h common/gl_canvas.c common/gl_canvas.h common/lz.c common/lz_common.h common/sw_canvas.c common/sw_canvas.h configure.ac server/Makefile.am server/red_worker.c server/zlib_encoder.c server/zlib_encoder.h spice.proto

Alexander Larsson alexl at kemper.freedesktop.org
Mon Jun 21 06:19:13 PDT 2010


 client/Makefile.am         |    2 
 client/canvas.h            |    3 
 client/red_gdi_canvas.cpp  |    3 
 client/red_gl_canvas.cpp   |    3 
 client/red_sw_canvas.cpp   |    6 
 client/windows/redc.vcproj |   12 +
 client/x11/Makefile.am     |    3 
 client/zlib_decoder.cpp    |   58 ++++++++
 client/zlib_decoder.h      |   41 ++++++
 common/canvas_base.c       |  143 +++++++++++++++++++--
 common/canvas_base.h       |   13 +
 common/gdi_canvas.c        |    4 
 common/gdi_canvas.h        |    3 
 common/gl_canvas.c         |    2 
 common/gl_canvas.h         |    1 
 common/lz.c                |   12 +
 common/lz_common.h         |    9 -
 common/sw_canvas.c         |    6 
 common/sw_canvas.h         |    2 
 configure.ac               |    3 
 server/Makefile.am         |    3 
 server/red_worker.c        |  300 +++++++++++++++++++++++++++++++++++++++------
 server/zlib_encoder.c      |  104 +++++++++++++++
 server/zlib_encoder.h      |   47 +++++++
 spice.proto                |   23 +++
 25 files changed, 743 insertions(+), 63 deletions(-)

New commits:
commit ae1de849acd1e58772dc3f318324ed936ff5f21c
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Jun 9 13:21:57 2010 +0300

    fix for not reseting client palette caches on migration

diff --git a/server/red_worker.c b/server/red_worker.c
index 4ee0c61..3a0b16e 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -10401,6 +10401,8 @@ static int display_channel_handle_migrate_data(DisplayChannel *channel, size_t s
         PANIC("restoring global lz dictionary failed");
     }
 
+    red_pipe_add_type((RedChannel *)channel, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
+
     channel->base.messages_window = 0;
     return TRUE;
 }
commit 537280f183d3a35e8dee8a20238c71e7440cb28c
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 20 17:18:56 2010 +0300

    Lossy compression of RGBA images (on WAN connection)
    
    The RGB channels are compressed using JPEG.
    The alpha channel is compressed using LZ.

diff --git a/common/canvas_base.c b/common/canvas_base.c
index 0148270..2b00f09 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -65,6 +65,10 @@
 
 #define ROUND(_x) ((int)floor((_x) + 0.5))
 
+#define IS_IMAGE_LOSSY(descriptor)                         \
+    (((descriptor)->type == SPICE_IMAGE_TYPE_JPEG) ||      \
+    ((descriptor)->type == SPICE_IMAGE_TYPE_JPEG_ALPHA))
+
 #ifdef WIN32
 typedef struct  __declspec (align(1)) LZImage {
 #else
@@ -607,6 +611,85 @@ static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceJPEGImage *image
     return surface;
 }
 
+static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
+                                             SpiceJPEGAlphaImage *image, int invers)
+{
+    pixman_image_t *surface = NULL;
+    int stride;
+    int width;
+    int height;
+    uint8_t *dest;
+    int alpha_top_down = FALSE;
+    LzData *lz_data = &canvas->lz_data;
+    LzImageType lz_alpha_type;
+    uint8_t *comp_alpha_buf = NULL;
+    uint8_t *decomp_alpha_buf = NULL;
+    int alpha_size;
+    int lz_alpha_width, lz_alpha_height, n_comp_pixels, lz_alpha_top_down;
+
+    canvas->jpeg->ops->begin_decode(canvas->jpeg, image->jpeg_alpha.data,
+                                    image->jpeg_alpha.jpeg_size,
+                                    &width, &height);
+    ASSERT((uint32_t)width == image->descriptor.width);
+    ASSERT((uint32_t)height == image->descriptor.height);
+
+    if (image->jpeg_alpha.flags & SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN) {
+        alpha_top_down = TRUE;
+    }
+
+#ifdef WIN32
+    lz_data->decode_data.dc = canvas->dc;
+#endif
+    surface = alloc_lz_image_surface(&lz_data->decode_data, PIXMAN_a8r8g8b8,
+                                     width, height, width*height, alpha_top_down);
+ 
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
+    }
+
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    stride = pixman_image_get_stride(surface);
+
+    canvas->jpeg->ops->decode(canvas->jpeg, dest, stride, SPICE_BITMAP_FMT_32BIT);
+    
+    comp_alpha_buf = image->jpeg_alpha.data + image->jpeg_alpha.jpeg_size;
+    alpha_size = image->jpeg_alpha.data_size - image->jpeg_alpha.jpeg_size;
+
+    lz_decode_begin(lz_data->lz, comp_alpha_buf, alpha_size, &lz_alpha_type,
+                    &lz_alpha_width, &lz_alpha_height, &n_comp_pixels,
+                    &lz_alpha_top_down, NULL);
+    ASSERT(lz_alpha_type == LZ_IMAGE_TYPE_XXXA);
+    ASSERT(lz_alpha_top_down == alpha_top_down);
+    ASSERT(lz_alpha_width == width);
+    ASSERT(lz_alpha_height == height);
+    ASSERT(n_comp_pixels == width * height);
+
+    if (!alpha_top_down) {
+        decomp_alpha_buf = dest + stride * (height - 1);
+    } else {
+        decomp_alpha_buf = dest;
+    }
+    lz_decode(lz_data->lz, LZ_IMAGE_TYPE_XXXA, decomp_alpha_buf);
+    
+    if (invers) {
+        uint8_t *end = dest + height * stride;
+        for (; dest != end; dest += stride) {
+            uint32_t *pix;
+            uint32_t *end_pix;
+
+            pix = (uint32_t *)dest;
+            end_pix = pix + width;
+            for (; pix < end_pix; pix++) {
+                *pix ^= 0x00ffffff;
+            }
+        }
+    }
+#ifdef DUMP_JPEG
+    dump_jpeg(image->jpeg_alpha.data, image->jpeg_alpha.jpeg_size);
+#endif
+    return surface;
+}
+
 static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap* bitmap,
                                                 SpicePalette *palette, int want_original)
 {
@@ -1088,6 +1171,11 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
         surface = canvas_get_jpeg(canvas, image, 0);
         break;
     }
+    case SPICE_IMAGE_TYPE_JPEG_ALPHA: {
+        SpiceJPEGAlphaImage *image = (SpiceJPEGAlphaImage *)descriptor;
+        surface = canvas_get_jpeg_alpha(canvas, image, 0);
+        break;
+    }
 #if defined(SW_CANVAS_CACHE)
     case SPICE_IMAGE_TYPE_GLZ_RGB: {
         LZImage *image = (LZImage *)descriptor;
@@ -1138,7 +1226,7 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 #endif
         descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE ) {
 #ifdef SW_CANVAS_CACHE
-       if (descriptor->type != SPICE_IMAGE_TYPE_JPEG) {
+        if (!IS_IMAGE_LOSSY(descriptor)) {
             canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
         } else {
             canvas->bits_cache->ops->put_lossy(canvas->bits_cache, descriptor->id, surface);
@@ -1151,7 +1239,7 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 #endif
 #ifdef SW_CANVAS_CACHE
     } else if (descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME) {
-        if (descriptor->type == SPICE_IMAGE_TYPE_JPEG) {
+        if (IS_IMAGE_LOSSY(descriptor)) {
             CANVAS_ERROR("invalid cache replace request: the image is lossy");
         }
         canvas->bits_cache->ops->replace_lossy(canvas->bits_cache, descriptor->id, surface);
diff --git a/common/lz.c b/common/lz.c
index 73a797c..e563584 100644
--- a/common/lz.c
+++ b/common/lz.c
@@ -565,6 +565,9 @@ int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_do
         lz_rgb32_compress(encoder);
         lz_rgb_alpha_compress(encoder);
         break;
+    case LZ_IMAGE_TYPE_XXXA:
+        lz_rgb_alpha_compress(encoder);
+        break;
     case LZ_IMAGE_TYPE_INVALID:
     default:
         encoder->usr->error(encoder->usr, "bad image type\n");
@@ -661,6 +664,7 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
             case LZ_IMAGE_TYPE_RGB24:
             case LZ_IMAGE_TYPE_RGB32:
             case LZ_IMAGE_TYPE_RGBA:
+            case LZ_IMAGE_TYPE_XXXA:
             case LZ_IMAGE_TYPE_INVALID:
             default:
                 encoder->usr->error(encoder->usr, "bad image type\n");
@@ -705,6 +709,14 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
                 encoder->usr->error(encoder->usr, "unsupported output format\n");
             }
             break;
+        case LZ_IMAGE_TYPE_XXXA:
+            if (encoder->type == to_type) {
+                alpha_size = lz_rgb_alpha_decompress(encoder, (rgb32_pixel_t *)buf, size);
+                out_size = alpha_size;
+            } else {
+                encoder->usr->error(encoder->usr, "unsupported output format\n");
+            }
+            break;
         case LZ_IMAGE_TYPE_PLT1_LE:
         case LZ_IMAGE_TYPE_PLT1_BE:
         case LZ_IMAGE_TYPE_PLT4_LE:
diff --git a/common/lz_common.h b/common/lz_common.h
index d5019fd..34276af 100644
--- a/common/lz_common.h
+++ b/common/lz_common.h
@@ -39,17 +39,18 @@ typedef enum {
     LZ_IMAGE_TYPE_RGB16,
     LZ_IMAGE_TYPE_RGB24,
     LZ_IMAGE_TYPE_RGB32,
-    LZ_IMAGE_TYPE_RGBA
+    LZ_IMAGE_TYPE_RGBA,
+    LZ_IMAGE_TYPE_XXXA
 } LzImageType;
 
 #define LZ_IMAGE_TYPE_MASK 0x0f
 #define LZ_IMAGE_TYPE_LOG 4 // number of bits required for coding the image type
 
 /* access to the arrays is based on the image types */
-static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0};
-static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
+static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
+static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
 static const int PLT_PIXELS_PER_BYTE[] = {0, 8, 8, 2, 2, 1};
-static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4};
+static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4};
 
 
 #define LZ_MAGIC (*(uint32_t *)"LZ  ")
diff --git a/server/red_worker.c b/server/red_worker.c
index ff78483..4ee0c61 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -177,6 +177,7 @@ static const char *glz_stat_name = "glz";
 static const char *quic_stat_name = "quic";
 static const char *jpeg_stat_name = "jpeg";
 static const char *zlib_stat_name = "zlib_glz";
+static const char *jpeg_alpha_stat_name = "jpeg_alpha";
 
 static inline void stat_compress_init(stat_info_t *info, const char *name)
 {
@@ -474,6 +475,7 @@ typedef struct  __attribute__ ((__packed__)) RedImage {
         SpiceSurface surface;
         SpiceJPEGData jpeg;
         SpiceZlibGlzRGBData zlib_glz;
+        SpiceJPEGAlphaData jpeg_alpha;
     };
 } RedImage;
 
@@ -700,6 +702,7 @@ struct DisplayChannel {
     stat_info_t quic_stat;
     stat_info_t jpeg_stat;
     stat_info_t zlib_glz_stat;
+    stat_info_t jpeg_alpha_stat;
 #endif
 };
 
@@ -1090,24 +1093,34 @@ static void print_compress_stats(DisplayChannel *display_channel)
                stat_byte_to_mega(display_channel->jpeg_stat.comp_size),
                stat_cpu_time_to_sec(display_channel->jpeg_stat.total)
                );
+    red_printf("JPEG-RGBA\t%8d\t%13.2f\t%12.2f\t%12.2f",
+               display_channel->jpeg_alpha_stat.count,
+               stat_byte_to_mega(display_channel->jpeg_alpha_stat.orig_size),
+               stat_byte_to_mega(display_channel->jpeg_alpha_stat.comp_size),
+               stat_cpu_time_to_sec(display_channel->jpeg_alpha_stat.total)
+               );
     red_printf("-------------------------------------------------------------------");
     red_printf("Total    \t%8d\t%13.2f\t%12.2f\t%12.2f",
                display_channel->lz_stat.count + display_channel->glz_stat.count +
                                                 display_channel->quic_stat.count +
-                                                display_channel->jpeg_stat.count,
+                                                display_channel->jpeg_stat.count +
+                                                display_channel->jpeg_alpha_stat.count,
                stat_byte_to_mega(display_channel->lz_stat.orig_size +
                                  display_channel->glz_stat.orig_size +
                                  display_channel->quic_stat.orig_size +
-                                 display_channel->jpeg_stat.orig_size),
+                                 display_channel->jpeg_stat.orig_size +
+                                 display_channel->jpeg_alpha_stat.orig_size),
                stat_byte_to_mega(display_channel->lz_stat.comp_size +
                                  glz_enc_size +
                                  display_channel->quic_stat.comp_size +
-                                 display_channel->jpeg_stat.comp_size),
+                                 display_channel->jpeg_stat.comp_size +
+                                 display_channel->jpeg_alpha_stat.comp_size),
                stat_cpu_time_to_sec(display_channel->lz_stat.total +
                                     display_channel->glz_stat.total +
                                     display_channel->zlib_glz_stat.total +
                                     display_channel->quic_stat.total +
-                                    display_channel->jpeg_stat.total)
+                                    display_channel->jpeg_stat.total +
+                                    display_channel->jpeg_alpha_stat.total)
                );
 }
 
@@ -6326,7 +6339,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
 
     stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size);
 
-   if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+    if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
         goto glz;
     }
 #ifdef COMPRESS_STAT
@@ -6471,22 +6484,34 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
 {
     RedWorker *worker = display_channel->base.worker;
     JpegData *jpeg_data = &worker->jpeg_data;
+    LzData *lz_data = &worker->lz_data;
     JpegEncoderContext *jpeg = worker->jpeg;
+    LzContext *lz = worker->lz;
     JpegEncoderImageType jpeg_in_type;
-    int size;
+    int jpeg_size = 0;
+    int has_alpha = FALSE;
+    int alpha_lz_size = 0;
+    int comp_head_filled;
+    int comp_head_left;
+    uint8_t *lz_out_start_byte;
+
 #ifdef COMPRESS_STAT
     stat_time_t start_time = stat_now();
 #endif
     switch (src->format) {
-    case SPICE_BITMAP_FMT_32BIT:
-        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
-        break;
     case SPICE_BITMAP_FMT_16BIT:
         jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
         break;
     case SPICE_BITMAP_FMT_24BIT:
         jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
         break;
+    case SPICE_BITMAP_FMT_32BIT:
+        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+        break;
+    case SPICE_BITMAP_FMT_RGBA:
+        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+        has_alpha = TRUE;
+        break;
     default:
         return FALSE;
     }
@@ -6525,6 +6550,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
         }
 
         if ((src->flags & QXL_BITMAP_UNSTABLE)) {
+            ASSERT(!has_alpha);
             jpeg_data->data.u.unstable_lines_data.next = data;
             jpeg_data->data.u.unstable_lines_data.src_stride = stride;
             jpeg_data->data.u.unstable_lines_data.dest_stride = src->stride;
@@ -6540,14 +6566,16 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
                                  sizeof(jpeg_data->data.u.unstable_lines_data.input_bufs[0]->buf) /
                                  jpeg_data->data.u.unstable_lines_data.dest_stride;
             jpeg_data->usr.more_lines = jpeg_usr_more_lines_unstable;
-            size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type, src->x, src->y,
-                               NULL, 0, src->stride, (uint8_t*)jpeg_data->data.bufs_head->buf,
-                               sizeof(jpeg_data->data.bufs_head->buf));
+            jpeg_size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type,
+                                    src->x, src->y, NULL, 0, src->stride,
+                                    (uint8_t*)jpeg_data->data.bufs_head->buf,
+                                    sizeof(jpeg_data->data.bufs_head->buf));
         } else {
             jpeg_data->usr.more_lines = jpeg_usr_no_more_lines;
-            size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type, src->x, src->y,
-                               data, src->y, stride, (uint8_t*)jpeg_data->data.bufs_head->buf,
-                               sizeof(jpeg_data->data.bufs_head->buf));
+            jpeg_size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type,
+                                    src->x, src->y, data, src->y, stride,
+                                    (uint8_t*)jpeg_data->data.bufs_head->buf,
+                                    sizeof(jpeg_data->data.bufs_head->buf));
         }
     } else {
         int stride;
@@ -6584,24 +6612,83 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
             jpeg_data->usr.more_lines = jpeg_usr_more_lines_reverse;
             stride = -src->stride;
         }
-        size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type, src->x, src->y, NULL,
-                           0, stride, (uint8_t*)jpeg_data->data.bufs_head->buf,
-                           sizeof(jpeg_data->data.bufs_head->buf));
+        jpeg_size = jpeg_encode(jpeg, display_channel->jpeg_quality, jpeg_in_type, src->x, src->y, NULL,
+                                0, stride, (uint8_t*)jpeg_data->data.bufs_head->buf,
+                                sizeof(jpeg_data->data.bufs_head->buf));
     }
 
     // the compressed buffer is bigger than the original data
-    if (size > (src->y * src->stride)) {
+    if (jpeg_size > (src->y * src->stride)) {
+        longjmp(jpeg_data->data.jmp_env, 1);
+    }
+
+    if (!has_alpha) {
+        dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
+        dest->jpeg.data_size = jpeg_size;
+
+        o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+        o_comp_data->comp_buf_size = jpeg_size;
+        o_comp_data->is_lossy = TRUE;
+
+        stat_compress_add(&display_channel->jpeg_stat, start_time, src->stride * src->y,
+                          o_comp_data->comp_buf_size);
+        return TRUE;
+    }
+
+     lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
+     lz_data->data.bufs_tail = lz_data->data.bufs_head;
+
+     comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
+     comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
+     lz_out_start_byte = ((uint8_t *)lz_data->data.bufs_head->buf) + comp_head_filled;
+
+     lz_data->data.display_channel = display_channel;
+
+     if ((src->flags & QXL_BITMAP_DIRECT)) {
+         lz_data->usr.more_lines = lz_usr_no_more_lines;
+         alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
+                                   (src->flags & QXL_BITMAP_TOP_DOWN),
+                                   (uint8_t*)get_virt(&worker->mem_slots, src->data,
+                                                      src->stride * src->y, group_id),
+                                   src->y, src->stride,
+                                   lz_out_start_byte,
+                                   comp_head_left);
+    } else {
+        lz_data->data.u.lines_data.enc_get_virt = cb_get_virt;
+        lz_data->data.u.lines_data.enc_get_virt_opaque = &worker->mem_slots;
+        lz_data->data.u.lines_data.enc_validate_virt = cb_validate_virt;
+        lz_data->data.u.lines_data.enc_validate_virt_opaque = &worker->mem_slots;
+        lz_data->data.u.lines_data.stride = src->stride;
+        lz_data->data.u.lines_data.next = src->data;
+        lz_data->data.u.lines_data.group_id = group_id;
+        lz_data->usr.more_lines = lz_usr_more_lines;
+
+        alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
+                                  (src->flags & QXL_BITMAP_TOP_DOWN),
+                                  NULL, 0, src->stride,
+                                  lz_out_start_byte,
+                                  comp_head_left);
+    }
+   
+
+    // the compressed buffer is bigger than the original data
+    if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
         longjmp(jpeg_data->data.jmp_env, 1);
     }
 
-    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
-    dest->jpeg.data_size = size;
+    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
+    dest->jpeg_alpha.flags = 0;
+    if (src->flags & QXL_BITMAP_TOP_DOWN) {
+        dest->jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
+    }
+
+    dest->jpeg_alpha.jpeg_size = jpeg_size;
+    dest->jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
 
     o_comp_data->comp_buf = jpeg_data->data.bufs_head;
-    o_comp_data->comp_buf_size = size;
+    o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
     o_comp_data->is_lossy = TRUE;
-
-    stat_compress_add(&display_channel->jpeg_stat, start_time, src->stride * src->y,
+    stat_compress_add(&display_channel->jpeg_alpha_stat, start_time, src->stride * src->y,
                       o_comp_data->comp_buf_size);
     return TRUE;
 }
@@ -6811,7 +6898,8 @@ static inline int red_compress_image(DisplayChannel *display_channel,
         if (can_lossy && display_channel->enable_jpeg &&
             ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) ||
             (image_compression == SPICE_IMAGE_COMPRESS_AUTO_GLZ))) {
-            if (src->format != SPICE_BITMAP_FMT_RGBA) {
+            // if we use lz for alpha, the stride can't be extra 
+            if (src->format != SPICE_BITMAP_FMT_RGBA || !_stride_is_extra(src)) {
                 return red_jpeg_compress_image(display_channel, dest,
                                                src, o_comp_data, drawable->group_id);
             }
@@ -9073,8 +9161,8 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
                                                           &bitmap,
                                                           worker->mem_slots.internal_groupslot_id);
                 if (grad_level == BITMAP_GRADUAL_HIGH) {
-                    lossy_comp = display_channel->enable_jpeg && item->can_lossy &&
-                                 (item->image_format != SPICE_BITMAP_FMT_RGBA);
+                    // if we use lz for alpha, the stride can't be extra 
+                    lossy_comp = display_channel->enable_jpeg && item->can_lossy;
                 } else {
                     lz_comp = TRUE;
                 }
@@ -10593,6 +10681,7 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
     stat_compress_init(&display_channel->quic_stat, quic_stat_name);
     stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
     stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
+    stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
 }
 
 static void red_disconnect_cursor(RedChannel *channel)
@@ -11144,6 +11233,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
             stat_reset(&worker->display_channel->glz_stat);
             stat_reset(&worker->display_channel->jpeg_stat);
             stat_reset(&worker->display_channel->zlib_glz_stat);
+            stat_reset(&worker->display_channel->jpeg_alpha_stat);
         }
 #endif
         break;
diff --git a/spice.proto b/spice.proto
index 84d3fa9..dbd3dde 100644
--- a/spice.proto
+++ b/spice.proto
@@ -294,6 +294,7 @@ enum8 image_type {
     JPEG,
     FROM_CACHE_LOSSLESS,
     ZLIB_GLZ_RGB,
+    JPEG_ALPHA,
 };
 
 flags8 image_flags {
@@ -321,6 +322,10 @@ flags8 bitmap_flags {
     TOP_DOWN,
 };
 
+flags8 jpeg_alpha_flags {
+    TOP_DOWN,
+};
+
 enum8 image_scale_mode {
     INTERPOLATE,
     NEAREST,
@@ -477,6 +482,13 @@ struct ZlibGlzRGBData {
     uint8 data[data_size] @end @nomarshal;
 } @ctype(SpiceZlibGlzRGBData);
 
+struct JPEGAlphaData {
+    jpeg_alpha_flags flags;
+    uint32 jpeg_size;
+    uint32 data_size;
+    uint8 data[data_size] @end @nomarshal;
+} @ctype(SpiceJPEGAlphaData);
+
 struct Surface {
     uint32 surface_id;
 };
@@ -500,6 +512,8 @@ struct Image {
         LZPLTData lzplt_data @ctype(SpiceLZPLTData);
     case ZLIB_GLZ_RGB:
         ZlibGlzRGBData zlib_glz_data @ctype(SpiceZlibGlzRGBData);
+    case JPEG_ALPHA:
+        JPEGAlphaData jpeg_alpha_data  @ctype(SpiceJPEGAlphaData);
     case SURFACE:
         Surface surface_data;
     } u @end;
commit 25bb38f643af6f0015df369a22176275b6ebfae0
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 20 15:24:49 2010 +0300

    applying zlib compression over glz on WAN connection

diff --git a/client/Makefile.am b/client/Makefile.am
index f700ed6..fb69ce6 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -111,6 +111,8 @@ RED_COMMON_SRCS =			\
 	threads.h			\
 	utils.cpp			\
 	utils.h				\
+	zlib_decoder.cpp		\
+	zlib_decoder.h			\
 	$(NULL)
 
 MAINTAINERCLEANFILES = $(spice_built_sources)
diff --git a/client/canvas.h b/client/canvas.h
index ff70e11..87bc060 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -30,6 +30,7 @@
 #include "glz_decoded_image.h"
 #include "glz_decoder.h"
 #include "jpeg_decoder.h"
+#include "zlib_decoder.h"
 
 enum CanvasType {
     CANVAS_TYPE_INVALID,
@@ -446,6 +447,7 @@ protected:
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
     JpegDecoder& jpeg_decoder() { return _jpeg_decoder;}
+    ZlibDecoder& zlib_decoder() { return _zlib_decoder;}
 
 private:
     void access_test(void* ptr, size_t size);
@@ -468,6 +470,7 @@ private:
     GlzDecoder _glz_decoder;
 
     JpegDecoder _jpeg_decoder;
+    ZlibDecoder _zlib_decoder;
 
     CSurfaces& _csurfaces;
 
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 453023e..72b31df 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -38,7 +38,8 @@ GDICanvas::GDICanvas(int width, int height, uint32_t format,
                                       &palette_cache.base,
                                       &csurfaces.base,
                                       &glz_decoder(),
-                                      &jpeg_decoder()))) {
+                                      &jpeg_decoder(),
+                                      &zlib_decoder()))) {
         THROW("create canvas failed");
     }
 }
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 1a219cd..d7841b9 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -41,7 +41,8 @@ GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
                                      &palette_cache.base,
                                      &csurfaces.base,
                                      &glz_decoder(),
-                                     &jpeg_decoder()))) {
+                                     &jpeg_decoder(),
+                                     &zlib_decoder()))) {
         THROW("create canvas failed");
     }
 }
diff --git a/client/red_sw_canvas.cpp b/client/red_sw_canvas.cpp
index 7a8daf4..b580e61 100644
--- a/client/red_sw_canvas.cpp
+++ b/client/red_sw_canvas.cpp
@@ -43,14 +43,16 @@ SCanvas::SCanvas(bool onscreen,
                                          &palette_cache.base,
                                          &csurfaces.base,
                                          &glz_decoder(),
-                                         &jpeg_decoder());
+                                         &jpeg_decoder(),
+                                         &zlib_decoder());
     } else {
         _canvas = canvas_create(width, height, format,
                                 &pixmap_cache.base,
                                 &palette_cache.base,
                                 &csurfaces.base,
                                 &glz_decoder(),
-                                &jpeg_decoder());
+                                &jpeg_decoder(),
+                                &zlib_decoder());
     }
     if (_canvas == NULL) {
         THROW("create canvas failed");
diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj
index 8f9e509..37ca23a 100644
--- a/client/windows/redc.vcproj
+++ b/client/windows/redc.vcproj
@@ -69,7 +69,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="log4cppD.lib pixman-1D.lib libeay32MTd.lib ssleay32MTd.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1D.lib pthreadVC2d.lib version.lib CEGUIBase_Static_d.lib CEGUITGAImageCodec_Static_d.lib CEGUIExpatParser_Static_d.lib freetype2312MT_D.lib libexpatMT_D.lib pcre_D.lib CEGUIFalagardWRBase_Static_d.lib libjpeg-static-mt-debug.lib"
+				AdditionalDependencies="log4cppD.lib pixman-1D.lib libeay32MTd.lib ssleay32MTd.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1D.lib pthreadVC2d.lib version.lib CEGUIBase_Static_d.lib CEGUITGAImageCodec_Static_d.lib CEGUIExpatParser_Static_d.lib freetype2312MT_D.lib libexpatMT_D.lib pcre_D.lib CEGUIFalagardWRBase_Static_d.lib libjpeg-static-mt-debug.lib zlibwapi.lib"
 				OutputFile="$(OutDir)\spicec.exe"
 				LinkIncremental="2"
 				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
@@ -148,7 +148,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="log4cpp.lib pixman-1.lib libeay32MT.lib ssleay32MT.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1.lib pthreadVC2.lib version.lib CEGUIBase_Static.lib CEGUITGAImageCodec_Static.lib CEGUIExpatParser_Static.lib freetype2312MT.lib libexpatMT.lib pcre.lib CEGUIFalagardWRBase_Static.lib libjpeg-static-mt.lib"
+				AdditionalDependencies="log4cpp.lib pixman-1.lib libeay32MT.lib ssleay32MT.lib ws2_32.lib msimg32.lib winmm.lib libcelt_0_5_1.lib pthreadVC2.lib version.lib CEGUIBase_Static.lib CEGUITGAImageCodec_Static.lib CEGUIExpatParser_Static.lib freetype2312MT.lib libexpatMT.lib pcre.lib CEGUIFalagardWRBase_Static.lib libjpeg-static-mt.lib zlibwapi.lib"
 				OutputFile="$(OutDir)\spicec.exe"
 				LinkIncremental="1"
 				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
@@ -437,6 +437,10 @@
 				RelativePath="..\utils.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\zlib_decoder.cpp"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Header Files"
@@ -671,6 +675,10 @@
 				RelativePath=".\win_platform.h"
 				>
 			</File>
+			<File
+				RelativePath="..\zlib_decoder.h"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 3d90e9f..81036fb 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -112,6 +112,8 @@ RED_COMMON_SRCS =					\
 	$(CLIENT_DIR)/tunnel_channel.h			\
 	$(CLIENT_DIR)/utils.cpp				\
 	$(CLIENT_DIR)/utils.h				\
+	$(CLIENT_DIR)/zlib_decoder.cpp			\
+	$(CLIENT_DIR)/zlib_decoder.h			\
 	$(CLIENT_DIR)/icon.h				\
 	$(CLIENT_DIR)/gui/softrenderer.h		\
 	$(CLIENT_DIR)/gui/softrenderer.cpp		\
@@ -174,6 +176,7 @@ spicec_LDFLAGS = \
 	$(SSL_LIBS)					\
 	$(CEGUI_LIBS)					\
 	$(JPEG_LIBS)					\
+	$(Z_LIBS)					\
 	$(SPICE_NONPKGCONFIG_LIBS)
 
 spicec_LDADD =						\
diff --git a/client/zlib_decoder.cpp b/client/zlib_decoder.cpp
new file mode 100644
index 0000000..68b1b33
--- /dev/null
+++ b/client/zlib_decoder.cpp
@@ -0,0 +1,58 @@
+#include "common.h"
+#include "zlib_decoder.h"
+#include "debug.h"
+#include "utils.h"
+
+static void op_decode(SpiceZlibDecoder *decoder,
+                      uint8_t *data,
+                      int data_size,
+                      uint8_t *dest,
+                      int dest_size)
+{
+    ZlibDecoder* _decoder = static_cast<ZlibDecoder*>(decoder);
+    _decoder->decode(data, data_size, dest, dest_size);
+}
+
+ZlibDecoder::ZlibDecoder()
+{
+    int z_ret;
+    
+    _z_strm.zalloc = Z_NULL;
+    _z_strm.zfree = Z_NULL;
+    _z_strm.opaque = Z_NULL;
+    _z_strm.next_in = Z_NULL;
+    _z_strm.avail_in = 0;
+    z_ret = inflateInit(&_z_strm);
+    if (z_ret != Z_OK) {
+        THROW("zlib decoder init failed, error %d", z_ret);
+    }
+
+    static SpiceZlibDecoderOps decoder_ops = {
+        op_decode,
+    };
+
+    ops = &decoder_ops;
+}
+
+ZlibDecoder::~ZlibDecoder()
+{
+    inflateEnd(&_z_strm);
+}
+
+
+void ZlibDecoder::decode(uint8_t *data, int data_size, uint8_t *dest, int dest_size)
+{
+    int z_ret;
+
+    inflateReset(&_z_strm);
+    _z_strm.next_in = data;
+    _z_strm.avail_in = data_size;
+    _z_strm.next_out = dest;
+    _z_strm.avail_out = dest_size;
+
+    z_ret = inflate(&_z_strm, Z_FINISH);
+   
+    if (z_ret != Z_STREAM_END) {
+        THROW("zlib inflate failed, error %d", z_ret);
+    }
+}
diff --git a/client/zlib_decoder.h b/client/zlib_decoder.h
new file mode 100644
index 0000000..84b6f83
--- /dev/null
+++ b/client/zlib_decoder.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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 General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_ZLIB_DECODER
+#define _H_ZLIB_DECODER
+
+#include "common.h"
+#include "canvas_base.h"
+
+#define ZLIB_WINAPI 
+#include <zlib.h>
+
+
+class ZlibDecoder : public SpiceZlibDecoder {
+public:
+    ZlibDecoder();
+    ~ZlibDecoder();
+
+    void decode(uint8_t *data, int data_size, uint8_t *dest, int dest_size);
+
+private:
+    z_stream _z_strm;
+
+};
+
+#endif
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 26bc52c..0148270 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -186,6 +186,7 @@ typedef struct CanvasBase {
     LzData lz_data;
     GlzData glz_data;
     SpiceJpegDecoder* jpeg;
+    SpiceZlibDecoder* zlib;
 
     void *usr_data;
     spice_destroy_fn_t usr_data_destroy;
@@ -817,6 +818,21 @@ static pixman_image_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int inv
     return lz_data->decode_data.out_surface;
 }
 
+static pixman_image_t *canvas_get_glz_rgb_common(CanvasBase *canvas, uint8_t *data,
+                                                 int want_original)
+{
+    if (canvas->glz_data.decoder == NULL) {
+        CANVAS_ERROR("glz not supported");
+    }
+
+    canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder,
+                                          data, NULL,
+                                          &canvas->glz_data.decode_data);
+
+    /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */
+    return (canvas->glz_data.decode_data.out_surface);
+}
+
 // don't handle plts since bitmaps with plt can be decoded globally to RGB32 (because
 // same byte sequence can be transformed to different RGB pixels by different plts)
 static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image,
@@ -827,15 +843,25 @@ static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image,
     canvas->glz_data.decode_data.dc = canvas->dc;
 #endif
 
-    if (canvas->glz_data.decoder == NULL) {
-        CANVAS_ERROR("glz not supported");
+    return canvas_get_glz_rgb_common(canvas, image->lz_rgb.data, want_original);
+}
+
+static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceZlibGlzRGBImage *image,
+                                               int want_original)
+{
+    uint8_t *glz_data;
+    pixman_image_t *surface;
+
+    if (canvas->zlib == NULL) {
+        CANVAS_ERROR("zlib not supported");
     }
 
-    canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder,
-                                          image->lz_rgb.data, NULL,
-                                          &canvas->glz_data.decode_data);
-    /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */
-    return (canvas->glz_data.decode_data.out_surface);
+    glz_data = (uint8_t*)spice_malloc(image->zlib_glz.glz_data_size);
+    canvas->zlib->ops->decode(canvas->zlib, image->zlib_glz.data, image->zlib_glz.data_size,
+                              glz_data, image->zlib_glz.glz_data_size);
+    surface = canvas_get_glz_rgb_common(canvas, glz_data, want_original);
+    free(glz_data);
+    return surface;
 }
 
 //#define DEBUG_DUMP_BITMAP
@@ -1025,7 +1051,8 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 #ifdef SW_CANVAS_CACHE
         !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME) &&
 #endif
-        (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB)) {
+        (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB) &&
+        (descriptor->type != SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB)) {
         return NULL;
     }
 
@@ -1067,8 +1094,12 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
         surface = canvas_get_glz(canvas, image, want_original);
         break;
     }
+    case SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB: {
+        SpiceZlibGlzRGBImage *image = (SpiceZlibGlzRGBImage *)descriptor;
+        surface = canvas_get_zlib_glz_rgb(canvas, image, want_original);
+        break;
+    }
 #endif
-
     case SPICE_IMAGE_TYPE_FROM_CACHE:
         surface = canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
         break;
@@ -3305,6 +3336,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
                             , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
                             , SpiceJpegDecoder *jpeg_decoder
+                            , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
 #endif
@@ -3339,6 +3371,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
     canvas->surfaces = surfaces;
     canvas->glz_data.decoder = glz_decoder;
     canvas->jpeg = jpeg_decoder;
+    canvas->zlib = zlib_decoder;
 
     canvas->format = format;
 
diff --git a/common/canvas_base.h b/common/canvas_base.h
index b54fce5..9dfbe85 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -32,6 +32,7 @@ typedef struct _SpiceImageSurfaces SpiceImageSurfaces;
 typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 typedef struct _SpiceJpegDecoder SpiceJpegDecoder;
+typedef struct _SpiceZlibDecoder SpiceZlibDecoder;
 typedef struct _SpiceVirtMapping SpiceVirtMapping;
 typedef struct _SpiceCanvas SpiceCanvas;
 
@@ -108,6 +109,18 @@ struct _SpiceJpegDecoder {
 };
 
 typedef struct {
+    void (*decode)(SpiceZlibDecoder *decoder,
+                   uint8_t *data,
+                   int data_size,
+                   uint8_t *dest,
+                   int dest_size);
+} SpiceZlibDecoderOps;
+
+struct _SpiceZlibDecoder {
+  SpiceZlibDecoderOps *ops;
+};
+
+typedef struct {
     void *(*get_virt)(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size);
     void (*validate_virt)(SpiceVirtMapping *mapping, unsigned long virt,
                           unsigned long from_addr, uint32_t add_size);
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index fea2390..76a7674 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1851,6 +1851,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
                             , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
                             , SpiceJpegDecoder *jpeg_decoder
+                            , SpiceZlibDecoder *zlib_decoder
                             )
 {
     GdiCanvas *canvas;
@@ -1870,7 +1871,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
 #endif
                                , surfaces
                                , glz_decoder
-                               , jpeg_decoder);
+                               , jpeg_decoder
+                               , zlib_decoder);
     canvas->dc = dc;
     canvas->lock = lock;
     return (SpiceCanvas *)canvas;
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 02e053d..3fdf07e 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -32,7 +32,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
                                SpicePaletteCache *palette_cache,
 			       SpiceImageSurfaces *surfaces,
                                SpiceGlzDecoder *glz_decoder,
-                               SpiceJpegDecoder *jpeg_decoder);
+                               SpiceJpegDecoder *jpeg_decoder,
+                               SpiceZlibDecoder *zlib_decoder);
 
 void gdi_canvas_init();
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 444fa4b..688b635 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -817,6 +817,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
                               , SpiceImageSurfaces *surfaces
                               , SpiceGlzDecoder *glz_decoder
                               , SpiceJpegDecoder *jpeg_decoder
+                              , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                               , SpiceVirtMapping *virt_mapping
 #endif
@@ -845,6 +846,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
                                , surfaces
                                , glz_decoder
                                , jpeg_decoder
+                               , zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                                , virt_mapping
 #endif
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index cd76f8d..d3f707a 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -31,6 +31,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
 			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
 #endif
diff --git a/common/sw_canvas.c b/common/sw_canvas.c
index c1a7392..56865c3 100644
--- a/common/sw_canvas.c
+++ b/common/sw_canvas.c
@@ -1172,6 +1172,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
                            , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
@@ -1200,6 +1201,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
                                , surfaces
                                , glz_decoder
                                , jpeg_decoder
+                               , zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                                , virt_mapping
 #endif
@@ -1222,6 +1224,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
                            , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
@@ -1242,6 +1245,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
                                 , surfaces
                                 , glz_decoder
                                 , jpeg_decoder
+                                , zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                                 , virt_mapping
 #endif
@@ -1259,6 +1263,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
                            , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
@@ -1279,6 +1284,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
                                 , surfaces
                                 , glz_decoder
                                 , jpeg_decoder
+                                , zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                                 , virt_mapping
 #endif
diff --git a/common/sw_canvas.h b/common/sw_canvas.h
index 63a7863..05e4071 100644
--- a/common/sw_canvas.h
+++ b/common/sw_canvas.h
@@ -36,6 +36,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
 			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
@@ -51,6 +52,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint
 			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
+                           , SpiceZlibDecoder *zlib_decoder
 #ifndef SW_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
diff --git a/configure.ac b/configure.ac
index ef5f7f7..0e91547 100644
--- a/configure.ac
+++ b/configure.ac
@@ -234,6 +234,9 @@ AC_CHECK_LIB(jpeg, jpeg_destroy_decompress,
     AC_MSG_ERROR([libjpeg not found]))
 AC_SUBST(JPEG_LIBS)
 
+AC_CHECK_LIB(z, deflate, Z_LIBS='-lz', AC_MSG_ERROR([zlib not found]))
+AC_SUBST(Z_LIBS)
+
 dnl ===========================================================================
 dnl check compiler flags
 
diff --git a/server/Makefile.am b/server/Makefile.am
index 9a6ac88..f87e3f4 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -61,6 +61,7 @@ libspice_server_la_LIBADD =			\
 	$(CELT051_LIBS)				\
 	$(SLIRP_LIBS)				\
 	$(LIBRT)				\
+	$(Z_LIBS)				\
 	$(NULL)
 
 if SUPPORT_TUNNEL
@@ -105,6 +106,8 @@ libspice_server_la_SOURCES =			\
 	generated_demarshallers.c		\
 	generated_marshallers.c			\
 	generated_marshallers.h			\
+	zlib_encoder.c				\
+	zlib_encoder.h				\
 	$(TUNNEL_SRCS)				\
 	$(COMMON_SRCS)				\
 	$(NULL)
diff --git a/server/red_worker.c b/server/red_worker.c
index 387861c..ff78483 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -53,6 +53,7 @@
 #include "marshaller.h"
 #include "demarshallers.h"
 #include "generated_marshallers.h"
+#include "zlib_encoder.h"
 
 //#define COMPRESS_STAT
 //#define DUMP_BITMAP
@@ -94,6 +95,9 @@
 
 #define RED_COMPRESS_BUF_SIZE (1024 * 64)
 
+#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
+#define MIN_GLZ_SIZE_FOR_ZLIB 100
+
 typedef int64_t red_time_t;
 
 static inline red_time_t timespec_to_red_time(struct timespec *time)
@@ -172,6 +176,7 @@ static const char *lz_stat_name = "lz";
 static const char *glz_stat_name = "glz";
 static const char *quic_stat_name = "quic";
 static const char *jpeg_stat_name = "jpeg";
+static const char *zlib_stat_name = "zlib_glz";
 
 static inline void stat_compress_init(stat_info_t *info, const char *name)
 {
@@ -468,6 +473,7 @@ typedef struct  __attribute__ ((__packed__)) RedImage {
         SpiceLZPLTData lz_plt;
         SpiceSurface surface;
         SpiceJPEGData jpeg;
+        SpiceZlibGlzRGBData zlib_glz;
     };
 } RedImage;
 
@@ -562,6 +568,10 @@ typedef struct  {
             int input_bufs_pos;
             RedCompressBuf *input_bufs[2];
         } unstable_lines_data;
+        struct {
+            RedCompressBuf* next;
+            int size_left;
+        } compressed_data; // for encoding data that was already compressed by another method
     } u;
     char message_buf[512];
 } EncoderData;
@@ -586,6 +596,11 @@ typedef struct {
     EncoderData data;
 } JpegData;
 
+typedef struct {
+    ZlibEncoderUsrContext usr;
+    EncoderData data;
+} ZlibData;
+
 /**********************************/
 /* LZ dictionary related entities */
 /**********************************/
@@ -671,6 +686,8 @@ struct DisplayChannel {
 
     int enable_jpeg;
     int jpeg_quality;
+    int enable_zlib_glz_wrap;
+    int zlib_level;
 #ifdef RED_STATISTICS
     StatNodeRef stat;
     uint64_t *cache_hits_counter;
@@ -682,6 +699,7 @@ struct DisplayChannel {
     stat_info_t glz_stat;
     stat_info_t quic_stat;
     stat_info_t jpeg_stat;
+    stat_info_t zlib_glz_stat;
 #endif
 };
 
@@ -953,6 +971,9 @@ typedef struct RedWorker {
 
     JpegData jpeg_data;
     JpegEncoderContext *jpeg;
+    
+    ZlibData zlib_data;
+    ZlibEncoder *zlib;
 
 #ifdef PIPE_DEBUG
     uint32_t last_id;
@@ -1027,9 +1048,16 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i
 #ifdef COMPRESS_STAT
 static void print_compress_stats(DisplayChannel *display_channel)
 {
+    uint64_t glz_enc_size;
+
     if (!display_channel) {
         return;
     }
+
+    glz_enc_size = display_channel->enable_zlib_glz_wrap ?
+                       display_channel->zlib_glz_stat.comp_size :
+                       display_channel->glz_stat.comp_size;
+
     red_printf("==> Compression stats for display %u", display_channel->base.id);
     red_printf("Method   \t  count  \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
     red_printf("QUIC     \t%8d\t%13.2f\t%12.2f\t%12.2f",
@@ -1044,6 +1072,12 @@ static void print_compress_stats(DisplayChannel *display_channel)
                stat_byte_to_mega(display_channel->glz_stat.comp_size),
                stat_cpu_time_to_sec(display_channel->glz_stat.total)
                );
+    red_printf("ZLIB GLZ \t%8d\t%13.2f\t%12.2f\t%12.2f",
+               display_channel->zlib_glz_stat.count,
+               stat_byte_to_mega(display_channel->zlib_glz_stat.orig_size),
+               stat_byte_to_mega(display_channel->zlib_glz_stat.comp_size),
+               stat_cpu_time_to_sec(display_channel->zlib_glz_stat.total)
+               );
     red_printf("LZ       \t%8d\t%13.2f\t%12.2f\t%12.2f",
                display_channel->lz_stat.count,
                stat_byte_to_mega(display_channel->lz_stat.orig_size),
@@ -1066,11 +1100,12 @@ static void print_compress_stats(DisplayChannel *display_channel)
                                  display_channel->quic_stat.orig_size +
                                  display_channel->jpeg_stat.orig_size),
                stat_byte_to_mega(display_channel->lz_stat.comp_size +
-                                 display_channel->glz_stat.comp_size +
+                                 glz_enc_size +
                                  display_channel->quic_stat.comp_size +
                                  display_channel->jpeg_stat.comp_size),
                stat_cpu_time_to_sec(display_channel->lz_stat.total +
                                     display_channel->glz_stat.total +
+                                    display_channel->zlib_glz_stat.total +
                                     display_channel->quic_stat.total +
                                     display_channel->jpeg_stat.total)
                );
@@ -5775,6 +5810,12 @@ static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
     return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
 }
 
+static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((ZlibData *)usr)->data);
+    return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
+}
+
 static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
 {
     uint32_t data_size;
@@ -5927,6 +5968,27 @@ static int jpeg_usr_no_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
     return 0;
 }
 
+static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
+{
+    EncoderData *usr_data = &(((ZlibData *)usr)->data);
+    int buf_size;
+
+    if (!usr_data->u.compressed_data.next) {
+        ASSERT(usr_data->u.compressed_data.size_left == 0);
+        return 0;
+    }
+
+    *input = (uint8_t*)usr_data->u.compressed_data.next->buf;
+    buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
+                   usr_data->u.compressed_data.size_left);
+
+    usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
+    usr_data->u.compressed_data.size_left -= buf_size;
+    
+    return buf_size;
+
+}
+
 static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
 {
     GlzData *lz_data = (GlzData *)usr;
@@ -6001,6 +6063,18 @@ static inline void red_init_jpeg(RedWorker *worker)
     }
 }
 
+static inline void red_init_zlib(RedWorker *worker)
+{
+    worker->zlib_data.usr.more_space = zlib_usr_more_space;
+    worker->zlib_data.usr.more_input = zlib_usr_more_input;
+
+    worker->zlib = zlib_encoder_create(&worker->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL);
+
+    if (!worker->zlib) {
+        PANIC("create zlib encoder failed");
+    }
+}
+
 #ifdef __GNUC__
 #define ATTR_PACKED __attribute__ ((__packed__))
 #else
@@ -6204,12 +6278,14 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
 #endif
     ASSERT(BITMAP_FMT_IS_RGB[src->format]);
     GlzData *glz_data = &display_channel->glz_data;
+    ZlibData *zlib_data;
     LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
     RedGlzDrawable *glz_drawable;
     GlzDrawableInstanceItem *glz_drawable_instance;
     uint8_t *lines;
     unsigned int num_lines;
-    int size;
+    int glz_size;
+    int zlib_size;
 
     glz_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
     glz_data->data.bufs_head = glz_data->data.bufs_tail;
@@ -6241,21 +6317,67 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
         num_lines = 0;
     }
 
-    size = glz_encode(display_channel->glz, type, src->x, src->y,
-                      (src->flags & QXL_BITMAP_TOP_DOWN), lines, num_lines,
-                      src->stride, (uint8_t*)glz_data->data.bufs_head->buf,
-                      sizeof(glz_data->data.bufs_head->buf),
-                      glz_drawable_instance,
-                      &glz_drawable_instance->glz_instance);
+    glz_size = glz_encode(display_channel->glz, type, src->x, src->y,
+                          (src->flags & QXL_BITMAP_TOP_DOWN), lines, num_lines,
+                          src->stride, (uint8_t*)glz_data->data.bufs_head->buf,
+                          sizeof(glz_data->data.bufs_head->buf),
+                          glz_drawable_instance,
+                          &glz_drawable_instance->glz_instance);
+
+    stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size);
+
+   if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+        goto glz;
+    }
+#ifdef COMPRESS_STAT
+    start_time = stat_now();
+#endif
+    zlib_data = &worker->zlib_data;
+
+    zlib_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+    zlib_data->data.bufs_head = zlib_data->data.bufs_tail;
+
+    if (!zlib_data->data.bufs_head) {
+        red_printf("failed to allocate zlib compress buffer");
+        goto glz;
+    }
+
+    zlib_data->data.bufs_head->send_next = NULL;
+    zlib_data->data.display_channel = display_channel;
+
+    zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
+    zlib_data->data.u.compressed_data.size_left = glz_size;
 
+    zlib_size = zlib_encode(worker->zlib, display_channel->zlib_level,
+                            glz_size, (uint8_t*)zlib_data->data.bufs_head->buf,
+                            sizeof(zlib_data->data.bufs_head->buf));
+
+    // the compressed buffer is bigger than the original data
+    if (zlib_size >= glz_size) {
+        while (zlib_data->data.bufs_head) {
+            RedCompressBuf *buf = zlib_data->data.bufs_head;
+            zlib_data->data.bufs_head = buf->send_next;
+            red_display_free_compress_buf(display_channel, buf);
+        }
+        goto glz;
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
+    dest->zlib_glz.glz_data_size = glz_size;
+    dest->zlib_glz.data_size = zlib_size;
+
+    o_comp_data->comp_buf = zlib_data->data.bufs_head;
+    o_comp_data->comp_buf_size = zlib_size;
+
+    stat_compress_add(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size);
+    return TRUE;
+glz:
     dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
-    dest->lz_rgb.data_size = size;
+    dest->lz_rgb.data_size = glz_size;
 
     o_comp_data->comp_buf = glz_data->data.bufs_head;
-    o_comp_data->comp_buf_size = size;
+    o_comp_data->comp_buf_size = glz_size;
 
-    stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y,
-                      size);
     return TRUE;
 }
 
@@ -9592,7 +9714,7 @@ static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, ui
 
     oglctx_make_current(ctx);
     if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base,
-                                    &worker->image_surfaces, NULL, NULL,
+                                    &worker->image_surfaces, NULL, NULL, NULL,
                                     &worker->preload_group_virt_mapping))) {
         return NULL;
     }
@@ -9650,7 +9772,7 @@ static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *sur
         canvas = canvas_create_for_data(width, height, format,
                                         line_0, stride,
                                         &worker->image_cache.base,
-                                        &worker->image_surfaces, NULL, NULL,
+                                        &worker->image_surfaces, NULL, NULL, NULL,
                                         &worker->preload_group_virt_mapping);
         surface->context.top_down = TRUE;
         surface->context.canvas_draws_on_surface = TRUE;
@@ -10459,6 +10581,9 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
     display_channel->enable_jpeg = IS_LOW_BANDWIDTH();
     display_channel->jpeg_quality = 85;
 
+    display_channel->enable_zlib_glz_wrap = IS_LOW_BANDWIDTH();
+    display_channel->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
+
     red_ref_channel((RedChannel*)display_channel);
     on_new_display_channel(worker);
     red_unref_channel((RedChannel*)display_channel);
@@ -10467,6 +10592,7 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
     stat_compress_init(&display_channel->glz_stat, glz_stat_name);
     stat_compress_init(&display_channel->quic_stat, quic_stat_name);
     stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
+    stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
 }
 
 static void red_disconnect_cursor(RedChannel *channel)
@@ -11017,6 +11143,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
             stat_reset(&worker->display_channel->lz_stat);
             stat_reset(&worker->display_channel->glz_stat);
             stat_reset(&worker->display_channel->jpeg_stat);
+            stat_reset(&worker->display_channel->zlib_glz_stat);
         }
 #endif
         break;
@@ -11180,6 +11307,7 @@ void *red_worker_main(void *arg)
     red_init_quic(&worker);
     red_init_lz(&worker);
     red_init_jpeg(&worker);
+    red_init_zlib(&worker);
     worker.epoll_timeout = INF_EPOLL_WAIT;
     for (;;) {
         struct epoll_event events[MAX_EPOLL_SOURCES];
diff --git a/server/zlib_encoder.c b/server/zlib_encoder.c
new file mode 100644
index 0000000..e0d8d83
--- /dev/null
+++ b/server/zlib_encoder.c
@@ -0,0 +1,104 @@
+#include "red_common.h"
+#include "zlib_encoder.h"
+#include <zlib.h>
+
+struct ZlibEncoder {
+    ZlibEncoderUsrContext *usr;
+
+    z_stream strm;
+    int last_level;
+};
+
+ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level)
+{
+    ZlibEncoder *enc;
+    int z_ret;
+
+    if (!usr->more_space || !usr->more_input) {
+        return NULL;
+    }
+
+    enc = spice_new0(ZlibEncoder, 1);
+
+    enc->usr = usr;
+
+    enc->strm.zalloc = Z_NULL;
+    enc->strm.zfree = Z_NULL;
+    enc->strm.opaque = Z_NULL;
+
+    z_ret = deflateInit(&enc->strm, level);
+    enc->last_level = level;
+    if (z_ret != Z_OK) {
+        red_printf("zlib error");
+        free(enc);
+        return NULL;
+    }
+
+    return enc;
+}
+
+void zlib_encoder_destroy(ZlibEncoder *encoder)
+{
+    deflateEnd(&encoder->strm);
+    free(encoder);
+}
+
+/* returns the total size of the encoded data */
+int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
+                uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+    int flush;
+    int enc_size = 0;
+    int out_size = 0;
+    int z_ret;
+
+    z_ret = deflateReset(&zlib->strm);
+
+    if (z_ret != Z_OK) {
+        red_error("deflateReset failed");
+    }
+
+    zlib->strm.next_out = io_ptr;
+    zlib->strm.avail_out = num_io_bytes;
+
+    if (level != zlib->last_level) {
+        if (zlib->strm.avail_out == 0) {
+            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
+            if (zlib->strm.avail_out == 0) {
+                red_error("not enough space");
+            }
+        }
+        z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY);
+        if (z_ret != Z_OK) {
+            red_error("deflateParams failed");
+        }
+        zlib->last_level = level;
+    }
+
+
+    do {
+        zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in);
+        if (zlib->strm.avail_in <= 0) {
+            red_error("more input failed\n");
+        }
+        enc_size += zlib->strm.avail_in;
+        flush = (enc_size == input_size) ?  Z_FINISH : Z_NO_FLUSH;
+        while (1) {
+            int deflate_size = zlib->strm.avail_out;
+            z_ret = deflate(&zlib->strm, flush);
+            ASSERT(z_ret != Z_STREAM_ERROR);
+            out_size += deflate_size - zlib->strm.avail_out;
+            if (zlib->strm.avail_out) {
+                break;
+            }
+
+            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
+            if (zlib->strm.avail_out == 0) {
+                red_error("not enough space");
+            }
+        }
+    } while (flush != Z_FINISH);
+
+    ASSERT(z_ret == Z_STREAM_END);
+    return out_size;
+}
diff --git a/server/zlib_encoder.h b/server/zlib_encoder.h
new file mode 100644
index 0000000..0620fc7
--- /dev/null
+++ b/server/zlib_encoder.h
@@ -0,0 +1,47 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in
+         the documentation and/or other materials provided with the
+         distribution.
+       * Neither the name of the copyright holder nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _H_ZLIB_ENCODER
+#define _H_ZLIB_ENCODER
+
+typedef struct ZlibEncoder ZlibEncoder;
+typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext;
+
+struct ZlibEncoderUsrContext {
+    int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr);
+    int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input);
+};
+
+ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level);
+void zlib_encoder_destroy(ZlibEncoder *encoder);
+
+/* returns the total size of the encoded data */
+int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
+                uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
diff --git a/spice.proto b/spice.proto
index 6cedfac..84d3fa9 100644
--- a/spice.proto
+++ b/spice.proto
@@ -293,6 +293,7 @@ enum8 image_type {
     SURFACE,
     JPEG,
     FROM_CACHE_LOSSLESS,
+    ZLIB_GLZ_RGB,
 };
 
 flags8 image_flags {
@@ -470,6 +471,12 @@ struct LZPLTData {
     uint8 data[data_size] @end @nomarshal;
 };
 
+struct ZlibGlzRGBData {
+    uint32 glz_data_size;
+    uint32 data_size;
+    uint8 data[data_size] @end @nomarshal;
+} @ctype(SpiceZlibGlzRGBData);
+
 struct Surface {
     uint32 surface_id;
 };
@@ -491,6 +498,8 @@ struct Image {
         BinaryData binary_data @ctype(SpiceQUICData);
     case LZ_PLT:
         LZPLTData lzplt_data @ctype(SpiceLZPLTData);
+    case ZLIB_GLZ_RGB:
+        ZlibGlzRGBData zlib_glz_data @ctype(SpiceZlibGlzRGBData);
     case SURFACE:
         Surface surface_data;
     } u @end;


More information about the Spice-commits mailing list