[gtk-vnc-devel] [PATCH] Support for tight encoding



Hi,

The attached patch implements the tight encoding. Tight turned out to require a bit more changes than ZRLE. Perhaps the strangest thing requirement is jpeg decoding. Instead of linking directly against libjpeg, I decide to make jpeg decoding a gvnc callback. This lets VncDisplay implement jpeg decompression using GdkPixbuf. jpeg support in tight is entirely optional so what we really should do in VncDisplay is detect whether GdkPixbuf supports jpeg and if not, disable jpeg support.

Tight is not well documented and I ran into a lot of problems getting the edge cases right so we'll really need to test this out.

I'm going to do a follow up patch to selectively enable/disabling encodings and also provide a mechanism to set the jpeg compression level.

Regards,

Anthony Liguori
diff -r 978cb4791ef2 src/blt.h
--- a/src/blt.h	Fri Dec 28 20:22:06 2007 -0600
+++ b/src/blt.h	Sun Dec 30 22:39:00 2007 -0600
@@ -2,6 +2,7 @@
  * a Solaris compiler/cpp  whitespace bug
  */
 #define src_pixel_t SPLICE(SPLICE(uint, SRC), _t)
+#define ssrc_pixel_t SPLICE(SPLICE(int, SRC), _t)
 #define dst_pixel_t SPLICE(SPLICE(uint, DST), _t)
 #define SUFFIX() SPLICE(SRC,SPLICE(x,DST))
 #define SET_PIXEL SPLICE(gvnc_set_pixel_, SUFFIX())
@@ -12,6 +13,10 @@
 #define HEXTILE SPLICE(gvnc_hextile_, SUFFIX())
 #define RRE SPLICE(gvnc_rre_, SUFFIX())
 #define RICH_CURSOR_BLIT SPLICE(gvnc_rich_cursor_blt_, SUFFIX())
+#define RGB24_BLIT SPLICE(gvnc_rgb24_blt_, SUFFIX())
+#define TIGHT_COMPUTE_PREDICTED SPLICE(gvnc_tight_compute_predicted_, SUFFIX())
+#define TIGHT_SUM_PIXEL SPLICE(gvnc_tight_sum_pixel_, SUFFIX())
+#define COMPONENT(color, pixel) (((pixel) >> gvnc->fmt.SPLICE(color, _shift) & gvnc->fmt.SPLICE(color, _max)))
 
 static void FAST_FILL(struct gvnc *gvnc, src_pixel_t *sp,
 		      int x, int y, int width, int height)
