LibVNCServer/LibVNCClient
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 defined(__GNUC__)
49 #define TLS __thread
50 #elif defined(_MSC_VER)
51 #define TLS __declspec(thread)
52 #else
53 #define TLS
54 #endif
55 
56 static TLS int zlibBeforeBufSize = 0;
57 static TLS char *zlibBeforeBuf = NULL;
58 
59 static TLS int zlibAfterBufSize = 0;
60 static TLS char *zlibAfterBuf = NULL;
61 static TLS int zlibAfterBufLen = 0;
62 
63 void rfbZlibCleanup(rfbScreenInfoPtr screen)
64 {
65  if (zlibBeforeBufSize) {
66  free(zlibBeforeBuf);
67  zlibBeforeBufSize=0;
68  }
69  if (zlibAfterBufSize) {
70  zlibAfterBufSize=0;
71  free(zlibAfterBuf);
72  }
73 }
74 
75 
76 /*
77  * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
78  * rectangle encoding.
79  */
80 
81 static rfbBool
82 rfbSendOneRectEncodingZlib(rfbClientPtr cl,
83  int x,
84  int y,
85  int w,
86  int h)
87 {
89  rfbZlibHeader hdr;
90  int deflateResult;
91  int previousOut;
92  int i;
93  char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
94  + (x * (cl->scaledScreen->bitsPerPixel / 8)));
95 
96  int maxRawSize;
97  int maxCompSize;
98 
99  maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height
100  * (cl->format.bitsPerPixel / 8));
101 
102  if (!zlibBeforeBuf || zlibBeforeBufSize < maxRawSize) {
103  if (zlibBeforeBuf == NULL)
104  zlibBeforeBuf = (char *)malloc(maxRawSize);
105  else {
106  char *reallocedBeforeEncBuf = (char *)realloc(zlibBeforeBuf, maxRawSize);
107  if (!reallocedBeforeEncBuf) return FALSE;
108  zlibBeforeBuf = reallocedBeforeEncBuf;
109  }
110  if(zlibBeforeBuf)
111  zlibBeforeBufSize = maxRawSize;
112  }
113 
114  /* zlib compression is not useful for very small data sets.
115  * So, we just send these raw without any compression.
116  */
117  if (( w * h * (cl->scaledScreen->bitsPerPixel / 8)) <
118  VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) {
119 
120  int result;
121 
122  /* The translation function (used also by the in raw encoding)
123  * requires 4/2/1 byte alignment in the output buffer (which is
124  * updateBuf for the raw encoding) based on the bitsPerPixel of
125  * the viewer/client. This prevents SIGBUS errors on some
126  * architectures like SPARC, PARISC...
127  */
128  if (( cl->format.bitsPerPixel > 8 ) &&
129  ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) {
130  if (!rfbSendUpdateBuf(cl))
131  return FALSE;
132  }
133 
134  result = rfbSendRectEncodingRaw(cl, x, y, w, h);
135 
136  return result;
137 
138  }
139 
140  /*
141  * zlib requires output buffer to be slightly larger than the input
142  * buffer, in the worst case.
143  */
144  maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12;
145 
146  if (!zlibAfterBuf || zlibAfterBufSize < maxCompSize) {
147  if (zlibAfterBuf == NULL)
148  zlibAfterBuf = (char *)malloc(maxCompSize);
149  else {
150  char *reallocedAfterEncBuf = (char *)realloc(zlibAfterBuf, maxCompSize);
151  if (!reallocedAfterEncBuf) return FALSE;
152  zlibAfterBuf = reallocedAfterEncBuf;
153  }
154  if(zlibAfterBuf)
155  zlibAfterBufSize = maxCompSize;
156  }
157 
158  if (!zlibBeforeBuf || !zlibAfterBuf)
159  {
160  rfbLog("rfbSendOneRectEncodingZlib: failed to allocate memory\n");
161  return FALSE;
162  }
163 
164  /*
165  * Convert pixel data to client format.
166  */
167  (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
168  &cl->format, fbptr, zlibBeforeBuf,
169  cl->scaledScreen->paddedWidthInBytes, w, h);
170 
171  cl->compStream.next_in = ( Bytef * )zlibBeforeBuf;
172  cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8);
173  cl->compStream.next_out = ( Bytef * )zlibAfterBuf;
174  cl->compStream.avail_out = maxCompSize;
175  cl->compStream.data_type = Z_BINARY;
176 
177  /* Initialize the deflation state. */
178  if ( cl->compStreamInited == FALSE ) {
179 
180  cl->compStream.total_in = 0;
181  cl->compStream.total_out = 0;
182  cl->compStream.zalloc = Z_NULL;
183  cl->compStream.zfree = Z_NULL;
184  cl->compStream.opaque = Z_NULL;
185 
186  deflateInit2( &(cl->compStream),
187  cl->zlibCompressLevel,
188  Z_DEFLATED,
189  MAX_WBITS,
190  MAX_MEM_LEVEL,
191  Z_DEFAULT_STRATEGY );
192  /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */
193  /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */
194  cl->compStreamInited = TRUE;
195 
196  }
197 
198  previousOut = cl->compStream.total_out;
199 
200  /* Perform the compression here. */
201  deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH );
202 
203  /* Find the total size of the resulting compressed data. */
204  zlibAfterBufLen = cl->compStream.total_out - previousOut;
205 
206  if ( deflateResult != Z_OK ) {
207  rfbErr("zlib deflation error: %s\n", cl->compStream.msg);
208  return FALSE;
209  }
210 
211  /* Note that it is not possible to switch zlib parameters based on
212  * the results of the compression pass. The reason is
213  * that we rely on the compressor and decompressor states being
214  * in sync. Compressing and then discarding the results would
215  * cause lose of synchronization.
216  */
217 
218  /* Update statics */
220  + w * (cl->format.bitsPerPixel / 8) * h);
221 
223  > UPDATE_BUF_SIZE)
224  {
225  if (!rfbSendUpdateBuf(cl))
226  return FALSE;
227  }
228 
229  rect.r.x = Swap16IfLE(x);
230  rect.r.y = Swap16IfLE(y);
231  rect.r.w = Swap16IfLE(w);
232  rect.r.h = Swap16IfLE(h);
234 
235  memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
238 
239  hdr.nBytes = Swap32IfLE(zlibAfterBufLen);
240 
241  memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
242  cl->ublen += sz_rfbZlibHeader;
243 
244  for (i = 0; i < zlibAfterBufLen;) {
245 
246  int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
247 
248  if (i + bytesToCopy > zlibAfterBufLen) {
249  bytesToCopy = zlibAfterBufLen - i;
250  }
251 
252  memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy);
253 
254  cl->ublen += bytesToCopy;
255  i += bytesToCopy;
256 
257  if (cl->ublen == UPDATE_BUF_SIZE) {
258  if (!rfbSendUpdateBuf(cl))
259  return FALSE;
260  }
261  }
262 
263  return TRUE;
264 
265 }
266 
267 
268 /*
269  * rfbSendRectEncodingZlib - send a given rectangle using one or more
270  * Zlib encoding rectangles.
271  */
272 
273 rfbBool
274 rfbSendRectEncodingZlib(rfbClientPtr cl,
275  int x,
276  int y,
277  int w,
278  int h)
279 {
280  int maxLines;
281  int linesRemaining;
282  rfbRectangle partialRect;
283 
284  partialRect.x = x;
285  partialRect.y = y;
286  partialRect.w = w;
287  partialRect.h = h;
288 
289  /* Determine maximum pixel/scan lines allowed per rectangle. */
290  maxLines = ( ZLIB_MAX_SIZE(w) / w );
291 
292  /* Initialize number of scan lines left to do. */
293  linesRemaining = h;
294 
295  /* Loop until all work is done. */
296  while ( linesRemaining > 0 ) {
297 
298  int linesToComp;
299 
300  if ( maxLines < linesRemaining )
301  linesToComp = maxLines;
302  else
303  linesToComp = linesRemaining;
304 
305  partialRect.h = linesToComp;
306 
307  /* Encode (compress) and send the next rectangle. */
308  if ( ! rfbSendOneRectEncodingZlib( cl,
309  partialRect.x,
310  partialRect.y,
311  partialRect.w,
312  partialRect.h )) {
313 
314  return FALSE;
315  }
316 
317  /* Technically, flushing the buffer here is not extremely
318  * efficient. However, this improves the overall throughput
319  * of the system over very slow networks. By flushing
320  * the buffer with every maximum size zlib rectangle, we
321  * improve the pipelining usage of the server CPU, network,
322  * and viewer CPU components. Insuring that these components
323  * are working in parallel actually improves the performance
324  * seen by the user.
325  * Since, zlib is most useful for slow networks, this flush
326  * is appropriate for the desired behavior of the zlib encoding.
327  */
328  if (( cl->ublen > 0 ) &&
329  ( linesToComp == maxLines )) {
330  if (!rfbSendUpdateBuf(cl)) {
331 
332  return FALSE;
333  }
334  }
335 
336  /* Update remaining and incremental rectangle location. */
337  linesRemaining -= linesToComp;
338  partialRect.y += linesToComp;
339 
340  }
341 
342  return TRUE;
343 
344 }
345 
int x
Definition: SDLvncviewer.c:34
rfbLogProc rfbErr
Definition: main.c:264
#define TRUE
Definition: rfbproto.h:112
rfbBool rfbSendRectEncodingRaw(rfbClientPtr cl, int x, int y, int w, int h)
Definition: rfbserver.c:3373
#define Swap32IfLE(l)
Definition: rfb.h:718
int8_t rfbBool
Definition: rfbproto.h:108
uint32_t nBytes
Definition: rfbproto.h:724
void rfbZlibCleanup(rfbScreenInfoPtr screen)
Definition: zlib.c:63
#define UPDATE_BUF_SIZE
UPDATE_BUF_SIZE must be big enough to send at least one whole line of the framebuffer.
Definition: rfb.h:558
#define sz_rfbZlibHeader
Definition: rfbproto.h:727
uint16_t y
Definition: rfbproto.h:142
rfbBool rfbSendUpdateBuf(rfbClientPtr cl)
Definition: rfbserver.c:3615
uint16_t h
Definition: rfbproto.h:144
uint16_t w
Definition: rfbproto.h:143
void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
Definition: stats.c:220
uint16_t x
Definition: rfbproto.h:141
int y
Definition: SDLvncviewer.c:34
#define sz_rfbFramebufferUpdateRectHeader
Definition: rfbproto.h:567
rfbLogProc rfbLog
Definition: main.c:263
#define rfbEncodingZlib
Definition: rfbproto.h:444
#define FALSE
Definition: rfbproto.h:110
#define TLS
Definition: zlib.c:53
rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, int h)
Definition: zlib.c:274
#define Swap16IfLE(s)
Definition: rfb.h:716