[Spice-commits] 8 commits - common/canvas_base.c common/canvas_base.h common/gl_canvas.c common/gl_canvas.h common/marshaller.c common/marshaller.h common/mem.c common/mem.h common/sw_canvas.c common/sw_canvas.h python_modules/demarshal.py python_modules/ptypes.py python_modules/spice_parser.py server/red_parse_qxl.c server/red_parse_qxl.h server/red_worker.c server/spice.h spice1.proto spice.proto

Alexander Larsson alexl at kemper.freedesktop.org
Thu Jul 8 03:05:15 PDT 2010


 common/canvas_base.c           |  267 +++-----
 common/canvas_base.h           |   11 
 common/gl_canvas.c             |    6 
 common/gl_canvas.h             |    3 
 common/marshaller.c            |   10 
 common/marshaller.h            |    2 
 common/mem.c                   |   59 +
 common/mem.h                   |    5 
 common/sw_canvas.c             |   18 
 common/sw_canvas.h             |    6 
 python_modules/demarshal.py    |   52 +
 python_modules/ptypes.py       |   74 +-
 python_modules/spice_parser.py |    2 
 server/red_parse_qxl.c         |  278 ++++++++-
 server/red_parse_qxl.h         |    1 
 server/red_worker.c            | 1246 +++++++++++------------------------------
 server/spice.h                 |    1 
 spice.proto                    |   81 +-
 spice1.proto                   |   69 +-
 19 files changed, 969 insertions(+), 1222 deletions(-)

New commits:
commit fe4f90210f492b8818af33dd1824c09295631c73
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Jul 8 10:40:23 2010 +0200

    remove QXLInterface->has_command()
    
    Not used any more, zap it before rolling up a release tarball.

diff --git a/server/spice.h b/server/spice.h
index 07bbeef..99546cf 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -172,7 +172,6 @@ struct QXLInterface {
     void (*get_init_info)(QXLInstance *qin, QXLDevInitInfo *info);
     int (*get_command)(QXLInstance *qin, struct QXLCommandExt *cmd);
     int (*req_cmd_notification)(QXLInstance *qin);
-    int (*has_command)(QXLInstance *qin);
     void (*release_resource)(QXLInstance *qin, struct QXLReleaseInfoExt release_info);
     int (*get_cursor_command)(QXLInstance *qin, struct QXLCommandExt *cmd);
     int (*req_cursor_notification)(QXLInstance *qin);
commit b54037a1f4d17593e3ee89027ac71a59baf907e1
Author: Uri Lublin <uril at redhat.com>
Date:   Tue Jul 6 14:01:20 2010 +0300

    server: add missing ifdef USE_OGL (red_worker.c)

diff --git a/server/red_worker.c b/server/red_worker.c
index abe4e7b..8c36c13 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -37,8 +37,10 @@
 #include <spice/protocol.h>
 #include "red_worker.h"
 #include "sw_canvas.h"
+#ifdef USE_OGL
 #include "gl_canvas.h"
 #include "ogl_ctx.h"
+#endif /* USE_OGL */
 #include "quic.h"
 #include "lz.h"
 #include "glz_encoder_dictionary.h"
commit 5ac88aa79fa6445f96e5419d8bf4fce81da63b90
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Jul 1 17:55:33 2010 +0200

    Properly parse QXLImage to the new-world SpiceImage
    
    SpiceImage now replaces RedImage and has all image types in it.
    All image data are now chunked (and as such not copied when demarshalling).

diff --git a/common/canvas_base.c b/common/canvas_base.c
index a0429a6..a1378a0 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -69,18 +69,6 @@
     (((descriptor)->type == SPICE_IMAGE_TYPE_JPEG) ||      \
     ((descriptor)->type == SPICE_IMAGE_TYPE_JPEG_ALPHA))
 
-#ifdef WIN32
-typedef struct  __declspec (align(1)) LZImage {
-#else
-typedef struct __attribute__ ((__packed__)) LZImage {
-#endif
-    SpiceImageDescriptor descriptor;
-    union {
-        SpiceLZRGBData lz_rgb;
-        SpiceLZPLTData lz_plt;
-    };
-} LZImage;
-
  static inline int fix_to_int(SPICE_FIXED28_4 fixed)
 {
     int val, rem;
@@ -157,11 +145,9 @@ typedef struct QuicData {
     QuicUsrContext usr;
     QuicContext *quic;
     jmp_buf jmp_env;
-#ifndef SW_CANVAS_NO_CHUNKS
-    SPICE_ADDRESS next;
-    SpiceVirtMapping *virt_mapping;
-#endif
     char message_buf[512];
+    SpiceChunks *chunks;
+    int current_chunk;
 } QuicData;
 
 typedef struct CanvasBase {
@@ -196,32 +182,6 @@ typedef struct CanvasBase {
     spice_destroy_fn_t usr_data_destroy;
 } CanvasBase;
 
-
-#ifndef SW_CANVAS_NO_CHUNKS
-
-#ifdef __GNUC__
-#define ATTR_PACKED __attribute__ ((__packed__))
-#else
-#pragma pack(push)
-#pragma pack(1)
-#define ATTR_PACKED
-#endif
-
-typedef struct ATTR_PACKED DataChunk {
-    uint32_t size;
-    SPICE_ADDRESS prev;
-    SPICE_ADDRESS next;
-    uint8_t data[0];
-} DataChunk;
-
-#undef ATTR_PACKED
-
-#ifndef __GNUC__
-#pragma pack(pop)
-#endif
-
-#endif
-
 typedef enum {
     ROP_INPUT_SRC,
     ROP_INPUT_BRUSH,
@@ -434,7 +394,7 @@ static pixman_format_code_t canvas_get_target_format(CanvasBase *canvas,
     return format;
 }
 
-static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *image,
+static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceImage *image,
                                        int invers, int want_original)
 {
     pixman_image_t *surface = NULL;
@@ -445,30 +405,21 @@ static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *image
     int stride;
     int width;
     int height;
-#ifndef SW_CANVAS_NO_CHUNKS
-    DataChunk **tmp;
-    DataChunk *chunk;
-#endif
 
     if (setjmp(quic_data->jmp_env)) {
         pixman_image_unref(surface);
         CANVAS_ERROR("quic error, %s", quic_data->message_buf);
     }
 
-#ifdef SW_CANVAS_NO_CHUNKS
-    if (quic_decode_begin(quic_data->quic, (uint32_t *)image->quic.data,
-                          image->quic.data_size >> 2, &type, &width, &height) == QUIC_ERROR) {
-        CANVAS_ERROR("quic decode begin failed");
-    }
-#else
-    tmp = (DataChunk **)image->quic.data;
-    chunk = *tmp;
-    quic_data->next = chunk->next;
-    if (quic_decode_begin(quic_data->quic, (uint32_t *)chunk->data, chunk->size >> 2,
+    quic_data->chunks = image->u.quic.data;
+    quic_data->current_chunk = 0;
+
+    if (quic_decode_begin(quic_data->quic,
+                          (uint32_t *)image->u.quic.data->chunk[0].data,
+                          image->u.quic.data->chunk[0].len >> 2,
                           &type, &width, &height) == QUIC_ERROR) {
         CANVAS_ERROR("quic decode begin failed");
     }
-#endif
 
    switch (type) {
     case QUIC_IMAGE_TYPE_RGBA:
@@ -564,7 +515,7 @@ static void dump_jpeg(uint8_t* data, int data_size)
 }
 #endif
 
-static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceJPEGImage *image, int invers)
+static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceImage *image, int invers)
 {
     pixman_image_t *surface = NULL;
     int stride;
@@ -572,7 +523,8 @@ static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceJPEGImage *image
     int height;
     uint8_t *dest;
 
-    canvas->jpeg->ops->begin_decode(canvas->jpeg, image->jpeg.data, image->jpeg.data_size,
+    ASSERT(image->u.jpeg.data->num_chunks == 1); /* TODO: Handle chunks */
+    canvas->jpeg->ops->begin_decode(canvas->jpeg, image->u.jpeg.data->chunk[0].data, image->u.jpeg.data->chunk[0].len,
                                     &width, &height);
     ASSERT((uint32_t)width == image->descriptor.width);
     ASSERT((uint32_t)height == image->descriptor.height);
@@ -606,13 +558,13 @@ static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceJPEGImage *image
         }
     }
 #ifdef DUMP_JPEG
-    dump_jpeg(image->jpeg.data, image->jpeg.data_size);
+    dump_jpeg(image->u.jpeg.data, image->u.jpeg.data_size);
 #endif
     return surface;
 }
 
 static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
-                                             SpiceJPEGAlphaImage *image, int invers)
+                                             SpiceImage *image, int invers)
 {
     pixman_image_t *surface = NULL;
     int stride;
@@ -627,13 +579,15 @@ static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
     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,
+    ASSERT(image->u.jpeg_alpha.data->num_chunks == 1);
+    canvas->jpeg->ops->begin_decode(canvas->jpeg,
+                                    image->u.jpeg_alpha.data->chunk[0].data,
+                                    image->u.jpeg_alpha.data->chunk[0].len,
                                     &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) {
+    if (image->u.jpeg_alpha.flags & SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN) {
         alpha_top_down = TRUE;
     }
 
@@ -642,7 +596,7 @@ static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
 #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");
     }
@@ -651,9 +605,9 @@ static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
     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;
+
+    comp_alpha_buf = image->u.jpeg_alpha.data->chunk[0].data + image->u.jpeg_alpha.jpeg_size;
+    alpha_size = image->u.jpeg_alpha.data_size - image->u.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,
@@ -685,7 +639,7 @@ static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
         }
     }
 #ifdef DUMP_JPEG
-    dump_jpeg(image->jpeg_alpha.data, image->jpeg_alpha.jpeg_size);
+    dump_jpeg(image->u.jpeg_alpha.data, image->u.jpeg_alpha.jpeg_size);
 #endif
     return surface;
 }
@@ -698,7 +652,9 @@ static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap*
     pixman_image_t *image;
     pixman_format_code_t format;
 
-    src = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
+    spice_chunks_linearize(bitmap->data);
+
+    src = bitmap->data->chunk[0].data;
     src_stride = bitmap->stride;
 
     if (want_original) {
@@ -730,27 +686,24 @@ static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap*
 
 #ifdef SW_CANVAS_CACHE
 
-static inline SpicePalette *canvas_get_palette(CanvasBase *canvas, SPICE_ADDRESS base_palette, uint8_t flags)
+static inline SpicePalette *canvas_get_palette(CanvasBase *canvas, SpicePalette *base_palette, uint64_t palette_id, uint8_t flags)
 {
     SpicePalette *palette;
-    if (!base_palette) {
-        return NULL;
-    }
 
     if (flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE) {
-        palette = canvas->palette_cache->ops->get(canvas->palette_cache, base_palette);
-    } else if (flags & SPICE_BITMAP_FLAGS_PAL_CACHE_ME) {
-        palette = (SpicePalette *)SPICE_GET_ADDRESS(base_palette);
-        canvas->palette_cache->ops->put(canvas->palette_cache, palette);
+        palette = canvas->palette_cache->ops->get(canvas->palette_cache, palette_id);
     } else {
-        palette = (SpicePalette *)SPICE_GET_ADDRESS(base_palette);
+        palette = base_palette;
+        if (palette != NULL && flags & SPICE_BITMAP_FLAGS_PAL_CACHE_ME) {
+            canvas->palette_cache->ops->put(canvas->palette_cache, palette);
+        }
     }
     return palette;
 }
 
-static inline SpicePalette *canvas_get_localized_palette(CanvasBase *canvas, SPICE_ADDRESS base_palette, uint8_t flags, int *free_palette)
+static inline SpicePalette *canvas_get_localized_palette(CanvasBase *canvas, SpicePalette *base_palette, uint64_t palette_id, uint8_t flags, int *free_palette)
 {
-    SpicePalette *palette = canvas_get_palette(canvas, base_palette, flags);
+    SpicePalette *palette = canvas_get_palette(canvas, base_palette, palette_id, flags);
     SpicePalette *copy;
     uint32_t *now, *end;
     size_t size;
@@ -784,7 +737,7 @@ static inline SpicePalette *canvas_get_localized_palette(CanvasBase *canvas, SPI
     return copy;
 }
 
-static pixman_image_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int invers,
+static pixman_image_t *canvas_get_lz(CanvasBase *canvas, SpiceImage *image, int invers,
                                      int want_original)
 {
     LzData *lz_data = &canvas->lz_data;
@@ -811,13 +764,15 @@ static pixman_image_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int inv
 
     free_palette = FALSE;
     if (image->descriptor.type == SPICE_IMAGE_TYPE_LZ_RGB) {
-        comp_buf = image->lz_rgb.data;
-        comp_size = image->lz_rgb.data_size;
+        ASSERT(image->u.lz_rgb.data->num_chunks == 1); /* TODO: Handle chunks */
+        comp_buf = image->u.lz_rgb.data->chunk[0].data;
+        comp_size = image->u.lz_rgb.data->chunk[0].len;
         palette = NULL;
     } else if (image->descriptor.type == SPICE_IMAGE_TYPE_LZ_PLT) {
-        comp_buf = image->lz_plt.data;
-        comp_size = image->lz_plt.data_size;
-        palette = canvas_get_localized_palette(canvas, image->lz_plt.palette, image->lz_plt.flags, &free_palette);
+        ASSERT(image->u.lz_plt.data->num_chunks == 1); /* TODO: Handle chunks */
+        comp_buf = image->u.lz_plt.data->chunk[0].data;
+        comp_size = image->u.lz_plt.data->chunk[0].len;
+        palette = canvas_get_localized_palette(canvas, image->u.lz_plt.palette, image->u.lz_plt.palette_id, image->u.lz_plt.flags, &free_palette);
     } else {
         CANVAS_ERROR("unexpected image type");
     }
@@ -918,7 +873,7 @@ static pixman_image_t *canvas_get_glz_rgb_common(CanvasBase *canvas, uint8_t *da
 
 // 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,
+static pixman_image_t *canvas_get_glz(CanvasBase *canvas, SpiceImage *image,
                                       int want_original)
 {
     ASSERT(image->descriptor.type == SPICE_IMAGE_TYPE_GLZ_RGB);
@@ -926,10 +881,11 @@ static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image,
     canvas->glz_data.decode_data.dc = canvas->dc;
 #endif
 
-    return canvas_get_glz_rgb_common(canvas, image->lz_rgb.data, want_original);
+    ASSERT(image->u.lz_rgb.data->num_chunks == 1); /* TODO: Handle chunks */
+    return canvas_get_glz_rgb_common(canvas, image->u.lz_rgb.data->chunk[0].data, want_original);
 }
 
-static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceZlibGlzRGBImage *image,
+static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceImage *image,
                                                int want_original)
 {
     uint8_t *glz_data;
@@ -939,9 +895,11 @@ static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceZlibGlzR
         CANVAS_ERROR("zlib not supported");
     }
 
-    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);
+    ASSERT(image->u.zlib_glz.data->num_chunks == 1); /* TODO: Handle chunks */
+    glz_data = (uint8_t*)spice_malloc(image->u.zlib_glz.glz_data_size);
+    canvas->zlib->ops->decode(canvas->zlib, image->u.zlib_glz.data->chunk[0].data,
+                              image->u.zlib_glz.data->chunk[0].len,
+                              glz_data, image->u.zlib_glz.glz_data_size);
     surface = canvas_get_glz_rgb_common(canvas, glz_data, want_original);
     free(glz_data);
     return surface;
@@ -993,7 +951,7 @@ static pixman_image_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap,
     pixman_image_t* surface;
     SpicePalette *palette;
 
-    palette = canvas_get_palette(canvas, bitmap->palette, bitmap->flags);
+    palette = canvas_get_palette(canvas, bitmap->palette, bitmap->palette_id, bitmap->flags);
 #ifdef DEBUG_DUMP_BITMAP
     if (palette) {
         dump_bitmap(bitmap, palette);
@@ -1079,26 +1037,20 @@ static void dump_surface(pixman_image_t *surface, int cache)
 
 #endif
 
-static SpiceCanvas *canvas_get_surface_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+static SpiceCanvas *canvas_get_surface_internal(CanvasBase *canvas, SpiceImage *image)
 {
-    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-
-    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
-        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
-        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurface *surface = &image->u.surface;
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface_id);
     }
     return NULL;
 }
 
-static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SpiceImage *image)
 {
-    SpiceImageDescriptor *descriptor;
-
-    descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-
-    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
-        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
-        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurface *surface = &image->u.surface;
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface_id);
     }
     return NULL;
 }
@@ -1115,10 +1067,10 @@ static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SPICE_A
  * you have to be able to handle any image format. This is useful to avoid
  * e.g. losing alpha when blending a argb32 image on a rgb16 surface.
  */
