diff options
author | Peter Åstrand <astrand@cendio.se> | 2004-12-07 08:22:42 +0000 |
---|---|---|
committer | Peter Åstrand <astrand@cendio.se> | 2004-12-07 08:22:42 +0000 |
commit | a6bb77043526551b3cccb4477fc11fd566db002c (patch) | |
tree | bbddcdc9a0015ca0256c1070bced6e55ed38edc3 | |
parent | c977b9c72f6c3b2539c59cd837457f8fc1c03a7c (diff) | |
download | tigervnc-a6bb77043526551b3cccb4477fc11fd566db002c.tar.gz tigervnc-a6bb77043526551b3cccb4477fc11fd566db002c.zip |
JPEG decompression support
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@20 3789f03b-4d11-0410-bbf8-ca57d06f2519
-rw-r--r-- | rfb/CMsgWriter.cxx | 2 | ||||
-rw-r--r-- | rfb/TightDecoder.cxx | 86 | ||||
-rw-r--r-- | rfb/tightDecode.h | 72 |
3 files changed, 157 insertions, 3 deletions
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx index 052c4818..d429d7ab 100644 --- a/rfb/CMsgWriter.cxx +++ b/rfb/CMsgWriter.cxx @@ -76,6 +76,8 @@ void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect) } } encodings[nEncodings++] = pseudoEncodingLastRect; + // FIXME + encodings[nEncodings++] = pseudoEncodingQualityLevel9; writeSetEncodings(nEncodings, encodings); } diff --git a/rfb/TightDecoder.cxx b/rfb/TightDecoder.cxx index 46776473..aa2c6756 100644 --- a/rfb/TightDecoder.cxx +++ b/rfb/TightDecoder.cxx @@ -18,9 +18,25 @@ #include <rfb/CMsgReader.h> #include <rfb/CMsgHandler.h> #include <rfb/TightDecoder.h> +#include <stdio.h> /* jpeglib.h needs FILE */ +#include <jpeglib.h> using namespace rfb; +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((PIXEL_T)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((PIXEL_T)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((PIXEL_T)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +#define TIGHT_MAX_WIDTH 2048 + +static void JpegSetSrcManager(j_decompress_ptr cinfo, char *compressedData, + int compressedLen); +static bool jpegError; + #define EXTRA_ARGS CMsgHandler* handler #define FILL_RECT(r, p) handler->fillRect(r, p) #define IMAGE_RECT(r, p) handler->imageRect(r, p) @@ -50,7 +66,9 @@ TightDecoder::~TightDecoder() void TightDecoder::readRect(const Rect& r, CMsgHandler* handler) { rdr::InStream* is = reader->getInStream(); - rdr::U8* buf = reader->getImageBuf(r.area()); + /* Uncompressed RGB24 JPEG data, before translated, can be up to 3 + times larger, if VNC bpp is 8. */ + rdr::U8* buf = reader->getImageBuf(r.area()*3); switch (reader->bpp()) { case 8: tightDecode8 (r, is, zis, (rdr::U8*) buf, handler); break; @@ -60,3 +78,69 @@ void TightDecoder::readRect(const Rect& r, CMsgHandler* handler) tightDecode32(r, is, zis, (rdr::U32*)buf, handler); break; } } + + +// +// A "Source manager" for the JPEG library. +// + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static void JpegInitSource(j_decompress_ptr cinfo); +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); +static void JpegTermSource(j_decompress_ptr cinfo); + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = false; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = true; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || (size_t)num_bytes > jpegSrcManager.bytes_in_buffer) { + jpegError = true; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } else { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* No work necessary here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, char *compressedData, int compressedLen) +{ + jpegBufferPtr = (JOCTET *)compressedData; + jpegBufferLen = (size_t)compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} diff --git a/rfb/tightDecode.h b/rfb/tightDecode.h index d3d9609a..023ff3e3 100644 --- a/rfb/tightDecode.h +++ b/rfb/tightDecode.h @@ -44,6 +44,8 @@ namespace rfb { #define TIGHT_DECODE CONCAT2E(tightDecode,BPP) #define TIGHT_MIN_TO_COMPRESS 12 +static bool DecompressJpegRect(const Rect& r, rdr::InStream* is, + PIXEL_T* buf, CMsgHandler* handler); // Main function implementing Tight decoder @@ -73,8 +75,8 @@ void TIGHT_DECODE (const Rect& r, rdr::InStream* is, // "JPEG" compression type. if (comp_ctl == rfbTightJpeg) { - throw Exception("TightDecoder: FIXME: JPEG compression is not supported yet"); - return; + DecompressJpegRect(r, is, buf, handler); + return; } // Quit on unsupported compression type. @@ -165,6 +167,72 @@ void TIGHT_DECODE (const Rect& r, rdr::InStream* is, IMAGE_RECT(r, buf); } +static bool +DecompressJpegRect(const Rect& r, rdr::InStream* is, + PIXEL_T* buf, CMsgHandler* handler) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + PIXEL_T *pixelPtr; + JSAMPROW scanline; + + // Read length + int compressedLen = is->readCompactLength(); + if (compressedLen <= 0) { + throw Exception("Incorrect data received from the server.\n"); + } + + // Allocate netbuf and read in data + rdr::U8* netbuf = new rdr::U8[compressedLen]; + if (!netbuf) + throw Exception("rfb::tightDecode unable to allocate buffer"); + is->readBytes(netbuf, compressedLen); + + // Set up JPEG decompression + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen); + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() || + cinfo.output_components != 3) { + jpeg_destroy_decompress(&cinfo); + throw Exception("Tight Encoding: Wrong JPEG data received.\n"); + } + + // Decompress + scanline = (JSAMPROW)buf; + const rfb::PixelFormat& myFormat = handler->cp.pf(); + int bytesPerRow = cinfo.output_width * myFormat.bpp/8; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &scanline, 1); + if (jpegError) { + break; + } + + pixelPtr = (PIXEL_T*)(scanline); + for (int dx = 0; dx < r.width(); dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(BPP, scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]); + } + scanline += bytesPerRow; + } + + IMAGE_RECT(r, buf); + + if (!jpegError) + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + + delete [] netbuf; + + return !jpegError; +} + + #undef TIGHT_DECODE #undef READ_PIXEL #undef PIXEL_T |