/* * 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