added libvnc/ with RealVNC compatibility fix
[presencevnc] / libvnc / libvncserver / ultra.c
1 /*
2  * ultra.c
3  *
4  * Routines to implement ultra based encoding (minilzo).
5  * ultrazip supports packed rectangles if the rects are tiny...
6  * This improves performance as lzo has more data to work with at once
7  * This is 'UltraZip' and is currently not implemented.
8  */
9
10 #include <rfb/rfb.h>
11 #include "minilzo.h"
12
13 /*
14  * lzoBeforeBuf contains pixel data in the client's format.
15  * lzoAfterBuf contains the lzo (deflated) encoding version.
16  * If the lzo compressed/encoded version is
17  * larger than the raw data or if it exceeds lzoAfterBufSize then
18  * raw encoding is used instead.
19  */
20
21 static int lzoBeforeBufSize = 0;
22 static char *lzoBeforeBuf = NULL;
23
24 static int lzoAfterBufSize = 0;
25 static char *lzoAfterBuf = NULL;
26 static int lzoAfterBufLen = 0;
27
28 /*
29  * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
30  *                              rectangle encoding.
31  */
32
33 #define MAX_WRKMEM ((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)
34
35 void rfbUltraCleanup(rfbScreenInfoPtr screen)
36 {
37   if (lzoBeforeBufSize) {
38     free(lzoBeforeBuf);
39     lzoBeforeBufSize=0;
40   }
41   if (lzoAfterBufSize) {
42     free(lzoAfterBuf);
43     lzoAfterBufSize=0;
44   }
45 }
46
47 void rfbFreeUltraData(rfbClientPtr cl) {
48   if (cl->compStreamInitedLZO) {
49     free(cl->lzoWrkMem);
50     cl->compStreamInitedLZO=FALSE;
51   }
52 }
53
54
55 static rfbBool
56 rfbSendOneRectEncodingUltra(rfbClientPtr cl,
57                            int x,
58                            int y,
59                            int w,
60                            int h)
61 {
62     rfbFramebufferUpdateRectHeader rect;
63     rfbZlibHeader hdr;
64     int deflateResult;
65     int i;
66     char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
67            + (x * (cl->scaledScreen->bitsPerPixel / 8)));
68
69     int maxRawSize;
70     int maxCompSize;
71
72     maxRawSize = (w * h * (cl->format.bitsPerPixel / 8));
73
74     if (lzoBeforeBufSize < maxRawSize) {
75         lzoBeforeBufSize = maxRawSize;
76         if (lzoBeforeBuf == NULL)
77             lzoBeforeBuf = (char *)malloc(lzoBeforeBufSize);
78         else
79             lzoBeforeBuf = (char *)realloc(lzoBeforeBuf, lzoBeforeBufSize);
80     }
81
82     /*
83      * lzo requires output buffer to be slightly larger than the input
84      * buffer, in the worst case.
85      */
86     maxCompSize = (maxRawSize + maxRawSize / 16 + 64 + 3);
87
88     if (lzoAfterBufSize < maxCompSize) {
89         lzoAfterBufSize = maxCompSize;
90         if (lzoAfterBuf == NULL)
91             lzoAfterBuf = (char *)malloc(lzoAfterBufSize);
92         else
93             lzoAfterBuf = (char *)realloc(lzoAfterBuf, lzoAfterBufSize);
94     }
95
96     /* 
97      * Convert pixel data to client format.
98      */
99     (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
100                        &cl->format, fbptr, lzoBeforeBuf,
101                        cl->scaledScreen->paddedWidthInBytes, w, h);
102
103     if ( cl->compStreamInitedLZO == FALSE ) {
104         cl->compStreamInitedLZO = TRUE;
105         /* Work-memory needed for compression. Allocate memory in units
106          * of `lzo_align_t' (instead of `char') to make sure it is properly aligned.
107          */  
108         cl->lzoWrkMem = malloc(sizeof(lzo_align_t) * (((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)));
109     }
110
111     /* Perform the compression here. */
112     deflateResult = lzo1x_1_compress((unsigned char *)lzoBeforeBuf, (lzo_uint)(w * h * (cl->format.bitsPerPixel / 8)), (unsigned char *)lzoAfterBuf, (lzo_uint *)&maxCompSize, cl->lzoWrkMem);
113     /* maxCompSize now contains the compressed size */
114
115     /* Find the total size of the resulting compressed data. */
116     lzoAfterBufLen = maxCompSize;
117
118     if ( deflateResult != LZO_E_OK ) {
119         rfbErr("lzo deflation error: %d\n", deflateResult);
120         return FALSE;
121     }
122
123     /* Update statics */
124     rfbStatRecordEncodingSent(cl, rfbEncodingUltra, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + lzoAfterBufLen, maxRawSize);
125
126     if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader
127         > UPDATE_BUF_SIZE)
128     {
129         if (!rfbSendUpdateBuf(cl))
130             return FALSE;
131     }
132
133     rect.r.x = Swap16IfLE(x);
134     rect.r.y = Swap16IfLE(y);
135     rect.r.w = Swap16IfLE(w);
136     rect.r.h = Swap16IfLE(h);
137     rect.encoding = Swap32IfLE(rfbEncodingUltra);
138
139     memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
140            sz_rfbFramebufferUpdateRectHeader);
141     cl->ublen += sz_rfbFramebufferUpdateRectHeader;
142
143     hdr.nBytes = Swap32IfLE(lzoAfterBufLen);
144
145     memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
146     cl->ublen += sz_rfbZlibHeader;
147
148     /* We might want to try sending the data directly... */
149     for (i = 0; i < lzoAfterBufLen;) {
150
151         int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
152
153         if (i + bytesToCopy > lzoAfterBufLen) {
154             bytesToCopy = lzoAfterBufLen - i;
155         }
156
157         memcpy(&cl->updateBuf[cl->ublen], &lzoAfterBuf[i], bytesToCopy);
158
159         cl->ublen += bytesToCopy;
160         i += bytesToCopy;
161
162         if (cl->ublen == UPDATE_BUF_SIZE) {
163             if (!rfbSendUpdateBuf(cl))
164                 return FALSE;
165         }
166     }
167
168     return TRUE;
169
170 }
171
172 /*
173  * rfbSendRectEncodingUltra - send a given rectangle using one or more
174  *                           LZO encoding rectangles.
175  */
176
177 rfbBool
178 rfbSendRectEncodingUltra(rfbClientPtr cl,
179                         int x,
180                         int y,
181                         int w,
182                         int h)
183 {
184     int  maxLines;
185     int  linesRemaining;
186     rfbRectangle partialRect;
187
188     partialRect.x = x;
189     partialRect.y = y;
190     partialRect.w = w;
191     partialRect.h = h;
192
193     /* Determine maximum pixel/scan lines allowed per rectangle. */
194     maxLines = ( ULTRA_MAX_SIZE(w) / w );
195
196     /* Initialize number of scan lines left to do. */
197     linesRemaining = h;
198
199     /* Loop until all work is done. */
200     while ( linesRemaining > 0 ) {
201
202         int linesToComp;
203
204         if ( maxLines < linesRemaining )
205             linesToComp = maxLines;
206         else
207             linesToComp = linesRemaining;
208
209         partialRect.h = linesToComp;
210
211         /* Encode (compress) and send the next rectangle. */
212         if ( ! rfbSendOneRectEncodingUltra( cl,
213                                            partialRect.x,
214                                            partialRect.y,
215                                            partialRect.w,
216                                            partialRect.h )) {
217
218             return FALSE;
219         }
220
221         /* Technically, flushing the buffer here is not extrememly
222          * efficient.  However, this improves the overall throughput
223          * of the system over very slow networks.  By flushing
224          * the buffer with every maximum size lzo rectangle, we
225          * improve the pipelining usage of the server CPU, network,
226          * and viewer CPU components.  Insuring that these components
227          * are working in parallel actually improves the performance
228          * seen by the user.
229          * Since, lzo is most useful for slow networks, this flush
230          * is appropriate for the desired behavior of the lzo encoding.
231          */
232         if (( cl->ublen > 0 ) &&
233             ( linesToComp == maxLines )) {
234             if (!rfbSendUpdateBuf(cl)) {
235
236                 return FALSE;
237             }
238         }
239
240         /* Update remaining and incremental rectangle location. */
241         linesRemaining -= linesToComp;
242         partialRect.y += linesToComp;
243
244     }
245
246     return TRUE;
247
248 }