diff options
author | Remko Tronçon <git@el-tramo.be> | 2011-02-01 20:24:32 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2012-04-24 18:16:10 (GMT) |
commit | 951cea590db4866188a1e372d6a966df7f0e79f9 (patch) | |
tree | d71c0bd5ae27c17c2e1ed68793dd1b851d1aa4a4 /3rdParty/LibVNC/src/libvncclient/tight.c | |
parent | 98f0f0c5cee040d3b0df5ff4d695320b2f93da24 (diff) | |
download | swift-contrib-remko/libvnc.zip swift-contrib-remko/libvnc.tar.bz2 |
Import part of libvnc.remko/libvnc
Diffstat (limited to '3rdParty/LibVNC/src/libvncclient/tight.c')
-rw-r--r-- | 3rdParty/LibVNC/src/libvncclient/tight.c | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/3rdParty/LibVNC/src/libvncclient/tight.c b/3rdParty/LibVNC/src/libvncclient/tight.c new file mode 100644 index 0000000..2f9fbab --- /dev/null +++ b/3rdParty/LibVNC/src/libvncclient/tight.c @@ -0,0 +1,688 @@ +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef LIBVNCSERVER_HAVE_LIBZ +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + +/* + * tight.c - handle ``tight'' encoding. + * + * This file shouldn't be compiled directly. It is included multiple + * times by rfbproto.c, each time with a different definition of the + * macro BPP. For each value of BPP, this file defines a function + * which handles a tight-encoded rectangle with BPP bits per pixel. + * + */ + +#define TIGHT_MIN_TO_COMPRESS 12 + +#define CARDBPP CONCAT3E(uint,BPP,_t) +#define filterPtrBPP CONCAT2E(filterPtr,BPP) + +#define HandleTightBPP CONCAT2E(HandleTight,BPP) +#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP) +#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP) +#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP) +#define FilterCopyBPP CONCAT2E(FilterCopy,BPP) +#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP) +#define FilterGradientBPP CONCAT2E(FilterGradient,BPP) + +#if BPP != 8 +#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP) +#endif + +#ifndef RGB_TO_PIXEL + +#define RGB_TO_PIXEL(bpp,r,g,b) \ + (((CARD##bpp)(r) & client->format.redMax) << client->format.redShift | \ + ((CARD##bpp)(g) & client->format.greenMax) << client->format.greenShift | \ + ((CARD##bpp)(b) & client->format.blueMax) << client->format.blueShift) + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * client->format.redMax + 127) / 255 \ + << client->format.redShift | \ + (((CARD##bpp)(g) & 0xFF) * client->format.greenMax + 127) / 255 \ + << client->format.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * client->format.blueMax + 127) / 255 \ + << client->format.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((uint32_t)(r) & 0xFF) << client->format.redShift | \ + ((uint32_t)(g) & 0xFF) << client->format.greenShift | \ + ((uint32_t)(b) & 0xFF) << client->format.blueShift) + +#endif + +/* Type declarations */ + +typedef void (*filterPtrBPP)(rfbClient* client, int, CARDBPP *); + +/* Prototypes */ + +static int InitFilterCopyBPP (rfbClient* client, int rw, int rh); +static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh); +static int InitFilterGradientBPP (rfbClient* client, int rw, int rh); +static void FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); +static void FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); +static void FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); + +#if BPP != 8 +static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h); +#endif + +/* Definitions */ + +static rfbBool +HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + CARDBPP fill_colour; + uint8_t comp_ctl; + uint8_t filter_id; + filterPtrBPP filterFn; + z_streamp zs; + char *buffer2; + int err, stream_id, compressedLen, bitsPixel; + int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes; + + if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1)) + return FALSE; + + /* Flush zlib streams if we are told by the server to do so. */ + for (stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) && client->zlibStreamActive[stream_id]) { + if (inflateEnd (&client->zlibStream[stream_id]) != Z_OK && + client->zlibStream[stream_id].msg != NULL) + rfbClientLog("inflateEnd: %s\n", client->zlibStream[stream_id].msg); + client->zlibStreamActive[stream_id] = FALSE; + } + comp_ctl >>= 1; + } + + /* Handle solid rectangles. */ + if (comp_ctl == rfbTightFill) { +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + if (!ReadFromRFBServer(client, client->buffer, 3)) + return FALSE; + fill_colour = RGB24_TO_PIXEL32(client->buffer[0], client->buffer[1], client->buffer[2]); + } else { + if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour))) + return FALSE; + } +#else + if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour))) + return FALSE; +#endif + + FillRectangle(client, rx, ry, rw, rh, fill_colour); + + return TRUE; + } + +#if BPP == 8 + if (comp_ctl == rfbTightJpeg) { + rfbClientLog("Tight encoding: JPEG is not supported in 8 bpp mode.\n"); + return FALSE; + } +#else + if (comp_ctl == rfbTightJpeg) { + return DecompressJpegRectBPP(client, rx, ry, rw, rh); + } +#endif + + /* Quit on unsupported subencoding value. */ + if (comp_ctl > rfbTightMaxSubencoding) { + rfbClientLog("Tight encoding: bad subencoding value received.\n"); + return FALSE; + } + + /* + * Here primary compression mode handling begins. + * Data was processed with optional filter + zlib compression. + */ + + /* First, we should identify a filter to use. */ + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + if (!ReadFromRFBServer(client, (char*)&filter_id, 1)) + return FALSE; + + switch (filter_id) { + case rfbTightFilterCopy: + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(client, rw, rh); + break; + case rfbTightFilterPalette: + filterFn = FilterPaletteBPP; + bitsPixel = InitFilterPaletteBPP(client, rw, rh); + break; + case rfbTightFilterGradient: + filterFn = FilterGradientBPP; + bitsPixel = InitFilterGradientBPP(client, rw, rh); + break; + default: + rfbClientLog("Tight encoding: unknown filter code received.\n"); + return FALSE; + } + } else { + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(client, rw, rh); + } + if (bitsPixel == 0) { + rfbClientLog("Tight encoding: error receiving palette.\n"); + return FALSE; + } + + /* Determine if the data should be decompressed or just copied. */ + rowSize = (rw * bitsPixel + 7) / 8; + if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) { + if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize)) + return FALSE; + + buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4]; + filterFn(client, rh, (CARDBPP *)buffer2); + + CopyRectangle(client, (uint8_t *)buffer2, rx, ry, rw, rh); + + return TRUE; + } + + /* Read the length (1..3 bytes) of compressed data following. */ + compressedLen = (int)ReadCompactLen(client); + if (compressedLen <= 0) { + rfbClientLog("Incorrect data received from the server.\n"); + return FALSE; + } + + /* Now let's initialize compression stream if needed. */ + stream_id = comp_ctl & 0x03; + zs = &client->zlibStream[stream_id]; + if (!client->zlibStreamActive[stream_id]) { + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + err = inflateInit(zs); + if (err != Z_OK) { + if (zs->msg != NULL) + rfbClientLog("InflateInit error: %s.\n", zs->msg); + return FALSE; + } + client->zlibStreamActive[stream_id] = TRUE; + } + + /* Read, decode and draw actual pixel data in a loop. */ + + bufferSize = RFB_BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC; + buffer2 = &client->buffer[bufferSize]; + if (rowSize > bufferSize) { + /* Should be impossible when RFB_BUFFER_SIZE >= 16384 */ + rfbClientLog("Internal error: incorrect buffer size.\n"); + return FALSE; + } + + rowsProcessed = 0; + extraBytes = 0; + + while (compressedLen > 0) { + if (compressedLen > ZLIB_BUFFER_SIZE) + portionLen = ZLIB_BUFFER_SIZE; + else + portionLen = compressedLen; + + if (!ReadFromRFBServer(client, (char*)client->zlib_buffer, portionLen)) + return FALSE; + + compressedLen -= portionLen; + + zs->next_in = (Bytef *)client->zlib_buffer; + zs->avail_in = portionLen; + + do { + zs->next_out = (Bytef *)&client->buffer[extraBytes]; + zs->avail_out = bufferSize - extraBytes; + + err = inflate(zs, Z_SYNC_FLUSH); + if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */ + break; + if (err != Z_OK && err != Z_STREAM_END) { + if (zs->msg != NULL) { + rfbClientLog("Inflate error: %s.\n", zs->msg); + } else { + rfbClientLog("Inflate error: %d.\n", err); + } + return FALSE; + } + + numRows = (bufferSize - zs->avail_out) / rowSize; + + filterFn(client, numRows, (CARDBPP *)buffer2); + + extraBytes = bufferSize - zs->avail_out - numRows * rowSize; + if (extraBytes > 0) + memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes); + + CopyRectangle(client, (uint8_t *)buffer2, rx, ry+rowsProcessed, rw, numRows); + + rowsProcessed += numRows; + } + while (zs->avail_out == 0); + } + + if (rowsProcessed != rh) { + rfbClientLog("Incorrect number of scan lines after decompression.\n"); + return FALSE; + } + + return TRUE; +} + +/*---------------------------------------------------------------------------- + * + * Filter stuff. + * + */ + +static int +InitFilterCopyBPP (rfbClient* client, int rw, int rh) +{ + client->rectWidth = rw; + +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + client->cutZeros = TRUE; + return 24; + } else { + client->cutZeros = FALSE; + } +#endif + + return BPP; +} + +static void +FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + +#if BPP == 32 + int x, y; + + if (client->cutZeros) { + for (y = 0; y < numRows; y++) { + for (x = 0; x < client->rectWidth; x++) { + dst[y*client->rectWidth+x] = + RGB24_TO_PIXEL32(client->buffer[(y*client->rectWidth+x)*3], + client->buffer[(y*client->rectWidth+x)*3+1], + client->buffer[(y*client->rectWidth+x)*3+2]); + } + } + return; + } +#endif + + memcpy (dst, client->buffer, numRows * client->rectWidth * (BPP / 8)); +} + +static int +InitFilterGradientBPP (rfbClient* client, int rw, int rh) +{ + int bits; + + bits = InitFilterCopyBPP(client, rw, rh); + if (client->cutZeros) + memset(client->tightPrevRow, 0, rw * 3); + else + memset(client->tightPrevRow, 0, rw * 3 * sizeof(uint16_t)); + + return bits; +} + +#if BPP == 32 + +static void +FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) +{ + int x, y, c; + uint8_t thisRow[2048*3]; + uint8_t pix[3]; + int est[3]; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = client->tightPrevRow[c] + client->buffer[y*client->rectWidth*3+c]; + thisRow[c] = pix[c]; + } + dst[y*client->rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < client->rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)client->tightPrevRow[x*3+c] + (int)pix[c] - + (int)client->tightPrevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (uint8_t)est[c] + client->buffer[(y*client->rectWidth+x)*3+c]; + thisRow[x*3+c] = pix[c]; + } + dst[y*client->rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(client->tightPrevRow, thisRow, client->rectWidth * 3); + } +} + +#endif + +static void +FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + int x, y, c; + CARDBPP *src = (CARDBPP *)client->buffer; + uint16_t *thatRow = (uint16_t *)client->tightPrevRow; + uint16_t thisRow[2048*3]; + uint16_t pix[3]; + uint16_t max[3]; + int shift[3]; + int est[3]; + +#if BPP == 32 + if (client->cutZeros) { + FilterGradient24(client, numRows, dst); + return; + } +#endif + + max[0] = client->format.redMax; + max[1] = client->format.greenMax; + max[2] = client->format.blueMax; + + shift[0] = client->format.redShift; + shift[1] = client->format.greenShift; + shift[2] = client->format.blueShift; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (uint16_t)(((src[y*client->rectWidth] >> shift[c]) + thatRow[c]) & max[c]); + thisRow[c] = pix[c]; + } + dst[y*client->rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < client->rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c]; + if (est[c] > (int)max[c]) { + est[c] = (int)max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (uint16_t)(((src[y*client->rectWidth+x] >> shift[c]) + est[c]) & max[c]); + thisRow[x*3+c] = pix[c]; + } + dst[y*client->rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + } + memcpy(thatRow, thisRow, client->rectWidth * 3 * sizeof(uint16_t)); + } +} + +static int +InitFilterPaletteBPP (rfbClient* client, int rw, int rh) +{ + uint8_t numColors; +#if BPP == 32 + int i; + CARDBPP *palette = (CARDBPP *)client->tightPalette; +#endif + + client->rectWidth = rw; + + if (!ReadFromRFBServer(client, (char*)&numColors, 1)) + return 0; + + client->rectColors = (int)numColors; + if (++client->rectColors < 2) + return 0; + +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * 3)) + return 0; + for (i = client->rectColors - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(client->tightPalette[i*3], + client->tightPalette[i*3+1], + client->tightPalette[i*3+2]); + } + return (client->rectColors == 2) ? 1 : 8; + } +#endif + + if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * (BPP / 8))) + return 0; + + return (client->rectColors == 2) ? 1 : 8; +} + +static void +FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + int x, y, b, w; + uint8_t *src = (uint8_t *)client->buffer; + CARDBPP *palette = (CARDBPP *)client->tightPalette; + + if (client->rectColors == 2) { + w = (client->rectWidth + 7) / 8; + for (y = 0; y < numRows; y++) { + for (x = 0; x < client->rectWidth / 8; x++) { + for (b = 7; b >= 0; b--) + dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + for (b = 7; b >= 8 - client->rectWidth % 8; b--) { + dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + } + } else { + for (y = 0; y < numRows; y++) + for (x = 0; x < client->rectWidth; x++) + dst[y*client->rectWidth+x] = palette[(int)src[y*client->rectWidth+x]]; + } +} + +#if BPP != 8 + +/*---------------------------------------------------------------------------- + * + * JPEG decompression. + * + */ + +static rfbBool +DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int compressedLen; + uint8_t *compressedData; + CARDBPP *pixelPtr; + JSAMPROW rowPointer[1]; + int dx, dy; + + compressedLen = (int)ReadCompactLen(client); + if (compressedLen <= 0) { + rfbClientLog("Incorrect data received from the server.\n"); + return FALSE; + } + + compressedData = malloc(compressedLen); + if (compressedData == NULL) { + rfbClientLog("Memory allocation error.\n"); + return FALSE; + } + + if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) { + free(compressedData); + return FALSE; + } + + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = client; + jpeg_create_decompress(&cinfo); + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != w || cinfo.output_height != h || + cinfo.output_components != 3) { + rfbClientLog("Tight Encoding: Wrong JPEG data received.\n"); + jpeg_destroy_decompress(&cinfo); + free(compressedData); + return FALSE; + } + + rowPointer[0] = (JSAMPROW)client->buffer; + dy = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + if (client->jpegError) { + break; + } + pixelPtr = (CARDBPP *)&client->buffer[RFB_BUFFER_SIZE / 2]; + for (dx = 0; dx < w; dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]); + } + CopyRectangle(client, (uint8_t *)&client->buffer[RFB_BUFFER_SIZE / 2], x, y + dy, w, 1); + dy++; + } + + if (!client->jpegError) + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + free(compressedData); + + return !client->jpegError; +} + +#else + +static long +ReadCompactLen (rfbClient* client) +{ + long len; + uint8_t b; + + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len = (int)b & 0x7F; + if (b & 0x80) { + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len |= ((int)b & 0x7F) << 7; + if (b & 0x80) { + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len |= ((int)b & 0xFF) << 14; + } + } + return len; +} + +/* + * JPEG source manager functions for JPEG decompression in Tight decoder. + */ + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + rfbClient* client=(rfbClient*)cinfo->client_data; + client->jpegError = FALSE; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + rfbClient* client=(rfbClient*)cinfo->client_data; + client->jpegError = TRUE; + client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; + client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + rfbClient* client=(rfbClient*)cinfo->client_data; + if (num_bytes < 0 || num_bytes > client->jpegSrcManager->bytes_in_buffer) { + client->jpegError = TRUE; + client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; + client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr; + } else { + client->jpegSrcManager->next_input_byte += (size_t) num_bytes; + client->jpegSrcManager->bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* nothing to do here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, + uint8_t *compressedData, + int compressedLen) +{ + rfbClient* client=(rfbClient*)cinfo->client_data; + client->jpegBufferPtr = compressedData; + client->jpegBufferLen = (size_t)compressedLen; + + if(client->jpegSrcManager == NULL) + client->jpegSrcManager = malloc(sizeof(struct jpeg_source_mgr)); + client->jpegSrcManager->init_source = JpegInitSource; + client->jpegSrcManager->fill_input_buffer = JpegFillInputBuffer; + client->jpegSrcManager->skip_input_data = JpegSkipInputData; + client->jpegSrcManager->resync_to_restart = jpeg_resync_to_restart; + client->jpegSrcManager->term_source = JpegTermSource; + client->jpegSrcManager->next_input_byte = (JOCTET*)client->jpegBufferPtr; + client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; + + cinfo->src = client->jpegSrcManager; +} + +#endif + +#undef CARDBPP + +/* LIBVNCSERVER_HAVE_LIBZ and LIBVNCSERVER_HAVE_LIBJPEG */ +#endif +#endif + |