1 /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 * Copyright 2004-2005 Cendio AB.
3 * Copyright 2009-2015 Pierre Ossman for Cendio AB
4 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
5 *
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 */
21
22 #include <assert.h>
23
24 #include <rdr/InStream.h>
25 #include <rdr/MemInStream.h>
26 #include <rdr/OutStream.h>
27
28 #include <rfb/ServerParams.h>
29 #include <rfb/Exception.h>
30 #include <rfb/PixelBuffer.h>
31 #include <rfb/TightConstants.h>
32 #include <rfb/TightDecoder.h>
33
34 using namespace rfb;
35
36 static const int TIGHT_MAX_WIDTH = 2048;
37 static const int TIGHT_MIN_TO_COMPRESS = 12;
38
39 #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
TightDecoder()49 TightDecoder::TightDecoder() : Decoder(DecoderPartiallyOrdered)
50 {
51 }
52
~TightDecoder()53 TightDecoder::~TightDecoder()
54 {
55 }
56
readRect(const Rect & r,rdr::InStream * is,const ServerParams & server,rdr::OutStream * os)57 bool TightDecoder::readRect(const Rect& r, rdr::InStream* is,
58 const ServerParams& server, rdr::OutStream* os)
59 {
60 rdr::U8 comp_ctl;
61
62 if (!is->hasData(1))
63 return false;
64
65 is->setRestorePoint();
66
67 comp_ctl = is->readU8();
68 os->writeU8(comp_ctl);
69
70 comp_ctl >>= 4;
71
72 // "Fill" compression type.
73 if (comp_ctl == tightFill) {
74 if (server.pf().is888()) {
75 if (!is->hasDataOrRestore(3))
76 return false;
77 os->copyBytes(is, 3);
78 } else {
79 if (!is->hasDataOrRestore(server.pf().bpp/8))
80 return false;
81 os->copyBytes(is, server.pf().bpp/8);
82 }
83 is->clearRestorePoint();
84 return true;
85 }
86
87 // "JPEG" compression type.
88 if (comp_ctl == tightJpeg) {
89 rdr::U32 len;
90
91 // FIXME: Might be less than 3 bytes
92 if (!is->hasDataOrRestore(3))
93 return false;
94
95 len = readCompact(is);
96 os->writeOpaque32(len);
97
98 if (!is->hasDataOrRestore(len))
99 return false;
100
101 os->copyBytes(is, len);
102
103 is->clearRestorePoint();
104
105 return true;
106 }
107
108 // Quit on unsupported compression type.
109 if (comp_ctl > tightMaxSubencoding)
110 throw Exception("TightDecoder: bad subencoding value received");
111
112 // "Basic" compression type.
113
114 int palSize = 0;
115
116 if (r.width() > TIGHT_MAX_WIDTH)
117 throw Exception("TightDecoder: too large rectangle (%d pixels)", r.width());
118
119 // Possible palette
120 if ((comp_ctl & tightExplicitFilter) != 0) {
121 rdr::U8 filterId;
122
123 if (!is->hasDataOrRestore(1))
124 return false;
125
126 filterId = is->readU8();
127 os->writeU8(filterId);
128
129 switch (filterId) {
130 case tightFilterPalette:
131 if (!is->hasDataOrRestore(1))
132 return false;
133
134 palSize = is->readU8() + 1;
135 os->writeU8(palSize - 1);
136
137 if (server.pf().is888()) {
138 if (!is->hasDataOrRestore(palSize * 3))
139 return false;
140 os->copyBytes(is, palSize * 3);
141 } else {
142 if (!is->hasDataOrRestore(palSize * server.pf().bpp/8))
143 return false;
144 os->copyBytes(is, palSize * server.pf().bpp/8);
145 }
146 break;
147 case tightFilterGradient:
148 if (server.pf().bpp == 8)
149 throw Exception("TightDecoder: invalid BPP for gradient filter");
150 break;
151 case tightFilterCopy:
152 break;
153 default:
154 throw Exception("TightDecoder: unknown filter code received");
155 }
156 }
157
158 size_t rowSize, dataSize;
159
160 if (palSize != 0) {
161 if (palSize <= 2)
162 rowSize = (r.width() + 7) / 8;
163 else
164 rowSize = r.width();
165 } else if (server.pf().is888()) {
166 rowSize = r.width() * 3;
167 } else {
168 rowSize = r.width() * server.pf().bpp/8;
169 }
170
171 dataSize = r.height() * rowSize;
172
173 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
174 if (!is->hasDataOrRestore(dataSize))
175 return false;
176 os->copyBytes(is, dataSize);
177 } else {
178 rdr::U32 len;
179
180 // FIXME: Might be less than 3 bytes
181 if (!is->hasDataOrRestore(3))
182 return false;
183
184 len = readCompact(is);
185 os->writeOpaque32(len);
186
187 if (!is->hasDataOrRestore(len))
188 return false;
189
190 os->copyBytes(is, len);
191 }
192
193 is->clearRestorePoint();
194
195 return true;
196 }
197
doRectsConflict(const Rect & rectA,const void * bufferA,size_t buflenA,const Rect & rectB,const void * bufferB,size_t buflenB,const ServerParams & server)198 bool TightDecoder::doRectsConflict(const Rect& rectA,
199 const void* bufferA,
200 size_t buflenA,
201 const Rect& rectB,
202 const void* bufferB,
203 size_t buflenB,
204 const ServerParams& server)
205 {
206 rdr::U8 comp_ctl_a, comp_ctl_b;
207
208 assert(buflenA >= 1);
209 assert(buflenB >= 1);
210
211 comp_ctl_a = *(const rdr::U8*)bufferA;
212 comp_ctl_b = *(const rdr::U8*)bufferB;
213
214 // Resets or use of zlib pose the same problem, so merge them
215 if ((comp_ctl_a & 0x80) == 0x00)
216 comp_ctl_a |= 1 << ((comp_ctl_a >> 4) & 0x03);
217 if ((comp_ctl_b & 0x80) == 0x00)
218 comp_ctl_b |= 1 << ((comp_ctl_b >> 4) & 0x03);
219
220 if (((comp_ctl_a & 0x0f) & (comp_ctl_b & 0x0f)) != 0)
221 return true;
222
223 return false;
224 }
225
decodeRect(const Rect & r,const void * buffer,size_t buflen,const ServerParams & server,ModifiablePixelBuffer * pb)226 void TightDecoder::decodeRect(const Rect& r, const void* buffer,
227 size_t buflen, const ServerParams& server,
228 ModifiablePixelBuffer* pb)
229 {
230 const rdr::U8* bufptr;
231 const PixelFormat& pf = server.pf();
232
233 rdr::U8 comp_ctl;
234
235 bufptr = (const rdr::U8*)buffer;
236
237 assert(buflen >= 1);
238
239 comp_ctl = *bufptr;
240 bufptr += 1;
241 buflen -= 1;
242
243 // Reset zlib streams if we are told by the server to do so.
244 for (int i = 0; i < 4; i++) {
245 if (comp_ctl & 1) {
246 zis[i].reset();
247 }
248 comp_ctl >>= 1;
249 }
250
251 // "Fill" compression type.
252 if (comp_ctl == tightFill) {
253 if (pf.is888()) {
254 rdr::U8 pix[4];
255
256 assert(buflen >= 3);
257
258 pf.bufferFromRGB(pix, bufptr, 1);
259 pb->fillRect(pf, r, pix);
260 } else {
261 assert(buflen >= (size_t)pf.bpp/8);
262 pb->fillRect(pf, r, bufptr);
263 }
264 return;
265 }
266
267 // "JPEG" compression type.
268 if (comp_ctl == tightJpeg) {
269 rdr::U32 len;
270
271 int stride;
272 rdr::U8 *buf;
273
274 JpegDecompressor jd;
275
276 assert(buflen >= 4);
277
278 memcpy(&len, bufptr, 4);
279 bufptr += 4;
280 buflen -= 4;
281
282 // We always use direct decoding with JPEG images
283 buf = pb->getBufferRW(r, &stride);
284 jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
285 pb->commitBufferRW(r);
286 return;
287 }
288
289 // Quit on unsupported compression type.
290 assert(comp_ctl <= tightMaxSubencoding);
291
292 // "Basic" compression type.
293
294 int palSize = 0;
295 rdr::U8 palette[256 * 4];
296 bool useGradient = false;
297
298 if ((comp_ctl & tightExplicitFilter) != 0) {
299 rdr::U8 filterId;
300
301 assert(buflen >= 1);
302
303 filterId = *bufptr;
304 bufptr += 1;
305 buflen -= 1;
306
307 switch (filterId) {
308 case tightFilterPalette:
309 assert(buflen >= 1);
310
311 palSize = *bufptr + 1;
312 bufptr += 1;
313 buflen -= 1;
314
315 if (pf.is888()) {
316 size_t len = palSize * 3;
317 rdr::U8Array tightPalette(len);
318
319 assert(buflen >= len);
320
321 memcpy(tightPalette.buf, bufptr, len);
322 bufptr += len;
323 buflen -= len;
324
325 pf.bufferFromRGB(palette, tightPalette.buf, palSize);
326 } else {
327 size_t len;
328
329 len = palSize * pf.bpp/8;
330
331 assert(buflen >= len);
332
333 memcpy(palette, bufptr, len);
334 bufptr += len;
335 buflen -= len;
336 }
337 break;
338 case tightFilterGradient:
339 useGradient = true;
340 break;
341 case tightFilterCopy:
342 break;
343 default:
344 assert(false);
345 }
346 }
347
348 // Determine if the data should be decompressed or just copied.
349 size_t rowSize, dataSize;
350 rdr::U8* netbuf;
351
352 netbuf = NULL;
353
354 if (palSize != 0) {
355 if (palSize <= 2)
356 rowSize = (r.width() + 7) / 8;
357 else
358 rowSize = r.width();
359 } else if (pf.is888()) {
360 rowSize = r.width() * 3;
361 } else {
362 rowSize = r.width() * pf.bpp/8;
363 }
364
365 dataSize = r.height() * rowSize;
366
367 if (dataSize < TIGHT_MIN_TO_COMPRESS)
368 assert(buflen >= dataSize);
369 else {
370 rdr::U32 len;
371 int streamId;
372 rdr::MemInStream* ms;
373
374 assert(buflen >= 4);
375
376 memcpy(&len, bufptr, 4);
377 bufptr += 4;
378 buflen -= 4;
379
380 assert(buflen >= len);
381
382 streamId = comp_ctl & 0x03;
383 ms = new rdr::MemInStream(bufptr, len);
384 zis[streamId].setUnderlying(ms, len);
385
386 // Allocate buffer and decompress the data
387 netbuf = new rdr::U8[dataSize];
388
389 if (!zis[streamId].hasData(dataSize))
390 throw Exception("Tight decode error");
391 zis[streamId].readBytes(netbuf, dataSize);
392
393 zis[streamId].flushUnderlying();
394 zis[streamId].setUnderlying(NULL, 0);
395 delete ms;
396
397 bufptr = netbuf;
398 buflen = dataSize;
399 }
400
401 // Time to decode the actual data
402 bool directDecode;
403
404 rdr::U8* outbuf;
405 int stride;
406
407 if (pb->getPF().equal(pf)) {
408 // Decode directly into the framebuffer (fast path)
409 directDecode = true;
410 } else {
411 // Decode into an intermediate buffer and use pixel translation
412 directDecode = false;
413 }
414
415 if (directDecode)
416 outbuf = pb->getBufferRW(r, &stride);
417 else {
418 outbuf = new rdr::U8[r.area() * (pf.bpp/8)];
419 stride = r.width();
420 }
421
422 if (palSize == 0) {
423 // Truecolor data
424 if (useGradient) {
425 if (pf.is888())
426 FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
427 else {
428 switch (pf.bpp) {
429 case 8:
430 assert(false);
431 break;
432 case 16:
433 FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
434 break;
435 case 32:
436 FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
437 break;
438 }
439 }
440 } else {
441 // Copy
442 rdr::U8* ptr = outbuf;
443 const rdr::U8* srcPtr = bufptr;
444 int w = r.width();
445 int h = r.height();
446 if (pf.is888()) {
447 while (h > 0) {
448 pf.bufferFromRGB(ptr, srcPtr, w);
449 ptr += stride * pf.bpp/8;
450 srcPtr += w * 3;
451 h--;
452 }
453 } else {
454 while (h > 0) {
455 memcpy(ptr, srcPtr, w * pf.bpp/8);
456 ptr += stride * pf.bpp/8;
457 srcPtr += w * pf.bpp/8;
458 h--;
459 }
460 }
461 }
462 } else {
463 // Indexed color
464 switch (pf.bpp) {
465 case 8:
466 FilterPalette((const rdr::U8*)palette, palSize,
467 bufptr, (rdr::U8*)outbuf, stride, r);
468 break;
469 case 16:
470 FilterPalette((const rdr::U16*)palette, palSize,
471 bufptr, (rdr::U16*)outbuf, stride, r);
472 break;
473 case 32:
474 FilterPalette((const rdr::U32*)palette, palSize,
475 bufptr, (rdr::U32*)outbuf, stride, r);
476 break;
477 }
478 }
479
480 if (directDecode)
481 pb->commitBufferRW(r);
482 else {
483 pb->imageRect(pf, r, outbuf);
484 delete [] outbuf;
485 }
486
487 delete [] netbuf;
488 }
489
readCompact(rdr::InStream * is)490 rdr::U32 TightDecoder::readCompact(rdr::InStream* is)
491 {
492 rdr::U8 b;
493 rdr::U32 result;
494
495 b = is->readU8();
496 result = (int)b & 0x7F;
497 if (b & 0x80) {
498 b = is->readU8();
499 result |= ((int)b & 0x7F) << 7;
500 if (b & 0x80) {
501 b = is->readU8();
502 result |= ((int)b & 0xFF) << 14;
503 }
504 }
505
506 return result;
507 }
508