blob: 548c1906c98e0b3d060b7094694a05db3cf9e5c4 [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 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 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
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,
157 const ConnParams& cp)
158{
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,
180 size_t buflen, const ConnParams& cp,
181 ModifiablePixelBuffer* pb)
182{
183 const rdr::U8* bufptr;
184 const PixelFormat& pf = cp.pf();
185
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()) {
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100269 size_t len = palSize * 3;
270 rdr::U8Array tightPalette(len);
Pierre Ossman80b42092015-11-10 17:17:34 +0100271
Pierre Ossman3648c702018-11-23 17:35:04 +0100272 assert(buflen >= len);
Pierre Ossman80b42092015-11-10 17:17:34 +0100273
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100274 memcpy(tightPalette.buf, bufptr, len);
275 bufptr += len;
276 buflen -= len;
Pierre Ossman80b42092015-11-10 17:17:34 +0100277
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100278 pf.bufferFromRGB(palette, tightPalette.buf, palSize);
Pierre Ossman80b42092015-11-10 17:17:34 +0100279 } else {
280 size_t len;
281
282 len = palSize * pf.bpp/8;
283
284 assert(buflen >= len);
285
286 memcpy(palette, bufptr, len);
287 bufptr += len;
288 buflen -= len;
289 }
290 break;
291 case tightFilterGradient:
292 useGradient = true;
293 break;
294 case tightFilterCopy:
295 break;
296 default:
297 assert(false);
298 }
299 }
300
301 // Determine if the data should be decompressed or just copied.
302 size_t rowSize, dataSize;
303 rdr::U8* netbuf;
304
305 netbuf = NULL;
306
307 if (palSize != 0) {
308 if (palSize <= 2)
309 rowSize = (r.width() + 7) / 8;
310 else
311 rowSize = r.width();
312 } else if (pf.is888()) {
313 rowSize = r.width() * 3;
314 } else {
315 rowSize = r.width() * pf.bpp/8;
316 }
317
318 dataSize = r.height() * rowSize;
319
320 if (dataSize < TIGHT_MIN_TO_COMPRESS)
321 assert(buflen >= dataSize);
322 else {
323 rdr::U32 len;
324 int streamId;
325 rdr::MemInStream* ms;
326
327 assert(buflen >= 4);
328
329 memcpy(&len, bufptr, 4);
330 bufptr += 4;
331 buflen -= 4;
332
333 assert(buflen >= len);
334
335 streamId = comp_ctl & 0x03;
336 ms = new rdr::MemInStream(bufptr, len);
337 zis[streamId].setUnderlying(ms, len);
338
339 // Allocate buffer and decompress the data
340 netbuf = new rdr::U8[dataSize];
341
342 zis[streamId].readBytes(netbuf, dataSize);
Pierre Ossman80b42092015-11-10 17:17:34 +0100343
Pierre Ossman6f318e42015-11-11 13:11:09 +0100344 zis[streamId].removeUnderlying();
Pierre Ossman80b42092015-11-10 17:17:34 +0100345 delete ms;
346
347 bufptr = netbuf;
348 buflen = dataSize;
349 }
350
351 // Time to decode the actual data
352 bool directDecode;
353
354 rdr::U8* outbuf;
355 int stride;
356
357 if (pb->getPF().equal(pf)) {
358 // Decode directly into the framebuffer (fast path)
DRC33c15e32011-11-03 18:49:21 +0000359 directDecode = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000360 } else {
Pierre Ossman80b42092015-11-10 17:17:34 +0100361 // Decode into an intermediate buffer and use pixel translation
DRC33c15e32011-11-03 18:49:21 +0000362 directDecode = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000363 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000364
Pierre Ossman80b42092015-11-10 17:17:34 +0100365 if (directDecode)
366 outbuf = pb->getBufferRW(r, &stride);
367 else {
Pierre Ossmanf81148c2018-07-25 20:44:32 +0200368 outbuf = new rdr::U8[r.area() * (pf.bpp/8)];
Pierre Ossman80b42092015-11-10 17:17:34 +0100369 stride = r.width();
DRC33c15e32011-11-03 18:49:21 +0000370 }
Pierre Ossman80b42092015-11-10 17:17:34 +0100371
372 if (palSize == 0) {
373 // Truecolor data
374 if (useGradient) {
375 if (pf.is888())
376 FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
377 else {
378 switch (pf.bpp) {
379 case 8:
380 assert(false);
381 break;
382 case 16:
383 FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
384 break;
385 case 32:
386 FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
387 break;
388 }
389 }
390 } else {
391 // Copy
392 rdr::U8* ptr = outbuf;
393 const rdr::U8* srcPtr = bufptr;
394 int w = r.width();
395 int h = r.height();
396 if (pf.is888()) {
397 while (h > 0) {
398 pf.bufferFromRGB(ptr, srcPtr, w);
399 ptr += stride * pf.bpp/8;
400 srcPtr += w * 3;
401 h--;
402 }
403 } else {
404 while (h > 0) {
405 memcpy(ptr, srcPtr, w * pf.bpp/8);
406 ptr += stride * pf.bpp/8;
407 srcPtr += w * pf.bpp/8;
408 h--;
409 }
410 }
411 }
412 } else {
413 // Indexed color
414 switch (pf.bpp) {
415 case 8:
416 FilterPalette((const rdr::U8*)palette, palSize,
417 bufptr, (rdr::U8*)outbuf, stride, r);
418 break;
419 case 16:
420 FilterPalette((const rdr::U16*)palette, palSize,
421 bufptr, (rdr::U16*)outbuf, stride, r);
422 break;
423 case 32:
424 FilterPalette((const rdr::U32*)palette, palSize,
425 bufptr, (rdr::U32*)outbuf, stride, r);
426 break;
427 }
428 }
429
430 if (directDecode)
431 pb->commitBufferRW(r);
432 else {
433 pb->imageRect(pf, r, outbuf);
434 delete [] outbuf;
435 }
436
437 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000438}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100439
440rdr::U32 TightDecoder::readCompact(rdr::InStream* is)
441{
442 rdr::U8 b;
443 rdr::U32 result;
444
445 b = is->readU8();
446 result = (int)b & 0x7F;
447 if (b & 0x80) {
448 b = is->readU8();
449 result |= ((int)b & 0x7F) << 7;
450 if (b & 0x80) {
451 b = is->readU8();
452 result |= ((int)b & 0xFF) << 14;
453 }
454 }
455
456 return result;
457}