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