[Spice-commits] server/Makefile.am server/dcc-encoders.c server/dcc-encoders.h server/dcc.h server/image-encoders.c server/image-encoders.h

Frediano Ziglio fziglio at kemper.freedesktop.org
Fri Jun 17 14:29:55 UTC 2016


 server/Makefile.am      |    4 
 server/dcc-encoders.c   | 1388 ------------------------------------------------
 server/dcc-encoders.h   |  217 -------
 server/dcc.h            |    2 
 server/image-encoders.c | 1388 ++++++++++++++++++++++++++++++++++++++++++++++++
 server/image-encoders.h |  217 +++++++
 6 files changed, 1608 insertions(+), 1608 deletions(-)

New commits:
commit e6ed1840a10eb049710ec1fd24f15df157c4bb6e
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Fri Jun 17 11:54:54 2016 +0100

    Rename dcc-encoders.[ch] to image-encoders.[ch]
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/Makefile.am b/server/Makefile.am
index 0af8a1b..921b082 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -148,8 +148,8 @@ libserver_la_SOURCES =				\
 	dcc-send.c					\
 	dcc.h					\
 	display-limits.h			\
-	dcc-encoders.c					\
-	dcc-encoders.h					\
+	image-encoders.c					\
+	image-encoders.h					\
 	$(NULL)
 
 if HAVE_LZ4
diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c
deleted file mode 100644
index 984f2d4..0000000
--- a/server/dcc-encoders.c
+++ /dev/null
@@ -1,1388 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#include "dcc-encoders.h"
-#include "spice-bitmap-utils.h"
-#include "red-worker.h" // red_drawable_unref
-#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
-
-#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
-
-#define ENCODER_MESSAGE_SIZE 512
-
-#define MAX_GLZ_DRAWABLE_INSTANCES 2
-
-typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
-
-struct GlzSharedDictionary {
-    RingItem base;
-    GlzEncDictContext *dict;
-    uint32_t refs;
-    uint8_t id;
-    pthread_rwlock_t encode_lock;
-    int migrate_freeze;
-    RedClient *client; // channel clients of the same client share the dict
-};
-
-/* for each qxl drawable, there may be several instances of lz drawables */
-/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
- * at the Drawable by keeping a ring, so:
- * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
- * and it should probably (but need to be sure...) be
- * Drawable -> ring of GlzDrawableInstanceItem.
- */
-struct GlzDrawableInstanceItem {
-    RingItem glz_link;
-    RingItem free_link;
-    GlzEncDictImageContext *context;
-    RedGlzDrawable         *glz_drawable;
-};
-
-struct RedGlzDrawable {
-    RingItem link;    // ordered by the time it was encoded
-    RingItem drawable_link;
-    RedDrawable *red_drawable;
-    GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
-    Ring instances;
-    uint8_t instances_count;
-    gboolean has_drawable;
-    ImageEncoders *encoders;
-};
-
-#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
-                                           drawable_link)
-#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
-    SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, LINK_TO_GLZ(link))
-
-static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance);
-static void encoder_data_init(EncoderData *data);
-static void encoder_data_reset(EncoderData *data);
-static void image_encoders_release_glz(ImageEncoders *enc);
-
-
-static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
-quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
-{
-    EncoderData *usr_data = &(((QuicData *)usr)->data);
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-    spice_critical("%s", message_buf);
-
-    longjmp(usr_data->jmp_env, 1);
-}
-
-static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
-lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
-{
-    EncoderData *usr_data = &(((LzData *)usr)->data);
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-    spice_critical("%s", message_buf);
-
-    longjmp(usr_data->jmp_env, 1);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
-{
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-
-    spice_critical("%s", message_buf); // if global lz fails in the middle
-                                        // the consequences are not predictable since the window
-                                        // can turn to be unsynchronized between the server and
-                                        // and the client
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
-{
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-    spice_warning("%s", message_buf);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
-{
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-    spice_warning("%s", message_buf);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
-{
-    va_list ap;
-    char message_buf[ENCODER_MESSAGE_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
-    va_end(ap);
-    spice_warning("%s", message_buf);
-}
-
-static void *quic_usr_malloc(QuicUsrContext *usr, int size)
-{
-    return spice_malloc(size);
-}
-
-static void *lz_usr_malloc(LzUsrContext *usr, int size)
-{
-    return spice_malloc(size);
-}
-
-static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
-{
-    return spice_malloc(size);
-}
-
-static void quic_usr_free(QuicUsrContext *usr, void *ptr)
-{
-    free(ptr);
-}
-
-static void lz_usr_free(LzUsrContext *usr, void *ptr)
-{
-    free(ptr);
-}
-
-static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
-{
-    free(ptr);
-}
-
-static void encoder_data_init(EncoderData *data)
-{
-    data->bufs_tail = g_new(RedCompressBuf, 1);
-    data->bufs_head = data->bufs_tail;
-    data->bufs_head->send_next = NULL;
-}
-
-static void encoder_data_reset(EncoderData *data)
-{
-    RedCompressBuf *buf = data->bufs_head;
-    while (buf) {
-        RedCompressBuf *next = buf->send_next;
-        g_free(buf);
-        buf = next;
-    }
-    data->bufs_head = data->bufs_tail = NULL;
-}
-
-/* Allocate more space for compressed buffer.
- * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
- */
-static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
-{
-    RedCompressBuf *buf;
-
-    buf = g_new(RedCompressBuf, 1);
-    enc_data->bufs_tail->send_next = buf;
-    enc_data->bufs_tail = buf;
-    buf->send_next = NULL;
-    *io_ptr = buf->buf.bytes;
-    return sizeof(buf->buf);
-}
-
-static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
-{
-    EncoderData *usr_data = &(((QuicData *)usr)->data);
-    return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / sizeof(uint32_t);
-}
-
-static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
-{
-    EncoderData *usr_data = &(((LzData *)usr)->data);
-    return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
-{
-    EncoderData *usr_data = &(((GlzData *)usr)->data);
-    return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
-{
-    EncoderData *usr_data = &(((JpegData *)usr)->data);
-    return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-#ifdef USE_LZ4
-static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
-{
-    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
-    return encoder_usr_more_space(usr_data, io_ptr);
-}
-#endif
-
-static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
-{
-    EncoderData *usr_data = &(((ZlibData *)usr)->data);
-    return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
-{
-    struct SpiceChunk *chunk;
-
-    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;
-        }
-    }
-
-    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;
-    }
-
-    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;
-    }
-
-    return chunk->len / enc_data->u.lines_data.stride;
-}
-
-static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((QuicData *)usr)->data);
-    return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((LzData *)usr)->data);
-    return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((GlzData *)usr)->data);
-    return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((JpegData *)usr)->data);
-    return encoder_usr_more_lines(usr_data, lines);
-}
-
-#ifdef USE_LZ4
-static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
-{
-    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
-    return encoder_usr_more_lines(usr_data, lines);
-}
-#endif
-
-static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
-{
-    EncoderData *usr_data = &(((ZlibData *)usr)->data);
-    int buf_size;
-
-    if (!usr_data->u.compressed_data.next) {
-        spice_assert(usr_data->u.compressed_data.size_left == 0);
-        return 0;
-    }
-
-    *input = usr_data->u.compressed_data.next->buf.bytes;
-    buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
-                   usr_data->u.compressed_data.size_left);
-
-    usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
-    usr_data->u.compressed_data.size_left -= buf_size;
-    return buf_size;
-}
-
-static void image_encoders_init_quic(ImageEncoders *enc)
-{
-    enc->quic_data.usr.error = quic_usr_error;
-    enc->quic_data.usr.warn = quic_usr_warn;
-    enc->quic_data.usr.info = quic_usr_warn;
-    enc->quic_data.usr.malloc = quic_usr_malloc;
-    enc->quic_data.usr.free = quic_usr_free;
-    enc->quic_data.usr.more_space = quic_usr_more_space;
-    enc->quic_data.usr.more_lines = quic_usr_more_lines;
-
-    enc->quic = quic_create(&enc->quic_data.usr);
-
-    if (!enc->quic) {
-        spice_critical("create quic failed");
-    }
-}
-
-static void image_encoders_init_lz(ImageEncoders *enc)
-{
-    enc->lz_data.usr.error = lz_usr_error;
-    enc->lz_data.usr.warn = lz_usr_warn;
-    enc->lz_data.usr.info = lz_usr_warn;
-    enc->lz_data.usr.malloc = lz_usr_malloc;
-    enc->lz_data.usr.free = lz_usr_free;
-    enc->lz_data.usr.more_space = lz_usr_more_space;
-    enc->lz_data.usr.more_lines = lz_usr_more_lines;
-
-    enc->lz = lz_create(&enc->lz_data.usr);
-
-    if (!enc->lz) {
-        spice_critical("create lz failed");
-    }
-}
-
-static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
-{
-    GlzData *lz_data = (GlzData *)usr;
-    GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image;
-    ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable->encoders;
-    ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, glz_data);
-    if (this_enc == drawable_enc) {
-        glz_drawable_instance_item_free(glz_drawable_instance);
-    } else {
-        /* The glz dictionary is shared between all DisplayChannelClient
-         * instances that belong to the same client, and glz_usr_free_image
-         * can be called by the dictionary code
-         * (glz_dictionary_window_remove_head). Thus this function can be
-         * called from any DisplayChannelClient thread, hence the need for
-         * this check.
-         */
-        pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
-        ring_add_before(&glz_drawable_instance->free_link,
-                        &drawable_enc->glz_drawables_inst_to_free);
-        pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
-    }
-}
-
-static void image_encoders_init_glz_data(ImageEncoders *enc)
-{
-    enc->glz_data.usr.error = glz_usr_error;
-    enc->glz_data.usr.warn = glz_usr_warn;
-    enc->glz_data.usr.info = glz_usr_warn;
-    enc->glz_data.usr.malloc = glz_usr_malloc;
-    enc->glz_data.usr.free = glz_usr_free;
-    enc->glz_data.usr.more_space = glz_usr_more_space;
-    enc->glz_data.usr.more_lines = glz_usr_more_lines;
-    enc->glz_data.usr.free_image = glz_usr_free_image;
-}
-
-static void image_encoders_init_jpeg(ImageEncoders *enc)
-{
-    enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
-    enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
-
-    enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
-
-    if (!enc->jpeg) {
-        spice_critical("create jpeg encoder failed");
-    }
-}
-
-#ifdef USE_LZ4
-static inline void image_encoders_init_lz4(ImageEncoders *enc)
-{
-    enc->lz4_data.usr.more_space = lz4_usr_more_space;
-    enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
-
-    enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
-
-    if (!enc->lz4) {
-        spice_critical("create lz4 encoder failed");
-    }
-}
-#endif
-
-static void image_encoders_init_zlib(ImageEncoders *enc)
-{
-    enc->zlib_data.usr.more_space = zlib_usr_more_space;
-    enc->zlib_data.usr.more_input = zlib_usr_more_input;
-
-    enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL);
-
-    if (!enc->zlib) {
-        spice_critical("create zlib encoder failed");
-    }
-}
-
-void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data)
-{
-    spice_assert(shared_data);
-    enc->shared_data = shared_data;
-
-    ring_init(&enc->glz_drawables);
-    ring_init(&enc->glz_drawables_inst_to_free);
-    pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
-
-    image_encoders_init_glz_data(enc);
-    image_encoders_init_quic(enc);
-    image_encoders_init_lz(enc);
-    image_encoders_init_jpeg(enc);
-#ifdef USE_LZ4
-    image_encoders_init_lz4(enc);
-#endif
-    image_encoders_init_zlib(enc);
-
-    // todo: tune level according to bandwidth
-    enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
-}
-
-void image_encoders_free(ImageEncoders *enc)
-{
-    image_encoders_release_glz(enc);
-    quic_destroy(enc->quic);
-    enc->quic = NULL;
-    lz_destroy(enc->lz);
-    enc->lz = NULL;
-    jpeg_encoder_destroy(enc->jpeg);
-    enc->jpeg = NULL;
-#ifdef USE_LZ4
-    lz4_encoder_destroy(enc->lz4);
-    enc->lz4 = NULL;
-#endif
-    zlib_encoder_destroy(enc->zlib);
-    enc->zlib = NULL;
-}
-
-/* Remove from the to_free list and the instances_list.
-   When no instance is left - the RedGlzDrawable is released too. (and the qxl drawable too, if
-   it is not used by Drawable).
-   NOTE - 1) can be called only by the display channel that created the drawable
-          2) it is assumed that the instance was already removed from the dictionary*/
-static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance)
-{
-    RedGlzDrawable *glz_drawable;
-
-    spice_assert(instance);
-    spice_assert(instance->glz_drawable);
-
-    glz_drawable = instance->glz_drawable;
-
-    spice_assert(glz_drawable->instances_count > 0);
-
-    ring_remove(&instance->glz_link);
-    glz_drawable->instances_count--;
-
-    // when the remove callback is performed from the channel that the
-    // drawable belongs to, the instance is not added to the 'to_free' list
-    if (ring_item_is_linked(&instance->free_link)) {
-        ring_remove(&instance->free_link);
-    }
-
-    if (ring_is_empty(&glz_drawable->instances)) {
-        spice_assert(glz_drawable->instances_count == 0);
-
-        if (glz_drawable->has_drawable) {
-            ring_remove(&glz_drawable->drawable_link);
-        }
-        red_drawable_unref(glz_drawable->red_drawable);
-        glz_drawable->encoders->shared_data->glz_drawable_count--;
-        if (ring_item_is_linked(&glz_drawable->link)) {
-            ring_remove(&glz_drawable->link);
-        }
-        free(glz_drawable);
-    }
-}
-
-/*
- * Releases all the instances of the drawable from the dictionary and the display channel client.
- * The release of the last instance will also release the drawable itself and the qxl drawable
- * if possible.
- * NOTE - the caller should prevent encoding using the dictionary during this operation
- */
-static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
-{
-    ImageEncoders *enc = glz_drawable->encoders;
-    RingItem *head_instance = ring_get_head(&glz_drawable->instances);
-    int cont = (head_instance != NULL);
-
-    while (cont) {
-        if (glz_drawable->instances_count == 1) {
-            /* Last instance: glz_drawable_instance_item_free will free the glz_drawable */
-            cont = FALSE;
-        }
-        GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
-                                                        GlzDrawableInstanceItem,
-                                                        glz_link);
-        if (!ring_item_is_linked(&instance->free_link)) {
-            // the instance didn't get out from window yet
-            glz_enc_dictionary_remove_image(enc->glz_dict->dict,
-                                            instance->context,
-                                            &enc->glz_data.usr);
-        }
-        glz_drawable_instance_item_free(instance);
-
-        if (cont) {
-            head_instance = ring_get_head(&glz_drawable->instances);
-        }
-    }
-}
-
-gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
-{
-    if (enc->glz_dict) {
-        pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
-        return TRUE;
-    }
-    return FALSE;
-}
-
-void image_encoders_glz_encode_unlock(ImageEncoders *enc)
-{
-    if (enc->glz_dict) {
-        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-    }
-}
-
-/*
- * Remove from the global lz dictionary some glz_drawables that have no reference to
- * Drawable (their qxl drawables are released too).
- * NOTE - the caller should prevent encoding using the dictionary during the operation
- */
-int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
-{
-    RingItem *ring_link;
-    int n = 0;
-
-    if (!enc) {
-        return 0;
-    }
-    ring_link = ring_get_head(&enc->glz_drawables);
-    while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
-        RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
-        ring_link = ring_next(&enc->glz_drawables, ring_link);
-        if (!glz_drawable->has_drawable) {
-            red_glz_drawable_free(glz_drawable);
-            n++;
-        }
-    }
-    return n;
-}
-
-void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
-{
-    RingItem *ring_link;
-
-    if (!enc->glz_dict) {
-        return;
-    }
-    pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
-    while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
-        GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link,
-                                                                 GlzDrawableInstanceItem,
-                                                                 free_link);
-        glz_drawable_instance_item_free(drawable_instance);
-    }
-    pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
-}
-
-/* Clear all lz drawables - enforce their removal from the global dictionary.
-   NOTE - prevents encoding using the dictionary during the operation*/
-void image_encoders_free_glz_drawables(ImageEncoders *enc)
-{
-    RingItem *ring_link;
-    GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
-
-    if (!glz_dict) {
-        return;
-    }
-
-    // assure no display channel is during global lz encoding
-    pthread_rwlock_wrlock(&glz_dict->encode_lock);
-    while ((ring_link = ring_get_head(&enc->glz_drawables))) {
-        RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
-        // no need to lock the to_free list, since we assured no other thread is encoding and
-        // thus not other thread access the to_free list of the channel
-        red_glz_drawable_free(drawable);
-    }
-    pthread_rwlock_unlock(&glz_dict->encode_lock);
-}
-
-void glz_retention_free_drawables(GlzImageRetention *ret)
-{
-    RingItem *glz_item, *next_item;
-    RedGlzDrawable *glz;
-    SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, LINK_TO_GLZ(glz_item)) {
-        red_glz_drawable_free(glz);
-    }
-}
-
-void glz_retention_detach_drawables(GlzImageRetention *ret)
-{
-    RingItem *item, *next;
-
-    RING_FOREACH_SAFE(item, next, &ret->ring) {
-        SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable = FALSE;
-        ring_remove(item);
-    }
-}
-
-static void image_encoders_freeze_glz(ImageEncoders *enc)
-{
-    pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
-    enc->glz_dict->migrate_freeze = TRUE;
-    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-}
-
-void image_encoders_glz_get_restore_data(ImageEncoders *enc,
-                                         uint8_t *out_id, GlzEncDictRestoreData *out_data)
-{
-    spice_assert(enc->glz_dict);
-    image_encoders_freeze_glz(enc);
-    *out_id = enc->glz_dict->id;
-    glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
-                                        &enc->glz_data.usr);
-}
-
-static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id,
-                                                      GlzEncDictContext *dict)
-{
-    spice_return_val_if_fail(dict != NULL, NULL);
-
-    GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
-
-    shared_dict->dict = dict;
-    shared_dict->id = id;
-    shared_dict->refs = 1;
-    shared_dict->migrate_freeze = FALSE;
-    shared_dict->client = client;
-    ring_item_init(&shared_dict->base);
-    pthread_rwlock_init(&shared_dict->encode_lock, NULL);
-
-    return shared_dict;
-}
-
-static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
-
-static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t dict_id)
-{
-    RingItem *now;
-    GlzSharedDictionary *ret = NULL;
-
-    now = &glz_dictionary_list;
-    while ((now = ring_next(&glz_dictionary_list, now))) {
-        GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
-        if ((dict->client == client) && (dict->id == dict_id)) {
-            ret = dict;
-            break;
-        }
-    }
-
-    return ret;
-}
-
-#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
-
-static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
-                                                  RedClient *client,
-                                                  uint8_t id, int window_size)
-{
-    spice_info("Lz Window %d Size=%d", id, window_size);
-
-    GlzEncDictContext *glz_dict =
-        glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc->glz_data.usr);
-
-    return glz_shared_dictionary_new(client, id, glz_dict);
-}
-
-gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
-                                           RedClient *client,
-                                           uint8_t id, int window_size)
-{
-    GlzSharedDictionary *shared_dict;
-
-    spice_return_val_if_fail(!enc->glz_dict, FALSE);
-
-    pthread_mutex_lock(&glz_dictionary_list_lock);
-
-    shared_dict = find_glz_dictionary(client, id);
-    if (shared_dict) {
-        shared_dict->refs++;
-    } else {
-        shared_dict = create_glz_dictionary(enc, client, id, window_size);
-        ring_add(&glz_dictionary_list, &shared_dict->base);
-    }
-
-    pthread_mutex_unlock(&glz_dictionary_list_lock);
-    enc->glz_dict = shared_dict;
-    return shared_dict != NULL;
-}
-
-static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
-                                                   RedClient *client,
-                                                   uint8_t id,
-                                                   GlzEncDictRestoreData *restore_data)
-{
-    GlzEncDictContext *glz_dict =
-        glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
-
-    return glz_shared_dictionary_new(client, id, glz_dict);
-}
-
-gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
-                                               RedClient *client,
-                                               uint8_t id,
-                                               GlzEncDictRestoreData *restore_data)
-{
-    GlzSharedDictionary *shared_dict = NULL;
-
-    spice_return_val_if_fail(!enc->glz_dict, FALSE);
-
-    pthread_mutex_lock(&glz_dictionary_list_lock);
-
-    shared_dict = find_glz_dictionary(client, id);
-
-    if (shared_dict) {
-        shared_dict->refs++;
-    } else {
-        shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
-        ring_add(&glz_dictionary_list, &shared_dict->base);
-    }
-
-    pthread_mutex_unlock(&glz_dictionary_list_lock);
-    enc->glz_dict = shared_dict;
-    return shared_dict != NULL;
-}
-
-gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
-{
-    enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc->glz_data.usr);
-    return enc->glz != NULL;
-}
-
-/* destroy encoder, and dictionary if no one uses it*/
-static void image_encoders_release_glz(ImageEncoders *enc)
-{
-    GlzSharedDictionary *shared_dict;
-
-    image_encoders_free_glz_drawables(enc);
-
-    glz_encoder_destroy(enc->glz);
-    enc->glz = NULL;
-
-    if (!(shared_dict = enc->glz_dict)) {
-        return;
-    }
-
-    enc->glz_dict = NULL;
-    pthread_mutex_lock(&glz_dictionary_list_lock);
-    if (--shared_dict->refs != 0) {
-        pthread_mutex_unlock(&glz_dictionary_list_lock);
-        return;
-    }
-    ring_remove(&shared_dict->base);
-    pthread_mutex_unlock(&glz_dictionary_list_lock);
-    glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
-    free(shared_dict);
-}
-
-int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
-                                 SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
-    QuicData *quic_data = &enc->quic_data;
-    QuicContext *quic = enc->quic;
-    volatile QuicImageType type;
-    int size, stride;
-    stat_start_time_t start_time;
-    stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
-
-#ifdef COMPRESS_DEBUG
-    spice_info("QUIC compress");
-#endif
-
-    switch (src->format) {
-    case SPICE_BITMAP_FMT_32BIT:
-        type = QUIC_IMAGE_TYPE_RGB32;
-        break;
-    case SPICE_BITMAP_FMT_RGBA:
-        type = QUIC_IMAGE_TYPE_RGBA;
-        break;
-    case SPICE_BITMAP_FMT_16BIT:
-        type = QUIC_IMAGE_TYPE_RGB16;
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        type = QUIC_IMAGE_TYPE_RGB24;
-        break;
-    default:
-        return FALSE;
-    }
-
-    encoder_data_init(&quic_data->data);
-
-    if (setjmp(quic_data->data.jmp_env)) {
-        encoder_data_reset(&quic_data->data);
-        return FALSE;
-    }
-
-    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
-        spice_chunks_linearize(src->data);
-    }
-
-    quic_data->data.u.lines_data.chunks = src->data;
-    quic_data->data.u.lines_data.stride = src->stride;
-    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 {
-        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.words,
-                       G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
-
-    // the compressed buffer is bigger than the original data
-    if ((size << 2) > (src->y * src->stride)) {
-        longjmp(quic_data->data.jmp_env, 1);
-    }
-
-    dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
-    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;
-
-    stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-static const LzImageType bitmap_fmt_to_lz_image_type[] = {
-    LZ_IMAGE_TYPE_INVALID,
-    LZ_IMAGE_TYPE_PLT1_LE,
-    LZ_IMAGE_TYPE_PLT1_BE,
-    LZ_IMAGE_TYPE_PLT4_LE,
-    LZ_IMAGE_TYPE_PLT4_BE,
-    LZ_IMAGE_TYPE_PLT8,
-    LZ_IMAGE_TYPE_RGB16,
-    LZ_IMAGE_TYPE_RGB24,
-    LZ_IMAGE_TYPE_RGB32,
-    LZ_IMAGE_TYPE_RGBA,
-    LZ_IMAGE_TYPE_A8
-};
-
-int image_encoders_compress_lz(ImageEncoders *enc,
-                               SpiceImage *dest, SpiceBitmap *src,
-                               compress_send_data_t* o_comp_data)
-{
-    LzData *lz_data = &enc->lz_data;
-    LzContext *lz = enc->lz;
-    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
-    int size;            // size of the compressed data
-
-    stat_start_time_t start_time;
-    stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
-
-#ifdef COMPRESS_DEBUG
-    spice_info("LZ LOCAL compress");
-#endif
-
-    encoder_data_init(&lz_data->data);
-
-    if (setjmp(lz_data->data.jmp_env)) {
-        encoder_data_reset(&lz_data->data);
-        return FALSE;
-    }
-
-    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;
-
-    size = lz_encode(lz, type, src->x, src->y,
-                     !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
-                     NULL, 0, src->stride,
-                     lz_data->data.bufs_head->buf.bytes,
-                     sizeof(lz_data->data.bufs_head->buf));
-
-    // the compressed buffer is bigger than the original data
-    if (size > (src->y * src->stride)) {
-        longjmp(lz_data->data.jmp_env, 1);
-    }
-
-    if (bitmap_fmt_is_rgb(src->format)) {
-        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
-        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 {
-        /* masks are 1BIT bitmaps without palettes, but they are not compressed
-         * (see fill_mask) */
-        spice_assert(src->palette);
-        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
-        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;
-
-        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
-    }
-
-    stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
-                                 SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
-    JpegData *jpeg_data = &enc->jpeg_data;
-    LzData *lz_data = &enc->lz_data;
-    JpegEncoderContext *jpeg = enc->jpeg;
-    LzContext *lz = enc->lz;
-    volatile JpegEncoderImageType jpeg_in_type;
-    int jpeg_size = 0;
-    volatile int has_alpha = FALSE;
-    int alpha_lz_size = 0;
-    int comp_head_filled;
-    int comp_head_left;
-    int stride;
-    uint8_t *lz_out_start_byte;
-    stat_start_time_t start_time;
-    stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
-
-#ifdef COMPRESS_DEBUG
-    spice_info("JPEG compress");
-#endif
-
-    switch (src->format) {
-    case SPICE_BITMAP_FMT_16BIT:
-        jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
-        break;
-    case SPICE_BITMAP_FMT_32BIT:
-        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
-        break;
-    case SPICE_BITMAP_FMT_RGBA:
-        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
-        has_alpha = TRUE;
-        break;
-    default:
-        return FALSE;
-    }
-
-    encoder_data_init(&jpeg_data->data);
-
-    if (setjmp(jpeg_data->data.jmp_env)) {
-        encoder_data_reset(&jpeg_data->data);
-        return FALSE;
-    }
-
-    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
-        spice_chunks_linearize(src->data);
-    }
-
-    jpeg_data->data.u.lines_data.chunks = src->data;
-    jpeg_data->data.u.lines_data.stride = src->stride;
-    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 {
-        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, enc->jpeg_quality, jpeg_in_type,
-                            src->x, src->y, NULL,
-                            0, stride, jpeg_data->data.bufs_head->buf.bytes,
-                            sizeof(jpeg_data->data.bufs_head->buf));
-
-    // the compressed buffer is bigger than the original data
-    if (jpeg_size > (src->y * src->stride)) {
-        longjmp(jpeg_data->data.jmp_env, 1);
-    }
-
-    if (!has_alpha) {
-        dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
-        dest->u.jpeg.data_size = jpeg_size;
-
-        o_comp_data->comp_buf = jpeg_data->data.bufs_head;
-        o_comp_data->comp_buf_size = jpeg_size;
-        o_comp_data->is_lossy = TRUE;
-
-        stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src->stride * src->y,
-                          o_comp_data->comp_buf_size);
-        return TRUE;
-    }
-
-    lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
-    lz_data->data.bufs_tail = lz_data->data.bufs_head;
-
-    comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
-    comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
-    lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + comp_head_filled;
-
-    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;
-
-    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)) {
-        longjmp(jpeg_data->data.jmp_env, 1);
-    }
-
-    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
-    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->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;
-    o_comp_data->is_lossy = TRUE;
-    stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-
-#ifdef USE_LZ4
-int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
-                                SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
-    Lz4Data *lz4_data = &enc->lz4_data;
-    Lz4EncoderContext *lz4 = enc->lz4;
-    int lz4_size = 0;
-    stat_start_time_t start_time;
-    stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
-
-#ifdef COMPRESS_DEBUG
-    spice_info("LZ4 compress");
-#endif
-
-    encoder_data_init(&lz4_data->data);
-
-    if (setjmp(lz4_data->data.jmp_env)) {
-        encoder_data_reset(&lz4_data->data);
-        return FALSE;
-    }
-
-    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
-        spice_chunks_linearize(src->data);
-    }
-
-    lz4_data->data.u.lines_data.chunks = src->data;
-    lz4_data->data.u.lines_data.stride = src->stride;
-    lz4_data->data.u.lines_data.next = 0;
-    lz4_data->data.u.lines_data.reverse = 0;
-
-    lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head->buf.bytes,
-                          sizeof(lz4_data->data.bufs_head->buf),
-                          src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src->format);
-
-    // the compressed buffer is bigger than the original data
-    if (lz4_size > (src->y * src->stride)) {
-        longjmp(lz4_data->data.jmp_env, 1);
-    }
-
-    dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
-    dest->u.lz4.data_size = lz4_size;
-
-    o_comp_data->comp_buf = lz4_data->data.bufs_head;
-    o_comp_data->comp_buf_size = lz4_size;
-
-    stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride * src->y,
-                      o_comp_data->comp_buf_size);
-    return TRUE;
-}
-#endif
-
-/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
-   in the channel (2) to the Drawable*/
-static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable *red_drawable,
-                                        GlzImageRetention *glz_retention)
-{
-    RedGlzDrawable *ret;
-    RingItem *item, *next;
-
-    // TODO - I don't really understand what's going on here, so doing the technical equivalent
-    // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
-    // over the glz_ring (unless adding some better data structure then a ring)
-    SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, LINK_TO_GLZ(item)) {
-        if (ret->encoders == enc) {
-            return ret;
-        }
-    }
-
-    ret = spice_new(RedGlzDrawable, 1);
-
-    ret->encoders = enc;
-    ret->red_drawable = red_drawable_ref(red_drawable);
-    ret->has_drawable = TRUE;
-    ret->instances_count = 0;
-    ring_init(&ret->instances);
-
-    ring_item_init(&ret->link);
-    ring_item_init(&ret->drawable_link);
-    ring_add_before(&ret->link, &enc->glz_drawables);
-    ring_add(&glz_retention->ring, &ret->drawable_link);
-    enc->shared_data->glz_drawable_count++;
-    return ret;
-}
-
-/* allocates new instance and adds it to instances in the given drawable.
-   NOTE - the caller should set the glz_instance returned by the encoder by itself.*/
-static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable *glz_drawable)
-{
-    spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
-    // NOTE: We assume the additions are performed consecutively, without removals in the middle
-    GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + glz_drawable->instances_count;
-    glz_drawable->instances_count++;
-
-    ring_item_init(&ret->free_link);
-    ring_item_init(&ret->glz_link);
-    ring_add(&glz_drawable->instances, &ret->glz_link);
-    ret->context = NULL;
-    ret->glz_drawable = glz_drawable;
-
-    return ret;
-}
-
-#define MIN_GLZ_SIZE_FOR_ZLIB 100
-
-int image_encoders_compress_glz(ImageEncoders *enc,
-                                SpiceImage *dest, SpiceBitmap *src,
-                                RedDrawable *red_drawable,
-                                GlzImageRetention *glz_retention,
-                                compress_send_data_t* o_comp_data,
-                                gboolean enable_zlib_glz_wrap)
-{
-    stat_start_time_t start_time;
-    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
-    spice_assert(bitmap_fmt_is_rgb(src->format));
-    GlzData *glz_data = &enc->glz_data;
-    ZlibData *zlib_data;
-    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
-    RedGlzDrawable *glz_drawable;
-    GlzDrawableInstanceItem *glz_drawable_instance;
-    int glz_size;
-    int zlib_size;
-
-#ifdef COMPRESS_DEBUG
-    spice_info("LZ global compress fmt=%d", src->format);
-#endif
-
-    if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict->dict)) {
-        return FALSE;
-    }
-
-    pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
-    /* using the global dictionary only if it is not frozen */
-    if (enc->glz_dict->migrate_freeze) {
-        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-        return FALSE;
-    }
-
-    encoder_data_init(&glz_data->data);
-
-    glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
-    glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
-
-    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_size = glz_encode(enc->glz, type, src->x, src->y,
-                          (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
-                          src->stride, glz_data->data.bufs_head->buf.bytes,
-                          sizeof(glz_data->data.bufs_head->buf),
-                          glz_drawable_instance,
-                          &glz_drawable_instance->context);
-
-    stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * src->y, glz_size);
-
-    if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
-        goto glz;
-    }
-    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
-    zlib_data = &enc->zlib_data;
-
-    encoder_data_init(&zlib_data->data);
-
-    zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
-    zlib_data->data.u.compressed_data.size_left = glz_size;
-
-    zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
-                            glz_size, zlib_data->data.bufs_head->buf.bytes,
-                            sizeof(zlib_data->data.bufs_head->buf));
-
-    // the compressed buffer is bigger than the original data
-    if (zlib_size >= glz_size) {
-        encoder_data_reset(&zlib_data->data);
-        goto glz;
-    } else {
-        encoder_data_reset(&glz_data->data);
-    }
-
-    dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
-    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;
-
-    stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size, zlib_size);
-    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-    return TRUE;
-
-glz:
-    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-
-    dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
-    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;
-
-    return TRUE;
-}
-
-void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
-{
-    clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
-
-    stat_compress_init(&shared_data->off_stat, "off", stat_clock);
-    stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
-    stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
-    stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
-    stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
-    stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
-    stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", stat_clock);
-    stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
-}
-
-void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
-{
-    stat_reset(&shared_data->off_stat);
-    stat_reset(&shared_data->quic_stat);
-    stat_reset(&shared_data->lz_stat);
-    stat_reset(&shared_data->glz_stat);
-    stat_reset(&shared_data->jpeg_stat);
-    stat_reset(&shared_data->zlib_glz_stat);
-    stat_reset(&shared_data->jpeg_alpha_stat);
-    stat_reset(&shared_data->lz4_stat);
-}
-
-#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
-
-#ifdef COMPRESS_STAT
-static void stat_print_one(const char *name, const stat_info_t *stat)
-{
-    spice_info(STAT_FMT, name, stat->count,
-               stat_byte_to_mega(stat->orig_size),
-               stat_byte_to_mega(stat->comp_size),
-               stat_cpu_time_to_sec(stat->total));
-}
-
-static void stat_sum(stat_info_t *total, const stat_info_t *stat)
-{
-    total->count += stat->count;
-    total->orig_size += stat->orig_size;
-    total->comp_size += stat->comp_size;
-    total->total += stat->total;
-}
-#endif
-
-void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data)
-{
-#ifdef COMPRESS_STAT
-    /* sum all statistics */
-    stat_info_t total = {
-        .count = 0,
-        .orig_size = 0,
-        .comp_size = 0,
-        .total = 0
-    };
-    stat_sum(&total, &shared_data->off_stat);
-    stat_sum(&total, &shared_data->quic_stat);
-    stat_sum(&total, &shared_data->glz_stat);
-    stat_sum(&total, &shared_data->lz_stat);
-    stat_sum(&total, &shared_data->jpeg_stat);
-    stat_sum(&total, &shared_data->jpeg_alpha_stat);
-    stat_sum(&total, &shared_data->lz4_stat);
-
-    /* fix for zlib glz */
-    total.total += shared_data->zlib_glz_stat.total;
-    if (shared_data->zlib_glz_stat.count) {
-        total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
-                          shared_data->zlib_glz_stat.comp_size;
-    }
-
-    spice_info("Method   \t  count  \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
-    stat_print_one("OFF      ", &shared_data->off_stat);
-    stat_print_one("QUIC     ", &shared_data->quic_stat);
-    stat_print_one("GLZ      ", &shared_data->glz_stat);
-    stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
-    stat_print_one("LZ       ", &shared_data->lz_stat);
-    stat_print_one("JPEG     ", &shared_data->jpeg_stat);
-    stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
-    stat_print_one("LZ4      ", &shared_data->lz4_stat);
-    spice_info("-------------------------------------------------------------------");
-    stat_print_one("Total    ", &total);
-#endif
-}
diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
deleted file mode 100644
index 9286970..0000000
--- a/server/dcc-encoders.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#ifndef DCC_ENCODERS_H_
-#define DCC_ENCODERS_H_
-
-#include <setjmp.h>
-#include <common/quic.h>
-#include <common/lz.h>
-
-#include "stat.h"
-#include "red-parse-qxl.h"
-#include "glz-encoder.h"
-#include "jpeg-encoder.h"
-#ifdef USE_LZ4
-#include "lz4-encoder.h"
-#endif
-#include "zlib-encoder.h"
-
-struct RedClient;
-
-typedef struct RedCompressBuf RedCompressBuf;
-typedef struct RedGlzDrawable RedGlzDrawable;
-typedef struct ImageEncoders ImageEncoders;
-typedef struct ImageEncoderSharedData ImageEncoderSharedData;
-typedef struct GlzSharedDictionary GlzSharedDictionary;
-typedef struct GlzImageRetention GlzImageRetention;
-
-void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
-void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
-void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data);
-
-void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data);
-void image_encoders_free(ImageEncoders *enc);
-int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
-void image_encoders_free_glz_drawables(ImageEncoders *enc);
-void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
-gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
-void image_encoders_glz_get_restore_data(ImageEncoders *enc,
-                                         uint8_t *out_id, GlzEncDictRestoreData *out_data);
-gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
-void image_encoders_glz_encode_unlock(ImageEncoders *enc);
-void glz_retention_free_drawables(GlzImageRetention *ret);
-void glz_retention_detach_drawables(GlzImageRetention *ret);
-
-#define RED_COMPRESS_BUF_SIZE (1024 * 64)
-struct RedCompressBuf {
-    /* This buffer provide space for compression algorithms.
-     * Some algorithms access the buffer as an array of 32 bit words
-     * so is defined to make sure is always aligned that way.
-     */
-    union {
-        uint8_t  bytes[RED_COMPRESS_BUF_SIZE];
-        uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
-    } buf;
-    RedCompressBuf *send_next;
-};
-
-static inline void compress_buf_free(RedCompressBuf *buf)
-{
-    g_free(buf);
-}
-
-gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
-                                           struct RedClient *client,
-                                           uint8_t id, int window_size);
-gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
-                                               struct RedClient *client,
-                                               uint8_t id,
-                                               GlzEncDictRestoreData *restore_data);
-
-typedef struct  {
-    RedCompressBuf *bufs_head;
-    RedCompressBuf *bufs_tail;
-    jmp_buf jmp_env;
-    union {
-        struct {
-            SpiceChunks *chunks;
-            int next;
-            int stride;
-            int reverse;
-        } lines_data;
-        struct {
-            RedCompressBuf* next;
-            int size_left;
-        } compressed_data; // for encoding data that was already compressed by another method
-    } u;
-} EncoderData;
-
-typedef struct {
-    QuicUsrContext usr;
-    EncoderData data;
-} QuicData;
-
-typedef struct {
-    LzUsrContext usr;
-    EncoderData data;
-} LzData;
-
-typedef struct {
-    JpegEncoderUsrContext usr;
-    EncoderData data;
-} JpegData;
-
-#ifdef USE_LZ4
-typedef struct {
-    Lz4EncoderUsrContext usr;
-    EncoderData data;
-} Lz4Data;
-#endif
-
-typedef struct {
-    ZlibEncoderUsrContext usr;
-    EncoderData data;
-} ZlibData;
-
-typedef struct {
-    GlzEncoderUsrContext usr;
-    EncoderData data;
-} GlzData;
-
-struct GlzImageRetention {
-    Ring ring;
-};
-
-static inline void glz_retention_init(GlzImageRetention *ret)
-{
-    ring_init(&ret->ring);
-}
-
-struct ImageEncoderSharedData {
-    uint32_t glz_drawable_count;
-
-    stat_info_t off_stat;
-    stat_info_t lz_stat;
-    stat_info_t glz_stat;
-    stat_info_t quic_stat;
-    stat_info_t jpeg_stat;
-    stat_info_t zlib_glz_stat;
-    stat_info_t jpeg_alpha_stat;
-    stat_info_t lz4_stat;
-};
-
-struct ImageEncoders {
-    ImageEncoderSharedData *shared_data;
-
-    QuicData quic_data;
-    QuicContext *quic;
-
-    LzData lz_data;
-    LzContext  *lz;
-
-    int jpeg_quality;
-
-    JpegData jpeg_data;
-    JpegEncoderContext *jpeg;
-
-#ifdef USE_LZ4
-    Lz4Data lz4_data;
-    Lz4EncoderContext *lz4;
-#endif
-
-    int zlib_level;
-
-    ZlibData zlib_data;
-    ZlibEncoder *zlib;
-
-    /* global lz encoding entities */
-    GlzSharedDictionary *glz_dict;
-    GlzEncoderContext *glz;
-    GlzData glz_data;
-
-    Ring glz_drawables;               // all the living lz drawable, ordered by encoding time
-    Ring glz_drawables_inst_to_free;               // list of instances to be freed
-    pthread_mutex_t glz_drawables_inst_to_free_lock;
-};
-
-typedef struct compress_send_data_t {
-    void* comp_buf;
-    uint32_t comp_buf_size;
-    SpicePalette *lzplt_palette;
-    int is_lossy;
-} compress_send_data_t;
-
-int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
-                                 SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_lz(ImageEncoders *enc,
-                               SpiceImage *dest, SpiceBitmap *src,
-                               compress_send_data_t* o_comp_data);
-int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
-                                 SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
-                                SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_glz(ImageEncoders *enc,
-                                SpiceImage *dest, SpiceBitmap *src,
-                                RedDrawable *red_drawable,
-                                GlzImageRetention *glz_retention,
-                                compress_send_data_t* o_comp_data,
-                                gboolean enable_zlib_glz_wrap);
-
-#define RED_RELEASE_BUNCH_SIZE 64
-
-#endif /* DCC_ENCODERS_H_ */
diff --git a/server/dcc.h b/server/dcc.h
index 0c19c02..5f7b16f 100644
--- a/server/dcc.h
+++ b/server/dcc.h
@@ -21,7 +21,7 @@
 #include "red-worker.h"
 #include "pixmap-cache.h"
 #include "cache-item.h"
