blob: cab49fdf16437217a5bc68a33465d64e00fa8739 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
Peter Åstrandd69bcc42011-09-28 12:52:53 +00002 * Copyright 2004-2005 Cendio AB.
Pierre Ossman80b42092015-11-10 17:17:34 +01003 * Copyright 2009-2015 Pierre Ossman for Cendio AB
DRC33c15e32011-11-03 18:49:21 +00004 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00005 *
6 * This is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
20 */
Pierre Ossman80b42092015-11-10 17:17:34 +010021
22#include <assert.h>
23
Pierre Ossman86350622015-11-10 13:02:12 +010024#include <rdr/InStream.h>
Pierre Ossman80b42092015-11-10 17:17:34 +010025#include <rdr/MemInStream.h>
26#include <rdr/OutStream.h>
27
Pierre Ossman86350622015-11-10 13:02:12 +010028#include <rfb/ConnParams.h>
Pierre Ossman80b42092015-11-10 17:17:34 +010029#include <rfb/Exception.h>
Pierre Ossman0c9bd4b2014-07-09 16:44:11 +020030#include <rfb/PixelBuffer.h>
Pierre Ossman80b42092015-11-10 17:17:34 +010031#include <rfb/TightConstants.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000032#include <rfb/TightDecoder.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033
34using namespace rfb;
35
Pierre Ossman80b42092015-11-10 17:17:34 +010036static const int TIGHT_MAX_WIDTH = 2048;
37static const int TIGHT_MIN_TO_COMPRESS = 12;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039#define BPP 8
40#include <rfb/tightDecode.h>
41#undef BPP
42#define BPP 16
43#include <rfb/tightDecode.h>
44#undef BPP
45#define BPP 32
46#include <rfb/tightDecode.h>
47#undef BPP
48
Pierre Ossman86350622015-11-10 13:02:12 +010049TightDecoder::TightDecoder()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050{
51}
52
53TightDecoder::~TightDecoder()
54{
55}
56
Pierre Ossman86350622015-11-10 13:02:12 +010057void TightDecoder::readRect(const Rect& r, rdr::InStream* is,
Pierre Ossman80b42092015-11-10 17:17:34 +010058 const ConnParams& cp, rdr::OutStream* os)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059{
Pierre Ossman80b42092015-11-10 17:17:34 +010060 rdr::U8 comp_ctl;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061
Pierre Ossman80b42092015-11-10 17:17:34 +010062 comp_ctl = is->readU8();
63 os->writeU8(comp_ctl);
64
65 comp_ctl >>= 4;
66
67 // "Fill" compression type.
68 if (comp_ctl == tightFill) {
69 if (cp.pf().is888())
70 os->copyBytes(is, 3);
71 else
72 os->copyBytes(is, cp.pf().bpp/8);
73 return;
74 }
75
76 // "JPEG" compression type.
77 if (comp_ctl == tightJpeg) {
78 rdr::U32 len;
79
80 len = readCompact(is);
81 os->writeOpaque32(len);
82 os->copyBytes(is, len);
83 return;
84 }
85
86 // Quit on unsupported compression type.
87 if (comp_ctl > tightMaxSubencoding)
88 throw Exception("TightDecoder: bad subencoding value received");
89
90 // "Basic" compression type.
91
92 int palSize = 0;
93
94 if (r.width() > TIGHT_MAX_WIDTH)
95 throw Exception("TightDecoder: too large rectangle (%d pixels)", r.width());
96
97 // Possible palette
98 if ((comp_ctl & tightExplicitFilter) != 0) {
99 rdr::U8 filterId;
100
101 filterId = is->readU8();
102 os->writeU8(filterId);
103
104 switch (filterId) {
105 case tightFilterPalette:
106 palSize = is->readU8() + 1;
107 os->writeU8(palSize - 1);
108
109 if (cp.pf().is888())
110 os->copyBytes(is, palSize * 3);
111 else
112 os->copyBytes(is, palSize * cp.pf().bpp/8);
113 break;
114 case tightFilterGradient:
115 if (cp.pf().bpp == 8)
116 throw Exception("TightDecoder: invalid BPP for gradient filter");
117 break;
118 case tightFilterCopy:
119 break;
120 default:
121 throw Exception("TightDecoder: unknown filter code received");
122 }
123 }
124
125 size_t rowSize, dataSize;
126
127 if (palSize != 0) {
128 if (palSize <= 2)
129 rowSize = (r.width() + 7) / 8;
130 else
131 rowSize = r.width();
132 } else if (cp.pf().is888()) {
133 rowSize = r.width() * 3;
134 } else {
135 rowSize = r.width() * cp.pf().bpp/8;
136 }
137
138 dataSize = r.height() * rowSize;
139
140 if (dataSize < TIGHT_MIN_TO_COMPRESS)
141 os->copyBytes(is, dataSize);
142 else {
143 rdr::U32 len;
144
145 len = readCompact(is);
146 os->writeOpaque32(len);
147 os->copyBytes(is, len);
148 }
149}
150
151void TightDecoder::decodeRect(const Rect& r, const void* buffer,
152 size_t buflen, const ConnParams& cp,
153 ModifiablePixelBuffer* pb)
154{
155 const rdr::U8* bufptr;
156 const PixelFormat& pf = cp.pf();
157
158 rdr::U8 comp_ctl;
159
160 bufptr = (const rdr::U8*)buffer;
161
162 assert(buflen >= 1);
163
164 comp_ctl = *bufptr;
165 bufptr += 1;
166 buflen -= 1;
167
168 // Flush zlib streams if we are told by the server to do so.
169 for (int i = 0; i < 4; i++) {
170 if (comp_ctl & 1) {
171 zis[i].reset();
172 }
173 comp_ctl >>= 1;
174 }
175
176 // "Fill" compression type.
177 if (comp_ctl == tightFill) {
178 if (pf.is888()) {
179 rdr::U8 pix[4];
180
181 assert(buflen >= 3);
182
183 pf.bufferFromRGB(pix, bufptr, 1);
184 pb->fillRect(pf, r, pix);
185 } else {
186 assert(buflen >= (size_t)pf.bpp/8);
187 pb->fillRect(pf, r, bufptr);
188 }
189 return;
190 }
191
192 // "JPEG" compression type.
193 if (comp_ctl == tightJpeg) {
194 rdr::U32 len;
195
196 int stride;
197 rdr::U8 *buf;
198
199 assert(buflen >= 4);
200
201 memcpy(&len, bufptr, 4);
202 bufptr += 4;
203 buflen -= 4;
204
205 // We always use direct decoding with JPEG images
206 buf = pb->getBufferRW(r, &stride);
207 jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
208 pb->commitBufferRW(r);
209 return;
210 }
211
212 // Quit on unsupported compression type.
213 assert(comp_ctl <= tightMaxSubencoding);
214
215 // "Basic" compression type.
216
217 int palSize = 0;
218 rdr::U8 palette[256 * 4];
219 bool useGradient = false;
220
221 if ((comp_ctl & tightExplicitFilter) != 0) {
222 rdr::U8 filterId;
223
224 assert(buflen >= 1);
225
226 filterId = *bufptr;
227 bufptr += 1;
228 buflen -= 1;
229
230 switch (filterId) {
231 case tightFilterPalette:
232 assert(buflen >= 1);
233
234 palSize = *bufptr + 1;
235 bufptr += 1;
236 buflen -= 1;
237
238 if (pf.is888()) {
239 rdr::U8 tightPalette[palSize * 3];
240
241 assert(buflen >= sizeof(tightPalette));
242
243 memcpy(tightPalette, bufptr, sizeof(tightPalette));
244 bufptr += sizeof(tightPalette);
245 buflen -= sizeof(tightPalette);
246
247 pf.bufferFromRGB(palette, tightPalette, palSize);
248 } else {
249 size_t len;
250
251 len = palSize * pf.bpp/8;
252
253 assert(buflen >= len);
254
255 memcpy(palette, bufptr, len);
256 bufptr += len;
257 buflen -= len;
258 }
259 break;
260 case tightFilterGradient:
261 useGradient = true;
262 break;
263 case tightFilterCopy:
264 break;
265 default:
266 assert(false);
267 }
268 }
269
270 // Determine if the data should be decompressed or just copied.
271 size_t rowSize, dataSize;
272 rdr::U8* netbuf;
273
274 netbuf = NULL;
275
276 if (palSize != 0) {
277 if (palSize <= 2)
278 rowSize = (r.width() + 7) / 8;
279 else
280 rowSize = r.width();
281 } else if (pf.is888()) {
282 rowSize = r.width() * 3;
283 } else {
284 rowSize = r.width() * pf.bpp/8;
285 }
286
287 dataSize = r.height() * rowSize;
288
289 if (dataSize < TIGHT_MIN_TO_COMPRESS)
290 assert(buflen >= dataSize);
291 else {
292 rdr::U32 len;
293 int streamId;
294 rdr::MemInStream* ms;
295
296 assert(buflen >= 4);
297
298 memcpy(&len, bufptr, 4);
299 bufptr += 4;
300 buflen -= 4;
301
302 assert(buflen >= len);
303
304 streamId = comp_ctl & 0x03;
305 ms = new rdr::MemInStream(bufptr, len);
306 zis[streamId].setUnderlying(ms, len);
307
308 // Allocate buffer and decompress the data
309 netbuf = new rdr::U8[dataSize];
310
311 zis[streamId].readBytes(netbuf, dataSize);
312 zis[streamId].reset();
313
314 delete ms;
315
316 bufptr = netbuf;
317 buflen = dataSize;
318 }
319
320 // Time to decode the actual data
321 bool directDecode;
322
323 rdr::U8* outbuf;
324 int stride;
325
326 if (pb->getPF().equal(pf)) {
327 // Decode directly into the framebuffer (fast path)
DRC33c15e32011-11-03 18:49:21 +0000328 directDecode = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000329 } else {
Pierre Ossman80b42092015-11-10 17:17:34 +0100330 // Decode into an intermediate buffer and use pixel translation
DRC33c15e32011-11-03 18:49:21 +0000331 directDecode = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000332 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000333
Pierre Ossman80b42092015-11-10 17:17:34 +0100334 if (directDecode)
335 outbuf = pb->getBufferRW(r, &stride);
336 else {
337 outbuf = new rdr::U8[r.area() * pf.bpp/8];
338 stride = r.width();
DRC33c15e32011-11-03 18:49:21 +0000339 }
Pierre Ossman80b42092015-11-10 17:17:34 +0100340
341 if (palSize == 0) {
342 // Truecolor data
343 if (useGradient) {
344 if (pf.is888())
345 FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
346 else {
347 switch (pf.bpp) {
348 case 8:
349 assert(false);
350 break;
351 case 16:
352 FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
353 break;
354 case 32:
355 FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
356 break;
357 }
358 }
359 } else {
360 // Copy
361 rdr::U8* ptr = outbuf;
362 const rdr::U8* srcPtr = bufptr;
363 int w = r.width();
364 int h = r.height();
365 if (pf.is888()) {
366 while (h > 0) {
367 pf.bufferFromRGB(ptr, srcPtr, w);
368 ptr += stride * pf.bpp/8;
369 srcPtr += w * 3;
370 h--;
371 }
372 } else {
373 while (h > 0) {
374 memcpy(ptr, srcPtr, w * pf.bpp/8);
375 ptr += stride * pf.bpp/8;
376 srcPtr += w * pf.bpp/8;
377 h--;
378 }
379 }
380 }
381 } else {
382 // Indexed color
383 switch (pf.bpp) {
384 case 8:
385 FilterPalette((const rdr::U8*)palette, palSize,
386 bufptr, (rdr::U8*)outbuf, stride, r);
387 break;
388 case 16:
389 FilterPalette((const rdr::U16*)palette, palSize,
390 bufptr, (rdr::U16*)outbuf, stride, r);
391 break;
392 case 32:
393 FilterPalette((const rdr::U32*)palette, palSize,
394 bufptr, (rdr::U32*)outbuf, stride, r);
395 break;
396 }
397 }
398
399 if (directDecode)
400 pb->commitBufferRW(r);
401 else {
402 pb->imageRect(pf, r, outbuf);
403 delete [] outbuf;
404 }
405
406 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000407}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100408
409rdr::U32 TightDecoder::readCompact(rdr::InStream* is)
410{
411 rdr::U8 b;
412 rdr::U32 result;
413
414 b = is->readU8();
415 result = (int)b & 0x7F;
416 if (b & 0x80) {
417 b = is->readU8();
418 result |= ((int)b & 0x7F) << 7;
419 if (b & 0x80) {
420 b = is->readU8();
421 result |= ((int)b & 0xFF) << 14;
422 }
423 }
424
425 return result;
426}