@@ -175,9 +180,9 @@ static void RICH_CURSOR_BLIT(struct gvnc
 		src_pixel_t *sp = (src_pixel_t *)src;
 		uint8_t *mp = alpha;
 		for (x1 = 0; x1 < width; x1++) {
-			*dst = (((*sp >> gvnc->fmt.red_shift) & gvnc->fmt.red_max) << rs)
-				| (((*sp >> gvnc->fmt.green_shift) & gvnc->fmt.green_max) << gs)
-				| (((*sp >> gvnc->fmt.blue_shift) & gvnc->fmt.blue_max) << bs);
+			*dst = (COMPONENT(red, *sp) << rs)
+				| (COMPONENT(green, *sp) << gs)
+				| (COMPONENT(blue, *sp) << bs);
 
 			if ((mp[x1 / 8] >> (7 - (x1 % 8))) & 1)
 				*dst |= 0xFF000000;
@@ -191,6 +196,73 @@ static void RICH_CURSOR_BLIT(struct gvnc
 }
 #endif
 
+#if SRC == 32
+static void RGB24_BLIT(struct gvnc *gvnc, int x, int y, int width, int height,
+		       uint8_t *data, int pitch)
+{
+	uint8_t *dst = gvnc_get_local(gvnc, x, y);
+	int i, j;
+
+	for (j = 0; j < height; j++) {
+		dst_pixel_t *dp = (dst_pixel_t *)dst;
+		uint8_t *sp = data;
+
+		for (i = 0; i < width; i++) {
+			*dp = (((sp[0] * gvnc->fmt.red_max) / 255) << gvnc->fmt.red_shift) |
+				(((sp[1] * gvnc->fmt.green_max) / 255) << gvnc->fmt.green_shift) |
+				(((sp[2] * gvnc->fmt.blue_max) / 255) << gvnc->fmt.blue_shift);
+			dp++;
+			sp += 3;
+		}
+
+		dst += gvnc->local.linesize;
+		data += pitch;
+	}
+}
+#endif
+
+#if SRC == DST
+
+static void TIGHT_COMPUTE_PREDICTED(struct gvnc *gvnc, src_pixel_t *ppixel,
+				    src_pixel_t *lp, src_pixel_t *cp,
+				    src_pixel_t *llp)
+{
+	ssrc_pixel_t red, green, blue;
+
+	red = COMPONENT(red, *lp) + COMPONENT(red, *cp) - COMPONENT(red, *llp);
+	red = MAX(red, 0);
+	red = MIN(red, gvnc->fmt.red_max);
+
+	green = COMPONENT(green, *lp) + COMPONENT(green, *cp) - COMPONENT(green, *llp);
+	green = MAX(green, 0);
+	green = MIN(green, gvnc->fmt.green_max);
+
+	blue = COMPONENT(blue, *lp) + COMPONENT(blue, *cp) - COMPONENT(blue, *llp);
+	blue = MAX(blue, 0);
+	blue = MIN(blue, gvnc->fmt.blue_max);
+
+	*ppixel = (red << gvnc->fmt.red_shift) |
+		(green << gvnc->fmt.green_shift) |
+		(blue << gvnc->fmt.blue_shift);
+}
+
+static void TIGHT_SUM_PIXEL(struct gvnc *gvnc,
+			    src_pixel_t *lhs, src_pixel_t *rhs)
+{
+	src_pixel_t red, green, blue;
+
+	red = COMPONENT(red, *lhs) + COMPONENT(red, *rhs);
+	green = COMPONENT(green, *lhs) + COMPONENT(green, *rhs);
+	blue = COMPONENT(blue, *lhs) + COMPONENT(blue, *rhs);
+
+	*lhs = ((red & gvnc->fmt.red_max) << gvnc->fmt.red_shift) |
+		((green & gvnc->fmt.green_max) << gvnc->fmt.green_shift) |
+		((blue & gvnc->fmt.blue_max) << gvnc->fmt.blue_shift);
+}
+
+#endif
+
+#undef COMPONENT
 #undef HEXTILE
 #undef FILL
 #undef FAST_FILL
diff -r 978cb4791ef2 src/gvnc.c
--- a/src/gvnc.c	Fri Dec 28 20:22:06 2007 -0600
+++ b/src/gvnc.c	Sun Dec 30 22:39:00 2007 -0600
@@ -58,6 +58,15 @@ typedef void gvnc_rich_cursor_blt_func(s
 typedef void gvnc_rich_cursor_blt_func(struct gvnc *, uint8_t *, uint8_t *,
 				       uint8_t *, int, uint16_t, uint16_t);
 
+typedef void gvnc_rgb24_blt_func(struct gvnc *, int, int, int, int,
+				 uint8_t *, int);
+
+typedef void gvnc_tight_compute_predicted_func(struct gvnc *, uint8_t *,
+					       uint8_t *, uint8_t *,
+					       uint8_t *);
+
+typedef void gvnc_tight_sum_pixel_func(struct gvnc *, uint8_t *, uint8_t *);
+
 /*
  * A special GSource impl which allows us to wait on a certain
  * condition to be satisified. This is effectively a boolean test
@@ -122,6 +131,9 @@ struct gvnc
 	gvnc_set_pixel_at_func *set_pixel_at;
 	gvnc_hextile_func *hextile;
 	gvnc_rich_cursor_blt_func *rich_cursor_blt;
+	gvnc_rgb24_blt_func *rgb24_blt;
+	gvnc_tight_compute_predicted_func *tight_compute_predicted;
+	gvnc_tight_sum_pixel_func *tight_sum_pixel;
 
 	struct gvnc_ops ops;
 	gpointer ops_data;
@@ -135,7 +147,8 @@ struct gvnc
 	int xmit_buffer_capacity;
 	int xmit_buffer_size;
 
-	z_stream strm;
+	z_stream *strm;
+	z_stream streams[5];
 
 	size_t uncompressed_length;
 	uint8_t uncompressed_buffer[4096];
@@ -294,21 +307,21 @@ static int gvnc_zread(struct gvnc *gvnc,
 		} else {
 			int err;
 
-			gvnc->strm.next_in = gvnc->compressed_buffer;
-			gvnc->strm.avail_in = gvnc->compressed_length;
-			gvnc->strm.next_out = gvnc->uncompressed_buffer;
-			gvnc->strm.avail_out = sizeof(gvnc->uncompressed_buffer);
+			gvnc->strm->next_in = gvnc->compressed_buffer;
+			gvnc->strm->avail_in = gvnc->compressed_length;
+			gvnc->strm->next_out = gvnc->uncompressed_buffer;
+			gvnc->strm->avail_out = sizeof(gvnc->uncompressed_buffer);
 
 			/* inflate as much as possible */
-			err = inflate(&gvnc->strm, Z_SYNC_FLUSH);
+			err = inflate(gvnc->strm, Z_SYNC_FLUSH);
 			if (err != Z_OK) {
 				errno = EIO;
 				return -1;
 			}
 
-			gvnc->uncompressed_length = (uint8_t *)gvnc->strm.next_out - gvnc->uncompressed_buffer;
-			gvnc->compressed_length -= (uint8_t *)gvnc->strm.next_in - gvnc->compressed_buffer;
-			gvnc->compressed_buffer = gvnc->strm.next_in;
+			gvnc->uncompressed_length = (uint8_t *)gvnc->strm->next_out - gvnc->uncompressed_buffer;
+			gvnc->compressed_length -= (uint8_t *)gvnc->strm->next_in - gvnc->compressed_buffer;
+			gvnc->compressed_buffer = gvnc->strm->next_in;
 		}
 	}
 
@@ -972,6 +985,24 @@ static gvnc_rich_cursor_blt_func *gvnc_r
 	gvnc_rich_cursor_blt_32x32,
 };
 
