From af85e5fee34f7998fe3f06c799b8364a9ddbebce Mon Sep 17 00:00:00 2001 From: dscho Date: Tue, 22 Oct 2002 08:46:27 +0000 Subject: [PATCH] updated to vnc-3.3.4 (ZRLE encoding) --- Makefile | 28 +++- corre.c | 15 +- cvs_update_anonymously | 3 +- httpd.c | 39 +++--- main.c | 1 - rfb.h | 10 +- rfbproto.h | 16 +++ rfbserver.c | 23 +++ stats.c | 10 +- zrle.cc | 180 ++++++++++++++++++++++++ zrleDecode.h | 241 ++++++++++++++++++++++++++++++++ zrleEncode.h | 310 +++++++++++++++++++++++++++++++++++++++++ 12 files changed, 836 insertions(+), 40 deletions(-) create mode 100644 zrle.cc create mode 100644 zrleDecode.h create mode 100644 zrleEncode.h diff --git a/Makefile b/Makefile index 0c5590a..8d28dfb 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ INCLUDES=-I. VNCSERVERLIB=-L. -lvncserver -L/usr/local/lib -lz -ljpeg -# for Solaris +CXX=g++ CC=gcc + +# for Solaris #EXTRALIBS=-lsocket -lnsl -L/usr/X/lib # for FreeBSD @@ -18,11 +20,8 @@ FLAG24 = -DALLOW24BPP OPTFLAGS=-g -Wall -pedantic #OPTFLAGS=-O2 -Wall -CFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) -DBACKCHANNEL RANLIB=ranlib -LIBS=$(LDFLAGS) $(VNCSERVERLIB) $(PTHREADLIB) $(EXTRALIBS) - # for Mac OS X OSX_LIBS = -framework ApplicationServices -framework Carbon -framework IOKit @@ -30,14 +29,31 @@ OSX_LIBS = -framework ApplicationServices -framework Carbon -framework IOKit #XLIBS = -L/usr/X11R6/lib -lXtst -lXext -lX11 XLIBS = -L/usr/X11R6/lib -L/usr/lib32 -lXtst -lXext -lX11 +ifdef CXX + +ZRLE_SRCS=zrle.cc rdr/FdInStream.cxx rdr/FdOutStream.cxx rdr/InStream.cxx \ + rdr/NullOutStream.cxx rdr/ZlibInStream.cxx rdr/ZlibOutStream.cxx +ZRLE_OBJS=zrle.o rdr/FdInStream.o rdr/FdOutStream.o rdr/InStream.o \ + rdr/NullOutStream.o rdr/ZlibInStream.o rdr/ZlibOutStream.o +ZRLE_DEF=-DHAVE_ZRLE + +%.o: %.cxx + $(CXX) $(CXXFLAGS) -c -o $@ $< + +endif + +CFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) $(ZRLE_DEF) -DBACKCHANNEL +CXXFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) $(ZRLE_DEF) -DBACKCHANNEL +LIBS=$(LDFLAGS) $(VNCSERVERLIB) $(PTHREADLIB) $(EXTRALIBS) + SOURCES=main.c rfbserver.c sraRegion.c auth.c sockets.c \ stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ zlib.c tight.c httpd.c cursor.c font.c \ - draw.c selbox.c d3des.c vncauth.c cargs.c + draw.c selbox.c d3des.c vncauth.c cargs.c $(ZRLE_SRCS) OBJS=main.o rfbserver.o sraRegion.o auth.o sockets.o \ stats.o corre.o hextile.o rre.o translate.o cutpaste.o \ zlib.o tight.o httpd.o cursor.o font.o \ - draw.o selbox.o d3des.o vncauth.o cargs.o + draw.o selbox.o d3des.o vncauth.o cargs.o $(ZRLE_OBJS) INSTALLHEADER=rfb.h rfbproto.h sraRegion.h keysym.h all: example pnmshow storepasswd diff --git a/corre.c b/corre.c index d6329a1..d866712 100644 --- a/corre.c +++ b/corre.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2002 RealVNC Ltd. * OSXvnc Copyright (C) 2001 Dan McGuirk . * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. * All Rights Reserved. @@ -63,17 +64,15 @@ rfbSendRectEncodingCoRRE(cl, x, y, w, h) int x, y, w, h; { if (h > cl->correMaxHeight) { - rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight ); - rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w, - h - cl->correMaxHeight); - return FALSE; + return (rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight) && + rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w, + h - cl->correMaxHeight)); } if (w > cl->correMaxWidth) { - rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h); - rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y, - w - cl->correMaxWidth, h); - return FALSE; + return (rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h) && + rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y, + w - cl->correMaxWidth, h)); } rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h); diff --git a/cvs_update_anonymously b/cvs_update_anonymously index 26f2014..47bfab3 100644 --- a/cvs_update_anonymously +++ b/cvs_update_anonymously @@ -1,8 +1,9 @@ if [ a"$1" = adiff ]; then cmd=diff + shift else cmd=update fi -cvs -z3 -d :pserver:anonymous@cvs.libvncserver.sf.net:/cvsroot/libvncserver $cmd +cvs -z3 -d :pserver:anonymous@cvs.libvncserver.sf.net:/cvsroot/libvncserver $cmd "$@" diff --git a/httpd.c b/httpd.c index 3baedce..5207c72 100644 --- a/httpd.c +++ b/httpd.c @@ -3,6 +3,7 @@ */ /* + * Copyright (C) 2002 RealVNC Ltd. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * * This is free software; you can redistribute it and/or modify @@ -47,30 +48,21 @@ #include "rfb.h" -#define NOT_FOUND_STR "HTTP/1.0 404 Not found\n\n" \ +#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ "File Not Found\n" \ "

