summaryrefslogtreecommitdiff
path: root/ui/vnc-enc-tight.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/vnc-enc-tight.c')
-rw-r--r--ui/vnc-enc-tight.c316
1 files changed, 274 insertions, 42 deletions
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index 4ff88a8b03..cc57c26796 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;