diff --git a/Makefile.objs b/Makefile.objs
index f1c7bfe..9691875 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -129,6 +129,7 @@ ui-obj-$(CONFIG_CURSES) += curses.o
 ui-obj-y += vnc.o d3des.o
 ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
 ui-obj-y += vnc-enc-tight.o vnc-palette.o
+ui-obj-y += vnc-enc-zrle.o
 ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
 ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
 ui-obj-$(CONFIG_COCOA) += cocoa.o
diff --git a/ui/vnc-enc-zrle-template.c b/ui/vnc-enc-zrle-template.c
new file mode 100644
index 0000000..70ae624
--- /dev/null
+++ b/ui/vnc-enc-zrle-template.c
@@ -0,0 +1,263 @@
+/*
+ * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE)
+ *
+ * From libvncserver/libvncserver/zrleencodetemplate.c
+ * Copyright (C) 2002 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Before including this file, you must define a number of CPP macros.
+ *
+ * ZRLE_BPP should be 8, 16 or 32 depending on the bits per pixel.
+ *
+ * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel
+ * bigger than the largest tile of pixel data, since the ZRLE encoding
+ * algorithm writes to the position one past the end of the pixel data.
+ */
+
+
+#include <assert.h>
+
+#undef ZRLE_ENDIAN_SUFFIX
+
+#if ZYWRLE_ENDIAN == ENDIAN_LITTLE
+#define ZRLE_ENDIAN_SUFFIX le
+#elif ZYWRLE_ENDIAN == ENDIAN_BIG
+#define ZRLE_ENDIAN_SUFFIX be
+#else
+#define ZRLE_ENDIAN_SUFFIX ne
+#endif
+
+#ifndef ZRLE_CONCAT
+#define ZRLE_CONCAT_I(a, b)    a##b
+#define ZRLE_CONCAT2(a, b)     ZRLE_CONCAT_I(a, b)
+#define ZRLE_CONCAT3(a, b, c)  ZRLE_CONCAT2(a, ZRLE_CONCAT2(b, c))
+#endif
+
+#ifdef ZRLE_COMPACT_PIXEL
+#define ZRLE_ENCODE_SUFFIX   ZRLE_CONCAT2(ZRLE_COMPACT_PIXEL,ZRLE_ENDIAN_SUFFIX)
+#define ZRLE_WRITE_SUFFIX    ZRLE_COMPACT_PIXEL
+#define ZRLE_PIXEL           ZRLE_CONCAT3(uint,ZRLE_BPP,_t)
+#define ZRLE_BPP_OUT         24
+#elif ZRLE_BPP == 15
+#define ZRLE_ENCODE_SUFFIX   ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX)
+#define ZRLE_WRITE_SUFFIX    16
+#define ZRLE_PIXEL           uint16_t
+#define ZRLE_BPP_OUT         16
+#else
+#define ZRLE_ENCODE_SUFFIX   ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX)
+#define ZRLE_WRITE_SUFFIX    ZRLE_BPP
+#define ZRLE_BPP_OUT         ZRLE_BPP
+#define ZRLE_PIXEL           ZRLE_CONCAT3(uint,ZRLE_BPP,_t)
+#endif
+
+#define ZRLE_WRITE_PIXEL     ZRLE_CONCAT2(zrle_write_u,       ZRLE_WRITE_SUFFIX)
+#define ZRLE_ENCODE          ZRLE_CONCAT2(zrle_encode_,      ZRLE_ENCODE_SUFFIX)
+#define ZRLE_ENCODE_TILE     ZRLE_CONCAT2(zrle_encode_tile,  ZRLE_ENCODE_SUFFIX)
+#define ZRLE_WRITE_PALETTE   ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX)
+
+static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h,
+                             int zywrle_level);
+
+#if ZRLE_BPP != 8
+#include "vnc-enc-zywrle-template.c"
+#endif
+
+
+static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h,
+                        int zywrle_level)
+{
+    int ty;
+
+    for (ty = y; ty < y + h; ty += VNC_ZRLE_TILE_HEIGHT) {
+
+        int tx, th;
+
+        th = MIN(VNC_ZRLE_TILE_HEIGHT, y + h - ty);
+
+        for (tx = x; tx < x + w; tx += VNC_ZRLE_TILE_WIDTH) {
+            int tw;
+            ZRLE_PIXEL *buf;
+
+            tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx);
+
+            buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP);
+            ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level);
+        }
+    }
+}
+
+static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h,
+                             int zywrle_level)
+{
+    VncPalette *palette = &vs->zrle.palette;
+
+    int runs = 0;
+    int single_pixels = 0;
+
+    bool use_rle;
+    bool use_palette;
+
+    int i;
+
+    ZRLE_PIXEL *ptr = data;
+    ZRLE_PIXEL *end = ptr + h * w;
+    *end = ~*(end-1); /* one past the end is different so the while loop ends */
+
+    /* Real limit is 127 but we wan't a way to know if there is more than 127 */
+    palette_init(palette, 256, ZRLE_BPP);
+
+    while (ptr < end) {
+        ZRLE_PIXEL pix = *ptr;
+        if (*++ptr != pix) { /* FIXME */
+            single_pixels++;
+        } else {
+            while (*++ptr == pix) ;
+            runs++;
+        }
+        palette_put(palette, pix);
+    }
+
+    /* Solid tile is a special case */
+
+    if (palette_size(palette) == 1) {
+        bool found;
+
+        vnc_write_u8(vs, 1);
+        ZRLE_WRITE_PIXEL(vs, palette_color(palette, 0, &found));
+        return;
+    }
+
+    zrle_choose_palette_rle(vs, w, h, palette, ZRLE_BPP_OUT,
+                            runs, single_pixels, zywrle_level,
+                            &use_rle, &use_palette);
+
+    if (!use_palette) {
+        vnc_write_u8(vs, (use_rle ? 128 : 0));
+    } else {
+        uint32_t colors[VNC_PALETTE_MAX_SIZE];
+        size_t size = palette_size(palette);
+
+        vnc_write_u8(vs, (use_rle ? 128 : 0) | size);
+        palette_fill(palette, colors);
+
+        for (i = 0; i < size; i++) {
+            ZRLE_WRITE_PIXEL(vs, colors[i]);
+        }
+    }
+
+    if (use_rle) {
+        ZRLE_PIXEL *ptr = data;
+        ZRLE_PIXEL *end = ptr + w * h;
+        ZRLE_PIXEL *run_start;
+        ZRLE_PIXEL pix;
+
+        while (ptr < end) {
+            int len;
+            int index = 0;
+
+            run_start = ptr;
+            pix = *ptr++;
+
+            while (*ptr == pix && ptr < end) {
+                ptr++;
+            }
+
+            len = ptr - run_start;
+
+            if (use_palette)
+                index = palette_idx(palette, pix);
+
+            if (len <= 2 && use_palette) {
+                if (len == 2) {
+                    vnc_write_u8(vs, index);
+                }
+                vnc_write_u8(vs, index);
+                continue;
+            }
+            if (use_palette) {
+                vnc_write_u8(vs, index | 128);
+            } else {
+                ZRLE_WRITE_PIXEL(vs, pix);
+            }
+
+            len -= 1;
+
+            while (len >= 255) {
+                vnc_write_u8(vs, 255);
+                len -= 255;
+            }
+
+            vnc_write_u8(vs, len);
+        }
+    } else if (use_palette) { /* no RLE */
+        int bppp;
+        ZRLE_PIXEL *ptr = data;
+
+        /* packed pixels */
+
+        assert (palette_size(palette) < 17);
+
+        bppp = bits_per_packed_pixel[palette_size(palette)-1];
+
+        for (i = 0; i < h; i++) {
+            uint8_t nbits = 0;
+            uint8_t byte = 0;
+
+            ZRLE_PIXEL *eol = ptr + w;
+
+            while (ptr < eol) {
+                ZRLE_PIXEL pix = *ptr++;
+                uint8_t index = palette_idx(palette, pix);
+
+                byte = (byte << bppp) | index;
+                nbits += bppp;
+                if (nbits >= 8) {
+                    vnc_write_u8(vs, byte);
+                    nbits = 0;
+                }
+            }
+            if (nbits > 0) {
+                byte <<= 8 - nbits;
+                vnc_write_u8(vs, byte);
+            }
+        }
+    } else {
+
+        /* raw */
+
+#if ZRLE_BPP != 8
+        if (zywrle_level > 0 && !(zywrle_level & 0x80)) {
+            ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf);
+            ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80);
+        }
+        else
+#endif
+        {
+#ifdef ZRLE_COMPACT_PIXEL
+            ZRLE_PIXEL *ptr;
+
+            for (ptr = data; ptr < data + w * h; ptr++) {
+                ZRLE_WRITE_PIXEL(vs, *ptr);
+            }
+#else
+            vnc_write(vs, data, w * h * (ZRLE_BPP / 8));
+#endif
+        }
+    }
+}
+
+#undef ZRLE_PIXEL
+#undef ZRLE_WRITE_PIXEL
+#undef ZRLE_ENCODE
+#undef ZRLE_ENCODE_TILE
+#undef ZYWRLE_ENCODE_TILE
+#undef ZRLE_BPP_OUT
+#undef ZRLE_WRITE_SUFFIX
+#undef ZRLE_ENCODE_SUFFIX
diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c
new file mode 100644
index 0000000..016a406
--- /dev/null
+++ b/ui/vnc-enc-zrle.c
@@ -0,0 +1,365 @@
+/*
+ * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE)
+ *
+ * From libvncserver/libvncserver/zrle.c
+ * Copyright (C) 2002 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-enc-zrle.h"
+
+static const int bits_per_packed_pixel[] = {
+  0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
+};
+
+
+static void vnc_zrle_start(VncState *vs)
+{
+    buffer_reset(&vs->zrle.zrle);
+
+    /* make the output buffer be the zlib buffer, so we can compress it later */
+    vs->zrle.tmp = vs->output;
+    vs->output = vs->zrle.zrle;
+}
+
+static void vnc_zrle_stop(VncState *vs)
+{
+    /* switch back to normal output/zlib buffers */
+    vs->zrle.zrle = vs->output;
+    vs->output = vs->zrle.tmp;
+}
+
+static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h,
+                             int bpp)
+{
+    Buffer tmp;
+
+    buffer_reset(&vs->zrle.fb);
+    buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp);
+
+    tmp = vs->output;
+    vs->output = vs->zrle.fb;
+
+    vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+
+    vs->zrle.fb = vs->output;
+    vs->output = tmp;
+    return vs->zrle.fb.buffer;
+}
+
+static int zrle_compress_data(VncState *vs, int level)
+{
+    z_streamp zstream = &vs->zrle.stream;
+
+    buffer_reset(&vs->zrle.zlib);
+
+    if (zstream->opaque != vs) {
+        int err;
+
+        zstream->zalloc = vnc_zlib_zalloc;
+        zstream->zfree = vnc_zlib_zfree;
+
+        err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+                           MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+        if (err != Z_OK) {
+            fprintf(stderr, "VNC: error initializing zlib\n");
+            return -1;
+        }
+
+        zstream->opaque = vs;
+    }
+
+    /* reserve memory in output buffer */
+    buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64);
+
+    /* set pointers */
+    zstream->next_in = vs->zrle.zrle.buffer;
+    zstream->avail_in = vs->zrle.zrle.offset;
+    zstream->next_out = vs->zrle.zlib.buffer + vs->zrle.zlib.offset;
+    zstream->avail_out = vs->zrle.zlib.capacity - vs->zrle.zlib.offset;
+    zstream->data_type = Z_BINARY;
+
+    /* start encoding */
+    if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+        fprintf(stderr, "VNC: error during zrle compression\n");
+        return -1;
+    }
+
+    vs->zrle.zlib.offset = vs->zrle.zlib.capacity - zstream->avail_out;
+    return vs->zrle.zlib.offset;
+}
+
+/* Try to work out whether to use RLE and/or a palette.  We do this by
+ * estimating the number of bytes which will be generated and picking the
+ * method which results in the fewest bytes.  Of course this may not result
+ * in the fewest bytes after compression... */
+static void zrle_choose_palette_rle(VncState *vs, int w, int h,
+                                    VncPalette *palette, int bpp_out,
+                                    int runs, int single_pixels,
+                                    int zywrle_level,
+                                    bool *use_rle, bool *use_palette)
+{
+    size_t estimated_bytes;
+    size_t plain_rle_bytes;
+
+    *use_palette = *use_rle = false;
+
+    estimated_bytes = w * h * (bpp_out / 8); /* start assuming raw */
+
+    if (bpp_out != 8) {
+        if (zywrle_level > 0 && !(zywrle_level & 0x80))
+            estimated_bytes >>= zywrle_level;
+    }
+
+    plain_rle_bytes = ((bpp_out / 8) + 1) * (runs + single_pixels);
+
+    if (plain_rle_bytes < estimated_bytes) {
+        *use_rle = true;
+        estimated_bytes = plain_rle_bytes;
+    }
+
+    if (palette_size(palette) < 128) {
+        int palette_rle_bytes;
+
+        palette_rle_bytes = (bpp_out / 8) * palette_size(palette);
+        palette_rle_bytes += 2 * runs + single_pixels;
+
+        if (palette_rle_bytes < estimated_bytes) {
+            *use_rle = true;
+            *use_palette = true;
+            estimated_bytes = palette_rle_bytes;
+        }
+
+        if (palette_size(palette) < 17) {
+            int packed_bytes;
+
+            packed_bytes = (bpp_out / 8) * palette_size(palette);
+            packed_bytes += w * h *
+                bits_per_packed_pixel[palette_size(palette)-1] / 8;
+
+            if (packed_bytes < estimated_bytes) {
+                *use_rle = false;
+                *use_palette = true;
+                estimated_bytes = packed_bytes;
+            }
+        }
+    }
+}
+
+static void zrle_write_u32(VncState *vs, uint32_t value)
+{
+    vnc_write(vs, (uint8_t *)&value, 4);
+}
+
+static void zrle_write_u24a(VncState *vs, uint32_t value)
+{
+    vnc_write(vs, (uint8_t *)&value, 3);
+}
+
+static void zrle_write_u24b(VncState *vs, uint32_t value)
+{
+    vnc_write(vs, ((uint8_t *)&value) + 1, 3);
+}
+
+static void zrle_write_u16(VncState *vs, uint16_t value)
+{
+    vnc_write(vs, (uint8_t *)&value, 2);
+}
+
+static void zrle_write_u8(VncState *vs, uint8_t value)
+{
+    vnc_write_u8(vs, value);
+}
+
+#define ENDIAN_LITTLE 0
+#define ENDIAN_BIG    1
+#define ENDIAN_NO     2
+
+#define ZRLE_BPP 8
+#define ZYWRLE_ENDIAN ENDIAN_NO
+#include "vnc-enc-zrle-template.c"
+#undef ZRLE_BPP
+
+#define ZRLE_BPP 15
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_LITTLE
+#include "vnc-enc-zrle-template.c"
+
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_BIG
+#include "vnc-enc-zrle-template.c"
+
+#undef ZRLE_BPP
+#define ZRLE_BPP 16
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_LITTLE
+#include "vnc-enc-zrle-template.c"
+
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_BIG
+#include "vnc-enc-zrle-template.c"
+
+#undef ZRLE_BPP
+#define ZRLE_BPP 32
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_LITTLE
+#include "vnc-enc-zrle-template.c"
+
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_BIG
+#include "vnc-enc-zrle-template.c"
+
+#define ZRLE_COMPACT_PIXEL 24a
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_LITTLE
+#include "vnc-enc-zrle-template.c"
+
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_BIG
+#include "vnc-enc-zrle-template.c"
+
+#undef ZRLE_COMPACT_PIXEL
+#define ZRLE_COMPACT_PIXEL 24b
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_LITTLE
+#include "vnc-enc-zrle-template.c"
+
+#undef ZYWRLE_ENDIAN
+#define ZYWRLE_ENDIAN ENDIAN_BIG
+#include "vnc-enc-zrle-template.c"
+#undef ZRLE_COMPACT_PIXEL
+#undef ZRLE_BPP
+
+static int zrle_send_framebuffer_update(VncState *vs, int x, int y,
+                                        int w, int h)
+{
+    bool be = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+    size_t bytes;
+    int zywrle_level;
+
+    if (vs->zrle.type == VNC_ENCODING_ZYWRLE) {
+        if (!vs->vd->lossy || vs->tight.quality < 0 || vs->tight.quality == 9) {
+            zywrle_level = 0;
+            vs->zrle.type = VNC_ENCODING_ZRLE;
+        } else if (vs->tight.quality < 3) {
+            zywrle_level = 3;
+        } else if (vs->tight.quality < 6) {
+            zywrle_level = 2;
+        } else {
+            zywrle_level = 1;
+        }
+    } else {
+        zywrle_level = 0;
+    }
+
+    vnc_zrle_start(vs);
+
+    switch(vs->clientds.pf.bytes_per_pixel) {
+    case 1:
+        zrle_encode_8ne(vs, x, y, w, h, zywrle_level);
+        break;
+
+    case 2:
+        if (vs->clientds.pf.gmax > 0x1F) {
+            if (be) {
+                zrle_encode_16be(vs, x, y, w, h, zywrle_level);
+            } else {
+                zrle_encode_16le(vs, x, y, w, h, zywrle_level);
+            }
+        } else {
+            if (be) {
+                zrle_encode_15be(vs, x, y, w, h, zywrle_level);
+            } else {
+                zrle_encode_15le(vs, x, y, w, h, zywrle_level);
+            }
+        }
+        break;
+
+    case 4:
+    {
+        bool fits_in_ls3bytes;
+        bool fits_in_ms3bytes;
+
+        fits_in_ls3bytes =
+            ((vs->clientds.pf.rmax << vs->clientds.pf.rshift) < (1 << 24) &&
+             (vs->clientds.pf.gmax << vs->clientds.pf.gshift) < (1 << 24) &&
+             (vs->clientds.pf.bmax << vs->clientds.pf.bshift) < (1 << 24));
+
+        fits_in_ms3bytes = (vs->clientds.pf.rshift > 7 &&
+                            vs->clientds.pf.gshift > 7 &&
+                            vs->clientds.pf.bshift > 7);
+
+        if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) {
+            if (be) {
+                zrle_encode_24abe(vs, x, y, w, h, zywrle_level);
+            } else {
+                zrle_encode_24ale(vs, x, y, w, h, zywrle_level);
+          }
+        } else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) {
+            if (be) {
+                zrle_encode_24bbe(vs, x, y, w, h, zywrle_level);
+            } else {
+                zrle_encode_24ble(vs, x, y, w, h, zywrle_level);
+            }
+        } else {
+            if (be) {
+                zrle_encode_32be(vs, x, y, w, h, zywrle_level);
+            } else {
+                zrle_encode_32le(vs, x, y, w, h, zywrle_level);
+            }
+        }
+    }
+    break;
+    }
+
+    vnc_zrle_stop(vs);
+    bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION);
+    vnc_framebuffer_update(vs, x, y, w, h, vs->zrle.type);
+    vnc_write_u32(vs, bytes);
+    vnc_write(vs, vs->zrle.zlib.buffer, vs->zrle.zlib.offset);
+    return 1;
+}
+
+int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+    vs->zrle.type = VNC_ENCODING_ZRLE;
+    return zrle_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+    vs->zrle.type = VNC_ENCODING_ZYWRLE;
+    return zrle_send_framebuffer_update(vs, x, y, w, h);
+}
+
+void vnc_zrle_clear(VncState *vs)
+{
+    if (vs->zrle.stream.opaque) {
+        deflateEnd(&vs->zrle.stream);
+    }
+    buffer_free(&vs->zrle.zrle);
+    buffer_free(&vs->zrle.fb);
+    buffer_free(&vs->zrle.zlib);
+}
diff --git a/ui/vnc-enc-zrle.h b/ui/vnc-enc-zrle.h
new file mode 100644
index 0000000..6b18213
--- /dev/null
+++ b/ui/vnc-enc-zrle.h
@@ -0,0 +1,40 @@
+/*
+ * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE)
+ *
+ * From libvncserver/libvncserver/zrle.c
+ * Copyright (C) 2002 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_ENCODING_ZRLE_H
+#define VNC_ENCODING_ZRLE_H
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * ZRLE - encoding combining Zlib compression, tiling, palettisation and
+ * run-length encoding.
+ */
+
+#define VNC_ZRLE_TILE_WIDTH  64
+#define VNC_ZRLE_TILE_HEIGHT 64
+
+#endif
diff --git a/ui/vnc-enc-zywrle-template.c b/ui/vnc-enc-zywrle-template.c
new file mode 100644
index 0000000..4cde6e4
--- /dev/null
+++ b/ui/vnc-enc-zywrle-template.c
@@ -0,0 +1,170 @@
+
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE.         *
+ *                                                                  *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE.                *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006         *
+ * BY Hitachi Systems & Services, Ltd.                              *
+ * (Noriaki Yamazaki, Research & Developement Center)               *
+ *                                                                  *
+ *                                                                  *
+ ********************************************************************
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Hitachi Systems & Services, Ltd. nor
+the names of its contributors may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************/
+
+/* Change Log:
+     V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline
+	                     (Thanks Johannes Schindelin, author of LibVNC
+						  Server/Client)
+     V0.01 : 2007/02/06 : Initial release
+*/
+
+/*
+[References]
+ PLHarr:
+   Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy,
+   "An Improved N-Bit to N-Bit Reversible Haar-Like Transform,"
+   Pacific Graphics 2004, October 2004, pp. 371-380.
+ EZW:
+   Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients,
+   IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993).
+*/
+
+
+/* Template Macro stuffs. */
+#undef ZYWRLE_ANALYZE
+#undef ZYWRLE_SYNTHESIZE
+
+#define ZYWRLE_SUFFIX     ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX)
+
+#define ZYWRLE_ANALYZE    ZRLE_CONCAT2(zywrle_analyze_,   ZYWRLE_SUFFIX)
+#define ZYWRLE_SYNTHESIZE ZRLE_CONCAT2(zywrle_synthesize_,ZYWRLE_SUFFIX)
+
+#define ZYWRLE_RGBYUV     ZRLE_CONCAT2(zywrle_rgbyuv_,    ZYWRLE_SUFFIX)
+#define ZYWRLE_YUVRGB     ZRLE_CONCAT2(zywrle_yuvrgb_,    ZYWRLE_SUFFIX)
+#define ZYWRLE_YMASK      ZRLE_CONCAT2(ZYWRLE_YMASK,      ZRLE_BPP)
+#define ZYWRLE_UVMASK     ZRLE_CONCAT2(ZYWRLE_UVMASK,     ZRLE_BPP)
+#define ZYWRLE_LOAD_PIXEL ZRLE_CONCAT2(ZYWRLE_LOAD_PIXEL, ZRLE_BPP)
+#define ZYWRLE_SAVE_PIXEL ZRLE_CONCAT2(ZYWRLE_SAVE_PIXEL, ZRLE_BPP)
+
+/* Packing/Unpacking pixel stuffs.
+   Endian conversion stuffs. */
+#undef S_0
+#undef S_1
+#undef L_0
+#undef L_1
+#undef L_2
+
+#if ZYWRLE_ENDIAN == ENDIAN_BIG
+#  define S_0	1
+#  define S_1	0
+#  define L_0	3
+#  define L_1	2
+#  define L_2	1
+#else
+#  define S_0	0
+#  define S_1	1
+#  define L_0	0
+#  define L_1	1
+#  define L_2	2
+#endif
+
+#define ZYWRLE_QUANTIZE
+#include "vnc-enc-zywrle.h"
+
+#ifndef ZRLE_COMPACT_PIXEL
+static inline void ZYWRLE_RGBYUV(int *buf, ZRLE_PIXEL *data,
+                                 int width, int height, int scanline)
+{
+    int r, g, b;
+    int y, u, v;
+    int *line;
+    int *end;
+
+    end = buf + height * width;
+    while (buf < end) {
+        line = buf + width;
+        while (buf < line) {
+            ZYWRLE_LOAD_PIXEL(data, r, g, b);
+            ZYWRLE_RGBYUV_(r, g, b, y, u, v, ZYWRLE_YMASK, ZYWRLE_UVMASK);
+            ZYWRLE_SAVE_COEFF(buf, v, y, u);
+            buf++;
+            data++;
+        }
+        data += scanline - width;
+    }
+}
+
+static ZRLE_PIXEL *ZYWRLE_ANALYZE(ZRLE_PIXEL *dst, ZRLE_PIXEL *src,
+                                  int w, int h, int scanline, int level,
+                                  int *buf) {
+    int l;
+    int uw = w;
+    int uh = h;
+    int *top;
+    int *end;
+    int *line;
+    ZRLE_PIXEL *p;
+    int r, g, b;
+    int s;
+    int *ph;
+
+    zywrle_calc_size(&w, &h, level);
+
+    if (w == 0 || h == 0) {
+        return NULL;
+    }
+    uw -= w;
+    uh -= h;
+
+    p = dst;
+    ZYWRLE_LOAD_UNALIGN(src,*(ZRLE_PIXEL*)top = *p;);
+    ZYWRLE_RGBYUV(buf, src, w, h, scanline);
+    wavelet(buf, w, h, level);
+    for (l = 0; l < level; l++) {
+        ZYWRLE_PACK_COEFF(buf, dst, 3, w, h, scanline, l);
+        ZYWRLE_PACK_COEFF(buf, dst, 2, w, h, scanline, l);
+        ZYWRLE_PACK_COEFF(buf, dst, 1, w, h, scanline, l);
+        if (l == level - 1) {
+            ZYWRLE_PACK_COEFF(buf, dst, 0, w, h, scanline, l);
+        }
+    }
+    ZYWRLE_SAVE_UNALIGN(dst,*dst = *(ZRLE_PIXEL*)top;);
+    return dst;
+}
+#endif  /* ZRLE_COMPACT_PIXEL */
+
+#undef ZYWRLE_RGBYUV
+#undef ZYWRLE_YUVRGB
+#undef ZYWRLE_LOAD_PIXEL
+#undef ZYWRLE_SAVE_PIXEL
diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h
new file mode 100644
index 0000000..ac5d27a
--- /dev/null
+++ b/ui/vnc-enc-zywrle.h
@@ -0,0 +1,659 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE.         *
+ *                                                                  *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE.                *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006         *
+ * BY Hitachi Systems & Services, Ltd.                              *
+ * (Noriaki Yamazaki, Research & Developement Center)               *
+ *                                                                  *
+ *                                                                  *
+ ********************************************************************
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Hitachi Systems & Services, Ltd. nor
+the names of its contributors may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************/
+
+#ifndef VNC_ENCODING_ZYWRLE_H
+#define VNC_ENCODING_ZYWRLE_H
+
+/* Tables for Coefficients filtering. */
+#ifndef ZYWRLE_QUANTIZE
+/* Type A:lower bit omitting of EZW style. */
+static const unsigned int zywrle_param[3][3]={
+	{0x0000F000, 0x00000000, 0x00000000},
+	{0x0000C000, 0x00F0F0F0, 0x00000000},
+	{0x0000C000, 0x00C0C0C0, 0x00F0F0F0},
+/*	{0x0000FF00, 0x00000000, 0x00000000},
+	{0x0000FF00, 0x00FFFFFF, 0x00000000},
+	{0x0000FF00, 0x00FFFFFF, 0x00FFFFFF}, */
+};
+#else
+/* Type B:Non liner quantization filter. */
+static const int8_t zywrle_conv[4][256]={
+{	/* bi=5, bo=5 r=0.0:PSNR=24.849 */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+},
+{	/* bi=5, bo=5 r=2.0:PSNR=74.031 */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 32,
+	32, 32, 32, 32, 32, 32, 32, 32,
+	32, 32, 32, 32, 32, 32, 32, 32,
+	48, 48, 48, 48, 48, 48, 48, 48,
+	48, 48, 48, 56, 56, 56, 56, 56,
+	56, 56, 56, 56, 64, 64, 64, 64,
+	64, 64, 64, 64, 72, 72, 72, 72,
+	72, 72, 72, 72, 80, 80, 80, 80,
+	80, 80, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 96, 96,
+	96, 96, 96, 104, 104, 104, 104, 104,
+	104, 104, 104, 104, 104, 112, 112, 112,
+	112, 112, 112, 112, 112, 112, 120, 120,
+	120, 120, 120, 120, 120, 120, 120, 120,
+	0, -120, -120, -120, -120, -120, -120, -120,
+	-120, -120, -120, -112, -112, -112, -112, -112,
+	-112, -112, -112, -112, -104, -104, -104, -104,
+	-104, -104, -104, -104, -104, -104, -96, -96,
+	-96, -96, -96, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -80,
+	-80, -80, -80, -80, -80, -72, -72, -72,
+	-72, -72, -72, -72, -72, -64, -64, -64,
+	-64, -64, -64, -64, -64, -56, -56, -56,
+	-56, -56, -56, -56, -56, -56, -48, -48,
+	-48, -48, -48, -48, -48, -48, -48, -48,
+	-48, -32, -32, -32, -32, -32, -32, -32,
+	-32, -32, -32, -32, -32, -32, -32, -32,
+	-32, -32, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+},
+{	/* bi=5, bo=4 r=2.0:PSNR=64.441 */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	48, 48, 48, 48, 48, 48, 48, 48,
+	48, 48, 48, 48, 48, 48, 48, 48,
+	48, 48, 48, 48, 48, 48, 48, 48,
+	64, 64, 64, 64, 64, 64, 64, 64,
+	64, 64, 64, 64, 64, 64, 64, 64,
+	80, 80, 80, 80, 80, 80, 80, 80,
+	80, 80, 80, 80, 80, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	104, 104, 104, 104, 104, 104, 104, 104,
+	104, 104, 104, 112, 112, 112, 112, 112,
+	112, 112, 112, 112, 120, 120, 120, 120,
+	120, 120, 120, 120, 120, 120, 120, 120,
+	0, -120, -120, -120, -120, -120, -120, -120,
+	-120, -120, -120, -120, -120, -112, -112, -112,
+	-112, -112, -112, -112, -112, -112, -104, -104,
+	-104, -104, -104, -104, -104, -104, -104, -104,
+	-104, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -80, -80, -80, -80,
+	-80, -80, -80, -80, -80, -80, -80, -80,
+	-80, -64, -64, -64, -64, -64, -64, -64,
+	-64, -64, -64, -64, -64, -64, -64, -64,
+	-64, -48, -48, -48, -48, -48, -48, -48,
+	-48, -48, -48, -48, -48, -48, -48, -48,
+	-48, -48, -48, -48, -48, -48, -48, -48,
+	-48, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+},
+{	/* bi=5, bo=2 r=2.0:PSNR=43.175 */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	88, 88, 88, 88, 88, 88, 88, 88,
+	0, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, -88, -88, -88, -88, -88, -88, -88,
+	-88, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+}
+};
+
+static const int8_t *zywrle_param[3][3][3]={
+	{{zywrle_conv[0], zywrle_conv[2], zywrle_conv[0]},
+         {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]},
+         {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}},
+	{{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]},
+         {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]},
+         {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}},
+	{{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]},
+         {zywrle_conv[2], zywrle_conv[2], zywrle_conv[2]},
+         {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}},
+};
+#endif
+
+/*   Load/Save pixel stuffs. */
+#define ZYWRLE_YMASK15  0xFFFFFFF8
+#define ZYWRLE_UVMASK15 0xFFFFFFF8
+#define ZYWRLE_LOAD_PIXEL15(src, r, g, b)                               \
+    do {                                                                \
+	r = (((uint8_t*)src)[S_1]<< 1)& 0xF8;                           \
+	g = (((uint8_t*)src)[S_1]<< 6) | (((uint8_t*)src)[S_0]>> 2);    \
+        g &= 0xF8;                                                      \
+	b =  (((uint8_t*)src)[S_0]<< 3)& 0xF8;                          \
+    } while (0)
+
+#define ZYWRLE_SAVE_PIXEL15(dst, r, g, b)                               \
+    do {                                                                \
+	r &= 0xF8;                                                      \
+	g &= 0xF8;                                                      \
+	b &= 0xF8;                                                      \
+	((uint8_t*)dst)[S_1] = (uint8_t)((r >> 1)|(g >> 6));            \
+	((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 2))& 0xFF);    \
+    } while (0)
+
+#define ZYWRLE_YMASK16  0xFFFFFFFC
+#define ZYWRLE_UVMASK16 0xFFFFFFF8
+#define ZYWRLE_LOAD_PIXEL16(src, r, g, b)                               \
+    do {                                                                \
+	r = ((uint8_t*)src)[S_1] & 0xF8;                                \
+	g = (((uint8_t*)src)[S_1]<< 5) | (((uint8_t*)src)[S_0] >> 3);   \
+        g &= 0xFC;                                                      \
+	b = (((uint8_t*)src)[S_0]<< 3) & 0xF8;                          \
+    } while (0)
+
+#define ZYWRLE_SAVE_PIXEL16(dst, r, g,b)                                \
+    do {                                                                \
+	r &= 0xF8;                                                      \
+	g &= 0xFC;                                                      \
+	b &= 0xF8;                                                      \
+	((uint8_t*)dst)[S_1] = (uint8_t)(r | (g >> 5));                 \
+	((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 3)) & 0xFF);   \
+    } while (0)
+
+#define ZYWRLE_YMASK32  0xFFFFFFFF
+#define ZYWRLE_UVMASK32 0xFFFFFFFF
+#define ZYWRLE_LOAD_PIXEL32(src, r, g, b)     \
+    do {                                      \
+	r = ((uint8_t*)src)[L_2];             \
+	g = ((uint8_t*)src)[L_1];             \
+	b = ((uint8_t*)src)[L_0];             \
+    } while (0)
+#define ZYWRLE_SAVE_PIXEL32(dst, r, g, b)             \
+    do {                                              \
+	((uint8_t*)dst)[L_2] = (uint8_t)r;            \
+	((uint8_t*)dst)[L_1] = (uint8_t)g;            \
+	((uint8_t*)dst)[L_0] = (uint8_t)b;            \
+    } while (0)
+
+static inline void harr(int8_t *px0, int8_t *px1)
+{
+    /* Piecewise-Linear Harr(PLHarr) */
+    int x0 = (int)*px0, x1 = (int)*px1;
+    int orgx0 = x0, orgx1 = x1;
+
+    if ((x0 ^ x1) & 0x80) {
+        /* differ sign */
+        x1 += x0;
+        if (((x1 ^ orgx1) & 0x80) == 0) {
+            /* |x1| > |x0| */
+            x0 -= x1;	/* H = -B */
+        }
+    } else {
+        /* same sign */
+        x0 -= x1;
+        if (((x0 ^ orgx0) & 0x80) == 0) {
+            /* |x0| > |x1| */
+            x1 += x0;	/* L = A */
+        }
+    }
+    *px0 = (int8_t)x1;
+    *px1 = (int8_t)x0;
+}
+
+/*
+ 1D-Wavelet transform.
+
+ In coefficients array, the famous 'pyramid' decomposition is well used.
+
+ 1D Model:
+   |L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0
+   |L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1
+
+ But this method needs line buffer because H/L is different position from X0/X1.
+ So, I used 'interleave' decomposition instead of it.
+
+ 1D Model:
+   |L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0
+   |L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1
+
+ In this method, H/L and X0/X1 is always same position.
+ This lead us to more speed and less memory.
+ Of cause, the result of both method is quite same
+ because it's only difference that coefficient position.
+*/
+static inline void wavelet_level(int *data, int size, int l, int skip_pixel)
+{
+    int s, ofs;
+    int8_t *px0;
+    int8_t *end;
+
+    px0 = (int8_t*)data;
+    s = (8 << l) * skip_pixel;
+    end = px0 + (size >> (l + 1)) * s;
+    s -= 2;
+    ofs = (4 << l) * skip_pixel;
+
+    while (px0 < end) {
+        harr(px0, px0 + ofs);
+        px0++;
+        harr(px0, px0 + ofs);
+        px0++;
+        harr(px0, px0 + ofs);
+        px0 += s;
+    }
+}
+
+#ifndef ZYWRLE_QUANTIZE
+/* Type A:lower bit omitting of EZW style. */
+static inline void filter_wavelet_square(int *buf, int width, int height,
+                                         int level, int l)
+{
+    int r, s;
+    int x, y;
+    int *h;
+    const unsigned int *m;
+
+    m = &(zywrle_param[level - 1][l]);
+    s = 2 << l;
+
+    for (r = 1; r < 4; r++) {
+        h = buf;
+        if (r & 0x01) {
+            h += s >> 1;
+        }
+        if (r & 0x02) {
+            h += (s >> 1) * width;
+        }
+        for (y = 0; y < height / s; y++) {
+            for (x = 0; x < width / s; x++) {
+                /*
+                  these are same following code.
+                  h[x] = h[x] / (~m[x]+1) * (~m[x]+1);
+                  ( round h[x] with m[x] bit )
+                  '&' operator isn't 'round' but is 'floor'.
+                  So, we must offset when h[x] is negative.
+                */
+                if (((int8_t*)h)[0] & 0x80) {
+                    ((int8_t*)h)[0] += ~((int8_t*)m)[0];
+                }
+                if (((int8_t*)h)[1] & 0x80) {
+                    ((int8_t*)h)[1] += ~((int8_t*)m)[1];
+                }
+                if (((int8_t*)h)[2] & 0x80) {
+                    ((int8_t*)h)[2] += ~((int8_t*)m)[2];
+                }
+                *h &= *m;
+                h += s;
+            }
+            h += (s-1)*width;
+        }
+    }
+}
+#else
+/*
+ Type B:Non liner quantization filter.
+
+ Coefficients have Gaussian curve and smaller value which is
+ large part of coefficients isn't more important than larger value.
+ So, I use filter of Non liner quantize/dequantize table.
+ In general, Non liner quantize formula is explained as following.
+
+    y=f(x)   = sign(x)*round( ((abs(x)/(2^7))^ r   )* 2^(bo-1) )*2^(8-bo)
+    x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi)
+ ( r:power coefficient  bi:effective MSB in input  bo:effective MSB in output )
+
+   r < 1.0 : Smaller value is more important than larger value.
+   r > 1.0 : Larger value is more important than smaller value.
+   r = 1.0 : Liner quantization which is same with EZW style.
+
+ r = 0.75 is famous non liner quantization used in MP3 audio codec.
+ In contrast to audio data, larger value is important in wavelet coefficients.
+ So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ).
+
+ As compared with EZW style liner quantization, this filter tended to be
+ more sharp edge and be more compression rate but be more blocking noise and be
+ less quality. Especially, the surface of graphic objects has distinguishable
+ noise in middle quality mode.
+
+ We need only quantized-dequantized(filtered) value rather than quantized value
+ itself because all values are packed or palette-lized in later ZRLE section.
+ This lead us not to need to modify client decoder when we change
+ the filtering procedure in future.
+ Client only decodes coefficients given by encoder.
+*/
+static inline void filter_wavelet_square(int *buf, int width, int height,
+                                         int level, int l)
+{
+    int r, s;
+    int x, y;
+    int *h;
+    const int8_t **m;
+
+    m = zywrle_param[level - 1][l];
+    s = 2 << l;
+
+    for (r = 1; r < 4; r++) {
+        h = buf;
+        if (r & 0x01) {
+            h += s >> 1;
+        }
+        if (r & 0x02) {
+            h += (s >> 1) * width;
+        }
+        for (y = 0; y < height / s; y++) {
+            for (x = 0; x < width / s; x++) {
+                ((int8_t*)h)[0] = m[0][((uint8_t*)h)[0]];
+                ((int8_t*)h)[1] = m[1][((uint8_t*)h)[1]];
+                ((int8_t*)h)[2] = m[2][((uint8_t*)h)[2]];
+                h += s;
+            }
+            h += (s - 1) * width;
+        }
+    }
+}
+#endif
+
+static inline void wavelet(int *buf, int width, int height, int level)
+{
+	int l, s;
+	int *top;
+	int *end;
+
+	for (l = 0; l < level; l++) {
+		top = buf;
+		end = buf + height * width;
+		s = width << l;
+		while (top < end) {
+			wavelet_level(top, width, l, 1);
+			top += s;
+		}
+		top = buf;
+		end = buf + width;
+		s = 1<<l;
+		while (top < end) {
+			wavelet_level(top, height, l, width);
+			top += s;
+		}
+		filter_wavelet_square(buf, width, height, level, l);
+	}
+}
+
+
+/* Load/Save coefficients stuffs.
+ Coefficients manages as 24 bits little-endian pixel. */
+#define ZYWRLE_LOAD_COEFF(src, r, g, b)         \
+    do {                                        \
+	r = ((int8_t*)src)[2];                  \
+	g = ((int8_t*)src)[1];                  \
+	b = ((int8_t*)src)[0];                  \
+    } while (0)
+
+#define ZYWRLE_SAVE_COEFF(dst, r, g, b)       \
+    do {                                      \
+	((int8_t*)dst)[2] = (int8_t)r;        \
+	((int8_t*)dst)[1] = (int8_t)g;        \
+	((int8_t*)dst)[0] = (int8_t)b;        \
+    } while (0)
+
+/*
+  RGB <=> YUV conversion stuffs.
+  YUV coversion is explained as following formula in strict meaning:
+  Y =  0.299R + 0.587G + 0.114B (   0<=Y<=255)
+  U = -0.169R - 0.331G + 0.500B (-128<=U<=127)
+  V =  0.500R - 0.419G - 0.081B (-128<=V<=127)
+
+  I use simple conversion RCT(reversible color transform) which is described
+  in JPEG-2000 specification.
+  Y = (R + 2G + B)/4 (   0<=Y<=255)
+  U = B-G (-256<=U<=255)
+  V = R-G (-256<=V<=255)
+*/
+
+/* RCT is N-bit RGB to N-bit Y and N+1-bit UV.
+   For make Same N-bit, UV is lossy.
+   More exact PLHarr, we reduce to odd range(-127<=x<=127). */
+#define ZYWRLE_RGBYUV_(r, g, b, y, u, v, ymask, uvmask)          \
+    do {                                                         \
+	y = (r + (g << 1) + b) >> 2;                             \
+	u =  b - g;                                              \
+	v =  r - g;                                              \
+	y -= 128;                                                \
+	u >>= 1;                                                 \
+	v >>= 1;                                                 \
+	y &= ymask;                                              \
+	u &= uvmask;                                             \
+	v &= uvmask;                                             \
+	if (y == -128) {                                         \
+            y += (0xFFFFFFFF - ymask + 1);                       \
+        }                                                        \
+	if (u == -128) {                                         \
+            u += (0xFFFFFFFF - uvmask + 1);                      \
+        }                                                        \
+	if (v == -128) {                                         \
+            v += (0xFFFFFFFF - uvmask + 1);                      \
+        }                                                        \
+    } while (0)
+
+
+/*
+ coefficient packing/unpacking stuffs.
+ Wavelet transform makes 4 sub coefficient image from 1 original image.
+
+ model with pyramid decomposition:
+   +------+------+
+   |      |      |
+   |  L   |  Hx  |
+   |      |      |
+   +------+------+
+   |      |      |
+   |  H   |  Hxy |
+   |      |      |
+   +------+------+
+
+ So, we must transfer each sub images individually in strict meaning.
+ But at least ZRLE meaning, following one decompositon image is same as
+ avobe individual sub image. I use this format.
+ (Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L)
+  for simplified procedure for any wavelet level.)
+
+   +------+------+
+   |      L      |
+   +------+------+
+   |      Hx     |
+   +------+------+
+   |      Hy     |
+   +------+------+
+   |      Hxy    |
+   +------+------+
+*/
+#define ZYWRLE_INC_PTR(data)                         \
+    do {                                             \
+        data++;                                      \
+        if( data - p >= (w + uw) ) {                 \
+            data += scanline-(w + uw);               \
+            p = data;                                \
+        }                                            \
+    } while (0)
+
+#define ZYWRLE_TRANSFER_COEFF(buf, data, t, w, h, scanline, level, TRANS) \
+    do {                                                                \
+        ph = buf;                                                       \
+        s = 2 << level;                                                 \
+        if (t & 0x01) {                                                 \
+            ph += s >> 1;                                               \
+        }                                                               \
+        if (t & 0x02) {                                                 \
+            ph += (s >> 1) * w;                                         \
+        }                                                               \
+        end = ph + h * w;                                               \
+        while (ph < end) {                                              \
+            line = ph + w;                                              \
+            while (ph < line) {                                         \
+                TRANS                                                   \
+                    ZYWRLE_INC_PTR(data);                               \
+                ph += s;                                                \
+            }                                                           \
+            ph += (s - 1) * w;                                          \
+        }                                                               \
+    } while (0)
+
+#define ZYWRLE_PACK_COEFF(buf, data, t, width, height, scanline, level)	\
+    ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \
+                          ZYWRLE_LOAD_COEFF(ph, r, g, b);               \
+                          ZYWRLE_SAVE_PIXEL(data, r, g, b);)
+
+#define ZYWRLE_UNPACK_COEFF(buf, data, t, width, height, scanline, level) \
+    ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \
+                          ZYWRLE_LOAD_PIXEL(data, r, g, b);             \
+                          ZYWRLE_SAVE_COEFF(ph, r, g, b);)
+
+#define ZYWRLE_SAVE_UNALIGN(data, TRANS)                     \
+    do {                                                     \
+        top = buf + w * h;                                   \
+        end = buf + (w + uw) * (h + uh);                     \
+        while (top < end) {                                  \
+            TRANS                                            \
+                ZYWRLE_INC_PTR(data);                        \
+                top++;                                       \
+        }                                                    \
+    } while (0)
+
+#define ZYWRLE_LOAD_UNALIGN(data,TRANS)                                 \
+    do {                                                                \
+        top = buf + w * h;                                              \
+        if (uw) {                                                       \
+            p = data + w;                                               \
+            end = (int*)(p + h * scanline);                             \
+            while (p < (ZRLE_PIXEL*)end) {                              \
+                line = (int*)(p + uw);                                  \
+                while (p < (ZRLE_PIXEL*)line) {                         \
+                    TRANS                                               \
+                        p++;                                            \
+                    top++;                                              \
+                }                                                       \
+                p += scanline - uw;                                     \
+            }                                                           \
+        }                                                               \
+        if (uh) {                                                       \
+            p = data + h * scanline;                                    \
+            end = (int*)(p + uh * scanline);                            \
+            while (p < (ZRLE_PIXEL*)end) {                              \
+                line = (int*)(p + w);                                   \
+                while (p < (ZRLE_PIXEL*)line) {                         \
+                    TRANS                                               \
+                        p++;                                            \
+                    top++;                                              \
+                }                                                       \
+                p += scanline - w;                                      \
+            }                                                           \
+        }                                                               \
+        if (uw && uh) {                                                 \
+            p= data + w + h * scanline;                                 \
+            end = (int*)(p + uh * scanline);                            \
+            while (p < (ZRLE_PIXEL*)end) {                              \
+                line = (int*)(p + uw);                                  \
+                while (p < (ZRLE_PIXEL*)line) {                         \
+                    TRANS                                               \
+                        p++;                                            \
+                    top++;                                              \
+                }                                                       \
+                p += scanline-uw;                                       \
+            }                                                           \
+        }                                                               \
+    } while (0)
+
+static inline void zywrle_calc_size(int *w, int *h, int level)
+{
+    *w &= ~((1 << level) - 1);
+    *h &= ~((1 << level) - 1);
+}
+
+#endif
diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c
index bfe8e86..1d4c5e7 100644
--- a/ui/vnc-jobs-async.c
+++ b/ui/vnc-jobs-async.c
@@ -172,6 +172,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
     local->tight = orig->tight;
     local->zlib = orig->zlib;
     local->hextile = orig->hextile;
