Commit 9edf7e56 authored by markmc's avatar markmc

2003-09-11 Mark McLoughlin <mark@skynet.ie>

        * rdr/Exception.h, rdr/FdInStream.cxx, rdr/FdInStream.h,
          rdr/FdOutStream.cxx, rdr/FdOutStream.h, rdr/FixedMemOutStream.h,
          rdr/InStream.cxx, rdr/InStream.h, rdr/MemInStream.h,
          rdr/MemOutStream.h, rdr/NullOutStream.cxx, rdr/NullOutStream.h,
          rdr/OutStream.h, rdr/ZlibInStream.cxx, rdr/ZlibInStream.h,
          rdr/ZlibOutStream.cxx, rdr/ZlibOutStream.h, rdr/types.h,
          zrle.cxx, zrleDecode.h, zrleEncode.h: remove original
        C++ ZRLE implementation. Its been ported to C.

        * NEWS: copy the existing ChangeLog to here and make
        this a more detailed ChangeLog.
parent 937b906f
Mark sent me patches to no longer need C++ for ZRLE encoding! 2003-09-11 Mark McLoughlin <mark@skynet.ie>
added --disable-cxx Option for configure
x11vnc changes from Karl Runge: * rdr/Exception.h, rdr/FdInStream.cxx, rdr/FdInStream.h,
- Changed all those whimpy printf(...)'s into manly fprintf(stdxxx,...)'s. rdr/FdOutStream.cxx, rdr/FdOutStream.h, rdr/FixedMemOutStream.h,
rdr/InStream.cxx, rdr/InStream.h, rdr/MemInStream.h,
- Added -q switch (quiet) to suppress printing all the debug-looking output. rdr/MemOutStream.h, rdr/NullOutStream.cxx, rdr/NullOutStream.h,
rdr/OutStream.h, rdr/ZlibInStream.cxx, rdr/ZlibInStream.h,
- Added -bg switch to fork into background after everything is set up. rdr/ZlibOutStream.cxx, rdr/ZlibOutStream.h, rdr/types.h,
(checks for LIBVNCSERVER_HAVE_FORK and LIBVNCSERVER_HAVE_SETSID) zrle.cxx, zrleDecode.h, zrleEncode.h: remove original
C++ ZRLE implementation. Its been ported to C.
- Print this string out to stdout: 'PORT=XXXX' (usually XXXX = 5900).
Combining with -bg, easy to write a ssh/rsh wrapper with something like: * NEWS: copy the existing ChangeLog to here and make
port=`ssh $host "x11vnc -bg .."` then run vncviewer based on $port output. this a more detailed ChangeLog.
(tunneling the vnc traffic thru ssh a bit more messy, but doable)
- Quite a bit of code to be more careful when doing 8bpp indexed color, e.g.
not assuming NCOLORS is 256, handling 8bit TrueColor and Direct Color, etc
(I did all this probably in April, not quite clear in my mind now, but
I did test it out a fair amount on my old Sparcstation 20 wrt a user's
questions).
introduce rfbErr for Errors (Erik)
make rfbLog overridable (suggested by Erik)
don't reutrn on EINTR in WriteExact()/ReadExact() (suggested by Erik)
use AX_PREFIX_CONFIG_H to prefix constants in config.h to avoid
name clashes (also suggested by Erik)
transformed Bool, KeySym, Pixel to rfbBool, rfbKeySym, rfbPixel
(as suggested by Erik)
purged exit() calls (suggested by Erik)
fixed bug with maxRectsPerUpdate and Tight Encoding (these are incompatible)
checked sync with TightVNC 1.2.8:
viewonly/full passwords; if given a list, only the first is a full one
vncRandomBytes is a little more secure now
new weights for tight encoding
checked sync with RealVNC 3.3.7
introduced maxRectsPerUpdate
added first alpha version of LibVNCClient
added simple and simple15 example (really simple examples)
finally got around to fix configure in CVS
long standing http bug (.jar was sent twice) fixed by a friend of Karl named Mike
http options in cargs
when closing a client and no longer listening for new ones, don't crash
fixed a bug with ClientConnectionGone
endianness is checked at configure time
fixed a bug that prevented the first client from being closed
fixed that annoying "libvncserver-config --link" bug
make rfbReverseByte public (for rdp2vnc)
fixed compilation on OS X, IRIX, Solaris
install target for headers is now ${prefix}/include/rfb ("#include <rfb/rfb.h>")
renamed "sraRegion.h" to "rfbregion.h"
CARD{8,16,32} are more standard uint{8,16,32}_t now
fixed LinuxVNC colour handling
fixed a bug with pthreads where the connection was not closed
moved vncterm to main package (LinuxVNC included)
portability fixes (IRIX, OSX, Solaris)
more portable way to determine endianness and types of a given size
through autoconf based methods
0.5
rpm packaging through autoconf
autoconf'ed the whole package (including optional support for zlib,
pthreads and libjpeg as well as zrle/c++)
moved appropriate files to contrib/ and examples/ respectively
fixed long standing cargs bug (Justin "Zippy" Dearing)
Even better x11vnc from Karl J. Runge! (supports different kbd layouts of
client/server)
Better x11vnc from Karl J. Runge!
fixed severe bug (Const Kaplinsky)
got patch from Const Kaplisnky with CursorPosUpdate encoding and some Docs
sync'ed with newest RealVNC (ZRLE encoding)
a HTTP request for tunnelling was added (to fool strict web proxies)
sync'ed with TightVNC 1.2.5
0.4
support for NewFB from Const Kaplinsky
memory leaks squashed (localtime pseudo leak is still there :-)
small improvements for OSXvnc (still not working correctly)
synced with TightVNC 1.2.3
solaris compile cleanups
many x11vnc improvements
added backchannel, an encoding which needs special clients to pass
arbitrary data to the client
changes from Tim Jansen regarding multi threading and client blocking
as well as C++ compliancy
x11vnc can be controlled by starting again with special options if compiling
with LOCAL_CONTROL defined
0.3
added x11vnc, a x0rfbserver clone
regard deferUpdateTime in processEvents, if usec<0
initialize deferUpdateTime (memory "leak"!)
changed command line handling (arguments are parsed and then removed)
added very simple example: zippy
added rfbDrawLine, rfbDrawPixel
0.2
inserted a deferUpdate mechanism (X11 independent).
removed deletion of requestedRegion
added rfbLoadConsoleFont
fixed font colour handling.
added rfbSelectBox
added rfbDrawCharWithClip to allow for clipping and a background colour.
fixed font colours
added rfbFillRect
added IO function to check password.
rfbNewClient now sets the socket in the fd_set (for the select() call)
when compiling the library with HAVE_PTHREADS and an application
which includes "rfb.h" without, the structures got mixed up.
So, the pthreads section is now always at the end, and also
you get a linker error for rfbInitServer when using two different
pthread setups.
fixed two deadlocks: when setting a cursor and when using CopyRect
fixed CopyRect when copying modified regions (they lost the modified
property)
WIN32 target compiles and works for example :-)
fixed CopyRect (was using the wrong order of rectangles...)
should also work with pthreads, because copyrects are
always sent immediately (so that two consecutive copy rects
cannot conflict).
changed rfbUndrawCursor(rfbClientPtr) to (rfbScreenInfoPtr), because
this makes more sense!
flag backgroundLoop in rfbScreenInfo (if having pthreads)
CopyRect & CopyRegion were implemented.
if you use a rfbDoCopyR* function, it copies the data in the
framebuffer. If you prefer to do that yourself, use rfbScheduleCopyR*
instead; this doesn't modify the frameBuffer.
added flag to optionally not send XCursor updates, but only RichCursor,
or if that is not possible, fall back to server side cursor.
This is useful if your cursor has many nice colours.
fixed java viewer on server side:
SendCursorUpdate would send data even before the client pixel format
was set, but the java applet doesn't like the server's format.
fixed two pthread issues:
rfbSendFramebuffer was sent by a ProcessClientMessage function
(unprotected by updateMutex).
cursor coordinates were set without protection by cursorMutex
source is now equivalent to TridiaVNC 1.2.1
pthreads now work (use iterators!)
cursors are supported (rfbSetCursor automatically undraws cursor)
support for 3 bytes/pixel (slow!)
server side colourmap support
fixed rfbCloseClient not to close the connection (pthreads!)
this is done lazily (and with proper signalling).
cleaned up mac.c (from original OSXvnc); now compiles (untested!)
compiles cleanly on Linux, IRIX, BSD, Apple (Darwin)
fixed prototypes
0.1
rewrote API to use pseudo-methods instead of required functions.
lots of clean up.
Example can show symbols now.
All encodings
HTTP
Question: Why the *&!"/(§ did you change so many things in the API? Mark sent me patches to no longer need C++ for ZRLE encoding!
Answer: It was necessary to make this library slightly less added --disable-cxx Option for configure
painful to use with other libraries. x11vnc changes from Karl Runge:
- Changed all those whimpy printf(...)'s into manly fprintf(stdxxx,...)'s.
Anyway, most changes are just search-and-replace: - Added -q switch (quiet) to suppress printing all the debug-looking output.
#include "rfb.h" -> #include <rfb/rfb.h> - Added -bg switch to fork into background after everything is set up.
CARD8 -> uint8_t (checks for LIBVNCSERVER_HAVE_FORK and LIBVNCSERVER_HAVE_SETSID)
CARD16 -> uint16_t
CARD32 -> uint32_t
Pixel -> rfbPixel
KeySym -> rfbKeySym
Bool -> rfbBool
also, rfbUsage doesn't exit any longer, but returns FALSE - Print this string out to stdout: 'PORT=XXXX' (usually XXXX = 5900).
Combining with -bg, easy to write a ssh/rsh wrapper with something like:
port=`ssh $host "x11vnc -bg .."` then run vncviewer based on $port output.
(tunneling the vnc traffic thru ssh a bit more messy, but doable)
If you used some constants defined in rfbconfig.h, you now - Quite a bit of code to be more careful when doing 8bpp indexed color, e.g.
have to prefix LIBVNCSERVER_ to the constants. not assuming NCOLORS is 256, handling 8bit TrueColor and Direct Color, etc
(I did all this probably in April, not quite clear in my mind now, but
Upcoming 0.6 release! I did test it out a fair amount on my old Sparcstation 20 wrt a user's
questions).
lots of bugs fixed. introduce rfbErr for Errors (Erik)
make rfbLog overridable (suggested by Erik)
don't reutrn on EINTR in WriteExact()/ReadExact() (suggested by Erik)
Version 5.0: use AX_PREFIX_CONFIG_H to prefix constants in config.h to avoid
name clashes (also suggested by Erik)
The library just got autoconf && automake support as well as a real install transformed Bool, KeySym, Pixel to rfbBool, rfbKeySym, rfbPixel
target! (as suggested by Erik)
purged exit() calls (suggested by Erik)
x11vnc was much improved thanks to Karl Runge! fixed bug with maxRectsPerUpdate and Tight Encoding (these are incompatible)
checked sync with TightVNC 1.2.8:
CursorPosUpdate encoding supported thanks to Const Kaplinsky! viewonly/full passwords; if given a list, only the first is a full one
vncRandomBytes is a little more secure now
ZRLE encoding, if you have a c++ compiler! new weights for tight encoding
checked sync with RealVNC 3.3.7
HTTP now optionally handles HTTP proxy functions to connect to the same introduced maxRectsPerUpdate
server only. added first alpha version of LibVNCClient
added simple and simple15 example (really simple examples)
Of course, a lot of bugs fixed since last release... finally got around to fix configure in CVS
long standing http bug (.jar was sent twice) fixed by a friend of Karl named Mike
http options in cargs
when closing a client and no longer listening for new ones, don't crash
fixed a bug with ClientConnectionGone
endianness is checked at configure time
fixed a bug that prevented the first client from being closed
fixed that annoying "libvncserver-config --link" bug
make rfbReverseByte public (for rdp2vnc)
fixed compilation on OS X, IRIX, Solaris
install target for headers is now ${prefix}/include/rfb ("#include <rfb/rfb.h>")
renamed "sraRegion.h" to "rfbregion.h"
CARD{8,16,32} are more standard uint{8,16,32}_t now
fixed LinuxVNC colour handling
fixed a bug with pthreads where the connection was not closed
moved vncterm to main package (LinuxVNC included)
portability fixes (IRIX, OSX, Solaris)
more portable way to determine endianness and types of a given size
through autoconf based methods
0.5
rpm packaging through autoconf
autoconf'ed the whole package (including optional support for zlib,
pthreads and libjpeg as well as zrle/c++)
moved appropriate files to contrib/ and examples/ respectively
fixed long standing cargs bug (Justin "Zippy" Dearing)
Even better x11vnc from Karl J. Runge! (supports different kbd layouts of
client/server)
Better x11vnc from Karl J. Runge!
fixed severe bug (Const Kaplinsky)
got patch from Const Kaplisnky with CursorPosUpdate encoding and some Docs
sync'ed with newest RealVNC (ZRLE encoding)
a HTTP request for tunnelling was added (to fool strict web proxies)
sync'ed with TightVNC 1.2.5
0.4
support for NewFB from Const Kaplinsky
memory leaks squashed (localtime pseudo leak is still there :-)
small improvements for OSXvnc (still not working correctly)
synced with TightVNC 1.2.3
solaris compile cleanups
many x11vnc improvements
added backchannel, an encoding which needs special clients to pass
arbitrary data to the client
changes from Tim Jansen regarding multi threading and client blocking
as well as C++ compliancy
x11vnc can be controlled by starting again with special options if compiling
with LOCAL_CONTROL defined
0.3
added x11vnc, a x0rfbserver clone
regard deferUpdateTime in processEvents, if usec<0
initialize deferUpdateTime (memory "leak"!)
changed command line handling (arguments are parsed and then removed)
added very simple example: zippy
added rfbDrawLine, rfbDrawPixel
0.2
inserted a deferUpdate mechanism (X11 independent).
removed deletion of requestedRegion
added rfbLoadConsoleFont
fixed font colour handling.
added rfbSelectBox
added rfbDrawCharWithClip to allow for clipping and a background colour.
fixed font colours
added rfbFillRect
added IO function to check password.
rfbNewClient now sets the socket in the fd_set (for the select() call)
when compiling the library with HAVE_PTHREADS and an application
which includes "rfb.h" without, the structures got mixed up.
So, the pthreads section is now always at the end, and also
you get a linker error for rfbInitServer when using two different
pthread setups.
fixed two deadlocks: when setting a cursor and when using CopyRect
fixed CopyRect when copying modified regions (they lost the modified
property)
WIN32 target compiles and works for example :-)
fixed CopyRect (was using the wrong order of rectangles...)
should also work with pthreads, because copyrects are
always sent immediately (so that two consecutive copy rects
cannot conflict).
changed rfbUndrawCursor(rfbClientPtr) to (rfbScreenInfoPtr), because
this makes more sense!
flag backgroundLoop in rfbScreenInfo (if having pthreads)
CopyRect & CopyRegion were implemented.
if you use a rfbDoCopyR* function, it copies the data in the
framebuffer. If you prefer to do that yourself, use rfbScheduleCopyR*
instead; this doesn't modify the frameBuffer.
added flag to optionally not send XCursor updates, but only RichCursor,
or if that is not possible, fall back to server side cursor.
This is useful if your cursor has many nice colours.
fixed java viewer on server side:
SendCursorUpdate would send data even before the client pixel format
was set, but the java applet doesn't like the server's format.
fixed two pthread issues:
rfbSendFramebuffer was sent by a ProcessClientMessage function
(unprotected by updateMutex).
cursor coordinates were set without protection by cursorMutex
source is now equivalent to TridiaVNC 1.2.1
pthreads now work (use iterators!)
cursors are supported (rfbSetCursor automatically undraws cursor)
support for 3 bytes/pixel (slow!)
server side colourmap support
fixed rfbCloseClient not to close the connection (pthreads!)
this is done lazily (and with proper signalling).
cleaned up mac.c (from original OSXvnc); now compiles (untested!)
compiles cleanly on Linux, IRIX, BSD, Apple (Darwin)
fixed prototypes
0.1
rewrote API to use pseudo-methods instead of required functions.
lots of clean up.
Example can show symbols now.
All encodings
HTTP
//
// 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.
#ifndef __RDR_EXCEPTION_H__
#define __RDR_EXCEPTION_H__
#include <stdio.h>
#include <string.h>
namespace rdr {
struct Exception {
enum { len = 256 };
char str_[len];
Exception(const char* s=0, const char* e="rdr::Exception") {
str_[0] = 0;
strncat(str_, e, len-1);
if (s) {
strncat(str_, ": ", len-1-strlen(str_));
strncat(str_, s, len-1-strlen(str_));
}
}
virtual const char* str() const { return str_; }
};
struct SystemException : public Exception {
int err;
SystemException(const char* s, int err_) : err(err_) {
str_[0] = 0;
strncat(str_, "rdr::SystemException: ", len-1);
strncat(str_, s, len-1-strlen(str_));
strncat(str_, ": ", len-1-strlen(str_));
strncat(str_, strerror(err), len-1-strlen(str_));
strncat(str_, " (", len-1-strlen(str_));
char buf[20];
sprintf(buf,"%d",err);
strncat(str_, buf, len-1-strlen(str_));
strncat(str_, ")", len-1-strlen(str_));
}
};
struct TimedOut : public Exception {
TimedOut(const char* s=0) : Exception(s,"rdr::TimedOut") {}
};
struct EndOfStream : public Exception {
EndOfStream(const char* s=0) : Exception(s,"rdr::EndOfStream") {}
};
struct FrameException : public Exception {
FrameException(const char* s=0) : Exception(s,"rdr::FrameException") {}
};
}
#endif
//
// 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.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock.h>
#include <sys/timeb.h>
#define read(s,b,l) recv(s,(char*)b,l,0)
#undef errno
#define errno WSAGetLastError()
#else
#include <unistd.h>
#include <sys/time.h>
#endif
// XXX should use autoconf HAVE_SYS_SELECT_H
#ifdef _AIX
#include <sys/select.h>
#endif
#include <rdr/FdInStream.h>
#include <rdr/Exception.h>
extern "C" {
extern void rfbLog(const char *format, ...);
}
using namespace rdr;
enum { DEFAULT_BUF_SIZE = 8192,
MIN_BULK_SIZE = 1024 };
FdInStream::FdInStream(int fd_, int timeout_, int bufSize_)
: fd(fd_), timeout(timeout_), blockCallback(0), blockCallbackArg(0),
timing(false), timeWaitedIn100us(5), timedKbits(0),
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
{
ptr = end = start = new U8[bufSize];
}
FdInStream::FdInStream(int fd_, void (*blockCallback_)(void*),
void* blockCallbackArg_, int bufSize_)
: fd(fd_), timeout(0), blockCallback(blockCallback_),
blockCallbackArg(blockCallbackArg_),
timing(false), timeWaitedIn100us(5), timedKbits(0),
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
{
ptr = end = start = new U8[bufSize];
}
FdInStream::~FdInStream()
{
delete [] start;
}
int FdInStream::pos()
{
return offset + ptr - start;
}
void FdInStream::readBytes(void* data, int length)
{
if (length < MIN_BULK_SIZE) {
InStream::readBytes(data, length);
return;
}
U8* dataPtr = (U8*)data;
int n = end - ptr;
if (n > length) n = length;
memcpy(dataPtr, ptr, n);
dataPtr += n;
length -= n;
ptr += n;
while (length > 0) {
n = readWithTimeoutOrCallback(dataPtr, length);
dataPtr += n;
length -= n;
offset += n;
}
}
int FdInStream::overrun(int itemSize, int nItems)
{
if (itemSize > bufSize)
throw Exception("FdInStream overrun: max itemSize exceeded");
if (end - ptr != 0)
memmove(start, ptr, end - ptr);
offset += ptr - start;
end -= ptr - start;
ptr = start;
while (end < start + itemSize) {
int n = readWithTimeoutOrCallback((U8*)end, start + bufSize - end);
end += n;
}
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
int FdInStream::checkReadable(int fd, int timeout)
{
while (true) {
fd_set rfds;
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
int n = select(fd+1, &rfds, 0, 0, &tv);
if (n != -1 || errno != EINTR)
return n;
rfbLog("select returned EINTR\n");
}
}
#ifdef _WIN32
static void gettimeofday(struct timeval* tv, void*)
{
LARGE_INTEGER counts, countsPerSec;
static double usecPerCount = 0.0;
if (QueryPerformanceCounter(&counts)) {
if (usecPerCount == 0.0) {
QueryPerformanceFrequency(&countsPerSec);
usecPerCount = 1000000.0 / countsPerSec.QuadPart;
}
LONGLONG usecs = (LONGLONG)(counts.QuadPart * usecPerCount);
tv->tv_usec = (long)(usecs % 1000000);
tv->tv_sec = (long)(usecs / 1000000);
} else {
struct timeb tb;
ftime(&tb);
tv->tv_sec = tb.time;
tv->tv_usec = tb.millitm * 1000;
}
}
#endif
int FdInStream::readWithTimeoutOrCallback(void* buf, int len)
{
struct timeval before, after;
if (timing)
gettimeofday(&before, 0);
int n = checkReadable(fd, timeout);
if (n < 0) throw SystemException("select",errno);
if (n == 0) {
if (timeout) throw TimedOut();
if (blockCallback) (*blockCallback)(blockCallbackArg);
}
while (true) {
n = ::read(fd, buf, len);
if (n != -1 || errno != EINTR)
break;
rfbLog("read returned EINTR\n");
}
if (n < 0) throw SystemException("read",errno);
if (n == 0) throw EndOfStream();
if (timing) {
gettimeofday(&after, 0);
// rfbLog("%d.%06d\n",(after.tv_sec - before.tv_sec),
// (after.tv_usec - before.tv_usec));
int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
(after.tv_usec - before.tv_usec) / 100);
int newKbits = n * 8 / 1000;
// if (newTimeWaited == 0) {
// rfbLog("new kbps infinite t %d k %d\n",
// newTimeWaited, newKbits);
// } else {
// rfbLog("new kbps %d t %d k %d\n",
// newKbits * 10000 / newTimeWaited, newTimeWaited, newKbits);
// }
// limit rate to between 10kbit/s and 40Mbit/s
if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
timeWaitedIn100us += newTimeWaited;
timedKbits += newKbits;
}
return n;
}
void FdInStream::startTiming()
{
timing = true;
// Carry over up to 1s worth of previous rate for smoothing.
if (timeWaitedIn100us > 10000) {
timedKbits = timedKbits * 10000 / timeWaitedIn100us;
timeWaitedIn100us = 10000;
}
}
void FdInStream::stopTiming()
{
timing = false;
if (timeWaitedIn100us < timedKbits/2)
timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
}
unsigned int FdInStream::kbitsPerSecond()
{
// The following calculation will overflow 32-bit arithmetic if we have
// received more than about 50Mbytes (400Mbits) since we started timing, so
// it should be OK for a single RFB update.
return timedKbits * 10000 / timeWaitedIn100us;
}
//
// 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.
//
// FdInStream streams from a file descriptor.
//
#ifndef __RDR_FDINSTREAM_H__
#define __RDR_FDINSTREAM_H__
#include <rdr/InStream.h>
namespace rdr {
class FdInStream : public InStream {
public:
FdInStream(int fd, int timeout=0, int bufSize=0);
FdInStream(int fd, void (*blockCallback)(void*), void* blockCallbackArg=0,
int bufSize=0);
virtual ~FdInStream();
int getFd() { return fd; }
int pos();
void readBytes(void* data, int length);
int bytesInBuf() { return end - ptr; }
void startTiming();
void stopTiming();
unsigned int kbitsPerSecond();
unsigned int timeWaited() { return timeWaitedIn100us; }
protected:
int overrun(int itemSize, int nItems);
private:
int checkReadable(int fd, int timeout);
int readWithTimeoutOrCallback(void* buf, int len);
int fd;
int timeout;
void (*blockCallback)(void*);
void* blockCallbackArg;
bool timing;
unsigned int timeWaitedIn100us;
unsigned int timedKbits;
int bufSize;
int offset;
U8* start;
};
} // end of namespace rdr
#endif
//
// 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.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock.h>
#define write(s,b,l) send(s,(const char*)b,l,0)
#undef errno
#define errno WSAGetLastError()
#else
#include <unistd.h>
#include <sys/time.h>
#endif
#include <rdr/FdOutStream.h>
#include <rdr/Exception.h>
using namespace rdr;
enum { DEFAULT_BUF_SIZE = 16384,
MIN_BULK_SIZE = 1024 };
FdOutStream::FdOutStream(int fd_, int bufSize_)
: fd(fd_), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
{
ptr = start = new U8[bufSize];
end = start + bufSize;
}
FdOutStream::~FdOutStream()
{
try {
flush();
} catch (Exception&) {
}
delete [] start;
}
void FdOutStream::writeBytes(const void* data, int length)
{
if (length < MIN_BULK_SIZE) {
OutStream::writeBytes(data, length);
return;
}
const U8* dataPtr = (const U8*)data;
flush();
while (length > 0) {
int n = write(fd, dataPtr, length);
if (n < 0) throw SystemException("write",errno);
length -= n;
dataPtr += n;
offset += n;
}
}
int FdOutStream::length()
{
return offset + ptr - start;
}
void FdOutStream::flush()
{
U8* sentUpTo = start;
while (sentUpTo < ptr) {
int n = write(fd, (const void*) sentUpTo, ptr - sentUpTo);
if (n < 0) throw SystemException("write",errno);
sentUpTo += n;
offset += n;
}
ptr = start;
}
int FdOutStream::overrun(int itemSize, int nItems)
{
if (itemSize > bufSize)
throw Exception("FdOutStream overrun: max itemSize exceeded");
flush();
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
//
// 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.
//
// FdOutStream streams to a file descriptor.
//
#ifndef __RDR_FDOUTSTREAM_H__
#define __RDR_FDOUTSTREAM_H__
#include <rdr/OutStream.h>
namespace rdr {
class FdOutStream : public OutStream {
public:
FdOutStream(int fd, int bufSize=0);
virtual ~FdOutStream();
int getFd() { return fd; }
void flush();
int length();
void writeBytes(const void* data, int length);
private:
int overrun(int itemSize, int nItems);
int fd;
int bufSize;
int offset;
U8* start;
};
}
#endif
//
// 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.
//
// A FixedMemOutStream writes to a buffer of a fixed length.
//
#ifndef __RDR_FIXEDMEMOUTSTREAM_H__
#define __RDR_FIXEDMEMOUTSTREAM_H__
#include <rdr/OutStream.h>
#include <rdr/Exception.h>
namespace rdr {
class FixedMemOutStream : public OutStream {
public:
FixedMemOutStream(void* buf, int len) {
ptr = start = (U8*)buf;
end = start + len;
}
int length() { return ptr - start; }
void reposition(int pos) { ptr = start + pos; }
const void* data() { return (const void*)start; }
private:
int overrun(int itemSize, int nItems) { throw EndOfStream(); }
U8* start;
};
}
#endif
//
// 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.
#include <rdr/InStream.h>
#include <rdr/Exception.h>
using namespace rdr;
U32 InStream::maxStringLength = 65535;
char* InStream::readString()
{
U32 len = readU32();
if (len > maxStringLength)
throw Exception("InStream max string length exceeded");
char* str = new char[len+1];
readBytes(str, len);
str[len] = 0;
return str;
}
//
// 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.
//
// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
// Representation).
//
#ifndef __RDR_INSTREAM_H__
#define __RDR_INSTREAM_H__
#include <rdr/types.h>
#include <string.h> // for memcpy
namespace rdr {
class InStream {
public:
virtual ~InStream() {}
// check() ensures there is buffer data for at least one item of size
// itemSize bytes. Returns the number of items in the buffer (up to a
// maximum of nItems).
inline int check(int itemSize, int nItems=1)
{
if (ptr + itemSize * nItems > end) {
if (ptr + itemSize > end)
return overrun(itemSize, nItems);
nItems = (end - ptr) / itemSize;
}
return nItems;
}
// readU/SN() methods read unsigned and signed N-bit integers.
inline U8 readU8() { check(1); return *ptr++; }
inline U16 readU16() { check(2); int b0 = *ptr++; int b1 = *ptr++;
return b0 << 8 | b1; }
inline U32 readU32() { check(4); int b0 = *ptr++; int b1 = *ptr++;
int b2 = *ptr++; int b3 = *ptr++;
return b0 << 24 | b1 << 16 | b2 << 8 | b3; }
inline S8 readS8() { return (S8) readU8(); }
inline S16 readS16() { return (S16)readU16(); }
inline S32 readS32() { return (S32)readU32(); }
// readString() reads a string - a U32 length followed by the data.
// Returns a null-terminated string - the caller should delete[] it
// afterwards.
char* readString();
// maxStringLength protects against allocating a huge buffer. Set it
// higher if you need longer strings.
static U32 maxStringLength;
inline void skip(int bytes) {
while (bytes > 0) {
int n = check(1, bytes);
ptr += n;
bytes -= n;
}
}
// readBytes() reads an exact number of bytes.
virtual void readBytes(void* data, int length) {
U8* dataPtr = (U8*)data;
U8* dataEnd = dataPtr + length;
while (dataPtr < dataEnd) {
int n = check(1, dataEnd - dataPtr);
memcpy(dataPtr, ptr, n);
ptr += n;
dataPtr += n;
}
}
// readOpaqueN() reads a quantity without byte-swapping.
inline U8 readOpaque8() { return readU8(); }
inline U16 readOpaque16() { check(2); U16 r; ((U8*)&r)[0] = *ptr++;
((U8*)&r)[1] = *ptr++; return r; }
inline U32 readOpaque32() { check(4); U32 r; ((U8*)&r)[0] = *ptr++;
((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
((U8*)&r)[3] = *ptr++; return r; }
inline U32 readOpaque24A() { check(3); U32 r=0; ((U8*)&r)[0] = *ptr++;
((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++;
return r; }
inline U32 readOpaque24B() { check(3); U32 r=0; ((U8*)&r)[1] = *ptr++;
((U8*)&r)[2] = *ptr++; ((U8*)&r)[3] = *ptr++;
return r; }
// pos() returns the position in the stream.
virtual int pos() = 0;
// getptr(), getend() and setptr() are "dirty" methods which allow you to
// manipulate the buffer directly. This is useful for a stream which is a
// wrapper around an underlying stream.
inline const U8* getptr() const { return ptr; }
inline const U8* getend() const { return end; }
inline void setptr(const U8* p) { ptr = p; }
private:
// overrun() is implemented by a derived class to cope with buffer overrun.
// It ensures there are at least itemSize bytes of buffer data. Returns
// the number of items in the buffer (up to a maximum of nItems). itemSize
// is supposed to be "small" (a few bytes).
virtual int overrun(int itemSize, int nItems) = 0;
protected:
InStream() {}
const U8* ptr;
const U8* end;
};
}
#endif
//
// 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.
#ifndef __RDR_MEMINSTREAM_H__
#define __RDR_MEMINSTREAM_H__
#include <rdr/InStream.h>
#include <rdr/Exception.h>
namespace rdr {
class MemInStream : public InStream {
public:
MemInStream(const void* data, int len) {
ptr = start = (const U8*)data;
end = start + len;
}
int pos() { return ptr - start; }
void reposition(int pos) { ptr = start + pos; }
private:
int overrun(int itemSize, int nItems) { throw EndOfStream(); }
const U8* start;
};
}
#endif
//
// 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.
//
// A MemOutStream grows as needed when data is written to it.
//
#ifndef __RDR_MEMOUTSTREAM_H__
#define __RDR_MEMOUTSTREAM_H__
#include <rdr/OutStream.h>
namespace rdr {
class MemOutStream : public OutStream {
public:
MemOutStream(int len=1024) {
start = ptr = new U8[len];
end = start + len;
}
virtual ~MemOutStream() {
delete [] start;
}
void writeBytes(const void* data, int length) {
check(length);
memcpy(ptr, data, length);
ptr += length;
}
int length() { return ptr - start; }
void clear() { ptr = start; };
void reposition(int pos) { ptr = start + pos; }
// data() returns a pointer to the buffer.
const void* data() { return (const void*)start; }
private:
// overrun() either doubles the buffer or adds enough space for nItems of
// size itemSize bytes.
int overrun(int itemSize, int nItems) {
int len = ptr - start + itemSize * nItems;
if (len < (end - start) * 2)
len = (end - start) * 2;
U8* newStart = new U8[len];
memcpy(newStart, start, ptr - start);
ptr = newStart + (ptr - start);
delete [] start;
start = newStart;
end = newStart + len;
return nItems;
}
U8* start;
};
}
#endif
//
// 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.
#include <rdr/NullOutStream.h>
#include <rdr/Exception.h>
using namespace rdr;
static const int bufferSize = 1024;
NullOutStream::NullOutStream()
: offset(0)
{
start = ptr = new U8[bufferSize];
end = start + bufferSize;
}
NullOutStream::~NullOutStream()
{
delete [] start;
}
int NullOutStream::length()
{
return offset + ptr - start;
}
void NullOutStream::writeBytes(const void* data, int length)
{
offset += length;
}
int NullOutStream::overrun(int itemSize, int nItems)
{
if (itemSize > bufferSize)
throw Exception("NullOutStream overrun: max itemSize exceeded");
offset += ptr - start;
ptr = start;
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
//
// 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.
#ifndef __RDR_NULLOUTSTREAM_H__
#define __RDR_NULLOUTSTREAM_H__
#include <rdr/OutStream.h>
namespace rdr {
class NullOutStream : public OutStream {
public:
NullOutStream();
virtual ~NullOutStream();
int length();
void writeBytes(const void* data, int length);
private:
int overrun(int itemSize, int nItems);
int offset;
U8* start;
};
}
#endif
//
// 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.
//
// rdr::OutStream marshalls data into a buffer stored in RDR (RFB Data
// Representation).
//
#ifndef __RDR_OUTSTREAM_H__
#define __RDR_OUTSTREAM_H__
#include <rdr/types.h>
#include <string.h> // for memcpy
namespace rdr {
class OutStream {
protected:
OutStream() {}
public:
virtual ~OutStream() {}
// check() ensures there is buffer space for at least one item of size
// itemSize bytes. Returns the number of items which fit (up to a maximum
// of nItems).
inline int check(int itemSize, int nItems=1)
{
if (ptr + itemSize * nItems > end) {
if (ptr + itemSize > end)
return overrun(itemSize, nItems);
nItems = (end - ptr) / itemSize;
}
return nItems;
}
// writeU/SN() methods write unsigned and signed N-bit integers.
inline void writeU8( U8 u) { check(1); *ptr++ = u; }
inline void writeU16(U16 u) { check(2); *ptr++ = u >> 8; *ptr++ = (U8)u; }
inline void writeU32(U32 u) { check(4); *ptr++ = u >> 24; *ptr++ = u >> 16;
*ptr++ = u >> 8; *ptr++ = u; }
inline void writeS8( S8 s) { writeU8((U8)s); }
inline void writeS16(S16 s) { writeU16((U16)s); }
inline void writeS32(S32 s) { writeU32((U32)s); }
// writeString() writes a string - a U32 length followed by the data. The
// given string should be null-terminated (but the terminating null is not
// written to the stream).
inline void writeString(const char* str) {
U32 len = strlen(str);
writeU32(len);
writeBytes(str, len);
}
inline void pad(int bytes) {
while (bytes-- > 0) writeU8(0);
}
inline void skip(int bytes) {
while (bytes > 0) {
int n = check(1, bytes);
ptr += n;
bytes -= n;
}
}
// writeBytes() writes an exact number of bytes.
virtual void writeBytes(const void* data, int length) {
const U8* dataPtr = (const U8*)data;
const U8* dataEnd = dataPtr + length;
while (dataPtr < dataEnd) {
int n = check(1, dataEnd - dataPtr);
memcpy(ptr, dataPtr, n);
ptr += n;
dataPtr += n;
}
}
// writeOpaqueN() writes a quantity without byte-swapping.
inline void writeOpaque8( U8 u) { writeU8(u); }
inline void writeOpaque16(U16 u) { check(2); *ptr++ = ((U8*)&u)[0];
*ptr++ = ((U8*)&u)[1]; }
inline void writeOpaque32(U32 u) { check(4); *ptr++ = ((U8*)&u)[0];
*ptr++ = ((U8*)&u)[1];
*ptr++ = ((U8*)&u)[2];
*ptr++ = ((U8*)&u)[3]; }
inline void writeOpaque24A(U32 u) { check(3); *ptr++ = ((U8*)&u)[0];
*ptr++ = ((U8*)&u)[1];
*ptr++ = ((U8*)&u)[2]; }
inline void writeOpaque24B(U32 u) { check(3); *ptr++ = ((U8*)&u)[1];
*ptr++ = ((U8*)&u)[2];
*ptr++ = ((U8*)&u)[3]; }
// length() returns the length of the stream.
virtual int length() = 0;
// flush() requests that the stream be flushed.
virtual void flush() {}
// getptr(), getend() and setptr() are "dirty" methods which allow you to
// manipulate the buffer directly. This is useful for a stream which is a
// wrapper around an underlying stream.
inline U8* getptr() { return ptr; }
inline U8* getend() { return end; }
inline void setptr(U8* p) { ptr = p; }
private:
// overrun() is implemented by a derived class to cope with buffer overrun.
// It ensures there are at least itemSize bytes of buffer space. Returns
// the number of items which fit (up to a maximum of nItems). itemSize is
// supposed to be "small" (a few bytes).
virtual int overrun(int itemSize, int nItems) = 0;
protected:
U8* ptr;
U8* end;
};
}
#endif
//
// 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.
#include <rdr/ZlibInStream.h>
#include <rdr/Exception.h>
#include <zlib.h>
using namespace rdr;
enum { DEFAULT_BUF_SIZE = 16384 };
ZlibInStream::ZlibInStream(int bufSize_)
: underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
bytesIn(0)
{
zs = new z_stream;
zs->zalloc = Z_NULL;
zs->zfree = Z_NULL;
zs->opaque = Z_NULL;
zs->next_in = Z_NULL;
zs->avail_in = 0;
if (inflateInit(zs) != Z_OK) {
delete zs;
throw Exception("ZlibInStream: inflateInit failed");
}
ptr = end = start = new U8[bufSize];
}
ZlibInStream::~ZlibInStream()
{
delete [] start;
inflateEnd(zs);
delete zs;
}
void ZlibInStream::setUnderlying(InStream* is, int bytesIn_)
{
underlying = is;
bytesIn = bytesIn_;
ptr = end = start;
}
int ZlibInStream::pos()
{
return offset + ptr - start;
}
void ZlibInStream::reset()
{
ptr = end = start;
if (!underlying) return;
while (bytesIn > 0) {
decompress();
end = start; // throw away any data
}
underlying = 0;
}
int ZlibInStream::overrun(int itemSize, int nItems)
{
if (itemSize > bufSize)
throw Exception("ZlibInStream overrun: max itemSize exceeded");
if (!underlying)
throw Exception("ZlibInStream overrun: no underlying stream");
if (end - ptr != 0)
memmove(start, ptr, end - ptr);
offset += ptr - start;
end -= ptr - start;
ptr = start;
while (end - ptr < itemSize) {
decompress();
}
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
// decompress() calls the decompressor once. Note that this won't necessarily
// generate any output data - it may just consume some input data.
void ZlibInStream::decompress()
{
zs->next_out = (U8*)end;
zs->avail_out = start + bufSize - end;
underlying->check(1);
zs->next_in = (U8*)underlying->getptr();
zs->avail_in = underlying->getend() - underlying->getptr();
if ((int)zs->avail_in > bytesIn)
zs->avail_in = bytesIn;
int rc = inflate(zs, Z_SYNC_FLUSH);
if (rc != Z_OK) {
throw Exception("ZlibInStream: inflate failed");
}
bytesIn -= zs->next_in - underlying->getptr();
end = zs->next_out;
underlying->setptr(zs->next_in);
}
//
// 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.
//
// ZlibInStream streams from a compressed data stream ("underlying"),
// decompressing with zlib on the fly.
//
#ifndef __RDR_ZLIBINSTREAM_H__
#define __RDR_ZLIBINSTREAM_H__
#include <rdr/InStream.h>
struct z_stream_s;
namespace rdr {
class ZlibInStream : public InStream {
public:
ZlibInStream(int bufSize=0);
virtual ~ZlibInStream();
void setUnderlying(InStream* is, int bytesIn);
void reset();
int pos();
private:
int overrun(int itemSize, int nItems);
void decompress();
InStream* underlying;
int bufSize;
int offset;
z_stream_s* zs;
int bytesIn;
U8* start;
};
} // end of namespace rdr
#endif
//
// 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.
#include <rdr/ZlibOutStream.h>
#include <rdr/Exception.h>
#include <zlib.h>
extern "C" {
extern void rfbLog(const char *format, ...);
}
using namespace rdr;
enum { DEFAULT_BUF_SIZE = 16384 };
ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_)
: underlying(os), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
{
zs = new z_stream;
zs->zalloc = Z_NULL;
zs->zfree = Z_NULL;
zs->opaque = Z_NULL;
if (deflateInit(zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
delete zs;
throw Exception("ZlibOutStream: deflateInit failed");
}
ptr = start = new U8[bufSize];
end = start + bufSize;
}
ZlibOutStream::~ZlibOutStream()
{
try {
flush();
} catch (Exception&) {
}
delete [] start;
deflateEnd(zs);
delete zs;
}
void ZlibOutStream::setUnderlying(OutStream* os)
{
underlying = os;
}
int ZlibOutStream::length()
{
return offset + ptr - start;
}
void ZlibOutStream::flush()
{
zs->next_in = start;
zs->avail_in = ptr - start;
// rfbLog("zos flush: avail_in %d\n",zs->avail_in);
while (zs->avail_in != 0) {
do {
underlying->check(1);
zs->next_out = underlying->getptr();
zs->avail_out = underlying->getend() - underlying->getptr();
// rfbLog("zos flush: calling deflate, avail_in %d, avail_out %d\n",
// zs->avail_in,zs->avail_out);
int rc = deflate(zs, Z_SYNC_FLUSH);
if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
// rfbLog("zos flush: after deflate: %d bytes\n",
// zs->next_out-underlying->getptr());
underlying->setptr(zs->next_out);
} while (zs->avail_out == 0);
}
offset += ptr - start;
ptr = start;
}
int ZlibOutStream::overrun(int itemSize, int nItems)
{
// rfbLog("ZlibOutStream overrun\n");
if (itemSize > bufSize)
throw Exception("ZlibOutStream overrun: max itemSize exceeded");
while (end - ptr < itemSize) {
zs->next_in = start;
zs->avail_in = ptr - start;
do {
underlying->check(1);
zs->next_out = underlying->getptr();
zs->avail_out = underlying->getend() - underlying->getptr();
// rfbLog("zos overrun: calling deflate, avail_in %d, avail_out %d\n",
// zs->avail_in,zs->avail_out);
int rc = deflate(zs, 0);
if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
// rfbLog("zos overrun: after deflate: %d bytes\n",
// zs->next_out-underlying->getptr());
underlying->setptr(zs->next_out);
} while (zs->avail_out == 0);
// output buffer not full
if (zs->avail_in == 0) {
offset += ptr - start;
ptr = start;
} else {
// but didn't consume all the data? try shifting what's left to the
// start of the buffer.
rfbLog("z out buf not full, but in data not consumed\n");
memmove(start, zs->next_in, ptr - zs->next_in);
offset += zs->next_in - start;
ptr -= zs->next_in - start;
}
}
if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;
return nItems;
}
//
// 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.
//
// ZlibOutStream streams to a compressed data stream (underlying), compressing
// with zlib on the fly.
//
#ifndef __RDR_ZLIBOUTSTREAM_H__
#define __RDR_ZLIBOUTSTREAM_H__
#include <rdr/OutStream.h>
struct z_stream_s;
namespace rdr {
class ZlibOutStream : public OutStream {
public:
ZlibOutStream(OutStream* os=0, int bufSize=0);
virtual ~ZlibOutStream();
void setUnderlying(OutStream* os);
void flush();
int length();
private:
int overrun(int itemSize, int nItems);
OutStream* underlying;
int bufSize;
int offset;
z_stream_s* zs;
U8* start;
};
} // end of namespace rdr
#endif
//
// 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.
#ifndef __RDR_TYPES_H__
#define __RDR_TYPES_H__
namespace rdr {
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
typedef signed char S8;
typedef signed short S16;
typedef signed int S32;
} // end of namespace rdr
#endif
//
// 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).
//
extern "C" {
#include <rfb/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.
*/
rfbBool 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, (uint8_t*)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;
}
//
// 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
//
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment