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