From 2742c9579f465399797992a0e031d941ed0ae18f Mon Sep 17 00:00:00 2001 From: runge Date: Sat, 28 Feb 2009 16:39:25 +0000 Subject: [PATCH] x11vnc: add kludge to experiment with turbovnc. --- configure.ac | 2 +- x11vnc/ChangeLog | 3 + x11vnc/README | 2 +- x11vnc/misc/Makefile.am | 2 + x11vnc/misc/turbovnc/Makefile.am | 1 + x11vnc/misc/turbovnc/README | 147 +++ x11vnc/misc/turbovnc/apply_turbovnc | 33 + x11vnc/misc/turbovnc/convert | 58 ++ x11vnc/misc/turbovnc/tight.c | 1502 +++++++++++++++++++++++++++ x11vnc/misc/turbovnc/turbojpeg.h | 229 ++++ x11vnc/misc/turbovnc/undo_turbovnc | 13 + 11 files changed, 1990 insertions(+), 2 deletions(-) create mode 100644 x11vnc/misc/turbovnc/Makefile.am create mode 100644 x11vnc/misc/turbovnc/README create mode 100755 x11vnc/misc/turbovnc/apply_turbovnc create mode 100755 x11vnc/misc/turbovnc/convert create mode 100644 x11vnc/misc/turbovnc/tight.c create mode 100644 x11vnc/misc/turbovnc/turbojpeg.h create mode 100755 x11vnc/misc/turbovnc/undo_turbovnc diff --git a/configure.ac b/configure.ac index 8c7db42..3f48b26 100644 --- a/configure.ac +++ b/configure.ac @@ -774,7 +774,7 @@ if test "$build_x11vnc" = "yes"; then # # configure.ac:690: required file `x11vnc/Makefile.in' not found # - AC_CONFIG_FILES([x11vnc/Makefile x11vnc/misc/Makefile]) + AC_CONFIG_FILES([x11vnc/Makefile x11vnc/misc/Makefile x11vnc/misc/turbovnc/Makefile]) fi AC_CONFIG_COMMANDS([chmod-libvncserver-config],[chmod a+x libvncserver-config]) diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index 36dc615..4692571 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,6 @@ +2009-02-28 Karl Runge + * x11vnc: add kludge to experiment with turbovnc. + 2009-02-25 Karl Runge * x11vnc: fix some -QD cases for use in tkx11vnc. diff --git a/x11vnc/README b/x11vnc/README index ffdfa15..c6637d4 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Wed Feb 25 21:10:01 EST 2009 +x11vnc README file Date: Sat Feb 28 10:23:00 EST 2009 The following information is taken from these URLs: diff --git a/x11vnc/misc/Makefile.am b/x11vnc/misc/Makefile.am index 5e3a813..efc5443 100644 --- a/x11vnc/misc/Makefile.am +++ b/x11vnc/misc/Makefile.am @@ -1 +1,3 @@ +SUBDIRS = turbovnc +DIST_SUBDIRS = turbovnc EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy diff --git a/x11vnc/misc/turbovnc/Makefile.am b/x11vnc/misc/turbovnc/Makefile.am new file mode 100644 index 0000000..7b5dfb3 --- /dev/null +++ b/x11vnc/misc/turbovnc/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=README apply_turbovnc convert tight.c turbojpeg.h undo_turbovnc diff --git a/x11vnc/misc/turbovnc/README b/x11vnc/misc/turbovnc/README new file mode 100644 index 0000000..b8e0ac9 --- /dev/null +++ b/x11vnc/misc/turbovnc/README @@ -0,0 +1,147 @@ +INTRO: +------ + +This is a "patch" to make x11vnc/libvncserver work with TurboVNC: + + http://www.virtualgl.org/About/TurboVNC + +It is very experimental/kludgy. Not all TurboVNC features may be enabled. +We are currently evaluating whether TurboVNC support should be officially +put into x11vnc/libvncserver. + +TurboVNC is an optimized VNC for fast refresh rates on fast networks. + +It does pretty well on good broadband as well. But it is not as fast +as regular TightVNC on slow links. + + +TURBOJPEG: +--------- + +TurboVNC uses the TurboJPEG library based on a fast proprietary JPEG +implementation. You will need to download it from the VirtualGL +sourceforge site: + + http://sourceforge.net/project/showfiles.php?group_id=117509&package_id=166100 + +Either install it or simply unpack the .deb or .rpm file into a directory. + +N.B. you can unpack a .deb via 'ar x package.deb' and the extracting +from the data.tar.gz file. rpm2cpio can be used to unpack .rpm's. + + +QUICK-START: +------------ + +For those in a hurry: + + cd x11vnc-x.y.z/x11vnc/misc/turbovnc + ./apply_turbovnc + cd ../../.. + env LDFLAGS='-L/DIR -Xlinker --rpath=/DIR' ./configure + make AM_LDFLAGS='-lturbojpeg' + +where you replace /DIR with your directory containing libturbojpeg.so. + + +PATCHING AND BUILDING: +---------------------- + +After unpacking your x11vnc-x.y.z.tar.gz tarball cd to the +x11vnc-x.y.z/x11vnc/misc/turbovnc (where this README file is) and from +that directory run: + + ./apply_turbovnc + +that will modify files in the libvncserver and x11vnc directories above +this directory. (To undo these changes run: ./undo_turbovnc) The input +sources, tight.c and turbojpeg.h are from the TurboVNC source package. + +After applying, go back to the top level source directory and run: + + env LDFLAGS='-L/path/to/turbojpeg -Xlinker --rpath=/path/to/turbojpeg' ./configure + +where the turbojpeg library is: + + /path/to/turbojpeg/libturbojpeg.so + +(change /path/to/turbojpeg to the directory where you installed or +unpacked it.) + +If you are not using gnu gcc and gnu linker the options may be a little +different (e.g. -R instead of -Xlinker --rpath). + +If you need additional ./configure options or env. var. settings, +add them too. + + +Next, run this make command: + + make AM_LDFLAGS='-lturbojpeg' + +This is a hack and may not always work, if it doesn't edit x11vnc/Makefile +and add '-lturbojpeg' to the LIBS variable. + +This should create a binary: + + ./x11vnc/x11vnc + +that supports VirtualGL's TurboVNC. + +You will need a TurboVNC viewer, you can get one here: + + http://sourceforge.net/project/showfiles.php?group_id=117509&package_id=128130 + +Let us know how it goes. + + +PERFORMANCE: +------------ + +Note that x11vnc has to read the display's screen pixels from the +graphics card memory. This can be slow, e.g. 10 MB/sec. + +There is not a big need for graphics card manufacturers to optimize the +read rate; the write rate is the one they optimize greatly. + + http://www.karlrunge.com/x11vnc/#limitations + +If you run x11vnc and see lines like this: + + 28/02/2009 00:52:07 Autoprobing selected port 5900 + 28/02/2009 00:52:07 fb read rate: 10 MB/sec + 28/02/2009 00:52:07 screen setup finished. + +you have a typical slow one. + +Whereas if you see this: + + 28/02/2009 00:54:46 Autoprobing selected port 5900 + 28/02/2009 00:54:46 fb read rate: 321 MB/sec + 28/02/2009 00:54:46 fast read: reset wait ms to: 10 + 28/02/2009 00:54:46 fast read: reset defer ms to: 10 + 28/02/2009 00:54:46 screen setup finished. + +that is very fast. + +We have only seen it this fast on Linux by using the nvidia proprietary +graphics drivers. The Xorg drivers are typically slow 10 MB/sec. + +It will also be fast if the X server is virtual: Xvfb or Xdummy +since the screen pixels are stored in RAM: + + http://www.karlrunge.com/x11vnc/faq.html#faq-xvfb + +And it will be fast if the ShadowFB xorg.conf option is enabled (if the +card supports it.) + + +The point we are trying to make is that even though TurboVNC uses a +wicked fast JPEG implementation, and cuts out overhead in its attempt to +pump out as many frames per second as it can, if it is slow for x11vnc +to read the screen pixels in the first place then you might not even +notice the TurboVNC speedup. + +So TurboVNC+x11vnc will be faster than TightVNC+x11vnc, but if there is +a large overhead/bottleneck from reading the graphics card framebuffer, +then the speedup will be marginal. diff --git a/x11vnc/misc/turbovnc/apply_turbovnc b/x11vnc/misc/turbovnc/apply_turbovnc new file mode 100755 index 0000000..0a8ca7a --- /dev/null +++ b/x11vnc/misc/turbovnc/apply_turbovnc @@ -0,0 +1,33 @@ +#!/bin/sh + +ldir="../../../libvncserver" + +fail="" +if [ ! -f "./tight.c" ]; then + fail=1 +fi +if [ ! -f "./turbojpeg.h" ]; then + fail=1 +fi +if [ ! -f "./convert" ]; then + fail=1 +fi +if [ ! -f "$ldir/tight.c" ]; then + ls -l "$ldir/tight.c" + fail=1 +fi +if [ "X$fail" = "X1" ]; then + echo "Must be run from inside the directory containing 'apply_turbovnc'" + exit 1 +fi + +if [ -f "$ldir/tight.c.ORIG" ]; then + set -xv +else + set -xv + cp -p "$ldir/tight.c" "$ldir/tight.c.ORIG" +fi + +perl ./convert ./tight.c > "$ldir/tight.c" +cp -p ./turbojpeg.h "$ldir" +ls -l $ldir/tight.c* $ldir/turbojpeg.h diff --git a/x11vnc/misc/turbovnc/convert b/x11vnc/misc/turbovnc/convert new file mode 100755 index 0000000..fdd0dbb --- /dev/null +++ b/x11vnc/misc/turbovnc/convert @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +while (<>) { + if (/^#include.*"rfb.h"/) { + print < +#define Bool rfbBool +#define CARD32 uint32_t +#define CARD16 uint16_t +#define CARD8 uint8_t +#define xalloc malloc +#define xrealloc realloc +#define rfbTightNoZlib 0x0A +END + next; + } + foreach $func (qw(FindBestSolidArea ExtendSolidArea CheckSolidTile CheckSolidTile##bpp CheckSolidTile8 CheckSolidTile16 CheckSolidTile32 Pack24)) { + if (/static.*\b\Q$func\E\b/ && !exists $did_static{$func}) { + $_ =~ s/\b\Q$func\E\b(\s*)\(/$func$1(rfbClientPtr cl, /; + $did_static{$func} = 1; + } elsif (/\b\Q$func\E\b\s*\(/) { + $_ =~ s/\b\Q$func\E\b(\s*)\(/$func$1(cl, /; + } + } + if (/^\s*subsampLevel\s*=\s*cl/) { + $_ = "//$_"; + print "subsampLevel = 0;\n"; + } + $_ =~ s/cl->tightQualityLevel;/cl->tightQualityLevel * 10;/; + + $_ =~ s/rfbScreen.pfbMemory/cl->scaledScreen->frameBuffer/g; + $_ =~ s/rfbScreen.paddedWidthInBytes/cl->scaledScreen->paddedWidthInBytes/g; + $_ =~ s/rfbScreen.bitsPerPixel/cl->scaledScreen->bitsPerPixel/g; + $_ =~ s/rfbServerFormat/cl->screen->serverFormat/g; + + if (/^(FindBestSolidArea|ExtendSolidArea|static void Pack24|CheckSolidTile)\(cl/) { + $_ .= "rfbClientPtr cl;\n"; + } + if (/^(CheckSolidTile##bpp)\(cl/) { + $_ .= "rfbClientPtr cl; \\\n"; + } + $_ =~ s/\bublen\b/cl->ublen/; + $_ =~ s/\bupdateBuf\b/cl->updateBuf/; + + if (/cl->(rfbRectanglesSent|rfbBytesSent)/) { + $_ = "//$_"; + } + print; +} + +print < +#include +#include +#include "rfb.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 + +/* This variable is set on every rfbSendRectEncodingTight() call. */ +static Bool usePixelFormat24; + + +/* 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; +} TIGHT_CONF; + +static TIGHT_CONF tightConf[2] = { + { 65536, 2048, 6, 0, 0, 0, 4 }, +#if 0 + { 2048, 128, 6, 1, 1, 1, 8 }, + { 6144, 256, 8, 3, 3, 2, 24 }, + { 10240, 1024, 12, 5, 5, 3, 32 }, + { 16384, 2048, 12, 6, 6, 4, 32 }, + { 32768, 2048, 12, 7, 7, 5, 32 }, + { 65536, 2048, 16, 7, 7, 6, 48 }, + { 65536, 2048, 16, 8, 8, 7, 64 }, + { 65536, 2048, 32, 9, 9, 8, 64 }, +#endif + { 65536, 2048, 32, 1, 1, 1, 96 } +}; + +static int compressLevel; +static int qualityLevel; +static int subsampLevel; + +static const int subsampLevel2tjsubsamp[4] = { + TJ_444, TJ_411, TJ_422, TJ_GRAYSCALE +}; + +/* Stuff dealing with palettes. */ + +typedef struct COLOR_LIST_s { + struct COLOR_LIST_s *next; + int idx; + CARD32 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; + +static int paletteNumColors, paletteMaxColors; +static CARD32 monoBackground, monoForeground; +static PALETTE palette; + +/* Pointers to dynamically-allocated buffers. */ + +static int tightBeforeBufSize = 0; +static char *tightBeforeBuf = NULL; + +static int tightAfterBufSize = 0; +static char *tightAfterBuf = NULL; + +static int *prevRowBuf = NULL; + + +/* Prototypes for static functions. */ + +static void FindBestSolidArea (int x, int y, int w, int h, + CARD32 colorValue, int *w_ptr, int *h_ptr); +static void ExtendSolidArea (int x, int y, int w, int h, + CARD32 colorValue, + int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); +static Bool CheckSolidTile (int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile8 (int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile16 (int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile32 (int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); + +static Bool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); +static Bool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); +static Bool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); + +static Bool SendSolidRect (rfbClientPtr cl); +static Bool SendMonoRect (rfbClientPtr cl, int w, int h); +static Bool SendIndexedRect (rfbClientPtr cl, int w, int h); +static Bool SendFullColorRect (rfbClientPtr cl, int w, int h); + +static Bool CompressData(rfbClientPtr cl, int streamId, int dataLen, + int zlibLevel, int zlibStrategy); +static Bool 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, CARD16 *data, int w, int pitch, + int h); +static void FastFillPalette32(rfbClientPtr cl, CARD32 *data, int w, int pitch, + int h); + +static void PaletteReset(void); +static int PaletteInsert(CARD32 rgb, int numPixels, int bpp); + +static void Pack24(char *buf, rfbPixelFormat *fmt, int count); + +static void EncodeIndexedRect16(CARD8 *buf, int count); +static void EncodeIndexedRect32(CARD8 *buf, int count); + +static void EncodeMonoRect8(CARD8 *buf, int w, int h); +static void EncodeMonoRect16(CARD8 *buf, int w, int h); +static void EncodeMonoRect32(CARD8 *buf, int w, int h); + +static Bool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, + int quality); + +/* + * Tight encoding implementation. + */ + +int +rfbNumCodedRectsTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, 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; + } +} + +Bool +rfbSendRectEncodingTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int nMaxRows; + CARD32 colorValue; + int dx, dy, dw, dh; + int x_best, y_best, w_best, h_best; + char *fbptr; + + compressLevel = cl->tightCompressLevel > 0 ? 1 : 0; + qualityLevel = cl->tightQualityLevel; + if (qualityLevel != -1) { + compressLevel = 1; + tightConf[compressLevel].idxZlibLevel = 1; + tightConf[compressLevel].monoZlibLevel = 1; + tightConf[compressLevel].rawZlibLevel = 1; + } else { + tightConf[compressLevel].idxZlibLevel = cl->tightCompressLevel; + tightConf[compressLevel].monoZlibLevel = cl->tightCompressLevel; + tightConf[compressLevel].rawZlibLevel = cl->tightCompressLevel; + } + subsampLevel = cl->tightSubsampLevel; + + 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 *)xalloc(tightBeforeBufSize); + else + tightBeforeBuf = (char *)xrealloc(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(dx, dy, dw, dh, &colorValue, FALSE)) { + + if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) { + CARD32 r=(colorValue>>16)&0xFF; + CARD32 g=(colorValue>>8)&0xFF; + CARD32 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(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(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 = (rfbScreen.pfbMemory + + (rfbScreen.paddedWidthInBytes * y_best) + + (x_best * (rfbScreen.bitsPerPixel / 8))); + + (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, + &cl->format, fbptr, tightBeforeBuf, + rfbScreen.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(x, y, w, h, colorValue, w_ptr, h_ptr) + int x, y, w, h; + CARD32 colorValue; + int *w_ptr, *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(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(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(x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr) + int x, y, w, h; + CARD32 colorValue; + int *x_ptr, *y_ptr, *w_ptr, *h_ptr; +{ + int cx, cy; + + /* Try to extend the area upwards. */ + for ( cy = *y_ptr - 1; + cy >= y && CheckSolidTile(*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(*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(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(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 Bool +CheckSolidTile(x, y, w, h, colorPtr, needSameColor) + int x, y, w, h; + CARD32 *colorPtr; + Bool needSameColor; +{ + switch(rfbServerFormat.bitsPerPixel) { + case 32: + return CheckSolidTile32(x, y, w, h, colorPtr, needSameColor); + case 16: + return CheckSolidTile16(x, y, w, h, colorPtr, needSameColor); + default: + return CheckSolidTile8(x, y, w, h, colorPtr, needSameColor); + } +} + +#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ + \ +static Bool \ +CheckSolidTile##bpp(x, y, w, h, colorPtr, needSameColor) \ + int x, y, w, h; \ + CARD32 *colorPtr; \ + Bool needSameColor; \ +{ \ + CARD##bpp *fbptr; \ + CARD##bpp colorValue; \ + int dx, dy; \ + \ + fbptr = (CARD##bpp *) \ + &rfbScreen.pfbMemory[y * rfbScreen.paddedWidthInBytes + x * (bpp/8)]; \ + \ + colorValue = *fbptr; \ + if (needSameColor && (CARD32)colorValue != *colorPtr) \ + return FALSE; \ + \ + for (dy = 0; dy < h; dy++) { \ + for (dx = 0; dx < w; dx++) { \ + if (colorValue != fbptr[dx]) \ + return FALSE; \ + } \ + fbptr = (CARD##bpp *)((CARD8 *)fbptr + rfbScreen.paddedWidthInBytes); \ + } \ + \ + *colorPtr = (CARD32)colorValue; \ + return TRUE; \ +} + +DEFINE_CHECK_SOLID_FUNCTION(8) +DEFINE_CHECK_SOLID_FUNCTION(16) +DEFINE_CHECK_SOLID_FUNCTION(32) + +static Bool +SendRectSimple(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, 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 *)xalloc(tightBeforeBufSize); + else + tightBeforeBuf = (char *)xrealloc(tightBeforeBuf, + tightBeforeBufSize); + } + + if (tightAfterBufSize < maxAfterSize) { + tightAfterBufSize = maxAfterSize; + if (tightAfterBuf == NULL) + tightAfterBuf = (char *)xalloc(tightAfterBufSize); + else + tightAfterBuf = (char *)xrealloc(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 Bool +SendSubrect(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + char *fbptr; + Bool success = FALSE; + + /* Send pending data if there is more than 128 bytes. */ + if (ublen > 128) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (!SendTightHeader(cl, x, y, w, h)) + return FALSE; + + fbptr = (rfbScreen.pfbMemory + (rfbScreen.paddedWidthInBytes * y) + + (x * (rfbScreen.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 = 24; + if ( paletteMaxColors < 2 && + w * h >= tightConf[compressLevel].monoMinRectSize ) { + paletteMaxColors = 2; + } + + if (cl->format.bitsPerPixel == rfbServerFormat.bitsPerPixel && + cl->format.redMax == rfbServerFormat.redMax && + cl->format.greenMax == rfbServerFormat.greenMax && + cl->format.blueMax == rfbServerFormat.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, (CARD16 *)fbptr, w, + rfbScreen.paddedWidthInBytes/2, h); + break; + default: + FastFillPalette32(cl, (CARD32 *)fbptr, w, + rfbScreen.paddedWidthInBytes/4, h); + } + + if(paletteNumColors != 0 || qualityLevel == -1) { + (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, + &cl->format, fbptr, tightBeforeBuf, + rfbScreen.paddedWidthInBytes, w, h); + } + } + else { + (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, + &cl->format, fbptr, tightBeforeBuf, + rfbScreen.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 Bool +SendTightHeader(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + + if (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(&updateBuf[ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingTight]++; + cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + +/* + * Subencoding implementations. + */ + +static Bool +SendSolidRect(cl) + rfbClientPtr cl; +{ + int len; + + if (usePixelFormat24) { + Pack24(tightBeforeBuf, &cl->format, 1); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + if (ublen + 1 + len > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + updateBuf[ublen++] = (char)(rfbTightFill << 4); + memcpy (&updateBuf[ublen], tightBeforeBuf, len); + ublen += len; + + cl->rfbBytesSent[rfbEncodingTight] += len + 1; + + return TRUE; +} + +static Bool +SendMonoRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 1; + int paletteLen, dataLen; + + if ( (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) + updateBuf[ublen++] = (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + updateBuf[ublen++] = (streamId | rfbTightExplicitFilter) << 4; + updateBuf[ublen++] = rfbTightFilterPalette; + updateBuf[ublen++] = 1; + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeMonoRect32((CARD8 *)tightBeforeBuf, w, h); + + ((CARD32 *)tightAfterBuf)[0] = monoBackground; + ((CARD32 *)tightAfterBuf)[1] = monoForeground; + if (usePixelFormat24) { + Pack24(tightAfterBuf, &cl->format, 2); + paletteLen = 6; + } else + paletteLen = 8; + + memcpy(&updateBuf[ublen], tightAfterBuf, paletteLen); + ublen += paletteLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen; + break; + + case 16: + EncodeMonoRect16((CARD8 *)tightBeforeBuf, w, h); + + ((CARD16 *)tightAfterBuf)[0] = (CARD16)monoBackground; + ((CARD16 *)tightAfterBuf)[1] = (CARD16)monoForeground; + + memcpy(&updateBuf[ublen], tightAfterBuf, 4); + ublen += 4; + cl->rfbBytesSent[rfbEncodingTight] += 7; + break; + + default: + EncodeMonoRect8((CARD8 *)tightBeforeBuf, w, h); + + updateBuf[ublen++] = (char)monoBackground; + updateBuf[ublen++] = (char)monoForeground; + cl->rfbBytesSent[rfbEncodingTight] += 5; + } + + return CompressData(cl, streamId, dataLen, + tightConf[compressLevel].monoZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static Bool +SendIndexedRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 2; + int i, entryLen; + + if ( (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) + updateBuf[ublen++] = (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + updateBuf[ublen++] = (streamId | rfbTightExplicitFilter) << 4; + updateBuf[ublen++] = rfbTightFilterPalette; + updateBuf[ublen++] = (char)(paletteNumColors - 1); + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeIndexedRect32((CARD8 *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((CARD32 *)tightAfterBuf)[i] = + palette.entry[i].listNode->rgb; + } + if (usePixelFormat24) { + Pack24(tightAfterBuf, &cl->format, paletteNumColors); + entryLen = 3; + } else + entryLen = 4; + + memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * entryLen); + ublen += paletteNumColors * entryLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; + break; + + case 16: + EncodeIndexedRect16((CARD8 *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((CARD16 *)tightAfterBuf)[i] = + (CARD16)palette.entry[i].listNode->rgb; + } + + memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * 2); + ublen += paletteNumColors * 2; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; + break; + + default: + return FALSE; /* Should never happen. */ + } + + return CompressData(cl, streamId, w * h, + tightConf[compressLevel].idxZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static Bool +SendFullColorRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 0; + int len; + + if (ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (tightConf[compressLevel].rawZlibLevel == 0) + updateBuf[ublen++] = (char)(rfbTightNoZlib << 4); + else + updateBuf[ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ + cl->rfbBytesSent[rfbEncodingTight]++; + + if (usePixelFormat24) { + Pack24(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 Bool +CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy) + rfbClientPtr cl; + int streamId, dataLen, zlibLevel, zlibStrategy; +{ + z_streamp pz; + int err, i; + + if (dataLen < TIGHT_MIN_TO_COMPRESS) { + memcpy(&updateBuf[ublen], tightBeforeBuf, dataLen); + ublen += dataLen; + cl->rfbBytesSent[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 Bool SendCompressedData(cl, buf, compressedLen) + rfbClientPtr cl; + char *buf; + int compressedLen; +{ + int i, portionLen; + + updateBuf[ublen++] = compressedLen & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x7F) { + updateBuf[ublen-1] |= 0x80; + updateBuf[ublen++] = compressedLen >> 7 & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x3FFF) { + updateBuf[ublen-1] |= 0x80; + updateBuf[ublen++] = compressedLen >> 14 & 0xFF; + cl->rfbBytesSent[rfbEncodingTight]++; + } + } + + portionLen = UPDATE_BUF_SIZE; + for (i = 0; i < compressedLen; i += portionLen) { + if (i + portionLen > compressedLen) { + portionLen = compressedLen - i; + } + if (ublen + portionLen > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + memcpy(&updateBuf[ublen], &buf[i], portionLen); + ublen += portionLen; + } + cl->rfbBytesSent[rfbEncodingTight] += compressedLen; + return TRUE; +} + +/* + * Code to determine how many different colors used in rectangle. + */ + +static void +FillPalette8(count) + int count; +{ + CARD8 *data = (CARD8 *)tightBeforeBuf; + CARD8 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 = (CARD32)c0; + monoForeground = (CARD32)c1; + } else { + monoBackground = (CARD32)c1; + monoForeground = (CARD32)c0; + } + paletteNumColors = 2; /* Two colors */ + } +} + +#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void \ +FillPalette##bpp(count) \ + int count; \ +{ \ + CARD##bpp *data = (CARD##bpp *)tightBeforeBuf; \ + CARD##bpp 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 = (CARD32)c0; \ + monoForeground = (CARD32)c1; \ + } else { \ + monoBackground = (CARD32)c1; \ + monoForeground = (CARD32)c0; \ + } \ + paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(); \ + PaletteInsert (c0, (CARD32)n0, bpp); \ + PaletteInsert (c1, (CARD32)n1, bpp); \ + \ + ni = 1; \ + for (i++; i < count; i++) { \ + if (data[i] == ci) { \ + ni++; \ + } else { \ + if (!PaletteInsert (ci, (CARD32)ni, bpp)) \ + return; \ + ci = data[i]; \ + ni = 1; \ + } \ + } \ + PaletteInsert (ci, (CARD32)ni, bpp); \ +} + +DEFINE_FILL_PALETTE_FUNCTION(16) +DEFINE_FILL_PALETTE_FUNCTION(32) + +#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void \ +FastFillPalette##bpp(cl, data, w, pitch, h) \ + rfbClientPtr cl; \ + CARD##bpp *data; \ + int w, pitch, h; \ +{ \ + CARD##bpp c0, c1, ci, mask, c0t, c1t, cit; \ + int i, j, i2, j2, n0, n1, ni; \ + \ + if (cl->translateFn != rfbTranslateNone) { \ + mask = rfbServerFormat.redMax << rfbServerFormat.redShift; \ + mask |= rfbServerFormat.greenMax << rfbServerFormat.greenShift; \ + mask |= rfbServerFormat.blueMax << rfbServerFormat.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, &rfbServerFormat, \ + &cl->format, (char *)&c0, (char *)&c0t, bpp/8, \ + 1, 1); \ + (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, \ + &cl->format, (char *)&c1, (char *)&c1t, bpp/8, \ + 1, 1); \ + if (j2 >= h) { \ + if (n0 > n1) { \ + monoBackground = (CARD32)c0t; \ + monoForeground = (CARD32)c1t; \ + } else { \ + monoBackground = (CARD32)c1t; \ + monoForeground = (CARD32)c0t; \ + } \ + paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(); \ + PaletteInsert (c0t, (CARD32)n0, bpp); \ + PaletteInsert (c1t, (CARD32)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, \ + &rfbServerFormat, &cl->format, \ + (char *)&ci, (char *)&cit, bpp/8, \ + 1, 1); \ + if (!PaletteInsert (cit, (CARD32)ni, bpp)) \ + return; \ + ci = data[j * pitch + i] & mask; \ + ni = 1; \ + } \ + } \ + i2 = 0; \ + } \ + \ + (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, \ + &cl->format, (char *)&ci, (char *)&cit, bpp/8, \ + 1, 1); \ + PaletteInsert (cit, (CARD32)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(rgb, numPixels, bpp) + CARD32 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(buf, fmt, count) + char *buf; + rfbPixelFormat *fmt; + int count; +{ + CARD32 *buf32; + CARD32 pix; + int r_shift, g_shift, b_shift; + + buf32 = (CARD32 *)buf; + + if (!rfbServerFormat.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(buf, count) \ + CARD8 *buf; \ + int count; \ +{ \ + COLOR_LIST *pnode; \ + CARD##bpp *src; \ + CARD##bpp rgb; \ + int rep = 0; \ + \ + src = (CARD##bpp *) buf; \ + \ + while (count--) { \ + rgb = *src++; \ + while (count && *src == rgb) { \ + rep++, src++, count--; \ + } \ + pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \ + while (pnode != NULL) { \ + if ((CARD##bpp)pnode->rgb == rgb) { \ + *buf++ = (CARD8)pnode->idx; \ + while (rep) { \ + *buf++ = (CARD8)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(buf, w, h) \ + CARD8 *buf; \ + int w, h; \ +{ \ + CARD##bpp *ptr; \ + CARD##bpp bg; \ + unsigned int value, mask; \ + int aligned_width; \ + int x, y, bg_bits; \ + \ + ptr = (CARD##bpp *) buf; \ + bg = (CARD##bpp) 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++ = (CARD8)value; \ + } \ + \ + mask = 0x80; \ + value = 0; \ + if (x >= w) \ + continue; \ + \ + for (; x < w; x++) { \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + mask >>= 1; \ + } \ + *buf++ = (CARD8)value; \ + } \ +} + +DEFINE_MONO_ENCODE_FUNCTION(8) +DEFINE_MONO_ENCODE_FUNCTION(16) +DEFINE_MONO_ENCODE_FUNCTION(32) + +/* + * JPEG compression stuff. + */ + +static unsigned long jpegDstDataLen; +static tjhandle j=NULL; + +static Bool +SendJpegRect(cl, x, y, w, h, quality) + rfbClientPtr cl; + int x, y, w, h; + int quality; +{ + int dy; + unsigned char *srcbuf; + int ps=rfbServerFormat.bitsPerPixel/8; + int subsamp=subsampLevel2tjsubsamp[subsampLevel]; + unsigned long size=0; + int flags=0, pitch; + unsigned char *tmpbuf=NULL; + + if (rfbServerFormat.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 *)xalloc(TJBUFSIZE(w,h)); + else + tightAfterBuf = (char *)xrealloc(tightAfterBuf, + TJBUFSIZE(w,h)); + if(!tightAfterBuf) { + rfbLog("Memory allocation failure!\n"); + return 0; + } + tightAfterBufSize = TJBUFSIZE(w,h); + } + + if (ps == 2) { + CARD16 *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 = (CARD16 *) + &rfbScreen.pfbMemory[y * rfbScreen.paddedWidthInBytes + + x * ps]; + dst = tmpbuf; + for(j=0; j> rfbServerFormat.redShift & rfbServerFormat.redMax); + inGreen = (int) + (pix >> rfbServerFormat.greenShift & rfbServerFormat.greenMax); + inBlue = (int) + (pix >> rfbServerFormat.blueShift & rfbServerFormat.blueMax); + *dst2++ = (CARD8)((inRed * 255 + rfbServerFormat.redMax / 2) / + rfbServerFormat.redMax); + *dst2++ = (CARD8)((inGreen * 255 + rfbServerFormat.greenMax / 2) / + rfbServerFormat.greenMax); + *dst2++ = (CARD8)((inBlue * 255 + rfbServerFormat.blueMax / 2) / + rfbServerFormat.blueMax); + } + srcptr+=rfbScreen.paddedWidthInBytes/ps; + dst+=w*3; + } + srcbuf = tmpbuf; + pitch = w*3; + ps = 3; + } else { + if(rfbServerFormat.bigEndian && ps==4) flags|=TJ_ALPHAFIRST; + if(rfbServerFormat.redShift==16 && rfbServerFormat.blueShift==0) + flags|=TJ_BGR; + if(rfbServerFormat.bigEndian) flags^=TJ_BGR; + srcbuf=(unsigned char *)&rfbScreen.pfbMemory[y * + rfbScreen.paddedWidthInBytes + x * ps]; + pitch=rfbScreen.paddedWidthInBytes; + } + + 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; + } + jpegDstDataLen=(int)size; + + if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;} + + if (ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + updateBuf[ublen++] = (char)(rfbTightJpeg << 4); + cl->rfbBytesSent[rfbEncodingTight]++; + + return SendCompressedData(cl, tightAfterBuf, jpegDstDataLen); +} diff --git a/x11vnc/misc/turbovnc/turbojpeg.h b/x11vnc/misc/turbovnc/turbojpeg.h new file mode 100644 index 0000000..8a6af05 --- /dev/null +++ b/x11vnc/misc/turbovnc/turbojpeg.h @@ -0,0 +1,229 @@ +/* Copyright (C)2004 Landmark Graphics + * Copyright (C)2005, 2006 Sun Microsystems, Inc. + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3 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_411, TJ_GRAYSCALE}; + +/* Flags */ +#define TJ_BGR 1 +#define TJ_BOTTOMUP 2 +#define TJ_FORCEMMX 8 /* Force IPP to use MMX code even if SSE available */ +#define TJ_FORCESSE 16 /* Force IPP to use SSE1 code even if SSE2 available */ +#define TJ_FORCESSE2 32 /* Force IPP to use SSE2 code (useful if auto-detect is not working properly) */ +#define TJ_ALPHAFIRST 64 /* BGR buffer is ABGR and RGB buffer is ARGB */ +#define TJ_FORCESSE3 128 /* Force IPP to use SSE3 code (useful if auto-detect is not working properly) */ + +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 will cause performance to suffer. + + 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 pixels in + RGB(A) or BGR(A) form + [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., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the source image + [INPUT] pixelsize = size (in bytes) of each pixel in the source image + RGBA and BGRA: 4, RGB and BGR: 3 + [INPUT] dstbuf = pointer to user-allocated image buffer which will receive + the JPEG image. Use the macro TJBUFSIZE(width, height) to determine + the appropriate size for this buffer based on the image width and height. + [OUTPUT] size = pointer to unsigned long which receives the size (in bytes) + of the compressed image + [INPUT] jpegsubsamp = Specifies either 4:1:1, 4:2:2, or 4:4:4 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_411: 4:1:1 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 following + + TJ_BGR: The components of each pixel in the source image are stored in + B,G,R order, not R,G,B + TJ_BOTTOMUP: The source image is stored in bottom-up (Windows) order, + not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + TJ_FORCESSE3: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE3 code (bypass CPU auto-detection) + + 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); + +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 will cause performance to suffer. + + RETURNS: NULL on error +*/ +DLLEXPORT tjhandle DLLCALL tjInitDecompress(void); + + +/* + int tjDecompressHeader(tjhandle j, + unsigned char *srcbuf, unsigned long size, + int *width, int *height) + + [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) + [OUTPUT] width = width (in pixels) of the JPEG image + [OUTPUT] height = height (in pixels) of the JPEG image + + RETURNS: 0 on success, -1 on error +*/ +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 which 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., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the destination image + [INPUT] pixelsize = size (in bytes) of each pixel in the destination image + RGBA/RGBx and BGRA/BGRx: 4, RGB and BGR: 3 + [INPUT] flags = the bitwise OR of one or more of the following + + TJ_BGR: The components of each pixel in the destination image should be + written in B,G,R order, not R,G,B + TJ_BOTTOMUP: The destination image should be stored in bottom-up + (Windows) order, not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + + 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/x11vnc/misc/turbovnc/undo_turbovnc b/x11vnc/misc/turbovnc/undo_turbovnc new file mode 100755 index 0000000..7080680 --- /dev/null +++ b/x11vnc/misc/turbovnc/undo_turbovnc @@ -0,0 +1,13 @@ +#!/bin/sh + +ldir="../../../libvncserver" + +if [ ! -f "$ldir/tight.c.ORIG" ]; then + ls -l "$ldir/tight.c.ORIG" + exit 1 +fi + +set -xv +rm -f "$ldir/tight.c" "$ldir/turbojpeg.h" +mv "$ldir/tight.c.ORIG" "$ldir/tight.c" +ls -l $ldir/tight.c*