+static gvnc_rgb24_blt_func *gvnc_rgb24_blt_table[3] = {
+	(gvnc_rgb24_blt_func *)gvnc_rgb24_blt_32x8,
+	(gvnc_rgb24_blt_func *)gvnc_rgb24_blt_32x16,
+	(gvnc_rgb24_blt_func *)gvnc_rgb24_blt_32x32,
+};
+
+static gvnc_tight_compute_predicted_func *gvnc_tight_compute_predicted_table[3] = {
+	(gvnc_tight_compute_predicted_func *)gvnc_tight_compute_predicted_8x8,
+	(gvnc_tight_compute_predicted_func *)gvnc_tight_compute_predicted_16x16,
+	(gvnc_tight_compute_predicted_func *)gvnc_tight_compute_predicted_32x32,
+};
+
+static gvnc_tight_sum_pixel_func *gvnc_tight_sum_pixel_table[3] = {
+	(gvnc_tight_sum_pixel_func *)gvnc_tight_sum_pixel_8x8,
+	(gvnc_tight_sum_pixel_func *)gvnc_tight_sum_pixel_16x16,
+	(gvnc_tight_sum_pixel_func *)gvnc_tight_sum_pixel_32x32,
+};
+
 /* a fast blit for the perfect match scenario */
 static void gvnc_blt_fast(struct gvnc *gvnc, uint8_t *src, int pitch,
 			  int x, int y, int width, int height)
