summaryrefslogtreecommitdiffstats
path: root/libvncclient/tight.c
diff options
context:
space:
mode:
Diffstat (limited to 'libvncclient/tight.c')
-rw-r--r--libvncclient/tight.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/libvncclient/tight.c b/libvncclient/tight.c
new file mode 100644
index 0000000..c858392
--- /dev/null
+++ b/libvncclient/tight.c
@@ -0,0 +1,606 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 Bool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h);
+#endif
+
+/* Definitions */
+
+static Bool
+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) && zlibStreamActive[stream_id]) {
+ if (inflateEnd (&zlibStream[stream_id]) != Z_OK &&
+ zlibStream[stream_id].msg != NULL)
+ fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg);
+ 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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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:
+ fprintf(stderr, "Tight encoding: unknown filter code received.\n");
+ return FALSE;
+ }
+ } else {
+ filterFn = FilterCopyBPP;
+ bitsPixel = InitFilterCopyBPP(client, rw, rh);
+ }
+ if (bitsPixel == 0) {
+ fprintf(stderr, "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, buffer2, rx, ry, rw, rh);
+
+ return TRUE;
+ }
+
+ /* Read the length (1..3 bytes) of compressed data following. */
+ compressedLen = (int)ReadCompactLen(client);
+ if (compressedLen <= 0) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return FALSE;
+ }
+
+ /* Now let's initialize compression stream if needed. */
+ stream_id = comp_ctl & 0x03;
+ zs = &zlibStream[stream_id];
+ if (!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)
+ fprintf(stderr, "InflateInit error: %s.\n", zs->msg);
+ return FALSE;
+ }
+ zlibStreamActive[stream_id] = TRUE;
+ }
+
+ /* Read, decode and draw actual pixel data in a loop. */
+
+ bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
+ buffer2 = &client->buffer[bufferSize];
+ if (rowSize > bufferSize) {
+ /* Should be impossible when BUFFER_SIZE >= 16384 */
+ fprintf(stderr, "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*)zlib_buffer, portionLen))
+ return FALSE;
+
+ compressedLen -= portionLen;
+
+ zs->next_in = (Bytef *)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) {
+ fprintf(stderr, "Inflate error: %s.\n", zs->msg);
+ } else {
+ fprintf(stderr, "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, buffer2, rx, ry+rowsProcessed, rw, numRows);
+
+ rowsProcessed += numRows;
+ }
+ while (zs->avail_out == 0);
+ }
+
+ if (rowsProcessed != rh) {
+ fprintf(stderr, "Incorrect number of scan lines after decompression.\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * Filter stuff.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool cutZeros;
+ static int rectWidth, rectColors;
+ static uint8_t tightPalette[256*4];
+ static uint8_t tightPrevRow[2048*3*sizeof(CARD16)];
+*/
+
+static int
+InitFilterCopyBPP (rfbClient* client, int rw, int rh)
+{
+ rectWidth = rw;
+
+#if BPP == 32
+ if (client->format.depth == 24 && client->format.redMax == 0xFF &&
+ client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
+ cutZeros = TRUE;
+ return 24;
+ } else {
+ cutZeros = FALSE;
+ }
+#endif
+
+ return BPP;
+}
+
+static void
+FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst)
+{
+
+#if BPP == 32
+ int x, y;
+
+ if (cutZeros) {
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth; x++) {
+ dst[y*rectWidth+x] =
+ RGB24_TO_PIXEL32(client->buffer[(y*rectWidth+x)*3],
+ client->buffer[(y*rectWidth+x)*3+1],
+ client->buffer[(y*rectWidth+x)*3+2]);
+ }
+ }
+ return;
+ }
+#endif
+
+ memcpy (dst, client->buffer, numRows * rectWidth * (BPP / 8));
+}
+
+static int
+InitFilterGradientBPP (rfbClient* client, int rw, int rh)
+{
+ int bits;
+
+ bits = InitFilterCopyBPP(client, rw, rh);
+ if (cutZeros)
+ memset(tightPrevRow, 0, rw * 3);
+ else
+ memset(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] = tightPrevRow[c] + client->buffer[y*rectWidth*3+c];
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < rectWidth; x++) {
+ for (c = 0; c < 3; c++) {
+ est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] -
+ (int)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*rectWidth+x)*3+c];
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+ }
+
+ memcpy(tightPrevRow, thisRow, 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 *)tightPrevRow;
+ uint16_t thisRow[2048*3];
+ uint16_t pix[3];
+ uint16_t max[3];
+ int shift[3];
+ int est[3];
+
+#if BPP == 32
+ if (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*rectWidth] >> shift[c]) + thatRow[c]) & max[c]);
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < 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*rectWidth+x] >> shift[c]) + est[c]) & max[c]);
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+ }
+ memcpy(thatRow, thisRow, 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 *)tightPalette;
+#endif
+
+ rectWidth = rw;
+
+ if (!ReadFromRFBServer(client, (char*)&numColors, 1))
+ return 0;
+
+ rectColors = (int)numColors;
+ if (++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*)&tightPalette, rectColors * 3))
+ return 0;
+ for (i = rectColors - 1; i >= 0; i--) {
+ palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3],
+ tightPalette[i*3+1],
+ tightPalette[i*3+2]);
+ }
+ return (rectColors == 2) ? 1 : 8;
+ }
+#endif
+
+ if (!ReadFromRFBServer(client, (char*)&tightPalette, rectColors * (BPP / 8)))
+ return 0;
+
+ return (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 *)tightPalette;
+
+ if (rectColors == 2) {
+ w = (rectWidth + 7) / 8;
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth / 8; x++) {
+ for (b = 7; b >= 0; b--)
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ for (b = 7; b >= 8 - rectWidth % 8; b--) {
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ }
+ } else {
+ for (y = 0; y < numRows; y++)
+ for (x = 0; x < rectWidth; x++)
+ dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]];
+ }
+}
+
+#if BPP != 8
+
+/*----------------------------------------------------------------------------
+ *
+ * JPEG decompression.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool jpegError;
+ static struct jpeg_source_mgr jpegSrcManager;
+ static JOCTET *jpegBufferPtr;
+ static size_t *jpegBufferLen;
+*/
+
+static Bool
+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) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return FALSE;
+ }
+
+ compressedData = malloc(compressedLen);
+ if (compressedData == NULL) {
+ fprintf(stderr, "Memory allocation error.\n");
+ return FALSE;
+ }
+
+ if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) {
+ free(compressedData);
+ return FALSE;
+ }
+
+ cinfo.err = jpeg_std_error(&jerr);
+ 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) {
+ fprintf(stderr, "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 (jpegError) {
+ break;
+ }
+ pixelPtr = (CARDBPP *)&client->buffer[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, &client->buffer[BUFFER_SIZE / 2], x, y + dy, w, 1);
+ dy++;
+ }
+
+ if (!jpegError)
+ jpeg_finish_decompress(&cinfo);
+
+ jpeg_destroy_decompress(&cinfo);
+ free(compressedData);
+
+ return !jpegError;
+}
+
+#endif
+