summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile28
-rw-r--r--corre.c15
-rw-r--r--cvs_update_anonymously3
-rw-r--r--httpd.c39
-rw-r--r--main.c1
-rw-r--r--rfb.h10
-rw-r--r--rfbproto.h16
-rw-r--r--rfbserver.c23
-rw-r--r--stats.c10
-rw-r--r--zrle.cc180
-rw-r--r--zrleDecode.h241
-rw-r--r--zrleEncode.h310
12 files changed, 836 insertions, 40 deletions
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 <mcguirk@incompleteness.net>.
* 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" \
"<HEAD><TITLE>File Not Found</TITLE></HEAD>\n" \
"<BODY><H1>File Not Found</H1></BODY>\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" \
"<HEAD><TITLE>Invalid Request</TITLE></HEAD>\n" \
"<BODY><H1>Invalid request</H1></BODY>\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 <mcguirk@incompleteness.net>.
* 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 <mcguirk@incompleteness.net>.
* 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) {
@@ -787,6 +793,15 @@ rfbProcessClientNormalMessage(cl)
}
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 &&
enc <= (CARD32)rfbEncodingCompressLevel9 ) {
@@ -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 <mcguirk@incompleteness.net>.
* 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 <stdio.h>
+extern "C" {
+#include "rfb.h"
+}
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+
+
+#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 <zrleEncode.h>
+#undef BPP
+#define BPP 16
+#include <zrleEncode.h>
+#undef BPP
+#define BPP 32
+#include <zrleEncode.h>
+#define CPIXEL 24A
+#include <zrleEncode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <zrleEncode.h>
+#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 <rdr/ZlibInStream.h>
+#include <rdr/InStream.h>
+#include <assert.h>
+
+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 <rdr/OutStream.h>
+#include <assert.h>
+
+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