-static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRESS addr,
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage *image,
                                                  int want_original, int real_get)
 {
-    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    SpiceImageDescriptor *descriptor = &image->descriptor;
     pixman_image_t *surface, *converted;
     pixman_format_code_t wanted_format, surface_format;
     int saved_want_original;
@@ -1150,40 +1102,33 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
     switch (descriptor->type) {
     case SPICE_IMAGE_TYPE_QUIC: {
-        SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
         surface = canvas_get_quic(canvas, image, 0, want_original);
         break;
     }
-#ifdef SW_CANVAS_NO_CHUNKS
+#if defined(SW_CANVAS_CACHE)
     case SPICE_IMAGE_TYPE_LZ_PLT: {
-        LZImage *image = (LZImage *)descriptor;
         surface = canvas_get_lz(canvas, image, 0, want_original);
         break;
     }
     case SPICE_IMAGE_TYPE_LZ_RGB: {
-        LZImage *image = (LZImage *)descriptor;
         surface = canvas_get_lz(canvas, image, 0, want_original);
         break;
     }
 #endif
     case SPICE_IMAGE_TYPE_JPEG: {
-        SpiceJPEGImage *image = (SpiceJPEGImage *)descriptor;
         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;
         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;
     }
@@ -1197,8 +1142,7 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
         break;
 #endif
     case SPICE_IMAGE_TYPE_BITMAP: {
-        SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
-        surface = canvas_get_bits(canvas, &bitmap->bitmap, want_original);
+        surface = canvas_get_bits(canvas, &image->u.bitmap, want_original);
         break;
     }
     default:
@@ -1298,10 +1242,10 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
 #else
 
-static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRESS addr,
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage *image,
                                                  int want_original, int real_get)
 {
-    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    SpiceImageDescriptor *descriptor = &image->descriptor;
     pixman_format_code_t format;
 
     /* When touching, never load image. */
@@ -1311,12 +1255,10 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
     switch (descriptor->type) {
     case SPICE_IMAGE_TYPE_QUIC: {
-        SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
         return canvas_get_quic(canvas, image, 0);
     }
     case SPICE_IMAGE_TYPE_BITMAP: {
-        SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
-        return canvas_get_bits(canvas, &bitmap->bitmap, want_original, &format);
+        return canvas_get_bits(canvas, &image->u.bitmap, want_original, &format);
     }
     default:
         CANVAS_ERROR("invalid image type");
@@ -1325,25 +1267,25 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
 #endif
 
-static SpiceCanvas *canvas_get_surface_mask(CanvasBase *canvas, SPICE_ADDRESS addr)
+static SpiceCanvas *canvas_get_surface_mask(CanvasBase *canvas, SpiceImage *image)
 {
-    return canvas_get_surface_mask_internal(canvas, addr);
+    return canvas_get_surface_mask_internal(canvas, image);
 }
 
-static SpiceCanvas *canvas_get_surface(CanvasBase *canvas, SPICE_ADDRESS addr)
+static SpiceCanvas *canvas_get_surface(CanvasBase *canvas, SpiceImage *image)
 {
-    return canvas_get_surface_internal(canvas, addr);
+    return canvas_get_surface_internal(canvas, image);
 }
 
-static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr,
+static pixman_image_t *canvas_get_image(CanvasBase *canvas, SpiceImage *image,
                                         int want_original)
 {
-    return canvas_get_image_internal(canvas, addr, want_original, TRUE);
+    return canvas_get_image_internal(canvas, image, want_original, TRUE);
 }
 
-static void canvas_touch_image(CanvasBase *canvas, SPICE_ADDRESS addr)
+static void canvas_touch_image(CanvasBase *canvas, SpiceImage *image)
 {
-    canvas_get_image_internal(canvas, addr, TRUE, FALSE);
+    canvas_get_image_internal(canvas, image, TRUE, FALSE);
 }
 
 static pixman_image_t* canvas_get_image_from_self(SpiceCanvas *canvas,
@@ -1407,7 +1349,8 @@ static pixman_image_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap* b
         CANVAS_ERROR("create surface failed");
     }
 
-    src_line = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
+    spice_chunks_linearize(bitmap->data);
+    src_line = bitmap->data->chunk[0].data;
     src_stride = bitmap->stride;
     end_line = src_line + (bitmap->y * src_stride);
     line_size = SPICE_ALIGN(bitmap->x, 8) >> 3;
@@ -1530,7 +1473,7 @@ static inline pixman_image_t *canvas_A1_invers(pixman_image_t *src_surf)
 
 static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int *needs_invert_out)
 {
-    SpiceImageDescriptor *descriptor;
+    SpiceImage *image;
     pixman_image_t *surface;
     int need_invers;
     int is_invers;
@@ -1540,31 +1483,30 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int
         *needs_invert_out = 0;
     }
 
-    descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(mask->bitmap);
+    image = mask->bitmap;
     need_invers = mask->flags & SPICE_MASK_FLAGS_INVERS;
 
 #ifdef SW_CANVAS_CACHE
-    cache_me = descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME;
+    cache_me = image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME;
 #else
     cache_me = 0;
 #endif
 
-    switch (descriptor->type) {
+    switch (image->descriptor.type) {
     case SPICE_IMAGE_TYPE_BITMAP: {
-        SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
         is_invers = need_invers && !cache_me;
-        surface = canvas_get_bitmap_mask(canvas, &bitmap->bitmap, is_invers);
+        surface = canvas_get_bitmap_mask(canvas, &image->u.bitmap, is_invers);
         break;
     }
 #if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
     case SPICE_IMAGE_TYPE_FROM_CACHE:
-        surface = canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
+        surface = canvas->bits_cache->ops->get(canvas->bits_cache, image->descriptor.id);
         is_invers = 0;
         break;
 #endif
 #ifdef SW_CANVAS_CACHE
     case SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS:
-        surface = canvas->bits_cache->ops->get_lossless(canvas->bits_cache, descriptor->id);
+        surface = canvas->bits_cache->ops->get_lossless(canvas->bits_cache, image->descriptor.id);
         is_invers = 0;
         break;
 #endif
@@ -1574,7 +1516,7 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int
 
 #if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
     if (cache_me) {
-        canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
+        canvas->bits_cache->ops->put(canvas->bits_cache, image->descriptor.id, surface);
     }
 
     if (need_invers && !is_invers) { // surface is in cache
@@ -1847,13 +1789,6 @@ static void quic_usr_free(QuicUsrContext *usr, void *ptr)
     free(ptr);
 }
 
-#ifdef SW_CANVAS_NO_CHUNKS
-
-static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
-{
-    return 0;
-}
-
 static void lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
 {
     LzData *usr_data = (LzData *)usr;
@@ -1896,29 +1831,19 @@ static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
     return 0;
 }
 
-#else
-
 static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
 {
     QuicData *quic_data = (QuicData *)usr;
-    DataChunk *chunk;
-    uint32_t size;
 
-    if (!quic_data->next) {
+    if (quic_data->current_chunk == quic_data->chunks->num_chunks -1) {
         return 0;
     }
-    chunk = (DataChunk *)quic_data->virt_mapping->ops->get_virt(quic_data->virt_mapping, quic_data->next,
-                                                                sizeof(DataChunk));
-    size = chunk->size;
-    quic_data->virt_mapping->ops->validate_virt(quic_data->virt_mapping, (unsigned long)chunk->data,
-                                                quic_data->next, size);
+    quic_data->current_chunk++;
 
-    quic_data->next = chunk->next;
-    *io_ptr = (uint32_t *)chunk->data;
-    return size >> 2;
+    *io_ptr = (uint32_t *)quic_data->chunks->chunk[quic_data->current_chunk].data;
+    return quic_data->chunks->chunk[quic_data->current_chunk].len >> 2;
 }
 
-#endif
 
 static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
 {
@@ -1928,9 +1853,7 @@ static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
 static void canvas_base_destroy(CanvasBase *canvas)
 {
     quic_destroy(canvas->quic_data.quic);
-#ifdef SW_CANVAS_NO_CHUNKS
     lz_destroy(canvas->lz_data.lz);
-#endif
 #ifdef GDI_CANVAS
     DeleteDC(canvas->dc);
 #endif
@@ -3398,9 +3321,6 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
                             , SpiceGlzDecoder *glz_decoder
                             , SpiceJpegDecoder *jpeg_decoder
                             , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                            , SpiceVirtMapping *virt_mapping
-#endif
                             )
 {
     canvas->parent.ops = ops;
@@ -3411,13 +3331,10 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
     canvas->quic_data.usr.free = quic_usr_free;
     canvas->quic_data.usr.more_space = quic_usr_more_space;
     canvas->quic_data.usr.more_lines = quic_usr_more_lines;
-#ifndef SW_CANVAS_NO_CHUNKS
-    canvas->quic_data.virt_mapping = virt_mapping;
-#endif
     if (!(canvas->quic_data.quic = quic_create(&canvas->quic_data.usr))) {
             return 0;
     }
-#ifdef SW_CANVAS_NO_CHUNKS
+
     canvas->lz_data.usr.error = lz_usr_error;
     canvas->lz_data.usr.warn = lz_usr_warn;
     canvas->lz_data.usr.info = lz_usr_warn;
@@ -3428,7 +3345,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
     if (!(canvas->lz_data.lz = lz_create(&canvas->lz_data.usr))) {
             return 0;
     }
-#endif
+
     canvas->surfaces = surfaces;
     canvas->glz_data.decoder = glz_decoder;
     canvas->jpeg = jpeg_decoder;
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 9dfbe85..36199a7 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -33,7 +33,6 @@ typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 typedef struct _SpiceJpegDecoder SpiceJpegDecoder;
 typedef struct _SpiceZlibDecoder SpiceZlibDecoder;
-typedef struct _SpiceVirtMapping SpiceVirtMapping;
 typedef struct _SpiceCanvas SpiceCanvas;
 
 typedef struct {
@@ -121,16 +120,6 @@ struct _SpiceZlibDecoder {
 };
 
 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);
-} SpiceVirtMappingOps;
-
-struct _SpiceVirtMapping {
-  SpiceVirtMappingOps *ops;
-};
-
-typedef struct {
     void (*draw_fill)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
     void (*draw_copy)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
     void (*draw_opaque)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index f0e10dd..a5c8511 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -806,9 +806,6 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
                               , SpiceGlzDecoder *glz_decoder
                               , SpiceJpegDecoder *jpeg_decoder
                               , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                              , SpiceVirtMapping *virt_mapping
-#endif
                            )
 {
     GLCanvas *canvas;
@@ -835,9 +832,6 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
                                , glz_decoder
                                , jpeg_decoder
                                , zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                               , virt_mapping
-#endif
                                );
     if (!init_ok) {
         goto error_2;
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index d3f707a..dfb59bb 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -32,9 +32,6 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                            , SpiceVirtMapping *virt_mapping
-#endif
                            );
 void gl_canvas_set_textures_lost(SpiceCanvas *canvas, int textures_lost);
 void gl_canvas_init();
diff --git a/common/sw_canvas.c b/common/sw_canvas.c
index 56865c3..78cc1e4 100644
--- a/common/sw_canvas.c
+++ b/common/sw_canvas.c
@@ -1173,9 +1173,6 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
-#endif
                            )
 {
     SwCanvas *canvas;
@@ -1202,9 +1199,6 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
                                , glz_decoder
                                , jpeg_decoder
                                , zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                               , virt_mapping
-#endif
                                );
     canvas->private_data = NULL;
     canvas->private_data_size = 0;
@@ -1225,9 +1219,6 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
-#endif
                            )
 {
     pixman_image_t *image;
@@ -1246,9 +1237,6 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
                                 , glz_decoder
                                 , jpeg_decoder
                                 , zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                                , virt_mapping
-#endif
                                 );
 }
 
@@ -1264,9 +1252,6 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
-#endif
                            )
 {
     pixman_image_t *image;
@@ -1285,9 +1270,6 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
                                 , 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 05e4071..d4573bb 100644
--- a/common/sw_canvas.h
+++ b/common/sw_canvas.h
@@ -37,9 +37,6 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
-#endif
                            );
 
 SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint8_t *data, size_t stride
@@ -53,9 +50,6 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint
                            , SpiceGlzDecoder *glz_decoder
                            , SpiceJpegDecoder *jpeg_decoder
                            , SpiceZlibDecoder *zlib_decoder
-#ifndef SW_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
-#endif
                            );
 
 
diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
index 323fc70..1ae1911 100644
--- a/python_modules/demarshal.py
+++ b/python_modules/demarshal.py
@@ -93,7 +93,7 @@ def write_read_primitive(writer, start, container, name, scope):
     writer.assign("pos", start + " + " + container.get_nw_offset(m, "", "__nw_size"))
     writer.error_check("pos + %s > message_end" % m.member_type.get_fixed_nw_size())
 
-    var = "%s__value" % (name)
+    var = "%s__value" % (name.replace(".", "_"))
     if not scope.variable_defined(var):
         scope.variable_def(m.member_type.c_type(), var)
     writer.assign(var, "read_%s(pos)" % (m.member_type.primitive_type()))
@@ -112,7 +112,7 @@ def write_read_primitive_item(writer, item, scope):
     assert(item.type.is_primitive())
     writer.assign("pos", item.get_position())
     writer.error_check("pos + %s > message_end" % item.type.get_fixed_nw_size())
-    var = "%s__value" % (item.subprefix)
+    var = "%s__value" % (item.subprefix.replace(".", "_"))
     scope.variable_def(item.type.c_type(), var)
     writer.assign(var, "read_%s(pos)" % (item.type.primitive_type()))
     return var
diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
index 68cf3df..715544f 100644
--- a/python_modules/ptypes.py
+++ b/python_modules/ptypes.py
@@ -811,14 +811,31 @@ class ContainerType(Type):
             return str(fixed)
 
     def lookup_member(self, name):
+        dot = name.find('.')
+        rest = None
+        if dot >= 0:
+            rest = name[dot+1:]
+            name = name[:dot]
+
+        member = None
         if self.members_by_name.has_key(name):
-            return self.members_by_name[name]
-        for m in self.members:
-            if m.is_switch():
-                member = m.lookup_case_member(name)
-                if member:
-                    return member
-        raise Exception, "No member called %s found" % name
+            member = self.members_by_name[name]
+        else:
+            for m in self.members:
+                if m.is_switch():
+                    member = m.lookup_case_member(name)
+                    if member != None:
+                        break
+                if member != None:
+                    break
+
+        if member == None:
+            raise Exception, "No member called %s found" % name
+
+        if rest != None:
+            return member.member_type.lookup_member(rest)
+
+        return member
 
 class StructType(ContainerType):
     def __init__(self, name, members, attribute_list):
diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
index 61ef458..ac2da8d 100644
--- a/python_modules/spice_parser.py
+++ b/python_modules/spice_parser.py
@@ -95,7 +95,7 @@ def SPICE_BNF():
 
         switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | case_.suppress() + identifier + colon)) + variableDef) \
             .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
-        switchBody = Group(switch_ + lparen + identifier + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
+        switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
             .setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
         messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody)  + rbrace)
         structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
diff --git a/server/red_parse_qxl.c b/server/red_parse_qxl.c
index 56f17dc..ee099a0 100644
--- a/server/red_parse_qxl.c
+++ b/server/red_parse_qxl.c
@@ -17,6 +17,7 @@
 */
 
 #include <stdbool.h>
+#include <inttypes.h>
 #include "red_common.h"
 #include "red_memslots.h"
 #include "red_parse_qxl.h"
@@ -99,7 +100,6 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
     return data_size;
 }
 
