Patchwork [v2,08/18] vnc: tight add PNG encoding

login
register
mail settings
Submitter Corentin Chary
Date July 7, 2010, 6:57 p.m.
Message ID <1278529086-10391-9-git-send-email-corentincj@iksaif.net>
Download mbox | patch
Permalink /patch/58150/
State New
Headers show

Comments

Corentin Chary - July 7, 2010, 6:57 p.m.
Introduce a new encoding: VNC_ENCODING_TIGHT_PNG [1] (-269) with a new
tight filter VNC_TIGHT_PNG (0x0A). When the client tells it supports the Tight PNG
encoding, the server will use tight, but will always send encoding pixels using
PNG instead of zlib. If the client also told it support JPEG, then the server can
send JPEG, because PNG will only be used in the cases zlib was used in normal tight.

This encoding was introduced to speed up HTML5 based VNC clients like noVNC [2], but
can also be used on devices like iPhone where PNG can be rendered in hardware.

[1] http://wiki.qemu.org/VNC_Tight_PNG
[2] http://github.com/kanaka/noVNC/

Signed-off-by: Corentin Chary <corentincj@iksaif.net>
---
 Makefile.target    |    1 +
 configure          |   37 ++++++
 ui/vnc-enc-tight.c |  316 +++++++++++++++++++++++++++++++++++++++++++++-------
 ui/vnc-enc-tight.h |   16 ++-
 ui/vnc.c           |   11 +-
 ui/vnc.h           |   15 +++-
 6 files changed, 342 insertions(+), 54 deletions(-)
Daniel P. Berrange - July 8, 2010, 9:48 a.m.
On Wed, Jul 07, 2010 at 08:57:56PM +0200, Corentin Chary wrote:
> Introduce a new encoding: VNC_ENCODING_TIGHT_PNG [1] (-269) with a new
> tight filter VNC_TIGHT_PNG (0x0A). When the client tells it supports the Tight PNG
> encoding, the server will use tight, but will always send encoding pixels using
> PNG instead of zlib. If the client also told it support JPEG, then the server can
> send JPEG, because PNG will only be used in the cases zlib was used in normal tight.

I know that VNC_ENCODING_TIGHT_PNG / -260 is already allocated to
QEMU in the RFB specification. Who is the authority for allocating
tight filter numbers, and have they recorded/approved use of 0x0A
for this PNG capability ?


Regards,
Daniel
Corentin Chary - July 8, 2010, 1:10 p.m.
On Thu, Jul 8, 2010 at 11:48 AM, Daniel P. Berrange <berrange@redhat.com> wrote:
> On Wed, Jul 07, 2010 at 08:57:56PM +0200, Corentin Chary wrote:
>> Introduce a new encoding: VNC_ENCODING_TIGHT_PNG [1] (-269) with a new
>> tight filter VNC_TIGHT_PNG (0x0A). When the client tells it supports the Tight PNG
>> encoding, the server will use tight, but will always send encoding pixels using
>> PNG instead of zlib. If the client also told it support JPEG, then the server can
>> send JPEG, because PNG will only be used in the cases zlib was used in normal tight.
>
> I know that VNC_ENCODING_TIGHT_PNG / -260 is already allocated to
> QEMU in the RFB specification. Who is the authority for allocating
> tight filter numbers, and have they recorded/approved use of 0x0A
> for this PNG capability ?
>

Tight PNG should considered as a new encoding, not as a tight pseudo encoding.
When using Tight PNG, the server will send rect updates with -260, not 7.

We discussed that issue on #qemu-gsoc, and Anthony told me htat:
"if it gets sent with a different encoding, then you don't need
anyone's approval, you just need an encoding number
but if it's a pseudo-encoding that modifies an existing encoding, then
the author of the existing encoding must be on board"

