Home | History | Annotate | Download | only in libvncserver
      1 /*
      2  * zlib.c
      3  *
      4  * Routines to implement zlib based encoding (deflate).
      5  */
      6 
      7 /*
      8  *  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
      9  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
     10  *
     11  *  This is free software; you can redistribute it and/or modify
     12  *  it under the terms of the GNU General Public License as published by
     13  *  the Free Software Foundation; either version 2 of the License, or
     14  *  (at your option) any later version.
     15  *
     16  *  This software is distributed in the hope that it will be useful,
     17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19  *  GNU General Public License for more details.
     20  *
     21  *  You should have received a copy of the GNU General Public License
     22  *  along with this software; if not, write to the Free Software
     23  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
     24  *  USA.
     25  *
     26  * For the latest source code, please check:
     27  *
     28  * http://www.developVNC.org/
     29  *
     30  * or send email to feedback (at) developvnc.org.
     31  */
     32 
     33 #include <rfb/rfb.h>
     34 
     35 /*
     36  * zlibBeforeBuf contains pixel data in the client's format.
     37  * zlibAfterBuf contains the zlib (deflated) encoding version.
     38  * If the zlib compressed/encoded version is
     39  * larger than the raw data or if it exceeds zlibAfterBufSize then
     40  * raw encoding is used instead.
     41  */
     42 
     43 /*
     44  * Out of lazyiness, we use thread local storage for zlib as we did for
     45  * tight.  N.B. ZRLE does it the traditional way with per-client storage
     46  * (and so at least ZRLE will work threaded on older systems.)
     47  */
     48 #if LIBVNCSERVER_HAVE_LIBPTHREAD && LIBVNCSERVER_HAVE_TLS && !defined(TLS) && defined(__linux__)
     49 #define TLS __thread
     50 #endif
     51 #ifndef TLS
     52 #define TLS
     53 #endif
     54 
     55 static TLS int zlibBeforeBufSize = 0;
     56 static TLS char *zlibBeforeBuf = NULL;
     57 
     58 static TLS int zlibAfterBufSize = 0;
     59 static TLS char *zlibAfterBuf = NULL;
     60 static TLS int zlibAfterBufLen = 0;
     61 
     62 void rfbZlibCleanup(rfbScreenInfoPtr screen)
     63 {
     64   if (zlibBeforeBufSize) {
     65     free(zlibBeforeBuf);
     66     zlibBeforeBufSize=0;
     67   }
     68   if (zlibAfterBufSize) {
     69     zlibAfterBufSize=0;
     70     free(zlibAfterBuf);
     71   }
     72 }
     73 
     74 
     75 /*
     76  * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
     77  *                              rectangle encoding.
     78  */
     79 
     80 static rfbBool
     81 rfbSendOneRectEncodingZlib(rfbClientPtr cl,
     82                            int x,
     83                            int y,
     84                            int w,
     85                            int h)
     86 {
     87     rfbFramebufferUpdateRectHeader rect;
     88     rfbZlibHeader hdr;
     89     int deflateResult;
     90     int previousOut;
     91     int i;
     92     char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
     93     	   + (x * (cl->scaledScreen->bitsPerPixel / 8)));
     94 
     95     int maxRawSize;
     96     int maxCompSize;
     97 
     98     maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height
     99                   * (cl->format.bitsPerPixel / 8));
    100 
    101     if (zlibBeforeBufSize < maxRawSize) {
    102 	zlibBeforeBufSize = maxRawSize;
    103 	if (zlibBeforeBuf == NULL)
    104 	    zlibBeforeBuf = (char *)malloc(zlibBeforeBufSize);
    105 	else
    106 	    zlibBeforeBuf = (char *)realloc(zlibBeforeBuf, zlibBeforeBufSize);
    107     }
    108 
    109     /* zlib compression is not useful for very small data sets.
    110      * So, we just send these raw without any compression.
    111      */
    112     if (( w * h * (cl->scaledScreen->bitsPerPixel / 8)) <
    113           VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) {
    114 
    115         int result;
    116 
    117         /* The translation function (used also by the in raw encoding)
    118          * requires 4/2/1 byte alignment in the output buffer (which is
    119          * updateBuf for the raw encoding) based on the bitsPerPixel of
    120          * the viewer/client.  This prevents SIGBUS errors on some
    121          * architectures like SPARC, PARISC...
    122          */
    123         if (( cl->format.bitsPerPixel > 8 ) &&
    124             ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) {
    125             if (!rfbSendUpdateBuf(cl))
    126                 return FALSE;
    127         }
    128 
    129         result = rfbSendRectEncodingRaw(cl, x, y, w, h);
    130 
    131         return result;
    132 
    133     }
    134 
    135     /*
    136      * zlib requires output buffer to be slightly larger than the input
    137      * buffer, in the worst case.
    138      */
    139     maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12;
    140 
    141     if (zlibAfterBufSize < maxCompSize) {
    142 	zlibAfterBufSize = maxCompSize;
    143 	if (zlibAfterBuf == NULL)
    144 	    zlibAfterBuf = (char *)malloc(zlibAfterBufSize);
    145 	else
    146 	    zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize);
    147     }
    148 
    149 
    150     /*
    151      * Convert pixel data to client format.
    152      */
    153     (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
    154 		       &cl->format, fbptr, zlibBeforeBuf,
    155 		       cl->scaledScreen->paddedWidthInBytes, w, h);
    156 
    157     cl->compStream.next_in = ( Bytef * )zlibBeforeBuf;
    158     cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8);
    159     cl->compStream.next_out = ( Bytef * )zlibAfterBuf;
    160     cl->compStream.avail_out = maxCompSize;
    161     cl->compStream.data_type = Z_BINARY;
    162 
    163     /* Initialize the deflation state. */
    164     if ( cl->compStreamInited == FALSE ) {
    165 
    166         cl->compStream.total_in = 0;
    167         cl->compStream.total_out = 0;
    168         cl->compStream.zalloc = Z_NULL;
    169         cl->compStream.zfree = Z_NULL;
    170         cl->compStream.opaque = Z_NULL;
    171 
    172         deflateInit2( &(cl->compStream),
    173                         cl->zlibCompressLevel,
    174                         Z_DEFLATED,
    175                         MAX_WBITS,
    176                         MAX_MEM_LEVEL,
    177                         Z_DEFAULT_STRATEGY );
    178         /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */
    179         /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */
    180         cl->compStreamInited = TRUE;
    181 
    182     }
    183 
    184     previousOut = cl->compStream.total_out;
    185 
    186     /* Perform the compression here. */
    187     deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH );
    188 
    189     /* Find the total size of the resulting compressed data. */
    190     zlibAfterBufLen = cl->compStream.total_out - previousOut;
    191 
    192     if ( deflateResult != Z_OK ) {
    193         rfbErr("zlib deflation error: %s\n", cl->compStream.msg);
    194         return FALSE;
    195     }
    196 
    197     /* Note that it is not possible to switch zlib parameters based on
    198      * the results of the compression pass.  The reason is
    199      * that we rely on the compressor and decompressor states being
    200      * in sync.  Compressing and then discarding the results would
    201      * cause lose of synchronization.
    202      */
    203 
    204     /* Update statics */
    205     rfbStatRecordEncodingSent(cl, rfbEncodingZlib, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + zlibAfterBufLen,
    206         + w * (cl->format.bitsPerPixel / 8) * h);
    207 
    208     if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader
    209 	> UPDATE_BUF_SIZE)
    210     {
    211 	if (!rfbSendUpdateBuf(cl))
    212 	    return FALSE;
    213     }
    214 
    215     rect.r.x = Swap16IfLE(x);
    216     rect.r.y = Swap16IfLE(y);
    217     rect.r.w = Swap16IfLE(w);
    218     rect.r.h = Swap16IfLE(h);
    219     rect.encoding = Swap32IfLE(rfbEncodingZlib);
    220 
    221     memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
    222 	   sz_rfbFramebufferUpdateRectHeader);
    223     cl->ublen += sz_rfbFramebufferUpdateRectHeader;
    224 
    225     hdr.nBytes = Swap32IfLE(zlibAfterBufLen);
    226 
    227     memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
    228     cl->ublen += sz_rfbZlibHeader;
    229 
    230     for (i = 0; i < zlibAfterBufLen;) {
    231 
    232 	int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
    233 
    234 	if (i + bytesToCopy > zlibAfterBufLen) {
    235 	    bytesToCopy = zlibAfterBufLen - i;
    236 	}
    237 
    238 	memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy);
    239 
    240 	cl->ublen += bytesToCopy;
    241 	i += bytesToCopy;
    242 
    243 	if (cl->ublen == UPDATE_BUF_SIZE) {
    244 	    if (!rfbSendUpdateBuf(cl))
    245 		return FALSE;
    246 	}
    247     }
    248 
    249     return TRUE;
    250 
    251 }
    252 
    253 
    254 /*
    255  * rfbSendRectEncodingZlib - send a given rectangle using one or more
    256  *                           Zlib encoding rectangles.
    257  */
    258 
    259 rfbBool
    260 rfbSendRectEncodingZlib(rfbClientPtr cl,
    261                         int x,
    262                         int y,
    263                         int w,
    264                         int h)
    265 {
    266     int  maxLines;
    267     int  linesRemaining;
    268     rfbRectangle partialRect;
    269 
    270     partialRect.x = x;
    271     partialRect.y = y;
    272     partialRect.w = w;
    273     partialRect.h = h;
    274 
    275     /* Determine maximum pixel/scan lines allowed per rectangle. */
    276     maxLines = ( ZLIB_MAX_SIZE(w) / w );
    277 
    278     /* Initialize number of scan lines left to do. */
    279     linesRemaining = h;
    280 
    281     /* Loop until all work is done. */
    282     while ( linesRemaining > 0 ) {
    283 
    284         int linesToComp;
    285 
    286         if ( maxLines < linesRemaining )
    287             linesToComp = maxLines;
    288         else
    289             linesToComp = linesRemaining;
    290 
    291         partialRect.h = linesToComp;
    292 
    293         /* Encode (compress) and send the next rectangle. */
    294         if ( ! rfbSendOneRectEncodingZlib( cl,
    295                                            partialRect.x,
    296                                            partialRect.y,
    297                                            partialRect.w,
    298                                            partialRect.h )) {
    299 
    300             return FALSE;
    301         }
    302 
    303         /* Technically, flushing the buffer here is not extrememly
    304          * efficient.  However, this improves the overall throughput
    305          * of the system over very slow networks.  By flushing
    306          * the buffer with every maximum size zlib rectangle, we
    307          * improve the pipelining usage of the server CPU, network,
    308          * and viewer CPU components.  Insuring that these components
    309          * are working in parallel actually improves the performance
    310          * seen by the user.
    311          * Since, zlib is most useful for slow networks, this flush
    312          * is appropriate for the desired behavior of the zlib encoding.
    313          */
    314         if (( cl->ublen > 0 ) &&
    315             ( linesToComp == maxLines )) {
    316             if (!rfbSendUpdateBuf(cl)) {
    317 
    318                 return FALSE;
    319             }
    320         }
    321 
    322         /* Update remaining and incremental rectangle location. */
    323         linesRemaining -= linesToComp;
    324         partialRect.y += linesToComp;
    325 
    326     }
    327 
    328     return TRUE;
    329 
    330 }
    331 
    332