blob: d91676100ddbcd68c86dd6ef850cb582c547c6bd [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;
Peter Åstrand6b911662004-12-13 08:53:49 +0000216 PIXEL_T *pixelPtr = buf;
217 static rdr::U8 scanline_buffer[TIGHT_MAX_WIDTH*3];
218 JSAMPROW scanline = scanline_buffer;
Peter Åstranda6bb7702004-12-07 08:22:42 +0000219
220 // Read length
221 int compressedLen = is->readCompactLength();
222 if (compressedLen <= 0) {
223 throw Exception("Incorrect data received from the server.\n");
224 }
225
226 // Allocate netbuf and read in data
227 rdr::U8* netbuf = new rdr::U8[compressedLen];
Peter Åstrand60444772004-12-12 13:17:30 +0000228 if (!netbuf) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000229 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000230 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000231 is->readBytes(netbuf, compressedLen);
232
233 // Set up JPEG decompression
234 cinfo.err = jpeg_std_error(&jerr);
235 jpeg_create_decompress(&cinfo);
236 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
237 jpeg_read_header(&cinfo, TRUE);
238 cinfo.out_color_space = JCS_RGB;
239
240 jpeg_start_decompress(&cinfo);
241 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
242 cinfo.output_components != 3) {
243 jpeg_destroy_decompress(&cinfo);
244 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
245 }
246
247 // Decompress
Peter Åstranda6bb7702004-12-07 08:22:42 +0000248 const rfb::PixelFormat& myFormat = handler->cp.pf();
Peter Åstranda6bb7702004-12-07 08:22:42 +0000249 while (cinfo.output_scanline < cinfo.output_height) {
250 jpeg_read_scanlines(&cinfo, &scanline, 1);
251 if (jpegError) {
252 break;
253 }
254
Peter Åstranda6bb7702004-12-07 08:22:42 +0000255 for (int dx = 0; dx < r.width(); dx++) {
256 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000257 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000258 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000259 }
260
261 IMAGE_RECT(r, buf);
262
Peter Åstrand60444772004-12-12 13:17:30 +0000263 if (!jpegError) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000264 jpeg_finish_decompress(&cinfo);
Peter Åstrand60444772004-12-12 13:17:30 +0000265 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000266
267 jpeg_destroy_decompress(&cinfo);
268
269 delete [] netbuf;
270
271 return !jpegError;
272}
273
274
Peter Åstrand0af24d42004-12-09 20:01:00 +0000275static void
276FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
277 PIXEL_T* buf, CMsgHandler* handler)
278{
279 int x, y, c;
280 static PIXEL_T prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
281 static PIXEL_T thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
282 int pix[3];
283 int max[3];
284 int shift[3];
285 int est[3];
286
287 memset(prevRow, 0, sizeof(prevRow));
288
289 // Allocate netbuf and read in data
290 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
Peter Åstrand60444772004-12-12 13:17:30 +0000291 if (!netbuf) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000292 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000293 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000294 is->readBytes(netbuf, dataSize);
295
296 // Set up shortcut variables
297 const rfb::PixelFormat& myFormat = handler->cp.pf();
298 max[0] = myFormat.redMax;
299 max[1] = myFormat.greenMax;
300 max[2] = myFormat.blueMax;
301 shift[0] = myFormat.redShift;
302 shift[1] = myFormat.greenShift;
303 shift[2] = myFormat.blueShift;
304 int rectHeight = r.height();
305 int rectWidth = r.width();
306
307 for (y = 0; y < rectHeight; y++) {
308 /* First pixel in a row */
309 for (c = 0; c < 3; c++) {
310 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
311 thisRow[c] = pix[c];
312 }
313 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
314
315 /* Remaining pixels of a row */
316 for (x = 1; x < rectWidth; x++) {
317 for (c = 0; c < 3; c++) {
318 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
319 if (est[c] > max[c]) {
320 est[c] = max[c];
321 } else if (est[c] < 0) {
322 est[c] = 0;
323 }
324 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
325 thisRow[x*3+c] = pix[c];
326 }
327 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
328 }
329
330 memcpy(prevRow, thisRow, sizeof(prevRow));
331 }
332
333 delete [] netbuf;
334}
335
336#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000337#undef TIGHT_DECODE
338#undef READ_PIXEL
339#undef PIXEL_T
340}