| /* |
| * 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) |
| |
| #define ENDIAN_LITTLE 0 |
| #define ENDIAN_BIG 1 |
| #define ENDIAN_NO 2 |
| #define ZYWRLE_ENDIAN ENDIAN_LITTLE |
| #undef END_FIX |
| #if ZYWRLE_ENDIAN == ENDIAN_LITTLE |
| # define END_FIX LE |
| #elif ZYWRLE_ENDIAN == ENDIAN_BIG |
| # define END_FIX BE |
| #else |
| # define END_FIX NE |
| #endif |
| #define __RFB_CONCAT3E(a,b,c) CONCAT3E(a,b,c) |
| #define __RFB_CONCAT2E(a,b) CONCAT2E(a,b) |
| #undef CPIXEL |
| #if REALBPP != BPP |
| #if UNCOMP == 0 |
| #define CPIXEL REALBPP |
| #elif UNCOMP>0 |
| #define CPIXEL CONCAT2E(REALBPP,Down) |
| #else |
| #define CPIXEL CONCAT2E(REALBPP,Up) |
| #endif |
| #endif |
| #define PIXEL_T __RFB_CONCAT3E(uint,BPP,_t) |
| #if BPP!=8 |
| #define ZYWRLE_DECODE 1 |
| #include "zywrletemplate.c" |
| #endif |
| #undef CPIXEL |
| |
| 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; |
| int min_buffer_size = rw * rh * (REALBPP / 8) * 2; |
| |
| /* 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 < min_buffer_size) { |
| |
| if ( client->raw_buffer != NULL ) { |
| |
| free( client->raw_buffer ); |
| |
| } |
| |
| client->raw_buffer_size = min_buffer_size; |
| 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 ) { |
| char* buf=client->raw_buffer; |
| int i,j; |
| |
| remaining = client->raw_buffer_size-client->decompStream.avail_out; |
| |
| for(j=0; j<rh; j+=rfbZRLETileHeight) |
| for(i=0; i<rw; i+=rfbZRLETileWidth) { |
| int subWidth=(i+rfbZRLETileWidth>rw)?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 BPP!=8 |
| uint8_t zywrle_level = (client->appData.qualityLevel & 0x80) ? |
| 0 : (3 - client->appData.qualityLevel / 3); |
| #endif |
| |
| if(buffer_length<1) |
| return -2; |
| |
| type = *buffer; |
| buffer++; |
| { |
| if( type == 0 ) /* raw */ |
| #if BPP!=8 |
| if( zywrle_level > 0 ){ |
| CARDBPP* pFrame = (CARDBPP*)client->frameBuffer + y*client->width+x; |
| int ret; |
| client->appData.qualityLevel |= 0x80; |
| ret = HandleZRLETile(client, buffer, buffer_end-buffer, x, y, w, h); |
| client->appData.qualityLevel &= 0x7F; |
| if( ret < 0 ){ |
| return ret; |
| } |
| ZYWRLE_SYNTHESIZE( pFrame, pFrame, w, h, client->width, zywrle_level, (int*)client->zlib_buffer ); |
| buffer += ret; |
| }else |
| #endif |
| { |
| #if REALBPP!=BPP |
| int i,j; |
| |
| 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; i<x+w; i++,buffer+=REALBPP/8) |
| ((CARDBPP*)client->frameBuffer)[j+i] = UncompressCPixel(buffer); |
| #else |
| CopyRectangle(client, buffer, x, y, w, h); |
| buffer+=w*h*REALBPP/8; |
| #endif |
| } |
| else if( type == 1 ) /* solid */ |
| { |
| CARDBPP color = UncompressCPixel(buffer); |
| |
| if(1+REALBPP/8>buffer_length) |
| return -4; |
| |
| FillRectangle(client, x, y, w, h, color); |
| |
| buffer+=REALBPP/8; |
| |
| } |
| else if( (type >= 2)&&(type <= 127) ) /* packed Palette */ |
| { |
| CARDBPP palette[16]; |
| int i,j,shift, |
| bpp=(type>4?(type>16?8:4):(type>2?2:1)), |
| mask=(1<<bpp)-1, |
| divider=(8/bpp); |
| |
| if(1+type*REALBPP/8+((w+divider-1)/divider)*h>buffer_length) |
| return -5; |
| |
| /* read palette */ |
| for(i=0; i<type; i++,buffer+=REALBPP/8) |
| palette[i] = UncompressCPixel(buffer); |
| |
| /* read palettized pixels */ |
| for(j=y*client->width; j<(y+h)*client->width; j+=client->width) { |
| for(i=x,shift=8-bpp; i<x+w; i++) { |
| ((CARDBPP*)client->frameBuffer)[j+i] = palette[((*buffer)>>shift)&mask]; |
| shift-=bpp; |
| if(shift<0) { |
| shift=8-bpp; |
| buffer++; |
| } |
| } |
| if(shift<8-bpp) |
| buffer++; |
| } |
| |
| } |
| /* case 17 ... 127: not used, but valid */ |
| else if( type == 128 ) /* plain RLE */ |
| { |
| int i=0,j=0; |
| while(j<h) { |
| int color,length; |
| /* read color */ |
| if(buffer+REALBPP/8+1>buffer_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(j<h && length>0) { |
| ((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"); |
| } |
| |
| } |
| else if( type == 129 ) /* unused */ |
| { |
| return -8; |
| } |
| else if( type >= 130 ) /* palette RLE */ |
| { |
| CARDBPP palette[128]; |
| int i,j; |
| |
| if(2+(type-128)*REALBPP/8>buffer_length) |
| return -9; |
| |
| /* read palette */ |
| for(i=0; i<type-128; i++,buffer+=REALBPP/8) |
| palette[i] = UncompressCPixel(buffer); |
| /* read palettized pixels */ |
| i=j=0; |
| while(j<h) { |
| int color,length; |
| /* read color */ |
| if(buffer>=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(j<h && length>0) { |
| ((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"); |
| } |
| } |
| } |
| |
| return buffer-buffer_copy; |
| } |
| |
| #undef CARDBPP |
| #undef CARDREALBPP |
| #undef HandleZRLE |
| #undef HandleZRLETile |
| #undef UncompressCPixel |
| #undef REALBPP |
| |
| #endif |
| |
| #undef UNCOMP |