@@ -1313,6 +1344,7 @@ static void gvnc_zrle_update(struct gvnc
 	gvnc->uncompressed_length = 0;
 	gvnc->compressed_length = length;
 	gvnc->compressed_buffer = zlib_data;
+	gvnc->strm = &gvnc->streams[0];
 
 	offset = 0;
 	for (j = 0; j < height; j += 64) {
@@ -1325,11 +1357,307 @@ static void gvnc_zrle_update(struct gvnc
 		}
 	}
 
+	gvnc->strm = NULL;
 	gvnc->uncompressed_length = 0;
 	gvnc->compressed_length = 0;
 	gvnc->compressed_buffer = NULL;
 
 	free(zlib_data);
+}
+
+static void gvnc_rgb24_blt(struct gvnc *gvnc, int x, int y,
+			   int width, int height, uint8_t *data, int pitch)
+{
+	gvnc->rgb24_blt(gvnc, x, y, width, height, data, pitch);
+}
+
+static uint32_t gvnc_read_cint(struct gvnc *gvnc)
+{
+	uint32_t value = 0;
+	uint8_t val;
+
+	val = gvnc_read_u8(gvnc);
+	value = (val & 0x7F);
+	if (!(val & 0x80))
+		return value;
+
+	val = gvnc_read_u8(gvnc);
+	value |= (val & 0x7F) << 7;
+
+	if (!(val & 0x80))
+		return value;
+
+	value |= gvnc_read_u8(gvnc) << 14;
+
+	return value;
+}
+
+static int gvnc_tpixel_size(struct gvnc *gvnc)
+{
+	if (gvnc->fmt.depth == 24)
+		return 3;
+	return gvnc->fmt.bits_per_pixel / 8;
+}
+
+static void gvnc_read_tpixel(struct gvnc *gvnc, uint8_t *pixel)
+{
+	if (gvnc->fmt.depth == 24) {
+		uint32_t val;
+		gvnc_read(gvnc, pixel, 3);
+		val = (pixel[0] << gvnc->fmt.red_shift)
+			| (pixel[1] << gvnc->fmt.green_shift)
+			| (pixel[2] << gvnc->fmt.blue_shift);
+		memcpy(pixel, &val, 4);
+	} else
+		gvnc_read_pixel(gvnc, pixel);
+}
+
+static void gvnc_tight_update_copy(struct gvnc *gvnc,
+				   uint16_t x, uint16_t y,
+				   uint16_t width, uint16_t height)
+{
+	uint8_t pixel[4];
+	int i, j;
+
+	for (j = 0; j < height; j++) {
+		for (i = 0; i < width; i++) {
+			gvnc_read_tpixel(gvnc, pixel);
+			gvnc_set_pixel_at(gvnc, x + i, y + j, pixel);
+		}
+	}
+}
+
+static int gvnc_tight_get_pi(struct gvnc *gvnc, uint8_t *ra,
+			     int i, uint8_t palette_size)
+{
+	if (palette_size == 2) {
+		if ((i % 8) == 0)
+			*ra = gvnc_read_u8(gvnc);
+		return (*ra >> (7 - (i % 8))) & 1;
+	}
+
+	return gvnc_read_u8(gvnc);
+}
+
+static void gvnc_tight_update_palette(struct gvnc *gvnc,
+				      int palette_size, uint8_t *palette,
+				      uint16_t x, uint16_t y,
+				      uint16_t width, uint16_t height)
+{
+	int i, j;
+
+	for (j = 0; j < height; j++) {
+		uint8_t ra = 0;
+
+		for (i = 0; i < width; i++) {
+			uint8_t ind;
+
+			ind = gvnc_tight_get_pi(gvnc, &ra, i, palette_size);
+			gvnc_set_pixel_at(gvnc, x + i, y + j,
+					  &palette[ind * 4]);
+		}
+	}
+}
+
+static void gvnc_tight_compute_predicted(struct gvnc *gvnc, uint8_t *ppixel,
+					  uint8_t *lp, uint8_t *cp,
+					  uint8_t *llp)
+{
+	gvnc->tight_compute_predicted(gvnc, ppixel, lp, cp, llp);
+}
+
+static void gvnc_tight_sum_pixel(struct gvnc *gvnc,
+				 uint8_t *lhs, uint8_t *rhs)
+{
+	gvnc->tight_sum_pixel(gvnc, lhs, rhs);
+}
+
+static void gvnc_tight_update_gradient(struct gvnc *gvnc,
+				       uint16_t x, uint16_t y,
+				       uint16_t width, uint16_t height)
+{
+	int i, j;
+	uint8_t zero_pixel[4];
+	uint8_t *last_row, *row;
+	int bpp;
+
+	bpp = gvnc_pixel_size(gvnc);
+	last_row = g_malloc(width * bpp);
+	row = g_malloc(width * bpp);
+
+	memset(last_row, 0, width * bpp);
+	memset(zero_pixel, 0, 4);
+
+	for (j = 0; j < height; j++) {
+		uint8_t *tmp_row;
+		uint8_t *llp, *lp;
+
+		/* use zero pixels for the edge cases */
+		llp = zero_pixel;
+		lp = zero_pixel;
+
+		for (i = 0; i < width; i++) {
+			uint8_t predicted_pixel[4];
+
+			/* compute predicted pixel value */
+			gvnc_tight_compute_predicted(gvnc, predicted_pixel,
+						     lp, last_row + i * bpp,
+						     llp);
+
+			/* read the difference pixel from the wire */
+			gvnc_read_tpixel(gvnc, row + i * bpp);
+
+			/* sum the predicted pixel and the difference to get
+			 * the original pixel value */
+			gvnc_tight_sum_pixel(gvnc, row + i * bpp,
+					     predicted_pixel);
+
+			llp = last_row + i * bpp;
+			lp = row + i * bpp;
+		}
+
+		/* write out row of pixel data */
+		gvnc_blt(gvnc, row, width * bpp, x, y + j, width, 1);
+
+		/* swap last row and current row */
+		tmp_row = last_row;
+		last_row = row;
+		row = tmp_row;
+	}
+
+	g_free(row);
+	g_free(last_row);
+}
+
+static void jpeg_draw(void *opaque, int x, int y, int w, int h,
+		      uint8_t *data, int stride)
+{
+	struct gvnc *gvnc = opaque;
+
+	gvnc_rgb24_blt(gvnc, x, y, w, h, data, stride);
+}
+
+static void gvnc_tight_update_jpeg(struct gvnc *gvnc, uint16_t x, uint16_t y,
+				   uint16_t width, uint16_t height,
+				   uint8_t *data, size_t length)
+{
+	uint8_t *image;
+
+	image = g_malloc(width * height * 3);
+
+	gvnc->ops.render_jpeg(gvnc->ops_data, jpeg_draw, gvnc,
+			      x, y, width, height, data, length);
+	g_free(image);
+}
+
+static void gvnc_tight_update(struct gvnc *gvnc,
+			      uint16_t x, uint16_t y,
+			      uint16_t width, uint16_t height)
+{
+	uint8_t ccontrol;
+	uint8_t pixel[4];
+	int i;
+
+	ccontrol = gvnc_read_u8(gvnc);
+
+	for (i = 0; i < 4; i++) {
+		if (ccontrol & (1 << i)) {
+			inflateEnd(&gvnc->streams[i + 1]);
+			inflateInit(&gvnc->streams[i + 1]);
+		}
+	}
+
+	ccontrol >>= 4;
+	ccontrol &= 0x0F;
+
+	switch (ccontrol) {
+	case 0 ... 7: { /* basic */
+		uint8_t filter_id = 0;
+		uint32_t data_size, zlib_length;
+		uint8_t *zlib_data;
+		uint8_t palette[256][4];
+		int palette_size;
+
+		if (ccontrol & 0x04)
+			filter_id = gvnc_read_u8(gvnc);
+
+		gvnc->strm = &gvnc->streams[(ccontrol & 0x03) + 1];
+
+		if (filter_id == 1) {
+			palette_size = gvnc_read_u8(gvnc);
+			palette_size += 1;
+			for (i = 0; i < palette_size; i++)
+				gvnc_read_tpixel(gvnc, palette[i]);
+		}
+
+		if (filter_id == 1) {
+			if (palette_size == 2)
+				data_size = ((width + 7) / 8) * height;
+			else
+				data_size = width * height;
+		} else
+			data_size = width * height * gvnc_tpixel_size(gvnc);
+
+		if (data_size >= 12) {
+			zlib_length = gvnc_read_cint(gvnc);
+			zlib_data = g_malloc(zlib_length);
+
+			gvnc_read(gvnc, zlib_data, zlib_length);
+
+			gvnc->uncompressed_length = 0;
+			gvnc->compressed_length = zlib_length;
+			gvnc->compressed_buffer = zlib_data;
+		}
+
+		switch (filter_id) {
+		case 0: /* copy */
+			gvnc_tight_update_copy(gvnc, x, y, width, height);
+			break;
+		case 1: /* palette */
+			gvnc_tight_update_palette(gvnc, palette_size,
+						  (uint8_t *)palette,
+						  x, y, width, height);
+			break;
+		case 2: /* gradient */
+			gvnc_tight_update_gradient(gvnc, x, y, width, height);
+			break;
+		default: /* error */
+			gvnc->has_error = TRUE;
+			break;
+		}
+
+		if (data_size >= 12) {
+			gvnc->uncompressed_length = 0;
+			gvnc->compressed_length = 0;
+			gvnc->compressed_buffer = NULL;
+
+			g_free(zlib_data);
+		}
+
+		gvnc->strm = NULL;
+		break;
+	}
+	case 8: /* fill */
+		/* FIXME check each width; endianness */
+		gvnc_read_tpixel(gvnc, pixel);
+		gvnc_fill(gvnc, pixel, x, y, width, height);
+		break;
+	case 9: { /* jpeg */
+		uint32_t length;
+		uint8_t *jpeg_data;
+
+		length = gvnc_read_cint(gvnc);
+		jpeg_data = g_malloc(length);
+		gvnc_read(gvnc, jpeg_data, length);
+		gvnc_tight_update_jpeg(gvnc, x, y, width, height,
+				       jpeg_data, length);
+		g_free(jpeg_data);
+		break;
+	}
+	default: /* error */
+		gvnc->has_error = TRUE;
+		break;
+	}
 }
 
 static void gvnc_update(struct gvnc *gvnc, int x, int y, int width, int height)
