4 * Routines to implement zlib based encoding (deflate).
8 * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
9 * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
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.
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.
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,
26 * For the latest source code, please check:
28 * http://www.developVNC.org/
30 * or send email to feedback@developvnc.org.
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.
43 static int zlibBeforeBufSize = 0;
44 static char *zlibBeforeBuf = NULL;
46 static int zlibAfterBufSize = 0;
47 static char *zlibAfterBuf = NULL;
48 static int zlibAfterBufLen;
50 void rfbZlibCleanup(rfbScreenInfoPtr screen)
52 if (zlibBeforeBufSize) {
56 if (zlibAfterBufSize) {
64 * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
69 rfbSendOneRectEncodingZlib(rfbClientPtr cl,
75 rfbFramebufferUpdateRectHeader rect;
80 char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
81 + (x * (cl->scaledScreen->bitsPerPixel / 8)));
86 maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height
87 * (cl->format.bitsPerPixel / 8));
89 if (zlibBeforeBufSize < maxRawSize) {
90 zlibBeforeBufSize = maxRawSize;
91 if (zlibBeforeBuf == NULL)
92 zlibBeforeBuf = (char *)malloc(zlibBeforeBufSize);
94 zlibBeforeBuf = (char *)realloc(zlibBeforeBuf, zlibBeforeBufSize);
97 /* zlib compression is not useful for very small data sets.
98 * So, we just send these raw without any compression.
100 if (( w * h * (cl->scaledScreen->bitsPerPixel / 8)) <
101 VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) {
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...
111 if (( cl->format.bitsPerPixel > 8 ) &&
112 ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) {
113 if (!rfbSendUpdateBuf(cl))
117 result = rfbSendRectEncodingRaw(cl, x, y, w, h);
124 * zlib requires output buffer to be slightly larger than the input
125 * buffer, in the worst case.
127 maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12;
129 if (zlibAfterBufSize < maxCompSize) {
130 zlibAfterBufSize = maxCompSize;
131 if (zlibAfterBuf == NULL)
132 zlibAfterBuf = (char *)malloc(zlibAfterBufSize);
134 zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize);
139 * Convert pixel data to client format.
141 (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
142 &cl->format, fbptr, zlibBeforeBuf,
143 cl->scaledScreen->paddedWidthInBytes, w, h);
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;
151 /* Initialize the deflation state. */
152 if ( cl->compStreamInited == FALSE ) {
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;
160 deflateInit2( &(cl->compStream),
161 cl->zlibCompressLevel,
165 Z_DEFAULT_STRATEGY );
166 /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */
167 /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */
168 cl->compStreamInited = TRUE;
172 previousOut = cl->compStream.total_out;
174 /* Perform the compression here. */
175 deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH );
177 /* Find the total size of the resulting compressed data. */
178 zlibAfterBufLen = cl->compStream.total_out - previousOut;
180 if ( deflateResult != Z_OK ) {
181 rfbErr("zlib deflation error: %s\n", cl->compStream.msg);
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.
193 rfbStatRecordEncodingSent(cl, rfbEncodingZlib, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + zlibAfterBufLen,
194 + w * (cl->format.bitsPerPixel / 8) * h);
196 if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader
199 if (!rfbSendUpdateBuf(cl))
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);
209 memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
210 sz_rfbFramebufferUpdateRectHeader);
211 cl->ublen += sz_rfbFramebufferUpdateRectHeader;
213 hdr.nBytes = Swap32IfLE(zlibAfterBufLen);
215 memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
216 cl->ublen += sz_rfbZlibHeader;
218 for (i = 0; i < zlibAfterBufLen;) {
220 int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
222 if (i + bytesToCopy > zlibAfterBufLen) {
223 bytesToCopy = zlibAfterBufLen - i;
226 memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy);
228 cl->ublen += bytesToCopy;
231 if (cl->ublen == UPDATE_BUF_SIZE) {
232 if (!rfbSendUpdateBuf(cl))
243 * rfbSendRectEncodingZlib - send a given rectangle using one or more
244 * Zlib encoding rectangles.
248 rfbSendRectEncodingZlib(rfbClientPtr cl,
256 rfbRectangle partialRect;
263 /* Determine maximum pixel/scan lines allowed per rectangle. */
264 maxLines = ( ZLIB_MAX_SIZE(w) / w );
266 /* Initialize number of scan lines left to do. */
269 /* Loop until all work is done. */
270 while ( linesRemaining > 0 ) {
274 if ( maxLines < linesRemaining )
275 linesToComp = maxLines;
277 linesToComp = linesRemaining;
279 partialRect.h = linesToComp;
281 /* Encode (compress) and send the next rectangle. */
282 if ( ! rfbSendOneRectEncodingZlib( cl,
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
299 * Since, zlib is most useful for slow networks, this flush
300 * is appropriate for the desired behavior of the zlib encoding.
302 if (( cl->ublen > 0 ) &&
303 ( linesToComp == maxLines )) {
304 if (!rfbSendUpdateBuf(cl)) {
310 /* Update remaining and incremental rectangle location. */
311 linesRemaining -= linesToComp;
312 partialRect.y += linesToComp;