+    local->zrle = orig->zrle;
     local->output =  queue->buffer;
     local->csock = -1; /* Don't do any network work on this thread */
 
@@ -183,6 +184,7 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local)
     orig->tight = local->tight;
     orig->zlib = local->zlib;
     orig->hextile = local->hextile;
+    orig->zrle = local->zrle;
     orig->lossy_rect = local->lossy_rect;
 }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 697b836..9920c0e 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -696,6 +696,12 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
         case VNC_ENCODING_TIGHT_PNG:
             n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
             break;
+        case VNC_ENCODING_ZRLE:
+            n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h);
+            break;
+        case VNC_ENCODING_ZYWRLE:
+            n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
+            break;
         default:
             vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
             n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -1028,6 +1034,7 @@ static void vnc_disconnect_finish(VncState *vs)
 
     vnc_zlib_clear(vs);
     vnc_tight_clear(vs);
+    vnc_zrle_clear(vs);
 
 #ifdef CONFIG_VNC_TLS
     vnc_tls_client_cleanup(vs);
@@ -1766,6 +1773,14 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
             vs->features |= VNC_FEATURE_ZLIB_MASK;
             vs->vnc_encoding = enc;
             break;
+        case VNC_ENCODING_ZRLE:
+            vs->features |= VNC_FEATURE_ZRLE_MASK;
+            vs->vnc_encoding = enc;
+            break;
+        case VNC_ENCODING_ZYWRLE:
+            vs->features |= VNC_FEATURE_ZYWRLE_MASK;
+            vs->vnc_encoding = enc;
+            break;
         case VNC_ENCODING_DESKTOPRESIZE:
             vs->features |= VNC_FEATURE_RESIZE_MASK;
             break;
