| /* |
| * 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> |
| #include "minilzo.h" |
| |
| /* |
| * 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 extrememly |
| * 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; |
| |
| } |