blob: 86bb00698655fc3b68a4dcdecabf670bb2b11574 [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
176 // We have a shared JpegDecompressor, so one at a time
177 if (((comp_ctl_a >> 4) == tightJpeg) &&
178 ((comp_ctl_b >> 4) == tightJpeg))
179 return true;
180
181 return false;
182}
183
Pierre Ossman80b42092015-11-10 17:17:34 +0100184void TightDecoder::decodeRect(const Rect& r, const void* buffer,
185 size_t buflen, const ConnParams& cp,
186 ModifiablePixelBuffer* pb)
187{
188 const rdr::U8* bufptr;
189 const PixelFormat& pf = cp.pf();
190
191 rdr::U8 comp_ctl;
192
193 bufptr = (const rdr::U8*)buffer;
194
195 assert(buflen >= 1);
196
197 comp_ctl = *bufptr;
198 bufptr += 1;
199 buflen -= 1;
200
Pierre Ossman6f318e42015-11-11 13:11:09 +0100201 // Reset zlib streams if we are told by the server to do so.
Pierre Ossman80b42092015-11-10 17:17:34 +0100202 for (int i = 0; i < 4; i++) {
203 if (comp_ctl & 1) {
204 zis[i].reset();
205 }
206 comp_ctl >>= 1;
207 }
208
209 // "Fill" compression type.
210 if (comp_ctl == tightFill) {
211 if (pf.is888()) {
212 rdr::U8 pix[4];
213
214 assert(buflen >= 3);
215
216 pf.bufferFromRGB(pix, bufptr, 1);
217 pb->fillRect(pf, r, pix);
218 } else {
219 assert(buflen >= (size_t)pf.bpp/8);
220 pb->fillRect(pf, r, bufptr);
221 }
222 return;
223 }
224
225 // "JPEG" compression type.
226 if (comp_ctl == tightJpeg) {
227 rdr::U32 len;
228
229 int stride;
230 rdr::U8 *buf;
231
232 assert(buflen >= 4);
233
234 memcpy(&len, bufptr, 4);
235 bufptr += 4;
236 buflen -= 4;
237
238 // We always use direct decoding with JPEG images
239 buf = pb->getBufferRW(r, &stride);
240 jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
241 pb->commitBufferRW(r);
242 return;
243 }
244
245 // Quit on unsupported compression type.
246 assert(comp_ctl <= tightMaxSubencoding);
247
248 // "Basic" compression type.
249
250 int palSize = 0;
251 rdr::U8 palette[256 * 4];
252 bool useGradient = false;
253
254 if ((comp_ctl & tightExplicitFilter) != 0) {
255 rdr::U8 filterId;
256
257 assert(buflen >= 1);
258
259 filterId = *bufptr;
260 bufptr += 1;
261 buflen -= 1;
262
263 switch (filterId) {
264 case tightFilterPalette:
265 assert(buflen >= 1);
266
267 palSize = *bufptr + 1;
268 bufptr += 1;
269 buflen -= 1;
270
271 if (pf.is888()) {
272 rdr::U8 tightPalette[palSize * 3];
273
274 assert(buflen >= sizeof(tightPalette));
275
276 memcpy(tightPalette, bufptr, sizeof(tightPalette));
277 bufptr += sizeof(tightPalette);
278 buflen -= sizeof(tightPalette);
279
280 pf.bufferFromRGB(palette, tightPalette, palSize);
281 } else {
282 size_t len;
283
284 len = palSize * pf.bpp/8;
285
286 assert(buflen >= len);
287
288 memcpy(palette, bufptr, len);
289 bufptr += len;
290 buflen -= len;
291 }
292 break;
293 case tightFilterGradient:
294 useGradient = true;
295 break;
296 case tightFilterCopy:
297 break;
298 default:
299 assert(false);
300 }
301 }
302
303 // Determine if the data should be decompressed or just copied.
304 size_t rowSize, dataSize;
305 rdr::U8* netbuf;
306
307 netbuf = NULL;
308
309 if (palSize != 0) {
310 if (palSize <= 2)
311 rowSize = (r.width() + 7) / 8;
312 else
313 rowSize = r.width();
314 } else if (pf.is888()) {
315 rowSize = r.width() * 3;
316 } else {
317 rowSize = r.width() * pf.bpp/8;
318 }
319
320 dataSize = r.height() * rowSize;
321
322 if (dataSize < TIGHT_MIN_TO_COMPRESS)
323 assert(buflen >= dataSize);
324 else {
325 rdr::U32 len;
326 int streamId;
327 rdr::MemInStream* ms;
328
329 assert(buflen >= 4);
330
331 memcpy(&len, bufptr, 4);
332 bufptr += 4;
333 buflen -= 4;
334
335 assert(buflen >= len);
336
337 streamId = comp_ctl & 0x03;
338 ms = new rdr::MemInStream(bufptr, len);
339 zis[streamId].setUnderlying(ms, len);
340
341 // Allocate buffer and decompress the data
342 netbuf = new rdr::U8[dataSize];
343
344 zis[streamId].readBytes(netbuf, dataSize);
Pierre Ossman80b42092015-11-10 17:17:34 +0100345
Pierre Ossman6f318e42015-11-11 13:11:09 +0100346 zis[streamId].removeUnderlying();
Pierre Ossman80b42092015-11-10 17:17:34 +0100347 delete ms;
348
349 bufptr = netbuf;
350 buflen = dataSize;
351 }
352
353 // Time to decode the actual data
354 bool directDecode;
355
356 rdr::U8* outbuf;
357 int stride;
358
359 if (pb->getPF().equal(pf)) {
360 // Decode directly into the framebuffer (fast path)
DRC33c15e32011-11-03 18:49:21 +0000361 directDecode = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000362 } else {
Pierre Ossman80b42092015-11-10 17:17:34 +0100363 // Decode into an intermediate buffer and use pixel translation
DRC33c15e32011-11-03 18:49:21 +0000364 directDecode = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000365 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366
Pierre Ossman80b42092015-11-10 17:17:34 +0100367 if (directDecode)
368 outbuf = pb->getBufferRW(r, &stride);
369 else {
370 outbuf = new rdr::U8[r.area() * pf.bpp/8];
371 stride = r.width();
DRC33c15e32011-11-03 18:49:21 +0000372 }
Pierre Ossman80b42092015-11-10 17:17:34 +0100373
374 if (palSize == 0) {
375 // Truecolor data
376 if (useGradient) {
377 if (pf.is888())
378 FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
379 else {
380 switch (pf.bpp) {
381 case 8:
382 assert(false);
383 break;
384 case 16:
385 FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
386 break;
387 case 32:
388 FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
389 break;
390 }
391 }
392 } else {
393 // Copy
394 rdr::U8* ptr = outbuf;
395 const rdr::U8* srcPtr = bufptr;
396 int w = r.width();
397 int h = r.height();
398 if (pf.is888()) {
399 while (h > 0) {
400 pf.bufferFromRGB(ptr, srcPtr, w);
401 ptr += stride * pf.bpp/8;
402 srcPtr += w * 3;
403 h--;
404 }
405 } else {
406 while (h > 0) {
407 memcpy(ptr, srcPtr, w * pf.bpp/8);
408 ptr += stride * pf.bpp/8;
409 srcPtr += w * pf.bpp/8;
410 h--;
411 }
412 }
413 }
414 } else {
415 // Indexed color
416 switch (pf.bpp) {
417 case 8:
418 FilterPalette((const rdr::U8*)palette, palSize,
419 bufptr, (rdr::U8*)outbuf, stride, r);
420 break;
421 case 16:
422 FilterPalette((const rdr::U16*)palette, palSize,
423 bufptr, (rdr::U16*)outbuf, stride, r);
424 break;
425 case 32:
426 FilterPalette((const rdr::U32*)palette, palSize,
427 bufptr, (rdr::U32*)outbuf, stride, r);
428 break;
429 }
430 }
431
432 if (directDecode)
433 pb->commitBufferRW(r);
434 else {
435 pb->imageRect(pf, r, outbuf);
436 delete [] outbuf;
437 }
438
439 delete [] netbuf;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000440}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100441
442rdr::U32 TightDecoder::readCompact(rdr::InStream* is)
443{
444 rdr::U8 b;
445 rdr::U32 result;
446
447 b = is->readU8();
448 result = (int)b & 0x7F;
449 if (b & 0x80) {
450 b = is->readU8();
451 result |= ((int)b & 0x7F) << 7;
452 if (b & 0x80) {
453 b = is->readU8();
454 result |= ((int)b & 0xFF) << 14;
455 }
456 }
457
458 return result;
459}