blob: d066a5583a0605548f5228d3fdcdec3c14d4ff0c [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 Åstrand462753d2004-11-16 15:23:25 +000050
51// Main function implementing Tight decoder
52
53void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
54 rdr::ZlibInStream zis[], PIXEL_T* buf
55#ifdef EXTRA_ARGS
56 , EXTRA_ARGS
57#endif
58 )
59{
60 rdr::U8 comp_ctl = is->readU8();
61
62 // Flush zlib streams if we are told by the server to do so.
63 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000064 if (comp_ctl & 1) {
65 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000066 }
Peter Åstrand462753d2004-11-16 15:23:25 +000067 comp_ctl >>= 1;
68 }
69
70 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000071 if (comp_ctl == rfbTightFill) {
Peter Åstrand462753d2004-11-16 15:23:25 +000072 PIXEL_T pix = is->READ_PIXEL();
73 FILL_RECT(r, pix);
74 return;
75 }
76
77 // "JPEG" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000078 if (comp_ctl == rfbTightJpeg) {
Peter Åstranda6bb7702004-12-07 08:22:42 +000079 DecompressJpegRect(r, is, buf, handler);
80 return;
Peter Åstrand462753d2004-11-16 15:23:25 +000081 }
82
83 // Quit on unsupported compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000084 if (comp_ctl > rfbTightMaxSubencoding) {
Peter Åstrand462753d2004-11-16 15:23:25 +000085 throw Exception("TightDecoder: bad subencoding value received");
86 return;
87 }
88
89 // "Basic" compression type.
90 int palSize = 0;
91 PIXEL_T palette[256];
92 bool useGradient = false;
93
Peter Åstrand5bbe3482004-11-17 09:45:44 +000094 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
Peter Åstrand462753d2004-11-16 15:23:25 +000095 rdr::U8 filterId = is->readU8();
96
97 switch (filterId) {
Peter Åstrand5bbe3482004-11-17 09:45:44 +000098 case rfbTightFilterPalette:
Peter Åstrand462753d2004-11-16 15:23:25 +000099 palSize = is->readU8() + 1;
100 {
101 for (int i = 0; i < palSize; i++)
102 palette[i] = is->READ_PIXEL();
103 }
104 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000105 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000106 useGradient = true;
107 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000108 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000109 break;
110 default:
111 throw Exception("TightDecoder: unknown filter code received");
112 return;
113 }
114 }
115
116 int bppp = BPP;
117 if (palSize != 0) {
118 bppp = (palSize <= 2) ? 1 : 8;
119 }
120
121 // Determine if the data should be decompressed or just copied.
122 int rowSize = (r.width() * bppp + 7) / 8;
123 int dataSize = r.height() * rowSize;
124 rdr::InStream *input;
125 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
126 input = is;
127 } else {
128 int length = is->readCompactLength();
129 int streamId = comp_ctl & 0x03;
130 zis[streamId].setUnderlying(is, length);
131 input = &zis[streamId];
132 }
133
134 if (palSize == 0) {
135 // Truecolor data
136 input->readBytes(buf, dataSize);
137 if (useGradient) {
138 // FIXME: Implement the "gradient" filter.
139 }
140 } else {
141 int x, y, b, w;
142 PIXEL_T *ptr = buf;
143 rdr::U8 bits;
144 if (palSize <= 2) {
145 // 2-color palette
146 w = (r.width() + 7) / 8;
147 for (y = 0; y < r.height(); y++) {
148 for (x = 0; x < r.width() / 8; x++) {
149 bits = input->readU8();
150 for (b = 7; b >= 0; b--)
151 *ptr++ = palette[bits >> b & 1];
152 }
153 if (r.width() % 8 != 0) {
154 bits = input->readU8();
155 for (b = 7; b >= 8 - r.width() % 8; b--) {
156 *ptr++ = palette[bits >> b & 1];
157 }
158 }
159 }
160 } else {
161 // 256-color palette
162 for (y = 0; y < r.height(); y++)
163 for (x = 0; x < r.width(); x++)
164 *ptr++ = palette[input->readU8()];
165 }
166 }
167
168 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000169}
170
Peter Åstranda6bb7702004-12-07 08:22:42 +0000171static bool
172DecompressJpegRect(const Rect& r, rdr::InStream* is,
173 PIXEL_T* buf, CMsgHandler* handler)
174{
175 struct jpeg_decompress_struct cinfo;
176 struct jpeg_error_mgr jerr;
177 PIXEL_T *pixelPtr;
178 JSAMPROW scanline;
179
180 // Read length
181 int compressedLen = is->readCompactLength();
182 if (compressedLen <= 0) {
183 throw Exception("Incorrect data received from the server.\n");
184 }
185
186 // Allocate netbuf and read in data
187 rdr::U8* netbuf = new rdr::U8[compressedLen];
188 if (!netbuf)
189 throw Exception("rfb::tightDecode unable to allocate buffer");
190 is->readBytes(netbuf, compressedLen);
191
192 // Set up JPEG decompression
193 cinfo.err = jpeg_std_error(&jerr);
194 jpeg_create_decompress(&cinfo);
195 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
196 jpeg_read_header(&cinfo, TRUE);
197 cinfo.out_color_space = JCS_RGB;
198
199 jpeg_start_decompress(&cinfo);
200 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
201 cinfo.output_components != 3) {
202 jpeg_destroy_decompress(&cinfo);
203 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
204 }
205
206 // Decompress
207 scanline = (JSAMPROW)buf;
208 const rfb::PixelFormat& myFormat = handler->cp.pf();
209 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
210 while (cinfo.output_scanline < cinfo.output_height) {
211 jpeg_read_scanlines(&cinfo, &scanline, 1);
212 if (jpegError) {
213 break;
214 }
215
216 pixelPtr = (PIXEL_T*)(scanline);
217 for (int dx = 0; dx < r.width(); dx++) {
218 *pixelPtr++ =
219 RGB24_TO_PIXEL(BPP, scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
220 }
221 scanline += bytesPerRow;
222 }
223
224 IMAGE_RECT(r, buf);
225
226 if (!jpegError)
227 jpeg_finish_decompress(&cinfo);
228
229 jpeg_destroy_decompress(&cinfo);
230
231 delete [] netbuf;
232
233 return !jpegError;
234}
235
236
Peter Åstrand462753d2004-11-16 15:23:25 +0000237#undef TIGHT_DECODE
238#undef READ_PIXEL
239#undef PIXEL_T
240}