LibVNCServer/LibVNCClient
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
zlib.c
Go to the documentation of this file.
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@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 {
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 */
206  + w * (cl->format.bitsPerPixel / 8) * h);
207 
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);
220 
221  memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
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