Thanks,
Daniel P. Berrange - July 8, 2010, 1:19 p.m.
On Thu, Jul 08, 2010 at 03:10:31PM +0200, Corentin Chary wrote:
> On Thu, Jul 8, 2010 at 11:48 AM, Daniel P. Berrange <berrange@redhat.com> wrote:
> > On Wed, Jul 07, 2010 at 08:57:56PM +0200, Corentin Chary wrote:
> >> Introduce a new encoding: VNC_ENCODING_TIGHT_PNG [1] (-269) with a new
> >> tight filter VNC_TIGHT_PNG (0x0A). When the client tells it supports the Tight PNG
> >> encoding, the server will use tight, but will always send encoding pixels using
> >> PNG instead of zlib. If the client also told it support JPEG, then the server can
> >> send JPEG, because PNG will only be used in the cases zlib was used in normal tight.
> >
> > I know that VNC_ENCODING_TIGHT_PNG / -260 is already allocated to
> > QEMU in the RFB specification. Who is the authority for allocating
> > tight filter numbers, and have they recorded/approved use of 0x0A
> > for this PNG capability ?
> >
> 
> Tight PNG should considered as a new encoding, not as a tight pseudo encoding.
> When using Tight PNG, the server will send rect updates with -260, not 7.

Why layer this into the rest of the Tight protocol decoding then ? What
benefit does it offer over a more straightforward standalone "PNG" encoding,
that was completely independant of any tight based encoding.

Regards,
Daniel
Corentin Chary - July 8, 2010, 1:35 p.m.
On Thu, Jul 8, 2010 at 3:19 PM, Daniel P. Berrange <berrange@redhat.com> wrote:
> On Thu, Jul 08, 2010 at 03:10:31PM +0200, Corentin Chary wrote:
>> On Thu, Jul 8, 2010 at 11:48 AM, Daniel P. Berrange <berrange@redhat.com> wrote:
>> > On Wed, Jul 07, 2010 at 08:57:56PM +0200, Corentin Chary wrote:
>> >> Introduce a new encoding: VNC_ENCODING_TIGHT_PNG [1] (-269) with a new
>> >> tight filter VNC_TIGHT_PNG (0x0A). When the client tells it supports the Tight PNG
>> >> encoding, the server will use tight, but will always send encoding pixels using
>> >> PNG instead of zlib. If the client also told it support JPEG, then the server can
>> >> send JPEG, because PNG will only be used in the cases zlib was used in normal tight.
>> >
>> > I know that VNC_ENCODING_TIGHT_PNG / -260 is already allocated to
>> > QEMU in the RFB specification. Who is the authority for allocating
>> > tight filter numbers, and have they recorded/approved use of 0x0A
>> > for this PNG capability ?
>> >
>>
>> Tight PNG should considered as a new encoding, not as a tight pseudo encoding.
>> When using Tight PNG, the server will send rect updates with -260, not 7.
>
> Why layer this into the rest of the Tight protocol decoding then ? What
> benefit does it offer over a more straightforward standalone "PNG" encoding,
> that was completely independant of any tight based encoding.
>

Because:
- we also want a lossy encoding (Jpeg) for some parts (adaptive choice
between jpeg and png/zlib based on update frequency is comming soon).
- we want the "fill" encoding for solid color rectangles

A standalone "PNG" encoding would work for some use case, and could be
added later, but Tight PNG is more than that.
Corentin Chary - July 8, 2010, 1:37 p.m.
> - we also want a lossy encoding (Jpeg) for some parts (adaptive choice
> between jpeg and png/zlib based on update frequency is comming soon).

Please note that Tight PNG can already switch from PNG to JPEG like the original
Tight was able to switch from Basic/Indexed to Jpeg (if lossy encoding
are enabled,
and if the client sent a Tight quality).

Patch

diff --git a/Makefile.target b/Makefile.target
index eb801df..fa9ec0d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -178,6 +178,7 @@  LIBS+=-lz
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
 QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
+QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
 
 # xen backend driver support
 obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
diff --git a/configure b/configure
index 27791b5..eba9606 100755
--- a/configure
+++ b/configure
@@ -269,6 +269,7 @@  vde=""
 vnc_tls=""
 vnc_sasl=""
 vnc_jpeg=""
+vnc_png=""
 xen=""
 linux_aio=""
 attr=""
@@ -580,6 +581,10 @@  for opt do
   ;;
   --enable-vnc-jpeg) vnc_jpeg="yes"
   ;;
+  --disable-vnc-png) vnc_png="no"
+  ;;
+  --enable-vnc-png) vnc_png="yes"
+  ;;
   --disable-slirp) slirp="no"
   ;;
   --disable-uuid) uuid="no"
@@ -832,6 +837,8 @@  echo "  --disable-vnc-sasl       disable SASL encryption for VNC server"
 echo "  --enable-vnc-sasl        enable SASL encryption for VNC server"
 echo "  --disable-vnc-jpeg       disable JPEG lossy compression for VNC server"
 echo "  --enable-vnc-jpeg        enable JPEG lossy compression for VNC server"
+echo "  --disable-vnc-png        disable PNG compression for VNC server"
+echo "  --enable-vnc-png         enable PNG compression for VNC server"
 echo "  --disable-curses         disable curses output"
 echo "  --enable-curses          enable curses output"
 echo "  --disable-curl           disable curl connectivity"
@@ -1274,6 +1281,31 @@  EOF
 fi
 
 ##########################################
+# VNC PNG detection
+if test "$vnc_png" = "yes" ; then
+cat > $TMPC <<EOF
+//#include <stdio.h>
+#include <png.h>
+int main(void) {
+    png_structp png_ptr;
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    return 0;
+}
+EOF
+    vnc_png_cflags=""
+    vnc_png_libs="-lpng"
+  if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
+    vnc_png=yes
+    libs_softmmu="$vnc_png_libs $libs_softmmu"
+  else
+    if test "$vnc_png" = "yes" ; then
+      feature_not_found "vnc-png"
+    fi
+    vnc_png=no
+  fi
+fi
+
+##########################################
 # fnmatch() probe, used for ACL routines
 fnmatch="no"
 cat > $TMPC << EOF
@@ -2123,6 +2155,7 @@  echo "Mixer emulation   $mixemu"
 echo "VNC TLS support   $vnc_tls"
 echo "VNC SASL support  $vnc_sasl"
 echo "VNC JPEG support  $vnc_jpeg"
+echo "VNC PNG support   $vnc_png"
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
@@ -2264,6 +2297,10 @@  if test "$vnc_jpeg" = "yes" ; then
   echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
   echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak
 fi
+if test "$vnc_png" = "yes" ; then
+  echo "CONFIG_VNC_PNG=y" >> $config_host_mak
+  echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
+fi
 if test "$fnmatch" = "yes" ; then
   echo "CONFIG_FNMATCH=y" >> $config_host_mak
 fi
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index 4ff88a8..cc57c26 100644
--- a/ui/vnc-enc-tight.c
+++ b/ui/vnc-enc-tight.c
@@ -26,13 +26,18 @@ 
  * THE SOFTWARE.
  */
 
-#include "qemu-common.h"
+#include "config-host.h"
 
+#ifdef CONFIG_VNC_PNG
+#include <png.h>
+#endif
 #ifdef CONFIG_VNC_JPEG
 #include <stdio.h>
 #include <jpeglib.h>
 #endif
 
+#include "qemu-common.h"
+
 #include "bswap.h"
 #include "qdict.h"
 #include "qint.h"
