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