blob: bc13a0d8e13adb9509f6dc2798b4a12a7301af3b [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 Åstrandfd21d652004-12-12 13:24:14 +000062 bool cutZeros = false;
63 const rfb::PixelFormat& myFormat = handler->cp.pf();
64#if BPP == 32
65 if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
66 myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
67 cutZeros = true;
68 }
69#endif
70
Peter Åstrand462753d2004-11-16 15:23:25 +000071 rdr::U8 comp_ctl = is->readU8();
72
73 // Flush zlib streams if we are told by the server to do so.
74 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000075 if (comp_ctl & 1) {
76 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000077 }
Peter Åstrand462753d2004-11-16 15:23:25 +000078 comp_ctl >>= 1;
79 }
80
81 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000082 if (comp_ctl == rfbTightFill) {
Peter Åstrandfd21d652004-12-12 13:24:14 +000083 PIXEL_T pix;
84 if (cutZeros) {
85 rdr::U8 *fillColorBuf = (rdr::U8*)buf;
86 is->readBytes(fillColorBuf, 3);
87 pix = RGB24_TO_PIXEL32(fillColorBuf[0], fillColorBuf[1], fillColorBuf[2]);
88 } 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;
145 }
146
147 // Determine if the data should be decompressed or just copied.
148 int rowSize = (r.width() * bppp + 7) / 8;
149 int dataSize = r.height() * rowSize;
150 rdr::InStream *input;
151 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
152 input = is;
153 } else {
154 int length = is->readCompactLength();
155 int streamId = comp_ctl & 0x03;
156 zis[streamId].setUnderlying(is, length);
157 input = &zis[streamId];
158 }
159
160 if (palSize == 0) {
161 // Truecolor data
Peter Åstrand462753d2004-11-16 15:23:25 +0000162 if (useGradient) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000163 FilterGradient(r, input, dataSize, buf, handler);
164 } else {
165 input->readBytes(buf, dataSize);
Peter Åstrand462753d2004-11-16 15:23:25 +0000166 }
167 } else {
168 int x, y, b, w;
169 PIXEL_T *ptr = buf;
170 rdr::U8 bits;
171 if (palSize <= 2) {
172 // 2-color palette
173 w = (r.width() + 7) / 8;
174 for (y = 0; y < r.height(); y++) {
175 for (x = 0; x < r.width() / 8; x++) {
176 bits = input->readU8();
Peter Åstrand60444772004-12-12 13:17:30 +0000177 for (b = 7; b >= 0; b--) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000178 *ptr++ = palette[bits >> b & 1];
Peter Åstrand60444772004-12-12 13:17:30 +0000179 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000180 }
181 if (r.width() % 8 != 0) {
182 bits = input->readU8();
183 for (b = 7; b >= 8 - r.width() % 8; b--) {
184 *ptr++ = palette[bits >> b & 1];
185 }
186 }
187 }
188 } else {
189 // 256-color palette
Peter Åstrand60444772004-12-12 13:17:30 +0000190 for (y = 0; y < r.height(); y++) {
191 for (x = 0; x < r.width(); x++) {
Peter Åstrand462753d2004-11-16 15:23:25 +0000192 *ptr++ = palette[input->readU8()];
Peter Åstrand60444772004-12-12 13:17:30 +0000193 }
194 }
Peter Åstrand462753d2004-11-16 15:23:25 +0000195 }
196 }
197
198 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000199}
200
Peter Åstranda6bb7702004-12-07 08:22:42 +0000201static bool
202DecompressJpegRect(const Rect& r, rdr::InStream* is,
203 PIXEL_T* buf, CMsgHandler* handler)
204{
205 struct jpeg_decompress_struct cinfo;
206 struct jpeg_error_mgr jerr;
207 PIXEL_T *pixelPtr;
208 JSAMPROW scanline;
209
210 // Read length
211 int compressedLen = is->readCompactLength();
212 if (compressedLen <= 0) {
213 throw Exception("Incorrect data received from the server.\n");
214 }
215
216 // Allocate netbuf and read in data
217 rdr::U8* netbuf = new rdr::U8[compressedLen];
Peter Åstrand60444772004-12-12 13:17:30 +0000218 if (!netbuf) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000219 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000220 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000221 is->readBytes(netbuf, compressedLen);
222
223 // Set up JPEG decompression
224 cinfo.err = jpeg_std_error(&jerr);
225 jpeg_create_decompress(&cinfo);
226 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
227 jpeg_read_header(&cinfo, TRUE);
228 cinfo.out_color_space = JCS_RGB;
229
230 jpeg_start_decompress(&cinfo);
231 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
232 cinfo.output_components != 3) {
233 jpeg_destroy_decompress(&cinfo);
234 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
235 }
236
237 // Decompress
238 scanline = (JSAMPROW)buf;
239 const rfb::PixelFormat& myFormat = handler->cp.pf();
240 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
241 while (cinfo.output_scanline < cinfo.output_height) {
242 jpeg_read_scanlines(&cinfo, &scanline, 1);
243 if (jpegError) {
244 break;
245 }
246
247 pixelPtr = (PIXEL_T*)(scanline);
248 for (int dx = 0; dx < r.width(); dx++) {
249 *pixelPtr++ =
Peter Åstrand0af24d42004-12-09 20:01:00 +0000250 RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
Peter Åstranda6bb7702004-12-07 08:22:42 +0000251 }
252 scanline += bytesPerRow;
253 }
254
255 IMAGE_RECT(r, buf);
256
Peter Åstrand60444772004-12-12 13:17:30 +0000257 if (!jpegError) {
Peter Åstranda6bb7702004-12-07 08:22:42 +0000258 jpeg_finish_decompress(&cinfo);
Peter Åstrand60444772004-12-12 13:17:30 +0000259 }
Peter Åstranda6bb7702004-12-07 08:22:42 +0000260
261 jpeg_destroy_decompress(&cinfo);
262
263 delete [] netbuf;
264
265 return !jpegError;
266}
267
268
Peter Åstrand0af24d42004-12-09 20:01:00 +0000269static void
270FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
271 PIXEL_T* buf, CMsgHandler* handler)
272{
273 int x, y, c;
274 static PIXEL_T prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
275 static PIXEL_T thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
276 int pix[3];
277 int max[3];
278 int shift[3];
279 int est[3];
280
281 memset(prevRow, 0, sizeof(prevRow));
282
283 // Allocate netbuf and read in data
284 PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize];
Peter Åstrand60444772004-12-12 13:17:30 +0000285 if (!netbuf) {
Peter Åstrand0af24d42004-12-09 20:01:00 +0000286 throw Exception("rfb::tightDecode unable to allocate buffer");
Peter Åstrand60444772004-12-12 13:17:30 +0000287 }
Peter Åstrand0af24d42004-12-09 20:01:00 +0000288 is->readBytes(netbuf, dataSize);
289
290 // Set up shortcut variables
291 const rfb::PixelFormat& myFormat = handler->cp.pf();
292 max[0] = myFormat.redMax;
293 max[1] = myFormat.greenMax;
294 max[2] = myFormat.blueMax;
295 shift[0] = myFormat.redShift;
296 shift[1] = myFormat.greenShift;
297 shift[2] = myFormat.blueShift;
298 int rectHeight = r.height();
299 int rectWidth = r.width();
300
301 for (y = 0; y < rectHeight; y++) {
302 /* First pixel in a row */
303 for (c = 0; c < 3; c++) {
304 pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c];
305 thisRow[c] = pix[c];
306 }
307 buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
308
309 /* Remaining pixels of a row */
310 for (x = 1; x < rectWidth; x++) {
311 for (c = 0; c < 3; c++) {
312 est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
313 if (est[c] > max[c]) {
314 est[c] = max[c];
315 } else if (est[c] < 0) {
316 est[c] = 0;
317 }
318 pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c];
319 thisRow[x*3+c] = pix[c];
320 }
321 buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]);
322 }
323
324 memcpy(prevRow, thisRow, sizeof(prevRow));
325 }
326
327 delete [] netbuf;
328}
329
330#undef TIGHT_MIN_TO_COMPRESS
Peter Åstrand462753d2004-11-16 15:23:25 +0000331#undef TIGHT_DECODE
332#undef READ_PIXEL
333#undef PIXEL_T
334}