• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
rfbZlibCleanup(rfbScreenInfoPtr screen)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
rfbSendOneRectEncodingZlib(rfbClientPtr cl,int x,int y,int w,int h)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
rfbSendRectEncodingZlib(rfbClientPtr cl,int x,int y,int w,int h)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