@@ -63,6 +68,29 @@  static const struct {
     { 65536, 2048,  32,  8192, 9, 9, 9, 6, 200, 500,  96, 80,   200,   500 }
 };
 
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                         int w, int h);
+
+#ifdef CONFIG_VNC_PNG
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+                         QDict *palette);
+
+static bool tight_can_send_png_rect(VncState *vs, int w, int h)
+{
+    if (vs->tight_type != VNC_ENCODING_TIGHT_PNG) {
+        return false;
+    }
+
+    if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+        vs->clientds.pf.bytes_per_pixel == 1) {
+        return false;
+    }
+
+    return true;
+}
+#endif
+
 /*
  * Code to guess if given rectangle is suitable for smooth image
  * compression (by applying "gradient" filter or JPEG coder).
@@ -466,6 +494,7 @@  static void print_palette(const char *key, QObject *obj, void *opaque)
         src = (uint##bpp##_t *) buf;                                    \
                                                                         \
         for (i = 0; i < count; i++) {                                   \
+                                                                        \
             rgb = *src++;                                               \
             rep = 0;                                                    \
             while (i < count && *src == rgb) {                          \
@@ -937,11 +966,17 @@  static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
     }
 }
 
-static int send_full_color_rect(VncState *vs, int w, int h)
+static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
 {
     int stream = 0;
     size_t bytes;
 
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        return send_png_rect(vs, x, y, w, h, NULL);
+    }
+#endif
+
     vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
 
     if (vs->tight_pixel24) {
@@ -975,12 +1010,27 @@  static int send_solid_rect(VncState *vs)
     return 1;
 }
 
-static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
+static int send_mono_rect(VncState *vs, int x, int y,
+                          int w, int h, uint32_t bg, uint32_t fg)
 {
     size_t bytes;
     int stream = 1;
     int level = tight_conf[vs->tight_compression].mono_zlib_level;
 
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        int ret;
+        QDict *palette = qdict_new();
+        int bpp = vs->clientds.pf.bytes_per_pixel * 8;
+
+        tight_palette_insert(palette, bg, bpp, 2);
+        tight_palette_insert(palette, fg, bpp, 2);
+        ret = send_png_rect(vs, x, y, w, h, palette);
+        QDECREF(palette);
+        return ret;
+    }
+#endif
+
     bytes = ((w + 7) / 8) * h;
 
     vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
@@ -1021,6 +1071,9 @@  static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
 struct palette_cb_priv {
     VncState *vs;
     uint8_t *header;
+#ifdef CONFIG_VNC_PNG
+    png_colorp png_palette;
+#endif
 };
 
 static void write_palette(const char *key, QObject *obj, void *opaque)
@@ -1041,14 +1094,14 @@  static void write_palette(const char *key, QObject *obj, void *opaque)
     }
 }
 
-static bool send_gradient_rect(VncState *vs, int w, int h)
+static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
 {
     int stream = 3;
     int level = tight_conf[vs->tight_compression].gradient_zlib_level;
     size_t bytes;
 
     if (vs->clientds.pf.bytes_per_pixel == 1)
-        return send_full_color_rect(vs, w, h);
+        return send_full_color_rect(vs, x, y, w, h);
 
     vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
     vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
@@ -1076,13 +1129,20 @@  static bool send_gradient_rect(VncState *vs, int w, int h)
     return (bytes >= 0);
 }
 
-static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
+static int send_palette_rect(VncState *vs, int x, int y,
+                             int w, int h, struct QDict *palette)
 {
     int stream = 2;
     int level = tight_conf[vs->tight_compression].idx_zlib_level;
     int colors;
     size_t bytes;
 
+#ifdef CONFIG_VNC_PNG
+    if (tight_can_send_png_rect(vs, w, h)) {
+        return send_png_rect(vs, x, y, w, h, palette);
+    }
+#endif
+
     colors = qdict_size(palette);
 
     vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
@@ -1130,12 +1190,9 @@  static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
     return (bytes >= 0);
 }
 
-/*
- * JPEG compression stuff.
- */
-#ifdef CONFIG_VNC_JPEG
-static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
-                                     int count)
+#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG)
+static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+                              int count)
 {
     VncDisplay *vd = vs->vd;
     uint32_t *fbptr;
@@ -1152,11 +1209,11 @@  static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
     }
 }
 
-#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp)                               \
+#define DEFINE_RGB_GET_ROW_FUNCTION(bpp)                                \
                                                                         \
     static void                                                         \
-    jpeg_prepare_row##bpp(VncState *vs, uint8_t *dst,                   \
-                                int x, int y, int count)                \
+    rgb_prepare_row##bpp(VncState *vs, uint8_t *dst,                    \
+                         int x, int y, int count)                       \
     {                                                                   \
         VncDisplay *vd = vs->vd;                                        \
         uint##bpp##_t *fbptr;                                           \
@@ -1186,21 +1243,26 @@  static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
         }                                                               \
     }
 
-DEFINE_JPEG_GET_ROW_FUNCTION(16)
-DEFINE_JPEG_GET_ROW_FUNCTION(32)
+DEFINE_RGB_GET_ROW_FUNCTION(16)
+DEFINE_RGB_GET_ROW_FUNCTION(32)
 
-static void jpeg_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
-                                       int count)
+static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+                            int count)
 {
     if (vs->tight_pixel24)
-        jpeg_prepare_row24(vs, dst, x, y, count);
+        rgb_prepare_row24(vs, dst, x, y, count);
     else if (ds_get_bytes_per_pixel(vs->ds) == 4)
-        jpeg_prepare_row32(vs, dst, x, y, count);
+        rgb_prepare_row32(vs, dst, x, y, count);
     else
-        jpeg_prepare_row16(vs, dst, x, y, count);
+        rgb_prepare_row16(vs, dst, x, y, count);
 }
