You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
6.9 KiB
235 lines
6.9 KiB
/* |
|
* ultra.c |
|
* |
|
* Routines to implement ultra based encoding (minilzo). |
|
* ultrazip supports packed rectangles if the rects are tiny... |
|
* This improves performance as lzo has more data to work with at once |
|
* This is 'UltraZip' and is currently not implemented. |
|
*/ |
|
|
|
#include <rfb/rfb.h> |
|
#ifdef LIBVNCSERVER_HAVE_LZO |
|
#include <lzo/lzo1x.h> |
|
#else |
|
#include "minilzo.h" |
|
#endif |
|
|
|
/* |
|
* cl->beforeEncBuf contains pixel data in the client's format. |
|
* cl->afterEncBuf contains the lzo (deflated) encoding version. |
|
* If the lzo compressed/encoded version is |
|
* larger than the raw data or if it exceeds cl->afterEncBufSize then |
|
* raw encoding is used instead. |
|
*/ |
|
|
|
|
|
/* |
|
* rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib |
|
* rectangle encoding. |
|
*/ |
|
|
|
#define MAX_WRKMEM ((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) |
|
|
|
|
|
void rfbFreeUltraData(rfbClientPtr cl) { |
|
if (cl->compStreamInitedLZO) { |
|
free(cl->lzoWrkMem); |
|
cl->compStreamInitedLZO=FALSE; |
|
} |
|
} |
|
|
|
|
|
static rfbBool |
|
rfbSendOneRectEncodingUltra(rfbClientPtr cl, |
|
int x, |
|
int y, |
|
int w, |
|
int h) |
|
{ |
|
rfbFramebufferUpdateRectHeader rect; |
|
rfbZlibHeader hdr; |
|
int deflateResult; |
|
int i; |
|
char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) |
|
+ (x * (cl->scaledScreen->bitsPerPixel / 8))); |
|
|
|
int maxRawSize; |
|
lzo_uint maxCompSize; |
|
|
|
maxRawSize = (w * h * (cl->format.bitsPerPixel / 8)); |
|
|
|
if (cl->beforeEncBufSize < maxRawSize) { |
|
cl->beforeEncBufSize = maxRawSize; |
|
if (cl->beforeEncBuf == NULL) |
|
cl->beforeEncBuf = (char *)malloc(cl->beforeEncBufSize); |
|
else |
|
cl->beforeEncBuf = (char *)realloc(cl->beforeEncBuf, cl->beforeEncBufSize); |
|
} |
|
|
|
/* |
|
* lzo requires output buffer to be slightly larger than the input |
|
* buffer, in the worst case. |
|
*/ |
|
maxCompSize = (maxRawSize + maxRawSize / 16 + 64 + 3); |
|
|
|
if (cl->afterEncBufSize < (int)maxCompSize) { |
|
cl->afterEncBufSize = maxCompSize; |
|
if (cl->afterEncBuf == NULL) |
|
cl->afterEncBuf = (char *)malloc(cl->afterEncBufSize); |
|
else |
|
cl->afterEncBuf = (char *)realloc(cl->afterEncBuf, cl->afterEncBufSize); |
|
} |
|
|
|
/* |
|
* Convert pixel data to client format. |
|
*/ |
|
(*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, |
|
&cl->format, fbptr, cl->beforeEncBuf, |
|
cl->scaledScreen->paddedWidthInBytes, w, h); |
|
|
|
if ( cl->compStreamInitedLZO == FALSE ) { |
|
cl->compStreamInitedLZO = TRUE; |
|
/* Work-memory needed for compression. Allocate memory in units |
|
* of `lzo_align_t' (instead of `char') to make sure it is properly aligned. |
|
*/ |
|
cl->lzoWrkMem = malloc(sizeof(lzo_align_t) * (((LZO1X_1_MEM_COMPRESS) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t))); |
|
} |
|
|
|
/* Perform the compression here. */ |
|
deflateResult = lzo1x_1_compress((unsigned char *)cl->beforeEncBuf, (lzo_uint)(w * h * (cl->format.bitsPerPixel / 8)), (unsigned char *)cl->afterEncBuf, &maxCompSize, cl->lzoWrkMem); |
|
/* maxCompSize now contains the compressed size */ |
|
|
|
/* Find the total size of the resulting compressed data. */ |
|
cl->afterEncBufLen = maxCompSize; |
|
|
|
if ( deflateResult != LZO_E_OK ) { |
|
rfbErr("lzo deflation error: %d\n", deflateResult); |
|
return FALSE; |
|
} |
|
|
|
/* Update statics */ |
|
rfbStatRecordEncodingSent(cl, rfbEncodingUltra, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + cl->afterEncBufLen, maxRawSize); |
|
|
|
if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader |
|
> UPDATE_BUF_SIZE) |
|
{ |
|
if (!rfbSendUpdateBuf(cl)) |
|
return FALSE; |
|
} |
|
|
|
rect.r.x = Swap16IfLE(x); |
|
rect.r.y = Swap16IfLE(y); |
|
rect.r.w = Swap16IfLE(w); |
|
rect.r.h = Swap16IfLE(h); |
|
rect.encoding = Swap32IfLE(rfbEncodingUltra); |
|
|
|
memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
|
sz_rfbFramebufferUpdateRectHeader); |
|
cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
|
|
|
hdr.nBytes = Swap32IfLE(cl->afterEncBufLen); |
|
|
|
memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader); |
|
cl->ublen += sz_rfbZlibHeader; |
|
|
|
/* We might want to try sending the data directly... */ |
|
for (i = 0; i < cl->afterEncBufLen;) { |
|
|
|
int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; |
|
|
|
if (i + bytesToCopy > cl->afterEncBufLen) { |
|
bytesToCopy = cl->afterEncBufLen - i; |
|
} |
|
|
|
memcpy(&cl->updateBuf[cl->ublen], &cl->afterEncBuf[i], bytesToCopy); |
|
|
|
cl->ublen += bytesToCopy; |
|
i += bytesToCopy; |
|
|
|
if (cl->ublen == UPDATE_BUF_SIZE) { |
|
if (!rfbSendUpdateBuf(cl)) |
|
return FALSE; |
|
} |
|
} |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
/* |
|
* rfbSendRectEncodingUltra - send a given rectangle using one or more |
|
* LZO encoding rectangles. |
|
*/ |
|
|
|
rfbBool |
|
rfbSendRectEncodingUltra(rfbClientPtr cl, |
|
int x, |
|
int y, |
|
int w, |
|
int h) |
|
{ |
|
int maxLines; |
|
int linesRemaining; |
|
rfbRectangle partialRect; |
|
|
|
partialRect.x = x; |
|
partialRect.y = y; |
|
partialRect.w = w; |
|
partialRect.h = h; |
|
|
|
/* Determine maximum pixel/scan lines allowed per rectangle. */ |
|
maxLines = ( ULTRA_MAX_SIZE(w) / w ); |
|
|
|
/* Initialize number of scan lines left to do. */ |
|
linesRemaining = h; |
|
|
|
/* Loop until all work is done. */ |
|
while ( linesRemaining > 0 ) { |
|
|
|
int linesToComp; |
|
|
|
if ( maxLines < linesRemaining ) |
|
linesToComp = maxLines; |
|
else |
|
linesToComp = linesRemaining; |
|
|
|
partialRect.h = linesToComp; |
|
|
|
/* Encode (compress) and send the next rectangle. */ |
|
if ( ! rfbSendOneRectEncodingUltra( cl, |
|
partialRect.x, |
|
partialRect.y, |
|
partialRect.w, |
|
partialRect.h )) { |
|
|
|
return FALSE; |
|
} |
|
|
|
/* Technically, flushing the buffer here is not extremely |
|
* efficient. However, this improves the overall throughput |
|
* of the system over very slow networks. By flushing |
|
* the buffer with every maximum size lzo rectangle, we |
|
* improve the pipelining usage of the server CPU, network, |
|
* and viewer CPU components. Insuring that these components |
|
* are working in parallel actually improves the performance |
|
* seen by the user. |
|
* Since, lzo is most useful for slow networks, this flush |
|
* is appropriate for the desired behavior of the lzo encoding. |
|
*/ |
|
if (( cl->ublen > 0 ) && |
|
( linesToComp == maxLines )) { |
|
if (!rfbSendUpdateBuf(cl)) { |
|
|
|
return FALSE; |
|
} |
|
} |
|
|
|
/* Update remaining and incremental rectangle location. */ |
|
linesRemaining -= linesToComp; |
|
partialRect.y += linesToComp; |
|
|
|
} |
|
|
|
return TRUE; |
|
|
|
}
|
|
|