blob: aa4b973ec3da235266504819bcf8ed2e8ed622ab [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{
62 rdr::U8 comp_ctl = is->readU8();
63
64 // Flush zlib streams if we are told by the server to do so.
65 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000066 if (comp_ctl & 1) {
67 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000068 }
Peter Åstrand462753d2004-11-16 15:23:25 +000069 comp_ctl >>= 1;
70 }
71
72 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000073 if (comp_ctl == rfbTightFill) {
Peter Åstrand462753d2004-11-16 15:23:25 +000074 PIXEL_T pix = is->READ_PIXEL();
75 FILL_RECT(r, pix);
76 return;
77 }
78
79 // "JPEG" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000080 if (comp_ctl == rfbTightJpeg) {
Peter Åstranda6bb7702004-12-07 08:22:42 +000081 DecompressJpegRect(r, is, buf, handler);
82 return;
Peter Åstrand462753d2004-11-16 15:23:25 +000083 }
84
85 // Quit on unsupported compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000086 if (comp_ctl > rfbTightMaxSubencoding) {
Peter Åstrand462753d2004-11-16 15:23:25 +000087 throw Exception("TightDecoder: bad subencoding value received");
88 return;
89 }
90
91 // "Basic" compression type.
92 int palSize = 0;
Peter Åstrand0af24d42004-12-09 20:01:00 +000093 static PIXEL_T palette[256];
Peter Åstrand462753d2004-11-16 15:23:25 +000094 bool useGradient = false;
95
Peter Åstrand5bbe3482004-11-17 09:45:44 +000096 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
Peter Åstrand462753d2004-11-16 15:23:25 +000097 rdr::U8 filterId = is->readU8();
98
99 switch (filterId) {
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000100 case rfbTightFilterPalette:
Peter Åstrand462753d2004-11-16 15:23:25 +0000101 palSize = is->readU8() + 1;
102 {
Peter Åstrand60444772004-12-12 13:17:30 +0000103 for (int i = 0; i < palSize; i++) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000104 palette[i] = is->READ_PIXEL();
Peter Åstrand60444772004-12-12 13:17:30 +0000105 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000106 }
107 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000108 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000109 useGradient = true;
110 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000111 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000112 break;
113 default:
114 throw Exception("TightDecoder: unknown filter code received");
115 return;
116 }
117 }
118
119 int bppp = BPP;
120 if (palSize != 0) {
121 bppp = (palSize <= 2) ? 1 : 8;
122 }
123
124 // Determine if the data should be decompressed or just copied.
125 int rowSize = (r.width() * bppp + 7) / 8;
126 int dataSize = r.height() * rowSize;
127 rdr::InStream *input;
128 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
129 input = is;
130 } else {
131 int length = is->readCompactLength();
132 int streamId = comp_ctl & 0x03;
133 zis[streamId].setUnderlying(is, length);
134 input = &zis[streamId];
135 }
136
137 if (palSize == 0) {
138 // Truecolor data
Peter Åstrand462753d2004-11-16 15:23:25 +0000139 if (useGradient) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000140 FilterGradient(r, input, dataSize, buf, handler);
141 } else {
142 input->readBytes(buf, dataSize);
Peter Åstrand462753d2004-11-16 15:23:25 +0000143 }
144 } else {
145 int x, y, b, w;
146 PIXEL_T *ptr = buf;
147 rdr::U8 bits;
148 if (palSize <= 2) {
149 // 2-color palette
150 w = (r.width() + 7) / 8;
151 for (y = 0; y < r.height(); y++) {
152 for (x = 0; x < r.width() / 8; x++) {
153 bits = input->readU8();
Peter Åstrand60444772004-12-12 13:17:30 +0000154 for (b = 7; b >= 0; b--) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000155 *ptr++ = palette[bits >> b & 1];
Peter Åstrand60444772004-12-12 13:17:30 +0000156 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000157 }
158 if (r.width() % 8 != 0) {
159 bits = input->readU8();
160 for (b = 7; b >= 8 - r.width() % 8; b--) {
161 *ptr++ = palette[bits >> b & 1];
162 }
163 }
164 }
165 } else {
166 // 256-color palette
Peter Åstrand60444772004-12-12 13:17:30 +0000167 for (y = 0; y < r.height(); y++) {
168 for (x = 0; x < r.width(); x++) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000169 *ptr++ = palette[input->readU8()];
Peter Åstrand60444772004-12-12 13:17:30 +0000170 }
171 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000172 }
173 }
174
175 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000176}
177
Peter Åstranda6bb7702004-12-07 08:22:42 +0000178static bool
179DecompressJpegRect(const Rect& r, rdr::InStream* is,
180 PIXEL_T* buf, CMsgHandler* handler)
181{
182 struct jpeg_decompress_struct cinfo;
183 struct jpeg_error_mgr jerr;
184 PIXEL_T *pixelPtr;
185 JSAMPROW scanline;
186
187 // Read length
188 int compressedLen = is->readCompactLength();
189 if (compressedLen <= 0) {
190 throw Exception("Incorrect data received from the server.\n");
191 }
192
193 // Allocate netbuf and read in data
194 rdr::U8* netbuf = new rdr::U8[compressedLen];
Peter Åstrand60444772004-12-12 13:17:30 +0000195 if (!netbuf) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000196 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000197 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000198 is->readBytes(netbuf, compressedLen);
199
200 // Set up JPEG decompression
201 cinfo.err = jpeg_std_error(&jerr);
202 jpeg_create_decompress(&cinfo);
203 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
204 jpeg_read_header(&cinfo, TRUE);
205 cinfo.out_color_space = JCS_RGB;
206
207 jpeg_start_decompress(&cinfo);
208 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
209 cinfo.output_components != 3) {
210 jpeg_destroy_decompress(&cinfo);
211 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
212 }
213
214 // Decompress
215 scanline = (JSAMPROW)buf;
216 const rfb::PixelFormat& myFormat = handler->cp.pf();
217 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
218 while (cinfo.output_scanline < cinfo.output_height) {
219 jpeg_read_scanlines(&cinfo, &scanline, 1);
220 if (jpegError) {
221 break;
222 }
223
224 pixelPtr = (PIXEL_T*)(scanline);
225 for (int dx = 0; dx < r.width(); dx++) {
226 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000227 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000228 }
229 scanline += bytesPerRow;
230 }
231
232 IMAGE_RECT(r, buf);
233
Peter Åstrand60444772004-12-12 13:17:30 +0000234 if (!jpegError) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000235 jpeg_finish_decompress(&cinfo);
Peter Åstrand60444772004-12-12 13:17:30 +0000236 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000237
238 jpeg_destroy_decompress(&cinfo);
239
240 delete [] netbuf;
241
242 return !jpegError;
243}
244
245
Peter Åstrand0af24d42004-12-09 20:01:00 +0000246static void
247FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
248 PIXEL_T* buf, CMsgHandler* handler)
249{
250 int x, y, c;
251 static PIXEL_T prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
252 static PIXEL_T thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
253 int pix[3];
254 int max[3];
255 int shift[3];
256 int est[3];
257
258 memset(prevRow, 0, sizeof(prevRow));
259
260 // Allocate netbuf and read in data
261 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
Peter Åstrand60444772004-12-12 13:17:30 +0000262 if (!netbuf) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000263 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000264 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000265 is->readBytes(netbuf, dataSize);
266
267 // Set up shortcut variables
268 const rfb::PixelFormat& myFormat = handler->cp.pf();
269 max[0] = myFormat.redMax;
270 max[1] = myFormat.greenMax;
271 max[2] = myFormat.blueMax;
272 shift[0] = myFormat.redShift;
273 shift[1] = myFormat.greenShift;
274 shift[2] = myFormat.blueShift;
275 int rectHeight = r.height();
276 int rectWidth = r.width();
277
278 for (y = 0; y < rectHeight; y++) {
279 /* First pixel in a row */
280 for (c = 0; c < 3; c++) {
281 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
282 thisRow[c] = pix[c];
283 }
284 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
285
286 /* Remaining pixels of a row */
287 for (x = 1; x < rectWidth; x++) {
288 for (c = 0; c < 3; c++) {
289 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
290 if (est[c] > max[c]) {
291 est[c] = max[c];
292 } else if (est[c] < 0) {
293 est[c] = 0;
294 }
295 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
296 thisRow[x*3+c] = pix[c];
297 }
298 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
299 }
300
301 memcpy(prevRow, thisRow, sizeof(prevRow));
302 }
303
304 delete [] netbuf;
305}
306
307#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000308#undef TIGHT_DECODE
309#undef READ_PIXEL
310#undef PIXEL_T
311}