+#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */
 
 /*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+/*
  * Destination manager implementation for JPEG library.
  */
 
@@ -1245,7 +1307,7 @@  static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
     int dy;
 
     if (ds_get_bytes_per_pixel(vs->ds) == 1)
-        return send_full_color_rect(vs, w, h);
+        return send_full_color_rect(vs, x, y, w, h);
 
     buffer_reserve(&vs->tight_jpeg, 2048);
 
@@ -1271,7 +1333,7 @@  static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
     buf = qemu_malloc(w * 3);
     row[0] = buf;
     for (dy = 0; dy < h; dy++) {
-        jpeg_prepare_row(vs, buf, x, y + dy, w);
+        rgb_prepare_row(vs, buf, x, y + dy, w);
         jpeg_write_scanlines(&cinfo, row, 1);
     }
     qemu_free(buf);
@@ -1289,6 +1351,162 @@  static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
 }
 #endif /* CONFIG_VNC_JPEG */
 
+/*
+ * PNG compression stuff.
+ */
+#ifdef CONFIG_VNC_PNG
+static void write_png_palette(const char *key, QObject *obj, void *opaque)
+{
+    struct palette_cb_priv *priv = opaque;
+    VncState *vs = priv->vs;
+    uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+    uint8_t idx = qint_get_int(qobject_to_qint(obj));
+    png_colorp color = &priv->png_palette[idx];
+    uint32_t pix;
+
+    if (bytes == 4) {
+        pix = tight_palette_buf2rgb(32, (uint8_t *)key);
+    } else {
+        pix = tight_palette_buf2rgb(16, (uint8_t *)key);
+    }
+
+    if (vs->tight_pixel24)
+    {
+        color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+        color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+        color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+    }
+    else
+    {
+        int red, green, blue;
+
+        red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+        green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+        blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+        color->red = ((red * 255 + vs->clientds.pf.rmax / 2) /
+                      vs->clientds.pf.rmax);
+        color->green = ((green * 255 + vs->clientds.pf.gmax / 2) /
+                        vs->clientds.pf.gmax);
+        color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) /
+                       vs->clientds.pf.bmax);
+    }
+}
+
+static void png_write_data(png_structp png_ptr, png_bytep data,
+                           png_size_t length)
+{
+    VncState *vs = png_get_io_ptr(png_ptr);
+
+    buffer_reserve(&vs->tight_png, vs->tight_png.offset + length);
+    memcpy(vs->tight_png.buffer + vs->tight_png.offset, data, length);
+
+    vs->tight_png.offset += length;
+}
+
+static void png_flush_data(png_structp png_ptr)
+{
+}
+
+static void *vnc_png_malloc(png_structp png_ptr, png_size_t size)
+{
+    return qemu_malloc(size);
+}
+
+static void vnc_png_free(png_structp png_ptr, png_voidp ptr)
+{
+    qemu_free(ptr);
+}
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+                         QDict *palette)
+{
+    png_byte color_type;
+    png_structp png_ptr;
+    png_infop info_ptr;
+    png_colorp png_palette = NULL;
+    size_t offset;
+    int level = tight_conf[vs->tight_compression].raw_zlib_level;
+    uint8_t *buf;
+    int dy;
+
+    png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+                                        NULL, vnc_png_malloc, vnc_png_free);
+
+    if (png_ptr == NULL)
+        return -1;
+
+    info_ptr = png_create_info_struct(png_ptr);
+
+    if (info_ptr == NULL) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return -1;
+    }
+
+    png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data);
+    png_set_compression_level(png_ptr, level);
+
+    if (palette) {
+        color_type = PNG_COLOR_TYPE_PALETTE;
+    } else {
+        color_type = PNG_COLOR_TYPE_RGB;
+    }
+
+    png_set_IHDR(png_ptr, info_ptr, w, h,
+                 8, color_type, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        struct palette_cb_priv priv;
+
+        png_palette = png_malloc(png_ptr, sizeof(*png_palette) *
+                                 qdict_size(palette));
+
+        priv.vs = vs;
+        priv.png_palette = png_palette;
+        qdict_iter(palette, write_png_palette, &priv);
+
+        png_set_PLTE(png_ptr, info_ptr, png_palette, qdict_size(palette));
+
+        offset = vs->tight.offset;
+        if (vs->clientds.pf.bytes_per_pixel == 4) {
+            tight_encode_indexed_rect32(vs->tight.buffer, w * h, palette);
+        } else {
+            tight_encode_indexed_rect16(vs->tight.buffer, w * h, palette);
+        }
+    }
+
+    png_write_info(png_ptr, info_ptr);
+
+    buffer_reserve(&vs->tight_png, 2048);
+    buf = qemu_malloc(w * 3);
+    for (dy = 0; dy < h; dy++)
+    {
+        if (color_type == PNG_COLOR_TYPE_PALETTE) {
+            memcpy(buf, vs->tight.buffer + (dy * w), w);
+        } else {
+            rgb_prepare_row(vs, buf, x, y + dy, w);
+        }
+        png_write_row(png_ptr, buf);
+    }
+    qemu_free(buf);
+
+    png_write_end(png_ptr, NULL);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_free(png_ptr, png_palette);
+    }
+
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+
+    vnc_write_u8(vs, VNC_TIGHT_PNG << 4);
+
+    tight_send_compact_size(vs, vs->tight_png.offset);
+    vnc_write(vs, vs->tight_png.buffer, vs->tight_png.offset);
+    buffer_reset(&vs->tight_png);
+    return 1;
+}
+#endif /* CONFIG_VNC_PNG */
+
 static void vnc_tight_start(VncState *vs)
 {
     buffer_reset(&vs->tight);
@@ -1312,7 +1530,7 @@  static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
     int colors;
     int ret = 0;
 
-    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
+    vnc_framebuffer_update(vs, x, y, w, h, vs->tight_type);
 
     vnc_tight_start(vs);
     vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -1323,23 +1541,23 @@  static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
     if (colors == 0) {
         if (tight_detect_smooth_image(vs, w, h)) {
             if (vs->tight_quality == -1) {
-                ret = send_gradient_rect(vs, w, h);
+                ret = send_gradient_rect(vs, x, y, w, h);
             } else {
 #ifdef CONFIG_VNC_JPEG
                 int quality = tight_conf[vs->tight_quality].jpeg_quality;
 
                 ret = send_jpeg_rect(vs, x, y, w, h, quality);
 #else
-                ret = send_full_color_rect(vs, w, h);
+                ret = send_full_color_rect(vs, x, y, w, h);
 #endif
             }
         } else {
-            ret = send_full_color_rect(vs, w, h);
+            ret = send_full_color_rect(vs, x, y, w, h);
         }
     } else if (colors == 1) {
         ret = send_solid_rect(vs);
     } else if (colors == 2) {
-        ret = send_mono_rect(vs, w, h, bg, fg);
+        ret = send_mono_rect(vs, x, y, w, h, bg, fg);
     } else if (colors <= 256) {
 #ifdef CONFIG_VNC_JPEG
         if (colors > 96 && vs->tight_quality != -1 && vs->tight_quality <= 3 &&
@@ -1348,10 +1566,10 @@  static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
 
             ret = send_jpeg_rect(vs, x, y, w, h, quality);
         } else {
-            ret = send_palette_rect(vs, w, h, palette);
+            ret = send_palette_rect(vs, x, y, w, h, palette);
         }
 #else
-        ret = send_palette_rect(vs, w, h, palette);
+        ret = send_palette_rect(vs, x, y, w, h, palette);
 #endif
     }
     QDECREF(palette);
@@ -1360,7 +1578,7 @@  static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
 
 static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
 {
-    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
+    vnc_framebuffer_update(vs, x, y, w, h, vs->tight_type);
 
     vnc_tight_start(vs);
     vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -1453,8 +1671,8 @@  static int find_large_solid_color_rect(VncState *vs, int x, int y,
                 n += send_rect_simple(vs, x, y, w, y_best-y);
             }
             if (x_best != x) {
-                n += vnc_tight_send_framebuffer_update(vs, x, y_best,
-                                                       x_best-x, h_best);
+                n += tight_send_framebuffer_update(vs, x, y_best,
+                                                   x_best-x, h_best);
             }
 
             /* Send solid-color rectangle. */
@@ -1463,14 +1681,14 @@  static int find_large_solid_color_rect(VncState *vs, int x, int y,
             /* Send remaining rectangles (at right and bottom). */
 
             if (x_best + w_best != x + w) {
-                n += vnc_tight_send_framebuffer_update(vs, x_best+w_best,
-                                                       y_best,
-                                                       w-(x_best-x)-w_best,
-                                                       h_best);
+                n += tight_send_framebuffer_update(vs, x_best+w_best,
+                                                   y_best,
+                                                   w-(x_best-x)-w_best,
+                                                   h_best);
             }
             if (y_best + h_best != y + h) {
-                n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best,
-                                                       w, h-(y_best-y)-h_best);
+                n += tight_send_framebuffer_update(vs, x, y_best+h_best,
+                                                   w, h-(y_best-y)-h_best);
             }
 
             /* Return after all recursive calls are done. */
@@ -1480,8 +1698,8 @@  static int find_large_solid_color_rect(VncState *vs, int x, int y,
     return n + send_rect_simple(vs, x, y, w, h);
 }
 
-int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
-                                      int w, int h)
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                         int w, int h)
 {
     int max_rows;
 
@@ -1503,6 +1721,20 @@  int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
     return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
 }
 
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+                                      int w, int h)
+{
+    vs->tight_type = VNC_ENCODING_TIGHT;
+    return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+                                          int w, int h)
+{
+    vs->tight_type = VNC_ENCODING_TIGHT_PNG;
+    return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
 void vnc_tight_clear(VncState *vs)
 {
     int i;
diff --git a/ui/vnc-enc-tight.h b/ui/vnc-enc-tight.h
index 9b0910c..a3add78 100644
--- a/ui/vnc-enc-tight.h
+++ b/ui/vnc-enc-tight.h
@@ -42,8 +42,9 @@ 
  *   bit 3:    if 1, then compression stream 3 should be reset;
  *   bits 7-4: if 1000 (0x08), then the compression type is "fill",
  *             if 1001 (0x09), then the compression type is "jpeg",
+ *             if 1010 (0x0A), then the compression type is "png",
  *             if 0xxx, then the compression type is "basic",
- *             values greater than 1001 are not valid.
+ *             values greater than 1010 are not valid.
  *
  * If the compression type is "basic", then bits 6..4 of the
  * compression control byte (those xxx in 0xxx) specify the following:
@@ -53,17 +54,17 @@ 
  *   bit 6:     if 1, then a "filter id" byte is following this byte.
  *
  *-- The data that follows after the compression control byte described
- * above depends on the compression type ("fill", "jpeg" or "basic").
+ * above depends on the compression type ("fill", "jpeg", "png" or "basic").
  *
  *-- If the compression type is "fill", then the only pixel value follows, in
  * client pixel format (see NOTE 1). This value applies to all pixels of the
  * rectangle.
  *
- *-- If the compression type is "jpeg", the following data stream looks like
- * this:
+ *-- If the compression type is "jpeg" or "png", the following data stream
+ * looks like this:
  *
  *   1..3 bytes:  data size (N) in compact representation;
- *   N bytes:     JPEG image.
+ *   N bytes:     JPEG or PNG image.
  *
  * Data size is compactly represented in one, two or three bytes, according
  * to the following scheme:
@@ -144,7 +145,7 @@ 
  *-- NOTE 2. The decoder must reset compression streams' states before
  * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
  * byte are set to 1. Note that the decoder must reset zlib streams even if
- * the compression type is "fill" or "jpeg".
+ * the compression type is "fill", "jpeg" or "png".
  *
  *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
  * when bits-per-pixel value is either 16 or 32, not 8.
@@ -158,7 +159,8 @@ 
 #define VNC_TIGHT_EXPLICIT_FILTER       0x04
 #define VNC_TIGHT_FILL                  0x08
 #define VNC_TIGHT_JPEG                  0x09
-#define VNC_TIGHT_MAX_SUBENCODING       0x09
+#define VNC_TIGHT_PNG                   0x0A
+#define VNC_TIGHT_MAX_SUBENCODING       0x0A
 
 /* Filters to improve compression efficiency */
 #define VNC_TIGHT_FILTER_COPY             0x00
diff --git a/ui/vnc.c b/ui/vnc.c
index ccd7aad..1fc6d38 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -351,10 +351,6 @@  void do_info_vnc(Monitor *mon, QObject **ret_data)
     }
 }
 