@@ -1521,6 +1849,9 @@ static void gvnc_framebuffer_update(stru
 		break;
 	case GVNC_ENCODING_ZRLE:
 		gvnc_zrle_update(gvnc, x, y, width, height);
+		break;
+	case GVNC_ENCODING_TIGHT:
+		gvnc_tight_update(gvnc, x, y, width, height);
 		break;
 	case GVNC_ENCODING_DESKTOP_RESIZE:
 		gvnc_resize(gvnc, width, height);
@@ -2188,6 +2519,8 @@ void gvnc_free(struct gvnc *gvnc)
 
 void gvnc_close(struct gvnc *gvnc)
 {
+	int i;
+
 	if (gvnc->tls_session) {
 		gnutls_bye(gvnc->tls_session, GNUTLS_SHUT_RDWR);
 		gvnc->tls_session = NULL;
@@ -2242,7 +2575,8 @@ void gvnc_close(struct gvnc *gvnc)
 		gvnc->cred_x509_key = NULL;
 	}
 
-	inflateEnd(&gvnc->strm);
+	for (i = 0; i < 5; i++)
+		inflateEnd(&gvnc->streams[i]);
 
 	gvnc->auth_type = GVNC_AUTH_INVALID;
 	gvnc->auth_subtype = GVNC_AUTH_INVALID;
@@ -2283,7 +2617,7 @@ gboolean gvnc_is_initialized(struct gvnc
 
 gboolean gvnc_initialize(struct gvnc *gvnc, gboolean shared_flag)
 {
-	int ret;
+	int ret, i;
 	char version[13];
 	uint32_t n_name;
 
@@ -2337,7 +2671,9 @@ gboolean gvnc_initialize(struct gvnc *gv
 
 	memset(&gvnc->strm, 0, sizeof(gvnc->strm));
 	/* FIXME what level? */
-	inflateInit(&gvnc->strm);
+	for (i = 0; i < 5; i++)
+		inflateInit(&gvnc->streams[i]);
+	gvnc->strm = NULL;
 
 	gvnc_resize(gvnc, gvnc->width, gvnc->height);
 	return !gvnc_has_error(gvnc);
@@ -2636,6 +2972,9 @@ gboolean gvnc_set_local(struct gvnc *gvn
 	gvnc->set_pixel_at = gvnc_set_pixel_at_table[i - 1][j - 1];
 	gvnc->hextile = gvnc_hextile_table[i - 1][j - 1];
 	gvnc->rich_cursor_blt = gvnc_rich_cursor_blt_table[i - 1];
+	gvnc->rgb24_blt = gvnc_rgb24_blt_table[i - 1];
+	gvnc->tight_compute_predicted = gvnc_tight_compute_predicted_table[i - 1];
+	gvnc->tight_sum_pixel = gvnc_tight_sum_pixel_table[i - 1];
 
 	if (gvnc->perfect_match)
 		gvnc->blt = gvnc_blt_fast;
diff -r 978cb4791ef2 src/gvnc.h
--- a/src/gvnc.h	Fri Dec 28 20:22:06 2007 -0600
+++ b/src/gvnc.h	Sun Dec 30 22:39:00 2007 -0600
@@ -5,6 +5,8 @@
 #include <stdint.h>
 
 struct gvnc;
+
+typedef void (rgb24_render_func)(void *, int, int, int, int, uint8_t *, int);
 
 struct gvnc_ops
 {
@@ -20,6 +22,8 @@ struct gvnc_ops
 	gboolean (*pointer_type_change)(void *, int);
 	gboolean (*local_cursor)(void *, int, int, int, int, uint8_t *);
 	gboolean (*auth_unsupported)(void *, unsigned int);
+	gboolean (*render_jpeg)(void *, rgb24_render_func *render, void *,
+				int, int, int, int, uint8_t *, int);
 };
 
 struct gvnc_pixel_format
@@ -63,8 +67,22 @@ typedef enum {
 	GVNC_ENCODING_RRE = 2,
 	GVNC_ENCODING_CORRE = 4,
 	GVNC_ENCODING_HEXTILE = 5,
+	GVNC_ENCODING_TIGHT = 7,
 	GVNC_ENCODING_ZRLE = 16,
 
+	/* Tight JPEG quality levels */
+	GVNC_ENCODING_TIGHT_JPEG0 = -32,
+	GVNC_ENCODING_TIGHT_JPEG1 = -31,
+	GVNC_ENCODING_TIGHT_JPEG2 = -30,
+	GVNC_ENCODING_TIGHT_JPEG3 = -29,
+	GVNC_ENCODING_TIGHT_JPEG4 = -28,
+	GVNC_ENCODING_TIGHT_JPEG5 = -27,
+	GVNC_ENCODING_TIGHT_JPEG6 = -26,
+	GVNC_ENCODING_TIGHT_JPEG7 = -25,
+	GVNC_ENCODING_TIGHT_JPEG8 = -24,
+	GVNC_ENCODING_TIGHT_JPEG9 = -23,
+
+	/* Pseudo encodings */
 	GVNC_ENCODING_DESKTOP_RESIZE = -223,
 	GVNC_ENCODING_CURSOR_POS = -232,
 	GVNC_ENCODING_RICH_CURSOR = -239,
diff -r 978cb4791ef2 src/vncdisplay.c
--- a/src/vncdisplay.c	Fri Dec 28 20:22:06 2007 -0600
+++ b/src/vncdisplay.c	Sun Dec 30 22:39:00 2007 -0600
@@ -18,6 +18,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <gdk/gdkkeysyms.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -656,6 +657,34 @@ static gboolean on_local_cursor(void *op
 	return TRUE;
 }
 
+static gboolean on_render_jpeg(void *opaque,
+			       rgb24_render_func *render, void *render_opaque,
+			       int x, int y, int w, int h,
+			       uint8_t *data, int size)
+{
+	GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+	GdkPixbuf *p;
+	uint8_t *pixels;
+
+	if (!gdk_pixbuf_loader_write(loader, data, size, NULL))
+		return FALSE;
+
+	gdk_pixbuf_loader_close(loader, NULL);
+
+	p = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
+	g_object_unref(loader);
+
+	pixels = gdk_pixbuf_get_pixels(p);
+
+	render(render_opaque, x, y, w, h,
+	       gdk_pixbuf_get_pixels(p),
+	       gdk_pixbuf_get_rowstride(p));
+
+	gdk_pixbuf_unref(p);
+
+	return TRUE;
+}
+
 static const struct gvnc_ops vnc_display_ops = {
 	.auth_cred = on_auth_cred,
 	.auth_type = on_auth_type,
@@ -667,7 +696,8 @@ static const struct gvnc_ops vnc_display
 	.local_cursor = on_local_cursor,
 	.auth_unsupported = on_auth_unsupported,
 	.server_cut_text = on_server_cut_text,
-	.bell = on_bell
+	.bell = on_bell,
+	.render_jpeg = on_render_jpeg,
 };
 
 static void *vnc_coroutine(void *opaque)
@@ -678,6 +708,8 @@ static void *vnc_coroutine(void *opaque)
 				GVNC_ENCODING_RICH_CURSOR,
 				GVNC_ENCODING_XCURSOR,
 				GVNC_ENCODING_POINTER_CHANGE,
+				GVNC_ENCODING_TIGHT_JPEG5,
+				GVNC_ENCODING_TIGHT,
 				GVNC_ENCODING_ZRLE,
 				GVNC_ENCODING_HEXTILE,
 				GVNC_ENCODING_RRE,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]