From 896ca2036c35b89a7f63e1adefe5e3724bf4d40d Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 19 Jul 2011 13:40:34 +0200 Subject: [PATCH] tightPng: Add initial tightPng encoding support. http://wiki.qemu.org/VNC_Tight_PNG Signed-off-by: Joel Martin Signed-off-by: Christian Beier --- CMakeLists.txt | 22 ++- configure.ac | 55 +++++++ libvncserver/Makefile.am | 8 +- libvncserver/rfbserver.c | 49 +++++- libvncserver/stats.c | 1 + libvncserver/tight.c | 321 ++++++++++++++++++++++++++++++++++----- rfb/rfb.h | 17 ++- rfb/rfbconfig.h.cmake | 3 + rfb/rfbproto.h | 22 ++- 9 files changed, 436 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5425cc..bf17aa3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/ find_package(ZLIB) find_package(JPEG) +find_package(PNG) find_package(SDL) find_package(GnuTLS) find_package(Threads) @@ -30,6 +31,9 @@ endif(ZLIB_FOUND) if(JPEG_FOUND) set(LIBVNCSERVER_HAVE_LIBJPEG 1) endif(JPEG_FOUND) +if(PNG_FOUND) + set(LIBVNCSERVER_HAVE_LIBPNG 1) +endif(PNG_FOUND) option(LIBVNCSERVER_ALLOW24BPP "Allow 24 bpp" ON) if(GNUTLS_FOUND) set(LIBVNCSERVER_WITH_CLIENT_TLS 1) @@ -142,12 +146,21 @@ endif(ZLIB_FOUND) if(JPEG_FOUND) add_definitions(-DLIBVNCSERVER_HAVE_LIBJPEG) include_directories(${JPEG_INCLUDE_DIR}) - set(LIBVNCSERVER_SOURCES - ${LIBVNCSERVER_SOURCES} - ${LIBVNCSERVER_DIR}/tight.c - ) + set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c) endif(JPEG_FOUND) +if(PNG_FOUND) + add_definitions(-DLIBVNCSERVER_HAVE_LIBPNG) + include_directories(${PNG_INCLUDE_DIR}) + set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c) +endif(PNG_FOUND) + + +set(LIBVNCSERVER_SOURCES + ${LIBVNCSERVER_SOURCES} + ${TIGHT_C} +) + if(TIGHTVNC_FILETRANSFER) set(LIBVNCSERVER_SOURCES ${LIBVNCSERVER_SOURCES} @@ -174,6 +187,7 @@ target_link_libraries(vncserver ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} ${SDL_LIBRARY} ) diff --git a/configure.ac b/configure.ac index 3203d7a..280ea58 100644 --- a/configure.ac +++ b/configure.ac @@ -578,6 +578,60 @@ ftp://ftp.uu.net/graphics/jpeg/ fi fi +AC_ARG_WITH(png, +[ --without-png disable support for png] +[ --with-png=DIR use png include/library files in DIR],,) + +# At this point: +# no png on command line with_png="" +# -with-png with_png="yes" +# -without-png with_png="no" +# -with-png=/foo/dir with_png="/foo/dir" + +if test "x$with_png" != "xno"; then + if test ! -z "$with_png" -a "x$with_png" != "xyes"; then + # add user supplied directory to flags: + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS -I$with_png/include" + LDFLAGS="$LDFLAGS -L$with_png/lib" + if test "x$ld_minus_R" = "xno"; then + : + elif test "x$GCC" = "xyes"; then + # this is not complete... in general a rat's nest. + LDFLAGS="$LDFLAGS -Xlinker -R$with_png/lib" + else + LDFLAGS="$LDFLAGS -R$with_png/lib" + fi + fi + AC_CHECK_HEADER(png.h, HAVE_PNGLIB_H="true") + if test "x$HAVE_PNGLIB_H" = "xtrue"; then + AC_CHECK_LIB(png, png_create_write_struct, , HAVE_PNGLIB_H="") + fi + if test ! -z "$with_png" -a "x$with_png" != "xyes"; then + if test "x$HAVE_PNGLIB_H" != "xtrue"; then + # restore old flags on failure: + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" + fi + fi + if test "$build_x11vnc" = "yes"; then + if test "x$HAVE_PNGLIB_H" != "xtrue"; then + AC_MSG_WARN([ +========================================================================== +*** The libpng compression library was not found. *** +This may lead to reduced performance, especially over slow links. +If libpng is in a non-standard location use --with-png=DIR to +indicate the header file is in DIR/include/png.h and the library +in DIR/lib/libpng.a. A copy of libpng may be obtained from: +http://www.libpng.org/pub/png/libpng.html +========================================================================== +]) + sleep 5 + fi + fi +fi + AC_ARG_WITH(libz, [ --without-libz disable support for deflate],,) AC_ARG_WITH(zlib, @@ -654,6 +708,7 @@ AM_CONDITIONAL(WITH_TIGHTVNC_FILETRANSFER, test "$with_tightvnc_filetransfer" = AM_CONDITIONAL(HAVE_LIBZ, test ! -z "$HAVE_ZLIB_H") AM_CONDITIONAL(HAVE_LIBJPEG, test ! -z "$HAVE_JPEGLIB_H") +AM_CONDITIONAL(HAVE_LIBPNG, test ! -z "$HAVE_PNGLIB_H") SDLCONFIG="sdl-config" diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 538617f..a685ed1 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -29,7 +29,11 @@ EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ if HAVE_LIBZ ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c ../common/zywrletemplate.c if HAVE_LIBJPEG -JPEGSRCS = tight.c +TIGHTSRCS = tight.c +else +if HAVE_LIBPNG +TIGHTSRCS = tight.c +endif endif endif @@ -37,7 +41,7 @@ LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ httpd.c cursor.c font.c \ draw.c selbox.c ../common/d3des.c ../common/vncauth.c cargs.c ../common/minilzo.c ultra.c scale.c \ - $(ZLIBSRCS) $(JPEGSRCS) $(TIGHTVNCFILETRANSFERSRCS) + $(ZLIBSRCS) $(TIGHTSRCS) $(TIGHTVNCFILETRANSFERSRCS) libvncserver_la_SOURCES=$(LIB_SRCS) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 8f0e390..587a2f0 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -358,10 +358,12 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbScreen->clientHead = cl; UNLOCK(rfbClientListMutex); -#ifdef LIBVNCSERVER_HAVE_LIBZ +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; +#endif +#ifdef LIBVNCSERVER_HAVE_LIBJPEG { int i; for (i = 0; i < 4; i++) @@ -917,6 +919,9 @@ rfbSendSupportedEncodings(rfbClientPtr cl) #endif #ifdef LIBVNCSERVER_HAVE_LIBJPEG rfbEncodingTight, +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + rfbEncodingTightPng, #endif rfbEncodingUltra, rfbEncodingUltraZip, @@ -1937,6 +1942,9 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) #ifdef LIBVNCSERVER_HAVE_LIBJPEG case rfbEncodingTight: #endif +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: #endif /* The first supported encoding is the 'preferred' encoding */ if (cl->preferredEncoding == -1) @@ -2026,11 +2034,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) } break; default: -#ifdef LIBVNCSERVER_HAVE_LIBZ +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && enc <= (uint32_t)rfbEncodingCompressLevel9 ) { cl->zlibCompressLevel = enc & 0x0F; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = enc & 0x0F; rfbLog("Using compression level %d for client %s\n", cl->tightCompressLevel, cl->host); @@ -2754,6 +2762,28 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, } sraRgnReleaseIterator(i); i=NULL; #endif +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + } else if (cl->preferredEncoding == rfbEncodingTightPng) { + nUpdateRegionRects = 0; + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + int n; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); + n = rfbNumCodedRectsTight(cl, x, y, w, h); + if (n == 0) { + nUpdateRegionRects = 0xFFFF; + break; + } + nUpdateRegionRects += n; + } + sraRgnReleaseIterator(i); i=NULL; #endif } else { nUpdateRegionRects = sraRgnCountRects(updateRegion); @@ -2773,6 +2803,10 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, /* Tight encoding counts the rectangles differently */ && cl->preferredEncoding != rfbEncodingTight #endif +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + /* Tight encoding counts the rectangles differently */ + && cl->preferredEncoding != rfbEncodingTightPng #endif && nUpdateRegionRects>cl->screen->maxRectsPerUpdate) { sraRegion* newUpdateRegion = sraRgnBBox(updateRegion); @@ -2868,6 +2902,13 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, break; #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: + /* TODO */ + if (!rfbSendRectEncodingTightPng(cl, x, y, w, h)) + goto updateFailed; + break; +#endif #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZRLE: case rfbEncodingZYWRLE: diff --git a/libvncserver/stats.c b/libvncserver/stats.c index 6dab13b..39de1c6 100644 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -100,6 +100,7 @@ char *encodingName(uint32_t type, char *buf, int len) { case rfbEncodingHextile: snprintf(buf, len, "hextile"); break; case rfbEncodingZlib: snprintf(buf, len, "zlib"); break; case rfbEncodingTight: snprintf(buf, len, "tight"); break; + case rfbEncodingTightPng: snprintf(buf, len, "tightPng"); break; case rfbEncodingZlibHex: snprintf(buf, len, "zlibhex"); break; case rfbEncodingUltra: snprintf(buf, len, "ultra"); break; case rfbEncodingZRLE: snprintf(buf, len, "ZRLE"); break; diff --git a/libvncserver/tight.c b/libvncserver/tight.c index bb033c3..83a25e3 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -37,6 +37,9 @@ #ifdef _RPCNDR_H /* This Windows header typedefs 'boolean', jpeglib has to know */ #define HAVE_BOOLEAN #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG +#include +#endif #include /* Note: The following constant should not be changed. */ @@ -91,6 +94,25 @@ static TIGHT_CONF tightConf[10] = { { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } }; +#ifdef LIBVNCSERVER_HAVE_LIBPNG +typedef struct TIGHT_PNG_CONF_s { + int png_zlib_level, png_filters; +} TIGHT_PNG_CONF; + +static TIGHT_PNG_CONF tightPngConf[10] = { + { 0, PNG_NO_FILTERS }, + { 1, PNG_NO_FILTERS }, + { 2, PNG_NO_FILTERS }, + { 3, PNG_NO_FILTERS }, + { 4, PNG_NO_FILTERS }, + { 5, PNG_ALL_FILTERS }, + { 6, PNG_ALL_FILTERS }, + { 7, PNG_ALL_FILTERS }, + { 8, PNG_ALL_FILTERS }, + { 9, PNG_ALL_FILTERS }, +}; +#endif + static TLS int compressLevel = 0; static TLS int qualityLevel = 0; @@ -146,6 +168,8 @@ void rfbTightCleanup(rfbScreenInfoPtr screen) /* Prototypes for static functions. */ +static rfbBool SendRectEncodingTight(rfbClientPtr cl, int x, int y, + int w, int h); static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, uint32_t colorValue, int *w_ptr, int *h_ptr); static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, @@ -165,10 +189,10 @@ static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendSolidRect (rfbClientPtr cl); -static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h); -static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h); -static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h); -static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h); +static rfbBool SendMonoRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendIndexedRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendFullColorRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendGradientRect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, int zlibLevel, int zlibStrategy); @@ -201,16 +225,20 @@ static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, i static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality); -static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); static void JpegInitDestination(j_compress_ptr cinfo); static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); static void JpegTermDestination(j_compress_ptr cinfo); static void JpegSetDstManager(j_compress_ptr cinfo); +#ifdef LIBVNCSERVER_HAVE_LIBPNG +static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h); +#endif /* * Tight encoding implementation. @@ -250,6 +278,29 @@ rfbSendRectEncodingTight(rfbClientPtr cl, int y, int w, int h) +{ + cl->tightEncoding = rfbEncodingTight; + return SendRectEncodingTight(cl, x, y, w, h); +} + +rfbBool +rfbSendRectEncodingTightPng(rfbClientPtr cl, + int x, + int y, + int w, + int h) +{ + cl->tightEncoding = rfbEncodingTightPng; + return SendRectEncodingTight(cl, x, y, w, h); +} + + +rfbBool +SendRectEncodingTight(rfbClientPtr cl, + int x, + int y, + int w, + int h) { int nMaxRows; uint32_t colorValue; @@ -341,7 +392,7 @@ rfbSendRectEncodingTight(rfbClientPtr cl, !SendRectSimple(cl, x, y, w, y_best-y) ) return FALSE; if ( x_best != x && - !rfbSendRectEncodingTight(cl, x, y_best, + !SendRectEncodingTight(cl, x, y_best, x_best-x, h_best) ) return FALSE; @@ -364,11 +415,11 @@ rfbSendRectEncodingTight(rfbClientPtr cl, /* Send remaining rectangles (at right and bottom). */ if ( x_best + w_best != x + w && - !rfbSendRectEncodingTight(cl, x_best+w_best, y_best, + !SendRectEncodingTight(cl, x_best+w_best, y_best, w-(x_best-x)-w_best, h_best) ) return FALSE; if ( y_best + h_best != y + h && - !rfbSendRectEncodingTight(cl, x, y_best+h_best, + !SendRectEncodingTight(cl, x, y_best+h_best, w, h-(y_best-y)-h_best) ) return FALSE; @@ -629,10 +680,10 @@ SendSubrect(rfbClientPtr cl, success = SendJpegRect(cl, x, y, w, h, tightConf[qualityLevel].jpegQuality); } else { - success = SendGradientRect(cl, w, h); + success = SendGradientRect(cl, x, y, w, h); } } else { - success = SendFullColorRect(cl, w, h); + success = SendFullColorRect(cl, x, y, w, h); } break; case 1: @@ -641,7 +692,7 @@ SendSubrect(rfbClientPtr cl, break; case 2: /* Two-color rectangle */ - success = SendMonoRect(cl, w, h); + success = SendMonoRect(cl, x, y, w, h); break; default: /* Up to 256 different colors */ @@ -651,7 +702,7 @@ SendSubrect(rfbClientPtr cl, success = SendJpegRect(cl, x, y, w, h, tightConf[qualityLevel].jpegQuality); } else { - success = SendIndexedRect(cl, w, h); + success = SendIndexedRect(cl, x, y, w, h); } } return success; @@ -675,13 +726,13 @@ SendTightHeader(rfbClientPtr cl, rect.r.y = Swap16IfLE(y); rect.r.w = Swap16IfLE(w); rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingTight); + rect.encoding = Swap32IfLE(cl->tightEncoding); memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - rfbStatRecordEncodingSent(cl, rfbEncodingTight, sz_rfbFramebufferUpdateRectHeader, + rfbStatRecordEncodingSent(cl, cl->tightEncoding, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); return TRUE; @@ -711,19 +762,29 @@ SendSolidRect(rfbClientPtr cl) memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); cl->ublen += len; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len+1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len+1); return TRUE; } static rfbBool SendMonoRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 1; int paletteLen, dataLen; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + /* TODO: setup palette maybe */ + return SendPngRect(cl, x, y, w, h); + /* TODO: destroy palette maybe */ + } +#endif + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { if (!rfbSendUpdateBuf(cl)) @@ -754,7 +815,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); cl->ublen += paletteLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteLen); break; case 16: @@ -765,7 +826,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); cl->ublen += 4; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 7); break; default: @@ -773,7 +834,7 @@ SendMonoRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (char)monoBackground; cl->updateBuf[cl->ublen++] = (char)monoForeground; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 5); } return CompressData(cl, streamId, dataLen, @@ -783,12 +844,20 @@ SendMonoRect(rfbClientPtr cl, static rfbBool SendIndexedRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 2; int i, entryLen; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(cl, x, y, w, h); + } +#endif + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + paletteNumColors * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { @@ -819,7 +888,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); cl->ublen += paletteNumColors * entryLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * entryLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * entryLen); break; case 16: @@ -832,7 +901,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); cl->ublen += paletteNumColors * 2; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * 2); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * 2); break; default: @@ -846,19 +915,27 @@ SendIndexedRect(rfbClientPtr cl, static rfbBool SendFullColorRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 0; int len; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(cl, x, y, w, h); + } +#endif + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) return FALSE; } cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (usePixelFormat24) { Pack24(cl, tightBeforeBuf, &cl->format, w * h); @@ -873,6 +950,8 @@ SendFullColorRect(rfbClientPtr cl, static rfbBool SendGradientRect(rfbClientPtr cl, + int x, + int y, int w, int h) { @@ -880,7 +959,7 @@ SendGradientRect(rfbClientPtr cl, int len; if (cl->format.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) @@ -892,7 +971,7 @@ SendGradientRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 2); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 2); if (usePixelFormat24) { FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); @@ -923,7 +1002,7 @@ CompressData(rfbClientPtr cl, if (dataLen < TIGHT_MIN_TO_COMPRESS) { memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); cl->ublen += dataLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, dataLen); return TRUE; } @@ -973,15 +1052,15 @@ static rfbBool SendCompressedData(rfbClientPtr cl, int i, portionLen; cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (compressedLen > 0x7F) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (compressedLen > 0x3FFF) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); } } @@ -997,7 +1076,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); cl->ublen += portionLen; } - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, compressedLen); return TRUE; } @@ -1659,11 +1738,11 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) int dy; if (cl->screen->serverFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); srcBuf = (uint8_t *)malloc(w * 3); if (srcBuf == NULL) { - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); } rowPointer[0] = srcBuf; @@ -1683,7 +1762,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) jpeg_start_compress(&cinfo, TRUE); for (dy = 0; dy < h; dy++) { - PrepareRowForJpeg(cl, srcBuf, x, y + dy, w); + PrepareRowForImg(cl, srcBuf, x, y + dy, w); jpeg_write_scanlines(&cinfo, rowPointer, 1); if (jpegError) break; @@ -1696,7 +1775,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) free(srcBuf); if (jpegError) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) @@ -1704,13 +1783,13 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) } cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); return SendCompressedData(cl, jpegDstDataLen); } static void -PrepareRowForJpeg(rfbClientPtr cl, +PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, @@ -1720,18 +1799,18 @@ PrepareRowForJpeg(rfbClientPtr cl, if ( cl->screen->serverFormat.redMax == 0xFF && cl->screen->serverFormat.greenMax == 0xFF && cl->screen->serverFormat.blueMax == 0xFF ) { - PrepareRowForJpeg24(cl, dst, x, y, count); + PrepareRowForImg24(cl, dst, x, y, count); } else { - PrepareRowForJpeg32(cl, dst, x, y, count); + PrepareRowForImg32(cl, dst, x, y, count); } } else { /* 16 bpp assumed. */ - PrepareRowForJpeg16(cl, dst, x, y, count); + PrepareRowForImg16(cl, dst, x, y, count); } } static void -PrepareRowForJpeg24(rfbClientPtr cl, +PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, @@ -1754,7 +1833,7 @@ PrepareRowForJpeg24(rfbClientPtr cl, #define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ \ static void \ -PrepareRowForJpeg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \ +PrepareRowForImg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \ uint##bpp##_t *fbptr; \ uint##bpp##_t pix; \ int inRed, inGreen, inBlue; \ @@ -1822,3 +1901,161 @@ JpegSetDstManager(j_compress_ptr cinfo) cinfo->dest = &jpegDstManager; } +/* + * PNG compression stuff. + */ + +#ifdef LIBVNCSERVER_HAVE_LIBPNG + +static TLS int pngDstDataLen = 0; + +static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h) { + if (cl->tightEncoding != rfbEncodingTightPng) { + return FALSE; + } + + if ( cl->screen->serverFormat.bitsPerPixel == 8 || + cl->format.bitsPerPixel == 8) { + return FALSE; + } + + return TRUE; +} + +static void pngWriteData(png_structp png_ptr, png_bytep data, + png_size_t length) +{ +#if 0 + rfbClientPtr cl = png_get_io_ptr(png_ptr); + + buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); + memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); +#endif + memcpy(tightAfterBuf + pngDstDataLen, data, length); + + pngDstDataLen += length; +} + +static void pngFlushData(png_structp png_ptr) +{ +} + + +static void *pngMalloc(png_structp png_ptr, png_size_t size) +{ + return malloc(size); +} + +static void pngFree(png_structp png_ptr, png_voidp ptr) +{ + free(ptr); +} + +static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { + /* rfbLog(">> SendPngRect x:%d, y:%d, w:%d, h:%d\n", x, y, w, h); */ + + png_byte color_type; + png_structp png_ptr; + png_infop info_ptr; + png_colorp png_palette = NULL; + int level = tightPngConf[cl->tightCompressLevel].png_zlib_level; + int filters = tightPngConf[cl->tightCompressLevel].png_filters; + uint8_t *buf; + int dy; + + pngDstDataLen = 0; + + png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, + NULL, pngMalloc, pngFree); + + if (png_ptr == NULL) + return FALSE; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, NULL); + return FALSE; + } + + png_set_write_fn(png_ptr, (void *) cl, pngWriteData, pngFlushData); + png_set_compression_level(png_ptr, level); + png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); + +#if 0 + /* TODO: */ + if (palette) { + color_type = PNG_COLOR_TYPE_PALETTE; + } else { + color_type = PNG_COLOR_TYPE_RGB; + } +#else + color_type = PNG_COLOR_TYPE_RGB; +#endif + png_set_IHDR(png_ptr, info_ptr, w, h, + 8, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + +#if 0 + if (color_type == PNG_COLOR_TYPE_PALETTE) { + struct palette_cb_priv priv; + + png_palette = pngMalloc(png_ptr, sizeof(*png_palette) * + palette_size(palette)); + + priv.vs = vs; + priv.png_palette = png_palette; + palette_iter(palette, write_png_palette, &priv); + + png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); + + offset = vs->tight.tight.offset; + if (vs->clientds.pf.bytes_per_pixel == 4) { + tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); + } else { + tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); + } + } + + buffer_reserve(&vs->tight.png, 2048); +#endif + + png_write_info(png_ptr, info_ptr); + buf = malloc(w * 3); + for (dy = 0; dy < h; dy++) + { +#if 0 + if (color_type == PNG_COLOR_TYPE_PALETTE) { + memcpy(buf, vs->tight.tight.buffer + (dy * w), w); + } else { + PrepareRowForImg(cl, buf, x, y + dy, w); + } +#else + PrepareRowForImg(cl, buf, x, y + dy, w); +#endif + png_write_row(png_ptr, buf); + } + free(buf); + + png_write_end(png_ptr, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + pngFree(png_ptr, png_palette); + } + + png_destroy_write_struct(&png_ptr, &info_ptr); + + /* done v */ + + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + cl->updateBuf[cl->ublen++] = (char)(rfbTightPng << 4); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); + + /* rfbLog("<< SendPngRect\n"); */ + return SendCompressedData(cl, pngDstDataLen); +} +#endif diff --git a/rfb/rfb.h b/rfb/rfb.h index c16336d..e79b68b 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -542,7 +542,9 @@ typedef struct _rfbClientRec { struct z_stream_s compStream; rfbBool compStreamInited; uint32_t zlibCompressLevel; - /** the quality level is also used by ZYWRLE */ +#endif +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) + /** the quality level is also used by ZYWRLE and TightPng */ int tightQualityLevel; #ifdef LIBVNCSERVER_HAVE_LIBJPEG @@ -550,6 +552,8 @@ typedef struct _rfbClientRec { z_stream zsStruct[4]; rfbBool zsActive[4]; int zsLevel[4]; +#endif +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) int tightCompressLevel; #endif #endif @@ -624,6 +628,9 @@ typedef struct _rfbClientRec { char *afterEncBuf; int afterEncBufSize; int afterEncBufLen; +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) + uint32_t tightEncoding; /* rfbEncodingTight or rfbEncodingTightPng */ +#endif } rfbClientRec, *rfbClientPtr; /** @@ -800,7 +807,7 @@ extern rfbBool rfbSendRectEncodingUltra(rfbClientPtr cl, int x,int y,int w,int h extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, int h); -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) /* tight.c */ #define TIGHT_DEFAULT_COMPRESSION 6 @@ -808,7 +815,13 @@ extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, extern rfbBool rfbTightDisableGradient; extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h); + +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) extern rfbBool rfbSendRectEncodingTight(rfbClientPtr cl, int x,int y,int w,int h); +#endif +#if defined(LIBVNCSERVER_HAVE_LIBPNG) +extern rfbBool rfbSendRectEncodingTightPng(rfbClientPtr cl, int x,int y,int w,int h); +#endif #endif #endif diff --git a/rfb/rfbconfig.h.cmake b/rfb/rfbconfig.h.cmake index de898fc..b7f225c 100644 --- a/rfb/rfbconfig.h.cmake +++ b/rfb/rfbconfig.h.cmake @@ -18,6 +18,9 @@ /* Define to 1 if you have the `jpeg' library (-ljpeg). */ #cmakedefine LIBVNCSERVER_HAVE_LIBJPEG 1 +/* Define if you have the `png' library (-lpng). */ +#cmakedefine LIBVNCSERVER_HAVE_LIBPNG 1 + /* Define to 1 if you have the `pthread' library (-lpthread). */ #cmakedefine LIBVNCSERVER_HAVE_LIBPTHREAD 1 diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index 73d200a..c6dfd2c 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -434,6 +434,7 @@ typedef struct { #define rfbEncodingHextile 5 #define rfbEncodingZlib 6 #define rfbEncodingTight 7 +#define rfbEncodingTightPng 0xFFFFFEFC /* -260 */ #define rfbEncodingZlibHex 8 #define rfbEncodingUltra 9 #define rfbEncodingZRLE 16 @@ -704,7 +705,10 @@ typedef struct { #ifdef LIBVNCSERVER_HAVE_LIBZ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Tight Encoding. + * Tight and TightPng Encoding. + * + *-- TightPng is like Tight but basic compression is not used, instead PNG + * data is sent. * *-- The first byte of each Tight-encoded rectangle is a "compression control * byte". Its format is as follows (bit 0 is the least significant one): @@ -715,8 +719,9 @@ typedef struct { * bit 3: if 1, then compression stream 3 should be reset; * bits 7-4: if 1000 (0x08), then the compression type is "fill", * if 1001 (0x09), then the compression type is "jpeg", + * if 1001 (0x0A), then the compression type is "png", * if 0xxx, then the compression type is "basic", - * values greater than 1001 are not valid. + * values greater than 1010 are not valid. * * If the compression type is "basic", then bits 6..4 of the * compression control byte (those xxx in 0xxx) specify the following: @@ -726,17 +731,17 @@ typedef struct { * bit 6: if 1, then a "filter id" byte is following this byte. * *-- The data that follows after the compression control byte described - * above depends on the compression type ("fill", "jpeg" or "basic"). + * above depends on the compression type ("fill", "jpeg", "png" or "basic"). * *-- If the compression type is "fill", then the only pixel value follows, in * client pixel format (see NOTE 1). This value applies to all pixels of the * rectangle. * - *-- If the compression type is "jpeg", the following data stream looks like - * this: + *-- If the compression type is "jpeg" or "png", the following data stream + * looks like this: * * 1..3 bytes: data size (N) in compact representation; - * N bytes: JPEG image. + * N bytes: JPEG or PNG image. * * Data size is compactly represented in one, two or three bytes, according * to the following scheme: @@ -817,7 +822,7 @@ typedef struct { *-- NOTE 2. The decoder must reset compression streams' states before * decoding the rectangle, if some of bits 0,1,2,3 in the compression control * byte are set to 1. Note that the decoder must reset zlib streams even if - * the compression type is "fill" or "jpeg". + * the compression type is "fill", "jpeg" or "png". * *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only * when bits-per-pixel value is either 16 or 32, not 8. @@ -831,7 +836,8 @@ typedef struct { #define rfbTightExplicitFilter 0x04 #define rfbTightFill 0x08 #define rfbTightJpeg 0x09 -#define rfbTightMaxSubencoding 0x09 +#define rfbTightPng 0x0A +#define rfbTightMaxSubencoding 0x0A /* Filters to improve compression efficiency */ #define rfbTightFilterCopy 0x00