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