summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/turbojpeg.c424
-rw-r--r--common/turbojpeg.h255
-rw-r--r--configure.ac74
-rw-r--r--libvncserver/Makefile.am4
-rw-r--r--libvncserver/rfbserver.c38
-rw-r--r--libvncserver/turbo.c1566
-rw-r--r--rfb/rfb.h8
-rw-r--r--rfb/rfbproto.h34
8 files changed, 2395 insertions, 8 deletions
diff --git a/common/turbojpeg.c b/common/turbojpeg.c
new file mode 100644
index 0000000..497ec59
--- /dev/null
+++ b/common/turbojpeg.c
@@ -0,0 +1,424 @@
+/* Copyright (C)2004 Landmark Graphics Corporation
+ * Copyright (C)2005 Sun Microsystems, Inc.
+ * Copyright (C)2009-2011 D. R. Commander
+ *
+ * This library is free software and may be redistributed and/or modified under
+ * the terms of the wxWindows Library License, Version 3.1 or (at your option)
+ * any later version. The full license is in the LICENSE.txt file included
+ * with this distribution.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * wxWindows Library License for more details.
+ */
+
+// This implements a JPEG compressor/decompressor using the libjpeg API
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+#include "./turbojpeg.h"
+
+
+#define CSTATE_START 100
+#define DSTATE_START 200
+
+
+// Error handling
+
+static char lasterror[JMSG_LENGTH_MAX]="No error";
+
+typedef struct _error_mgr
+{
+ struct jpeg_error_mgr pub;
+ jmp_buf jb;
+} error_mgr;
+
+static void my_error_exit(j_common_ptr cinfo)
+{
+ error_mgr *myerr = (error_mgr *)cinfo->err;
+ (*cinfo->err->output_message)(cinfo);
+ longjmp(myerr->jb, 1);
+}
+
+static void my_output_message(j_common_ptr cinfo)
+{
+ (*cinfo->err->format_message)(cinfo, lasterror);
+}
+
+
+// Global structures, macros, etc.
+
+typedef struct _jpgstruct
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_decompress_struct dinfo;
+ struct jpeg_destination_mgr jdms;
+ struct jpeg_source_mgr jsms;
+ error_mgr jerr;
+ int initc, initd;
+} jpgstruct;
+
+static const int hsampfactor[NUMSUBOPT]={1, 2, 2, 1};
+static const int vsampfactor[NUMSUBOPT]={1, 1, 2, 1};
+static const int pixelsize[NUMSUBOPT]={3, 3, 3, 1};
+
+#define _throw(c) {sprintf(lasterror, "%s", c); retval=-1; goto bailout;}
+#define checkhandle(h) jpgstruct *j=(jpgstruct *)h; \
+ if(!j) {sprintf(lasterror, "Invalid handle"); return -1;}
+
+
+// CO
+
+static boolean empty_output_buffer(struct jpeg_compress_struct *cinfo)
+{
+ ERREXIT(cinfo, JERR_BUFFER_SIZE);
+ return TRUE;
+}
+
+static void destination_noop(struct jpeg_compress_struct *cinfo)
+{
+}
+
+DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
+{
+ jpgstruct *j=NULL;
+ if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
+ {sprintf(lasterror, "Memory allocation failure"); return NULL;}
+ memset(j, 0, sizeof(jpgstruct));
+ j->cinfo.err=jpeg_std_error(&j->jerr.pub);
+ j->jerr.pub.error_exit=my_error_exit;
+ j->jerr.pub.output_message=my_output_message;
+
+ if(setjmp(j->jerr.jb))
+ { // this will execute if LIBJPEG has an error
+ if(j) free(j); return NULL;
+ }
+
+ jpeg_create_compress(&j->cinfo);
+ j->cinfo.dest=&j->jdms;
+ j->jdms.init_destination=destination_noop;
+ j->jdms.empty_output_buffer=empty_output_buffer;
+ j->jdms.term_destination=destination_noop;
+
+ j->initc=1;
+ return (tjhandle)j;
+}
+
+
+DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
+{
+ unsigned long retval=0;
+ if(width<1 || height<1)
+ _throw("Invalid argument in TJBUFSIZE()");
+
+ // This allows for rare corner cases in which a JPEG image can actually be
+ // larger than the uncompressed input (we wouldn't mention it if it hadn't
+ // happened before.)
+ retval=((width+15)&(~15)) * ((height+15)&(~15)) * 6 + 2048;
+
+ bailout:
+ return retval;
+}
+
+
+DLLEXPORT int DLLCALL tjCompress(tjhandle h,
+ unsigned char *srcbuf, int width, int pitch, int height, int ps,
+ unsigned char *dstbuf, unsigned long *size,
+ int jpegsub, int qual, int flags)
+{
+ int i, retval=0; JSAMPROW *row_pointer=NULL;
+
+ checkhandle(h);
+
+ if(srcbuf==NULL || width<=0 || pitch<0 || height<=0
+ || dstbuf==NULL || size==NULL
+ || jpegsub<0 || jpegsub>=NUMSUBOPT || qual<0 || qual>100)
+ _throw("Invalid argument in tjCompress()");
+ if(ps!=3 && ps!=4 && ps!=1)
+ _throw("This compressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale input");
+ if(!j->initc) _throw("Instance has not been initialized for compression");
+
+ if(pitch==0) pitch=width*ps;
+
+ j->cinfo.image_width = width;
+ j->cinfo.image_height = height;
+ j->cinfo.input_components = ps;
+
+ if(ps==1) j->cinfo.in_color_space = JCS_GRAYSCALE;
+ #if JCS_EXTENSIONS==1
+ else j->cinfo.in_color_space = JCS_EXT_RGB;
+ if(ps==3 && (flags&TJ_BGR))
+ j->cinfo.in_color_space = JCS_EXT_BGR;
+ else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST))
+ j->cinfo.in_color_space = JCS_EXT_RGBX;
+ else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST))
+ j->cinfo.in_color_space = JCS_EXT_BGRX;
+ else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST))
+ j->cinfo.in_color_space = JCS_EXT_XBGR;
+ else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST))
+ j->cinfo.in_color_space = JCS_EXT_XRGB;
+ #else
+ #error "TurboJPEG requires JPEG colorspace extensions"
+ #endif
+
+ if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
+ else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1");
+ else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
+
+ if(setjmp(j->jerr.jb))
+ { // this will execute if LIBJPEG has an error
+ retval=-1;
+ goto bailout;
+ }
+
+ jpeg_set_defaults(&j->cinfo);
+
+ jpeg_set_quality(&j->cinfo, qual, TRUE);
+ if(jpegsub==TJ_GRAYSCALE)
+ jpeg_set_colorspace(&j->cinfo, JCS_GRAYSCALE);
+ else
+ jpeg_set_colorspace(&j->cinfo, JCS_YCbCr);
+ if(qual>=96) j->cinfo.dct_method=JDCT_ISLOW;
+ else j->cinfo.dct_method=JDCT_FASTEST;
+
+ j->cinfo.comp_info[0].h_samp_factor=hsampfactor[jpegsub];
+ j->cinfo.comp_info[1].h_samp_factor=1;
+ j->cinfo.comp_info[2].h_samp_factor=1;
+ j->cinfo.comp_info[0].v_samp_factor=vsampfactor[jpegsub];
+ j->cinfo.comp_info[1].v_samp_factor=1;
+ j->cinfo.comp_info[2].v_samp_factor=1;
+
+ j->jdms.next_output_byte = dstbuf;
+ j->jdms.free_in_buffer = TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height);
+
+ jpeg_start_compress(&j->cinfo, TRUE);
+ if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
+ _throw("Memory allocation failed in tjCompress()");
+ for(i=0; i<height; i++)
+ {
+ if(flags&TJ_BOTTOMUP) row_pointer[i]= &srcbuf[(height-i-1)*pitch];
+ else row_pointer[i]= &srcbuf[i*pitch];
+ }
+ while(j->cinfo.next_scanline<j->cinfo.image_height)
+ {
+ jpeg_write_scanlines(&j->cinfo, &row_pointer[j->cinfo.next_scanline],
+ j->cinfo.image_height-j->cinfo.next_scanline);
+ }
+ jpeg_finish_compress(&j->cinfo);
+ *size=TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height)
+ -(unsigned long)(j->jdms.free_in_buffer);
+
+ bailout:
+ if(j->cinfo.global_state>CSTATE_START) jpeg_abort_compress(&j->cinfo);
+ if(row_pointer) free(row_pointer);
+ return retval;
+}
+
+
+// DEC
+
+static boolean fill_input_buffer (struct jpeg_decompress_struct *dinfo)
+{
+ ERREXIT(dinfo, JERR_BUFFER_SIZE);
+ return TRUE;
+}
+
+static void skip_input_data (struct jpeg_decompress_struct *dinfo, long num_bytes)
+{
+ dinfo->src->next_input_byte += (size_t) num_bytes;
+ dinfo->src->bytes_in_buffer -= (size_t) num_bytes;
+}
+
+static void source_noop (struct jpeg_decompress_struct *dinfo)
+{
+}
+
+DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
+{
+ jpgstruct *j;
+ if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
+ {sprintf(lasterror, "Memory allocation failure"); return NULL;}
+ memset(j, 0, sizeof(jpgstruct));
+ j->dinfo.err=jpeg_std_error(&j->jerr.pub);
+ j->jerr.pub.error_exit=my_error_exit;
+ j->jerr.pub.output_message=my_output_message;
+
+ if(setjmp(j->jerr.jb))
+ { // this will execute if LIBJPEG has an error
+ free(j); return NULL;
+ }
+
+ jpeg_create_decompress(&j->dinfo);
+ j->dinfo.src=&j->jsms;
+ j->jsms.init_source=source_noop;
+ j->jsms.fill_input_buffer = fill_input_buffer;
+ j->jsms.skip_input_data = skip_input_data;
+ j->jsms.resync_to_restart = jpeg_resync_to_restart;
+ j->jsms.term_source = source_noop;
+
+ j->initd=1;
+ return (tjhandle)j;
+}
+
+
+DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h,
+ unsigned char *srcbuf, unsigned long size,
+ int *width, int *height, int *jpegsub)
+{
+ int i, k, retval=0;
+
+ checkhandle(h);
+
+ if(srcbuf==NULL || size<=0 || width==NULL || height==NULL || jpegsub==NULL)
+ _throw("Invalid argument in tjDecompressHeader2()");
+ if(!j->initd) _throw("Instance has not been initialized for decompression");
+
+ if(setjmp(j->jerr.jb))
+ { // this will execute if LIBJPEG has an error
+ return -1;
+ }
+
+ j->jsms.bytes_in_buffer = size;
+ j->jsms.next_input_byte = srcbuf;
+
+ jpeg_read_header(&j->dinfo, TRUE);
+
+ *width=j->dinfo.image_width; *height=j->dinfo.image_height;
+ *jpegsub=-1;
+ for(i=0; i<NUMSUBOPT; i++)
+ {
+ if(j->dinfo.num_components==pixelsize[i])
+ {
+ if(j->dinfo.comp_info[0].h_samp_factor==hsampfactor[i]
+ && j->dinfo.comp_info[0].v_samp_factor==vsampfactor[i])
+ {
+ int match=0;
+ for(k=1; k<j->dinfo.num_components; k++)
+ {
+ if(j->dinfo.comp_info[k].h_samp_factor==1
+ && j->dinfo.comp_info[k].v_samp_factor==1)
+ match++;
+ }
+ if(match==j->dinfo.num_components-1)
+ {
+ *jpegsub=i; break;
+ }
+ }
+ }
+ }
+
+ jpeg_abort_decompress(&j->dinfo);
+
+ if(*jpegsub<0) _throw("Could not determine subsampling type for JPEG image");
+ if(*width<1 || *height<1) _throw("Invalid data returned in header");
+
+ bailout:
+ return retval;
+}
+
+
+DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle h,
+ unsigned char *srcbuf, unsigned long size,
+ int *width, int *height)
+{
+ int jpegsub;
+ return tjDecompressHeader2(h, srcbuf, size, width, height, &jpegsub);
+}
+
+
+DLLEXPORT int DLLCALL tjDecompress(tjhandle h,
+ unsigned char *srcbuf, unsigned long size,
+ unsigned char *dstbuf, int width, int pitch, int height, int ps,
+ int flags)
+{
+ int i, retval=0; JSAMPROW *row_pointer=NULL;
+
+ checkhandle(h);
+
+ if(srcbuf==NULL || size<=0
+ || dstbuf==NULL || width<=0 || pitch<0 || height<=0)
+ _throw("Invalid argument in tjDecompress()");
+ if(ps!=3 && ps!=4 && ps!=1)
+ _throw("This decompressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale output");
+ if(!j->initd) _throw("Instance has not been initialized for decompression");
+
+ if(pitch==0) pitch=width*ps;
+
+ if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
+ else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1");
+ else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
+
+ if(setjmp(j->jerr.jb))
+ { // this will execute if LIBJPEG has an error
+ retval=-1;
+ goto bailout;
+ }
+
+ j->jsms.bytes_in_buffer = size;
+ j->jsms.next_input_byte = srcbuf;
+
+ jpeg_read_header(&j->dinfo, TRUE);
+
+ if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
+ _throw("Memory allocation failed in tjDecompress()");
+ for(i=0; i<height; i++)
+ {
+ if(flags&TJ_BOTTOMUP) row_pointer[i]= &dstbuf[(height-i-1)*pitch];
+ else row_pointer[i]= &dstbuf[i*pitch];
+ }
+
+ if(ps==1) j->dinfo.out_color_space = JCS_GRAYSCALE;
+ #if JCS_EXTENSIONS==1
+ else j->dinfo.out_color_space = JCS_EXT_RGB;
+ if(ps==3 && (flags&TJ_BGR))
+ j->dinfo.out_color_space = JCS_EXT_BGR;
+ else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST))
+ j->dinfo.out_color_space = JCS_EXT_RGBX;
+ else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST))
+ j->dinfo.out_color_space = JCS_EXT_BGRX;
+ else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST))
+ j->dinfo.out_color_space = JCS_EXT_XBGR;
+ else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST))
+ j->dinfo.out_color_space = JCS_EXT_XRGB;
+ #else
+ #error "TurboJPEG requires JPEG colorspace extensions"
+ #endif
+
+ if(flags&TJ_FASTUPSAMPLE) j->dinfo.do_fancy_upsampling=FALSE;
+
+ jpeg_start_decompress(&j->dinfo);
+ while(j->dinfo.output_scanline<j->dinfo.output_height)
+ {
+ jpeg_read_scanlines(&j->dinfo, &row_pointer[j->dinfo.output_scanline],
+ j->dinfo.output_height-j->dinfo.output_scanline);
+ }
+ jpeg_finish_decompress(&j->dinfo);
+
+ bailout:
+ if(j->dinfo.global_state>DSTATE_START) jpeg_abort_decompress(&j->dinfo);
+ if(row_pointer) free(row_pointer);
+ return retval;
+}
+
+
+// General
+
+DLLEXPORT char* DLLCALL tjGetErrorStr(void)
+{
+ return lasterror;
+}
+
+DLLEXPORT int DLLCALL tjDestroy(tjhandle h)
+{
+ checkhandle(h);
+ if(setjmp(j->jerr.jb)) return -1;
+ if(j->initc) jpeg_destroy_compress(&j->cinfo);
+ if(j->initd) jpeg_destroy_decompress(&j->dinfo);
+ free(j);
+ return 0;
+}
diff --git a/common/turbojpeg.h b/common/turbojpeg.h
new file mode 100644
index 0000000..6e3e259
--- /dev/null
+++ b/common/turbojpeg.h
@@ -0,0 +1,255 @@
+/* Copyright (C)2004 Landmark Graphics Corporation
+ * Copyright (C)2005, 2006 Sun Microsystems, Inc.
+ * Copyright (C)2009-2011 D. R. Commander
+ *
+ * This library is free software and may be redistributed and/or modified under
+ * the terms of the wxWindows Library License, Version 3.1 or (at your option)
+ * any later version. The full license is in the LICENSE.txt file included
+ * with this distribution.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * wxWindows Library License for more details.
+ */
+
+#if (defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)) \
+ && defined(_WIN32) && defined(DLLDEFINE)
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+
+#define DLLCALL
+
+
+/* Subsampling */
+#define NUMSUBOPT 4
+
+enum {TJ_444=0, TJ_422, TJ_420, TJ_GRAYSCALE};
+#define TJ_411 TJ_420 /* for backward compatibility with VirtualGL <= 2.1.x,
+ TurboVNC <= 0.6, and TurboJPEG/IPP */
+
+
+/* Flags */
+#define TJ_BGR 1
+ /* The components of each pixel in the source/destination bitmap are stored
+ in B,G,R order, not R,G,B */
+#define TJ_BOTTOMUP 2
+ /* The source/destination bitmap is stored in bottom-up (Windows, OpenGL)
+ order, not top-down (X11) order */
+#define TJ_FORCEMMX 8
+ /* Turn off CPU auto-detection and force TurboJPEG to use MMX code
+ (IPP and 32-bit libjpeg-turbo versions only) */
+#define TJ_FORCESSE 16
+ /* Turn off CPU auto-detection and force TurboJPEG to use SSE code
+ (32-bit IPP and 32-bit libjpeg-turbo versions only) */
+#define TJ_FORCESSE2 32
+ /* Turn off CPU auto-detection and force TurboJPEG to use SSE2 code
+ (32-bit IPP and 32-bit libjpeg-turbo versions only) */
+#define TJ_ALPHAFIRST 64
+ /* If the source/destination bitmap is 32 bpp, assume that each pixel is
+ ARGB/XRGB (or ABGR/XBGR if TJ_BGR is also specified) */
+#define TJ_FORCESSE3 128
+ /* Turn off CPU auto-detection and force TurboJPEG to use SSE3 code
+ (64-bit IPP version only) */
+#define TJ_FASTUPSAMPLE 256
+ /* Use fast, inaccurate 4:2:2 and 4:2:0 YUV upsampling routines
+ (libjpeg and libjpeg-turbo versions only) */
+
+
+typedef void* tjhandle;
+
+#define TJPAD(p) (((p)+3)&(~3))
+#ifndef max
+ #define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* API follows */
+
+
+/*
+ tjhandle tjInitCompress(void)
+
+ Creates a new JPEG compressor instance, allocates memory for the structures,
+ and returns a handle to the instance. Most applications will only
+ need to call this once at the beginning of the program or once for each
+ concurrent thread. Don't try to create a new instance every time you
+ compress an image, because this may cause performance to suffer in some
+ TurboJPEG implementations.
+
+ RETURNS: NULL on error
+*/
+DLLEXPORT tjhandle DLLCALL tjInitCompress(void);
+
+
+/*
+ int tjCompress(tjhandle j,
+ unsigned char *srcbuf, int width, int pitch, int height, int pixelsize,
+ unsigned char *dstbuf, unsigned long *size,
+ int jpegsubsamp, int jpegqual, int flags)
+
+ [INPUT] j = instance handle previously returned from a call to
+ tjInitCompress()
+ [INPUT] srcbuf = pointer to user-allocated image buffer containing RGB or
+ grayscale pixels to be compressed
+ [INPUT] width = width (in pixels) of the source image
+ [INPUT] pitch = bytes per line of the source image (width*pixelsize if the
+ bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap
+ is padded to the nearest 32-bit boundary, such as is the case for Windows
+ bitmaps. You can also be clever and use this parameter to skip lines,
+ etc. Setting this parameter to 0 is the equivalent of setting it to
+ width*pixelsize.
+ [INPUT] height = height (in pixels) of the source image
+ [INPUT] pixelsize = size (in bytes) of each pixel in the source image
+ RGBX/BGRX/XRGB/XBGR: 4, RGB/BGR: 3, Grayscale: 1
+ [INPUT] dstbuf = pointer to user-allocated image buffer that will receive
+ the JPEG image. Use the TJBUFSIZE(width, height) function to determine
+ the appropriate size for this buffer based on the image width and height.
+ [OUTPUT] size = pointer to unsigned long that receives the size (in bytes)
+ of the compressed image
+ [INPUT] jpegsubsamp = Specifies either 4:2:0, 4:2:2, 4:4:4, or grayscale
+ subsampling. When the image is converted from the RGB to YCbCr colorspace
+ as part of the JPEG compression process, every other Cb and Cr
+ (chrominance) pixel can be discarded to produce a smaller image with
+ little perceptible loss of image clarity (the human eye is more sensitive
+ to small changes in brightness than small changes in color.)
+
+ TJ_420: 4:2:0 subsampling. Discards every other Cb, Cr pixel in both
+ horizontal and vertical directions
+ TJ_422: 4:2:2 subsampling. Discards every other Cb, Cr pixel only in
+ the horizontal direction
+ TJ_444: no subsampling
+ TJ_GRAYSCALE: Generate grayscale JPEG image
+
+ [INPUT] jpegqual = JPEG quality (an integer between 0 and 100 inclusive)
+ [INPUT] flags = the bitwise OR of one or more of the flags described in the
+ "Flags" section above
+
+ RETURNS: 0 on success, -1 on error
+*/
+DLLEXPORT int DLLCALL tjCompress(tjhandle j,
+ unsigned char *srcbuf, int width, int pitch, int height, int pixelsize,
+ unsigned char *dstbuf, unsigned long *size,
+ int jpegsubsamp, int jpegqual, int flags);
+
+
+/*
+ unsigned long TJBUFSIZE(int width, int height)
+
+ Convenience function that returns the maximum size of the buffer required to
+ hold a JPEG image with the given width and height
+
+ RETURNS: -1 if arguments are out of bounds
+*/
+DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
+
+
+/*
+ tjhandle tjInitDecompress(void)
+
+ Creates a new JPEG decompressor instance, allocates memory for the
+ structures, and returns a handle to the instance. Most applications will
+ only need to call this once at the beginning of the program or once for each
+ concurrent thread. Don't try to create a new instance every time you
+ decompress an image, because this may cause performance to suffer in some
+ TurboJPEG implementations.
+
+ RETURNS: NULL on error
+*/
+DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
+
+
+/*
+ int tjDecompressHeader2(tjhandle j,
+ unsigned char *srcbuf, unsigned long size,
+ int *width, int *height, int *jpegsubsamp)
+
+ [INPUT] j = instance handle previously returned from a call to
+ tjInitDecompress()
+ [INPUT] srcbuf = pointer to a user-allocated buffer containing a JPEG image
+ [INPUT] size = size of the JPEG image buffer (in bytes)
+ [OUTPUT] width = width (in pixels) of the JPEG image
+ [OUTPUT] height = height (in pixels) of the JPEG image
+ [OUTPUT] jpegsubsamp = type of chrominance subsampling used when compressing
+ the JPEG image
+
+ RETURNS: 0 on success, -1 on error
+*/
+DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle j,
+ unsigned char *srcbuf, unsigned long size,
+ int *width, int *height, int *jpegsubsamp);
+
+/*
+ Legacy version of the above function
+*/
+DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle j,
+ unsigned char *srcbuf, unsigned long size,
+ int *width, int *height);
+
+
+/*
+ int tjDecompress(tjhandle j,
+ unsigned char *srcbuf, unsigned long size,
+ unsigned char *dstbuf, int width, int pitch, int height, int pixelsize,
+ int flags)
+
+ [INPUT] j = instance handle previously returned from a call to
+ tjInitDecompress()
+ [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image
+ to decompress
+ [INPUT] size = size of the JPEG image buffer (in bytes)
+ [INPUT] dstbuf = pointer to user-allocated image buffer that will receive
+ the bitmap image. This buffer should normally be pitch*height
+ bytes in size, although this pointer may also be used to decompress into
+ a specific region of a larger buffer.
+ [INPUT] width = width (in pixels) of the destination image
+ [INPUT] pitch = bytes per line of the destination image (width*pixelsize if
+ the bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the
+ bitmap is padded to the nearest 32-bit boundary, such as is the case for
+ Windows bitmaps. You can also be clever and use this parameter to skip
+ lines, etc. Setting this parameter to 0 is the equivalent of setting it
+ to width*pixelsize.
+ [INPUT] height = height (in pixels) of the destination image
+ [INPUT] pixelsize = size (in bytes) of each pixel in the destination image
+ RGBX/BGRX/XRGB/XBGR: 4, RGB/BGR: 3, Grayscale: 1
+ [INPUT] flags = the bitwise OR of one or more of the flags described in the
+ "Flags" section above.
+
+ RETURNS: 0 on success, -1 on error
+*/
+DLLEXPORT int DLLCALL tjDecompress(tjhandle j,
+ unsigned char *srcbuf, unsigned long size,
+ unsigned char *dstbuf, int width, int pitch, int height, int pixelsize,
+ int flags);
+
+
+/*
+ int tjDestroy(tjhandle h)
+
+ Frees structures associated with a compression or decompression instance
+
+ [INPUT] h = instance handle (returned from a previous call to
+ tjInitCompress() or tjInitDecompress()
+
+ RETURNS: 0 on success, -1 on error
+*/
+DLLEXPORT int DLLCALL tjDestroy(tjhandle h);
+
+
+/*
+ char *tjGetErrorStr(void)
+
+ Returns a descriptive error message explaining why the last command failed
+*/
+DLLEXPORT char* DLLCALL tjGetErrorStr(void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/configure.ac b/configure.ac
index c5578fa..211cad6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -528,7 +528,11 @@ AC_ARG_WITH(jpeg,
# -without-jpeg with_jpeg="no"
# -with-jpeg=/foo/dir with_jpeg="/foo/dir"
+HAVE_LIBJPEG_TURBO="false"
+
if test "x$with_jpeg" != "xno"; then
+ AC_ARG_VAR(JPEG_LDFLAGS,
+ [Linker flags to use when linking with libjpeg, e.g. -L/foo/dir/lib -Wl,-static -ljpeg -Wl,-shared. This overrides the linker flags set by --with-jpeg.])
if test ! -z "$with_jpeg" -a "x$with_jpeg" != "xyes"; then
# add user supplied directory to flags:
saved_CPPFLAGS="$CPPFLAGS"
@@ -544,9 +548,19 @@ if test "x$with_jpeg" != "xno"; then
LDFLAGS="$LDFLAGS -R$with_jpeg/lib"
fi
fi
+ if test "x$JPEG_LDFLAGS" != "x"; then
+ LDFLAGS="$saved_LDFLAGS"
+ LIBS="$LIBS $JPEG_LDFLAGS"
+ else
+ LIBS="-ljpeg"
+ fi
AC_CHECK_HEADER(jpeglib.h, HAVE_JPEGLIB_H="true")
+ AC_MSG_CHECKING(for jpeg_CreateCompress in libjpeg)
if test "x$HAVE_JPEGLIB_H" = "xtrue"; then
- AC_CHECK_LIB(jpeg, jpeg_CreateCompress, , HAVE_JPEGLIB_H="")
+ AC_LINK_IFELSE([AC_LANG_CALL([], [jpeg_CreateCompress])],
+ [AC_MSG_RESULT(yes);
+ AC_DEFINE(HAVE_LIBJPEG, 1, libjpeg support enabled)],
+ [AC_MSG_RESULT(no); HAVE_JPEGLIB_H=""])
fi
if test ! -z "$with_jpeg" -a "x$with_jpeg" != "xyes"; then
if test "x$HAVE_JPEGLIB_H" != "xtrue"; then
@@ -563,15 +577,67 @@ if test "x$with_jpeg" != "xno"; then
This may lead to reduced performance, especially over slow links.
If libjpeg is in a non-standard location use --with-jpeg=DIR to
indicate the header file is in DIR/include/jpeglib.h and the library
-in DIR/lib/libjpeg.a. A copy of libjpeg may be obtained from:
-ftp://ftp.uu.net/graphics/jpeg/
+in DIR/lib/libjpeg.a. You can also set the JPEG_LDFLAGS variable to
+specify more detailed linker flags. A copy of libjpeg-turbo may be
+obtained from: https://sourceforge.net/projects/libjpeg-turbo/files/
+A copy of libjpeg may be obtained from: http://ijg.org/files/
==========================================================================
])
sleep 5
fi
fi
+
+ if test "x$HAVE_JPEGLIB_H" = "xtrue"; then
+ AC_MSG_CHECKING(whether JPEG library is libjpeg-turbo)
+ m4_define([LJT_TEST],
+ [AC_LANG_PROGRAM([#include <stdio.h>
+ #include <jpeglib.h>],
+ [struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ cinfo.err=jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+ cinfo.input_components = 3;
+ jpeg_set_defaults(&cinfo);
+ cinfo.in_color_space = JCS_EXT_RGB;
+ jpeg_default_colorspace(&cinfo);
+ return 0;])]
+ )
+ if test "x$cross_compiling" != "xyes"; then
+ AC_RUN_IFELSE([LJT_TEST],
+ [HAVE_LIBJPEG_TURBO="true"; AC_MSG_RESULT(yes)],
+ [AC_MSG_RESULT(no)])
+ else
+ AC_LINK_IFELSE([LJT_TEST],
+ [HAVE_LIBJPEG_TURBO="true"; AC_MSG_RESULT(yes)],
+ [AC_MSG_RESULT(no)])
+ fi
+ fi
fi
+AC_ARG_WITH(turbovnc,
+[ --with-turbovnc use TurboVNC encoder instead of TightVNC encoder],,)
+
+AC_MSG_CHECKING(whether to enable TurboVNC encoder)
+if test "x$with_turbovnc" = "xyes"; then
+ if test "x$HAVE_LIBJPEG_TURBO" != "xtrue"; then
+ AC_MSG_ERROR([
+==========================================================================
+*** The TurboVNC encoder requires libjpeg-turbo, which was not detected.
+You can obtain libjpeg-turbo from:
+https://sourceforge.net/projects/libjpeg-turbo/files/
+Optionally, you can pass --without-turbovnc to configure to use the
+TightVNC encoder instead. ***
+==========================================================================
+])
+ fi
+ AC_DEFINE(HAVE_TURBOVNC, 1, TurboVNC support enabled)
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+AM_CONDITIONAL(HAVE_TURBOVNC, test "x$with_turbovnc" = "xyes" )
+
AC_ARG_WITH(png,
[ --without-png disable support for png]
[ --with-png=DIR use png include/library files in DIR],,)
@@ -582,7 +648,7 @@ AC_ARG_WITH(png,
# -without-png with_png="no"
# -with-png=/foo/dir with_png="/foo/dir"
-if test "x$with_png" != "xno"; then
+if test "x$with_png" != "xno" -a "x$with_turbovnc" != "xyes"; then
if test ! -z "$with_png" -a "x$with_png" != "xyes"; then
# add user supplied directory to flags:
saved_CPPFLAGS="$CPPFLAGS"
diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am
index fce398d..6a29b84 100644
--- a/libvncserver/Makefile.am
+++ b/libvncserver/Makefile.am
@@ -46,9 +46,13 @@ EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \
if HAVE_LIBZ
ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c ../common/zywrletemplate.c
if HAVE_LIBJPEG
+if HAVE_TURBOVNC
+TIGHTSRCS = turbo.c ../common/turbojpeg.c
+else
TIGHTSRCS = tight.c
endif
endif
+endif
LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c $(WEBSOCKETSSRCS) \
stats.c corre.c hextile.c rre.c translate.c cutpaste.c \
diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c
index 9be255f..04231f2 100644
--- a/libvncserver/rfbserver.c
+++ b/libvncserver/rfbserver.c
@@ -3,6 +3,7 @@
*/
/*
+ * Copyright (C) 2011 D. R. Commander
* Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
* Copyright (C) 2002 RealVNC Ltd.
* OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
@@ -85,6 +86,21 @@ static int compat_mkdir(const char *path, int mode)
#define mkdir compat_mkdir
#endif
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+/*
+ * Map of quality levels to provide compatibility with TightVNC/TigerVNC
+ * clients
+ */
+
+static const int tight2turbo_qual[10] = {
+ 15, 29, 41, 42, 62, 77, 79, 86, 92, 100
+};
+
+static const int tight2turbo_subsamp[10] = {
+ 1, 1, 1, 2, 2, 2, 0, 0, 0, 0
+};
+#endif
+
static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
static void rfbProcessClientNormalMessage(rfbClientPtr cl);
static void rfbProcessClientInitMessage(rfbClientPtr cl);
@@ -367,6 +383,9 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG)
cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
#endif
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+ cl->tightSubsampLevel = TIGHT_DEFAULT_SUBSAMP;
+#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
{
int i;
@@ -2077,11 +2096,30 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
rfbLog("Using compression level %d for client %s\n",
cl->tightCompressLevel, cl->host);
#endif
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+ } else if ( enc >= (uint32_t)rfbEncodingSubsamp1X &&
+ enc <= (uint32_t)rfbEncodingSubsampGray ) {
+ cl->tightSubsampLevel = enc & 0xFF;
+ rfbLog("Using JPEG subsampling %d for client %s\n",
+ cl->tightSubsampLevel, cl->host);
+ } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 &&
+ enc <= (uint32_t)rfbEncodingQualityLevel9 ) {
+ cl->tightQualityLevel = tight2turbo_qual[enc & 0x0F];
+ cl->tightSubsampLevel = tight2turbo_subsamp[enc & 0x0F];
+ rfbLog("Using JPEG subsampling %d, Q%d for client %s\n",
+ cl->tightSubsampLevel, cl->tightQualityLevel, cl->host);
+ } else if ( enc >= (uint32_t)rfbEncodingFineQualityLevel0 + 1 &&
+ enc <= (uint32_t)rfbEncodingFineQualityLevel100 ) {
+ cl->tightQualityLevel = enc & 0xFF;
+ rfbLog("Using image quality level %d for client %s\n",
+ cl->tightQualityLevel, cl->host);
+#else
} else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 &&
enc <= (uint32_t)rfbEncodingQualityLevel9 ) {
cl->tightQualityLevel = enc & 0x0F;
rfbLog("Using image quality level %d for client %s\n",
cl->tightQualityLevel, cl->host);
+#endif
} else
#endif
{
diff --git a/libvncserver/turbo.c b/libvncserver/turbo.c
new file mode 100644
index 0000000..f205b6d
--- /dev/null
+++ b/libvncserver/turbo.c
@@ -0,0 +1,1566 @@
+/*
+ * turbo.c
+ *
+ * Routines to implement TurboVNC Encoding
+ */
+
+/*
+ * Copyright (C) 2010-2012 D. R. Commander. All Rights Reserved.
+ * Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <rfb/rfb.h>
+#include "private.h"
+
+#include "turbojpeg.h"
+
+
+/* Note: The following constant should not be changed. */
+#define TIGHT_MIN_TO_COMPRESS 12
+
+/* The parameters below may be adjusted. */
+#define MIN_SPLIT_RECT_SIZE 4096
+#define MIN_SOLID_SUBRECT_SIZE 2048
+#define MAX_SPLIT_TILE_SIZE 16
+
+/*
+ * There is so much access of the Tight encoding static data buffers
+ * that we resort to using thread local storage instead of having
+ * per-client data.
+ */
+#if LIBVNCSERVER_HAVE_LIBPTHREAD && LIBVNCSERVER_HAVE_TLS && !defined(TLS) && defined(__linux__)
+#define TLS __thread
+#endif
+#ifndef TLS
+#define TLS
+#endif
+
+/* This variable is set on every rfbSendRectEncodingTight() call. */
+static TLS rfbBool usePixelFormat24 = FALSE;
+
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+typedef struct TIGHT_CONF_s {
+ int maxRectSize, maxRectWidth;
+ int monoMinRectSize;
+ int idxZlibLevel, monoZlibLevel, rawZlibLevel;
+ int idxMaxColorsDivisor;
+ int palMaxColorsWithJPEG;
+} TIGHT_CONF;
+
+static TIGHT_CONF tightConf[3] = {
+ { 65536, 2048, 6, 0, 0, 0, 4, 24 },
+ { 65536, 2048, 32, 1, 1, 1, 96, 24 },
+ { 65536, 2048, 32, 3, 3, 2, 96, 96 }
+};
+
+static TLS int compressLevel = 1;
+static TLS int qualityLevel = 95;
+static TLS int subsampLevel = TJ_444;
+
+static const int subsampLevel2tjsubsamp[4] = {
+ TJ_444, TJ_420, TJ_422, TJ_GRAYSCALE
+};
+
+
+/* Stuff dealing with palettes. */
+
+typedef struct COLOR_LIST_s {
+ struct COLOR_LIST_s *next;
+ int idx;
+ uint32_t rgb;
+} COLOR_LIST;
+
+typedef struct PALETTE_ENTRY_s {
+ COLOR_LIST *listNode;
+ int numPixels;
+} PALETTE_ENTRY;
+
+typedef struct PALETTE_s {
+ PALETTE_ENTRY entry[256];
+ COLOR_LIST *hash[256];
+ COLOR_LIST list[256];
+} PALETTE;
+
+/* TODO: move into rfbScreen struct */
+static TLS int paletteNumColors = 0;
+static TLS int paletteMaxColors = 0;
+static TLS uint32_t monoBackground = 0;
+static TLS uint32_t monoForeground = 0;
+static TLS PALETTE palette;
+
+/* Pointers to dynamically-allocated buffers. */
+
+static TLS int tightBeforeBufSize = 0;
+static TLS char *tightBeforeBuf = NULL;
+
+static TLS int tightAfterBufSize = 0;
+static TLS char *tightAfterBuf = NULL;
+
+static TLS tjhandle j = NULL;
+
+void rfbTightCleanup (rfbScreenInfoPtr screen)
+{
+ if (tightBeforeBufSize) {
+ free (tightBeforeBuf);
+ tightBeforeBufSize = 0;
+ tightBeforeBuf = NULL;
+ }
+ if (tightAfterBufSize) {
+ free (tightAfterBuf);
+ tightAfterBufSize = 0;
+ tightAfterBuf = NULL;
+ }
+ if (j) tjDestroy(j);
+}
+
+
+/* Prototypes for static functions. */
+
+static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t colorValue, int *w_ptr, int *h_ptr);
+static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t colorValue,
+ int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
+static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t *colorPtr, rfbBool needSameColor);
+static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t *colorPtr, rfbBool needSameColor);
+static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t *colorPtr, rfbBool needSameColor);
+static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h,
+ uint32_t *colorPtr, rfbBool needSameColor);
+
+static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
+static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
+static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
+
+static rfbBool SendSolidRect (rfbClientPtr cl);
+static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h);
+static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h);
+static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h);
+
+static rfbBool CompressData (rfbClientPtr cl, int streamId, int dataLen,
+ int zlibLevel, int zlibStrategy);
+static rfbBool SendCompressedData (rfbClientPtr cl, char *buf,
+ int compressedLen);
+
+static void FillPalette8 (int count);
+static void FillPalette16 (int count);
+static void FillPalette32 (int count);
+static void FastFillPalette16 (rfbClientPtr cl, uint16_t *data, int w,
+ int pitch, int h);
+static void FastFillPalette32 (rfbClientPtr cl, uint32_t *data, int w,
+ int pitch, int h);
+
+static void PaletteReset (void);
+static int PaletteInsert (uint32_t rgb, int numPixels, int bpp);
+
+static void Pack24 (rfbClientPtr cl, char *buf, rfbPixelFormat *fmt,
+ int count);
+
+static void EncodeIndexedRect16 (uint8_t *buf, int count);
+static void EncodeIndexedRect32 (uint8_t *buf, int count);
+
+static void EncodeMonoRect8 (uint8_t *buf, int w, int h);
+static void EncodeMonoRect16 (uint8_t *buf, int w, int h);
+static void EncodeMonoRect32 (uint8_t *buf, int w, int h);
+
+static rfbBool SendJpegRect (rfbClientPtr cl, int x, int y, int w, int h,
+ int quality);
+
+/*
+ * Tight encoding implementation.
+ */
+
+int
+rfbNumCodedRectsTight(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ int maxRectSize, maxRectWidth;
+ int subrectMaxWidth, subrectMaxHeight;
+
+ /* No matter how many rectangles we will send if LastRect markers
+ are used to terminate rectangle stream. */
+ if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
+ return 0;
+
+ maxRectSize = tightConf[compressLevel].maxRectSize;
+ maxRectWidth = tightConf[compressLevel].maxRectWidth;
+
+ if (w > maxRectWidth || w * h > maxRectSize) {
+ subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ subrectMaxHeight = maxRectSize / subrectMaxWidth;
+ return (((w - 1) / maxRectWidth + 1) *
+ ((h - 1) / subrectMaxHeight + 1));
+ } else {
+ return 1;
+ }
+}
+
+rfbBool
+rfbSendRectEncodingTight(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ int nMaxRows;
+ uint32_t colorValue;
+ int dx, dy, dw, dh;
+ int x_best, y_best, w_best, h_best;
+ char *fbptr;
+
+ rfbSendUpdateBuf(cl);
+
+ compressLevel = cl->tightCompressLevel;
+ qualityLevel = cl->tightQualityLevel;
+ subsampLevel = cl->tightSubsampLevel;
+
+ /* We only allow compression levels that have a demonstrable performance
+ benefit. CL 0 with JPEG reduces CPU usage for workloads that have low
+ numbers of unique colors, but the same thing can be accomplished by
+ using CL 0 without JPEG (AKA "Lossless Tight.") CL 2 is a mixed bag.
+ It can be shown to reduce bandwidth (and commensurately increase CPU
+ usage) by typically 30-40% relative to CL 1, but only when it is used in
+ conjunction with high-quality JPEG, and only on workloads that have low
+ numbers of unique colors. Increasing the amount of Zlib compression
+ beyond CL 2 cannot be shown to provide any significant bandwidth savings
+ except in very rare corner cases that are not performance-critical to
+ begin with, and higher Zlib levels increase CPU usage exponentially. */
+ if (qualityLevel != -1) {
+ if (compressLevel < 1) compressLevel = 1;
+ if (compressLevel > 2) compressLevel = 2;
+ }
+
+ /* With JPEG disabled, increasing the Zlib compression level beyond CL 1
+ offers no significant bandwidth savings, and the CPU usage starts to
+ increase exponentially. */
+ else if (compressLevel > 1) compressLevel = 1;
+
+ if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
+ cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
+ usePixelFormat24 = TRUE;
+ } else {
+ usePixelFormat24 = FALSE;
+ }
+
+ if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
+ return SendRectSimple(cl, x, y, w, h);
+
+ /* Make sure we can write at least one pixel into tightBeforeBuf. */
+
+ if (tightBeforeBufSize < 4) {
+ tightBeforeBufSize = 4;
+ if (tightBeforeBuf == NULL)
+ tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
+ else
+ tightBeforeBuf = (char *)realloc(tightBeforeBuf,
+ tightBeforeBufSize);
+ }
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ {
+ int maxRectSize, maxRectWidth, nMaxWidth;
+
+ maxRectSize = tightConf[compressLevel].maxRectSize;
+ maxRectWidth = tightConf[compressLevel].maxRectWidth;
+ nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ nMaxRows = maxRectSize / nMaxWidth;
+ }
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= nMaxRows) {
+ if (!SendRectSimple(cl, x, y, w, nMaxRows))
+ return 0;
+ y += nMaxRows;
+ h -= nMaxRows;
+ }
+
+ dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
+ MAX_SPLIT_TILE_SIZE : (y + h - dy);
+
+ for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
+
+ dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
+ MAX_SPLIT_TILE_SIZE : (x + w - dx);
+
+ if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) {
+
+ if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) {
+ uint32_t r = (colorValue >> 16) & 0xFF;
+ uint32_t g = (colorValue >> 8) & 0xFF;
+ uint32_t b = (colorValue) & 0xFF;
+ double y = (0.257 * (double)r) + (0.504 * (double)g)
+ + (0.098 * (double)b) + 16.;
+ colorValue = (int)y + (((int)y) << 8) + (((int)y) << 16);
+ }
+
+ /* Get dimensions of solid-color area. */
+
+ FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y),
+ colorValue, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if ( w_best * h_best != w * h &&
+ w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
+ continue;
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ ExtendSolidArea(cl, x, y, w, h, colorValue,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if ( y_best != y &&
+ !SendRectSimple(cl, x, y, w, y_best-y) )
+ return FALSE;
+ if ( x_best != x &&
+ !rfbSendRectEncodingTight(cl, x, y_best,
+ x_best-x, h_best) )
+ return FALSE;
+
+ /* Send solid-color rectangle. */
+
+ if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
+ return FALSE;
+
+ fbptr = (cl->scaledScreen->frameBuffer +
+ (cl->scaledScreen->paddedWidthInBytes * y_best) +
+ (x_best * (cl->scaledScreen->bitsPerPixel / 8)));
+
+ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
+ &cl->format, fbptr, tightBeforeBuf,
+ cl->scaledScreen->paddedWidthInBytes, 1, 1);
+
+ if (!SendSolidRect(cl))
+ return FALSE;
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if ( x_best + w_best != x + w &&
+ !rfbSendRectEncodingTight(cl, x_best + w_best, y_best,
+ w - (x_best-x) - w_best, h_best) )
+ return FALSE;
+ if ( y_best + h_best != y + h &&
+ !rfbSendRectEncodingTight(cl, x, y_best + h_best,
+ w, h - (y_best-y) - h_best) )
+ return FALSE;
+
+ /* Return after all recursive calls are done. */
+
+ return TRUE;
+ }
+
+ }
+
+ }
+
+ /* No suitable solid-color rectangles found. */
+
+ return SendRectSimple(cl, x, y, w, h);
+}
+
+
+static void
+FindBestSolidArea(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h,
+ uint32_t colorValue,
+ int *w_ptr,
+ int *h_ptr)
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
+
+ dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
+ MAX_SPLIT_TILE_SIZE : (y + h - dy);
+ dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
+ MAX_SPLIT_TILE_SIZE : w_prev;
+
+ if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE))
+ break;
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
+ MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
+ if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE))
+ break;
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+
+static void
+ExtendSolidArea(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h,
+ uint32_t colorValue,
+ int *x_ptr,
+ int *y_ptr,
+ int *w_ptr,
+ int *h_ptr)
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successfull, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor)
+{
+ switch(cl->screen->serverFormat.bitsPerPixel) {
+ case 32:
+ return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor);
+ case 16:
+ return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor);
+ default:
+ return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor);
+ }
+}
+
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+static rfbBool \
+CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \
+ uint32_t* colorPtr, rfbBool needSameColor) \
+{ \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t colorValue; \
+ int dx, dy; \
+ \
+ fbptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer \
+ [y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \
+ \
+ colorValue = *fbptr; \
+ if (needSameColor && (uint32_t)colorValue != *colorPtr) \
+ return FALSE; \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (colorValue != fbptr[dx]) \
+ return FALSE; \
+ } \
+ fbptr = (uint##bpp##_t *)((uint8_t *)fbptr \
+ + cl->scaledScreen->paddedWidthInBytes); \
+ } \
+ \
+ *colorPtr = (uint32_t)colorValue; \
+ return TRUE; \
+}
+
+DEFINE_CHECK_SOLID_FUNCTION(8)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(32)
+
+static rfbBool
+SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h)
+{
+ int maxBeforeSize, maxAfterSize;
+ int maxRectSize, maxRectWidth;
+ int subrectMaxWidth, subrectMaxHeight;
+ int dx, dy;
+ int rw, rh;
+
+ maxRectSize = tightConf[compressLevel].maxRectSize;
+ maxRectWidth = tightConf[compressLevel].maxRectWidth;
+
+ maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
+ maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
+
+ if (tightBeforeBufSize < maxBeforeSize) {
+ tightBeforeBufSize = maxBeforeSize;
+ if (tightBeforeBuf == NULL)
+ tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
+ else
+ tightBeforeBuf = (char *)realloc(tightBeforeBuf,
+ tightBeforeBufSize);
+ }
+
+ if (tightAfterBufSize < maxAfterSize) {
+ tightAfterBufSize = maxAfterSize;
+ if (tightAfterBuf == NULL)
+ tightAfterBuf = (char *)malloc(tightAfterBufSize);
+ else
+ tightAfterBuf = (char *)realloc(tightAfterBuf,
+ tightAfterBufSize);
+ }
+
+ if (w > maxRectWidth || w * h > maxRectSize) {
+ subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ subrectMaxHeight = maxRectSize / subrectMaxWidth;
+
+ for (dy = 0; dy < h; dy += subrectMaxHeight) {
+ for (dx = 0; dx < w; dx += maxRectWidth) {
+ rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
+ rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
+ if (!SendSubrect(cl, x + dx, y + dy, rw, rh))
+ return FALSE;
+ }
+ }
+ } else {
+ if (!SendSubrect(cl, x, y, w, h))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static rfbBool
+SendSubrect(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ char *fbptr;
+ rfbBool success = FALSE;
+
+ /* Send pending data if there is more than 128 bytes. */
+ if (cl->ublen > 128) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ if (!SendTightHeader(cl, x, y, w, h))
+ return FALSE;
+
+ fbptr = (cl->scaledScreen->frameBuffer
+ + (cl->scaledScreen->paddedWidthInBytes * y)
+ + (x * (cl->scaledScreen->bitsPerPixel / 8)));
+
+ if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1)
+ return SendJpegRect(cl, x, y, w, h, qualityLevel);
+
+ paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
+ if(qualityLevel != -1)
+ paletteMaxColors = tightConf[compressLevel].palMaxColorsWithJPEG;
+ if ( paletteMaxColors < 2 &&
+ w * h >= tightConf[compressLevel].monoMinRectSize ) {
+ paletteMaxColors = 2;
+ }
+
+ if (cl->format.bitsPerPixel == cl->screen->serverFormat.bitsPerPixel &&
+ cl->format.redMax == cl->screen->serverFormat.redMax &&
+ cl->format.greenMax == cl->screen->serverFormat.greenMax &&
+ cl->format.blueMax == cl->screen->serverFormat.blueMax &&
+ cl->format.bitsPerPixel >= 16) {
+
+ /* This is so we can avoid translating the pixels when compressing
+ with JPEG, since it is unnecessary */
+ switch (cl->format.bitsPerPixel) {
+ case 16:
+ FastFillPalette16(cl, (uint16_t *)fbptr, w,
+ cl->scaledScreen->paddedWidthInBytes / 2, h);
+ break;
+ default:
+ FastFillPalette32(cl, (uint32_t *)fbptr, w,
+ cl->scaledScreen->paddedWidthInBytes / 4, h);
+ }
+
+ if(paletteNumColors != 0 || qualityLevel == -1) {
+ (*cl->translateFn)(cl->translateLookupTable,
+ &cl->screen->serverFormat, &cl->format, fbptr,
+ tightBeforeBuf,
+ cl->scaledScreen->paddedWidthInBytes, w, h);
+ }
+ }
+ else {
+ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
+ &cl->format, fbptr, tightBeforeBuf,
+ cl->scaledScreen->paddedWidthInBytes, w, h);
+
+ switch (cl->format.bitsPerPixel) {
+ case 8:
+ FillPalette8(w * h);
+ break;
+ case 16:
+ FillPalette16(w * h);
+ break;
+ default:
+ FillPalette32(w * h);
+ }
+ }
+
+ switch (paletteNumColors) {
+ case 0:
+ /* Truecolor image */
+ if (qualityLevel != -1) {
+ success = SendJpegRect(cl, x, y, w, h, qualityLevel);
+ } else {
+ success = SendFullColorRect(cl, w, h);
+ }
+ break;
+ case 1:
+ /* Solid rectangle */
+ success = SendSolidRect(cl);
+ break;
+ case 2:
+ /* Two-color rectangle */
+ success = SendMonoRect(cl, w, h);
+ break;
+ default:
+ /* Up to 256 different colors */
+ success = SendIndexedRect(cl, w, h);
+ }
+ return success;
+}
+
+static rfbBool
+SendTightHeader(rfbClientPtr cl,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ rfbFramebufferUpdateRectHeader rect;
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingTight);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ rfbStatRecordEncodingSent(cl, rfbEncodingTight,
+ sz_rfbFramebufferUpdateRectHeader,
+ sz_rfbFramebufferUpdateRectHeader
+ + w * (cl->format.bitsPerPixel / 8) * h);
+
+ return TRUE;
+}
+
+/*
+ * Subencoding implementations.
+ */
+
+static rfbBool
+SendSolidRect(rfbClientPtr cl)
+{
+ int len;
+
+ if (usePixelFormat24) {
+ Pack24(cl, tightBeforeBuf, &cl->format, 1);
+ len = 3;
+ } else
+ len = cl->format.bitsPerPixel / 8;
+
+ if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4);
+ memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len);
+ cl->ublen += len;
+
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len + 1);
+
+ return TRUE;
+}
+
+static rfbBool
+SendMonoRect(rfbClientPtr cl,
+ int w,
+ int h)
+{
+ int streamId = 1;
+ int paletteLen, dataLen;
+
+ if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
+ 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ /* Prepare tight encoding header. */
+ dataLen = (w + 7) / 8;
+ dataLen *= h;
+
+ if (tightConf[compressLevel].monoZlibLevel == 0)
+ cl->updateBuf[cl->ublen++] =
+ (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
+ else
+ cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+ cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
+ cl->updateBuf[cl->ublen++] = 1;
+
+ /* Prepare palette, convert image. */
+ switch (cl->format.bitsPerPixel) {
+
+ case 32:
+ EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h);
+
+ ((uint32_t *)tightAfterBuf)[0] = monoBackground;
+ ((uint32_t *)tightAfterBuf)[1] = monoForeground;
+ if (usePixelFormat24) {
+ Pack24(cl, tightAfterBuf, &cl->format, 2);
+ paletteLen = 6;
+ } else
+ paletteLen = 8;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen);
+ cl->ublen += paletteLen;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen);
+ break;
+
+ case 16:
+ EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h);
+
+ ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground;
+ ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4);
+ cl->ublen += 4;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7);
+ break;
+
+ default:
+ EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h);
+
+ cl->updateBuf[cl->ublen++] = (char)monoBackground;
+ cl->updateBuf[cl->ublen++] = (char)monoForeground;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5);
+ }
+
+ return CompressData(cl, streamId, dataLen,
+ tightConf[compressLevel].monoZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static rfbBool
+SendIndexedRect(rfbClientPtr cl,
+ int w,
+ int h)
+{
+ int streamId = 2;
+ int i, entryLen;
+
+ if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
+ paletteNumColors * cl->format.bitsPerPixel / 8 >
+ UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ /* Prepare tight encoding header. */
+ if (tightConf[compressLevel].idxZlibLevel == 0)
+ cl->updateBuf[cl->ublen++] =
+ (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
+ else
+ cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+ cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
+ cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1);
+
+ /* Prepare palette, convert image. */
+ switch (cl->format.bitsPerPixel) {
+
+ case 32:
+ EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h);
+
+ for (i = 0; i < paletteNumColors; i++) {
+ ((uint32_t *)tightAfterBuf)[i] =
+ palette.entry[i].listNode->rgb;
+ }
+ if (usePixelFormat24) {
+ Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors);
+ entryLen = 3;
+ } else
+ entryLen = 4;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf,
+ paletteNumColors * entryLen);
+ cl->ublen += paletteNumColors * entryLen;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight,
+ 3 + paletteNumColors * entryLen);
+ break;
+
+ case 16:
+ EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h);
+
+ for (i = 0; i < paletteNumColors; i++) {
+ ((uint16_t *)tightAfterBuf)[i] =
+ (uint16_t)palette.entry[i].listNode->rgb;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2);
+ cl->ublen += paletteNumColors * 2;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight,
+ 3 + paletteNumColors * 2);
+ break;
+
+ default:
+ return FALSE; /* Should never happen. */
+ }
+
+ return CompressData(cl, streamId, w * h,
+ tightConf[compressLevel].idxZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static rfbBool
+SendFullColorRect(rfbClientPtr cl,
+ int w,
+ int h)
+{
+ int streamId = 0;
+ int len;
+
+ if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ if (tightConf[compressLevel].rawZlibLevel == 0)
+ cl->updateBuf[cl->ublen++] = (char)(rfbTightNoZlib << 4);
+ else
+ cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
+
+ if (usePixelFormat24) {
+ Pack24(cl, tightBeforeBuf, &cl->format, w * h);
+ len = 3;
+ } else
+ len = cl->format.bitsPerPixel / 8;
+
+ return CompressData(cl, streamId, w * h * len,
+ tightConf[compressLevel].rawZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static rfbBool
+CompressData(rfbClientPtr cl,
+ int streamId,
+ int dataLen,
+ int zlibLevel,
+ int zlibStrategy)
+{
+ z_streamp pz;
+ int err;
+
+ if (dataLen < TIGHT_MIN_TO_COMPRESS) {
+ memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen);
+ cl->ublen += dataLen;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen);
+ return TRUE;
+ }
+
+ if (zlibLevel == 0)
+ return SendCompressedData (cl, tightBeforeBuf, dataLen);
+
+ pz = &cl->zsStruct[streamId];
+
+ /* Initialize compression stream if needed. */
+ if (!cl->zsActive[streamId]) {
+ pz->zalloc = Z_NULL;
+ pz->zfree = Z_NULL;
+ pz->opaque = Z_NULL;
+
+ err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, zlibStrategy);
+ if (err != Z_OK)
+ return FALSE;
+
+ cl->zsActive[streamId] = TRUE;
+ cl->zsLevel[streamId] = zlibLevel;
+ }
+
+ /* Prepare buffer pointers. */
+ pz->next_in = (Bytef *)tightBeforeBuf;
+ pz->avail_in = dataLen;
+ pz->next_out = (Bytef *)tightAfterBuf;
+ pz->avail_out = tightAfterBufSize;
+
+ /* Change compression parameters if needed. */
+ if (zlibLevel != cl->zsLevel[streamId]) {
+ if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
+ return FALSE;
+ }
+ cl->zsLevel[streamId] = zlibLevel;
+ }
+
+ /* Actual compression. */
+ if (deflate(pz, Z_SYNC_FLUSH) != Z_OK ||
+ pz->avail_in != 0 || pz->avail_out == 0) {
+ return FALSE;
+ }
+
+ return SendCompressedData(cl, tightAfterBuf,
+ tightAfterBufSize - pz->avail_out);
+}
+
+static rfbBool SendCompressedData(rfbClientPtr cl, char *buf,
+ int compressedLen)
+{
+ int i, portionLen;
+
+ cl->updateBuf[cl->ublen++] = compressedLen & 0x7F;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
+ if (compressedLen > 0x7F) {
+ cl->updateBuf[cl->ublen-1] |= 0x80;
+ cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
+ if (compressedLen > 0x3FFF) {
+ cl->updateBuf[cl->ublen-1] |= 0x80;
+ cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF;
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
+ }
+ }
+
+ portionLen = UPDATE_BUF_SIZE;
+ for (i = 0; i < compressedLen; i += portionLen) {
+ if (i + portionLen > compressedLen) {
+ portionLen = compressedLen - i;
+ }
+ if (cl->ublen + portionLen > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ memcpy(&cl->updateBuf[cl->ublen], &buf[i], portionLen);
+ cl->ublen += portionLen;
+ }
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen);
+
+ return TRUE;
+}
+
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+
+static void
+FillPalette8(int count)
+{
+ uint8_t *data = (uint8_t *)tightBeforeBuf;
+ uint8_t c0, c1;
+ int i, n0, n1;
+
+ paletteNumColors = 0;
+
+ c0 = data[0];
+ for (i = 1; i < count && data[i] == c0; i++);
+ if (i == count) {
+ paletteNumColors = 1;
+ return; /* Solid rectangle */
+ }
+
+ if (paletteMaxColors < 2)
+ return;
+
+ n0 = i;
+ c1 = data[i];
+ n1 = 0;
+ for (i++; i < count; i++) {
+ if (data[i] == c0) {
+ n0++;
+ } else if (data[i] == c1) {
+ n1++;
+ } else
+ break;
+ }
+ if (i == count) {
+ if (n0 > n1) {
+ monoBackground = (uint32_t)c0;
+ monoForeground = (uint32_t)c1;
+ } else {
+ monoBackground = (uint32_t)c1;
+ monoForeground = (uint32_t)c0;
+ }
+ paletteNumColors = 2; /* Two colors */
+ }
+}
+
+
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+static void \
+FillPalette##bpp(int count) { \
+ uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \
+ uint##bpp##_t c0, c1, ci; \
+ int i, n0, n1, ni; \
+ \
+ c0 = data[0]; \
+ for (i = 1; i < count && data[i] == c0; i++); \
+ if (i >= count) { \
+ paletteNumColors = 1; /* Solid rectangle */ \
+ return; \
+ } \
+ \
+ if (paletteMaxColors < 2) { \
+ paletteNumColors = 0; /* Full-color encoding preferred */ \
+ return; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ monoBackground = (uint32_t)c0; \
+ monoForeground = (uint32_t)c1; \
+ } else { \
+ monoBackground = (uint32_t)c1; \
+ monoForeground = (uint32_t)c0; \
+ } \
+ paletteNumColors = 2; /* Two colors */ \
+ return; \
+ } \
+ \
+ PaletteReset(); \
+ PaletteInsert (c0, (uint32_t)n0, bpp); \
+ PaletteInsert (c1, (uint32_t)n1, bpp); \
+ \
+ ni = 1; \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ ni++; \
+ } else { \
+ if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \
+ return; \
+ ci = data[i]; \
+ ni = 1; \
+ } \
+ } \
+ PaletteInsert (ci, (uint32_t)ni, bpp); \
+}
+
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \
+ \
+static void \
+FastFillPalette##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, \
+ int pitch, int h) \
+{ \
+ uint##bpp##_t c0, c1, ci, mask, c0t, c1t, cit; \
+ int i, j, i2 = 0, j2, n0, n1, ni; \
+ \
+ if (cl->translateFn != rfbTranslateNone) { \
+ mask = cl->screen->serverFormat.redMax \
+ << cl->screen->serverFormat.redShift; \
+ mask |= cl->screen->serverFormat.greenMax \
+ << cl->screen->serverFormat.greenShift; \
+ mask |= cl->screen->serverFormat.blueMax \
+ << cl->screen->serverFormat.blueShift; \
+ } else mask = ~0; \
+ \
+ c0 = data[0] & mask; \
+ for (j = 0; j < h; j++) { \
+ for (i = 0; i < w; i++) { \
+ if ((data[j * pitch + i] & mask) != c0) \
+ goto done; \
+ } \
+ } \
+ done: \
+ if (j >= h) { \
+ paletteNumColors = 1; /* Solid rectangle */ \
+ return; \
+ } \
+ if (paletteMaxColors < 2) { \
+ paletteNumColors = 0; /* Full-color encoding preferred */ \
+ return; \
+ } \
+ \
+ n0 = j * w + i; \
+ c1 = data[j * pitch + i] & mask; \
+ n1 = 0; \
+ i++; if (i >= w) {i = 0; j++;} \
+ for (j2 = j; j2 < h; j2++) { \
+ for (i2 = i; i2 < w; i2++) { \
+ ci = data[j2 * pitch + i2] & mask; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ goto done2; \
+ } \
+ i = 0; \
+ } \
+ done2: \
+ (*cl->translateFn)(cl->translateLookupTable, \
+ &cl->screen->serverFormat, &cl->format, \
+ (char *)&c0, (char *)&c0t, bpp/8, 1, 1); \
+ (*cl->translateFn)(cl->translateLookupTable, \
+ &cl->screen->serverFormat, &cl->format, \
+ (char *)&c1, (char *)&c1t, bpp/8, 1, 1); \
+ if (j2 >= h) { \
+ if (n0 > n1) { \
+ monoBackground = (uint32_t)c0t; \
+ monoForeground = (uint32_t)c1t; \
+ } else { \
+ monoBackground = (uint32_t)c1t; \
+ monoForeground = (uint32_t)c0t; \
+ } \
+ paletteNumColors = 2; /* Two colors */ \
+ return; \
+ } \
+ \
+ PaletteReset(); \
+ PaletteInsert (c0t, (uint32_t)n0, bpp); \
+ PaletteInsert (c1t, (uint32_t)n1, bpp); \
+ \
+ ni = 1; \
+ i2++; if (i2 >= w) {i2 = 0; j2++;} \
+ for (j = j2; j < h; j++) { \
+ for (i = i2; i < w; i++) { \
+ if ((data[j * pitch + i] & mask) == ci) { \
+ ni++; \
+ } else { \
+ (*cl->translateFn)(cl->translateLookupTable, \
+ &cl->screen->serverFormat, \
+ &cl->format, (char *)&ci, \
+ (char *)&cit, bpp/8, 1, 1); \
+ if (!PaletteInsert (cit, (uint32_t)ni, bpp)) \
+ return; \
+ ci = data[j * pitch + i] & mask; \
+ ni = 1; \
+ } \
+ } \
+ i2 = 0; \
+ } \
+ \
+ (*cl->translateFn)(cl->translateLookupTable, \
+ &cl->screen->serverFormat, &cl->format, \
+ (char *)&ci, (char *)&cit, bpp/8, 1, 1); \
+ PaletteInsert (cit, (uint32_t)ni, bpp); \
+}
+
+DEFINE_FAST_FILL_PALETTE_FUNCTION(16)
+DEFINE_FAST_FILL_PALETTE_FUNCTION(32)
+
+
+/*
+ * Functions to operate with palette structures.
+ */
+
+#define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF))
+#define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF))
+
+
+static void
+PaletteReset(void)
+{
+ paletteNumColors = 0;
+ memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
+}
+
+
+static int
+PaletteInsert(uint32_t rgb,
+ int numPixels,
+ int bpp)
+{
+ COLOR_LIST *pnode;
+ COLOR_LIST *prev_pnode = NULL;
+ int hash_key, idx, new_idx, count;
+
+ hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
+
+ pnode = palette.hash[hash_key];
+
+ while (pnode != NULL) {
+ if (pnode->rgb == rgb) {
+ /* Such palette entry already exists. */
+ new_idx = idx = pnode->idx;
+ count = palette.entry[idx].numPixels + numPixels;
+ if (new_idx && palette.entry[new_idx-1].numPixels < count) {
+ do {
+ palette.entry[new_idx] = palette.entry[new_idx-1];
+ palette.entry[new_idx].listNode->idx = new_idx;
+ new_idx--;
+ }
+ while (new_idx && palette.entry[new_idx-1].numPixels < count);
+ palette.entry[new_idx].listNode = pnode;
+ pnode->idx = new_idx;
+ }
+ palette.entry[new_idx].numPixels = count;
+ return paletteNumColors;
+ }
+ prev_pnode = pnode;
+ pnode = pnode->next;
+ }
+
+ /* Check if palette is full. */
+ if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
+ paletteNumColors = 0;
+ return 0;
+ }
+
+ /* Move palette entries with lesser pixel counts. */
+ for ( idx = paletteNumColors;
+ idx > 0 && palette.entry[idx-1].numPixels < numPixels;
+ idx-- ) {
+ palette.entry[idx] = palette.entry[idx-1];
+ palette.entry[idx].listNode->idx = idx;
+ }
+
+ /* Add new palette entry into the freed slot. */
+ pnode = &palette.list[paletteNumColors];
+ if (prev_pnode != NULL) {
+ prev_pnode->next = pnode;
+ } else {
+ palette.hash[hash_key] = pnode;
+ }
+ pnode->next = NULL;
+ pnode->idx = idx;
+ pnode->rgb = rgb;
+ palette.entry[idx].listNode = pnode;
+ palette.entry[idx].numPixels = numPixels;
+
+ return (++paletteNumColors);
+}
+
+
+/*
+ * Converting 32-bit color samples into 24-bit colors.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void Pack24(rfbClientPtr cl,
+ char *buf,
+ rfbPixelFormat *fmt,
+ int count)
+{
+ uint32_t *buf32;
+ uint32_t pix;
+ int r_shift, g_shift, b_shift;
+
+ buf32 = (uint32_t *)buf;
+
+ if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) {
+ r_shift = fmt->redShift;
+ g_shift = fmt->greenShift;
+ b_shift = fmt->blueShift;
+ } else {
+ r_shift = 24 - fmt->redShift;
+ g_shift = 24 - fmt->greenShift;
+ b_shift = 24 - fmt->blueShift;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> r_shift);
+ *buf++ = (char)(pix >> g_shift);
+ *buf++ = (char)(pix >> b_shift);
+ }
+}
+
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+static void \
+EncodeIndexedRect##bpp(uint8_t *buf, int count) { \
+ COLOR_LIST *pnode; \
+ uint##bpp##_t *src; \
+ uint##bpp##_t rgb; \
+ int rep = 0; \
+ \
+ src = (uint##bpp##_t *) buf; \
+ \
+ while (count--) { \
+ rgb = *src++; \
+ while (count && *src == rgb) { \
+ rep++, src++, count--; \
+ } \
+ pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
+ while (pnode != NULL) { \
+ if ((uint##bpp##_t)pnode->rgb == rgb) { \
+ *buf++ = (uint8_t)pnode->idx; \
+ while (rep) { \
+ *buf++ = (uint8_t)pnode->idx; \
+ rep--; \
+ } \
+ break; \
+ } \
+ pnode = pnode->next; \
+ } \
+ } \
+}
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+static void \
+EncodeMonoRect##bpp(uint8_t *buf, int w, int h) { \
+ uint##bpp##_t *ptr; \
+ uint##bpp##_t bg; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (uint##bpp##_t *) buf; \
+ bg = (uint##bpp##_t) monoBackground; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) \
+ break; \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) \
+ continue; \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+}
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+
+/*
+ * JPEG compression stuff.
+ */
+
+static rfbBool
+SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality)
+{
+ unsigned char *srcbuf;
+ int ps = cl->screen->serverFormat.bitsPerPixel / 8;
+ int subsamp = subsampLevel2tjsubsamp[subsampLevel];
+ unsigned long size = 0;
+ int flags = 0, pitch;
+ unsigned char *tmpbuf = NULL;
+
+ if (cl->screen->serverFormat.bitsPerPixel == 8)
+ return SendFullColorRect(cl, w, h);
+
+ if (ps < 2) {
+ rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n");
+ return 0;
+ }
+ if (!j) {
+ if ((j = tjInitCompress()) == NULL) {
+ rfbLog("JPEG Error: %s\n", tjGetErrorStr());
+ return 0;
+ }
+ }
+
+ if (tightAfterBufSize < TJBUFSIZE(w, h)) {
+ if (tightAfterBuf == NULL)
+ tightAfterBuf = (char *)malloc(TJBUFSIZE(w, h));
+ else
+ tightAfterBuf = (char *)realloc(tightAfterBuf,
+ TJBUFSIZE(w, h));
+ if (!tightAfterBuf) {
+ rfbLog("Memory allocation failure!\n");
+ return 0;
+ }
+ tightAfterBufSize = TJBUFSIZE(w, h);
+ }
+
+ if (ps == 2) {
+ uint16_t *srcptr, pix;
+ unsigned char *dst;
+ int inRed, inGreen, inBlue, i, j;
+
+ if((tmpbuf = (unsigned char *)malloc(w * h * 3)) == NULL)
+ rfbLog("Memory allocation failure!\n");
+ srcptr = (uint16_t *)&cl->scaledScreen->frameBuffer
+ [y * cl->scaledScreen->paddedWidthInBytes + x * ps];
+ dst = tmpbuf;
+ for(j = 0; j < h; j++) {
+ uint16_t *srcptr2 = srcptr;
+ unsigned char *dst2 = dst;
+ for (i = 0; i < w; i++) {
+ pix = *srcptr2++;
+ inRed = (int) (pix >> cl->screen->serverFormat.redShift
+ & cl->screen->serverFormat.redMax);
+ inGreen = (int) (pix >> cl->screen->serverFormat.greenShift
+ & cl->screen->serverFormat.greenMax);
+ inBlue = (int) (pix >> cl->screen->serverFormat.blueShift
+ & cl->screen->serverFormat.blueMax);
+ *dst2++ = (uint8_t)((inRed * 255
+ + cl->screen->serverFormat.redMax / 2)
+ / cl->screen->serverFormat.redMax);
+ *dst2++ = (uint8_t)((inGreen * 255
+ + cl->screen->serverFormat.greenMax / 2)
+ / cl->screen->serverFormat.greenMax);
+ *dst2++ = (uint8_t)((inBlue * 255
+ + cl->screen->serverFormat.blueMax / 2)
+ / cl->screen->serverFormat.blueMax);
+ }
+ srcptr += cl->scaledScreen->paddedWidthInBytes / ps;
+ dst += w * 3;
+ }
+ srcbuf = tmpbuf;
+ pitch = w * 3;
+ ps = 3;
+ } else {
+ if (cl->screen->serverFormat.bigEndian && ps == 4)
+ flags |= TJ_ALPHAFIRST;
+ if (cl->screen->serverFormat.redShift == 16
+ && cl->screen->serverFormat.blueShift == 0)
+ flags |= TJ_BGR;
+ if (cl->screen->serverFormat.bigEndian)
+ flags ^= TJ_BGR;
+ pitch = cl->scaledScreen->paddedWidthInBytes;
+ srcbuf = (unsigned char *)&cl->scaledScreen->frameBuffer
+ [y * pitch + x * ps];
+ }
+
+ if (tjCompress(j, srcbuf, w, pitch, h, ps, (unsigned char *)tightAfterBuf,
+ &size, subsamp, quality, flags) == -1) {
+ rfbLog("JPEG Error: %s\n", tjGetErrorStr());
+ if (tmpbuf) {
+ free(tmpbuf);
+ tmpbuf = NULL;
+ }
+ return 0;
+ }
+
+ if (tmpbuf) {
+ free(tmpbuf);
+ tmpbuf = NULL;
+ }
+
+ if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
+ rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
+
+ return SendCompressedData(cl, tightAfterBuf, (int)size);
+}
diff --git a/rfb/rfb.h b/rfb/rfb.h
index e068e76..0d11357 100644
--- a/rfb/rfb.h
+++ b/rfb/rfb.h
@@ -599,6 +599,10 @@ typedef struct _rfbClientRec {
#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG)
int tightCompressLevel;
#endif
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+ /* TurboVNC Encoding support (extends TightVNC) */
+ int tightSubsampLevel;
+#endif
#endif
/* Ultra Encoding support */
@@ -871,6 +875,10 @@ extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w,
#define TIGHT_DEFAULT_COMPRESSION 6
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+#define TIGHT_DEFAULT_SUBSAMP 0
+#endif
+
extern rfbBool rfbTightDisableGradient;
extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h);
diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h
index c6dfd2c..d99fbe5 100644
--- a/rfb/rfbproto.h
+++ b/rfb/rfbproto.h
@@ -13,7 +13,9 @@
*/
/*
+ * Copyright (C) 2009-2010 D. R. Commander. All Rights Reserved.
* Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
@@ -457,6 +459,8 @@ typedef struct {
/*
* Special encoding numbers:
+ * 0xFFFFFD00 .. 0xFFFFFD05 -- subsampling level
+ * 0xFFFFFE00 .. 0xFFFFFE64 -- fine-grained quality level (0-100 scale)
* 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels;
* 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data;
* 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions;
@@ -465,6 +469,17 @@ typedef struct {
* 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels.
*/
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+#define rfbEncodingFineQualityLevel0 0xFFFFFE00
+#define rfbEncodingFineQualityLevel100 0xFFFFFE64
+#define rfbEncodingSubsamp1X 0xFFFFFD00
+#define rfbEncodingSubsamp4X 0xFFFFFD01
+#define rfbEncodingSubsamp2X 0xFFFFFD02
+#define rfbEncodingSubsampGray 0xFFFFFD03
+#define rfbEncodingSubsamp8X 0xFFFFFD04
+#define rfbEncodingSubsamp16X 0xFFFFFD05
+#endif
+
#define rfbEncodingCompressLevel0 0xFFFFFF00
#define rfbEncodingCompressLevel1 0xFFFFFF01
#define rfbEncodingCompressLevel2 0xFFFFFF02
@@ -719,12 +734,19 @@ typedef struct {
* 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 1001 (0x0A), then the compression type is "png",
- * if 0xxx, then the compression type is "basic",
+ * (TurboVNC) if 1010 (0x0A), then the compression type is "basic"
+ * and no Zlib compression was used,
+ * (TurboVNC) if 1110 (0x0E), then the compression type is "basic",
+ * no Zlib compression was used, and a "filter id" byte follows
+ * this byte,
+ * (TightVNC) if 1010 (0x0A), then the compression type is "png",
+ * if 0xxx, then the compression type is "basic" and Zlib
+ * compression was used,
* 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:
+ * If the compression type is "basic" and Zlib compression was used, then bits
+ * 6..4 of the compression control byte (those xxx in 0xxx) specify the
+ * following:
*
* bits 5-4: decimal representation is the index of a particular zlib
* stream which should be used for decompressing the data;
@@ -836,7 +858,11 @@ typedef struct {
#define rfbTightExplicitFilter 0x04
#define rfbTightFill 0x08
#define rfbTightJpeg 0x09
+#ifdef LIBVNCSERVER_HAVE_TURBOVNC
+#define rfbTightNoZlib 0x0A
+#else
#define rfbTightPng 0x0A
+#endif
#define rfbTightMaxSubencoding 0x0A
/* Filters to improve compression efficiency */