/* * 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 #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 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; }