blob: 02589c9989db21d4b9de34265613e8fb6d3618f5 [file] [log] [blame]
Peter Åstrand462753d2004-11-16 15:23:25 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
Peter Åstrandb04748d2004-12-07 14:13:51 +00002 * Copyright (C) 2004 Peter Astrand, Cendio AB. All Rights Reserved.
Peter Åstrand462753d2004-11-16 15:23:25 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20//
21// Tight decoding functions.
22//
23// This file is #included after having set the following macros:
24// BPP - 8, 16 or 32
25// EXTRA_ARGS - optional extra arguments
Peter Åstrand60444772004-12-12 13:17:30 +000026// FILL_RECT - fill a rectangle with a single color
Peter Åstrand462753d2004-11-16 15:23:25 +000027// IMAGE_RECT - draw a rectangle of pixel data from a buffer
28
29#include <rdr/InStream.h>
30#include <rdr/ZlibInStream.h>
31#include <rfb/Exception.h>
32#include <assert.h>
33
34namespace rfb {
35
36// CONCAT2E concatenates its arguments, expanding them if they are macros
37
38#ifndef CONCAT2E
39#define CONCAT2(a,b) a##b
40#define CONCAT2E(a,b) CONCAT2(a,b)
41#endif
42
43#define PIXEL_T rdr::CONCAT2E(U,BPP)
44#define READ_PIXEL CONCAT2E(readOpaque,BPP)
45#define TIGHT_DECODE CONCAT2E(tightDecode,BPP)
46
47#define TIGHT_MIN_TO_COMPRESS 12
Peter Åstranda6bb7702004-12-07 08:22:42 +000048static bool DecompressJpegRect(const Rect& r, rdr::InStream* is,
49 PIXEL_T* buf, CMsgHandler* handler);
Peter Åstrand0af24d42004-12-09 20:01:00 +000050static void FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
51 PIXEL_T* buf, CMsgHandler* handler);
Peter Åstrand55ad6452004-12-13 10:18:45 +000052#if BPP == 32
53static void FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
54 PIXEL_T* buf, CMsgHandler* handler);
55#endif
Peter Åstrand462753d2004-11-16 15:23:25 +000056
57// Main function implementing Tight decoder
58
59void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
60 rdr::ZlibInStream zis[], PIXEL_T* buf
61#ifdef EXTRA_ARGS
62 , EXTRA_ARGS
63#endif
64 )
65{
Peter Åstrandbf27e3a2004-12-13 08:00:47 +000066 rdr::U8 *bytebuf = (rdr::U8*) buf;
Peter Åstrandfd21d652004-12-12 13:24:14 +000067 bool cutZeros = false;
68 const rfb::PixelFormat& myFormat = handler->cp.pf();
69#if BPP == 32
70 if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
71 myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
72 cutZeros = true;
73 }
74#endif
75
Peter Åstrand462753d2004-11-16 15:23:25 +000076 rdr::U8 comp_ctl = is->readU8();
77
78 // Flush zlib streams if we are told by the server to do so.
79 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000080 if (comp_ctl & 1) {
81 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000082 }
Peter Åstrand462753d2004-11-16 15:23:25 +000083 comp_ctl >>= 1;
84 }
85
86 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000087 if (comp_ctl == rfbTightFill) {
Peter Åstrandfd21d652004-12-12 13:24:14 +000088 PIXEL_T pix;
89 if (cutZeros) {
Peter Åstrandbf27e3a2004-12-13 08:00:47 +000090 is->readBytes(bytebuf, 3);
91 pix = RGB24_TO_PIXEL32(bytebuf[0], bytebuf[1], bytebuf[2]);
Peter Åstrandfd21d652004-12-12 13:24:14 +000092 } else {
93 pix = is->READ_PIXEL();
94 }
Peter Åstrand462753d2004-11-16 15:23:25 +000095 FILL_RECT(r, pix);
96 return;
97 }
98
99 // "JPEG" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000100 if (comp_ctl == rfbTightJpeg) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000101 DecompressJpegRect(r, is, buf, handler);
102 return;
Peter Åstrand462753d2004-11-16 15:23:25 +0000103 }
104
105 // Quit on unsupported compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000106 if (comp_ctl > rfbTightMaxSubencoding) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000107 throw Exception("TightDecoder: bad subencoding value received");
108 return;
109 }
110
111 // "Basic" compression type.
112 int palSize = 0;
Peter Åstrand0af24d42004-12-09 20:01:00 +0000113 static PIXEL_T palette[256];
Peter Åstrand462753d2004-11-16 15:23:25 +0000114 bool useGradient = false;
115
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000116 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000117 rdr::U8 filterId = is->readU8();
118
119 switch (filterId) {
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000120 case rfbTightFilterPalette:
Peter Åstrand462753d2004-11-16 15:23:25 +0000121 palSize = is->readU8() + 1;
Peter Åstrandfd21d652004-12-12 13:24:14 +0000122 if (cutZeros) {
123 rdr::U8 *tightPalette = (rdr::U8*) palette;
124 is->readBytes(tightPalette, palSize*3);
125 for (int i = palSize - 1; i >= 0; i--) {
126 palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3],
127 tightPalette[i*3+1],
128 tightPalette[i*3+2]);
129 }
130 } else {
131 for (int i = 0; i < palSize; i++)
132 palette[i] = is->READ_PIXEL();
Peter Åstrand462753d2004-11-16 15:23:25 +0000133 }
134 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000135 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000136 useGradient = true;
137 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000138 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000139 break;
140 default:
141 throw Exception("TightDecoder: unknown filter code received");
142 return;
143 }
144 }
145
146 int bppp = BPP;
147 if (palSize != 0) {
148 bppp = (palSize <= 2) ? 1 : 8;
Peter Åstrandbf27e3a2004-12-13 08:00:47 +0000149 } else if (cutZeros) {
150 bppp = 24;
Peter Åstrand462753d2004-11-16 15:23:25 +0000151 }
152
153 // Determine if the data should be decompressed or just copied.
154 int rowSize = (r.width() * bppp + 7) / 8;
155 int dataSize = r.height() * rowSize;
156 rdr::InStream *input;
157 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
158 input = is;
159 } else {
160 int length = is->readCompactLength();
161 int streamId = comp_ctl & 0x03;
162 zis[streamId].setUnderlying(is, length);
163 input = &zis[streamId];
164 }
165
166 if (palSize == 0) {
167 // Truecolor data
Peter Åstrand462753d2004-11-16 15:23:25 +0000168 if (useGradient) {
Peter Åstrand55ad6452004-12-13 10:18:45 +0000169#if BPP == 32
170 if (cutZeros) {
171 FilterGradient24(r, input, dataSize, buf, handler);
172 } else
173#endif
174 {
175 FilterGradient(r, input, dataSize, buf, handler);
176 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000177 } else {
Peter Åstrandbf27e3a2004-12-13 08:00:47 +0000178 input->readBytes(buf, dataSize);
179 if (cutZeros) {
180 for (int p = r.height() * r.width() - 1; p >= 0; p--) {
181 buf[p] = RGB24_TO_PIXEL32(bytebuf[p*3],
182 bytebuf[p*3+1],
183 bytebuf[p*3+2]);
184 }
185 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000186 }
187 } else {
188 int x, y, b, w;
189 PIXEL_T *ptr = buf;
190 rdr::U8 bits;
191 if (palSize <= 2) {
192 // 2-color palette
193 w = (r.width() + 7) / 8;
194 for (y = 0; y < r.height(); y++) {
195 for (x = 0; x < r.width() / 8; x++) {
196 bits = input->readU8();
Peter Åstrand60444772004-12-12 13:17:30 +0000197 for (b = 7; b >= 0; b--) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000198 *ptr++ = palette[bits >> b & 1];
Peter Åstrand60444772004-12-12 13:17:30 +0000199 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000200 }
201 if (r.width() % 8 != 0) {
202 bits = input->readU8();
203 for (b = 7; b >= 8 - r.width() % 8; b--) {
204 *ptr++ = palette[bits >> b & 1];
205 }
206 }
207 }
208 } else {
209 // 256-color palette
Peter Åstrand60444772004-12-12 13:17:30 +0000210 for (y = 0; y < r.height(); y++) {
211 for (x = 0; x < r.width(); x++) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000212 *ptr++ = palette[input->readU8()];
Peter Åstrand60444772004-12-12 13:17:30 +0000213 }
214 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000215 }
216 }
217
218 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000219}
220
Peter Åstranda6bb7702004-12-07 08:22:42 +0000221static bool
222DecompressJpegRect(const Rect& r, rdr::InStream* is,
223 PIXEL_T* buf, CMsgHandler* handler)
224{
225 struct jpeg_decompress_struct cinfo;
226 struct jpeg_error_mgr jerr;
Peter Åstrand6b911662004-12-13 08:53:49 +0000227 PIXEL_T *pixelPtr = buf;
228 static rdr::U8 scanline_buffer[TIGHT_MAX_WIDTH*3];
229 JSAMPROW scanline = scanline_buffer;
Peter Åstranda6bb7702004-12-07 08:22:42 +0000230
231 // Read length
232 int compressedLen = is->readCompactLength();
233 if (compressedLen <= 0) {
234 throw Exception("Incorrect data received from the server.\n");
235 }
236
237 // Allocate netbuf and read in data
238 rdr::U8* netbuf = new rdr::U8[compressedLen];
Peter Åstrand60444772004-12-12 13:17:30 +0000239 if (!netbuf) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000240 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000241 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000242 is->readBytes(netbuf, compressedLen);
243
244 // Set up JPEG decompression
245 cinfo.err = jpeg_std_error(&jerr);
246 jpeg_create_decompress(&cinfo);
247 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
248 jpeg_read_header(&cinfo, TRUE);
249 cinfo.out_color_space = JCS_RGB;
250
251 jpeg_start_decompress(&cinfo);
252 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
253 cinfo.output_components != 3) {
254 jpeg_destroy_decompress(&cinfo);
255 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
256 }
257
258 // Decompress
Peter Åstranda6bb7702004-12-07 08:22:42 +0000259 const rfb::PixelFormat& myFormat = handler->cp.pf();
Peter Åstranda6bb7702004-12-07 08:22:42 +0000260 while (cinfo.output_scanline < cinfo.output_height) {
261 jpeg_read_scanlines(&cinfo, &scanline, 1);
262 if (jpegError) {
263 break;
264 }
265
Peter Åstranda6bb7702004-12-07 08:22:42 +0000266 for (int dx = 0; dx < r.width(); dx++) {
267 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000268 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000269 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000270 }
271
272 IMAGE_RECT(r, buf);
273
Peter Åstrand60444772004-12-12 13:17:30 +0000274 if (!jpegError) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000275 jpeg_finish_decompress(&cinfo);
Peter Åstrand60444772004-12-12 13:17:30 +0000276 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000277
278 jpeg_destroy_decompress(&cinfo);
279
280 delete [] netbuf;
281
282 return !jpegError;
283}
284
Peter Åstrand55ad6452004-12-13 10:18:45 +0000285#if BPP == 32
286
287static void
288FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
289 PIXEL_T* buf, CMsgHandler* handler)
290{
291 int x, y, c;
292 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
293 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3];
294 rdr::U8 pix[3];
295 int est[3];
296
297 memset(prevRow, 0, sizeof(prevRow));
298
299 // Allocate netbuf and read in data
300 rdr::U8 *netbuf = new rdr::U8[dataSize];
301 if (!netbuf) {
302 throw Exception("rfb::tightDecode unable to allocate buffer");
303 }
304 is->readBytes(netbuf, dataSize);
305
306 // Set up shortcut variables
307 const rfb::PixelFormat& myFormat = handler->cp.pf();
308 int rectHeight = r.height();
309 int rectWidth = r.width();
310
311 for (y = 0; y < rectHeight; y++) {
312 /* First pixel in a row */
313 for (c = 0; c < 3; c++) {
314 pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
315 thisRow[c] = pix[c];
316 }
317 buf[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
318
319 /* Remaining pixels of a row */
320 for (x = 1; x < rectWidth; x++) {
321 for (c = 0; c < 3; c++) {
322 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
323 if (est[c] > 0xff) {
324 est[c] = 0xff;
325 } else if (est[c] < 0) {
326 est[c] = 0;
327 }
328 pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
329 thisRow[x*3+c] = pix[c];
330 }
331 buf[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
332 }
333
334 memcpy(prevRow, thisRow, sizeof(prevRow));
335 }
336
337 delete [] netbuf;
338}
339
340#endif
Peter Åstranda6bb7702004-12-07 08:22:42 +0000341
Peter Åstrand0af24d42004-12-09 20:01:00 +0000342static void
343FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
344 PIXEL_T* buf, CMsgHandler* handler)
345{
346 int x, y, c;
Peter Åstrand55ad6452004-12-13 10:18:45 +0000347 static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
348 static rdr::U8 thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
Peter Åstrand0af24d42004-12-09 20:01:00 +0000349 int pix[3];
350 int max[3];
351 int shift[3];
352 int est[3];
353
354 memset(prevRow, 0, sizeof(prevRow));
355
356 // Allocate netbuf and read in data
357 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
Peter Åstrand60444772004-12-12 13:17:30 +0000358 if (!netbuf) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000359 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000360 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000361 is->readBytes(netbuf, dataSize);
362
363 // Set up shortcut variables
364 const rfb::PixelFormat& myFormat = handler->cp.pf();
365 max[0] = myFormat.redMax;
366 max[1] = myFormat.greenMax;
367 max[2] = myFormat.blueMax;
368 shift[0] = myFormat.redShift;
369 shift[1] = myFormat.greenShift;
370 shift[2] = myFormat.blueShift;
371 int rectHeight = r.height();
372 int rectWidth = r.width();
373
374 for (y = 0; y < rectHeight; y++) {
375 /* First pixel in a row */
376 for (c = 0; c < 3; c++) {
377 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
378 thisRow[c] = pix[c];
379 }
380 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
381
382 /* Remaining pixels of a row */
383 for (x = 1; x < rectWidth; x++) {
384 for (c = 0; c < 3; c++) {
385 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
386 if (est[c] > max[c]) {
387 est[c] = max[c];
388 } else if (est[c] < 0) {
389 est[c] = 0;
390 }
391 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
392 thisRow[x*3+c] = pix[c];
393 }
394 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
395 }
396
397 memcpy(prevRow, thisRow, sizeof(prevRow));
398 }
399
400 delete [] netbuf;
401}
402
403#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000404#undef TIGHT_DECODE
405#undef READ_PIXEL
406#undef PIXEL_T
407}