-static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
-    return (vs->features & (1 << feature));
-}
-
 /* TODO
    1) Get the queue working for IO.
    2) there is some weirdness when using the -S option (the screen is grey
@@ -661,6 +657,9 @@  static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
         case VNC_ENCODING_TIGHT:
             n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
             break;
+        case VNC_ENCODING_TIGHT_PNG:
+            n = vnc_tight_png_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);
@@ -1669,6 +1668,10 @@  static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
             vs->features |= VNC_FEATURE_TIGHT_MASK;
             vs->vnc_encoding = enc;
             break;
+        case VNC_ENCODING_TIGHT_PNG:
+            vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+            vs->vnc_encoding = enc;
+            break;
         case VNC_ENCODING_ZLIB:
             vs->features |= VNC_FEATURE_ZLIB_MASK;
             vs->vnc_encoding = enc;
diff --git a/ui/vnc.h b/ui/vnc.h
index ec90cd3..3b8b911 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -172,6 +172,7 @@  struct VncState
     /* Encoding specific */
 
     /* Tight */
+    int tight_type;
     uint8_t tight_quality;
     uint8_t tight_compression;
     uint8_t tight_pixel24;
@@ -182,6 +183,9 @@  struct VncState
 #ifdef CONFIG_VNC_JPEG
     Buffer tight_jpeg;
 #endif
