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