blob: e604e0ec18365e9e8bc4ae22fb7e376caa4faeb3 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
Peter Åstrandd69bcc42011-09-28 12:52:53 +00002 * Copyright 2004-2005 Cendio AB.
DRC33c15e32011-11-03 18:49:21 +00003 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21//
22// Tight decoding functions.
23//
24// This file is #included after having set the following macros:
25// BPP - 8, 16 or 32
26// EXTRA_ARGS - optional extra arguments
27// FILL_RECT - fill a rectangle with a single color
28// IMAGE_RECT - draw a rectangle of pixel data from a buffer
29
30#include <rdr/InStream.h>
31#include <rdr/ZlibInStream.h>
32#include <rfb/Exception.h>
33#include <assert.h>
34
35namespace rfb {
36
37// CONCAT2E concatenates its arguments, expanding them if they are macros
38
39#ifndef CONCAT2E
40#define CONCAT2(a,b) a##b
41#define CONCAT2E(a,b) CONCAT2(a,b)
42#endif
43
44#define PIXEL_T rdr::CONCAT2E(U,BPP)
45#define READ_PIXEL CONCAT2E(readOpaque,BPP)
DRC33c15e32011-11-03 18:49:21 +000046#define TIGHT_DECODE TightDecoder::CONCAT2E(tightDecode,BPP)
47#define DECOMPRESS_JPEG_RECT TightDecoder::CONCAT2E(DecompressJpegRect,BPP)
48#define FILTER_GRADIENT TightDecoder::CONCAT2E(FilterGradient,BPP)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
50#define TIGHT_MIN_TO_COMPRESS 12
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051
52// Main function implementing Tight decoder
53
DRC33c15e32011-11-03 18:49:21 +000054void TIGHT_DECODE (const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056 bool cutZeros = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057#if BPP == 32
DRC33c15e32011-11-03 18:49:21 +000058 if (serverpf.is888()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059 cutZeros = true;
60 }
61#endif
62
63 rdr::U8 comp_ctl = is->readU8();
64
65 // Flush zlib streams if we are told by the server to do so.
66 for (int i = 0; i < 4; i++) {
67 if (comp_ctl & 1) {
68 zis[i].reset();
69 }
70 comp_ctl >>= 1;
71 }
72
73 // "Fill" compression type.
74 if (comp_ctl == rfbTightFill) {
75 PIXEL_T pix;
76 if (cutZeros) {
DRC33c15e32011-11-03 18:49:21 +000077 rdr::U8 bytebuf[3];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000078 is->readBytes(bytebuf, 3);
DRC33c15e32011-11-03 18:49:21 +000079 serverpf.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080 } else {
81 pix = is->READ_PIXEL();
82 }
DRC4f24c1a2011-11-03 23:56:10 +000083 FILL_RECT(r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084 return;
85 }
86
87 // "JPEG" compression type.
88 if (comp_ctl == rfbTightJpeg) {
DRC33c15e32011-11-03 18:49:21 +000089 DECOMPRESS_JPEG_RECT(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000090 return;
91 }
92
93 // Quit on unsupported compression type.
94 if (comp_ctl > rfbTightMaxSubencoding) {
95 throw Exception("TightDecoder: bad subencoding value received");
96 return;
97 }
98
99 // "Basic" compression type.
100 int palSize = 0;
101 static PIXEL_T palette[256];
102 bool useGradient = false;
103
104 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
105 rdr::U8 filterId = is->readU8();
106
107 switch (filterId) {
108 case rfbTightFilterPalette:
109 palSize = is->readU8() + 1;
110 if (cutZeros) {
DRCa5004a32011-11-04 04:51:17 +0000111 rdr::U8 tightPalette[256 * 3];
112 is->readBytes(tightPalette, palSize * 3);
113 serverpf.bufferFromRGB((rdr::U8*)palette, tightPalette, palSize, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114 } else {
DRCa5004a32011-11-04 04:51:17 +0000115 is->readBytes(palette, palSize * sizeof(PIXEL_T));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116 }
117 break;
118 case rfbTightFilterGradient:
119 useGradient = true;
120 break;
121 case rfbTightFilterCopy:
122 break;
123 default:
124 throw Exception("TightDecoder: unknown filter code received");
125 return;
126 }
127 }
128
129 int bppp = BPP;
130 if (palSize != 0) {
131 bppp = (palSize <= 2) ? 1 : 8;
132 } else if (cutZeros) {
133 bppp = 24;
134 }
135
136 // Determine if the data should be decompressed or just copied.
137 int rowSize = (r.width() * bppp + 7) / 8;
138 int dataSize = r.height() * rowSize;
139 int streamId = -1;
140 rdr::InStream *input;
141 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
142 input = is;
143 } else {
144 int length = is->readCompactLength();
145 streamId = comp_ctl & 0x03;
146 zis[streamId].setUnderlying(is, length);
147 input = &zis[streamId];
148 }
149
DRCa5004a32011-11-04 04:51:17 +0000150 // Allocate netbuf and read in data
151 rdr::U8 *netbuf = new rdr::U8[dataSize];
152 if (!netbuf) {
153 throw Exception("rfb::TightDecoder::tightDecode unable to allocate buffer");
154 }
155 input->readBytes(netbuf, dataSize);
156
DRC33c15e32011-11-03 18:49:21 +0000157 PIXEL_T *buf;
158 int stride = r.width();
159 if (directDecode) buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
160 else buf = (PIXEL_T *)reader->getImageBuf(r.area());
161
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000162 if (palSize == 0) {
163 // Truecolor data
164 if (useGradient) {
165#if BPP == 32
166 if (cutZeros) {
DRCa5004a32011-11-04 04:51:17 +0000167 FilterGradient24(netbuf, buf, stride, r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000168 } else
169#endif
DRC33c15e32011-11-03 18:49:21 +0000170 {
DRCa5004a32011-11-04 04:51:17 +0000171 FILTER_GRADIENT(netbuf, buf, stride, r);
DRC33c15e32011-11-03 18:49:21 +0000172 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 } else {
DRC33c15e32011-11-03 18:49:21 +0000174 // Copy
175 int h = r.height();
176 PIXEL_T *ptr = buf;
DRCa5004a32011-11-04 04:51:17 +0000177 rdr::U8 *srcPtr = netbuf;
178 int w = r.width();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000179 if (cutZeros) {
DRC33c15e32011-11-03 18:49:21 +0000180 while (h > 0) {
DRCa5004a32011-11-04 04:51:17 +0000181 serverpf.bufferFromRGB((rdr::U8*)ptr, srcPtr, w, NULL);
182 ptr += stride;
Pierre Ossmane3cb4a22011-11-08 13:52:33 +0000183 srcPtr += w * 3;
DRC33c15e32011-11-03 18:49:21 +0000184 h--;
Pierre Ossman7c4efd72010-09-30 11:30:20 +0000185 }
186 } else {
DRC33c15e32011-11-03 18:49:21 +0000187 while (h > 0) {
DRCa5004a32011-11-04 04:51:17 +0000188 memcpy(ptr, srcPtr, rowSize * sizeof(PIXEL_T));
DRC33c15e32011-11-03 18:49:21 +0000189 ptr += stride;
DRCa5004a32011-11-04 04:51:17 +0000190 srcPtr += w * sizeof(PIXEL_T);
DRC33c15e32011-11-03 18:49:21 +0000191 h--;
192 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000193 }
194 }
195 } else {
DRC33c15e32011-11-03 18:49:21 +0000196 // Indexed color
197 int x, h = r.height(), w = r.width(), b, pad = stride - w;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198 PIXEL_T *ptr = buf;
DRCa5004a32011-11-04 04:51:17 +0000199 rdr::U8 bits, *srcPtr = netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000200 if (palSize <= 2) {
201 // 2-color palette
DRC33c15e32011-11-03 18:49:21 +0000202 while (h > 0) {
203 for (x = 0; x < w / 8; x++) {
DRCa5004a32011-11-04 04:51:17 +0000204 bits = *srcPtr++;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 for (b = 7; b >= 0; b--) {
206 *ptr++ = palette[bits >> b & 1];
DRC33c15e32011-11-03 18:49:21 +0000207 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000208 }
DRC33c15e32011-11-03 18:49:21 +0000209 if (w % 8 != 0) {
DRCa5004a32011-11-04 04:51:17 +0000210 bits = *srcPtr++;
DRC33c15e32011-11-03 18:49:21 +0000211 for (b = 7; b >= 8 - w % 8; b--) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 *ptr++ = palette[bits >> b & 1];
213 }
214 }
DRC33c15e32011-11-03 18:49:21 +0000215 ptr += pad;
216 h--;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000217 }
218 } else {
219 // 256-color palette
DRC33c15e32011-11-03 18:49:21 +0000220 while (h > 0) {
221 PIXEL_T *endOfRow = ptr + w;
222 while (ptr < endOfRow) {
DRCa5004a32011-11-04 04:51:17 +0000223 *ptr++ = palette[*srcPtr++];
DRC33c15e32011-11-03 18:49:21 +0000224 }
225 ptr += pad;
226 h--;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227 }
228 }
229 }
230
DRC33c15e32011-11-03 18:49:21 +0000231 if (directDecode) handler->releaseRawPixels(r);
232 else IMAGE_RECT(r, buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233
DRCa5004a32011-11-04 04:51:17 +0000234 delete [] netbuf;
235
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236 if (streamId != -1) {
237 zis[streamId].reset();
238 }
239}
240
DRC33c15e32011-11-03 18:49:21 +0000241void
242DECOMPRESS_JPEG_RECT(const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000243{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244 // Read length
245 int compressedLen = is->readCompactLength();
246 if (compressedLen <= 0) {
247 throw Exception("Incorrect data received from the server.\n");
248 }
249
250 // Allocate netbuf and read in data
251 rdr::U8* netbuf = new rdr::U8[compressedLen];
252 if (!netbuf) {
DRCa5004a32011-11-04 04:51:17 +0000253 throw Exception("rfb::TightDecoder::DecompressJpegRect unable to allocate buffer");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000254 }
255 is->readBytes(netbuf, compressedLen);
256
DRC33c15e32011-11-03 18:49:21 +0000257 // We always use direct decoding with JPEG images
258 int stride;
259 rdr::U8 *buf = handler->getRawPixelsRW(r, &stride);
260 jd.decompress(netbuf, compressedLen, buf, stride * clientpf.bpp / 8, r,
261 clientpf);
262 handler->releaseRawPixels(r);
DRCe420dcb2009-04-06 07:20:34 +0000263
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265}
266
267#if BPP == 32
268
DRC33c15e32011-11-03 18:49:21 +0000269void
DRCa5004a32011-11-04 04:51:17 +0000270TightDecoder::FilterGradient24(rdr::U8 *netbuf, PIXEL_T* buf, int stride,
271 const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000272{
273 int x, y, c;
274 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
275 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3];
276 rdr::U8 pix[3];
277 int est[3];
278
279 memset(prevRow, 0, sizeof(prevRow));
280
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000281 // Set up shortcut variables
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000282 int rectHeight = r.height();
283 int rectWidth = r.width();
284
285 for (y = 0; y < rectHeight; y++) {
286 /* First pixel in a row */
287 for (c = 0; c < 3; c++) {
288 pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
289 thisRow[c] = pix[c];
290 }
DRC33c15e32011-11-03 18:49:21 +0000291 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292
293 /* Remaining pixels of a row */
294 for (x = 1; x < rectWidth; x++) {
295 for (c = 0; c < 3; c++) {
DRC33c15e32011-11-03 18:49:21 +0000296 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
297 if (est[c] > 0xff) {
298 est[c] = 0xff;
299 } else if (est[c] < 0) {
300 est[c] = 0;
301 }
302 pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
303 thisRow[x*3+c] = pix[c];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304 }
DRC33c15e32011-11-03 18:49:21 +0000305 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000306 }
307
308 memcpy(prevRow, thisRow, sizeof(prevRow));
309 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310}
311
312#endif
313
DRC33c15e32011-11-03 18:49:21 +0000314void
DRCa5004a32011-11-04 04:51:17 +0000315FILTER_GRADIENT(rdr::U8 *netbuf, PIXEL_T* buf, int stride, const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000316{
317 int x, y, c;
318 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
319 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000320 rdr::U8 pix[3];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321 int est[3];
322
323 memset(prevRow, 0, sizeof(prevRow));
324
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000325 // Set up shortcut variables
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000326 int rectHeight = r.height();
327 int rectWidth = r.width();
328
329 for (y = 0; y < rectHeight; y++) {
330 /* First pixel in a row */
DRC33c15e32011-11-03 18:49:21 +0000331 serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000332 for (c = 0; c < 3; c++)
333 pix[c] += prevRow[c];
334
335 memcpy(thisRow, pix, sizeof(pix));
336
DRC33c15e32011-11-03 18:49:21 +0000337 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000338
339 /* Remaining pixels of a row */
340 for (x = 1; x < rectWidth; x++) {
341 for (c = 0; c < 3; c++) {
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000342 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
343 if (est[c] > 255) {
344 est[c] = 255;
345 } else if (est[c] < 0) {
346 est[c] = 0;
347 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000348 }
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000349
DRC33c15e32011-11-03 18:49:21 +0000350 serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000351 for (c = 0; c < 3; c++)
352 pix[c] += est[c];
353
354 memcpy(&thisRow[x*3], pix, sizeof(pix));
355
DRC33c15e32011-11-03 18:49:21 +0000356 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000357 }
358
359 memcpy(prevRow, thisRow, sizeof(prevRow));
360 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000361}
362
363#undef TIGHT_MIN_TO_COMPRESS
DRC33c15e32011-11-03 18:49:21 +0000364#undef FILTER_GRADIENT
365#undef DECOMPRESS_JPEG_RECT
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366#undef TIGHT_DECODE
367#undef READ_PIXEL
368#undef PIXEL_T
369}