blob: a6b36f86d57aa2aa7a1f6e80507e8ae7b5e1bdd6 [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
26// FILL_RECT - fill a rectangle with a single colour
27// 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 {
103 for (int i = 0; i < palSize; i++)
104 palette[i] = is->READ_PIXEL();
105 }
106 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000107 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000108 useGradient = true;
109 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000110 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000111 break;
112 default:
113 throw Exception("TightDecoder: unknown filter code received");
114 return;
115 }
116 }
117
118 int bppp = BPP;
119 if (palSize != 0) {
120 bppp = (palSize <= 2) ? 1 : 8;
121 }
122
123 // Determine if the data should be decompressed or just copied.
124 int rowSize = (r.width() * bppp + 7) / 8;
125 int dataSize = r.height() * rowSize;
126 rdr::InStream *input;
127 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
128 input = is;
129 } else {
130 int length = is->readCompactLength();
131 int streamId = comp_ctl & 0x03;
132 zis[streamId].setUnderlying(is, length);
133 input = &zis[streamId];
134 }
135
136 if (palSize == 0) {
137 // Truecolor data
Peter Åstrand462753d2004-11-16 15:23:25 +0000138 if (useGradient) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000139 FilterGradient(r, input, dataSize, buf, handler);
140 } else {
141 input->readBytes(buf, dataSize);
Peter Åstrand462753d2004-11-16 15:23:25 +0000142 }
143 } else {
144 int x, y, b, w;
145 PIXEL_T *ptr = buf;
146 rdr::U8 bits;
147 if (palSize <= 2) {
148 // 2-color palette
149 w = (r.width() + 7) / 8;
150 for (y = 0; y < r.height(); y++) {
151 for (x = 0; x < r.width() / 8; x++) {
152 bits = input->readU8();
153 for (b = 7; b >= 0; b--)
154 *ptr++ = palette[bits >> b & 1];
155 }
156 if (r.width() % 8 != 0) {
157 bits = input->readU8();
158 for (b = 7; b >= 8 - r.width() % 8; b--) {
159 *ptr++ = palette[bits >> b & 1];
160 }
161 }
162 }
163 } else {
164 // 256-color palette
165 for (y = 0; y < r.height(); y++)
166 for (x = 0; x < r.width(); x++)
167 *ptr++ = palette[input->readU8()];
168 }
169 }
170
171 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000172}
173
Peter Åstranda6bb7702004-12-07 08:22:42 +0000174static bool
175DecompressJpegRect(const Rect& r, rdr::InStream* is,
176 PIXEL_T* buf, CMsgHandler* handler)
177{
178 struct jpeg_decompress_struct cinfo;
179 struct jpeg_error_mgr jerr;
180 PIXEL_T *pixelPtr;
181 JSAMPROW scanline;
182
183 // Read length
184 int compressedLen = is->readCompactLength();
185 if (compressedLen <= 0) {
186 throw Exception("Incorrect data received from the server.\n");
187 }
188
189 // Allocate netbuf and read in data
190 rdr::U8* netbuf = new rdr::U8[compressedLen];
191 if (!netbuf)
192 throw Exception("rfb::tightDecode unable to allocate buffer");
193 is->readBytes(netbuf, compressedLen);
194
195 // Set up JPEG decompression
196 cinfo.err = jpeg_std_error(&jerr);
197 jpeg_create_decompress(&cinfo);
198 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
199 jpeg_read_header(&cinfo, TRUE);
200 cinfo.out_color_space = JCS_RGB;
201
202 jpeg_start_decompress(&cinfo);
203 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
204 cinfo.output_components != 3) {
205 jpeg_destroy_decompress(&cinfo);
206 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
207 }
208
209 // Decompress
210 scanline = (JSAMPROW)buf;
211 const rfb::PixelFormat& myFormat = handler->cp.pf();
212 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
213 while (cinfo.output_scanline < cinfo.output_height) {
214 jpeg_read_scanlines(&cinfo, &scanline, 1);
215 if (jpegError) {
216 break;
217 }
218
219 pixelPtr = (PIXEL_T*)(scanline);
220 for (int dx = 0; dx < r.width(); dx++) {
221 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000222 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000223 }
224 scanline += bytesPerRow;
225 }
226
227 IMAGE_RECT(r, buf);
228
229 if (!jpegError)
230 jpeg_finish_decompress(&cinfo);
231
232 jpeg_destroy_decompress(&cinfo);
233
234 delete [] netbuf;
235
236 return !jpegError;
237}
238
239
Peter Åstrand0af24d42004-12-09 20:01:00 +0000240static void
241FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
242 PIXEL_T* buf, CMsgHandler* handler)
243{
244 int x, y, c;
245 static PIXEL_T prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
246 static PIXEL_T thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
247 int pix[3];
248 int max[3];
249 int shift[3];
250 int est[3];
251
252 memset(prevRow, 0, sizeof(prevRow));
253
254 // Allocate netbuf and read in data
255 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
256 if (!netbuf)
257 throw Exception("rfb::tightDecode unable to allocate buffer");
258 is->readBytes(netbuf, dataSize);
259
260 // Set up shortcut variables
261 const rfb::PixelFormat& myFormat = handler->cp.pf();
262 max[0] = myFormat.redMax;
263 max[1] = myFormat.greenMax;
264 max[2] = myFormat.blueMax;
265 shift[0] = myFormat.redShift;
266 shift[1] = myFormat.greenShift;
267 shift[2] = myFormat.blueShift;
268 int rectHeight = r.height();
269 int rectWidth = r.width();
270
271 for (y = 0; y < rectHeight; y++) {
272 /* First pixel in a row */
273 for (c = 0; c < 3; c++) {
274 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
275 thisRow[c] = pix[c];
276 }
277 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
278
279 /* Remaining pixels of a row */
280 for (x = 1; x < rectWidth; x++) {
281 for (c = 0; c < 3; c++) {
282 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
283 if (est[c] > max[c]) {
284 est[c] = max[c];
285 } else if (est[c] < 0) {
286 est[c] = 0;
287 }
288 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
289 thisRow[x*3+c] = pix[c];
290 }
291 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
292 }
293
294 memcpy(prevRow, thisRow, sizeof(prevRow));
295 }
296
297 delete [] netbuf;
298}
299
300#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000301#undef TIGHT_DECODE
302#undef READ_PIXEL
303#undef PIXEL_T
304}