From b225ee993ac49bbde5f3cf56c06a7ae80bb7e1fe Mon Sep 17 00:00:00 2001 From: dscho Date: Tue, 24 May 2005 08:59:31 +0000 Subject: [PATCH] implement ZRLE decoding --- TODO | 2 - libvncclient/rfbproto.c | 52 ++++++ libvncclient/tight.c | 2 + libvncclient/vncviewer.c | 2 +- libvncclient/zlib.c | 2 +- libvncclient/zrle.c | 380 +++++++++++++++++++++++++++++++++++++++ test/encodingstest.c | 3 +- 7 files changed, 437 insertions(+), 6 deletions(-) create mode 100644 libvncclient/zrle.c diff --git a/TODO b/TODO index e74dc41..49bdd71 100644 --- a/TODO +++ b/TODO @@ -6,9 +6,7 @@ VisualNaCro testing test IRIX -overlay (x11vnc) java vncviewer doesn't do colour cursors? MinGW32 doesn't do fcntl on sockets; use setsockopt instead... -make libvncclient threadsafe (static zlib buffer -> rfbClient*) make corre work again (libvncclient or libvncserver?) -implement zrle in libvncclient implement "-record" in libvncclient libtoolize come up with an alpha cursor patch for Windows vncviewer diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index dd5d961..0e8ccfe 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -164,6 +164,12 @@ static void JpegTermSource(j_decompress_ptr cinfo); static void JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData, int compressedLen); #endif +static rfbBool HandleZRLE8(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleZRLE16(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleZRLE24(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleZRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleZRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleZRLE32(rfbClient* client, int rx, int ry, int rw, int rh); #endif /* @@ -497,6 +503,7 @@ SetFormatAndEncodings(rfbClient* client) encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile); #ifdef LIBVNCSERVER_HAVE_LIBZ encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib); + encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE); #endif encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE); encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE); @@ -879,6 +886,40 @@ HandleRFBServerMessage(rfbClient* client) break; } #endif + case rfbEncodingZRLE: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleZRLE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleZRLE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + { + uint32_t maxColor=(client->format.redMax<format.redShift)| + (client->format.greenMax<format.greenShift)| + (client->format.blueMax<format.blueShift); + if ((client->format.bigEndian && (maxColor&0xff)==0) || + (!client->format.bigEndian && (maxColor&0xff000000)==0)) { + if (!HandleZRLE24(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + } else if (!client->format.bigEndian && (maxColor&0xff)==0) { + if (!HandleZRLE24Up(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + } else if (client->format.bigEndian && (maxColor&0xff000000)==0) { + if (!HandleZRLE24Down(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + } else if (!HandleZRLE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + } + break; + } + #endif default: @@ -972,6 +1013,7 @@ HandleRFBServerMessage(rfbClient* client) #include "hextile.c" #include "zlib.c" #include "tight.c" +#include "zrle.c" #undef BPP #define BPP 16 #include "rre.c" @@ -979,6 +1021,7 @@ HandleRFBServerMessage(rfbClient* client) #include "hextile.c" #include "zlib.c" #include "tight.c" +#include "zrle.c" #undef BPP #define BPP 32 #include "rre.c" @@ -986,6 +1029,15 @@ HandleRFBServerMessage(rfbClient* client) #include "hextile.c" #include "zlib.c" #include "tight.c" +#include "zrle.c" +#define REALBPP 24 +#include "zrle.c" +#define REALBPP 24 +#define UNCOMP 8 +#include "zrle.c" +#define REALBPP 24 +#define UNCOMP -8 +#include "zrle.c" #undef BPP diff --git a/libvncclient/tight.c b/libvncclient/tight.c index 348c423..48a27e9 100644 --- a/libvncclient/tight.c +++ b/libvncclient/tight.c @@ -679,5 +679,7 @@ JpegSetSrcManager(j_decompress_ptr cinfo, #endif +#undef CARDBPP + #endif diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index 12c801b..607a2bb 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -87,7 +87,7 @@ static rfbBool MallocFrameBuffer(rfbClient* client) { static void initAppData(AppData* data) { data->shareDesktop=TRUE; data->viewOnly=FALSE; - data->encodingsString="tight hextile zlib corre rre raw"; + data->encodingsString="tight zrle hextile zlib corre rre raw"; data->useBGR233=FALSE; data->nColours=0; data->forceOwnCmap=FALSE; diff --git a/libvncclient/zlib.c b/libvncclient/zlib.c index 2a32d1c..89db504 100644 --- a/libvncclient/zlib.c +++ b/libvncclient/zlib.c @@ -30,7 +30,7 @@ */ #define HandleZlibBPP CONCAT2E(HandleZlib,BPP) -#define CARDBPP CONCAT2E(uint,BPP,_t) +#define CARDBPP CONCAT3E(uint,BPP,_t) static rfbBool HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh) diff --git a/libvncclient/zrle.c b/libvncclient/zrle.c new file mode 100644 index 0000000..53ae6f1 --- /dev/null +++ b/libvncclient/zrle.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2005 Johannes E. Schindelin. 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. + */ + +#ifdef LIBVNCSERVER_HAVE_LIBZ + +/* + * zrle.c - handle zrle encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an zrle + * encoded rectangle with BPP bits per pixel. + */ + +#ifndef REALBPP +#define REALBPP BPP +#endif + +#if !defined(UNCOMP) || UNCOMP==0 +#define HandleZRLE CONCAT2E(HandleZRLE,REALBPP) +#define HandleZRLETile CONCAT2E(HandleZRLETile,REALBPP) +#elif UNCOMP>0 +#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Down) +#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Down) +#else +#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Up) +#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Up) +#endif +#define CARDBPP CONCAT3E(uint,BPP,_t) +#define CARDREALBPP CONCAT3E(uint,REALBPP,_t) + +static int HandleZRLETile(rfbClient* client, + uint8_t* buffer,size_t buffer_length, + int x,int y,int w,int h); + +static rfbBool +HandleZRLE (rfbClient* client, int rx, int ry, int rw, int rh) +{ + rfbZRLEHeader header; + int remaining; + int inflateResult; + int toRead; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed REALBPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if ( client->raw_buffer_size < (( rw * rh ) * ( REALBPP / 8 ))) { + + if ( client->raw_buffer != NULL ) { + + free( client->raw_buffer ); + + } + + client->raw_buffer_size = (( rw * rh ) * ( REALBPP / 8 )); + client->raw_buffer = (char*) malloc( client->raw_buffer_size ); + + } + + if (!ReadFromRFBServer(client, (char *)&header, sz_rfbZRLEHeader)) + return FALSE; + + remaining = rfbClientSwap32IfLE(header.length); + + /* Need to initialize the decompressor state. */ + client->decompStream.next_in = ( Bytef * )client->buffer; + client->decompStream.avail_in = 0; + client->decompStream.next_out = ( Bytef * )client->raw_buffer; + client->decompStream.avail_out = client->raw_buffer_size; + client->decompStream.data_type = Z_BINARY; + + /* Initialize the decompression stream structures on the first invocation. */ + if ( client->decompStreamInited == FALSE ) { + + inflateResult = inflateInit( &client->decompStream ); + + if ( inflateResult != Z_OK ) { + rfbClientLog( + "inflateInit returned error: %d, msg: %s\n", + inflateResult, + client->decompStream.msg); + return FALSE; + } + + client->decompStreamInited = TRUE; + + } + + inflateResult = Z_OK; + + /* Process buffer full of data until no more to process, or + * some type of inflater error, or Z_STREAM_END. + */ + while (( remaining > 0 ) && + ( inflateResult == Z_OK )) { + + if ( remaining > RFB_BUFFER_SIZE ) { + toRead = RFB_BUFFER_SIZE; + } + else { + toRead = remaining; + } + + /* Fill the buffer, obtaining data from the server. */ + if (!ReadFromRFBServer(client, client->buffer,toRead)) + return FALSE; + + client->decompStream.next_in = ( Bytef * )client->buffer; + client->decompStream.avail_in = toRead; + + /* Need to uncompress buffer full. */ + inflateResult = inflate( &client->decompStream, Z_SYNC_FLUSH ); + + /* We never supply a dictionary for compression. */ + if ( inflateResult == Z_NEED_DICT ) { + rfbClientLog("zlib inflate needs a dictionary!\n"); + return FALSE; + } + if ( inflateResult < 0 ) { + rfbClientLog( + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + client->decompStream.msg); + return FALSE; + } + + /* Result buffer allocated to be at least large enough. We should + * never run out of space! + */ + if (( client->decompStream.avail_in > 0 ) && + ( client->decompStream.avail_out <= 0 )) { + rfbClientLog("zlib inflate ran out of space!\n"); + return FALSE; + } + + remaining -= toRead; + + } /* while ( remaining > 0 ) */ + + if ( inflateResult == Z_OK ) { + void* buf=client->raw_buffer; + int i,j; + + remaining = client->raw_buffer_size-client->decompStream.avail_out; + + for(j=0; jrw)?rw-i:rfbZRLETileWidth; + int subHeight=(j+rfbZRLETileHeight>rh)?rh-j:rfbZRLETileHeight; + int result=HandleZRLETile(client,buf,remaining,rx+i,ry+j,subWidth,subHeight); + + if(result<0) { + rfbClientLog("ZRLE decoding failed (%d)\n",result); +return TRUE; + return FALSE; + } + + buf+=result; + remaining-=result; + } + } + else { + + rfbClientLog( + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + client->decompStream.msg); + return FALSE; + + } + + return TRUE; +} + +#if REALBPP!=BPP && defined(UNCOMP) && UNCOMP!=0 +#if UNCOMP>0 +#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)>>UNCOMP) +#else +#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)<<(-(UNCOMP))) +#endif +#else +#define UncompressCPixel(pointer) (*(CARDBPP*)pointer) +#endif + +static int HandleZRLETile(rfbClient* client, + uint8_t* buffer,size_t buffer_length, + int x,int y,int w,int h) { + uint8_t* buffer_copy = buffer; + uint8_t* buffer_end = buffer+buffer_length; + uint8_t type; + + if(buffer_length<1) + return -2; + + type = *buffer; + buffer++; + switch(type) { + case 0: /* raw */ + { +#if REALBPP!=BPP + int i,j,j2; + + if(1+w*h*REALBPP/8>buffer_length) { + rfbClientLog("expected %d bytes, got only %d (%dx%d)\n",1+w*h*REALBPP/8,buffer_length,w,h); + return -3; + } + + for(j=y*client->width; j<(y+h)*client->width; j+=client->width) + for(i=x; iframeBuffer)[j+i] = UncompressCPixel(buffer); +#else + CopyRectangle(client, buffer, x, y, w, h); +#endif + break; + } + case 1: /* solid */ + { + CARDBPP color = UncompressCPixel(buffer); + + if(1+REALBPP/8>buffer_length) + return -4; + + FillRectangle(client, x, y, w, h, color); + + break; + } + case 2 ... 127: /* packed Palette */ + { + CARDBPP palette[16]; + int i,j,shift, + bpp=(type>4?(type>16?8:4):(type>2?2:1)), + mask=(1<buffer_length) + return -5; + + /* read palette */ + for(i=0; iwidth; j<(y+h)*client->width; j+=client->width) { + for(i=x,shift=8-bpp; iframeBuffer)[j+i] = palette[((*buffer)>>shift)&mask]; + shift-=bpp; + if(shift<0) { + shift=8-bpp; + buffer++; + } + } + if(shift<8-bpp) + buffer++; + } + + break; + } + /* case 17 ... 127: not used, but valid */ + case 128: /* plain RLE */ + { + int i=0,j=0; + while(jbuffer_end) + return -7; + color = UncompressCPixel(buffer); + buffer+=REALBPP/8; + /* read run length */ + length=1; + while(*buffer==0xff) { + if(buffer+1>=buffer_end) + return -8; + length+=*buffer; + buffer++; + } + length+=*buffer; + buffer++; + while(j0) { + ((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color; + length--; + i++; + if(i>=w) { + i=0; + j++; + } + } + if(length>0) + rfbClientLog("Warning: possible ZRLE corruption\n"); + } + + break; + } + case 129: /* unused */ + { + return -8; + } + case 130 ... 255: /* palette RLE */ + { + CARDBPP palette[128]; + int i,j; + + if(2+(type-128)*REALBPP/8>buffer_length) + return -9; + + /* read palette */ + for(i=0; i=buffer_end) + return -10; + color = palette[(*buffer)&0x7f]; + length=1; + if(*buffer&0x80) { + if(buffer+1>=buffer_end) + return -11; + buffer++; + /* read run length */ + while(*buffer==0xff) { + if(buffer+1>=buffer_end) + return -8; + length+=*buffer; + buffer++; + } + length+=*buffer; + } + buffer++; + while(j0) { + ((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color; + length--; + i++; + if(i>=w) { + i=0; + j++; + } + } + if(length>0) + rfbClientLog("Warning: possible ZRLE corruption\n"); + } + + break; + } + } + + return buffer-buffer_copy; +} + +#undef CARDBPP +#undef CARDREALBPP +#undef HandleZRLE +#undef HandleZRLETile +#undef UncompressCPixel +#undef REALBPP +#undef UNCOMP + +#endif + diff --git a/test/encodingstest.c b/test/encodingstest.c index 821b2ae..c2f8135 100644 --- a/test/encodingstest.c +++ b/test/encodingstest.c @@ -25,8 +25,7 @@ static encoding_t testEncodings[]={ #ifdef LIBVNCSERVER_HAVE_LIBZ { rfbEncodingZlib, "zlib" }, { rfbEncodingZlibHex, "zlibhex" }, - /* TODO: implement ZRLE decoding */ - /* { rfbEncodingZRLE, "zrle" }, */ + { rfbEncodingZRLE, "zrle" }, #ifdef LIBVNCSERVER_HAVE_LIBJPEG { rfbEncodingTight, "tight" }, #endif