-#include "dcc-encoders.h"
+#include "image-encoders.h"
 #include "stream.h"
 #include "display-limits.h"
 
diff --git a/server/image-encoders.c b/server/image-encoders.c
new file mode 100644
index 0000000..b47ae35
--- /dev/null
+++ b/server/image-encoders.c
@@ -0,0 +1,1388 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "image-encoders.h"
+#include "spice-bitmap-utils.h"
+#include "red-worker.h" // red_drawable_unref
+#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
+
+#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
+
+#define ENCODER_MESSAGE_SIZE 512
+
+#define MAX_GLZ_DRAWABLE_INSTANCES 2
+
+typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
+
+struct GlzSharedDictionary {
+    RingItem base;
+    GlzEncDictContext *dict;
+    uint32_t refs;
+    uint8_t id;
+    pthread_rwlock_t encode_lock;
+    int migrate_freeze;
+    RedClient *client; // channel clients of the same client share the dict
+};
+
+/* for each qxl drawable, there may be several instances of lz drawables */
+/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
+ * at the Drawable by keeping a ring, so:
+ * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
+ * and it should probably (but need to be sure...) be
+ * Drawable -> ring of GlzDrawableInstanceItem.
+ */
+struct GlzDrawableInstanceItem {
+    RingItem glz_link;
+    RingItem free_link;
+    GlzEncDictImageContext *context;
+    RedGlzDrawable         *glz_drawable;
+};
+
+struct RedGlzDrawable {
+    RingItem link;    // ordered by the time it was encoded
+    RingItem drawable_link;
+    RedDrawable *red_drawable;
+    GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
+    Ring instances;
+    uint8_t instances_count;
+    gboolean has_drawable;
+    ImageEncoders *encoders;
+};
+
+#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
+                                           drawable_link)
+#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
+    SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, LINK_TO_GLZ(link))
+
+static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance);
+static void encoder_data_init(EncoderData *data);
+static void encoder_data_reset(EncoderData *data);
+static void image_encoders_release_glz(ImageEncoders *enc);
+
+
+static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
+quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
+{
+    EncoderData *usr_data = &(((QuicData *)usr)->data);
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+    spice_critical("%s", message_buf);
+
+    longjmp(usr_data->jmp_env, 1);
+}
+
+static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
+lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
+{
+    EncoderData *usr_data = &(((LzData *)usr)->data);
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+    spice_critical("%s", message_buf);
+
+    longjmp(usr_data->jmp_env, 1);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
+{
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+
+    spice_critical("%s", message_buf); // if global lz fails in the middle
+                                        // the consequences are not predictable since the window
+                                        // can turn to be unsynchronized between the server and
+                                        // and the client
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
+{
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+    spice_warning("%s", message_buf);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
+{
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+    spice_warning("%s", message_buf);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
+{
+    va_list ap;
+    char message_buf[ENCODER_MESSAGE_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+    va_end(ap);
+    spice_warning("%s", message_buf);
+}
+
+static void *quic_usr_malloc(QuicUsrContext *usr, int size)
+{
+    return spice_malloc(size);
+}
+
+static void *lz_usr_malloc(LzUsrContext *usr, int size)
+{
+    return spice_malloc(size);
+}
+
+static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
+{
+    return spice_malloc(size);
+}
+
+static void quic_usr_free(QuicUsrContext *usr, void *ptr)
+{
+    free(ptr);
+}
+
+static void lz_usr_free(LzUsrContext *usr, void *ptr)
+{
+    free(ptr);
+}
+
+static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
+{
+    free(ptr);
+}
+
+static void encoder_data_init(EncoderData *data)
+{
+    data->bufs_tail = g_new(RedCompressBuf, 1);
+    data->bufs_head = data->bufs_tail;
+    data->bufs_head->send_next = NULL;
+}
+
+static void encoder_data_reset(EncoderData *data)
+{
+    RedCompressBuf *buf = data->bufs_head;
+    while (buf) {
+        RedCompressBuf *next = buf->send_next;
+        g_free(buf);
+        buf = next;
+    }
+    data->bufs_head = data->bufs_tail = NULL;
+}
+
+/* Allocate more space for compressed buffer.
+ * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
+ */
+static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
+{
+    RedCompressBuf *buf;
+
+    buf = g_new(RedCompressBuf, 1);
+    enc_data->bufs_tail->send_next = buf;
+    enc_data->bufs_tail = buf;
+    buf->send_next = NULL;
+    *io_ptr = buf->buf.bytes;
+    return sizeof(buf->buf);
+}
+
+static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
+{
+    EncoderData *usr_data = &(((QuicData *)usr)->data);
+    return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / sizeof(uint32_t);
+}
+
+static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((LzData *)usr)->data);
+    return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((GlzData *)usr)->data);
+    return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((JpegData *)usr)->data);
+    return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+#ifdef USE_LZ4
+static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+    return encoder_usr_more_space(usr_data, io_ptr);
+}
+#endif
+
+static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+    EncoderData *usr_data = &(((ZlibData *)usr)->data);
+    return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
+{
+    struct SpiceChunk *chunk;
+
+    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;
+        }
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    return chunk->len / enc_data->u.lines_data.stride;
+}
+
+static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((QuicData *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((LzData *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((GlzData *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((JpegData *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+
+#ifdef USE_LZ4
+static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
+{
+    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+    return encoder_usr_more_lines(usr_data, lines);
+}
+#endif
+
+static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
+{
+    EncoderData *usr_data = &(((ZlibData *)usr)->data);
+    int buf_size;
+
+    if (!usr_data->u.compressed_data.next) {
+        spice_assert(usr_data->u.compressed_data.size_left == 0);
+        return 0;
+    }
+
+    *input = usr_data->u.compressed_data.next->buf.bytes;
+    buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
+                   usr_data->u.compressed_data.size_left);
+
+    usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
+    usr_data->u.compressed_data.size_left -= buf_size;
+    return buf_size;
+}
+
+static void image_encoders_init_quic(ImageEncoders *enc)
+{
+    enc->quic_data.usr.error = quic_usr_error;
+    enc->quic_data.usr.warn = quic_usr_warn;
+    enc->quic_data.usr.info = quic_usr_warn;
+    enc->quic_data.usr.malloc = quic_usr_malloc;
+    enc->quic_data.usr.free = quic_usr_free;
+    enc->quic_data.usr.more_space = quic_usr_more_space;
+    enc->quic_data.usr.more_lines = quic_usr_more_lines;
+
+    enc->quic = quic_create(&enc->quic_data.usr);
+
+    if (!enc->quic) {
+        spice_critical("create quic failed");
+    }
+}
+
+static void image_encoders_init_lz(ImageEncoders *enc)
+{
+    enc->lz_data.usr.error = lz_usr_error;
+    enc->lz_data.usr.warn = lz_usr_warn;
+    enc->lz_data.usr.info = lz_usr_warn;
+    enc->lz_data.usr.malloc = lz_usr_malloc;
+    enc->lz_data.usr.free = lz_usr_free;
+    enc->lz_data.usr.more_space = lz_usr_more_space;
+    enc->lz_data.usr.more_lines = lz_usr_more_lines;
+
+    enc->lz = lz_create(&enc->lz_data.usr);
+
+    if (!enc->lz) {
+        spice_critical("create lz failed");
+    }
+}
+
+static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
+{
+    GlzData *lz_data = (GlzData *)usr;
+    GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image;
+    ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable->encoders;
+    ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, glz_data);
+    if (this_enc == drawable_enc) {
+        glz_drawable_instance_item_free(glz_drawable_instance);
+    } else {
+        /* The glz dictionary is shared between all DisplayChannelClient
+         * instances that belong to the same client, and glz_usr_free_image
+         * can be called by the dictionary code
+         * (glz_dictionary_window_remove_head). Thus this function can be
+         * called from any DisplayChannelClient thread, hence the need for
+         * this check.
+         */
+        pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
+        ring_add_before(&glz_drawable_instance->free_link,
+                        &drawable_enc->glz_drawables_inst_to_free);
+        pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
+    }
+}
+
+static void image_encoders_init_glz_data(ImageEncoders *enc)
+{
+    enc->glz_data.usr.error = glz_usr_error;
+    enc->glz_data.usr.warn = glz_usr_warn;
+    enc->glz_data.usr.info = glz_usr_warn;
+    enc->glz_data.usr.malloc = glz_usr_malloc;
+    enc->glz_data.usr.free = glz_usr_free;
+    enc->glz_data.usr.more_space = glz_usr_more_space;
+    enc->glz_data.usr.more_lines = glz_usr_more_lines;
+    enc->glz_data.usr.free_image = glz_usr_free_image;
+}
+
+static void image_encoders_init_jpeg(ImageEncoders *enc)
+{
+    enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
+    enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
+
+    enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
+
+    if (!enc->jpeg) {
+        spice_critical("create jpeg encoder failed");
+    }
+}
+
+#ifdef USE_LZ4
+static inline void image_encoders_init_lz4(ImageEncoders *enc)
+{
+    enc->lz4_data.usr.more_space = lz4_usr_more_space;
+    enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
+
+    enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
+
+    if (!enc->lz4) {
+        spice_critical("create lz4 encoder failed");
+    }
+}
+#endif
+
+static void image_encoders_init_zlib(ImageEncoders *enc)
+{
+    enc->zlib_data.usr.more_space = zlib_usr_more_space;
+    enc->zlib_data.usr.more_input = zlib_usr_more_input;
+
+    enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL);
+
+    if (!enc->zlib) {
+        spice_critical("create zlib encoder failed");
+    }
+}
+
+void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data)
+{
+    spice_assert(shared_data);
+    enc->shared_data = shared_data;
+
+    ring_init(&enc->glz_drawables);
+    ring_init(&enc->glz_drawables_inst_to_free);
+    pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
+
+    image_encoders_init_glz_data(enc);
+    image_encoders_init_quic(enc);
+    image_encoders_init_lz(enc);
+    image_encoders_init_jpeg(enc);
+#ifdef USE_LZ4
+    image_encoders_init_lz4(enc);
+#endif
+    image_encoders_init_zlib(enc);
+
+    // todo: tune level according to bandwidth
+    enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
+}
+
+void image_encoders_free(ImageEncoders *enc)
+{
+    image_encoders_release_glz(enc);
+    quic_destroy(enc->quic);
+    enc->quic = NULL;
+    lz_destroy(enc->lz);
+    enc->lz = NULL;
+    jpeg_encoder_destroy(enc->jpeg);
+    enc->jpeg = NULL;
+#ifdef USE_LZ4
+    lz4_encoder_destroy(enc->lz4);
+    enc->lz4 = NULL;
+#endif
+    zlib_encoder_destroy(enc->zlib);
+    enc->zlib = NULL;
+}
+
+/* Remove from the to_free list and the instances_list.
+   When no instance is left - the RedGlzDrawable is released too. (and the qxl drawable too, if
+   it is not used by Drawable).
+   NOTE - 1) can be called only by the display channel that created the drawable
+          2) it is assumed that the instance was already removed from the dictionary*/
+static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance)
+{
+    RedGlzDrawable *glz_drawable;
+
+    spice_assert(instance);
+    spice_assert(instance->glz_drawable);
+
+    glz_drawable = instance->glz_drawable;
+
+    spice_assert(glz_drawable->instances_count > 0);
+
+    ring_remove(&instance->glz_link);
+    glz_drawable->instances_count--;
+
+    // when the remove callback is performed from the channel that the
+    // drawable belongs to, the instance is not added to the 'to_free' list
+    if (ring_item_is_linked(&instance->free_link)) {
+        ring_remove(&instance->free_link);
+    }
+
+    if (ring_is_empty(&glz_drawable->instances)) {
+        spice_assert(glz_drawable->instances_count == 0);
+
+        if (glz_drawable->has_drawable) {
+            ring_remove(&glz_drawable->drawable_link);
+        }
+        red_drawable_unref(glz_drawable->red_drawable);
+        glz_drawable->encoders->shared_data->glz_drawable_count--;
+        if (ring_item_is_linked(&glz_drawable->link)) {
+            ring_remove(&glz_drawable->link);
+        }
+        free(glz_drawable);
+    }
+}
+
+/*
+ * Releases all the instances of the drawable from the dictionary and the display channel client.
+ * The release of the last instance will also release the drawable itself and the qxl drawable
+ * if possible.
+ * NOTE - the caller should prevent encoding using the dictionary during this operation
+ */
+static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
+{
+    ImageEncoders *enc = glz_drawable->encoders;
+    RingItem *head_instance = ring_get_head(&glz_drawable->instances);
+    int cont = (head_instance != NULL);
+
+    while (cont) {
+        if (glz_drawable->instances_count == 1) {
+            /* Last instance: glz_drawable_instance_item_free will free the glz_drawable */
+            cont = FALSE;
+        }
+        GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
+                                                        GlzDrawableInstanceItem,
+                                                        glz_link);
+        if (!ring_item_is_linked(&instance->free_link)) {
+            // the instance didn't get out from window yet
+            glz_enc_dictionary_remove_image(enc->glz_dict->dict,
+                                            instance->context,
+                                            &enc->glz_data.usr);
+        }
+        glz_drawable_instance_item_free(instance);
+
+        if (cont) {
+            head_instance = ring_get_head(&glz_drawable->instances);
+        }
+    }
+}
+
+gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
+{
+    if (enc->glz_dict) {
+        pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void image_encoders_glz_encode_unlock(ImageEncoders *enc)
+{
+    if (enc->glz_dict) {
+        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+    }
+}
+
+/*
+ * Remove from the global lz dictionary some glz_drawables that have no reference to
+ * Drawable (their qxl drawables are released too).
+ * NOTE - the caller should prevent encoding using the dictionary during the operation
+ */
+int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
+{
+    RingItem *ring_link;
+    int n = 0;
+
+    if (!enc) {
+        return 0;
+    }
+    ring_link = ring_get_head(&enc->glz_drawables);
+    while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
+        RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
+        ring_link = ring_next(&enc->glz_drawables, ring_link);
+        if (!glz_drawable->has_drawable) {
+            red_glz_drawable_free(glz_drawable);
+            n++;
+        }
+    }
+    return n;
+}
+
+void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
+{
+    RingItem *ring_link;
+
+    if (!enc->glz_dict) {
+        return;
+    }
+    pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
+    while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
+        GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link,
+                                                                 GlzDrawableInstanceItem,
+                                                                 free_link);
+        glz_drawable_instance_item_free(drawable_instance);
+    }
+    pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
+}
+
+/* Clear all lz drawables - enforce their removal from the global dictionary.
+   NOTE - prevents encoding using the dictionary during the operation*/
+void image_encoders_free_glz_drawables(ImageEncoders *enc)
+{
+    RingItem *ring_link;
+    GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
+
+    if (!glz_dict) {
+        return;
+    }
+
+    // assure no display channel is during global lz encoding
+    pthread_rwlock_wrlock(&glz_dict->encode_lock);
+    while ((ring_link = ring_get_head(&enc->glz_drawables))) {
+        RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
+        // no need to lock the to_free list, since we assured no other thread is encoding and
+        // thus not other thread access the to_free list of the channel
+        red_glz_drawable_free(drawable);
+    }
+    pthread_rwlock_unlock(&glz_dict->encode_lock);
+}
+
+void glz_retention_free_drawables(GlzImageRetention *ret)
+{
+    RingItem *glz_item, *next_item;
+    RedGlzDrawable *glz;
+    SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, LINK_TO_GLZ(glz_item)) {
+        red_glz_drawable_free(glz);
+    }
+}
+
+void glz_retention_detach_drawables(GlzImageRetention *ret)
+{
+    RingItem *item, *next;
+
+    RING_FOREACH_SAFE(item, next, &ret->ring) {
+        SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable = FALSE;
+        ring_remove(item);
+    }
+}
+
+static void image_encoders_freeze_glz(ImageEncoders *enc)
+{
+    pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
+    enc->glz_dict->migrate_freeze = TRUE;
+    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+}
+
+void image_encoders_glz_get_restore_data(ImageEncoders *enc,
+                                         uint8_t *out_id, GlzEncDictRestoreData *out_data)
+{
+    spice_assert(enc->glz_dict);
+    image_encoders_freeze_glz(enc);
+    *out_id = enc->glz_dict->id;
+    glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
+                                        &enc->glz_data.usr);
+}
+
+static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id,
+                                                      GlzEncDictContext *dict)
+{
+    spice_return_val_if_fail(dict != NULL, NULL);
+
+    GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
+
+    shared_dict->dict = dict;
+    shared_dict->id = id;
+    shared_dict->refs = 1;
+    shared_dict->migrate_freeze = FALSE;
+    shared_dict->client = client;
+    ring_item_init(&shared_dict->base);
+    pthread_rwlock_init(&shared_dict->encode_lock, NULL);
+
+    return shared_dict;
+}
+
+static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
+
+static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t dict_id)
+{
+    RingItem *now;
+    GlzSharedDictionary *ret = NULL;
+
+    now = &glz_dictionary_list;
+    while ((now = ring_next(&glz_dictionary_list, now))) {
+        GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
+        if ((dict->client == client) && (dict->id == dict_id)) {
+            ret = dict;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
+
+static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
+                                                  RedClient *client,
+                                                  uint8_t id, int window_size)
+{
+    spice_info("Lz Window %d Size=%d", id, window_size);
+
+    GlzEncDictContext *glz_dict =
+        glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc->glz_data.usr);
+
+    return glz_shared_dictionary_new(client, id, glz_dict);
+}
+
+gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
+                                           RedClient *client,
+                                           uint8_t id, int window_size)
+{
+    GlzSharedDictionary *shared_dict;
+
+    spice_return_val_if_fail(!enc->glz_dict, FALSE);
+
+    pthread_mutex_lock(&glz_dictionary_list_lock);
+
+    shared_dict = find_glz_dictionary(client, id);
+    if (shared_dict) {
+        shared_dict->refs++;
+    } else {
+        shared_dict = create_glz_dictionary(enc, client, id, window_size);
+        ring_add(&glz_dictionary_list, &shared_dict->base);
+    }
+
+    pthread_mutex_unlock(&glz_dictionary_list_lock);
+    enc->glz_dict = shared_dict;
+    return shared_dict != NULL;
+}
+
+static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
+                                                   RedClient *client,
+                                                   uint8_t id,
+                                                   GlzEncDictRestoreData *restore_data)
+{
+    GlzEncDictContext *glz_dict =
+        glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
+
+    return glz_shared_dictionary_new(client, id, glz_dict);
+}
+
+gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
+                                               RedClient *client,
+                                               uint8_t id,
+                                               GlzEncDictRestoreData *restore_data)
+{
+    GlzSharedDictionary *shared_dict = NULL;
+
+    spice_return_val_if_fail(!enc->glz_dict, FALSE);
+
+    pthread_mutex_lock(&glz_dictionary_list_lock);
+
+    shared_dict = find_glz_dictionary(client, id);
+
+    if (shared_dict) {
+        shared_dict->refs++;
+    } else {
+        shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
+        ring_add(&glz_dictionary_list, &shared_dict->base);
+    }
+
+    pthread_mutex_unlock(&glz_dictionary_list_lock);
+    enc->glz_dict = shared_dict;
+    return shared_dict != NULL;
+}
+
+gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
+{
+    enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc->glz_data.usr);
+    return enc->glz != NULL;
+}
+
+/* destroy encoder, and dictionary if no one uses it*/
+static void image_encoders_release_glz(ImageEncoders *enc)
+{
+    GlzSharedDictionary *shared_dict;
+
+    image_encoders_free_glz_drawables(enc);
+
+    glz_encoder_destroy(enc->glz);
+    enc->glz = NULL;
+
+    if (!(shared_dict = enc->glz_dict)) {
+        return;
+    }
+
+    enc->glz_dict = NULL;
+    pthread_mutex_lock(&glz_dictionary_list_lock);
+    if (--shared_dict->refs != 0) {
+        pthread_mutex_unlock(&glz_dictionary_list_lock);
+        return;
+    }
+    ring_remove(&shared_dict->base);
+    pthread_mutex_unlock(&glz_dictionary_list_lock);
+    glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
+    free(shared_dict);
+}
+
+int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
+                                 SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+    QuicData *quic_data = &enc->quic_data;
+    QuicContext *quic = enc->quic;
+    volatile QuicImageType type;
+    int size, stride;
+    stat_start_time_t start_time;
+    stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
+
+#ifdef COMPRESS_DEBUG
+    spice_info("QUIC compress");
+#endif
+
+    switch (src->format) {
+    case SPICE_BITMAP_FMT_32BIT:
+        type = QUIC_IMAGE_TYPE_RGB32;
+        break;
+    case SPICE_BITMAP_FMT_RGBA:
+        type = QUIC_IMAGE_TYPE_RGBA;
+        break;
+    case SPICE_BITMAP_FMT_16BIT:
+        type = QUIC_IMAGE_TYPE_RGB16;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        type = QUIC_IMAGE_TYPE_RGB24;
+        break;
+    default:
+        return FALSE;
+    }
+
+    encoder_data_init(&quic_data->data);
+
+    if (setjmp(quic_data->data.jmp_env)) {
+        encoder_data_reset(&quic_data->data);
+        return FALSE;
+    }
+
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
+
+    quic_data->data.u.lines_data.chunks = src->data;
+    quic_data->data.u.lines_data.stride = src->stride;
+    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 {
+        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.words,
+                       G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
+
+    // the compressed buffer is bigger than the original data
+    if ((size << 2) > (src->y * src->stride)) {
+        longjmp(quic_data->data.jmp_env, 1);
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
+    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;
+
+    stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+static const LzImageType bitmap_fmt_to_lz_image_type[] = {
+    LZ_IMAGE_TYPE_INVALID,
+    LZ_IMAGE_TYPE_PLT1_LE,
+    LZ_IMAGE_TYPE_PLT1_BE,
+    LZ_IMAGE_TYPE_PLT4_LE,
+    LZ_IMAGE_TYPE_PLT4_BE,
+    LZ_IMAGE_TYPE_PLT8,
+    LZ_IMAGE_TYPE_RGB16,
+    LZ_IMAGE_TYPE_RGB24,
+    LZ_IMAGE_TYPE_RGB32,
+    LZ_IMAGE_TYPE_RGBA,
+    LZ_IMAGE_TYPE_A8
+};
+
+int image_encoders_compress_lz(ImageEncoders *enc,
+                               SpiceImage *dest, SpiceBitmap *src,
+                               compress_send_data_t* o_comp_data)
+{
+    LzData *lz_data = &enc->lz_data;
+    LzContext *lz = enc->lz;
+    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
+    int size;            // size of the compressed data
+
+    stat_start_time_t start_time;
+    stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
+
+#ifdef COMPRESS_DEBUG
+    spice_info("LZ LOCAL compress");
+#endif
+
+    encoder_data_init(&lz_data->data);
+
+    if (setjmp(lz_data->data.jmp_env)) {
+        encoder_data_reset(&lz_data->data);
+        return FALSE;
+    }
+
+    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;
+
+    size = lz_encode(lz, type, src->x, src->y,
+                     !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+                     NULL, 0, src->stride,
+                     lz_data->data.bufs_head->buf.bytes,
+                     sizeof(lz_data->data.bufs_head->buf));
+
+    // the compressed buffer is bigger than the original data
+    if (size > (src->y * src->stride)) {
+        longjmp(lz_data->data.jmp_env, 1);
+    }
+
+    if (bitmap_fmt_is_rgb(src->format)) {
+        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
+        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 {
+        /* masks are 1BIT bitmaps without palettes, but they are not compressed
+         * (see fill_mask) */
+        spice_assert(src->palette);
+        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
+        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;
+
+        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
+    }
+
+    stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
+                                 SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+    JpegData *jpeg_data = &enc->jpeg_data;
+    LzData *lz_data = &enc->lz_data;
+    JpegEncoderContext *jpeg = enc->jpeg;
+    LzContext *lz = enc->lz;
+    volatile JpegEncoderImageType jpeg_in_type;
+    int jpeg_size = 0;
+    volatile int has_alpha = FALSE;
+    int alpha_lz_size = 0;
+    int comp_head_filled;
+    int comp_head_left;
+    int stride;
+    uint8_t *lz_out_start_byte;
+    stat_start_time_t start_time;
+    stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
+
+#ifdef COMPRESS_DEBUG
+    spice_info("JPEG compress");
+#endif
+
+    switch (src->format) {
+    case SPICE_BITMAP_FMT_16BIT:
+        jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
+        break;
+    case SPICE_BITMAP_FMT_32BIT:
+        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+        break;
+    case SPICE_BITMAP_FMT_RGBA:
+        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+        has_alpha = TRUE;
+        break;
+    default:
+        return FALSE;
+    }
+
+    encoder_data_init(&jpeg_data->data);
+
+    if (setjmp(jpeg_data->data.jmp_env)) {
+        encoder_data_reset(&jpeg_data->data);
+        return FALSE;
+    }
+
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
+
+    jpeg_data->data.u.lines_data.chunks = src->data;
+    jpeg_data->data.u.lines_data.stride = src->stride;
+    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 {
+        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, enc->jpeg_quality, jpeg_in_type,
+                            src->x, src->y, NULL,
+                            0, stride, jpeg_data->data.bufs_head->buf.bytes,
+                            sizeof(jpeg_data->data.bufs_head->buf));
+
+    // the compressed buffer is bigger than the original data
+    if (jpeg_size > (src->y * src->stride)) {
+        longjmp(jpeg_data->data.jmp_env, 1);
+    }
+
+    if (!has_alpha) {
+        dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
+        dest->u.jpeg.data_size = jpeg_size;
+
+        o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+        o_comp_data->comp_buf_size = jpeg_size;
+        o_comp_data->is_lossy = TRUE;
+
+        stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src->stride * src->y,
+                          o_comp_data->comp_buf_size);
+        return TRUE;
+    }
+
+    lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
+    lz_data->data.bufs_tail = lz_data->data.bufs_head;
+
+    comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
+    comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
+    lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + comp_head_filled;
+
+    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;
+
+    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)) {
+        longjmp(jpeg_data->data.jmp_env, 1);
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
+    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->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;
+    o_comp_data->is_lossy = TRUE;
+    stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+
+#ifdef USE_LZ4
+int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
+                                SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+    Lz4Data *lz4_data = &enc->lz4_data;
+    Lz4EncoderContext *lz4 = enc->lz4;
+    int lz4_size = 0;
+    stat_start_time_t start_time;
+    stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
+
+#ifdef COMPRESS_DEBUG
+    spice_info("LZ4 compress");
+#endif
+
+    encoder_data_init(&lz4_data->data);
+
+    if (setjmp(lz4_data->data.jmp_env)) {
+        encoder_data_reset(&lz4_data->data);
+        return FALSE;
+    }
+
+    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+        spice_chunks_linearize(src->data);
+    }
+
+    lz4_data->data.u.lines_data.chunks = src->data;
+    lz4_data->data.u.lines_data.stride = src->stride;
+    lz4_data->data.u.lines_data.next = 0;
+    lz4_data->data.u.lines_data.reverse = 0;
+
+    lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head->buf.bytes,
+                          sizeof(lz4_data->data.bufs_head->buf),
+                          src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src->format);
+
+    // the compressed buffer is bigger than the original data
+    if (lz4_size > (src->y * src->stride)) {
+        longjmp(lz4_data->data.jmp_env, 1);
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
+    dest->u.lz4.data_size = lz4_size;
+
+    o_comp_data->comp_buf = lz4_data->data.bufs_head;
+    o_comp_data->comp_buf_size = lz4_size;
+
+    stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride * src->y,
+                      o_comp_data->comp_buf_size);
+    return TRUE;
+}
+#endif
+
+/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
+   in the channel (2) to the Drawable*/
+static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable *red_drawable,
+                                        GlzImageRetention *glz_retention)
+{
+    RedGlzDrawable *ret;
+    RingItem *item, *next;
+
+    // TODO - I don't really understand what's going on here, so doing the technical equivalent
+    // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
+    // over the glz_ring (unless adding some better data structure then a ring)
+    SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, LINK_TO_GLZ(item)) {
+        if (ret->encoders == enc) {
+            return ret;
+        }
+    }
+
+    ret = spice_new(RedGlzDrawable, 1);
+
+    ret->encoders = enc;
+    ret->red_drawable = red_drawable_ref(red_drawable);
+    ret->has_drawable = TRUE;
+    ret->instances_count = 0;
+    ring_init(&ret->instances);
+
+    ring_item_init(&ret->link);
+    ring_item_init(&ret->drawable_link);
+    ring_add_before(&ret->link, &enc->glz_drawables);
+    ring_add(&glz_retention->ring, &ret->drawable_link);
+    enc->shared_data->glz_drawable_count++;
+    return ret;
+}
+
+/* allocates new instance and adds it to instances in the given drawable.
+   NOTE - the caller should set the glz_instance returned by the encoder by itself.*/
+static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable *glz_drawable)
+{
+    spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
+    // NOTE: We assume the additions are performed consecutively, without removals in the middle
+    GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + glz_drawable->instances_count;
+    glz_drawable->instances_count++;
+
+    ring_item_init(&ret->free_link);
+    ring_item_init(&ret->glz_link);
+    ring_add(&glz_drawable->instances, &ret->glz_link);
+    ret->context = NULL;
+    ret->glz_drawable = glz_drawable;
+
+    return ret;
+}
+
+#define MIN_GLZ_SIZE_FOR_ZLIB 100
+
+int image_encoders_compress_glz(ImageEncoders *enc,
+                                SpiceImage *dest, SpiceBitmap *src,
+                                RedDrawable *red_drawable,
+                                GlzImageRetention *glz_retention,
+                                compress_send_data_t* o_comp_data,
+                                gboolean enable_zlib_glz_wrap)
+{
+    stat_start_time_t start_time;
+    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
+    spice_assert(bitmap_fmt_is_rgb(src->format));
+    GlzData *glz_data = &enc->glz_data;
+    ZlibData *zlib_data;
+    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
+    RedGlzDrawable *glz_drawable;
+    GlzDrawableInstanceItem *glz_drawable_instance;
+    int glz_size;
+    int zlib_size;
+
+#ifdef COMPRESS_DEBUG
+    spice_info("LZ global compress fmt=%d", src->format);
+#endif
+
+    if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict->dict)) {
+        return FALSE;
+    }
+
+    pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
+    /* using the global dictionary only if it is not frozen */
+    if (enc->glz_dict->migrate_freeze) {
+        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+        return FALSE;
+    }
+
+    encoder_data_init(&glz_data->data);
+
+    glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
+    glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
+
+    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_size = glz_encode(enc->glz, type, src->x, src->y,
+                          (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
+                          src->stride, glz_data->data.bufs_head->buf.bytes,
+                          sizeof(glz_data->data.bufs_head->buf),
+                          glz_drawable_instance,
+                          &glz_drawable_instance->context);
+
+    stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * src->y, glz_size);
+
+    if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+        goto glz;
+    }
+    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
+    zlib_data = &enc->zlib_data;
+
+    encoder_data_init(&zlib_data->data);
+
+    zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
+    zlib_data->data.u.compressed_data.size_left = glz_size;
+
+    zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
+                            glz_size, zlib_data->data.bufs_head->buf.bytes,
+                            sizeof(zlib_data->data.bufs_head->buf));
+
+    // the compressed buffer is bigger than the original data
+    if (zlib_size >= glz_size) {
+        encoder_data_reset(&zlib_data->data);
+        goto glz;
+    } else {
+        encoder_data_reset(&glz_data->data);
+    }
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
+    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;
+
+    stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size, zlib_size);
+    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+    return TRUE;
+
+glz:
+    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+
+    dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
+    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;
+
+    return TRUE;
+}
+
+void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
+{
+    clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
+
+    stat_compress_init(&shared_data->off_stat, "off", stat_clock);
+    stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
+    stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
+    stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
+    stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
+    stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
+    stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", stat_clock);
+    stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
+}
+
+void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
+{
+    stat_reset(&shared_data->off_stat);
+    stat_reset(&shared_data->quic_stat);
+    stat_reset(&shared_data->lz_stat);
+    stat_reset(&shared_data->glz_stat);
+    stat_reset(&shared_data->jpeg_stat);
+    stat_reset(&shared_data->zlib_glz_stat);
+    stat_reset(&shared_data->jpeg_alpha_stat);
+    stat_reset(&shared_data->lz4_stat);
+}
+
+#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
+
+#ifdef COMPRESS_STAT
+static void stat_print_one(const char *name, const stat_info_t *stat)
+{
+    spice_info(STAT_FMT, name, stat->count,
+               stat_byte_to_mega(stat->orig_size),
+               stat_byte_to_mega(stat->comp_size),
+               stat_cpu_time_to_sec(stat->total));
+}
+
+static void stat_sum(stat_info_t *total, const stat_info_t *stat)
+{
+    total->count += stat->count;
+    total->orig_size += stat->orig_size;
+    total->comp_size += stat->comp_size;
+    total->total += stat->total;
+}
+#endif
+
+void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data)
+{
+#ifdef COMPRESS_STAT
+    /* sum all statistics */
+    stat_info_t total = {
+        .count = 0,
+        .orig_size = 0,
+        .comp_size = 0,
+        .total = 0
+    };
+    stat_sum(&total, &shared_data->off_stat);
+    stat_sum(&total, &shared_data->quic_stat);
+    stat_sum(&total, &shared_data->glz_stat);
+    stat_sum(&total, &shared_data->lz_stat);
+    stat_sum(&total, &shared_data->jpeg_stat);
+    stat_sum(&total, &shared_data->jpeg_alpha_stat);
+    stat_sum(&total, &shared_data->lz4_stat);
+
+    /* fix for zlib glz */
+    total.total += shared_data->zlib_glz_stat.total;
+    if (shared_data->zlib_glz_stat.count) {
+        total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
+                          shared_data->zlib_glz_stat.comp_size;
+    }
+
+    spice_info("Method   \t  count  \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
+    stat_print_one("OFF      ", &shared_data->off_stat);
+    stat_print_one("QUIC     ", &shared_data->quic_stat);
+    stat_print_one("GLZ      ", &shared_data->glz_stat);
+    stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
+    stat_print_one("LZ       ", &shared_data->lz_stat);
+    stat_print_one("JPEG     ", &shared_data->jpeg_stat);
+    stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
+    stat_print_one("LZ4      ", &shared_data->lz4_stat);
+    spice_info("-------------------------------------------------------------------");
+    stat_print_one("Total    ", &total);
+#endif
+}
diff --git a/server/image-encoders.h b/server/image-encoders.h
new file mode 100644
index 0000000..1ca6b87
--- /dev/null
+++ b/server/image-encoders.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef IMAGE_ENCODERS_H_
+#define IMAGE_ENCODERS_H_
+
+#include <setjmp.h>
+#include <common/quic.h>
+#include <common/lz.h>
+
+#include "stat.h"
+#include "red-parse-qxl.h"
+#include "glz-encoder.h"
+#include "jpeg-encoder.h"
+#ifdef USE_LZ4
+#include "lz4-encoder.h"
+#endif
+#include "zlib-encoder.h"
+
+struct RedClient;
+
+typedef struct RedCompressBuf RedCompressBuf;
+typedef struct RedGlzDrawable RedGlzDrawable;
+typedef struct ImageEncoders ImageEncoders;
+typedef struct ImageEncoderSharedData ImageEncoderSharedData;
+typedef struct GlzSharedDictionary GlzSharedDictionary;
+typedef struct GlzImageRetention GlzImageRetention;
+
+void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
+void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
+void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data);
+
+void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data);
+void image_encoders_free(ImageEncoders *enc);
+int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
+void image_encoders_free_glz_drawables(ImageEncoders *enc);
+void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
+gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
+void image_encoders_glz_get_restore_data(ImageEncoders *enc,
+                                         uint8_t *out_id, GlzEncDictRestoreData *out_data);
+gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
+void image_encoders_glz_encode_unlock(ImageEncoders *enc);
+void glz_retention_free_drawables(GlzImageRetention *ret);
+void glz_retention_detach_drawables(GlzImageRetention *ret);
+
+#define RED_COMPRESS_BUF_SIZE (1024 * 64)
+struct RedCompressBuf {
+    /* This buffer provide space for compression algorithms.
+     * Some algorithms access the buffer as an array of 32 bit words
+     * so is defined to make sure is always aligned that way.
+     */
+    union {
+        uint8_t  bytes[RED_COMPRESS_BUF_SIZE];
+        uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
+    } buf;
+    RedCompressBuf *send_next;
+};
+
+static inline void compress_buf_free(RedCompressBuf *buf)
+{
+    g_free(buf);
+}
+
+gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
+                                           struct RedClient *client,
+                                           uint8_t id, int window_size);
+gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
+                                               struct RedClient *client,
+                                               uint8_t id,
+                                               GlzEncDictRestoreData *restore_data);
+
+typedef struct  {
+    RedCompressBuf *bufs_head;
+    RedCompressBuf *bufs_tail;
+    jmp_buf jmp_env;
+    union {
+        struct {
+            SpiceChunks *chunks;
+            int next;
+            int stride;
+            int reverse;
+        } lines_data;
+        struct {
+            RedCompressBuf* next;
+            int size_left;
+        } compressed_data; // for encoding data that was already compressed by another method
+    } u;
+} EncoderData;
+
+typedef struct {
+    QuicUsrContext usr;
+    EncoderData data;
+} QuicData;
+
+typedef struct {
+    LzUsrContext usr;
+    EncoderData data;
+} LzData;
+
+typedef struct {
+    JpegEncoderUsrContext usr;
+    EncoderData data;
+} JpegData;
+
+#ifdef USE_LZ4
+typedef struct {
+    Lz4EncoderUsrContext usr;
+    EncoderData data;
+} Lz4Data;
+#endif
+
+typedef struct {
+    ZlibEncoderUsrContext usr;
+    EncoderData data;
+} ZlibData;
+
+typedef struct {
+    GlzEncoderUsrContext usr;
+    EncoderData data;
+} GlzData;
+
+struct GlzImageRetention {
+    Ring ring;
+};
+
+static inline void glz_retention_init(GlzImageRetention *ret)
+{
+    ring_init(&ret->ring);
+}
+
+struct ImageEncoderSharedData {
+    uint32_t glz_drawable_count;
+
+    stat_info_t off_stat;
+    stat_info_t lz_stat;
+    stat_info_t glz_stat;
+    stat_info_t quic_stat;
+    stat_info_t jpeg_stat;
+    stat_info_t zlib_glz_stat;
+    stat_info_t jpeg_alpha_stat;
+    stat_info_t lz4_stat;
+};
+
+struct ImageEncoders {
+    ImageEncoderSharedData *shared_data;
+
+    QuicData quic_data;
+    QuicContext *quic;
+
+    LzData lz_data;
+    LzContext  *lz;
+
+    int jpeg_quality;
+
+    JpegData jpeg_data;
+    JpegEncoderContext *jpeg;
+
+#ifdef USE_LZ4
+    Lz4Data lz4_data;
+    Lz4EncoderContext *lz4;
+#endif
+
+    int zlib_level;
+
+    ZlibData zlib_data;
+    ZlibEncoder *zlib;
+
+    /* global lz encoding entities */
+    GlzSharedDictionary *glz_dict;
+    GlzEncoderContext *glz;
+    GlzData glz_data;
+
+    Ring glz_drawables;               // all the living lz drawable, ordered by encoding time
+    Ring glz_drawables_inst_to_free;               // list of instances to be freed
+    pthread_mutex_t glz_drawables_inst_to_free_lock;
+};
+
+typedef struct compress_send_data_t {
+    void* comp_buf;
+    uint32_t comp_buf_size;
+    SpicePalette *lzplt_palette;
+    int is_lossy;
+} compress_send_data_t;
+
+int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
+                                 SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_lz(ImageEncoders *enc,
+                               SpiceImage *dest, SpiceBitmap *src,
+                               compress_send_data_t* o_comp_data);
+int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
+                                 SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
+                                SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_glz(ImageEncoders *enc,
+                                SpiceImage *dest, SpiceBitmap *src,
+                                RedDrawable *red_drawable,
+                                GlzImageRetention *glz_retention,
+                                compress_send_data_t* o_comp_data,
+                                gboolean enable_zlib_glz_wrap);
+
+#define RED_RELEASE_BUNCH_SIZE 64
+
+#endif /* IMAGE_ENCODERS_H_ */


More information about the Spice-commits mailing list