blob: 4ea18a972c027dc9b95e57df1ecc2822e2bbb51c [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)
49#define DIRECT_FILL_RECT TightDecoder::CONCAT2E(directFillRect,BPP)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050
51#define TIGHT_MIN_TO_COMPRESS 12
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052
53// Main function implementing Tight decoder
54
DRC33c15e32011-11-03 18:49:21 +000055void TIGHT_DECODE (const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057 bool cutZeros = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058#if BPP == 32
DRC33c15e32011-11-03 18:49:21 +000059 if (serverpf.is888()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060 cutZeros = true;
61 }
62#endif
63
64 rdr::U8 comp_ctl = is->readU8();
65
66 // Flush zlib streams if we are told by the server to do so.
67 for (int i = 0; i < 4; i++) {
68 if (comp_ctl & 1) {
69 zis[i].reset();
70 }
71 comp_ctl >>= 1;
72 }
73
74 // "Fill" compression type.
75 if (comp_ctl == rfbTightFill) {
76 PIXEL_T pix;
77 if (cutZeros) {
DRC33c15e32011-11-03 18:49:21 +000078 rdr::U8 bytebuf[3];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079 is->readBytes(bytebuf, 3);
DRC33c15e32011-11-03 18:49:21 +000080 serverpf.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081 } else {
82 pix = is->READ_PIXEL();
83 }
DRC33c15e32011-11-03 18:49:21 +000084 if (directDecode) DIRECT_FILL_RECT(r, pix);
85 else FILL_RECT(r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086 return;
87 }
88
89 // "JPEG" compression type.
90 if (comp_ctl == rfbTightJpeg) {
DRC33c15e32011-11-03 18:49:21 +000091 DECOMPRESS_JPEG_RECT(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092 return;
93 }
94
95 // Quit on unsupported compression type.
96 if (comp_ctl > rfbTightMaxSubencoding) {
97 throw Exception("TightDecoder: bad subencoding value received");
98 return;
99 }
100
101 // "Basic" compression type.
102 int palSize = 0;
103 static PIXEL_T palette[256];
104 bool useGradient = false;
105
106 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
107 rdr::U8 filterId = is->readU8();
108
109 switch (filterId) {
110 case rfbTightFilterPalette:
111 palSize = is->readU8() + 1;
112 if (cutZeros) {
Pierre Ossman7c4efd72010-09-30 11:30:20 +0000113 rdr::U8 elem[3];
114 for (int i = 0;i < palSize;i++) {
115 is->readBytes(elem, 3);
DRC33c15e32011-11-03 18:49:21 +0000116 serverpf.bufferFromRGB((rdr::U8*)&palette[i], elem, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000117 }
118 } else {
DRC33c15e32011-11-03 18:49:21 +0000119 for (int i = 0; i < palSize; i++)
120 palette[i] = is->READ_PIXEL();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121 }
122 break;
123 case rfbTightFilterGradient:
124 useGradient = true;
125 break;
126 case rfbTightFilterCopy:
127 break;
128 default:
129 throw Exception("TightDecoder: unknown filter code received");
130 return;
131 }
132 }
133
134 int bppp = BPP;
135 if (palSize != 0) {
136 bppp = (palSize <= 2) ? 1 : 8;
137 } else if (cutZeros) {
138 bppp = 24;
139 }
140
141 // Determine if the data should be decompressed or just copied.
142 int rowSize = (r.width() * bppp + 7) / 8;
143 int dataSize = r.height() * rowSize;
144 int streamId = -1;
145 rdr::InStream *input;
146 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
147 input = is;
148 } else {
149 int length = is->readCompactLength();
150 streamId = comp_ctl & 0x03;
151 zis[streamId].setUnderlying(is, length);
152 input = &zis[streamId];
153 }
154
DRC33c15e32011-11-03 18:49:21 +0000155 PIXEL_T *buf;
156 int stride = r.width();
157 if (directDecode) buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
158 else buf = (PIXEL_T *)reader->getImageBuf(r.area());
159
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000160 if (palSize == 0) {
161 // Truecolor data
162 if (useGradient) {
163#if BPP == 32
164 if (cutZeros) {
DRC33c15e32011-11-03 18:49:21 +0000165 FilterGradient24(input, buf, stride, r, dataSize);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000166 } else
167#endif
DRC33c15e32011-11-03 18:49:21 +0000168 {
169 FILTER_GRADIENT(input, buf, stride, r, dataSize);
170 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171 } else {
DRC33c15e32011-11-03 18:49:21 +0000172 // Copy
173 int h = r.height();
174 PIXEL_T *ptr = buf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175 if (cutZeros) {
DRC33c15e32011-11-03 18:49:21 +0000176 int w = r.width(), pad = stride - w;
Pierre Ossman7c4efd72010-09-30 11:30:20 +0000177 rdr::U8 elem[3];
DRC33c15e32011-11-03 18:49:21 +0000178 while (h > 0) {
179 PIXEL_T *endOfRow = ptr + w;
180 while (ptr < endOfRow) {
181 input->readBytes(elem, 3);
182 serverpf.bufferFromRGB((rdr::U8*)ptr++, elem, 1, NULL);
183 }
184 ptr += pad;
185 h--;
Pierre Ossman7c4efd72010-09-30 11:30:20 +0000186 }
187 } else {
DRC33c15e32011-11-03 18:49:21 +0000188 while (h > 0) {
189 input->readBytes(ptr, rowSize);
190 ptr += stride;
191 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;
199 rdr::U8 bits;
200 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++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 bits = input->readU8();
205 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) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210 bits = input->readU8();
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) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 *ptr++ = palette[input->readU8()];
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
234 if (streamId != -1) {
235 zis[streamId].reset();
236 }
237}
238
DRC33c15e32011-11-03 18:49:21 +0000239void
240DECOMPRESS_JPEG_RECT(const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000241{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242 // Read length
243 int compressedLen = is->readCompactLength();
244 if (compressedLen <= 0) {
245 throw Exception("Incorrect data received from the server.\n");
246 }
247
248 // Allocate netbuf and read in data
249 rdr::U8* netbuf = new rdr::U8[compressedLen];
250 if (!netbuf) {
251 throw Exception("rfb::tightDecode unable to allocate buffer");
252 }
253 is->readBytes(netbuf, compressedLen);
254
DRC33c15e32011-11-03 18:49:21 +0000255 // We always use direct decoding with JPEG images
256 int stride;
257 rdr::U8 *buf = handler->getRawPixelsRW(r, &stride);
258 jd.decompress(netbuf, compressedLen, buf, stride * clientpf.bpp / 8, r,
259 clientpf);
260 handler->releaseRawPixels(r);
DRCe420dcb2009-04-06 07:20:34 +0000261
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000262 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000263}
264
265#if BPP == 32
266
DRC33c15e32011-11-03 18:49:21 +0000267void
268TightDecoder::FilterGradient24(rdr::InStream* is, PIXEL_T* buf, int stride,
269 const Rect& r, int dataSize)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000270{
271 int x, y, c;
272 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
273 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3];
274 rdr::U8 pix[3];
275 int est[3];
276
277 memset(prevRow, 0, sizeof(prevRow));
278
279 // Allocate netbuf and read in data
280 rdr::U8 *netbuf = new rdr::U8[dataSize];
281 if (!netbuf) {
282 throw Exception("rfb::tightDecode unable to allocate buffer");
283 }
284 is->readBytes(netbuf, dataSize);
285
286 // Set up shortcut variables
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000287 int rectHeight = r.height();
288 int rectWidth = r.width();
289
290 for (y = 0; y < rectHeight; y++) {
291 /* First pixel in a row */
292 for (c = 0; c < 3; c++) {
293 pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
294 thisRow[c] = pix[c];
295 }
DRC33c15e32011-11-03 18:49:21 +0000296 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000297
298 /* Remaining pixels of a row */
299 for (x = 1; x < rectWidth; x++) {
300 for (c = 0; c < 3; c++) {
DRC33c15e32011-11-03 18:49:21 +0000301 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
302 if (est[c] > 0xff) {
303 est[c] = 0xff;
304 } else if (est[c] < 0) {
305 est[c] = 0;
306 }
307 pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
308 thisRow[x*3+c] = pix[c];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000309 }
DRC33c15e32011-11-03 18:49:21 +0000310 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000311 }
312
313 memcpy(prevRow, thisRow, sizeof(prevRow));
314 }
315
316 delete [] netbuf;
317}
318
319#endif
320
DRC33c15e32011-11-03 18:49:21 +0000321void
322FILTER_GRADIENT(rdr::InStream* is, PIXEL_T* buf, int stride, const Rect& r,
323 int dataSize)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000324{
325 int x, y, c;
326 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
327 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000328 rdr::U8 pix[3];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000329 int est[3];
330
331 memset(prevRow, 0, sizeof(prevRow));
332
333 // Allocate netbuf and read in data
334 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
335 if (!netbuf) {
336 throw Exception("rfb::tightDecode unable to allocate buffer");
337 }
338 is->readBytes(netbuf, dataSize);
339
340 // Set up shortcut variables
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000341 int rectHeight = r.height();
342 int rectWidth = r.width();
343
344 for (y = 0; y < rectHeight; y++) {
345 /* First pixel in a row */
DRC33c15e32011-11-03 18:49:21 +0000346 serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000347 for (c = 0; c < 3; c++)
348 pix[c] += prevRow[c];
349
350 memcpy(thisRow, pix, sizeof(pix));
351
DRC33c15e32011-11-03 18:49:21 +0000352 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000353
354 /* Remaining pixels of a row */
355 for (x = 1; x < rectWidth; x++) {
356 for (c = 0; c < 3; c++) {
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000357 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
358 if (est[c] > 255) {
359 est[c] = 255;
360 } else if (est[c] < 0) {
361 est[c] = 0;
362 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000363 }
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000364
DRC33c15e32011-11-03 18:49:21 +0000365 serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000366 for (c = 0; c < 3; c++)
367 pix[c] += est[c];
368
369 memcpy(&thisRow[x*3], pix, sizeof(pix));
370
DRC33c15e32011-11-03 18:49:21 +0000371 serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000372 }
373
374 memcpy(prevRow, thisRow, sizeof(prevRow));
375 }
376
377 delete [] netbuf;
378}
379
DRC33c15e32011-11-03 18:49:21 +0000380void
381DIRECT_FILL_RECT(const Rect& r, Pixel pix) {
382
383 int stride;
384 PIXEL_T *buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
385
386 int w = r.width(), h = r.height();
387 PIXEL_T *ptr = buf;
388#if BPP != 8
389 int pad = stride - w;
390#endif
391
392 while (h > 0) {
393#if BPP == 8
394 memset(ptr, pix, w);
395 ptr += stride;
396#else
397 PIXEL_T *endOfRow = ptr + w;
398 while (ptr < endOfRow) {
399 *ptr++ = pix;
400 }
401 ptr += pad;
402#endif
403 h--;
404 }
405
406 handler->releaseRawPixels(r);
407}
408
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000409#undef TIGHT_MIN_TO_COMPRESS
DRC33c15e32011-11-03 18:49:21 +0000410#undef DIRECT_FILL_RECT
411#undef FILTER_GRADIENT
412#undef DECOMPRESS_JPEG_RECT
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413#undef TIGHT_DECODE
414#undef READ_PIXEL
415#undef PIXEL_T
416}