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
30void rfbFreeUltraData(rfbClientPtr cl) {
31  if (cl->compStreamInitedLZO) {
32    free(cl->lzoWrkMem);
33    cl->compStreamInitedLZO=FALSE;
34  }
35}
36
37
38static rfbBool
39rfbSendOneRectEncodingUltra(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
160rfbBool
161rfbSendRectEncodingUltra(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