+#ifdef CONFIG_VNC_PNG
+    Buffer tight_png;
+#endif
     int tight_levels[4];
     z_stream tight_stream[4];
 
@@ -259,6 +263,7 @@  enum {
 #define VNC_ENCODING_POINTER_TYPE_CHANGE  0XFFFFFEFF /* -257 */
 #define VNC_ENCODING_EXT_KEY_EVENT        0XFFFFFEFE /* -258 */
 #define VNC_ENCODING_AUDIO                0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_TIGHT_PNG            0xFFFFFEFC /* -260 */
 #define VNC_ENCODING_WMVi                 0x574D5669
 
 /*****************************************************************************
@@ -275,6 +280,7 @@  enum {
 #define VNC_TIGHT_CCB_TYPE_MASK    (0x0f << 4)
 #define VNC_TIGHT_CCB_TYPE_FILL    (0x08 << 4)
 #define VNC_TIGHT_CCB_TYPE_JPEG    (0x09 << 4)
+#define VNC_TIGHT_CCB_TYPE_PNG     (0x0A << 4)
 #define VNC_TIGHT_CCB_BASIC_MAX    (0x07 << 4)
 #define VNC_TIGHT_CCB_BASIC_ZLIB   (0x03 << 4)
 #define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
@@ -293,6 +299,7 @@  enum {
 #define VNC_FEATURE_ZLIB                     5
 #define VNC_FEATURE_COPYRECT                 6
 #define VNC_FEATURE_RICH_CURSOR              7
+#define VNC_FEATURE_TIGHT_PNG                8
 
 #define VNC_FEATURE_RESIZE_MASK              (1 << VNC_FEATURE_RESIZE)
 #define VNC_FEATURE_HEXTILE_MASK             (1 << VNC_FEATURE_HEXTILE)
@@ -302,6 +309,7 @@  enum {
 #define VNC_FEATURE_ZLIB_MASK                (1 << VNC_FEATURE_ZLIB)
 #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)
 
 
 /* Client -> Server message IDs */
@@ -405,6 +413,10 @@  void buffer_append(Buffer *buffer, const void *data, size_t len);
 char *vnc_socket_local_addr(const char *format, int fd);
 char *vnc_socket_remote_addr(const char *format, int fd);
 
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+    return (vs->features & (1 << feature));
+}
+
 /* Framebuffer */
 void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
                             int32_t encoding);
@@ -423,8 +435,9 @@  void vnc_zlib_zfree(void *x, void *addr);
 int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
 void vnc_zlib_clear(VncState *vs);
 
-
 int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+                                          int w, int h);
 void vnc_tight_clear(VncState *vs);
 
 #endif /* __QEMU_VNC_H */