Home | History | Annotate | Download | only in libvncserver
      1 /*
      2  * ultra.c
      3  *
      4  * Routines to implement ultra based encoding (minilzo).
      5  * ultrazip supports packed rectangles if the rects are tiny...
      6  * This improves performance as lzo has more data to work with at once
      7  * This is 'UltraZip' and is currently not implemented.
      8  */
      9 
     10 #include <rfb/rfb.h>
     11 #include "minilzo.h"
     12 
     13 /*
     14  * cl->beforeEncBuf contains pixel data in the client's format.
     15  * cl->afterEncBuf contains the lzo (deflated) encoding version.
     16  * If the lzo compressed/encoded version is
     17  * larger than the raw data or if it exceeds cl->afterEncBufSize then
     18  * raw encoding is used instead.
     19  */
     20 
     21 
     22 /*
     23  * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
     24  *                              rectangle encoding.
     25  */
     26 
     27 #define MAX_WRKMEM ((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)
     28 
     29 
     30 void rfbFreeUltraData(rfbClientPtr cl) {
     31   if (cl->compStreamInitedLZO) {
     32     free(cl->lzoWrkMem);
     33     cl->compStreamInitedLZO=FALSE;
     34   }
     35 }
     36 
     37 
     38 static rfbBool
     39 rfbSendOneRectEncodingUltra(rfbClientPtr cl,
     40                            int x,
     41                            int y,
     42                            int w,
     43                            int h)
     44 {
     45     rfbFramebufferUpdateRectHeader rect;
     46     rfbZlibHeader hdr;
     47     int deflateResult;
     48     int i;
     49     char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
     50     	   + (x * (cl->scaledScreen->bitsPerPixel / 8)));
     51 
     52     int maxRawSize;
     53     lzo_uint maxCompSize;
     54 
     55     maxRawSize = (w * h * (cl->format.bitsPerPixel / 8));
     56 
     57     if (cl->beforeEncBufSize < maxRawSize) {
     58 	cl->beforeEncBufSize = maxRawSize;
     59 	if (cl->beforeEncBuf == NULL)
     60 	    cl->beforeEncBuf = (char *)malloc(cl->beforeEncBufSize);
     61 	else
     62 	    cl->beforeEncBuf = (char *)realloc(cl->beforeEncBuf, cl->beforeEncBufSize);
     63     }
     64 
     65     /*
     66      * lzo requires output buffer to be slightly larger than the input
     67      * buffer, in the worst case.
     68      */
     69     maxCompSize = (maxRawSize + maxRawSize / 16 + 64 + 3);
     70 
     71     if (cl->afterEncBufSize < (int)maxCompSize) {
     72 	cl->afterEncBufSize = maxCompSize;
     73 	if (cl->afterEncBuf == NULL)
     74 	    cl->afterEncBuf = (char *)malloc(cl->afterEncBufSize);
     75 	else
     76 	    cl->afterEncBuf = (char *)realloc(cl->afterEncBuf, cl->afterEncBufSize);
     77     }
     78 
     79     /*
     80      * Convert pixel data to client format.
     81      */
     82     (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
     83 		       &cl->format, fbptr, cl->beforeEncBuf,
     84 		       cl->scaledScreen->paddedWidthInBytes, w, h);
     85 
     86     if ( cl->compStreamInitedLZO == FALSE ) {
     87         cl->compStreamInitedLZO = TRUE;
     88         /* Work-memory needed for compression. Allocate memory in units
     89          * of `lzo_align_t' (instead of `char') to make sure it is properly aligned.
     90          */
     91         cl->lzoWrkMem = malloc(sizeof(lzo_align_t) * (((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)));
     92     }
     93 
     94     /* Perform the compression here. */
     95     deflateResult = lzo1x_1_compress((unsigned char *)cl->beforeEncBuf, (lzo_uint)(w * h * (cl->format.bitsPerPixel / 8)), (unsigned char *)cl->afterEncBuf, &maxCompSize, cl->lzoWrkMem);
     96     /* maxCompSize now contains the compressed size */
     97 
     98     /* Find the total size of the resulting compressed data. */
     99     cl->afterEncBufLen = maxCompSize;
    100 
    101     if ( deflateResult != LZO_E_OK ) {
    102         rfbErr("lzo deflation error: %d\n", deflateResult);
    103         return FALSE;
    104     }
    105 
    106     /* Update statics */
    107     rfbStatRecordEncodingSent(cl, rfbEncodingUltra, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + cl->afterEncBufLen, maxRawSize);
    108 
    109     if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader
    110 	> UPDATE_BUF_SIZE)
    111     {
    112 	if (!rfbSendUpdateBuf(cl))
    113 	    return FALSE;
    114     }
    115 
    116     rect.r.x = Swap16IfLE(x);
    117     rect.r.y = Swap16IfLE(y);
    118     rect.r.w = Swap16IfLE(w);
    119     rect.r.h = Swap16IfLE(h);
    120     rect.encoding = Swap32IfLE(rfbEncodingUltra);
    121 
    122     memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
    123 	   sz_rfbFramebufferUpdateRectHeader);
    124     cl->ublen += sz_rfbFramebufferUpdateRectHeader;
    125 
    126     hdr.nBytes = Swap32IfLE(cl->afterEncBufLen);
    127 
    128     memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
    129     cl->ublen += sz_rfbZlibHeader;
    130 
    131     /* We might want to try sending the data directly... */
    132     for (i = 0; i < cl->afterEncBufLen;) {
    133 
    134 	int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
    135 
    136 	if (i + bytesToCopy > cl->afterEncBufLen) {
    137 	    bytesToCopy = cl->afterEncBufLen - i;
    138 	}
    139 
    140 	memcpy(&cl->updateBuf[cl->ublen], &cl->afterEncBuf[i], bytesToCopy);
    141 
    142 	cl->ublen += bytesToCopy;
    143 	i += bytesToCopy;
    144 
    145 	if (cl->ublen == UPDATE_BUF_SIZE) {
    146 	    if (!rfbSendUpdateBuf(cl))
    147 		return FALSE;
    148 	}
    149     }
    150 
    151     return TRUE;
    152 
    153 }
    154 
    155 /*
    156  * rfbSendRectEncodingUltra - send a given rectangle using one or more
    157  *                           LZO encoding rectangles.
    158  */
    159 
    160 rfbBool
    161 rfbSendRectEncodingUltra(rfbClientPtr cl,
    162                         int x,
    163                         int y,
    164                         int w,
    165                         int h)
    166 {
    167     int  maxLines;
    168     int  linesRemaining;
    169     rfbRectangle partialRect;
    170 
    171     partialRect.x = x;
    172     partialRect.y = y;
    173     partialRect.w = w;
    174     partialRect.h = h;
    175 
    176     /* Determine maximum pixel/scan lines allowed per rectangle. */
    177     maxLines = ( ULTRA_MAX_SIZE(w) / w );
    178 
    179     /* Initialize number of scan lines left to do. */
    180     linesRemaining = h;
    181 
    182     /* Loop until all work is done. */
    183     while ( linesRemaining > 0 ) {
    184 
    185         int linesToComp;
    186 
    187         if ( maxLines < linesRemaining )
    188             linesToComp = maxLines;
    189         else
    190             linesToComp = linesRemaining;
    191 
    192         partialRect.h = linesToComp;
    193 
    194         /* Encode (compress) and send the next rectangle. */
    195         if ( ! rfbSendOneRectEncodingUltra( cl,
    196                                            partialRect.x,
    197                                            partialRect.y,
    198                                            partialRect.w,
    199                                            partialRect.h )) {
    200 
    201             return FALSE;
    202         }
    203 
    204         /* Technically, flushing the buffer here is not extrememly
    205          * efficient.  However, this improves the overall throughput
    206          * of the system over very slow networks.  By flushing
    207          * the buffer with every maximum size lzo rectangle, we
    208          * improve the pipelining usage of the server CPU, network,
    209          * and viewer CPU components.  Insuring that these components
    210          * are working in parallel actually improves the performance
    211          * seen by the user.
    212          * Since, lzo is most useful for slow networks, this flush
    213          * is appropriate for the desired behavior of the lzo encoding.
    214          */
    215         if (( cl->ublen > 0 ) &&
    216             ( linesToComp == maxLines )) {
    217             if (!rfbSendUpdateBuf(cl)) {
    218 
    219                 return FALSE;
    220             }
    221         }
    222 
    223         /* Update remaining and incremental rectangle location. */
    224         linesRemaining -= linesToComp;
    225         partialRect.y += linesToComp;
    226 
    227     }
    228 
    229     return TRUE;
    230 
    231 }
    232