File Not Found

\n" -#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\n\n" \ +#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \ "Invalid Request\n" \ "

Invalid request

\n" -#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\n\n" +#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\r\n\r\n" static void httpProcessInput(); static Bool compareAndSkip(char **ptr, const char *str); static Bool parseParams(const char *request, char *result, int max_bytes); static Bool validateString(char *str); -/* -int httpPort = 0; -char *httpDir = NULL; - -int httpListenSock = -1; -int httpSock = -1; -FILE* httpFP = NULL; -*/ - #define BUF_SIZE 32768 static char buf[BUF_SIZE]; @@ -163,8 +155,10 @@ httpCheckFds(rfbScreenInfoPtr rfbScreen) rfbLog("Rejected HTTP connection from client %s\n", inet_ntoa(addr.sin_addr)); #else - if ((rfbScreen->httpFP = fdopen(rfbScreen->httpSock, "r+")) == NULL) { - rfbLogPerror("httpCheckFds: fdopen"); + flags = fcntl(rfbScreen->httpSock, F_SETFL); + + if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) { + rfbLogPerror("httpCheckFds: fcntl"); #endif close(rfbScreen->httpSock); rfbScreen->httpSock = -1; @@ -187,9 +181,6 @@ httpCheckFds(rfbScreenInfoPtr rfbScreen) static void httpCloseSock(rfbScreenInfoPtr rfbScreen) { - fclose(rfbScreen->httpFP); - rfbScreen->httpFP = NULL; - /*RemoveEnabledDevice(httpSock);*/ rfbScreen->httpSock = -1; } @@ -231,7 +222,15 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) /* Read data from the HTTP client until we get a complete request. */ while (1) { - ssize_t got = read (rfbScreen->httpSock, buf + buf_filled, + ssize_t got; + + if (buf_filled > sizeof (buf)) { + rfbLog("httpProcessInput: HTTP request is too long\n"); + httpCloseSock(rfbScreen); + return; + } + + got = read (rfbScreen->httpSock, buf + buf_filled, sizeof (buf) - buf_filled - 1); if (got <= 0) { @@ -271,8 +270,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) rfbLog("httpd: client asked for CONNECT\n"); WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); - // don't fclose(rfbScreen->httpFP), because this would kill the connection - rfbScreen->httpFP = NULL; rfbScreen->httpSock = -1; return; } @@ -281,8 +278,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) rfbLog("httpd: client asked for /proxied.connection\n"); WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); - // don't fclose(rfbScreen->httpFP), because this would kill the connection - rfbScreen->httpFP = NULL; rfbScreen->httpSock = -1; return; } diff --git a/main.c b/main.c index adc231d..044e3c6 100644 --- a/main.c +++ b/main.c @@ -549,7 +549,6 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, rfbScreen->httpDir=NULL; rfbScreen->httpListenSock=-1; rfbScreen->httpSock=-1; - rfbScreen->httpFP=NULL; rfbScreen->desktopName = "LibVNCServer"; rfbScreen->rfbAlwaysShared = FALSE; diff --git a/rfb.h b/rfb.h index fcf7b09..7e096f0 100644 --- a/rfb.h +++ b/rfb.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2002 RealVNC Ltd. * OSXvnc Copyright (C) 2001 Dan McGuirk . * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. * All Rights Reserved. @@ -297,7 +298,6 @@ typedef struct _rfbScreenInfo char* httpDir; SOCKET httpListenSock; SOCKET httpSock; - FILE* httpFP; PasswordCheckProcPtr passwordCheck; void* rfbAuthPasswdData; @@ -409,6 +409,9 @@ typedef struct _rfbClientRec { Bool useCopyRect; int preferredEncoding; int correMaxWidth, correMaxHeight; +#ifdef HAVE_ZRLE + void* zrleData; +#endif /* The following member is only used during VNC authentication */ CARD8 authChallenge[CHALLENGESIZE]; @@ -716,6 +719,11 @@ extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld) /* cursor handling for the pointer */ extern void defaultPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl); +/* zrle.c */ + +extern Bool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w,int h); +extern void FreeZrleData(rfbClientPtr cl); + /* stats.c */ extern void rfbResetStats(rfbClientPtr cl); diff --git a/rfbproto.h b/rfbproto.h index f839019..e84f6a0 100644 --- a/rfbproto.h +++ b/rfbproto.h @@ -302,6 +302,7 @@ typedef struct { #ifdef BACKCHANNEL #define rfbEncodingBackChannel 15 #endif +#define rfbEncodingZRLE 16 /* * Special encoding numbers: @@ -559,6 +560,21 @@ typedef struct { */ +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ZRLE - encoding combining Zlib compression, tiling, palettisation and + * run-length encoding. + */ + +typedef struct { + CARD32 length; +} rfbZRLEHeader; + +#define sz_rfbZRLEHeader 4 + +#define rfbZRLETileWidth 64 +#define rfbZRLETileHeight 64 + + /*----------------------------------------------------------------------------- * SetColourMapEntries - these messages are only sent if the pixel * format uses a "colour map" (i.e. trueColour false) and the client has not diff --git a/rfbserver.c b/rfbserver.c index 7ba360e..51769be 100644 --- a/rfbserver.c +++ b/rfbserver.c @@ -3,6 +3,7 @@ */ /* + * Copyright (C) 2002 RealVNC Ltd. * OSXvnc Copyright (C) 2001 Dan McGuirk . * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. * All Rights Reserved. @@ -257,6 +258,7 @@ rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP) cl->preferredEncoding = rfbEncodingRaw; cl->correMaxWidth = 48; cl->correMaxHeight = 48; + cl->zrleData = 0; cl->copyRegion = sraRgnCreate(); cl->copyDX = 0; @@ -372,6 +374,10 @@ rfbClientConnectionGone(cl) if (cl->next) cl->next->prev = cl->prev; +#ifdef HAVE_ZRLE + FreeZrleData(cl); +#endif + #ifdef HAVE_PTHREADS LOCK(cl->refCountMutex); if(cl->refCount) { @@ -786,6 +792,15 @@ rfbProcessClientNormalMessage(cl) cl->enableBackChannel = TRUE; } break; +#endif +#ifdef HAVE_ZRLE + case rfbEncodingZRLE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using ZRLE encoding for client %s\n", + cl->host); + } + break; #endif default: if ( enc >= (CARD32)rfbEncodingCompressLevel0 && @@ -1187,6 +1202,14 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) return FALSE; } break; +#ifdef HAVE_ZRLE + case rfbEncodingZRLE: + if (!rfbSendRectEncodingZRLE(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + return FALSE; + } + break; +#endif } } sraRgnReleaseIterator(i); diff --git a/stats.c b/stats.c index a0b5ccc..a941741 100644 --- a/stats.c +++ b/stats.c @@ -3,6 +3,7 @@ */ /* + * Copyright (C) 2002 RealVNC Ltd. * OSXvnc Copyright (C) 2001 Dan McGuirk . * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. * All Rights Reserved. @@ -29,7 +30,14 @@ static const char* encNames[] = { "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", - "zlib", "tight", "[encoding 8]", "[encoding 9]" + "zlib", "tight", "[encoding 8]", "[encoding 9]", "[encoding 10]", + "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", +#ifdef BACKCHANNEL + "BackChannel", +#else + "[encoding 15]", +#endif + "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" }; diff --git a/zrle.cc b/zrle.cc new file mode 100644 index 0000000..7839677 --- /dev/null +++ b/zrle.cc @@ -0,0 +1,180 @@ +// +// Copyright (C) 2002 RealVNC Ltd. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// zrle.cc +// +// Routines to implement Zlib Run-length Encoding (ZRLE). +// + +#include +extern "C" { +#include "rfb.h" +} +#include +#include + + +#define GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf) \ + char *fbptr = (cl->screen->frameBuffer \ + + (cl->screen->paddedWidthInBytes * ty) \ + + (tx * (cl->screen->bitsPerPixel / 8))); \ + \ + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,\ + &cl->format, fbptr, (char*)buf, \ + cl->screen->paddedWidthInBytes, tw, th); + +#define EXTRA_ARGS , rfbClientPtr cl + +#define BPP 8 +#include +#undef BPP +#define BPP 16 +#include +#undef BPP +#define BPP 32 +#include +#define CPIXEL 24A +#include +#undef CPIXEL +#define CPIXEL 24B +#include +#undef CPIXEL +#undef BPP + + +/* + * zrleBeforeBuf contains pixel data in the client's format. It must be at + * least one pixel bigger than the largest tile of pixel data, since the + * ZRLE encoding algorithm writes to the position one past the end of the pixel + * data. + */ + +static char zrleBeforeBuf[rfbZRLETileWidth * rfbZRLETileHeight * 4 + 4]; + +static rdr::MemOutStream mos; + + +/* + * rfbSendRectEncodingZRLE - send a given rectangle using ZRLE encoding. + */ + + +Bool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h) +{ + if (!cl->zrleData) cl->zrleData = new rdr::ZlibOutStream; + rdr::ZlibOutStream* zos = (rdr::ZlibOutStream*)cl->zrleData; + mos.clear(); + + switch (cl->format.bitsPerPixel) { + + case 8: + zrleEncode8( x, y, w, h, &mos, zos, zrleBeforeBuf, cl); + break; + + case 16: + zrleEncode16(x, y, w, h, &mos, zos, zrleBeforeBuf, cl); + break; + + case 32: + bool fitsInLS3Bytes + = ((cl->format.redMax << cl->format.redShift) < (1<<24) && + (cl->format.greenMax << cl->format.greenShift) < (1<<24) && + (cl->format.blueMax << cl->format.blueShift) < (1<<24)); + + bool fitsInMS3Bytes = (cl->format.redShift > 7 && + cl->format.greenShift > 7 && + cl->format.blueShift > 7); + + if ((fitsInLS3Bytes && !cl->format.bigEndian) || + (fitsInMS3Bytes && cl->format.bigEndian)) + { + zrleEncode24A(x, y, w, h, &mos, zos, zrleBeforeBuf, cl); + } + else if ((fitsInLS3Bytes && cl->format.bigEndian) || + (fitsInMS3Bytes && !cl->format.bigEndian)) + { + zrleEncode24B(x, y, w, h, &mos, zos, zrleBeforeBuf, cl); + } + else + { + zrleEncode32(x, y, w, h, &mos, zos, zrleBeforeBuf, cl); + } + break; + } + + cl->rfbRectanglesSent[rfbEncodingZRLE]++; + cl->rfbBytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbZRLEHeader + mos.length()); + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rfbFramebufferUpdateRectHeader rect; + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingZRLE); + + memcpy(cl->updateBuf+cl->ublen, (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + rfbZRLEHeader hdr; + + hdr.length = Swap32IfLE(mos.length()); + + memcpy(cl->updateBuf+cl->ublen, (char *)&hdr, sz_rfbZRLEHeader); + cl->ublen += sz_rfbZRLEHeader; + + // copy into updateBuf and send from there. Maybe should send directly? + + for (int i = 0; i < mos.length();) { + + int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; + + if (i + bytesToCopy > mos.length()) { + bytesToCopy = mos.length() - i; + } + + memcpy(cl->updateBuf+cl->ublen, (CARD8*)mos.data() + i, bytesToCopy); + + cl->ublen += bytesToCopy; + i += bytesToCopy; + + if (cl->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + +void FreeZrleData(rfbClientPtr cl) +{ + delete (rdr::ZlibOutStream*)cl->zrleData; +} + diff --git a/zrleDecode.h b/zrleDecode.h new file mode 100644 index 0000000..8ddb5c8 --- /dev/null +++ b/zrleDecode.h @@ -0,0 +1,241 @@ +// +// Copyright (C) 2002 RealVNC Ltd. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. + +// +// zrleDecode.h - zrle decoding function. +// +// Before including this file, you must define a number of CPP macros. +// +// BPP should be 8, 16 or 32 depending on the bits per pixel. +// FILL_RECT +// IMAGE_RECT + +#include +#include +#include + +using namespace rdr; + +/* __RFB_CONCAT2 concatenates its two arguments. __RFB_CONCAT2E does the same + but also expands its arguments if they are macros */ + +#ifndef __RFB_CONCAT2E +#define __RFB_CONCAT2(a,b) a##b +#define __RFB_CONCAT2E(a,b) __RFB_CONCAT2(a,b) +#endif + +#ifdef CPIXEL +#define PIXEL_T __RFB_CONCAT2E(rdr::U,BPP) +#define READ_PIXEL __RFB_CONCAT2E(readOpaque,CPIXEL) +#define ZRLE_DECODE_BPP __RFB_CONCAT2E(zrleDecode,CPIXEL) +#else +#define PIXEL_T __RFB_CONCAT2E(rdr::U,BPP) +#define READ_PIXEL __RFB_CONCAT2E(readOpaque,BPP) +#define ZRLE_DECODE_BPP __RFB_CONCAT2E(zrleDecode,BPP) +#endif + +void ZRLE_DECODE_BPP (int x, int y, int w, int h, rdr::InStream* is, + rdr::ZlibInStream* zis, PIXEL_T* buf) +{ + int length = is->readU32(); + zis->setUnderlying(is, length); + + for (int ty = y; ty < y+h; ty += rfbZRLETileHeight) { + int th = rfbZRLETileHeight; + if (th > y+h-ty) th = y+h-ty; + for (int tx = x; tx < x+w; tx += rfbZRLETileWidth) { + int tw = rfbZRLETileWidth; + if (tw > x+w-tx) tw = x+w-tx; + + int mode = zis->readU8(); + bool rle = mode & 128; + int palSize = mode & 127; + PIXEL_T palette[128]; + + // fprintf(stderr,"rle %d palSize %d\n",rle,palSize); + + for (int i = 0; i < palSize; i++) { + palette[i] = zis->READ_PIXEL(); + } + + if (palSize == 1) { + PIXEL_T pix = palette[0]; + FILL_RECT(tx,ty,tw,th,pix); + continue; + } + + if (!rle) { + if (palSize == 0) { + + // raw + +#ifdef CPIXEL + for (PIXEL_T* ptr = buf; ptr < buf+tw*th; ptr++) { + *ptr = zis->READ_PIXEL(); + } +#else + zis->readBytes(buf, tw * th * (BPP / 8)); +#endif + + } else { + + // packed pixels + int bppp = ((palSize > 16) ? 8 : + ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); + + PIXEL_T* ptr = buf; + + for (int i = 0; i < th; i++) { + PIXEL_T* eol = ptr + tw; + U8 byte = 0; + U8 nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + byte = zis->readU8(); + nbits = 8; + } + nbits -= bppp; + U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127; + *ptr++ = palette[index]; + } + } + } + +#ifdef FAVOUR_FILL_RECT + //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",tw,th,tx,ty); + IMAGE_RECT(tx,ty,tw,th,buf); +#endif + + } else { + + if (palSize == 0) { + + // plain RLE + + PIXEL_T* ptr = buf; + PIXEL_T* end = ptr + th * tw; + while (ptr < end) { + PIXEL_T pix = zis->READ_PIXEL(); + int len = 1; + int b; + do { + b = zis->readU8(); + len += b; + } while (b == 255); + + assert(len <= end - ptr); + +#ifdef FAVOUR_FILL_RECT + int i = ptr - buf; + ptr += len; + + int runX = i % tw; + int runY = i / tw; + + if (runX + len > tw) { + if (runX != 0) { + FILL_RECT(tx+runX, ty+runY, tw-runX, 1, pix); + len -= tw-runX; + runX = 0; + runY++; + } + + if (len > tw) { + FILL_RECT(tx, ty+runY, tw, len/tw, pix); + runY += len / tw; + len = len % tw; + } + } + + if (len != 0) { + FILL_RECT(tx+runX, ty+runY, len, 1, pix); + } +#else + while (len-- > 0) *ptr++ = pix; +#endif + + } + } else { + + // palette RLE + + PIXEL_T* ptr = buf; + PIXEL_T* end = ptr + th * tw; + while (ptr < end) { + int index = zis->readU8(); + int len = 1; + if (index & 128) { + int b; + do { + b = zis->readU8(); + len += b; + } while (b == 255); + + assert(len <= end - ptr); + } + + index &= 127; + + PIXEL_T pix = palette[index]; + +#ifdef FAVOUR_FILL_RECT + int i = ptr - buf; + ptr += len; + + int runX = i % tw; + int runY = i / tw; + + if (runX + len > tw) { + if (runX != 0) { + FILL_RECT(tx+runX, ty+runY, tw-runX, 1, pix); + len -= tw-runX; + runX = 0; + runY++; + } + + if (len > tw) { + FILL_RECT(tx, ty+runY, tw, len/tw, pix); + runY += len / tw; + len = len % tw; + } + } + + if (len != 0) { + FILL_RECT(tx+runX, ty+runY, len, 1, pix); + } +#else + while (len-- > 0) *ptr++ = pix; +#endif + } + } + } + +#ifndef FAVOUR_FILL_RECT + //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",tw,th,tx,ty); + IMAGE_RECT(tx,ty,tw,th,buf); +#endif + } + } + + zis->reset(); +} + +#undef ZRLE_DECODE_BPP +#undef READ_PIXEL +#undef PIXEL_T diff --git a/zrleEncode.h b/zrleEncode.h new file mode 100644 index 0000000..6d58a5a --- /dev/null +++ b/zrleEncode.h @@ -0,0 +1,310 @@ +// +// Copyright (C) 2002 RealVNC Ltd. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. + +// +// zrleEncode.h - zrle encoding function. +// +// Before including this file, you must define a number of CPP macros. +// +// BPP should be 8, 16 or 32 depending on the bits per pixel. +// GET_IMAGE_INTO_BUF should be some code which gets a rectangle of pixel data +// into the given buffer. EXTRA_ARGS can be defined to pass any other +// arguments needed by GET_IMAGE_INTO_BUF. +// +// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel +// bigger than the largest tile of pixel data, since the ZRLE encoding +// algorithm writes to the position one past the end of the pixel data. +// + +#include +#include + +using namespace rdr; + +/* __RFB_CONCAT2 concatenates its two arguments. __RFB_CONCAT2E does the same + but also expands its arguments if they are macros */ + +#ifndef __RFB_CONCAT2E +#define __RFB_CONCAT2(a,b) a##b +#define __RFB_CONCAT2E(a,b) __RFB_CONCAT2(a,b) +#endif + +#ifdef CPIXEL +#define PIXEL_T __RFB_CONCAT2E(rdr::U,BPP) +#define WRITE_PIXEL __RFB_CONCAT2E(writeOpaque,CPIXEL) +#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,CPIXEL) +#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,CPIXEL) +#define BPPOUT 24 +#else +#define PIXEL_T __RFB_CONCAT2E(rdr::U,BPP) +#define WRITE_PIXEL __RFB_CONCAT2E(writeOpaque,BPP) +#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,BPP) +#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,BPP) +#define BPPOUT BPP +#endif + +#ifndef ZRLE_ONCE +#define ZRLE_ONCE +static const int bitsPerPackedPixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +// The PaletteHelper class helps us build up the palette from pixel data by +// storing a reverse index using a simple hash-table + +class PaletteHelper { +public: + enum { MAX_SIZE = 127 }; + + PaletteHelper() + { + memset(index, 255, sizeof(index)); + size = 0; + } + + inline int hash(rdr::U32 pix) + { + return (pix ^ (pix >> 17)) & 4095; + } + + inline void insert(rdr::U32 pix) + { + if (size < MAX_SIZE) { + int i = hash(pix); + while (index[i] != 255 && key[i] != pix) + i++; + if (index[i] != 255) return; + + index[i] = size; + key[i] = pix; + palette[size] = pix; + } + size++; + } + + inline int lookup(rdr::U32 pix) + { + assert(size <= MAX_SIZE); + int i = hash(pix); + while (index[i] != 255 && key[i] != pix) + i++; + if (index[i] != 255) return index[i]; + return -1; + } + + rdr::U32 palette[MAX_SIZE]; + rdr::U8 index[4096+MAX_SIZE]; + rdr::U32 key[4096+MAX_SIZE]; + int size; +}; +#endif + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os); + +void ZRLE_ENCODE (int x, int y, int w, int h, rdr::OutStream* os, + rdr::ZlibOutStream* zos, void* buf + EXTRA_ARGS + ) +{ + zos->setUnderlying(os); + + for (int ty = y; ty < y+h; ty += rfbZRLETileHeight) { + int th = rfbZRLETileHeight; + if (th > y+h-ty) th = y+h-ty; + for (int tx = x; tx < x+w; tx += rfbZRLETileWidth) { + int tw = rfbZRLETileWidth; + if (tw > x+w-tx) tw = x+w-tx; + + GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf); + + ZRLE_ENCODE_TILE((PIXEL_T*)buf, tw, th, zos); + } + } + zos->flush(); +} + + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os) +{ + // First find the palette and the number of runs + + PaletteHelper ph; + + int runs = 0; + int singlePixels = 0; + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + h * w; + *end = ~*(end-1); // one past the end is different so the while loop ends + + while (ptr < end) { + PIXEL_T pix = *ptr; + if (*++ptr != pix) { + singlePixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + ph.insert(pix); + } + + //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n", + // runs, singlePixels, ph.size); + + // Solid tile is a special case + + if (ph.size == 1) { + os->writeU8(1); + os->WRITE_PIXEL(ph.palette[0]); + return; + } + + // Try to work out whether to use RLE and/or a palette. We do this by + // estimating the number of bytes which will be generated and picking the + // method which results in the fewest bytes. Of course this may not result + // in the fewest bytes after compression... + + bool useRle = false; + bool usePalette = false; + + int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw + + int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels); + + if (plainRleBytes < estimatedBytes) { + useRle = true; + estimatedBytes = plainRleBytes; + } + + if (ph.size < 128) { + int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels; + + if (paletteRleBytes < estimatedBytes) { + useRle = true; + usePalette = true; + estimatedBytes = paletteRleBytes; + } + + if (ph.size < 17) { + int packedBytes = ((BPPOUT/8) * ph.size + + w * h * bitsPerPackedPixel[ph.size-1] / 8); + + if (packedBytes < estimatedBytes) { + useRle = false; + usePalette = true; + estimatedBytes = packedBytes; + } + } + } + + if (!usePalette) ph.size = 0; + + os->writeU8((useRle ? 128 : 0) | ph.size); + + for (int i = 0; i < ph.size; i++) { + os->WRITE_PIXEL(ph.palette[i]); + } + + if (useRle) { + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + w * h; + PIXEL_T* runStart; + PIXEL_T pix; + while (ptr < end) { + runStart = ptr; + pix = *ptr++; + while (*ptr == pix && ptr < end) + ptr++; + int len = ptr - runStart; + if (len <= 2 && usePalette) { + int index = ph.lookup(pix); + if (len == 2) + os->writeU8(index); + os->writeU8(index); + continue; + } + if (usePalette) { + int index = ph.lookup(pix); + os->writeU8(index | 128); + } else { + os->WRITE_PIXEL(pix); + } + len -= 1; + while (len >= 255) { + os->writeU8(255); + len -= 255; + } + os->writeU8(len); + } + + } else { + + // no RLE + + if (usePalette) { + + // packed pixels + + assert (ph.size < 17); + + int bppp = bitsPerPackedPixel[ph.size-1]; + + PIXEL_T* ptr = data; + + for (int i = 0; i < h; i++) { + U8 nbits = 0; + U8 byte = 0; + + PIXEL_T* eol = ptr + w; + + while (ptr < eol) { + PIXEL_T pix = *ptr++; + U8 index = ph.lookup(pix); + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + os->writeU8(byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + os->writeU8(byte); + } + } + } else { + + // raw + +#ifdef CPIXEL + for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) { + os->WRITE_PIXEL(*ptr); + } +#else + os->writeBytes(data, w*h*(BPP/8)); +#endif + } + } +} + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef BPPOUT