/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
+ * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <rfb/CMsgReader.h>
#include <rfb/CMsgHandler.h>
#include <rfb/TightDecoder.h>
-#include <stdio.h> /* jpeglib.h needs FILE */
-extern "C" {
-#include <jpeglib.h>
-}
using namespace rfb;
#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)
void TightDecoder::readRect(const Rect& r, CMsgHandler* handler)
{
- rdr::InStream* is = reader->getInStream();
- /* 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()) {
+ is = reader->getInStream();
+ this->handler = handler;
+ clientpf = handler->getPreferredPF();
+ serverpf = handler->cp.pf();
+
+ if (clientpf.equal(serverpf)) {
+ /* Decode directly into the framebuffer (fast path) */
+ directDecode = true;
+ } else {
+ /* Decode into an intermediate buffer and use pixel translation */
+ directDecode = false;
+ }
+
+ switch (serverpf.bpp) {
case 8:
- tightDecode8 (r, is, zis, (rdr::U8*) buf, handler); break;
+ tightDecode8 (r); break;
case 16:
- tightDecode16(r, is, zis, (rdr::U16*)buf, handler); break;
+ tightDecode16(r); break;
case 32:
- tightDecode32(r, is, zis, (rdr::U32*)buf, handler); break;
+ tightDecode32(r); 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;
-}
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
+ * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#define PIXEL_T rdr::CONCAT2E(U,BPP)
#define READ_PIXEL CONCAT2E(readOpaque,BPP)
-#define TIGHT_DECODE CONCAT2E(tightDecode,BPP)
+#define TIGHT_DECODE TightDecoder::CONCAT2E(tightDecode,BPP)
+#define DECOMPRESS_JPEG_RECT TightDecoder::CONCAT2E(DecompressJpegRect,BPP)
+#define FILTER_GRADIENT TightDecoder::CONCAT2E(FilterGradient,BPP)
+#define DIRECT_FILL_RECT TightDecoder::CONCAT2E(directFillRect,BPP)
#define TIGHT_MIN_TO_COMPRESS 12
-static bool DecompressJpegRect(const Rect& r, rdr::InStream* is,
- PIXEL_T* buf, CMsgHandler* handler);
-static void FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
- PIXEL_T* buf, CMsgHandler* handler);
-#if BPP == 32
-static void FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
- PIXEL_T* buf, CMsgHandler* handler);
-#endif
// Main function implementing Tight decoder
-void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
- rdr::ZlibInStream zis[], PIXEL_T* buf
-#ifdef EXTRA_ARGS
- , EXTRA_ARGS
-#endif
- )
+void TIGHT_DECODE (const Rect& r)
{
- rdr::U8 *bytebuf = (rdr::U8*) buf;
bool cutZeros = false;
- const rfb::PixelFormat& myFormat = handler->cp.pf();
#if BPP == 32
- if (myFormat.is888()) {
+ if (serverpf.is888()) {
cutZeros = true;
}
#endif
if (comp_ctl == rfbTightFill) {
PIXEL_T pix;
if (cutZeros) {
+ rdr::U8 bytebuf[3];
is->readBytes(bytebuf, 3);
- myFormat.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
} else {
pix = is->READ_PIXEL();
}
- FILL_RECT(r, pix);
+ if (directDecode) DIRECT_FILL_RECT(r, pix);
+ else FILL_RECT(r, pix);
return;
}
// "JPEG" compression type.
if (comp_ctl == rfbTightJpeg) {
- DecompressJpegRect(r, is, buf, handler);
+ DECOMPRESS_JPEG_RECT(r);
return;
}
rdr::U8 elem[3];
for (int i = 0;i < palSize;i++) {
is->readBytes(elem, 3);
- myFormat.bufferFromRGB((rdr::U8*)&palette[i], elem, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&palette[i], elem, 1, NULL);
}
} else {
- for (int i = 0; i < palSize; i++)
- palette[i] = is->READ_PIXEL();
+ for (int i = 0; i < palSize; i++)
+ palette[i] = is->READ_PIXEL();
}
break;
case rfbTightFilterGradient:
input = &zis[streamId];
}
+ PIXEL_T *buf;
+ int stride = r.width();
+ if (directDecode) buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
+ else buf = (PIXEL_T *)reader->getImageBuf(r.area());
+
if (palSize == 0) {
// Truecolor data
if (useGradient) {
#if BPP == 32
if (cutZeros) {
- FilterGradient24(r, input, dataSize, buf, handler);
+ FilterGradient24(input, buf, stride, r, dataSize);
} else
#endif
- {
- FilterGradient(r, input, dataSize, buf, handler);
- }
+ {
+ FILTER_GRADIENT(input, buf, stride, r, dataSize);
+ }
} else {
+ // Copy
+ int h = r.height();
+ PIXEL_T *ptr = buf;
if (cutZeros) {
+ int w = r.width(), pad = stride - w;
rdr::U8 elem[3];
- for (int i = 0;i < r.area();i++) {
- input->readBytes(elem, 3);
- myFormat.bufferFromRGB((rdr::U8*)&buf[i], elem, 1, NULL);
+ while (h > 0) {
+ PIXEL_T *endOfRow = ptr + w;
+ while (ptr < endOfRow) {
+ input->readBytes(elem, 3);
+ serverpf.bufferFromRGB((rdr::U8*)ptr++, elem, 1, NULL);
+ }
+ ptr += pad;
+ h--;
}
} else {
- input->readBytes(buf, dataSize);
+ while (h > 0) {
+ input->readBytes(ptr, rowSize);
+ ptr += stride;
+ h--;
+ }
}
}
} else {
- int x, y, b;
+ // Indexed color
+ int x, h = r.height(), w = r.width(), b, pad = stride - w;
PIXEL_T *ptr = buf;
rdr::U8 bits;
if (palSize <= 2) {
// 2-color palette
- for (y = 0; y < r.height(); y++) {
- for (x = 0; x < r.width() / 8; x++) {
+ while (h > 0) {
+ for (x = 0; x < w / 8; x++) {
bits = input->readU8();
for (b = 7; b >= 0; b--) {
*ptr++ = palette[bits >> b & 1];
- }
+ }
}
- if (r.width() % 8 != 0) {
+ if (w % 8 != 0) {
bits = input->readU8();
- for (b = 7; b >= 8 - r.width() % 8; b--) {
+ for (b = 7; b >= 8 - w % 8; b--) {
*ptr++ = palette[bits >> b & 1];
}
}
+ ptr += pad;
+ h--;
}
} else {
// 256-color palette
- for (y = 0; y < r.height(); y++) {
- for (x = 0; x < r.width(); x++) {
+ while (h > 0) {
+ PIXEL_T *endOfRow = ptr + w;
+ while (ptr < endOfRow) {
*ptr++ = palette[input->readU8()];
- }
+ }
+ ptr += pad;
+ h--;
}
}
}
- IMAGE_RECT(r, buf);
+ if (directDecode) handler->releaseRawPixels(r);
+ else IMAGE_RECT(r, buf);
if (streamId != -1) {
zis[streamId].reset();
}
}
-static bool
-DecompressJpegRect(const Rect& r, rdr::InStream* is,
- PIXEL_T* buf, CMsgHandler* handler)
+void
+DECOMPRESS_JPEG_RECT(const Rect& r)
{
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr jerr;
- int w = r.width();
- int h = r.height();
- int pixelsize;
- rdr::U8 *dstBuf = NULL;
- bool dstBufIsTemp = false;
- const rfb::PixelFormat& pf = handler->cp.pf();
-
// Read length
int compressedLen = is->readCompactLength();
if (compressedLen <= 0) {
}
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);
+ // We always use direct decoding with JPEG images
+ int stride;
+ rdr::U8 *buf = handler->getRawPixelsRW(r, &stride);
+ jd.decompress(netbuf, compressedLen, buf, stride * clientpf.bpp / 8, r,
+ clientpf);
+ handler->releaseRawPixels(r);
- cinfo.out_color_space = JCS_RGB;
- pixelsize = 3;
-
-#ifdef JCS_EXTENSIONS
- // Try to have libjpeg output directly to our native format
- if (pf.is888()) {
- int redShift, greenShift, blueShift;
-
- if(pf.bigEndian) {
- redShift = 24 - pf.redShift;
- greenShift = 24 - pf.greenShift;
- blueShift = 24 - pf.blueShift;
- } else {
- redShift = pf.redShift;
- greenShift = pf.greenShift;
- blueShift = pf.blueShift;
- }
-
- // libjpeg can only handle some "standard" formats
- if(redShift == 0 && greenShift == 8 && blueShift == 16)
- cinfo.out_color_space = JCS_EXT_RGBX;
- if(redShift == 16 && greenShift == 8 && blueShift == 0)
- cinfo.out_color_space = JCS_EXT_BGRX;
- if(redShift == 24 && greenShift == 16 && blueShift == 8)
- cinfo.out_color_space = JCS_EXT_XBGR;
- if(redShift == 8 && greenShift == 16 && blueShift == 24)
- cinfo.out_color_space = JCS_EXT_XRGB;
-
- if (cinfo.out_color_space != JCS_RGB) {
- dstBuf = (rdr::U8 *)buf;
- pixelsize = 4;
- }
- }
-#endif
-
- if (cinfo.out_color_space == JCS_RGB) {
- dstBuf = new rdr::U8[w * h * pixelsize];
- dstBufIsTemp = true;
- }
-
- JSAMPROW *rowPointer = new JSAMPROW[h];
- for (int dy = 0; dy < h; dy++)
- rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * w * pixelsize]);
-
- jpeg_start_decompress(&cinfo);
- if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
- cinfo.output_components != pixelsize) {
- jpeg_destroy_decompress(&cinfo);
- throw Exception("Tight Encoding: Wrong JPEG data received.\n");
- }
-
- // Decompress
- const rfb::PixelFormat& myFormat = handler->cp.pf();
- while (cinfo.output_scanline < cinfo.output_height) {
- jpeg_read_scanlines(&cinfo, &rowPointer[cinfo.output_scanline],
- cinfo.output_height - cinfo.output_scanline);
- if (jpegError) {
- break;
- }
- }
-
- delete [] rowPointer;
-
- if (cinfo.out_color_space == JCS_RGB)
- myFormat.bufferFromRGB((rdr::U8*)buf, dstBuf, w * h);
-
- IMAGE_RECT(r, buf);
-
- if (!jpegError) {
- jpeg_finish_decompress(&cinfo);
- }
-
- jpeg_destroy_decompress(&cinfo);
-
- if (dstBufIsTemp) delete [] dstBuf;
delete [] netbuf;
-
- return !jpegError;
}
#if BPP == 32
-static void
-FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
- PIXEL_T* buf, CMsgHandler* handler)
+void
+TightDecoder::FilterGradient24(rdr::InStream* is, PIXEL_T* buf, int stride,
+ const Rect& r, int dataSize)
{
int x, y, c;
static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
is->readBytes(netbuf, dataSize);
// Set up shortcut variables
- const rfb::PixelFormat& myFormat = handler->cp.pf();
int rectHeight = r.height();
int rectWidth = r.width();
pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
thisRow[c] = pix[c];
}
- myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth], pix, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 0xff) {
- est[c] = 0xff;
- } else if (est[c] < 0) {
- est[c] = 0;
- }
- pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
- thisRow[x*3+c] = pix[c];
+ est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
+ if (est[c] > 0xff) {
+ est[c] = 0xff;
+ } else if (est[c] < 0) {
+ est[c] = 0;
+ }
+ pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
+ thisRow[x*3+c] = pix[c];
}
- myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth+x], pix, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
}
memcpy(prevRow, thisRow, sizeof(prevRow));
#endif
-static void
-FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
- PIXEL_T* buf, CMsgHandler* handler)
+void
+FILTER_GRADIENT(rdr::InStream* is, PIXEL_T* buf, int stride, const Rect& r,
+ int dataSize)
{
int x, y, c;
static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
is->readBytes(netbuf, dataSize);
// Set up shortcut variables
- const rfb::PixelFormat& myFormat = handler->cp.pf();
int rectHeight = r.height();
int rectWidth = r.width();
for (y = 0; y < rectHeight; y++) {
/* First pixel in a row */
- myFormat.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
+ serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
for (c = 0; c < 3; c++)
pix[c] += prevRow[c];
memcpy(thisRow, pix, sizeof(pix));
- myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth], pix, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
}
}
- myFormat.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
+ serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
for (c = 0; c < 3; c++)
pix[c] += est[c];
memcpy(&thisRow[x*3], pix, sizeof(pix));
- myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth+x], pix, 1, NULL);
+ serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
}
memcpy(prevRow, thisRow, sizeof(prevRow));
delete [] netbuf;
}
+void
+DIRECT_FILL_RECT(const Rect& r, Pixel pix) {
+
+ int stride;
+ PIXEL_T *buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
+
+ int w = r.width(), h = r.height();
+ PIXEL_T *ptr = buf;
+#if BPP != 8
+ int pad = stride - w;
+#endif
+
+ while (h > 0) {
+#if BPP == 8
+ memset(ptr, pix, w);
+ ptr += stride;
+#else
+ PIXEL_T *endOfRow = ptr + w;
+ while (ptr < endOfRow) {
+ *ptr++ = pix;
+ }
+ ptr += pad;
+#endif
+ h--;
+ }
+
+ handler->releaseRawPixels(r);
+}
+
#undef TIGHT_MIN_TO_COMPRESS
+#undef DIRECT_FILL_RECT
+#undef FILTER_GRADIENT
+#undef DECOMPRESS_JPEG_RECT
#undef TIGHT_DECODE
#undef READ_PIXEL
#undef PIXEL_T