blob: ac20c9c9aa305832a4ffbca61da11f11c1792ea1 [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
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
55static TLS int zlibBeforeBufSize = 0;
56static TLS char *zlibBeforeBuf = NULL;
57
58static TLS int zlibAfterBufSize = 0;
59static TLS char *zlibAfterBuf = NULL;
60static TLS int zlibAfterBufLen = 0;
61
62void 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
80static rfbBool
81rfbSendOneRectEncodingZlib(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
259rfbBool
260rfbSendRectEncodingZlib(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