blob: ac8b0a048e5fce355c1d9b644f64fd78e27bebac [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 Åstrand462753d2004-11-16 15:23:25 +000052
53// Main function implementing Tight decoder
54
55void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
56 rdr::ZlibInStream zis[], PIXEL_T* buf
57#ifdef EXTRA_ARGS
58 , EXTRA_ARGS
59#endif
60 )
61{
Peter Åstrandbf27e3a2004-12-13 08:00:47 +000062 rdr::U8 *bytebuf = (rdr::U8*) buf;
Peter Åstrandfd21d652004-12-12 13:24:14 +000063 bool cutZeros = false;
64 const rfb::PixelFormat& myFormat = handler->cp.pf();
65#if BPP == 32
66 if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
67 myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
68 cutZeros = true;
69 }
70#endif
71
Peter Åstrand462753d2004-11-16 15:23:25 +000072 rdr::U8 comp_ctl = is->readU8();
73
74 // Flush zlib streams if we are told by the server to do so.
75 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000076 if (comp_ctl & 1) {
77 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000078 }
Peter Åstrand462753d2004-11-16 15:23:25 +000079 comp_ctl >>= 1;
80 }
81
82 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000083 if (comp_ctl == rfbTightFill) {
Peter Åstrandfd21d652004-12-12 13:24:14 +000084 PIXEL_T pix;
85 if (cutZeros) {
Peter Åstrandbf27e3a2004-12-13 08:00:47 +000086 is->readBytes(bytebuf, 3);
87 pix = RGB24_TO_PIXEL32(bytebuf[0], bytebuf[1], bytebuf[2]);
Peter Åstrandfd21d652004-12-12 13:24:14 +000088 } else {
89 pix = is->READ_PIXEL();
90 }
Peter Åstrand462753d2004-11-16 15:23:25 +000091 FILL_RECT(r, pix);
92 return;
93 }
94
95 // "JPEG" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000096 if (comp_ctl == rfbTightJpeg) {
Peter Åstranda6bb7702004-12-07 08:22:42 +000097 DecompressJpegRect(r, is, buf, handler);
98 return;
Peter Åstrand462753d2004-11-16 15:23:25 +000099 }
100
101 // Quit on unsupported compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000102 if (comp_ctl > rfbTightMaxSubencoding) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000103 throw Exception("TightDecoder: bad subencoding value received");
104 return;
105 }
106
107 // "Basic" compression type.
108 int palSize = 0;
Peter Åstrand0af24d42004-12-09 20:01:00 +0000109 static PIXEL_T palette[256];
Peter Åstrand462753d2004-11-16 15:23:25 +0000110 bool useGradient = false;
111
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000112 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000113 rdr::U8 filterId = is->readU8();
114
115 switch (filterId) {
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000116 case rfbTightFilterPalette:
Peter Åstrand462753d2004-11-16 15:23:25 +0000117 palSize = is->readU8() + 1;
Peter Åstrandfd21d652004-12-12 13:24:14 +0000118 if (cutZeros) {
119 rdr::U8 *tightPalette = (rdr::U8*) palette;
120 is->readBytes(tightPalette, palSize*3);
121 for (int i = palSize - 1; i >= 0; i--) {
122 palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3],
123 tightPalette[i*3+1],
124 tightPalette[i*3+2]);
125 }
126 } else {
127 for (int i = 0; i < palSize; i++)
128 palette[i] = is->READ_PIXEL();
Peter Åstrand462753d2004-11-16 15:23:25 +0000129 }
130 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000131 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000132 useGradient = true;
133 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000134 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000135 break;
136 default:
137 throw Exception("TightDecoder: unknown filter code received");
138 return;
139 }
140 }
141
142 int bppp = BPP;
143 if (palSize != 0) {
144 bppp = (palSize <= 2) ? 1 : 8;
Peter Åstrandbf27e3a2004-12-13 08:00:47 +0000145 } else if (cutZeros) {
146 bppp = 24;
Peter Åstrand462753d2004-11-16 15:23:25 +0000147 }
148
149 // Determine if the data should be decompressed or just copied.
150 int rowSize = (r.width() * bppp + 7) / 8;
151 int dataSize = r.height() * rowSize;
152 rdr::InStream *input;
153 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
154 input = is;
155 } else {
156 int length = is->readCompactLength();
157 int streamId = comp_ctl & 0x03;
158 zis[streamId].setUnderlying(is, length);
159 input = &zis[streamId];
160 }
161
162 if (palSize == 0) {
163 // Truecolor data
Peter Åstrand462753d2004-11-16 15:23:25 +0000164 if (useGradient) {
Peter Åstrandbf27e3a2004-12-13 08:00:47 +0000165 FilterGradient(r, input, dataSize, buf, handler);
Peter Åstrand0af24d42004-12-09 20:01:00 +0000166 } else {
Peter Åstrandbf27e3a2004-12-13 08:00:47 +0000167 input->readBytes(buf, dataSize);
168 if (cutZeros) {
169 for (int p = r.height() * r.width() - 1; p >= 0; p--) {
170 buf[p] = RGB24_TO_PIXEL32(bytebuf[p*3],
171 bytebuf[p*3+1],
172 bytebuf[p*3+2]);
173 }
174 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000175 }
176 } else {
177 int x, y, b, w;
178 PIXEL_T *ptr = buf;
179 rdr::U8 bits;
180 if (palSize <= 2) {
181 // 2-color palette
182 w = (r.width() + 7) / 8;
183 for (y = 0; y < r.height(); y++) {
184 for (x = 0; x < r.width() / 8; x++) {
185 bits = input->readU8();
Peter Åstrand60444772004-12-12 13:17:30 +0000186 for (b = 7; b >= 0; b--) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000187 *ptr++ = palette[bits >> b & 1];
Peter Åstrand60444772004-12-12 13:17:30 +0000188 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000189 }
190 if (r.width() % 8 != 0) {
191 bits = input->readU8();
192 for (b = 7; b >= 8 - r.width() % 8; b--) {
193 *ptr++ = palette[bits >> b & 1];
194 }
195 }
196 }
197 } else {
198 // 256-color palette
Peter Åstrand60444772004-12-12 13:17:30 +0000199 for (y = 0; y < r.height(); y++) {
200 for (x = 0; x < r.width(); x++) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000201 *ptr++ = palette[input->readU8()];
Peter Åstrand60444772004-12-12 13:17:30 +0000202 }
203 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000204 }
205 }
206
207 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000208}
209
Peter Åstranda6bb7702004-12-07 08:22:42 +0000210static bool
211DecompressJpegRect(const Rect& r, rdr::InStream* is,
212 PIXEL_T* buf, CMsgHandler* handler)
213{
214 struct jpeg_decompress_struct cinfo;
215 struct jpeg_error_mgr jerr;
216 PIXEL_T *pixelPtr;
217 JSAMPROW scanline;
218
219 // Read length
220 int compressedLen = is->readCompactLength();
221 if (compressedLen <= 0) {
222 throw Exception("Incorrect data received from the server.\n");
223 }
224
225 // Allocate netbuf and read in data
226 rdr::U8* netbuf = new rdr::U8[compressedLen];
Peter Åstrand60444772004-12-12 13:17:30 +0000227 if (!netbuf) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000228 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000229 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000230 is->readBytes(netbuf, compressedLen);
231
232 // Set up JPEG decompression
233 cinfo.err = jpeg_std_error(&jerr);
234 jpeg_create_decompress(&cinfo);
235 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
236 jpeg_read_header(&cinfo, TRUE);
237 cinfo.out_color_space = JCS_RGB;
238
239 jpeg_start_decompress(&cinfo);
240 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
241 cinfo.output_components != 3) {
242 jpeg_destroy_decompress(&cinfo);
243 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
244 }
245
246 // Decompress
247 scanline = (JSAMPROW)buf;
248 const rfb::PixelFormat& myFormat = handler->cp.pf();
249 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
250 while (cinfo.output_scanline < cinfo.output_height) {
251 jpeg_read_scanlines(&cinfo, &scanline, 1);
252 if (jpegError) {
253 break;
254 }
255
256 pixelPtr = (PIXEL_T*)(scanline);
257 for (int dx = 0; dx < r.width(); dx++) {
258 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000259 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000260 }
261 scanline += bytesPerRow;
262 }
263
264 IMAGE_RECT(r, buf);
265
Peter Åstrand60444772004-12-12 13:17:30 +0000266 if (!jpegError) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000267 jpeg_finish_decompress(&cinfo);
Peter Åstrand60444772004-12-12 13:17:30 +0000268 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000269
270 jpeg_destroy_decompress(&cinfo);
271
272 delete [] netbuf;
273
274 return !jpegError;
275}
276
277
Peter Åstrand0af24d42004-12-09 20:01:00 +0000278static void
279FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
280 PIXEL_T* buf, CMsgHandler* handler)
281{
282 int x, y, c;
283 static PIXEL_T prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
284 static PIXEL_T thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
285 int pix[3];
286 int max[3];
287 int shift[3];
288 int est[3];
289
290 memset(prevRow, 0, sizeof(prevRow));
291
292 // Allocate netbuf and read in data
293 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
Peter Åstrand60444772004-12-12 13:17:30 +0000294 if (!netbuf) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000295 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000296 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000297 is->readBytes(netbuf, dataSize);
298
299 // Set up shortcut variables
300 const rfb::PixelFormat& myFormat = handler->cp.pf();
301 max[0] = myFormat.redMax;
302 max[1] = myFormat.greenMax;
303 max[2] = myFormat.blueMax;
304 shift[0] = myFormat.redShift;
305 shift[1] = myFormat.greenShift;
306 shift[2] = myFormat.blueShift;
307 int rectHeight = r.height();
308 int rectWidth = r.width();
309
310 for (y = 0; y < rectHeight; y++) {
311 /* First pixel in a row */
312 for (c = 0; c < 3; c++) {
313 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
314 thisRow[c] = pix[c];
315 }
316 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
317
318 /* Remaining pixels of a row */
319 for (x = 1; x < rectWidth; x++) {
320 for (c = 0; c < 3; c++) {
321 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
322 if (est[c] > max[c]) {
323 est[c] = max[c];
324 } else if (est[c] < 0) {
325 est[c] = 0;
326 }
327 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
328 thisRow[x*3+c] = pix[c];
329 }
330 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
331 }
332
333 memcpy(prevRow, thisRow, sizeof(prevRow));
334 }
335
336 delete [] netbuf;
337}
338
339#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000340#undef TIGHT_DECODE
341#undef READ_PIXEL
342#undef PIXEL_T
343}