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