-#if 0
 static size_t red_get_data_chunks(RedMemSlotInfo *slots, int group_id,
                                   RedDataChunk *red, SPICE_ADDRESS addr)
 {
@@ -109,7 +109,6 @@ static size_t red_get_data_chunks(RedMemSlotInfo *slots, int group_id,
     qxl = (QXLDataChunk*)get_virt(slots, addr, sizeof(*qxl), group_id);
     return red_get_data_chunks_ptr(slots, group_id, memslot_id, red, qxl);
 }
-#endif
 
 static void red_put_data_chunks(RedDataChunk *red)
 {
@@ -255,6 +254,151 @@ static SpiceClipRects *red_get_clip_rects(RedMemSlotInfo *slots, int group_id,
     return red;
 }
 
+static SpiceChunks *red_get_image_data_flat(RedMemSlotInfo *slots, int group_id,
+                                            SPICE_ADDRESS addr, size_t size)
+{
+    SpiceChunks *data;
+
+    data = spice_chunks_new(1);
+    data->data_size      = size;
+    data->chunk[0].data  = (void*)get_virt(slots, addr, size, group_id);
+    data->chunk[0].len   = size;
+    return data;
+}
+
+static SpiceChunks *red_get_image_data_chunked(RedMemSlotInfo *slots, int group_id,
+                                               RedDataChunk *head)
+{
+    SpiceChunks *data;
+    RedDataChunk *chunk;
+    int i;
+
+    for (i = 0, chunk = head; chunk != NULL; chunk = chunk->next_chunk) {
+        i++;
+    }
+
+    data = spice_chunks_new(i);
+    data->data_size = 0;
+    for (i = 0, chunk = head;
+         chunk != NULL && i < data->num_chunks;
+         chunk = chunk->next_chunk, i++) {
+        data->chunk[i].data  = chunk->data;
+        data->chunk[i].len   = chunk->data_size;
+        data->data_size     += chunk->data_size;
+    }
+    ASSERT(i == data->num_chunks);
+    return data;
+}
+
+static SpiceImage *red_get_image(RedMemSlotInfo *slots, int group_id,
+                                 SPICE_ADDRESS addr)
+{
+    RedDataChunk chunks;
+    QXLImage *qxl;
+    SpiceImage *red;
+    size_t bitmap_size, size;
+    uint8_t qxl_flags;
+
+    if (addr == 0) {
+        return NULL;
+    }
+
+    qxl = (QXLImage *)get_virt(slots, addr, sizeof(*qxl), group_id);
+    red = spice_new0(SpiceImage, 1);
+    red->descriptor.id     = qxl->descriptor.id;
+    red->descriptor.type   = qxl->descriptor.type;
+    red->descriptor.flags = 0;
+    if (qxl->descriptor.flags & QXL_IMAGE_HIGH_BITS_SET) {
+        red->descriptor.flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
+    }
+    if (qxl->descriptor.flags & QXL_IMAGE_CACHE) {
+        red->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+    }
+    red->descriptor.width  = qxl->descriptor.width;
+    red->descriptor.height = qxl->descriptor.height;
+
+    switch (red->descriptor.type) {
+    case SPICE_IMAGE_TYPE_BITMAP:
+        red->u.bitmap.format = qxl->bitmap.format;
+        qxl_flags = qxl->bitmap.flags;
+        if (qxl_flags & QXL_BITMAP_TOP_DOWN) {
+            red->u.bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN;
+        }
+        red->u.bitmap.x      = qxl->bitmap.x;
+        red->u.bitmap.y      = qxl->bitmap.y;
+        red->u.bitmap.stride = qxl->bitmap.stride;
+        if (qxl->bitmap.palette) {
+            QXLPalette *qp;
+            SpicePalette *rp;
+            int i, num_ents;
+            qp = (QXLPalette *)get_virt(slots, qxl->bitmap.palette,
+                                        sizeof(*qp), group_id);
+            num_ents = qp->num_ents;
+            validate_virt(slots, (intptr_t)qp->ents,
+                          get_memslot_id(slots, qxl->bitmap.palette),
+                          num_ents * sizeof(qp->ents[0]), group_id);
+            rp = spice_malloc_n_m(num_ents, sizeof(rp->ents[0]), sizeof(*rp));
+            rp->unique   = qp->unique;
+            rp->num_ents = num_ents;
+            for (i = 0; i < num_ents; i++) {
+                rp->ents[i] = qp->ents[i];
+            }
+            red->u.bitmap.palette = rp;
+            red->u.bitmap.palette_id = rp->unique;
+        }
+        bitmap_size = red->u.bitmap.y * abs(red->u.bitmap.stride);
+        if (qxl_flags & QXL_BITMAP_DIRECT) {
+            red->u.bitmap.data = red_get_image_data_flat(slots, group_id,
+                                                         qxl->bitmap.data,
+                                                         bitmap_size);
+        } else {
+            size = red_get_data_chunks(slots, group_id,
+                                       &chunks, qxl->bitmap.data);
+            ASSERT(size == bitmap_size);
+            red->u.bitmap.data = red_get_image_data_chunked(slots, group_id,
+                                                            &chunks);
+            red_put_data_chunks(&chunks);
+        }
+        if (qxl_flags & QXL_BITMAP_UNSTABLE) {
+            red->u.bitmap.data->flags |= SPICE_CHUNKS_FLAGS_UNSTABLE;
+        }
+        break;
+    case SPICE_IMAGE_TYPE_SURFACE:
+        red->u.surface.surface_id = qxl->surface_image.surface_id;
+        break;
+    case SPICE_IMAGE_TYPE_QUIC:
+        red->u.quic.data_size = qxl->quic.data_size;
+        size = red_get_data_chunks_ptr(slots, group_id,
+                                       get_memslot_id(slots, addr),
+                                       &chunks, (QXLDataChunk *)qxl->quic.data);
+        ASSERT(size == red->u.quic.data_size);
+        red->u.quic.data = red_get_image_data_chunked(slots, group_id,
+                                                      &chunks);
+        red_put_data_chunks(&chunks);
+        break;
+    default:
+        red_error("%s: unknown type %d", __FUNCTION__, red->descriptor.type);
+        abort();
+    }
+    return red;
+}
+
+void red_put_image(SpiceImage *red)
+{
+    if (red == NULL)
+        return;
+
+    switch (red->descriptor.type) {
+    case SPICE_IMAGE_TYPE_BITMAP:
+        if (red->u.bitmap.palette) {
+            free(red->u.bitmap.palette);
+        }
+        spice_chunks_destroy(red->u.bitmap.data);
+        break;
+    }
+    free(red);
+}
+
 static void red_get_brush_ptr(RedMemSlotInfo *slots, int group_id,
                               SpiceBrush *red, QXLBrush *qxl)
 {
@@ -264,18 +408,32 @@ static void red_get_brush_ptr(RedMemSlotInfo *slots, int group_id,
         red->u.color = qxl->u.color;
         break;
     case SPICE_BRUSH_TYPE_PATTERN:
-        red->u.pattern.pat = qxl->u.pattern.pat;
+        red->u.pattern.pat = red_get_image(slots, group_id, qxl->u.pattern.pat);
         red_get_point_ptr(&red->u.pattern.pos, &qxl->u.pattern.pos);
         break;
     }
 }
 
+static void red_put_brush(SpiceBrush *red)
+{
+    switch (red->type) {
+    case SPICE_BRUSH_TYPE_PATTERN:
+        red_put_image(red->u.pattern.pat);
+        break;
+    }
+}
+
 static void red_get_qmask_ptr(RedMemSlotInfo *slots, int group_id,
                               SpiceQMask *red, QXLQMask *qxl)
 {
     red->flags  = qxl->flags;
     red_get_point_ptr(&red->pos, &qxl->pos);
-    red->bitmap = qxl->bitmap;
+    red->bitmap = red_get_image(slots, group_id, qxl->bitmap);
+}
+
+static void red_put_qmask(SpiceQMask *red)
+{
+    red_put_image(red->bitmap);
 }
 
 static void red_get_fill_ptr(RedMemSlotInfo *slots, int group_id,
@@ -286,10 +444,16 @@ static void red_get_fill_ptr(RedMemSlotInfo *slots, int group_id,
     red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_fill(SpiceFill *red)
+{
+    red_put_brush(&red->brush);
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_opaque_ptr(RedMemSlotInfo *slots, int group_id,
                                SpiceOpaque *red, QXLOpaque *qxl)
 {
-   red->src_bitmap     = qxl->src_bitmap;
+   red->src_bitmap     = red_get_image(slots, group_id, qxl->src_bitmap);
    red_get_rect_ptr(&red->src_area, &qxl->src_area);
    red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush);
    red->rop_descriptor = qxl->rop_descriptor;
@@ -297,41 +461,65 @@ static void red_get_opaque_ptr(RedMemSlotInfo *slots, int group_id,
    red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_opaque(SpiceOpaque *red)
+{
+    red_put_image(red->src_bitmap);
+    red_put_brush(&red->brush);
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_copy_ptr(RedMemSlotInfo *slots, int group_id,
                              SpiceCopy *red, QXLCopy *qxl)
 {
-   red->src_bitmap      = qxl->src_bitmap;
+   red->src_bitmap      = red_get_image(slots, group_id, qxl->src_bitmap);
    red_get_rect_ptr(&red->src_area, &qxl->src_area);
    red->rop_descriptor  = qxl->rop_descriptor;
    red->scale_mode      = qxl->scale_mode;
    red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_copy(SpiceCopy *red)
+{
+    red_put_image(red->src_bitmap);
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_blend_ptr(RedMemSlotInfo *slots, int group_id,
                              SpiceBlend *red, QXLBlend *qxl)
 {
-   red->src_bitmap      = qxl->src_bitmap;
+   red->src_bitmap      = red_get_image(slots, group_id, qxl->src_bitmap);
    red_get_rect_ptr(&red->src_area, &qxl->src_area);
    red->rop_descriptor  = qxl->rop_descriptor;
    red->scale_mode      = qxl->scale_mode;
    red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_blend(SpiceBlend *red)
+{
+    red_put_image(red->src_bitmap);
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_transparent_ptr(RedMemSlotInfo *slots, int group_id,
                                     SpiceTransparent *red, QXLTransparent *qxl)
 {
-   red->src_bitmap      = qxl->src_bitmap;
+   red->src_bitmap      = red_get_image(slots, group_id, qxl->src_bitmap);
    red_get_rect_ptr(&red->src_area, &qxl->src_area);
    red->src_color       = qxl->src_color;
    red->true_color      = qxl->true_color;
 }
 
+static void red_put_transparent(SpiceTransparent *red)
+{
+    red_put_image(red->src_bitmap);
+}
+
 static void red_get_alpha_blend_ptr(RedMemSlotInfo *slots, int group_id,
                                     SpiceAlphaBlnd *red, QXLAlphaBlnd *qxl)
 {
     red->alpha_flags = qxl->alpha_flags;
     red->alpha       = qxl->alpha;
-    red->src_bitmap  = qxl->src_bitmap;
+    red->src_bitmap  = red_get_image(slots, group_id, qxl->src_bitmap);
     red_get_rect_ptr(&red->src_area, &qxl->src_area);
 }
 
@@ -339,14 +527,19 @@ static void red_get_alpha_blend_ptr_compat(RedMemSlotInfo *slots, int group_id,
                                            SpiceAlphaBlnd *red, QXLCompatAlphaBlnd *qxl)
 {
     red->alpha       = qxl->alpha;
-    red->src_bitmap  = qxl->src_bitmap;
+    red->src_bitmap  = red_get_image(slots, group_id, qxl->src_bitmap);
     red_get_rect_ptr(&red->src_area, &qxl->src_area);
 }
 
+static void red_put_alpha_blend(SpiceAlphaBlnd *red)
+{
+    red_put_image(red->src_bitmap);
+}
+
 static void red_get_rop3_ptr(RedMemSlotInfo *slots, int group_id,
                              SpiceRop3 *red, QXLRop3 *qxl)
 {
-   red->src_bitmap = qxl->src_bitmap;
+   red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap);
    red_get_rect_ptr(&red->src_area, &qxl->src_area);
    red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush);
    red->rop3       = qxl->rop3;
@@ -354,6 +547,13 @@ static void red_get_rop3_ptr(RedMemSlotInfo *slots, int group_id,
    red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_rop3(SpiceRop3 *red)
+{
+    red_put_image(red->src_bitmap);
+    red_put_brush(&red->brush);
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_stroke_ptr(RedMemSlotInfo *slots, int group_id,
                                SpiceStroke *red, QXLStroke *qxl)
 {
@@ -371,8 +571,9 @@ static void red_get_stroke_ptr(RedMemSlotInfo *slots, int group_id,
    red->back_mode        = qxl->back_mode;
 }
 
-static void red_put_stroke_ptr(SpiceStroke *red)
+static void red_put_stroke(SpiceStroke *red)
 {
+    red_put_brush(&red->brush);
     free(red->path);
 }
 
@@ -465,6 +666,8 @@ static void red_get_text_ptr(RedMemSlotInfo *slots, int group_id,
 static void red_put_text_ptr(SpiceText *red)
 {
     free(red->str);
+    red_put_brush(&red->fore_brush);
+    red_put_brush(&red->back_brush);
 }
 
 static void red_get_whiteness_ptr(RedMemSlotInfo *slots, int group_id,
@@ -473,18 +676,33 @@ static void red_get_whiteness_ptr(RedMemSlotInfo *slots, int group_id,
     red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_whiteness(SpiceWhiteness *red)
+{
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_blackness_ptr(RedMemSlotInfo *slots, int group_id,
                                   SpiceBlackness *red, QXLBlackness *qxl)
 {
     red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_blackness(SpiceWhiteness *red)
+{
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_invers_ptr(RedMemSlotInfo *slots, int group_id,
                                SpiceInvers *red, QXLInvers *qxl)
 {
     red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask);
 }
 
+static void red_put_invers(SpiceWhiteness *red)
+{
+    red_put_qmask(&red->mask);
+}
+
 static void red_get_clip_ptr(RedMemSlotInfo *slots, int group_id,
                              SpiceClip *red, QXLClip *qxl)
 {
@@ -573,7 +791,7 @@ void red_get_drawable(RedMemSlotInfo *slots, int group_id,
         red_get_whiteness_ptr(slots, group_id, &red->u.whiteness, &qxl->u.whiteness);
         break;
     default:
-        red_error("unknown type");
+        red_error("%s: unknown type %d", __FUNCTION__, red->type);
         break;
     };
 }
@@ -637,7 +855,7 @@ void red_get_compat_drawable(RedMemSlotInfo *slots, int group_id,
         red_get_whiteness_ptr(slots, group_id, &red->u.whiteness, &qxl->u.whiteness);
         break;
     default:
-        red_error("unknown type");
+        red_error("%s: unknown type %d", __FUNCTION__, red->type);
         break;
     };
 }
@@ -646,12 +864,42 @@ void red_put_drawable(RedDrawable *red)
 {
     red_put_clip(&red->clip);
     switch (red->type) {
+    case QXL_DRAW_ALPHA_BLEND:
+        red_put_alpha_blend(&red->u.alpha_blend);
+        break;
+    case QXL_DRAW_BLACKNESS:
+        red_put_blackness(&red->u.blackness);
+        break;
+    case QXL_DRAW_BLEND:
+        red_put_blend(&red->u.blend);
+        break;
+    case QXL_DRAW_COPY:
+        red_put_copy(&red->u.copy);
+        break;
+    case QXL_DRAW_FILL:
+        red_put_fill(&red->u.fill);
+        break;
+    case QXL_DRAW_OPAQUE:
+        red_put_opaque(&red->u.opaque);
+        break;
+    case QXL_DRAW_INVERS:
+        red_put_invers(&red->u.invers);
+        break;
+    case QXL_DRAW_ROP3:
+        red_put_rop3(&red->u.rop3);
+        break;
     case QXL_DRAW_STROKE:
-        red_put_stroke_ptr(&red->u.stroke);
+        red_put_stroke(&red->u.stroke);
         break;
     case QXL_DRAW_TEXT:
         red_put_text_ptr(&red->u.text);
         break;
+    case QXL_DRAW_TRANSPARENT:
+        red_put_transparent(&red->u.transparent);
+        break;
+    case QXL_DRAW_WHITENESS:
+        red_put_whiteness(&red->u.whiteness);
+        break;
     }
 }
 
diff --git a/server/red_parse_qxl.h b/server/red_parse_qxl.h
index 079cf06..7c4fa0c 100644
--- a/server/red_parse_qxl.h
+++ b/server/red_parse_qxl.h
@@ -118,6 +118,7 @@ void red_get_drawable(RedMemSlotInfo *slots, int group_id,
 void red_get_compat_drawable(RedMemSlotInfo *slots, int group_id,
                              RedDrawable *red, SPICE_ADDRESS addr);
 void red_put_drawable(RedDrawable *red);
+void red_put_image(SpiceImage *red);
 
 void red_get_update_cmd(RedMemSlotInfo *slots, int group_id,
                         RedUpdateCmd *red, SPICE_ADDRESS addr);
diff --git a/server/red_worker.c b/server/red_worker.c
index 255a46e..abe4e7b 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -326,7 +326,6 @@ typedef struct LocalCursor {
     SpiceCursor red_cursor;
 } LocalCursor;
 
-#define MAX_BITMAPS 4
 #define MAX_PIPE_SIZE 50
 #define RECIVE_BUF_SIZE 1024
 
@@ -465,20 +464,6 @@ static const int BITMAP_FMT_IS_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0};
 static const int BITMAP_FMT_IS_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
 static const int BITMAP_FMP_BYTES_PER_PIXEL[] = {0, 0, 0, 0, 0, 1, 2, 3, 4, 4};
 
-typedef struct  __attribute__ ((__packed__)) RedImage {
-    SpiceImageDescriptor descriptor;
-    union { // variable length
-        SpiceBitmap bitmap;
-        SpiceQUICData quic;
-        SpiceLZRGBData lz_rgb;
-        SpiceLZPLTData lz_plt;
-        SpiceSurface surface;
-        SpiceJPEGData jpeg;
-        SpiceZlibGlzRGBData zlib_glz;
-        SpiceJPEGAlphaData jpeg_alpha;
-    };
-} RedImage;
-
 pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 Ring pixmap_cache_list = {&pixmap_cache_list, &pixmap_cache_list};
 
@@ -552,25 +537,12 @@ typedef struct  {
     jmp_buf jmp_env;
     union {
         struct {
-            SPICE_ADDRESS next;
-            uint32_t stride;
-            uint32_t group_id;
-
-            void *enc_get_virt_opaque;
-            enc_get_virt_fn_t enc_get_virt;
-            void *enc_validate_virt_opaque;
-            enc_validate_virt_fn_t enc_validate_virt;
+            SpiceChunks *chunks;
+            int next;
+            int stride;
+            int reverse;
         } lines_data;
         struct {
-            uint8_t* next;
-            int src_stride;
-            uint32_t dest_stride;
-            int lines;
-            int max_lines_bunch;
-            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
@@ -625,7 +597,7 @@ struct RedGlzDrawable {
     Drawable    *drawable;
     uint32_t     group_id;
     int32_t      surface_id;
-    uint8_t     *self_bitmap;
+    SpiceImage  *self_bitmap;
     GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
     Ring instances;
     uint8_t instances_count;
@@ -719,11 +691,6 @@ typedef struct CursorChannel {
 #endif
 } CursorChannel;
 
-typedef struct __attribute__ ((__packed__)) LocalImage {
-    QXLImage qxl_image;
-    uint8_t buf[sizeof(QXLDataChunk *)]; // quic data area
-} LocalImage;
-
 typedef struct ImageCacheItem {
     RingItem lru_link;
     uint64_t id;
@@ -819,7 +786,7 @@ struct Drawable {
 #endif
     BitmapGradualType copy_bitmap_graduality;
     uint32_t group_id;
-    uint8_t *self_bitmap;
+    SpiceImage *self_bitmap;
     DependItem depend_items[3];
 
     uint8_t *backed_surface_data;
@@ -947,9 +914,6 @@ typedef struct RedWorker {
 
     uint32_t preload_group_id;
 
-    uint32_t local_images_pos;
-    LocalImage local_images[MAX_BITMAPS];
-
     ImageCache image_cache;
 
     spice_image_compression_t image_compression;
@@ -992,8 +956,6 @@ typedef struct RedWorker {
     uint64_t *wakeup_counter;
     uint64_t *command_counter;
 #endif
-    SpiceVirtMapping preload_group_virt_mapping;
-
 } RedWorker;
 
 typedef enum {
@@ -1145,22 +1107,6 @@ static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
     PANIC_ON(!worker->surfaces[surface_id].context.canvas);
 }
 
-static void *op_get_virt_preload_group(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size)
-{
-    RedWorker *worker = SPICE_CONTAINEROF(mapping, RedWorker, preload_group_virt_mapping);
-    return (void *)get_virt(&worker->mem_slots, addr, add_size,
-                            worker->preload_group_id);
-}
-
-static void op_validate_virt_preload_group(SpiceVirtMapping *mapping, unsigned long virt,
-                                           unsigned long from_addr, uint32_t add_size)
-{
-    RedWorker *worker = SPICE_CONTAINEROF(mapping, RedWorker, preload_group_virt_mapping);
-    int slot_id = get_memslot_id(&worker->mem_slots, from_addr);
-    validate_virt(&worker->mem_slots, virt, slot_id, add_size,
-                  worker->preload_group_id);
-}
-
 char *draw_type_to_str(uint8_t type)
 {
     switch (type) {
@@ -1603,13 +1549,13 @@ static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_
 }
 
 static inline void free_red_drawable(RedWorker *worker, RedDrawable *drawable, uint32_t group_id,
-                                     uint8_t *self_bitmap, int surface_id)
+                                     SpiceImage *self_bitmap, int surface_id)
 {
     QXLReleaseInfoExt release_info_ext;
     red_destroy_surface(worker, surface_id);
 
     if (self_bitmap) {
-        free(self_bitmap);
+        red_put_image(self_bitmap);
     }
     release_info_ext.group_id = group_id;
     release_info_ext.info = drawable->release_info;
@@ -2768,9 +2714,8 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
 #endif
     stream->refs = 1;
     stream->bit_rate = get_bit_rate(stream_width, stream_height);
-    QXLImage *qxl_image = (QXLImage *)get_virt(&worker->mem_slots, drawable->red_drawable->u.copy.src_bitmap,
-                                               sizeof(QXLImage), drawable->group_id);
-    stream->top_down = !!(qxl_image->bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
+    SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
+    stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
     drawable->stream = stream;
 
     if (worker->display_channel) {
@@ -2855,9 +2800,8 @@ static inline int __red_is_next_stream_frame(RedWorker *worker,
     }
 
     if (stream) {
-        QXLImage *qxl_image = (QXLImage *)get_virt(&worker->mem_slots, red_drawable->u.copy.src_bitmap,
-                                                   sizeof(QXLImage), candidate->group_id);
-        if (stream->top_down != !!(qxl_image->bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+        SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
+        if (stream->top_down != !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
             return FALSE;
         }
     }
@@ -2882,7 +2826,7 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca
 
 static inline int red_is_next_stream_frame(RedWorker *worker, Drawable *candidate, Drawable *prev)
 {
-    QXLImage *qxl_image;
+    SpiceImage *image;
     RedDrawable *red_drawable;
     RedDrawable *prev_red_drawable;
 
@@ -2891,33 +2835,32 @@ static inline int red_is_next_stream_frame(RedWorker *worker, Drawable *candidat
         return FALSE;
     }
 
-    qxl_drawable = candidate->red_drawable;
-    prev_qxl_drawable = prev->red_drawable;
-    if (qxl_drawable->type != QXL_DRAW_COPY || prev_qxl_drawable->type != QXL_DRAW_COPY) {
+    red_drawable = candidate->red_drawable;
+    prev_red_drawable = prev->red_drawable;
+    if (red_drawable->type != QXL_DRAW_COPY || prev_red_drawable->type != QXL_DRAW_COPY) {
         return FALSE;
     }
 
-    if (!rect_is_equal(&qxl_drawable->bbox, &prev_qxl_drawable->bbox)) {
+    if (!rect_is_equal(&red_drawable->bbox, &prev_red_drawable->bbox)) {
         return FALSE;
     }
 
-    if (!rect_is_same_size(&qxl_drawable->u.copy.src_area, &prev_qxl_drawable->u.copy.src_area)) {
+    if (!rect_is_same_size(&red_drawable->u.copy.src_area, &prev_red_drawable->u.copy.src_area)) {
         return FALSE;
     }
 
-    if (qxl_drawable->u.copy.rop_decriptor != SPICE_ROPD_OP_PUT ||
-                                    prev_qxl_drawable->u.copy.rop_decriptor != SPICE_ROPD_OP_PUT) {
+    if (red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT ||
+        prev_red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
         return FALSE;
     }
 
-    qxl_image = (QXLImage *)get_virt(&worker->mem_slots, qxl_drawable->u.copy.src_bitmap, sizeof(QXLImage),
-                                     candidate->group_id);
+    image = red_drawable->u.copy.src_bitmap;
 
-    if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
+    if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
         return FALSE;
     }
 
-    if (prev->stream && prev->stream->top_down != !!(qxl_image->bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+    if (prev->stream && prev->stream->top_down != !!(image->u.bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
         return FALSE;
     }
 
@@ -2976,7 +2919,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
 
 static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawable)
 {
-    QXLImage *qxl_image;
+    SpiceBitmap *bitmap;
     ASSERT(drawable->red_drawable->type == QXL_DRAW_COPY);
 
     if (worker->streaming_video != STREAM_VIDEO_FILTER) {
@@ -2988,14 +2931,13 @@ static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawa
         return; // already set
     }
 
-    qxl_image = (QXLImage *)get_virt(&worker->mem_slots, drawable->red_drawable->u.copy.src_bitmap,
-                                     sizeof(QXLImage), drawable->group_id);
+    bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
 
-    if (!BITMAP_FMT_IS_RGB[qxl_image->bitmap.format] || _stride_is_extra(&qxl_image->bitmap) ||
-        (qxl_image->bitmap.flags & QXL_BITMAP_UNSTABLE)) {
+    if (!BITMAP_FMT_IS_RGB[bitmap->format] || _stride_is_extra(bitmap) ||
+        (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
         drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
     } else  {
-        drawable->copy_bitmap_graduality = _get_bitmap_graduality_level(worker, &qxl_image->bitmap, drawable->group_id);
+        drawable->copy_bitmap_graduality = _get_bitmap_graduality_level(worker, bitmap, drawable->group_id);
     }
 }
 
@@ -3048,7 +2990,7 @@ static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate
     }
 #else
     if ((worker->streaming_video == STREAM_VIDEO_OFF) ||
-        !red_is_next_stream_frame(worker, candidate, prev) {
+        !red_is_next_stream_frame(worker, candidate, prev)) {
         return;
     }
 #endif
@@ -3507,7 +3449,7 @@ static inline int has_shadow(RedDrawable *drawable)
 static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
                                          RedDrawable *red_drawable)
 {
-    QXLImage *qxl_image;
+    SpiceImage *image;
 
     if (worker->streaming_video == STREAM_VIDEO_OFF) {
         return;
@@ -3519,9 +3461,8 @@ static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
         return;
     }
 
-    qxl_image = (QXLImage *)get_virt(&worker->mem_slots, red_drawable->u.copy.src_bitmap, sizeof(QXLImage),
-                                     drawable->group_id);
-    if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
+    image = red_drawable->u.copy.src_bitmap;
+    if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
         return;
     }
 
@@ -3648,7 +3589,7 @@ static int rgb32_data_has_alpha(int width, int height, size_t stride,
 
 static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
 {
-    QXLImage *image;
+    SpiceImage *image;
     int32_t width;
     int32_t height;
     uint8_t *dest;
@@ -3670,21 +3611,21 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     height = drawable->red_drawable->bbox.bottom - drawable->red_drawable->bbox.top;
     dest_stride = SPICE_ALIGN(width * bpp, 4);
 
-    image = spice_malloc_n_m(height, dest_stride, sizeof(QXLImage));
-    dest = (uint8_t *)(image + 1);
-
+    image = spice_new0(SpiceImage, 1);
     image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
     image->descriptor.flags = 0;
 
     QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_RED, ++worker->bits_unique);
-    image->bitmap.flags = QXL_BITMAP_DIRECT | (surface->context.top_down ?
-                                               QXL_BITMAP_TOP_DOWN : 0);
-    image->bitmap.format = surface_format_to_image_type(surface->context.format);
-    image->bitmap.stride = dest_stride;
-    image->descriptor.width = image->bitmap.x = width;
-    image->descriptor.height = image->bitmap.y = height;
-    image->bitmap.data = (QXLPHYSICAL)dest;
-    image->bitmap.palette = 0;
+    image->u.bitmap.flags = surface->context.top_down ? SPICE_BITMAP_FLAGS_TOP_DOWN : 0;
+    image->u.bitmap.format = surface_format_to_image_type(surface->context.format);
+    image->u.bitmap.stride = dest_stride;
+    image->descriptor.width = image->u.bitmap.x = width;
+    image->descriptor.height = image->u.bitmap.y = height;
+    image->u.bitmap.palette = NULL;
+
+    dest = (uint8_t *)spice_malloc_n(height, dest_stride);
+    image->u.bitmap.data = spice_chunks_new_linear(dest, height * dest_stride);
+    image->u.bitmap.data->flags |= SPICE_CHUNKS_FLAGS_FREE;
 
     red_get_area(worker, drawable->surface_id,
                  &drawable->red_drawable->self_bitmap_area, dest, dest_stride, TRUE);
@@ -3692,16 +3633,16 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     /* For 32bit non-primary surfaces we need to keep any non-zero
        high bytes as the surface may be used as source to an alpha_blend */
     if (!is_primary_surface(worker, drawable->surface_id) &&
-        image->bitmap.format == SPICE_BITMAP_FMT_32BIT &&
+        image->u.bitmap.format == SPICE_BITMAP_FMT_32BIT &&
         rgb32_data_has_alpha(width, height, dest_stride, dest, &all_set)) {
         if (all_set) {
             image->descriptor.flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
         } else {
-            image->bitmap.format = SPICE_BITMAP_FMT_RGBA;
+            image->u.bitmap.format = SPICE_BITMAP_FMT_RGBA;
         }
     }
 
-    drawable->self_bitmap = (uint8_t *)image;
+    drawable->self_bitmap = image;
     return TRUE;
 }
 
@@ -3947,12 +3888,6 @@ static inline void red_process_surface(RedWorker *worker, RedSurfaceCmd *surface
     free(surface);
 }
 
-static LocalImage *alloc_local_image(RedWorker *worker)
-{
-    ASSERT(worker->local_images_pos < MAX_BITMAPS);
-    return &worker->local_images[worker->local_images_pos++];
-}
-
 static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
                                        uint32_t surface_id)
 {
@@ -4107,156 +4042,52 @@ static void image_cache_eaging(ImageCache *cache)
 #endif
 }
 
-static void localize_bitmap(RedWorker *worker, QXLPHYSICAL *in_bitmap, uint32_t group_id)
+static void localize_bitmap(RedWorker *worker, SpiceImage **image_ptr, SpiceImage *image_store)
 {
-    QXLImage *image;
-    QXLImage *local_image;
-
-    ASSERT(in_bitmap && *in_bitmap);
-    image = (QXLImage *)get_virt(&worker->mem_slots, *in_bitmap, sizeof(QXLImage), group_id);
-    local_image = (QXLImage *)alloc_local_image(worker);
-    *local_image = *image;
-    *in_bitmap = (QXLPHYSICAL)local_image;
-    local_image->descriptor.flags = 0;
+    SpiceImage *image = *image_ptr;
 
-    if (image_cache_hit(&worker->image_cache, local_image->descriptor.id)) {
-        local_image->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
+    if (image_cache_hit(&worker->image_cache, image->descriptor.id)) {
+        image_store->descriptor = image->descriptor;
+        image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
+        image_store->descriptor.flags = 0;
+        *image_ptr = image_store;
         return;
     }
 
-    if (image->descriptor.flags & QXL_IMAGE_HIGH_BITS_SET) {
-        local_image->descriptor.flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
-    }
-
-    switch (local_image->descriptor.type) {
+    switch (image->descriptor.type) {
     case SPICE_IMAGE_TYPE_QUIC: {
-        QXLDataChunk **chanks_head;
+        image_store->descriptor = image->descriptor;
+        image_store->u.quic = image->u.quic;
+        *image_ptr = image_store;
 #ifdef IMAGE_CACHE_AGE
-        local_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+        image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
 #else
-        if (local_image->descriptor.width * local_image->descriptor.height >= 640 * 480) {
-            local_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+        if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
+            image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
         }
 #endif
-        chanks_head = (QXLDataChunk **)local_image->quic.data;
-        *chanks_head = (QXLDataChunk *)image->quic.data;
         break;
     }
     case SPICE_IMAGE_TYPE_BITMAP:
-        if (image->bitmap.flags & QXL_BITMAP_DIRECT) {
-            local_image->bitmap.data = (QXLPHYSICAL)get_virt(&worker->mem_slots, image->bitmap.data,
-                                                          image->bitmap.stride * image->bitmap.y,
-                                                          group_id);
-        } else {
-            QXLPHYSICAL src_data;
-            int size = image->bitmap.y * image->bitmap.stride;
-            uint8_t *data = spice_malloc_n(image->bitmap.y, image->bitmap.stride);
-            local_image->bitmap.data = (QXLPHYSICAL)data;
-            src_data = image->bitmap.data;
-
-            while (size) {
-                QXLDataChunk *chunk;
-                uint32_t data_size;
-                int cp_size;
-
-                ASSERT(src_data);
-                chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, src_data, sizeof(QXLDataChunk), group_id);
-                data_size = chunk->data_size;
-                validate_virt(&worker->mem_slots, (unsigned long)chunk->data, get_memslot_id(&worker->mem_slots, src_data),
-                              data_size, group_id);
-                cp_size = MIN(data_size, size);
-                memcpy(data, chunk->data, cp_size);
-                data += cp_size;
-                size -= cp_size;
-                src_data = chunk->next_chunk;
-            }
-        }
-
-        if (local_image->bitmap.palette) {
-            uint16_t num_ents;
-            uint32_t *ents;
-            SpicePalette *tmp_palette;
-            SpicePalette *shadow_palette;
-
-            int slot_id = get_memslot_id(&worker->mem_slots, local_image->bitmap.palette);
-            tmp_palette = (SpicePalette *)get_virt(&worker->mem_slots, local_image->bitmap.palette,
-                                              sizeof(SpicePalette), group_id);
-
-            num_ents = tmp_palette->num_ents;
-            ents = tmp_palette->ents;
-
-            validate_virt(&worker->mem_slots, (unsigned long)ents, slot_id, (num_ents * sizeof(uint32_t)),
-                          group_id);
-
-            shadow_palette = (SpicePalette *)spice_malloc_n_m(num_ents, sizeof(uint32_t),sizeof(SpicePalette) + sizeof(QXLPHYSICAL));
-
-            memcpy(shadow_palette->ents, ents, num_ents * sizeof(uint32_t));
-            shadow_palette->num_ents = num_ents;
-            shadow_palette->unique = tmp_palette->unique;
-
-            local_image->bitmap.palette = (SPICE_ADDRESS)shadow_palette;
-        }
-        break;
-    case SPICE_IMAGE_TYPE_SURFACE: {
-        break;
-    }
-    default:
-        red_error("invalid image type");
-    }
-}
-
-static void unlocalize_bitmap(QXLPHYSICAL *bitmap)
-{
-    QXLImage *image;
-
-    ASSERT(bitmap && *bitmap);
-    image = (QXLImage *)*bitmap;
-    *bitmap = 0;
-
-    switch (image->descriptor.type) {
-    case SPICE_IMAGE_TYPE_BITMAP:
-        if (!(image->bitmap.flags & QXL_BITMAP_DIRECT)) {
-            free((void *)image->bitmap.data);
-        }
-        if (image->bitmap.palette) {
-            free((void *)image->bitmap.palette);
-        }
-        break;
-    case SPICE_IMAGE_TYPE_QUIC:
-    case SPICE_IMAGE_TYPE_FROM_CACHE:
-        *bitmap = 0;
     case SPICE_IMAGE_TYPE_SURFACE:
+        /* nothing */
         break;
     default:
-        red_error("invalid image type %u", image->descriptor.type);
-    }
-}
-
-static void localize_brush(RedWorker *worker, SpiceBrush *brush, uint32_t group_id)
-{
-    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
-        localize_bitmap(worker, &brush->u.pattern.pat, group_id);
+        red_error("invalid image type");
     }
 }
 
-static void unlocalize_brush(SpiceBrush *brush)
+static void localize_brush(RedWorker *worker, SpiceBrush *brush, SpiceImage *image_store)
 {
     if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
-        unlocalize_bitmap(&brush->u.pattern.pat);
+        localize_bitmap(worker, &brush->u.pattern.pat, image_store);
     }
 }
 
-static void localize_mask(RedWorker *worker, SpiceQMask *mask, uint32_t group_id)
+static void localize_mask(RedWorker *worker, SpiceQMask *mask, SpiceImage *image_store)
 {
     if (mask->bitmap) {
-        localize_bitmap(worker, &mask->bitmap, group_id);
-    }
-}
-
-static void unlocalize_mask(SpiceQMask *mask)
-{
-    if (mask->bitmap) {
-        unlocalize_bitmap(&mask->bitmap);
+        localize_bitmap(worker, &mask->bitmap, image_store);
     }
 }
 
@@ -4292,7 +4123,6 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
     surface = &worker->surfaces[drawable->surface_id];
     canvas = surface->context.canvas;
 
-    worker->local_images_pos = 0;
     image_cache_eaging(&worker->image_cache);
 
     worker->preload_group_id = drawable->group_id;
@@ -4302,48 +4132,45 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
     switch (drawable->red_drawable->type) {
     case QXL_DRAW_FILL: {
         SpiceFill fill = drawable->red_drawable->u.fill;
-        localize_brush(worker, &fill.brush, drawable->group_id);
-        localize_mask(worker, &fill.mask, drawable->group_id);
+        SpiceImage img1, img2;
+        localize_brush(worker, &fill.brush, &img1);
+        localize_mask(worker, &fill.mask, &img2);
         canvas->ops->draw_fill(canvas, &drawable->red_drawable->bbox,
-                               &clip, &fill); unlocalize_mask(&fill.mask);
-        unlocalize_brush(&fill.brush);
+                               &clip, &fill);
         break;
     }
     case QXL_DRAW_OPAQUE: {
         SpiceOpaque opaque = drawable->red_drawable->u.opaque;
-        localize_brush(worker, &opaque.brush, drawable->group_id);
-        localize_bitmap(worker, &opaque.src_bitmap, drawable->group_id);
-        localize_mask(worker, &opaque.mask, drawable->group_id);
+        SpiceImage img1, img2, img3;
+        localize_brush(worker, &opaque.brush, &img1);
+        localize_bitmap(worker, &opaque.src_bitmap, &img2);
+        localize_mask(worker, &opaque.mask, &img3);
         canvas->ops->draw_opaque(canvas, &drawable->red_drawable->bbox, &clip, &opaque);
-        unlocalize_mask(&opaque.mask);
-        unlocalize_bitmap(&opaque.src_bitmap);
-        unlocalize_brush(&opaque.brush);
         break;
     }
     case QXL_DRAW_COPY: {
         SpiceCopy copy = drawable->red_drawable->u.copy;
-        localize_bitmap(worker, &copy.src_bitmap, drawable->group_id);
-        localize_mask(worker, &copy.mask, drawable->group_id);
+        SpiceImage img1, img2;
+        localize_bitmap(worker, &copy.src_bitmap, &img1);
+        localize_mask(worker, &copy.mask, &img2);
         canvas->ops->draw_copy(canvas, &drawable->red_drawable->bbox,
                                &clip, &copy);
-        unlocalize_mask(&copy.mask);
-        unlocalize_bitmap(&copy.src_bitmap);
         break;
     }
     case QXL_DRAW_TRANSPARENT: {
         SpiceTransparent transparent = drawable->red_drawable->u.transparent;
-        localize_bitmap(worker, &transparent.src_bitmap, drawable->group_id);
+        SpiceImage img1;
+        localize_bitmap(worker, &transparent.src_bitmap, &img1);
         canvas->ops->draw_transparent(canvas,
                                       &drawable->red_drawable->bbox, &clip, &transparent);
-        unlocalize_bitmap(&transparent.src_bitmap);
         break;
     }
     case QXL_DRAW_ALPHA_BLEND: {
         SpiceAlphaBlnd alpha_blend = drawable->red_drawable->u.alpha_blend;
-        localize_bitmap(worker, &alpha_blend.src_bitmap, drawable->group_id);
+        SpiceImage img1;
+        localize_bitmap(worker, &alpha_blend.src_bitmap, &img1);
         canvas->ops->draw_alpha_blend(canvas,
                                       &drawable->red_drawable->bbox, &clip, &alpha_blend);
-        unlocalize_bitmap(&alpha_blend.src_bitmap);
         break;
     }
     case QXL_COPY_BITS: {
@@ -4353,67 +4180,64 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
     }
     case QXL_DRAW_BLEND: {
         SpiceBlend blend = drawable->red_drawable->u.blend;
-        localize_bitmap(worker, &blend.src_bitmap, drawable->group_id);
-        localize_mask(worker, &blend.mask, drawable->group_id);
+        SpiceImage img1, img2;
+        localize_bitmap(worker, &blend.src_bitmap, &img1);
+        localize_mask(worker, &blend.mask, &img2);
         canvas->ops->draw_blend(canvas, &drawable->red_drawable->bbox,
                                 &clip, &blend);
-        unlocalize_mask(&blend.mask);
-        unlocalize_bitmap(&blend.src_bitmap);
         break;
     }
     case QXL_DRAW_BLACKNESS: {
         SpiceBlackness blackness = drawable->red_drawable->u.blackness;
-        localize_mask(worker, &blackness.mask, drawable->group_id);
+        SpiceImage img1;
+        localize_mask(worker, &blackness.mask, &img1);
         canvas->ops->draw_blackness(canvas,
                                     &drawable->red_drawable->bbox, &clip, &blackness);
-        unlocalize_mask(&blackness.mask);
         break;
     }
     case QXL_DRAW_WHITENESS: {
         SpiceWhiteness whiteness = drawable->red_drawable->u.whiteness;
-        localize_mask(worker, &whiteness.mask, drawable->group_id);
+        SpiceImage img1;
+        localize_mask(worker, &whiteness.mask, &img1);
         canvas->ops->draw_whiteness(canvas,
                                     &drawable->red_drawable->bbox, &clip, &whiteness);
-        unlocalize_mask(&whiteness.mask);
         break;
     }
     case QXL_DRAW_INVERS: {
         SpiceInvers invers = drawable->red_drawable->u.invers;
-        localize_mask(worker, &invers.mask, drawable->group_id);
+        SpiceImage img1;
+        localize_mask(worker, &invers.mask, &img1);
         canvas->ops->draw_invers(canvas,
                                  &drawable->red_drawable->bbox, &clip, &invers);
-        unlocalize_mask(&invers.mask);
         break;
     }
     case QXL_DRAW_ROP3: {
         SpiceRop3 rop3 = drawable->red_drawable->u.rop3;
-        localize_brush(worker, &rop3.brush, drawable->group_id);
-        localize_bitmap(worker, &rop3.src_bitmap, drawable->group_id);
-        localize_mask(worker, &rop3.mask, drawable->group_id);
+        SpiceImage img1, img2, img3;
+        localize_brush(worker, &rop3.brush, &img1);
+        localize_bitmap(worker, &rop3.src_bitmap, &img2);
+        localize_mask(worker, &rop3.mask, &img3);
         canvas->ops->draw_rop3(canvas, &drawable->red_drawable->bbox,
-                               &clip, &rop3); unlocalize_mask(&rop3.mask);
-        unlocalize_bitmap(&rop3.src_bitmap);
-        unlocalize_brush(&rop3.brush);
+                               &clip, &rop3);
         break;
     }
     case QXL_DRAW_STROKE: {
         SpiceStroke stroke = drawable->red_drawable->u.stroke;
-        localize_brush(worker, &stroke.brush, drawable->group_id);
+        SpiceImage img1;
+        localize_brush(worker, &stroke.brush, &img1);
         localize_attr(worker, &stroke.attr, drawable->group_id);
         canvas->ops->draw_stroke(canvas,
                                  &drawable->red_drawable->bbox, &clip, &stroke);
         unlocalize_attr(&stroke.attr);
-        unlocalize_brush(&stroke.brush);
         break;
     }
     case QXL_DRAW_TEXT: {
         SpiceText text = drawable->red_drawable->u.text;
-        localize_brush(worker, &text.fore_brush, drawable->group_id);
-        localize_brush(worker, &text.back_brush, drawable->group_id);
+        SpiceImage img1, img2;
+        localize_brush(worker, &text.fore_brush, &img1);
+        localize_brush(worker, &text.back_brush, &img2);
         canvas->ops->draw_text(canvas, &drawable->red_drawable->bbox,
                                &clip, &text);
-        unlocalize_brush(&text.back_brush);
-        unlocalize_brush(&text.fore_brush);
         break;
     }
     default:
@@ -5077,6 +4901,7 @@ static void marshaller_add_compressed(RedWorker *worker, SpiceMarshaller *m,
     } while (max);
 }
 
+
 static void marshaller_add_chunk(RedWorker *worker, SpiceMarshaller *m, QXLDataChunk *chunk, size_t size,
                                  int memslot_id, uint32_t group_id)
 {
@@ -5139,31 +4964,22 @@ static void fill_base(DisplayChannel *display_channel, Drawable *drawable)
     spice_marshall_DisplayBase(channel->send_data.marshaller, &base);
 }
 
-/* io_palette is relative address of the palette*/
-static inline void fill_palette(DisplayChannel *display_channel, SPICE_ADDRESS *io_palette, uint8_t *flags,
-                                uint32_t group_id, SpicePalette **palette_out)
+static inline void fill_palette(DisplayChannel *display_channel,
+                                SpicePalette *palette,
+                                uint8_t *flags)
 {
-    RedChannel *channel = &display_channel->base;
-    RedWorker *worker = channel->worker;
-    SpicePalette *palette;
-
-    *palette_out = NULL;
-    if (!(*io_palette)) {
+    if (palette == NULL) {
         return;
     }
-
-    palette = (SpicePalette *)get_virt(&worker->mem_slots, *io_palette, sizeof(SpicePalette), group_id);
     if (palette->unique) {
         if (red_palette_cache_find(display_channel, palette->unique)) {
             *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
-            *io_palette = palette->unique;
             return;
         }
         if (red_palette_cache_add(display_channel, palette->unique, 1)) {
             *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
         }
     }
-    *palette_out = palette;
 }
 
 static inline RedCompressBuf *red_display_alloc_compress_buf(DisplayChannel *display_channel)
@@ -5562,31 +5378,32 @@ static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
 
 static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
 {
-    uint32_t data_size;
-    uint8_t *data;
+    struct SpiceChunk *chunk;
 
-    if (!enc_data->u.lines_data.next) {
-        return 0;
+    if (enc_data->u.lines_data.reverse) {
+        if (!(enc_data->u.lines_data.next >= 0)) {
+            return 0;
+        }
+    } else {
+        if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks->num_chunks)) {
+            return 0;
+        }
     }
 
-    QXLDataChunk *chunk = (QXLDataChunk *)enc_data->u.lines_data.enc_get_virt(
-                          enc_data->u.lines_data.enc_get_virt_opaque, enc_data->u.lines_data.next,
-                          sizeof(QXLDataChunk), enc_data->u.lines_data.group_id);
-
-    data_size = chunk->data_size;
-    data = chunk->data;
-
-    if (data_size % enc_data->u.lines_data.stride) {
+    chunk = &enc_data->u.lines_data.chunks->chunk[enc_data->u.lines_data.next];
+    if (chunk->len % enc_data->u.lines_data.stride) {
         return 0;
     }
 
-    enc_data->u.lines_data.enc_validate_virt(enc_data->u.lines_data.enc_validate_virt_opaque,
-                                             (unsigned long)data, enc_data->u.lines_data.next,
-                                             data_size, enc_data->u.lines_data.group_id);
+    if (enc_data->u.lines_data.reverse) {
+        enc_data->u.lines_data.next--;
+        *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride;
+    } else {
+        enc_data->u.lines_data.next++;
+        *lines = chunk->data;
+    }
 
-    enc_data->u.lines_data.next = chunk->next_chunk;
-    *lines = data;
-    return data_size / enc_data->u.lines_data.stride;
+    return chunk->len / enc_data->u.lines_data.stride;
 }
 
 static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
@@ -5613,105 +5430,6 @@ static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
     return encoder_usr_more_lines(usr_data, lines);
 }
 
-static int encoder_usr_more_lines_reverse(EncoderData *enc_data, uint8_t **lines)
-{
-    uint8_t *data;
-    uint32_t data_size;
-
-    if (!enc_data->u.lines_data.next) {
-        return 0;
-    }
-
-    QXLDataChunk *chunk = (QXLDataChunk *)enc_data->u.lines_data.enc_get_virt(
-                          enc_data->u.lines_data.enc_get_virt_opaque,
-                          enc_data->u.lines_data.next,
-                          sizeof(QXLDataChunk), enc_data->u.lines_data.group_id);
-
-    data_size = chunk->data_size;
-    data = chunk->data;
-
-    if (data_size % enc_data->u.lines_data.stride) {
-        return 0;
-    }
-
-    enc_data->u.lines_data.enc_validate_virt(enc_data->u.lines_data.enc_validate_virt_opaque,
-                                             (unsigned long)data,
-                                             enc_data->u.lines_data.next, data_size,
-                                             enc_data->u.lines_data.group_id);
-
-    enc_data->u.lines_data.next = chunk->prev_chunk;
-    *lines = data + data_size - enc_data->u.lines_data.stride;
-    return data_size / enc_data->u.lines_data.stride;
-}
-
-static int quic_usr_more_lines_reverse(QuicUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((QuicData *)usr)->data);
-    return encoder_usr_more_lines_reverse(usr_data, lines);
-
-}
-
-static int jpeg_usr_more_lines_reverse(JpegEncoderUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((JpegData *)usr)->data);
-    return encoder_usr_more_lines_reverse(usr_data, lines);
-}
-
-static int encoder_usr_more_lines_unstable(EncoderData *enc_data, uint8_t **out_lines)
-{
-    if (!enc_data->u.unstable_lines_data.lines) {
-        return 0;
-    }
-    uint8_t *src = enc_data->u.unstable_lines_data.next;
-    int lines = MIN(enc_data->u.unstable_lines_data.lines,
-                    enc_data->u.unstable_lines_data.max_lines_bunch);
-    enc_data->u.unstable_lines_data.lines -= lines;
-    uint8_t *end = src + lines * enc_data->u.unstable_lines_data.src_stride;
-    enc_data->u.unstable_lines_data.next = end;
-
-    uint8_t *out = (uint8_t *)enc_data->u.unstable_lines_data.input_bufs[
-                enc_data->u.unstable_lines_data.input_bufs_pos++ & 1]->buf;
-    uint8_t *dest = out;
-    for (; src != end; src += enc_data->u.unstable_lines_data.src_stride,
-                       dest += enc_data->u.unstable_lines_data.dest_stride) {
-        memcpy(dest, src, enc_data->u.unstable_lines_data.dest_stride);
-    }
-    *out_lines = out;
-    return lines;
-}
-
-static int quic_usr_more_lines_unstable(QuicUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((QuicData *)usr)->data);
-    return encoder_usr_more_lines_unstable(usr_data, lines);
-}
-
-static int jpeg_usr_more_lines_unstable(JpegEncoderUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((JpegData *)usr)->data);
-    return encoder_usr_more_lines_unstable(usr_data, lines);
-}
-
-static int quic_usr_no_more_lines(QuicUsrContext *usr, uint8_t **lines)
-{
-    return 0;
-}
-
-static int lz_usr_no_more_lines(LzUsrContext *usr, uint8_t **lines)
-{
-    return 0;
-}
-
-static int glz_usr_no_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
-{
-    return 0;
-}
-
-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);
@@ -5869,81 +5587,34 @@ static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBi
 {
     double score = 0.0;
     int num_samples = 0;
-
-    if ((bitmap->flags & QXL_BITMAP_DIRECT)) {
-        uint32_t x;
-        uint32_t y;
-
+    int num_lines;
+    double chunk_score = 0.0;
+    int chunk_num_samples = 0;
+    uint32_t x, i;
+    SpiceChunk *chunk;
+
+    chunk = bitmap->data->chunk;
+    for (i = 0; i < bitmap->data->num_chunks; i++) {
+        num_lines = chunk[i].len / bitmap->stride;
         x = bitmap->x;
-        y = bitmap->y;
         switch (bitmap->format) {
-        case SPICE_BITMAP_FMT_16BIT: {
-            uint8_t *lines = (uint8_t*)get_virt(&worker->mem_slots, bitmap->data, x * y *
-                                                sizeof(rgb16_pixel_t), group_id);
-            compute_lines_gradual_score_rgb16((rgb16_pixel_t*)lines, x, y, &score, &num_samples);
+        case SPICE_BITMAP_FMT_16BIT:
+            compute_lines_gradual_score_rgb16((rgb16_pixel_t *)chunk[i].data, x, num_lines,
+                                              &chunk_score, &chunk_num_samples);
+        case SPICE_BITMAP_FMT_24BIT:
+            compute_lines_gradual_score_rgb24((rgb24_pixel_t *)chunk[i].data, x, num_lines,
+                                              &chunk_score, &chunk_num_samples);
             break;
-        }
-        case SPICE_BITMAP_FMT_24BIT: {
-            uint8_t *lines = (uint8_t*)get_virt(&worker->mem_slots, bitmap->data, x * y *
-                                                sizeof(rgb24_pixel_t), group_id);
-            compute_lines_gradual_score_rgb24((rgb24_pixel_t*)lines, x, y, &score, &num_samples);
-            break;
-        }
         case SPICE_BITMAP_FMT_32BIT:
-        case SPICE_BITMAP_FMT_RGBA: {
-            uint8_t *lines = (uint8_t*)get_virt(&worker->mem_slots, bitmap->data, x * y *
-                                                sizeof(rgb32_pixel_t), group_id);
-            compute_lines_gradual_score_rgb32((rgb32_pixel_t*)lines, x, y, &score, &num_samples);
+        case SPICE_BITMAP_FMT_RGBA:
+            compute_lines_gradual_score_rgb32((rgb32_pixel_t *)chunk[i].data, x, num_lines,
+                                              &chunk_score, &chunk_num_samples);
             break;
-        }
         default:
             red_error("invalid bitmap format (not RGB) %u", bitmap->format);
         }
-    } else {
-        QXLDataChunk *chunk = NULL;
-        int num_lines;
-        double chunk_score = 0.0;
-        int chunk_num_samples = 0;
-        uint32_t x;
-        SPICE_ADDRESS relative_address = bitmap->data;
-
-        while (relative_address) {
-            chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, relative_address, sizeof(QXLDataChunk),
-                                             group_id);
-            num_lines = chunk->data_size / bitmap->stride;
-            x = bitmap->x;
-            switch (bitmap->format) {
-            case SPICE_BITMAP_FMT_16BIT:
-                validate_virt(&worker->mem_slots, (unsigned long)chunk->data,
-                              get_memslot_id(&worker->mem_slots, relative_address),
-                              sizeof(rgb16_pixel_t) * x * num_lines, group_id);
-                compute_lines_gradual_score_rgb16((rgb16_pixel_t*)chunk->data, x, num_lines,
-                                                  &chunk_score, &chunk_num_samples);
-                break;
-            case SPICE_BITMAP_FMT_24BIT:
-                validate_virt(&worker->mem_slots, (unsigned long)chunk->data,
-                              get_memslot_id(&worker->mem_slots, relative_address),
-                              sizeof(rgb24_pixel_t) * x * num_lines, group_id);
-                compute_lines_gradual_score_rgb24((rgb24_pixel_t*)chunk->data, x, num_lines,
-                                                  &chunk_score, &chunk_num_samples);
-                break;
-            case SPICE_BITMAP_FMT_32BIT:
-            case SPICE_BITMAP_FMT_RGBA:
-                validate_virt(&worker->mem_slots, (unsigned long)chunk->data,
-                              get_memslot_id(&worker->mem_slots, relative_address),
-                              sizeof(rgb32_pixel_t) *  x * num_lines, group_id);
-                compute_lines_gradual_score_rgb32((rgb32_pixel_t*)chunk->data, x, num_lines,
-                                                  &chunk_score, &chunk_num_samples);
-                break;
-            default:
-                red_error("invalid bitmap format (not RGB) %u", bitmap->format);
-            }
-
-            score += chunk_score;
-            num_samples += chunk_num_samples;
-
-            relative_address = chunk->next_chunk;
-        }
+        score += chunk_score;
+        num_samples += chunk_num_samples;
     }
 
     ASSERT(num_samples);
@@ -6013,7 +5684,7 @@ typedef struct compress_send_data_t {
 
 
 static inline int red_glz_compress_image(DisplayChannel *display_channel,
-                                         RedImage *dest, SpiceBitmap *src, Drawable *drawable,
+                                         SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
                                          compress_send_data_t* o_comp_data)
 {
     RedWorker *worker = (RedWorker *)display_channel->base.worker;
@@ -6027,7 +5698,6 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     RedGlzDrawable *glz_drawable;
     GlzDrawableInstanceItem *glz_drawable_instance;
     uint8_t *lines;
-    unsigned int num_lines;
     int glz_size;
     int zlib_size;
 
@@ -6044,25 +5714,14 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     glz_drawable = red_display_get_glz_drawable(display_channel, drawable);
     glz_drawable_instance = red_display_add_glz_drawable_instance(glz_drawable);
 
-    if ((src->flags & QXL_BITMAP_DIRECT)) {
-        glz_data->usr.more_lines = glz_usr_no_more_lines;
-        lines = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, drawable->group_id);
-        num_lines = src->y;
-    } else {
-        glz_data->data.u.lines_data.enc_get_virt = cb_get_virt;
-        glz_data->data.u.lines_data.enc_get_virt_opaque = &worker->mem_slots;
-        glz_data->data.u.lines_data.enc_validate_virt = cb_validate_virt;
-        glz_data->data.u.lines_data.enc_validate_virt_opaque = &worker->mem_slots;
-        glz_data->data.u.lines_data.stride = src->stride;
-        glz_data->data.u.lines_data.next = src->data;
-        glz_data->data.u.lines_data.group_id = drawable->group_id;
-        glz_data->usr.more_lines = glz_usr_more_lines;
-        lines = NULL;
-        num_lines = 0;
-    }
+    glz_data->data.u.lines_data.chunks = src->data;
+    glz_data->data.u.lines_data.stride = src->stride;
+    glz_data->data.u.lines_data.next = 0;
+    glz_data->data.u.lines_data.reverse = 0;
+    glz_data->usr.more_lines = glz_usr_more_lines;
 
     glz_size = glz_encode(display_channel->glz, type, src->x, src->y,
-                          (src->flags & QXL_BITMAP_TOP_DOWN), lines, num_lines,
+                          (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), lines, 0,
                           src->stride, (uint8_t*)glz_data->data.bufs_head->buf,
                           sizeof(glz_data->data.bufs_head->buf),
                           glz_drawable_instance,
@@ -6107,8 +5766,8 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     }
 
     dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
-    dest->zlib_glz.glz_data_size = glz_size;
-    dest->zlib_glz.data_size = zlib_size;
+    dest->u.zlib_glz.glz_data_size = glz_size;
+    dest->u.zlib_glz.data_size = zlib_size;
 
     o_comp_data->comp_buf = zlib_data->data.bufs_head;
     o_comp_data->comp_buf_size = zlib_size;
@@ -6117,7 +5776,7 @@ static inline int red_glz_compress_image(DisplayChannel *display_channel,
     return TRUE;
 glz:
     dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
-    dest->lz_rgb.data_size = glz_size;
+    dest->u.lz_rgb.data_size = glz_size;
 
     o_comp_data->comp_buf = glz_data->data.bufs_head;
     o_comp_data->comp_buf_size = glz_size;
@@ -6126,7 +5785,7 @@ glz:
 }
 
 static inline int red_lz_compress_image(DisplayChannel *display_channel,
-                                        RedImage *dest, SpiceBitmap *src,
+                                        SpiceImage *dest, SpiceBitmap *src,
                                         compress_send_data_t* o_comp_data, uint32_t group_id)
 {
     RedWorker *worker = display_channel->base.worker;
@@ -6158,27 +5817,16 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
         return FALSE;
     }
 
-    if ((src->flags & QXL_BITMAP_DIRECT)) {
-        lz_data->usr.more_lines = lz_usr_no_more_lines;
-        size = lz_encode(lz, type, 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, (uint8_t*)lz_data->data.bufs_head->buf,
-                         sizeof(lz_data->data.bufs_head->buf));
-    } 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;
+    lz_data->data.u.lines_data.chunks = src->data;
+    lz_data->data.u.lines_data.stride = src->stride;
+    lz_data->data.u.lines_data.next = 0;
+    lz_data->data.u.lines_data.reverse = 0;
+    lz_data->usr.more_lines = lz_usr_more_lines;
 
-        size = lz_encode(lz, type, src->x, src->y, (src->flags & QXL_BITMAP_TOP_DOWN),
-                         NULL, 0, src->stride,
-                         (uint8_t*)lz_data->data.bufs_head->buf,
-                         sizeof(lz_data->data.bufs_head->buf));
-    }
+    size = lz_encode(lz, type, src->x, src->y, (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+                     NULL, 0, src->stride,
+                     (uint8_t*)lz_data->data.bufs_head->buf,
+                     sizeof(lz_data->data.bufs_head->buf));
 
     // the compressed buffer is bigger than the original data
     if (size > (src->y * src->stride)) {
@@ -6187,21 +5835,22 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
 
     if (BITMAP_FMT_IS_RGB[src->format]) {
         dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
-        dest->lz_rgb.data_size = size;
+        dest->u.lz_rgb.data_size = size;
 
         o_comp_data->comp_buf = lz_data->data.bufs_head;
         o_comp_data->comp_buf_size = size;
     } else {
         dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
-        dest->lz_plt.data_size = size;
-        dest->lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
-        dest->lz_plt.palette = src->palette;
+        dest->u.lz_plt.data_size = size;
+        dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
+        dest->u.lz_plt.palette = src->palette;
+        dest->u.lz_plt.palette_id = src->palette->unique;
 
         o_comp_data->comp_buf = lz_data->data.bufs_head;
         o_comp_data->comp_buf_size = size;
 
-        fill_palette(display_channel, &(dest->lz_plt.palette), &(dest->lz_plt.flags),
-                     group_id, &o_comp_data->lzplt_palette);
+        fill_palette(display_channel, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
+        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
     }
 
     stat_compress_add(&display_channel->lz_stat, start_time, src->stride * src->y,
@@ -6209,7 +5858,7 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
     return TRUE;
 }
 
-static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *dest,
+static int red_jpeg_compress_image(DisplayChannel *display_channel, SpiceImage *dest,
                                    SpiceBitmap *src, compress_send_data_t* o_comp_data,
                                    uint32_t group_id)
 {
@@ -6224,6 +5873,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
     int alpha_lz_size = 0;
     int comp_head_filled;
     int comp_head_left;
+    int stride;
     uint8_t *lz_out_start_byte;
 
 #ifdef COMPRESS_STAT
@@ -6267,83 +5917,26 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
         return FALSE;
     }
 
-    if ((src->flags & QXL_BITMAP_DIRECT)) {
-        int stride;
-        uint8_t *data;
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
 
-        if (!(src->flags & QXL_BITMAP_TOP_DOWN)) {
-            data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id) +
-                   src->stride * (src->y - 1);
-            stride = -src->stride;
-        } else {
-            data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id);
-            stride = src->stride;
-        }
-
-        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;
-            jpeg_data->data.u.unstable_lines_data.lines = src->y;
-            jpeg_data->data.u.unstable_lines_data.input_bufs_pos = 0;
-            if (!(jpeg_data->data.u.unstable_lines_data.input_bufs[0] =
-                                            red_display_alloc_compress_buf(display_channel)) ||
-                !(jpeg_data->data.u.unstable_lines_data.input_bufs[1] =
-                                            red_display_alloc_compress_buf(display_channel))) {
-                return FALSE;
-            }
-            jpeg_data->data.u.unstable_lines_data.max_lines_bunch =
-                                 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;
-            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;
-            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));
-        }
+    jpeg_data->data.u.lines_data.chunks = src->data;
+    jpeg_data->data.u.lines_data.stride = src->stride;
+    jpeg_data->usr.more_lines = jpeg_usr_more_lines;
+    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+        jpeg_data->data.u.lines_data.next = 0;
+        jpeg_data->data.u.lines_data.reverse = 0;
+        stride = src->stride;
     } else {
-        int stride;
-
-        if ((src->flags & QXL_BITMAP_UNSTABLE)) {
-            red_printf_once("unexpected unstable bitmap");
-            return FALSE;
-        }
-        jpeg_data->data.u.lines_data.enc_get_virt = cb_get_virt;
-        jpeg_data->data.u.lines_data.enc_get_virt_opaque = &worker->mem_slots;
-        jpeg_data->data.u.lines_data.enc_validate_virt = cb_validate_virt;
-        jpeg_data->data.u.lines_data.enc_validate_virt_opaque = &worker->mem_slots;
-        jpeg_data->data.u.lines_data.stride = src->stride;
-        jpeg_data->data.u.lines_data.group_id = group_id;
-
-        if ((src->flags & QXL_BITMAP_TOP_DOWN)) {
-            jpeg_data->data.u.lines_data.next = src->data;
-            jpeg_data->usr.more_lines = jpeg_usr_more_lines;
-            stride = src->stride;
-        } else {
-            SPICE_ADDRESS prev_addr = src->data;
-            QXLDataChunk *chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, src->data,
-                                                           sizeof(QXLDataChunk), group_id);
-            while (chunk->next_chunk) {
-                prev_addr = chunk->next_chunk;
-                chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, chunk->next_chunk, sizeof(QXLDataChunk),
-                                                 group_id);
-                ASSERT(chunk->prev_chunk);
-            }
-            jpeg_data->data.u.lines_data.next = (SPICE_ADDRESS)prev_addr;
-            jpeg_data->usr.more_lines = jpeg_usr_more_lines_reverse;
-            stride = -src->stride;
-        }
-        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));
+        jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
+        jpeg_data->data.u.lines_data.reverse = 1;
+        stride = -src->stride;
     }
+    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 (jpeg_size > (src->y * src->stride)) {
@@ -6352,7 +5945,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
 
     if (!has_alpha) {
         dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
-        dest->jpeg.data_size = jpeg_size;
+        dest->u.jpeg.data_size = jpeg_size;
 
         o_comp_data->comp_buf = jpeg_data->data.bufs_head;
         o_comp_data->comp_buf_size = jpeg_size;
@@ -6372,32 +5965,17 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
 
      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);
-    }
-   
+     lz_data->data.u.lines_data.chunks = src->data;
+     lz_data->data.u.lines_data.stride = src->stride;
+     lz_data->data.u.lines_data.next = 0;
+     lz_data->data.u.lines_data.reverse = 0;
+     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 & SPICE_BITMAP_FLAGS_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)) {
@@ -6405,13 +5983,13 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
     }
 
     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->u.jpeg_alpha.flags = 0;
+    if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
+        dest->u.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;
+    dest->u.jpeg_alpha.jpeg_size = jpeg_size;
+    dest->u.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 = jpeg_size + alpha_lz_size;
@@ -6421,7 +5999,7 @@ static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *de
     return TRUE;
 }
 
-static inline int red_quic_compress_image(DisplayChannel *display_channel, RedImage *dest,
+static inline int red_quic_compress_image(DisplayChannel *display_channel, SpiceImage *dest,
                                           SpiceBitmap *src, compress_send_data_t* o_comp_data,
                                           uint32_t group_id)
 {
@@ -6429,7 +6007,7 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, RedIm
     QuicData *quic_data = &worker->quic_data;
     QuicContext *quic = worker->quic;
     QuicImageType type;
-    int size;
+    int size, stride;
 
 #ifdef COMPRESS_STAT
     stat_time_t start_time = stat_now();
@@ -6471,80 +6049,25 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, RedIm
         return FALSE;
     }
 
-    if ((src->flags & QXL_BITMAP_DIRECT)) {
-        int stride;
-        uint8_t *data;
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
 
-        if (!(src->flags & QXL_BITMAP_TOP_DOWN)) {
-            data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id) +
-                   src->stride * (src->y - 1);
-            stride = -src->stride;
-        } else {
-            data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id);
-            stride = src->stride;
-        }
-
-        if ((src->flags & QXL_BITMAP_UNSTABLE)) {
-            quic_data->data.u.unstable_lines_data.next = data;
-            quic_data->data.u.unstable_lines_data.src_stride = stride;
-            quic_data->data.u.unstable_lines_data.dest_stride = src->stride;
-            quic_data->data.u.unstable_lines_data.lines = src->y;
-            quic_data->data.u.unstable_lines_data.input_bufs_pos = 0;
-            if (!(quic_data->data.u.unstable_lines_data.input_bufs[0] =
-                                            red_display_alloc_compress_buf(display_channel)) ||
-                !(quic_data->data.u.unstable_lines_data.input_bufs[1] =
-                                            red_display_alloc_compress_buf(display_channel))) {
-                return FALSE;
-            }
-            quic_data->data.u.unstable_lines_data.max_lines_bunch =
-                                 sizeof(quic_data->data.u.unstable_lines_data.input_bufs[0]->buf) /
-                                 quic_data->data.u.unstable_lines_data.dest_stride;
-            quic_data->usr.more_lines = quic_usr_more_lines_unstable;
-            size = quic_encode(quic, type, src->x, src->y, NULL, 0, src->stride,
-                               quic_data->data.bufs_head->buf,
-                               sizeof(quic_data->data.bufs_head->buf) >> 2);
-        } else {
-            quic_data->usr.more_lines = quic_usr_no_more_lines;
-            size = quic_encode(quic, type, src->x, src->y, data, src->y, stride,
-                               quic_data->data.bufs_head->buf,
-                               sizeof(quic_data->data.bufs_head->buf) >> 2);
-        }
+    quic_data->data.u.lines_data.chunks = src->data;
+    quic_data->data.u.lines_data.stride = src->stride;
+    quic_data->usr.more_lines = quic_usr_more_lines;
+    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+        quic_data->data.u.lines_data.next = 0;
+        quic_data->data.u.lines_data.reverse = 0;
+        stride = src->stride;
     } else {
-        int stride;
-
-        if ((src->flags & QXL_BITMAP_UNSTABLE)) {
-            red_printf_once("unexpected unstable bitmap");
-            return FALSE;
-        }
-        quic_data->data.u.lines_data.enc_get_virt = cb_get_virt;
-        quic_data->data.u.lines_data.enc_get_virt_opaque = &worker->mem_slots;
-        quic_data->data.u.lines_data.enc_validate_virt = cb_validate_virt;
-        quic_data->data.u.lines_data.enc_validate_virt_opaque = &worker->mem_slots;
-        quic_data->data.u.lines_data.stride = src->stride;
-        quic_data->data.u.lines_data.group_id = group_id;
-
-        if ((src->flags & QXL_BITMAP_TOP_DOWN)) {
-            quic_data->data.u.lines_data.next = src->data;
-            quic_data->usr.more_lines = quic_usr_more_lines;
-            stride = src->stride;
-        } else {
-            SPICE_ADDRESS prev_addr = src->data;
-            QXLDataChunk *chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, src->data,
-                                                           sizeof(QXLDataChunk), group_id);
-            while (chunk->next_chunk) {
-                prev_addr = chunk->next_chunk;
-                chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, chunk->next_chunk, sizeof(QXLDataChunk),
-                                                 group_id);
-                ASSERT(chunk->prev_chunk);
-            }
-            quic_data->data.u.lines_data.next = prev_addr;
-            quic_data->usr.more_lines = quic_usr_more_lines_reverse;
-            stride = -src->stride;
-        }
-        size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
-                           quic_data->data.bufs_head->buf,
-                           sizeof(quic_data->data.bufs_head->buf) >> 2);
+        quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
+        quic_data->data.u.lines_data.reverse = 1;
+        stride = -src->stride;
     }
+    size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
+                       quic_data->data.bufs_head->buf,
+                       sizeof(quic_data->data.bufs_head->buf) >> 2);
 
     // the compressed buffer is bigger than the original data
     if ((size << 2) > (src->y * src->stride)) {
@@ -6552,7 +6075,7 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, RedIm
     }
 
     dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
-    dest->quic.data_size = size << 2;
+    dest->u.quic.data_size = size << 2;
 
     o_comp_data->comp_buf = quic_data->data.bufs_head;
     o_comp_data->comp_buf_size = size << 2;
@@ -6565,7 +6088,7 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, RedIm
 #define MIN_SIZE_TO_COMPRESS 54
 #define MIN_DIMENSION_TO_QUIC 3
 static inline int red_compress_image(DisplayChannel *display_channel,
-                                     RedImage *dest, SpiceBitmap *src, Drawable *drawable,
+                                     SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
                                      int can_lossy,
                                      compress_send_data_t* o_comp_data)
 {
@@ -6587,7 +6110,7 @@ static inline int red_compress_image(DisplayChannel *display_channel,
             lz doesn't handle (1) bitmaps with strides that are larger than the width
             of the image in bytes (2) unstable bitmaps
         */
-        if (_stride_is_extra(src) || (src->flags & QXL_BITMAP_UNSTABLE)) {
+        if (_stride_is_extra(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
             if ((image_compression == SPICE_IMAGE_COMPRESS_LZ) ||
                 (image_compression == SPICE_IMAGE_COMPRESS_GLZ) ||
                 BITMAP_FMT_IS_PLT[src->format]) {
@@ -6676,14 +6199,14 @@ static inline int red_compress_image(DisplayChannel *display_channel,
 }
 
 static inline void red_display_add_image_to_pixmap_cache(DisplayChannel *display_channel,
-                                                         QXLImage *qxl_image, RedImage *io_image,
+                                                         SpiceImage *image, SpiceImage *io_image,
                                                          int is_lossy)
 {
-    if ((qxl_image->descriptor.flags & QXL_IMAGE_CACHE)) {
-        ASSERT(qxl_image->descriptor.width * qxl_image->descriptor.height > 0);
+    if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
+        ASSERT(image->descriptor.width * image->descriptor.height > 0);
         if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) {
-            if (pixmap_cache_add(display_channel->pixmap_cache, qxl_image->descriptor.id,
-                                 qxl_image->descriptor.width * qxl_image->descriptor.height, is_lossy,
+            if (pixmap_cache_add(display_channel->pixmap_cache, image->descriptor.id,
+                                 image->descriptor.width * image->descriptor.height, is_lossy,
                                  display_channel)) {
                 io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
                 stat_inc_counter(display_channel->add_to_cache_counter, 1);
@@ -6708,36 +6231,22 @@ typedef enum {
 /* if the number of times fill_bits can be called per one qxl_drawable increases -
    MAX_LZ_DRAWABLE_INSTANCES must be increased as well */
 static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *m,
-                              QXLPHYSICAL in_bitmap, Drawable *drawable, int can_lossy)
+                              SpiceImage *simage, Drawable *drawable, int can_lossy)
 {
     RedChannel *channel = &display_channel->base;
     RedWorker *worker = channel->worker;
-    RedImage image;
-    QXLImage *qxl_image;
-    uint8_t *data;
-    int memslot_id;
+    SpiceImage image;
     compress_send_data_t comp_send_data = {0};
     SpiceMarshaller *bitmap_palette_out, *data_out, *lzplt_palette_out;
 
-    if (in_bitmap == 0) {
+    if (simage == NULL) {
         ASSERT(drawable->self_bitmap);
-        qxl_image = (QXLImage *)drawable->self_bitmap;
-    } else {
-        qxl_image = (QXLImage *)get_virt(&worker->mem_slots, in_bitmap, sizeof(QXLImage),
-                                         drawable->group_id);
+        simage = drawable->self_bitmap;
     }
 
-    image.descriptor.id = qxl_image->descriptor.id;
-    image.descriptor.type = qxl_image->descriptor.type;
-    image.descriptor.flags = 0;
-    if (qxl_image->descriptor.flags & QXL_IMAGE_HIGH_BITS_SET) {
-        image.descriptor.flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
-    }
-    image.descriptor.width = qxl_image->descriptor.width;
-    image.descriptor.height = qxl_image->descriptor.height;
+    image.descriptor = simage->descriptor;
 
-    memslot_id = get_memslot_id(&worker->mem_slots, in_bitmap);
-    if ((qxl_image->descriptor.flags & QXL_IMAGE_CACHE)) {
+    if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         int lossy_cache_item;
         if (pixmap_cache_hit(display_channel->pixmap_cache, image.descriptor.id,
                              &lossy_cache_item, display_channel)) {
@@ -6750,7 +6259,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
                     // will be retrieved as lossless by another display channel.
                     image.descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS;
                 }
-                spice_marshall_Image(m, (SpiceImageDescriptor *)&image,
+                spice_marshall_Image(m, &image,
                                      &bitmap_palette_out, &data_out, &lzplt_palette_out);
                 ASSERT(bitmap_palette_out == NULL);
                 ASSERT(data_out == NULL);
@@ -6758,19 +6267,19 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
                 stat_inc_counter(display_channel->cache_hits_counter, 1);
                 return FILL_BITS_TYPE_CACHE;
             } else {
-                pixmap_cache_set_lossy(display_channel->pixmap_cache, qxl_image->descriptor.id,
+                pixmap_cache_set_lossy(display_channel->pixmap_cache, simage->descriptor.id,
                                        FALSE);
                 image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME;
             }
         }
     }
 
-    switch (qxl_image->descriptor.type) {
+    switch (simage->descriptor.type) {
     case SPICE_IMAGE_TYPE_SURFACE: {
         int surface_id;
         RedSurface *surface;
 
-        surface_id = qxl_image->surface_image.surface_id;
+        surface_id = simage->u.surface.surface_id;
         validate_surface(worker, surface_id);
 
         surface = &worker->surfaces[surface_id];
@@ -6779,68 +6288,63 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
         image.descriptor.width = surface->context.width;
         image.descriptor.height = surface->context.height;
 
-        image.surface.surface_id = surface_id;
-        spice_marshall_Image(m, (SpiceImageDescriptor *)&image,
+        image.u.surface.surface_id = surface_id;
+        spice_marshall_Image(m, &image,
                              &bitmap_palette_out, &data_out, &lzplt_palette_out);
         ASSERT(bitmap_palette_out == NULL);
         ASSERT(data_out == NULL);
         ASSERT(lzplt_palette_out == NULL);
         return FILL_BITS_TYPE_SURFACE;
     }
-    case SPICE_IMAGE_TYPE_BITMAP:
+    case SPICE_IMAGE_TYPE_BITMAP: {
+        SpiceBitmap *bitmap = &image.u.bitmap;
 #ifdef DUMP_BITMAP
-        dump_bitmap(display_channel->base.worker, &qxl_image->bitmap, drawable->group_id);
+        dump_bitmap(display_channel->base.worker, &simage->u.bitmap, drawable->group_id);
 #endif
         /* Images must be added to the cache only after they are compressed
            in order to prevent starvation in the client between pixmap_cache and
            global dictionary (in cases of multiple monitors) */
-        if (!red_compress_image(display_channel, &image, &qxl_image->bitmap,
+        if (!red_compress_image(display_channel, &image, &simage->u.bitmap,
                                 drawable, can_lossy, &comp_send_data)) {
-            uint32_t y;
+            uint32_t y, i;
             uint32_t stride;
-            SPICE_ADDRESS image_data;
             SpicePalette *palette;
+            SpiceChunk *chunk;
 
-            red_display_add_image_to_pixmap_cache(display_channel, qxl_image, &image, FALSE);
+            red_display_add_image_to_pixmap_cache(display_channel, simage, &image, FALSE);
 
-            image.bitmap = qxl_image->bitmap;
-            y = image.bitmap.y;
-            stride = image.bitmap.stride;
-            image_data = image.bitmap.data;
-            image.bitmap.flags = image.bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
+            *bitmap = simage->u.bitmap;
+            y = bitmap->y;
+            stride = bitmap->stride;
+            bitmap->flags = bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
 
-            fill_palette(display_channel, &image.bitmap.palette, &image.bitmap.flags,
-                         drawable->group_id, &palette);
-            spice_marshall_Image(m, (SpiceImageDescriptor *)&image,
+            palette = bitmap->palette;
+            fill_palette(display_channel, palette, &bitmap->flags);
+            spice_marshall_Image(m, &image,
                                  &bitmap_palette_out, &data_out, &lzplt_palette_out);
             ASSERT(lzplt_palette_out == NULL);
 
-            if (palette) {
+            if (bitmap_palette_out && palette) {
                 spice_marshall_Palette(bitmap_palette_out, palette);
             }
 
-            if (qxl_image->bitmap.flags & QXL_BITMAP_DIRECT) {
-                data = (uint8_t *)get_virt(&worker->mem_slots, image_data, stride * y, drawable->group_id);
-                spice_marshaller_add_ref(data_out, data, y * stride);
-            } else {
-                data = (uint8_t *)get_virt(&worker->mem_slots, image_data, sizeof(QXLDataChunk),
-                                           drawable->group_id);
-                marshaller_add_chunk(worker, data_out, (QXLDataChunk *)data,
-                                     y * stride, memslot_id, drawable->group_id);
+            chunk = bitmap->data->chunk;
+            for (i = 0; i < bitmap->data->num_chunks; i++) {
+                spice_marshaller_add_ref(data_out, chunk[i].data, chunk[i].len);
             }
             return FILL_BITS_TYPE_BITMAP;
         } else {
-            red_display_add_image_to_pixmap_cache(display_channel, qxl_image, &image,
+            red_display_add_image_to_pixmap_cache(display_channel, simage, &image,
                                                   comp_send_data.is_lossy);
 
-            spice_marshall_Image(m, (SpiceImageDescriptor *)&image,
+            spice_marshall_Image(m, &image,
                                  &bitmap_palette_out, &data_out, &lzplt_palette_out);
             ASSERT(bitmap_palette_out == NULL);
 
             marshaller_add_compressed(worker, m, comp_send_data.comp_buf,
                                       comp_send_data.comp_buf_size);
 
-            if (comp_send_data.lzplt_palette) {
+            if (lzplt_palette_out && comp_send_data.lzplt_palette) {
                 spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette);
             }
 
@@ -6849,16 +6353,15 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
                                               FILL_BITS_TYPE_COMPRESS_LOSSLESS);
         }
         break;
+    }
     case SPICE_IMAGE_TYPE_QUIC:
-        red_display_add_image_to_pixmap_cache(display_channel, qxl_image, &image, FALSE);
-        image.quic = qxl_image->quic;
-        spice_marshall_Image(m, (SpiceImageDescriptor *)&image,
+        red_display_add_image_to_pixmap_cache(display_channel, simage, &image, FALSE);
+        image.u.quic = simage->u.quic;
+        spice_marshall_Image(m, &image,
                              &bitmap_palette_out, &data_out, &lzplt_palette_out);
         ASSERT(bitmap_palette_out == NULL);
         ASSERT(lzplt_palette_out == NULL);
-        marshaller_add_chunk(worker, m, (QXLDataChunk *)qxl_image->quic.data,
-                             qxl_image->quic.data_size,
-                             memslot_id, drawable->group_id);
+        spice_marshaller_add_ref_chunks(m, image.u.quic.data);
         return FILL_BITS_TYPE_COMPRESS_LOSSLESS;
     default:
         red_error("invalid image type %u", image.descriptor.type);
@@ -6866,7 +6369,7 @@ static FillBitsType fill_bits(DisplayChannel *display_channel, SpiceMarshaller *
 }
 
 static void fill_mask(DisplayChannel *display_channel, SpiceMarshaller *m,
-                      SPICE_ADDRESS mask_bitmap, Drawable *drawable)
+                      SpiceImage *mask_bitmap, Drawable *drawable)
 {
     if (mask_bitmap && m) {
         if (display_channel->base.worker->image_compression != SPICE_IMAGE_COMPRESS_OFF) {
@@ -7010,26 +6513,20 @@ static int is_surface_area_lossy(DisplayChannel *display_channel, uint32_t surfa
    to the client, returns false. "area" is for surfaces. If area = NULL,
    all the surface is considered. out_lossy_data will hold info about the bitmap, and its lossy
    area in case it is lossy and part of a surface. */
-static int is_bitmap_lossy(DisplayChannel *display_channel, SPICE_ADDRESS bitmap, SpiceRect *area,
+static int is_bitmap_lossy(DisplayChannel *display_channel, SpiceImage *image, SpiceRect *area,
                            Drawable *drawable, BitmapData *out_data)
 {
-    RedWorker *worker = display_channel->base.worker;
-    QXLImage *qxl_image;
-
-    if (bitmap == 0) {
+    if (image == NULL) {
         // self bitmap
         out_data->type = BITMAP_DATA_TYPE_BITMAP;
         return FALSE;
     }
 
-    qxl_image = (QXLImage *)get_virt(&worker->mem_slots, bitmap, sizeof(QXLImage),
-                                     drawable->group_id);
-
-    if ((qxl_image->descriptor.flags & QXL_IMAGE_CACHE)) {
+    if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
         int is_hit_lossy;
 
-        out_data->id = qxl_image->descriptor.id;
-        if (pixmap_cache_hit(display_channel->pixmap_cache, qxl_image->descriptor.id,
+        out_data->id = image->descriptor.id;
+        if (pixmap_cache_hit(display_channel->pixmap_cache, image->descriptor.id,
                              &is_hit_lossy, display_channel)) {
             out_data->type = BITMAP_DATA_TYPE_CACHE;
             if (is_hit_lossy) {
@@ -7044,14 +6541,14 @@ static int is_bitmap_lossy(DisplayChannel *display_channel, SPICE_ADDRESS bitmap
          out_data->type = BITMAP_DATA_TYPE_BITMAP;
     }
 
-    if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_SURFACE) {
+    if (image->descriptor.type != SPICE_IMAGE_TYPE_SURFACE) {
         return FALSE;
     }
 
     out_data->type = BITMAP_DATA_TYPE_SURFACE;
-    out_data->id = qxl_image->surface_image.surface_id;
+    out_data->id = image->u.surface.surface_id;
 
-    if (is_surface_area_lossy(display_channel, qxl_image->surface_image.surface_id,
+    if (is_surface_area_lossy(display_channel, out_data->id,
                               area, &out_data->lossy_rect))
     {
         return TRUE;
@@ -8373,31 +7870,28 @@ static inline void red_unref_channel(RedChannel *channel)
     }
 }
 
-static inline uint8_t *red_get_image_line(RedWorker *worker, QXLDataChunk **chunk, int *offset,
-                                          int stride, long phys_delta, int memslot_id,
-                                          uint32_t group_id)
+static inline uint8_t *red_get_image_line(RedWorker *worker, SpiceChunks *chunks, size_t *offset,
+                                          int *chunk_nr, int stride)
 {
     uint8_t *ret;
-    uint32_t data_size;
+    SpiceChunk *chunk;
 
-    validate_virt(&worker->mem_slots, (unsigned long)*chunk, memslot_id, sizeof(QXLDataChunk),
-                  group_id);
-    data_size = (*chunk)->data_size;
-    validate_virt(&worker->mem_slots, (unsigned long)(*chunk)->data, memslot_id, data_size, group_id);
+    chunk = &chunks->chunk[*chunk_nr];
 
-    if (data_size == *offset) {
-        if ((*chunk)->next_chunk == 0) {
-            return NULL;
+    if (*offset == chunk->len) {
+        if (*chunk_nr == chunks->num_chunks - 1) {
+            return NULL; /* Last chunk */
         }
         *offset = 0;
-        *chunk = (QXLDataChunk *)((*chunk)->next_chunk + phys_delta);
+        (*chunk_nr)++;
+        chunk = &chunks->chunk[*chunk_nr];
     }
 
-    if (data_size - *offset < stride) {
+    if (chunk->len - *offset < stride) {
         red_printf("bad chunk alignment");
         return NULL;
     }
-    ret = (*chunk)->data + *offset;
+    ret = chunk->data + *offset;
     *offset += stride;
     return ret;
 }
@@ -8413,31 +7907,29 @@ static void red_display_unshare_stream_buf(DisplayChannel *display_channel)
 static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               long phys_delta, int memslot_id, int id,
-                               Stream *stream, uint32_t group_id)
+                               int id, Stream *stream)
 {
-    QXLDataChunk *chunk;
+    SpiceChunks *chunks;
     uint32_t image_stride;
     uint8_t *frame_row;
-    int offset;
-    int i, x;
+    size_t offset;
+    int i, x, chunk;
 
+    chunks = image->data;
     offset = 0;
-    chunk = (QXLDataChunk *)(image->data + phys_delta);
+    chunk = 0;
     image_stride = image->stride;
 
     const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
     for (i = 0; i < skip_lines; i++) {
-        red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
-                           group_id);
+        red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
     }
 
     const int image_height = src->bottom - src->top;
     const int image_width = src->right - src->left;
     for (i = 0; i < image_height; i++) {
         uint32_t *src_line =
-            (uint32_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
-                                           memslot_id, group_id);
+            (uint32_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
 
         if (!src_line) {
             return FALSE;
@@ -8462,31 +7954,29 @@ static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               long phys_delta, int memslot_id, int id,
-                               Stream *stream, uint32_t group_id)
+                               int id, Stream *stream)
 {
-    QXLDataChunk *chunk;
+    SpiceChunks *chunks;
     uint32_t image_stride;
     uint8_t *frame_row;
-    int offset;
-    int i;
+    size_t offset;
+    int i, chunk;
 
+    chunks = image->data;
     offset = 0;
-    chunk = (QXLDataChunk *)(image->data + phys_delta);
+    chunk = 0;
     image_stride = image->stride;
 
     const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
     for (i = 0; i < skip_lines; i++) {
-        red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
-                           group_id);
+        red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
     }
 
     const int image_height = src->bottom - src->top;
     const int image_width = src->right - src->left;
     for (i = 0; i < image_height; i++) {
         uint8_t *src_line =
-            (uint8_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
-                                           memslot_id, group_id);
+            (uint8_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
 
         if (!src_line) {
             return FALSE;
@@ -8505,31 +7995,29 @@ static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 static int red_rgb16bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               long phys_delta, int memslot_id, int id,
-                               Stream *stream, uint32_t group_id)
+                               int id, Stream *stream)
 {
-    QXLDataChunk *chunk;
+    SpiceChunks *chunks;
     uint32_t image_stride;
     uint8_t *frame_row;
-    int offset;
-    int i, x;
+    size_t offset;
+    int i, x, chunk;
 
+    chunks = image->data;
     offset = 0;
-    chunk = (QXLDataChunk *)(image->data + phys_delta);
+    chunk = 0;
     image_stride = image->stride;
 
     const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
     for (i = 0; i < skip_lines; i++) {
-        red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta, memslot_id,
-                           group_id);
+        red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
     }
 
     const int image_height = src->bottom - src->top;
     const int image_width = src->right - src->left;
     for (i = 0; i < image_height; i++) {
         uint16_t *src_line =
-            (uint16_t *)red_get_image_line(worker, &chunk, &offset, image_stride, phys_delta,
-                                           memslot_id, group_id);
+            (uint16_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
 
         if (!src_line) {
             return FALSE;
@@ -8556,10 +8044,9 @@ static int red_rgb16bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable *drawable)
 {
     Stream *stream = drawable->stream;
-    QXLImage *qxl_image;
+    SpiceImage *image;
     RedChannel *channel;
     RedWorker* worker;
-    unsigned long data;
     uint8_t *frame;
     size_t frame_stride;
     int n;
@@ -8569,11 +8056,9 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
 
     channel = &display_channel->base;
     worker = channel->worker;
-    qxl_image = (QXLImage *)get_virt(&worker->mem_slots,  drawable->red_drawable->u.copy.src_bitmap,
-                                     sizeof(QXLImage), drawable->group_id);
+    image = drawable->red_drawable->u.copy.src_bitmap;
 
-    if (qxl_image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP ||
-        (qxl_image->bitmap.flags & QXL_BITMAP_DIRECT)) {
+    if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
         return FALSE;
     }
 
@@ -8584,40 +8069,33 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
         return TRUE;
     }
 
-    data = qxl_image->bitmap.data;
     frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
     frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
 
-    switch (qxl_image->bitmap.format) {
+    switch (image->u.bitmap.format) {
     case SPICE_BITMAP_FMT_32BIT:
         if (!red_rgb32bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
-                                &qxl_image->bitmap, frame, frame_stride,
-                                get_virt_delta(&worker->mem_slots, data, drawable->group_id),
-                                get_memslot_id(&worker->mem_slots, data),
-                                stream - worker->streams_buf, stream, drawable->group_id)) {
+                                &image->u.bitmap, frame, frame_stride,
+                                stream - worker->streams_buf, stream)) {
             return FALSE;
         }
         break;
     case SPICE_BITMAP_FMT_16BIT:
         if (!red_rgb16bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
-                                &qxl_image->bitmap, frame, frame_stride,
-                                get_virt_delta(&worker->mem_slots, data, drawable->group_id),
-                                get_memslot_id(&worker->mem_slots, data),
-                                stream - worker->streams_buf, stream, drawable->group_id)) {
+                                &image->u.bitmap, frame, frame_stride,
+                                stream - worker->streams_buf, stream)) {
             return FALSE;
         }
         break;
     case SPICE_BITMAP_FMT_24BIT:
         if (!red_rgb24bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
-                                &qxl_image->bitmap, frame, frame_stride,
-                                get_virt_delta(&worker->mem_slots, data, drawable->group_id),
-                                get_memslot_id(&worker->mem_slots, data),
-                                stream - worker->streams_buf, stream, drawable->group_id)) {
+                                &image->u.bitmap, frame, frame_stride,
+                                stream - worker->streams_buf, stream)) {
             return FALSE;
         }
         break;
     default:
-        red_printf_some(1000, "unsupported format %d", qxl_image->bitmap.format);
+        red_printf_some(1000, "unsupported format %d", image->u.bitmap.format);
         return FALSE;
     }
 
@@ -8804,9 +8282,10 @@ static void display_channel_reset_cache(DisplayChannel *display_channel)
 static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
 {
     RedChannel *channel;
-    RedImage red_image;
+    SpiceImage red_image;
     RedWorker *worker;
     SpiceBitmap bitmap;
+    SpiceChunks *chunks;
     QRegion *surface_lossy_region;
     int comp_succeeded;
     int lossy_comp = FALSE;
@@ -8827,13 +8306,17 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
     red_image.descriptor.height = item->height;
 
     bitmap.format = item->image_format;
-    bitmap.flags = QXL_BITMAP_DIRECT;
-    bitmap.flags |= item->top_down ? QXL_BITMAP_TOP_DOWN : 0;
+    if (item->top_down) {
+        bitmap.flags |= SPICE_BITMAP_FLAGS_TOP_DOWN;
+    }
     bitmap.x = item->width;
     bitmap.y = item->height;
     bitmap.stride = item->stride;
     bitmap.palette = 0;
-    bitmap.data = (SPICE_ADDRESS)item->data;
+    bitmap.palette_id = 0;
+
+    chunks = spice_chunks_new_linear(item->data, bitmap.stride * bitmap.y);
+    bitmap.data = chunks;
 
     channel->send_data.header->type = SPICE_MSG_DISPLAY_DRAW_COPY;
 
@@ -8903,13 +8386,13 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
 
     surface_lossy_region = &display_channel->surface_client_lossy_region[item->surface_id];
     if (comp_succeeded) {
-        spice_marshall_Image(src_bitmap_out, (SpiceImageDescriptor *)&red_image,
+        spice_marshall_Image(src_bitmap_out, &red_image,
                              &bitmap_palette_out, &data_out, &lzplt_palette_out);
 
         marshaller_add_compressed(worker, src_bitmap_out,
                                   comp_send_data.comp_buf, comp_send_data.comp_buf_size);
 
-        if (comp_send_data.lzplt_palette) {
+        if (lzplt_palette_out && comp_send_data.lzplt_palette) {
             spice_marshall_Palette(lzplt_palette_out, comp_send_data.lzplt_palette);
         }
 
@@ -8920,15 +8403,16 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
         }
     } else {
         red_image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
-        red_image.bitmap = bitmap;
-        red_image.bitmap.flags &= ~QXL_BITMAP_DIRECT;
+        red_image.u.bitmap = bitmap;
 
-        spice_marshall_Image(src_bitmap_out, (SpiceImageDescriptor *)&red_image,
+        spice_marshall_Image(src_bitmap_out, &red_image,
                              &bitmap_palette_out, &data_out, &lzplt_palette_out);
         spice_marshaller_add_ref(data_out, item->data, bitmap.y * bitmap.stride);
         region_remove(surface_lossy_region, &copy.base.box);
     }
     display_begin_send_massage(display_channel, &item->link);
+
+    spice_chunks_destroy(chunks);
 }
 
 static void red_display_send_upgrade(DisplayChannel *display_channel, UpgradeItem *item)
@@ -9493,8 +8977,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, NULL,
-                                    &worker->preload_group_virt_mapping))) {
+                                    &worker->image_surfaces, NULL, NULL, NULL))) {
         return NULL;
     }
 
@@ -9552,8 +9035,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, NULL,
-                                        &worker->preload_group_virt_mapping);
+                                        &worker->image_surfaces, NULL, NULL, NULL);
         surface->context.top_down = TRUE;
         surface->context.canvas_draws_on_surface = TRUE;
         return canvas;
@@ -11055,10 +10537,6 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     struct epoll_event event;
     RedWorkerMessage message;
     int epoll;
-    static SpiceVirtMappingOps preload_group_virt_mapping_ops = {
-        op_get_virt_preload_group,
-        op_validate_virt_preload_group
-    };
 
     ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
 
@@ -11115,8 +10593,6 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     PANIC_ON(init_data->n_surfaces > NUM_SURFACES);
     worker->n_surfaces = init_data->n_surfaces;
 
-    worker->preload_group_virt_mapping.ops = &preload_group_virt_mapping_ops;
-
     message = RED_WORKER_MESSAGE_READY;
     write_message(worker->channel, &message);
 }
@@ -11128,7 +10604,6 @@ void *red_worker_main(void *arg)
     red_printf("begin");
     ASSERT(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW &&
            MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by ack message
-    ASSERT(QXL_BITMAP_TOP_DOWN == SPICE_BITMAP_FLAGS_TOP_DOWN);
 
 #if  defined(RED_WORKER_STAT) || defined(COMPRESS_STAT)
     if (pthread_getcpuclockid(pthread_self(), &clock_id)) {
@@ -11239,6 +10714,7 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i
     int32_t tmp_32;
     uint16_t tmp_u16;
     FILE *f;
+    int i;
 
     switch (bitmap->format) {
     case SPICE_BITMAP_FMT_1BIT_BE:
@@ -11276,7 +10752,7 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i
         if (!bitmap->palette) {
             return; // dont dump masks.
         }
-        plt = (SpicePalette *)get_virt(&worker->mem_slots, bitmap->palette, sizeof(SpicePalette), group_id);
+        plt = bitmap->palette;
     }
     row_size = (((bitmap->x * n_pixel_bits) + 31) / 32) * 4;
     bitmap_data_offset = header_size;
@@ -11331,29 +10807,11 @@ static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_i
         dump_palette(f, plt);
     }
     /* writing the data */
-    if ((bitmap->flags & QXL_BITMAP_DIRECT)) {
-        uint8_t *lines = (uint8_t*)get_virt(&worker->mem_slots, bitmap->data, bitmap->stride * bitmap->y,
-                                            group_id);
-        int i;
-        for (i = 0; i < bitmap->y; i++) {
-            dump_line(f, lines + (i * bitmap->stride), n_pixel_bits, bitmap->x, row_size);
-        }
-    } else {
-        QXLDataChunk *chunk = NULL;
-        int num_lines;
-        SPICE_ADDRESS relative_address = bitmap->data;
-
-        while (relative_address) {
-            int i;
-            chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, relative_address, sizeof(QXLDataChunk),
-                                             group_id);
-            validate_virt(&worker->mem_slots, chunk->data, get_memslot_id(&worker->mem_slots, relative_address),
-                          chunk->data_size, group_id);
-            num_lines = chunk->data_size / bitmap->stride;
-            for (i = 0; i < num_lines; i++) {
-                dump_line(f, chunk->data + (i * bitmap->stride), n_pixel_bits, bitmap->x, row_size);
-            }
-            relative_address = chunk->next_chunk;
+    for (i = 0; i < bitmap->data->num_chunks; i++) {
+        SpiceChunk *chunk = &bitmap->data->chunk[i];
+        int num_lines = chunk->len / bitmap->stride;
+        for (i = 0; i < num_lines; i++) {
+            dump_line(f, chunk->data + (i * bitmap->stride), n_pixel_bits, bitmap->x, row_size);
         }
     }
     fclose(f);
diff --git a/spice.proto b/spice.proto
index e437630..e16250f 100644
--- a/spice.proto
+++ b/spice.proto
@@ -111,7 +111,7 @@ channel BaseChannel {
     message {
 	uint32 id;
 	uint64 timestamp;
-	uint8 data[] @end @ctype(uint8_t) @as_ptr(data_len);
+	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
     } ping;
 
     message {
@@ -435,16 +435,16 @@ struct BitmapData {
     uint32 stride;
     switch (flags) {
     case PAL_FROM_CACHE:
-	uint64 palette;
+	uint64 palette_id;
     default:
-	Palette *palette @outvar(bitmap);
+	Palette *palette @outvar(bitmap) @c_ptr;
     } pal @anon;
-    uint8 *data[image_size(8, stride, y)] @nocopy; /* pointer to array, not array of pointers as in C */
+    uint8 *data[image_size(8, stride, y)] @chunk; /* pointer to array, not array of pointers as in C */
 } @ctype(SpiceBitmap);
 
 struct BinaryData {
     uint32 data_size;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 } @ctype(SpiceQUICData);
 
 struct LZPLTData {
@@ -452,58 +452,63 @@ struct LZPLTData {
     uint32 data_size;
     switch (flags) {
     case PAL_FROM_CACHE:
-	uint64 palette;
+	uint64 palette_id;
     default:
-	Palette *palette @nonnull @outvar(lzplt);
+	Palette *palette @nonnull @outvar(lzplt) @c_ptr;
     } pal @anon;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 };
 
 struct ZlibGlzRGBData {
     uint32 glz_data_size;
     uint32 data_size;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 } @ctype(SpiceZlibGlzRGBData);
 
 struct JPEGAlphaData {
     jpeg_alpha_flags flags;
     uint32 jpeg_size;
     uint32 data_size;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 } @ctype(SpiceJPEGAlphaData);
 
 struct Surface {
     uint32 surface_id;
 };
 
-struct Image {
-    uint64 id;
-    image_type type;
-    image_flags flags;
-    uint32 width;
-    uint32 height;
 
-    switch (type) {
+struct Image {
+    struct ImageDescriptor {
+        uint64 id;
+        image_type type;
+        image_flags flags;
+        uint32 width;
+        uint32 height;
+    } descriptor;
+
+    switch (descriptor.type) {
     case BITMAP:
-        BitmapData bitmap_data @ctype(SpiceBitmap);
+        BitmapData bitmap;
     case QUIC:
+        BinaryData quic;
     case LZ_RGB:
     case GLZ_RGB:
+        BinaryData lz_rgb;
     case JPEG:
-        BinaryData binary_data @ctype(SpiceQUICData);
+        BinaryData jpeg;
     case LZ_PLT:
-        LZPLTData lzplt_data @ctype(SpiceLZPLTData);
+        LZPLTData lz_plt;
     case ZLIB_GLZ_RGB:
-        ZlibGlzRGBData zlib_glz_data @ctype(SpiceZlibGlzRGBData);
+        ZlibGlzRGBData zlib_glz;
     case JPEG_ALPHA:
-        JPEGAlphaData jpeg_alpha_data  @ctype(SpiceJPEGAlphaData);
+        JPEGAlphaData jpeg_alpha;
     case SURFACE:
-        Surface surface_data;
-    } u @end;
-} @ctype(SpiceImageDescriptor);
+        Surface surface;
+    } u;
+};
 
 struct Pattern {
-    Image *pat @nonnull;
+    Image *pat @nonnull @c_ptr;
     Point pos;
 };
 
@@ -520,7 +525,7 @@ struct Brush {
 struct QMask {
     mask_flags flags;
     Point pos;
-    Image *bitmap;
+    Image *bitmap @c_ptr;
 };
 
 struct LineAttr {
@@ -649,7 +654,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Opaque {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    Brush brush;
 	    ropd rop_descriptor;
@@ -661,7 +666,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Copy {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    ropd rop_descriptor;
 	    image_scale_mode scale_mode;
@@ -672,7 +677,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Blend {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    ropd rop_descriptor;
 	    image_scale_mode scale_mode;
@@ -704,7 +709,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Rop3 {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    Brush brush;
 	    uint8 rop3;
@@ -739,7 +744,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Transparent {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    uint32 src_color;
 	    uint32 true_color;
@@ -751,7 +756,7 @@ channel DisplayChannel : BaseChannel {
 	struct AlphaBlnd {
 	    alpha_flags alpha_flags;
 	    uint8 alpha;
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	} data;
     } draw_alpha_blend;
@@ -875,7 +880,7 @@ struct CursorHeader {
 struct Cursor {
     cursor_flags flags;
     CursorHeader header;
-    uint8 data[] @end @as_ptr(data_size);
+    uint8 data[] @as_ptr(data_size);
 };
 
 channel CursorChannel : BaseChannel {
@@ -929,13 +934,13 @@ channel PlaybackChannel : BaseChannel {
  server:
     message {
 	uint32 time;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } @ctype(SpiceMsgPlaybackPacket) data = 101;
 
     message {
 	uint32 time;
 	audio_data_mode mode;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } mode;
 
     message {
@@ -960,13 +965,13 @@ channel RecordChannel : BaseChannel {
  client:
     message {
 	uint32 time;
-	uint8 data[] @end @nomarshal @as_ptr(data_size);
+	uint8 data[] @nomarshal @as_ptr(data_size);
     } @ctype(SpiceMsgcRecordPacket) data = 101;
 
     message {
 	uint32 time;
 	audio_data_mode mode;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } mode;
 
     message {
diff --git a/spice1.proto b/spice1.proto
index b49371a..7bbccb7 100644
--- a/spice1.proto
+++ b/spice1.proto
@@ -111,7 +111,7 @@ channel BaseChannel {
     message {
 	uint32 id;
 	uint64 timestamp;
-	uint8 data[] @end @ctype(uint8_t) @as_ptr(data_len);
+	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
     } ping;
 
     message {
@@ -419,16 +419,16 @@ struct BitmapData {
     uint32 stride;
     switch (flags) {
     case PAL_FROM_CACHE:
-	uint64 palette;
+	uint64 palette_id;
     default:
-	Palette *palette @outvar(bitmap);
+	Palette *palette @outvar(bitmap) @c_ptr;
     } pal @anon;
-    uint8 *data[image_size(8, stride, y)] @nocopy; /* pointer to array, not array of pointers as in C */
+    uint8 *data[image_size(8, stride, y)] @chunk; /* pointer to array, not array of pointers as in C */
 } @ctype(SpiceBitmap);
 
 struct BinaryData {
     uint32 data_size;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 } @ctype(SpiceQUICData);
 
 struct LZPLTData {
@@ -436,34 +436,37 @@ struct LZPLTData {
     uint32 data_size;
     switch (flags) {
     case PAL_FROM_CACHE:
-	uint64 palette;
+	uint64 palette_id;
     default:
-	Palette *palette @nonnull @outvar(lzplt);
+	Palette *palette @nonnull @outvar(lzplt) @c_ptr;
     } pal @anon;
-    uint8 data[data_size] @end @nomarshal;
+    uint8 data[data_size] @nomarshal @chunk;
 };
 
 struct Image {
-    uint64 id;
-    image_type type;
-    image_flags flags;
-    uint32 width;
-    uint32 height;
-
-    switch (type) {
+    struct ImageDescriptor {
+        uint64 id;
+        image_type type;
+        image_flags flags;
+        uint32 width;
+        uint32 height;
+    } descriptor;
+
+    switch (descriptor.type) {
     case BITMAP:
-        BitmapData bitmap_data @ctype(SpiceBitmap);
+        BitmapData bitmap;
     case QUIC:
+        BinaryData quic;
     case LZ_RGB:
     case GLZ_RGB:
-        BinaryData binary_data @ctype(SpiceQUICData);
+        BinaryData lz_rgb;
     case LZ_PLT:
-        LZPLTData lzplt_data @ctype(SpiceLZPLTData);
-    } u @end;
-} @ctype(SpiceImageDescriptor);
+        LZPLTData lz_plt;
+    } u;
+};
 
 struct Pattern {
-    Image *pat @nonnull;
+    Image *pat @nonnull @c_ptr;
     Point pos;
 };
 
@@ -480,7 +483,7 @@ struct Brush {
 struct QMask {
     mask_flags flags;
     Point pos;
-    Image *bitmap;
+    Image *bitmap @c_ptr;
 };
 
 struct LineAttr {
@@ -608,7 +611,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Opaque {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    Brush brush;
 	    ropd rop_descriptor;
@@ -620,7 +623,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Copy {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    ropd rop_descriptor;
 	    image_scale_mode scale_mode;
@@ -631,7 +634,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Blend {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    ropd rop_descriptor;
 	    image_scale_mode scale_mode;
@@ -663,7 +666,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Rop3 {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    Brush brush;
 	    uint8 rop3;
@@ -698,7 +701,7 @@ channel DisplayChannel : BaseChannel {
     message {
 	DisplayBase base;
 	struct Transparent {
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	    uint32 src_color;
 	    uint32 true_color;
@@ -710,7 +713,7 @@ channel DisplayChannel : BaseChannel {
 	struct AlphaBlnd {
 	    int8 alpha_flags @virtual(0);
 	    uint8 alpha;
-	    Image *src_bitmap;
+	    Image *src_bitmap @c_ptr;
 	    Rect src_area;
 	} data;
     } draw_alpha_blend;
@@ -822,7 +825,7 @@ struct CursorHeader {
 struct Cursor {
     cursor_flags flags;
     CursorHeader header;
-    uint8 data[] @end @as_ptr(data_size);
+    uint8 data[] @as_ptr(data_size);
 };
 
 channel CursorChannel : BaseChannel {
@@ -876,13 +879,13 @@ channel PlaybackChannel : BaseChannel {
  server:
     message {
 	uint32 time;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } @ctype(SpiceMsgPlaybackPacket) data = 101;
 
     message {
 	uint32 time;
 	audio_data_mode mode;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } mode;
 
     message {
@@ -907,13 +910,13 @@ channel RecordChannel : BaseChannel {
  client:
     message {
 	uint32 time;
-	uint8 data[] @end @nomarshal @as_ptr(data_size);
+	uint8 data[] @nomarshal @as_ptr(data_size);
     } @ctype(SpiceMsgcRecordPacket) data = 101;
 
     message {
 	uint32 time;
 	audio_data_mode mode;
-	uint8 data[] @end @as_ptr(data_size);
+	uint8 data[] @as_ptr(data_size);
     } mode;
 
     message {
commit 26c1a0767f3fdcd0211b6b4c91a63ac9bc7abc6f
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Jul 7 20:40:06 2010 +0200

    codegen: support @chunk on non-pointer arrays
    
    This is similar to @as_ptr, but generates a single chunk of data.

diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
index 51dbe31..323fc70 100644
--- a/python_modules/demarshal.py
+++ b/python_modules/demarshal.py
@@ -357,6 +357,18 @@ def write_validate_array_item(writer, container, item, scope, parent_scope, star
             writer.assign(nw_size, "(%s) * %s" % (element_size, nelements))
         want_nw_size = False
 
+    if array.has_attr("as_ptr") and want_mem_size:
+        writer.assign(mem_size, "sizeof(void *)")
+        want_mem_size = False
+
+    if array.has_attr("chunk"):
+        if want_mem_size:
+            writer.assign(extra_size, "sizeof(SpiceChunks *)")
+            want_mem_size = False
+        if want_extra_size:
+            writer.assign(extra_size, "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            want_extra_size = False
+
     if element_type.is_fixed_sizeof() and want_mem_size and not is_byte_size:
         # TODO: Overflow check the multiplication
         if array.has_attr("ptr_array"):
@@ -832,7 +844,7 @@ def write_member_parser(writer, container, member, dest, scope):
             scope.variable_def("SpiceChunks *", "chunks");
             writer.assign("chunks", "(SpiceChunks *)end")
             writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
-            writer.assign(dest.get_ref(member.name), "chunks") # spice_chunks_new_linear(message_start + consume_%s(&in), %s)" % (t.primitive_type(), nelements))
+            writer.assign(dest.get_ref(member.name), "chunks")
             writer.assign("chunks->data_size", nelements)
             writer.assign("chunks->flags", 0)
             writer.assign("chunks->num_chunks", 1)
@@ -851,7 +863,6 @@ def write_member_parser(writer, container, member, dest, scope):
             writer.increment("end", t.sizeof())
         else:
             if member.has_attr("bytes_count"):
-                print member.attributes["bytes_count"]
                 dest_var = dest.get_ref(member.attributes["bytes_count"][0])
             else:
                 dest_var = dest.get_ref(member.name)
@@ -859,7 +870,20 @@ def write_member_parser(writer, container, member, dest, scope):
         #TODO validate e.g. flags and enums
     elif t.is_array():
         nelements = read_array_len(writer, member.name, t, dest, scope)
-        if member.has_attr("as_ptr") and t.element_type.is_fixed_nw_size():
+        if member.has_attr("chunk") and t.element_type.is_fixed_nw_size() and t.element_type.get_fixed_nw_size() == 1:
+            writer.comment("use array as chunk").newline()
+
+            scope.variable_def("SpiceChunks *", "chunks");
+            writer.assign("chunks", "(SpiceChunks *)end")
+            writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            writer.assign(dest.get_ref(member.name), "chunks")
+            writer.assign("chunks->data_size", nelements)
+            writer.assign("chunks->flags", 0)
+            writer.assign("chunks->num_chunks", 1)
+            writer.assign("chunks->chunk[0].len", nelements)
+            writer.assign("chunks->chunk[0].data", "in")
+            writer.increment("in", "%s" % (nelements))
+        elif member.has_attr("as_ptr") and t.element_type.is_fixed_nw_size():
             writer.comment("use array as pointer").newline()
             writer.assign(dest.get_ref(member.name), "(%s *)in" % t.element_type.c_type())
             len_var = member.attributes["as_ptr"]
diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
index f02437a..68cf3df 100644
--- a/python_modules/ptypes.py
+++ b/python_modules/ptypes.py
@@ -60,7 +60,7 @@ class FixedSize:
 # only to attributes that affect pointer or array attributes, as these
 # are member local types, unlike e.g. a Struct that may be used by
 # other members
-propagated_attributes=["ptr_array", "c_ptr", "nonnull"]
+propagated_attributes=["ptr_array", "c_ptr", "nonnull", "chunk"]
 
 class Type:
     def __init__(self):
@@ -428,7 +428,7 @@ class ArrayType(Type):
         return self.has_attr("ptr_array")
 
     def contains_extra_size(self):
-        return self.element_type.contains_extra_size()
+        return self.element_type.contains_extra_size() or self.has_attr("chunk")
 
     def sizeof(self):
         return "%s * %s" % (self.element_type.sizeof(), self.size)
commit bda492f4aa28c8921e7277c8c0dcfdc59b19c88f
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Jul 6 22:13:18 2010 +0200

    Add support for @chunk

diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
index bd7660d..51dbe31 100644
--- a/python_modules/demarshal.py
+++ b/python_modules/demarshal.py
@@ -261,7 +261,9 @@ def write_validate_pointer_item(writer, container, item, scope, parent_scope, st
                 writer.error_check("message_start + %s + %s > message_end" % (v, array_item.nw_size()))
 
             if want_extra_size:
-                if item.member and item.member.has_attr("nocopy"):
+                if item.member and item.member.has_attr("chunk"):
+                    writer.assign(item.extra_size(), "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+                elif item.member and item.member.has_attr("nocopy"):
                     writer.comment("@nocopy, so no extra size").newline()
                     writer.assign(item.extra_size(), 0)
                 elif target_type.element_type.get_fixed_nw_size == 1:
@@ -823,7 +825,20 @@ def write_member_parser(writer, container, member, dest, scope):
     t = member.member_type
 
     if t.is_pointer():
-        if member.has_attr("nocopy"):
+        if member.has_attr("chunk"):
+            assert(t.target_type.is_array())
+            nelements = read_array_len(writer, member.name, t.target_type, dest, scope)
+            writer.comment("Reuse data from network message as chunk").newline()
+            scope.variable_def("SpiceChunks *", "chunks");
+            writer.assign("chunks", "(SpiceChunks *)end")
+            writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            writer.assign(dest.get_ref(member.name), "chunks") # spice_chunks_new_linear(message_start + consume_%s(&in), %s)" % (t.primitive_type(), nelements))
+            writer.assign("chunks->data_size", nelements)
+            writer.assign("chunks->flags", 0)
+            writer.assign("chunks->num_chunks", 1)
+            writer.assign("chunks->chunk[0].len", nelements)
+            writer.assign("chunks->chunk[0].data", "message_start + consume_%s(&in)" % t.primitive_type())
+        elif member.has_attr("nocopy"):
             writer.comment("Reuse data from network message").newline()
             writer.assign(dest.get_ref(member.name), "(size_t)(message_start + consume_%s(&in))" % t.primitive_type())
         else:
@@ -1140,6 +1155,7 @@ def write_includes(writer):
     writer.writeln("#include <stdio.h>")
     writer.writeln("#include <spice/protocol.h>")
     writer.writeln("#include <spice/macros.h>")
+    writer.writeln("#include <common/mem.h>")
     writer.newline()
     writer.writeln("#ifdef _MSC_VER")
     writer.writeln("#pragma warning(disable:4101)")
commit 96bb4d31f417f26284c32a1f56469a2b2dfc3349
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Jul 6 22:12:26 2010 +0200

    marshaller: Make get_nw_offset() handle deep member references

diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
index 31ae79d..f02437a 100644
--- a/python_modules/ptypes.py
+++ b/python_modules/ptypes.py
@@ -92,6 +92,9 @@ class Type:
     def is_array(self):
         return isinstance(self, ArrayType)
 
+    def contains_member(self, member):
+        return False
+
     def is_struct(self):
         return isinstance(self, StructType)
 
@@ -527,6 +530,9 @@ class Member(Containee):
                 self.member_type.attributes[i] = self.attributes[i]
         return self
 
+    def contains_member(self, member):
+        return self.member_type.contains_member(member)
+
     def is_primitive(self):
         return self.member_type.is_primitive()
 
@@ -694,6 +700,9 @@ class Switch(Containee):
         return "sizeof(((%s *)NULL)->%s)" % (self.container.c_type(),
                                              self.name)
 
+    def contains_member(self, member):
+        return False # TODO: Don't support switch deep member lookup yet
+
     def has_pointer(self):
         for c in self.cases:
             if c.has_pointer():
@@ -739,11 +748,20 @@ class ContainerType(Type):
             size = size + i.get_fixed_nw_size()
         return size
 
+    def contains_member(self, member):
+        for m in self.members:
+            if m == member or m.contains_member(member):
+                return True
+        return False
+
     def get_fixed_nw_offset(self, member):
         size = 0
         for i in self.members:
             if i == member:
                 break
+            if i.contains_member(member):
+                size = size  + i.member_type.get_fixed_nw_offset(member)
+                break
             if i.is_fixed_nw_size():
                 size = size + i.get_fixed_nw_size()
         return size
@@ -773,13 +791,20 @@ class ContainerType(Type):
     def get_nw_offset(self, member, prefix = "", postfix = ""):
         fixed = self.get_fixed_nw_offset(member)
         v = []
-        for m in self.members:
-            if m == member:
-                break
-            if m.is_switch() and m.has_switch_member(member):
-                break
-            if not m.is_fixed_nw_size():
-                v.append(prefix + m.name + postfix)
+        container = self
+        while container != None:
+            members = container.members
+            container = None
+            for m in members:
+                if m == member:
+                    break
+                if m.contains_member(member):
+                    container = m.member_type
+                    break
+                if m.is_switch() and m.has_switch_member(member):
+                    break
+                if not m.is_fixed_nw_size():
+                    v.append(prefix + m.name + postfix)
         if len(v) > 0:
             return str(fixed) + " + " + (" + ".join(v))
         else:
commit ee26a9a77372b92ee615ae783676cb0ed7172ecd
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Jul 7 22:40:16 2010 +0200

    Add spice_marshaller_add_ref_chunks

diff --git a/common/marshaller.c b/common/marshaller.c
index be6198d..70008a8 100644
--- a/common/marshaller.c
+++ b/common/marshaller.c
@@ -346,6 +346,16 @@ uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size
     return spice_marshaller_add_ref_full(m, data, size, NULL, NULL);
 }
 
+void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks)
+{
+    int i;
+
+    for (i = 0; i < chunks->num_chunks; i++) {
+        spice_marshaller_add_ref(m, chunks->chunk[i].data,
+                                 chunks->chunk[i].len);
+    }
+}
+
 SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m)
 {
     SpiceMarshallerData *d;
diff --git a/common/marshaller.h b/common/marshaller.h
index 59a190d..5118934 100644
--- a/common/marshaller.h
+++ b/common/marshaller.h
@@ -20,6 +20,7 @@
 #define _H_MARSHALLER
 
 #include <spice/types.h>
+#include <spice/draw.h> /* for SpiceChunk, temporary */
 #ifndef WIN32
 #include <sys/uio.h>
 #endif
@@ -36,6 +37,7 @@ uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size);
 uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size);
 uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
                                        spice_marshaller_item_free_func free_data, void *opaque);
+void     spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks);
 void spice_marshaller_flush(SpiceMarshaller *m);
 void spice_marshaller_set_base(SpiceMarshaller *m, size_t base);
 uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip,
commit a415c557657c2009798d086b229c17ac0d47a978
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Jul 6 14:39:15 2010 +0200

    Add spice_chunks_* helpers

diff --git a/common/mem.c b/common/mem.c
index ffb2fa4..427ab43 100644
--- a/common/mem.c
+++ b/common/mem.c
@@ -178,3 +178,62 @@ void *spice_realloc_n(void *mem, size_t n_blocks, size_t n_block_bytes)
 
     return spice_realloc(mem, n_blocks * n_block_bytes);
 }
+
+SpiceChunks *spice_chunks_new(uint32_t count)
+{
+    SpiceChunks *chunks;
+
+    chunks = (SpiceChunks *)spice_malloc_n_m(count, sizeof(SpiceChunk), sizeof(SpiceChunks));
+    chunks->flags = 0;
+    chunks->num_chunks = count;
+
+    return chunks;
+}
+
+SpiceChunks *spice_chunks_new_linear(uint8_t *data, uint32_t len)
+{
+    SpiceChunks *chunks;
+
+    chunks = spice_chunks_new(1);
+    chunks->data_size = chunks->chunk[0].len = len;
+    chunks->chunk[0].data = data;
+    return chunks;
+}
+
+void spice_chunks_destroy(SpiceChunks *chunks)
+{
+    int i;
+
+    if (chunks->flags & SPICE_CHUNKS_FLAGS_FREE) {
+        for (i = 0; i < chunks->num_chunks; i++) {
+            free(chunks->chunk[i].data);
+        }
+    }
+
+    free(chunks);
+}
+
+void spice_chunks_linearize(SpiceChunks *chunks)
+{
+    uint8_t *data, *p;
+    int i;
+
+    if (chunks->num_chunks > 1) {
+        data = (uint8_t*)spice_malloc(chunks->data_size);
+        for (p = data, i = 0; i < chunks->num_chunks; i++) {
+            memcpy(p, chunks->chunk[i].data,
+                   chunks->chunk[i].len);
+            p += chunks->chunk[i].len;
+        }
+        if (chunks->flags & SPICE_CHUNKS_FLAGS_FREE) {
+            for (i = 0; i < chunks->num_chunks; i++) {
+                free(chunks->chunk[i].data);
+            }
+        }
+        chunks->num_chunks = 1;
+        chunks->flags |= SPICE_CHUNKS_FLAGS_FREE;
+        chunks->flags &= ~SPICE_CHUNKS_FLAGS_UNSTABLE;
+        chunks->chunk[0].data = data;
+        chunks->chunk[0].len = chunks->data_size;
+    }
+}
diff --git a/common/mem.h b/common/mem.h
index 9b257ad..ebf2fb8 100644
--- a/common/mem.h
+++ b/common/mem.h
@@ -21,6 +21,7 @@
 
 #include <stdlib.h>
 #include <spice/macros.h>
+#include <spice/draw.h> /* for SpiceChunks, temporary */
 
 char *spice_strdup(const char *str) SPICE_GNUC_MALLOC;
 char *spice_strndup(const char *str, size_t n_bytes) SPICE_GNUC_MALLOC;
@@ -32,6 +33,10 @@ void *spice_malloc_n(size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_MALLOC SP
 void *spice_malloc_n_m(size_t n_blocks, size_t n_block_bytes, size_t extra_size) SPICE_GNUC_MALLOC;
 void *spice_malloc0_n(size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_MALLOC SPICE_GNUC_ALLOC_SIZE2(1,2);
 void *spice_realloc_n(void *mem, size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_WARN_UNUSED_RESULT;
+SpiceChunks *spice_chunks_new(uint32_t count) SPICE_GNUC_MALLOC;
+SpiceChunks *spice_chunks_new_linear(uint8_t *data, uint32_t len) SPICE_GNUC_MALLOC;
+void spice_chunks_destroy(SpiceChunks *chunks);
+void spice_chunks_linearize(SpiceChunks *chunks);
 
 size_t spice_strnlen(const char *str, size_t max_len);
 


More information about the Spice-commits mailing list