blob: fad4731c2c82754181975a7850dd8e1ca2b8b4f5 [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 Ossmanb14a6bc2018-06-18 15:44:26 +020028#include <rfb/ServerParams.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 Ossmane6ad4452015-11-13 10:47:28 +010049TightDecoder::TightDecoder() : Decoder(DecoderPartiallyOrdered)
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 Ossmanb14a6bc2018-06-18 15:44:26 +020058 const ServerParams& server, 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) {
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020069 if (server.pf().is888())
Pierre Ossman80b42092015-11-10 17:17:34 +010070 os->copyBytes(is, 3);
71 else
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020072 os->copyBytes(is, server.pf().bpp/8);
Pierre Ossman80b42092015-11-10 17:17:34 +010073 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
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200109 if (server.pf().is888())
Pierre Ossman80b42092015-11-10 17:17:34 +0100110 os->copyBytes(is, palSize * 3);
111 else
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200112 os->copyBytes(is, palSize * server.pf().bpp/8);
Pierre Ossman80b42092015-11-10 17:17:34 +0100113 break;
114 case tightFilterGradient:
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200115 if (server.pf().bpp == 8)
Pierre Ossman80b42092015-11-10 17:17:34 +0100116 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();
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200132 } else if (server.pf().is888()) {
Pierre Ossman80b42092015-11-10 17:17:34 +0100133 rowSize = r.width() * 3;
134 } else {
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200135 rowSize = r.width() * server.pf().bpp/8;
Pierre Ossman80b42092015-11-10 17:17:34 +0100136 }
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
Pierre Ossmane6ad4452015-11-13 10:47:28 +0100151bool TightDecoder::doRectsConflict(const Rect& rectA,
152 const void* bufferA,
153 size_t buflenA,
154 const Rect& rectB,
155 const void* bufferB,
156 size_t buflenB,
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200157 const ServerParams& server)
Pierre Ossmane6ad4452015-11-13 10:47:28 +0100158{
159 rdr::U8 comp_ctl_a, comp_ctl_b;
160
161 assert(buflenA >= 1);
162 assert(buflenB >= 1);
163
164 comp_ctl_a = *(const rdr::U8*)bufferA;
165 comp_ctl_b = *(const rdr::U8*)bufferB;
166
167 // Resets or use of zlib pose the same problem, so merge them
168 if ((comp_ctl_a & 0x80) == 0x00)
169 comp_ctl_a |= 1 << ((comp_ctl_a >> 4) & 0x03);
170 if ((comp_ctl_b & 0x80) == 0x00)
171 comp_ctl_b |= 1 << ((comp_ctl_b >> 4) & 0x03);
172
173 if (((comp_ctl_a & 0x0f) & (comp_ctl_b & 0x0f)) != 0)
174 return true;
175
Pierre Ossmane6ad4452015-11-13 10:47:28 +0100176 return false;
177}
178
Pierre Ossman80b42092015-11-10 17:17:34 +0100179void TightDecoder::decodeRect(const Rect& r, const void* buffer,
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200180 size_t buflen, const ServerParams& server,
Pierre Ossman80b42092015-11-10 17:17:34 +0100181 ModifiablePixelBuffer* pb)
182{
183 const rdr::U8* bufptr;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200184 const PixelFormat& pf = server.pf();
Pierre Ossman80b42092015-11-10 17:17:34 +0100185
186 rdr::U8 comp_ctl;
187
188 bufptr = (const rdr::U8*)buffer;
189
190 assert(buflen >= 1);
191
192 comp_ctl = *bufptr;
193 bufptr += 1;
194 buflen -= 1;
195
Pierre Ossman6f318e42015-11-11 13:11:09 +0100196 // Reset zlib streams if we are told by the server to do so.
Pierre Ossman80b42092015-11-10 17:17:34 +0100197 for (int i = 0; i < 4; i++) {
198 if (comp_ctl & 1) {
199 zis[i].reset();
200 }
201 comp_ctl >>= 1;
202 }
203
204 // "Fill" compression type.
205 if (comp_ctl == tightFill) {
206 if (pf.is888()) {
207 rdr::U8 pix[4];
208
209 assert(buflen >= 3);
210
211 pf.bufferFromRGB(pix, bufptr, 1);
212 pb->fillRect(pf, r, pix);
213 } else {
214 assert(buflen >= (size_t)pf.bpp/8);
215 pb->fillRect(pf, r, bufptr);
216 }
217 return;
218 }
219
220 // "JPEG" compression type.
221 if (comp_ctl == tightJpeg) {
222 rdr::U32 len;
223
224 int stride;
225 rdr::U8 *buf;
226
Pierre Ossmanbf431a62015-11-13 11:16:33 +0100227 JpegDecompressor jd;
228
Pierre Ossman80b42092015-11-10 17:17:34 +0100229 assert(buflen >= 4);
230
231 memcpy(&len, bufptr, 4);
232 bufptr += 4;
233 buflen -= 4;
234
235 // We always use direct decoding with JPEG images
236 buf = pb->getBufferRW(r, &stride);
237 jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
238 pb->commitBufferRW(r);
239 return;
240 }
241
242 // Quit on unsupported compression type.
243 assert(comp_ctl <= tightMaxSubencoding);
244
245 // "Basic" compression type.
246
247 int palSize = 0;
248 rdr::U8 palette[256 * 4];
249 bool useGradient = false;
250
251 if ((comp_ctl & tightExplicitFilter) != 0) {
252 rdr::U8 filterId;
253
254 assert(buflen >= 1);
255
256 filterId = *bufptr;
257 bufptr += 1;
258 buflen -= 1;
259
260 switch (filterId) {
261 case tightFilterPalette:
262 assert(buflen >= 1);
263
264 palSize = *bufptr + 1;
265 bufptr += 1;
266 buflen -= 1;
267
268 if (pf.is888()) {
269 rdr::U8 tightPalette[palSize * 3];
270
271 assert(buflen >= sizeof(tightPalette));
272
273 memcpy(tightPalette, bufptr, sizeof(tightPalette));
274 bufptr += sizeof(tightPalette);
275 buflen -= sizeof(tightPalette);
276
277 pf.bufferFromRGB(palette, tightPalette, palSize);
278 } else {
279 size_t len;
280
281 len = palSize * pf.bpp/8;
282
283 assert(buflen >= len);
284
285 memcpy(palette, bufptr, len);
286 bufptr += len;
287 buflen -= len;
288 }
289 break;
290 case tightFilterGradient:
291 useGradient = true;
292 break;
293 case tightFilterCopy:
294 break;
295 default:
296 assert(false);
297 }
298 }
299
300 // Determine if the data should be decompressed or just copied.
301 size_t rowSize, dataSize;
302 rdr::U8* netbuf;
303
304 netbuf = NULL;
305
306 if (palSize != 0) {
307 if (palSize <= 2)
308 rowSize = (r.width() + 7) / 8;
309 else
310 rowSize = r.width();
311 } else if (pf.is888()) {
312 rowSize = r.width() * 3;
313 } else {
314 rowSize = r.width() * pf.bpp/8;
315 }
316
317 dataSize = r.height() * rowSize;
318
319 if (dataSize < TIGHT_MIN_TO_COMPRESS)
320 assert(buflen >= dataSize);
321 else {
322 rdr::U32 len;
323 int streamId;
324 rdr::MemInStream* ms;
325
326 assert(buflen >= 4);
327
328 memcpy(&len, bufptr, 4);
329 bufptr += 4;
330 buflen -= 4;
331
332 assert(buflen >= len);
333
334 streamId = comp_ctl & 0x03;
335 ms = new rdr::MemInStream(bufptr, len);
336 zis[streamId].setUnderlying(ms, len);
337
338 // Allocate buffer and decompress the data
339 netbuf = new rdr::U8[dataSize];
340
341 zis[streamId].readBytes(netbuf, dataSize);
Pierre Ossman80b42092015-11-10 17:17:34 +0100342
Pierre Ossman6f318e42015-11-11 13:11:09 +0100343 zis[streamId].removeUnderlying();
Pierre Ossman80b42092015-11-10 17:17:34 +0100344 delete ms;
345
346 bufptr = netbuf;
347 buflen = dataSize;
348 }
349
350 // Time to decode the actual data
351 bool directDecode;
352
353 rdr::U8* outbuf;
354 int stride;
355
356 if (pb->getPF().equal(pf)) {
357 // Decode directly into the framebuffer (fast path)
DRC33c15e32011-11-03 18:49:21 +0000358 directDecode = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000359 } else {
Pierre Ossman80b42092015-11-10 17:17:34 +0100360 // Decode into an intermediate buffer and use pixel translation
DRC33c15e32011-11-03 18:49:21 +0000361 directDecode = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000362 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000363
Pierre Ossman80b42092015-11-10 17:17:34 +0100364 if (directDecode)
365 outbuf = pb->getBufferRW(r, &stride);
366 else {
Pierre Ossmanf81148c2018-07-25 20:44:32 +0200367 outbuf = new rdr::U8[r.area() * (pf.bpp/8)];
Pierre Ossman80b42092015-11-10 17:17:34 +0100368 stride = r.width();
DRC33c15e32011-11-03 18:49:21 +0000369 }
Pierre Ossman80b42092015-11-10 17:17:34 +0100370
371 if (palSize == 0) {
372 // Truecolor data
373 if (useGradient) {
374 if (pf.is888())
375 FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
376 else {
377 switch (pf.bpp) {
378 case 8:
379 assert(false);
380 break;
381 case 16:
382 FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
383 break;
384 case 32:
385 FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
386 break;
387 }
388 }
389 } else {
390 // Copy
391 rdr::U8* ptr = outbuf;
392 const rdr::U8* srcPtr = bufptr;
393 int w = r.width();
394 int h = r.height();
395 if (pf.is888()) {
396 while (h > 0) {
397 pf.bufferFromRGB(ptr, srcPtr, w);
398 ptr += stride * pf.bpp/8;
399 srcPtr += w * 3;
400 h--;
401 }
402 } else {
403 while (h > 0) {
404 memcpy(ptr, srcPtr, w * pf.bpp/8);
405 ptr += stride * pf.bpp/8;
406 srcPtr += w * pf.bpp/8;
407 h--;
408 }
409 }
410 }
411 } else {
412 // Indexed color
413 switch (pf.bpp) {
414 case 8:
415 FilterPalette((const rdr::U8*)palette, palSize,
416 bufptr, (rdr::U8*)outbuf, stride, r);
417 break;
418 case 16:
419 FilterPalette((const rdr::U16*)palette, palSize,
420 bufptr, (rdr::U16*)outbuf, stride, r);
421 break;
422 case 32:
423 FilterPalette((const rdr::U32*)palette, palSize,
424 bufptr, (rdr::U32*)outbuf, stride, r);
425 break;
426 }
427 }
428
429 if (directDecode)
430 pb->commitBufferRW(r);
431 else {
432 pb->imageRect(pf, r, outbuf);
433 delete [] outbuf;
434 }
435
436 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000437}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100438
439rdr::U32 TightDecoder::readCompact(rdr::InStream* is)
440{
441 rdr::U8 b;
442 rdr::U32 result;
443
444 b = is->readU8();
445 result = (int)b & 0x7F;
446 if (b & 0x80) {
447 b = is->readU8();
448 result |= ((int)b & 0x7F) << 7;
449 if (b & 0x80) {
450 b = is->readU8();
451 result |= ((int)b & 0xFF) << 14;
452 }
453 }
454
455 return result;
456}