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
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
332