diff --git a/ui/vnc.h b/ui/vnc.h
index 57835f1..706ba3c 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -39,6 +39,8 @@
 #include <stdbool.h>
 
 #include "keymaps.h"
+#include "vnc-palette.h"
+#include "vnc-enc-zrle.h"
 
 // #define _VNC_DEBUG 1
 
@@ -180,6 +182,20 @@ typedef struct VncZlib {
     int level;
 } VncZlib;
 
+typedef struct VncZrle {
+    int type;
+    Buffer fb;
+    Buffer zrle;
+    Buffer tmp;
+    Buffer zlib;
+    z_stream stream;
+    VncPalette palette;
+} VncZrle;
+
+typedef struct VncZywrle {
+    int buf[VNC_ZRLE_TILE_WIDTH * VNC_ZRLE_TILE_HEIGHT];
+} VncZywrle;
+
 #ifdef CONFIG_VNC_THREAD
 struct VncRect
 {
@@ -273,7 +289,8 @@ struct VncState
     VncTight tight;
     VncZlib zlib;
     VncHextile hextile;
-
+    VncZrle zrle;
+    VncZywrle zywrle;
 
     Notifier mouse_mode_notifier;
 
@@ -377,6 +394,8 @@ enum {
 #define VNC_FEATURE_COPYRECT                 6
 #define VNC_FEATURE_RICH_CURSOR              7
 #define VNC_FEATURE_TIGHT_PNG                8
+#define VNC_FEATURE_ZRLE                     9
+#define VNC_FEATURE_ZYWRLE                  10
 
 #define VNC_FEATURE_RESIZE_MASK              (1 << VNC_FEATURE_RESIZE)
 #define VNC_FEATURE_HEXTILE_MASK             (1 << VNC_FEATURE_HEXTILE)
@@ -387,6 +406,8 @@ enum {
 #define VNC_FEATURE_COPYRECT_MASK            (1 << VNC_FEATURE_COPYRECT)
 #define VNC_FEATURE_RICH_CURSOR_MASK         (1 << VNC_FEATURE_RICH_CURSOR)
 #define VNC_FEATURE_TIGHT_PNG_MASK           (1 << VNC_FEATURE_TIGHT_PNG)
+#define VNC_FEATURE_ZRLE_MASK                (1 << VNC_FEATURE_ZRLE)
+#define VNC_FEATURE_ZYWRLE_MASK              (1 << VNC_FEATURE_ZYWRLE)
 
 
 /* Client -> Server message IDs */
@@ -521,4 +542,8 @@ int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
                                           int w, int h);
 void vnc_tight_clear(VncState *vs);
 
+int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+void vnc_zrle_clear(VncState *vs);
+
 #endif /* __QEMU_VNC_H */
