1 /* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
2  * Copyright (C) 2011 D. R. Commander.  All Rights Reserved.
3  * Copyright 2014 Pierre Ossman for Cendio AB
4  *
5  * This is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This software is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this software; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
18  * USA.
19  */
20 #include <assert.h>
21 
22 #include <rdr/OutStream.h>
23 #include <rfb/PixelBuffer.h>
24 #include <rfb/Palette.h>
25 #include <rfb/encodings.h>
26 #include <rfb/SConnection.h>
27 #include <rfb/TightEncoder.h>
28 #include <rfb/TightConstants.h>
29 
30 using namespace rfb;
31 
32 struct TightConf {
33   int idxZlibLevel, monoZlibLevel, rawZlibLevel;
34 };
35 
36 //
37 // Compression level stuff. The following array contains zlib
38 // settings for each of 10 compression levels (0..9).
39 //
40 // NOTE: The parameters used in this encoder are the result of painstaking
41 // research by The VirtualGL Project using RFB session captures from a variety
42 // of both 2D and 3D applications.  See http://www.VirtualGL.org for the full
43 // reports.
44 
45 static const TightConf conf[10] = {
46   { 0, 0, 0 }, // 0
47   { 1, 1, 1 }, // 1
48   { 3, 3, 2 }, // 2
49   { 5, 5, 2 }, // 3
50   { 6, 7, 3 }, // 4
51   { 7, 8, 4 }, // 5
52   { 7, 8, 5 }, // 6
53   { 8, 9, 6 }, // 7
54   { 9, 9, 7 }, // 8
55   { 9, 9, 9 }  // 9
56 };
57 
TightEncoder(SConnection * conn)58 TightEncoder::TightEncoder(SConnection* conn) :
59   Encoder(conn, encodingTight, EncoderPlain, 256)
60 {
61   setCompressLevel(-1);
62 }
63 
~TightEncoder()64 TightEncoder::~TightEncoder()
65 {
66 }
67 
isSupported()68 bool TightEncoder::isSupported()
69 {
70   return conn->client.supportsEncoding(encodingTight);
71 }
72 
setCompressLevel(int level)73 void TightEncoder::setCompressLevel(int level)
74 {
75   if (level < 0 || level > 9)
76     level = 2;
77 
78   idxZlibLevel = conf[level].idxZlibLevel;
79   monoZlibLevel = conf[level].monoZlibLevel;
80   rawZlibLevel = conf[level].rawZlibLevel;
81 }
82 
writeRect(const PixelBuffer * pb,const Palette & palette)83 void TightEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
84 {
85   switch (palette.size()) {
86   case 0:
87     writeFullColourRect(pb, palette);
88     break;
89   case 1:
90     Encoder::writeSolidRect(pb, palette);
91     break;
92   case 2:
93     writeMonoRect(pb, palette);
94     break;
95   default:
96     writeIndexedRect(pb, palette);
97   }
98 }
99 
writeSolidRect(int width,int height,const PixelFormat & pf,const rdr::U8 * colour)100 void TightEncoder::writeSolidRect(int width, int height,
101                                   const PixelFormat& pf,
102                                   const rdr::U8* colour)
103 {
104   rdr::OutStream* os;
105 
106   os = conn->getOutStream();
107 
108   os->writeU8(tightFill << 4);
109   writePixels(colour, pf, 1, os);
110 }
111 
writeMonoRect(const PixelBuffer * pb,const Palette & palette)112 void TightEncoder::writeMonoRect(const PixelBuffer* pb, const Palette& palette)
113 {
114   const rdr::U8* buffer;
115   int stride;
116 
117   buffer = pb->getBuffer(pb->getRect(), &stride);
118 
119   switch (pb->getPF().bpp) {
120   case 32:
121     writeMonoRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride,
122                   pb->getPF(), palette);
123     break;
124   case 16:
125     writeMonoRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride,
126                   pb->getPF(), palette);
127     break;
128   default:
129     writeMonoRect(pb->width(), pb->height(), (rdr::U8*)buffer, stride,
130                   pb->getPF(), palette);
131   }
132 }
133 
writeIndexedRect(const PixelBuffer * pb,const Palette & palette)134 void TightEncoder::writeIndexedRect(const PixelBuffer* pb, const Palette& palette)
135 {
136   const rdr::U8* buffer;
137   int stride;
138 
139   buffer = pb->getBuffer(pb->getRect(), &stride);
140 
141   switch (pb->getPF().bpp) {
142   case 32:
143     writeIndexedRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride,
144                      pb->getPF(), palette);
145     break;
146   case 16:
147     writeIndexedRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride,
148                      pb->getPF(), palette);
149     break;
150   default:
151     // It's more efficient to just do raw pixels
152     writeFullColourRect(pb, palette);
153   }
154 }
155 
writeFullColourRect(const PixelBuffer * pb,const Palette & palette)156 void TightEncoder::writeFullColourRect(const PixelBuffer* pb, const Palette& palette)
157 {
158   const int streamId = 0;
159 
160   rdr::OutStream* os;
161   rdr::OutStream* zos;
162   int length;
163 
164   const rdr::U8* buffer;
165   int stride, h;
166 
167   os = conn->getOutStream();
168 
169   os->writeU8(streamId << 4);
170 
171   // Set up compression
172   if ((pb->getPF().bpp != 32) || !pb->getPF().is888())
173     length = pb->getRect().area() * pb->getPF().bpp/8;
174   else
175     length = pb->getRect().area() * 3;
176 
177   zos = getZlibOutStream(streamId, rawZlibLevel, length);
178 
179   // And then just dump all the raw pixels
180   buffer = pb->getBuffer(pb->getRect(), &stride);
181   h = pb->height();
182 
183   while (h--) {
184     writePixels(buffer, pb->getPF(), pb->width(), zos);
185     buffer += stride * pb->getPF().bpp/8;
186   }
187 
188   // Finish the zlib stream
189   flushZlibOutStream(zos);
190 }
191 
writePixels(const rdr::U8 * buffer,const PixelFormat & pf,unsigned int count,rdr::OutStream * os)192 void TightEncoder::writePixels(const rdr::U8* buffer, const PixelFormat& pf,
193                                unsigned int count, rdr::OutStream* os)
194 {
195   rdr::U8 rgb[2048];
196 
197   if ((pf.bpp != 32) || !pf.is888()) {
198     os->writeBytes(buffer, count * pf.bpp/8);
199     return;
200   }
201 
202   while (count) {
203     unsigned int iter_count;
204 
205     iter_count = sizeof(rgb)/3;
206     if (iter_count > count)
207       iter_count = count;
208 
209     pf.rgbFromBuffer(rgb, buffer, iter_count);
210     os->writeBytes(rgb, iter_count * 3);
211 
212     buffer += iter_count * pf.bpp/8;
213     count -= iter_count;
214   }
215 }
216 
writeCompact(rdr::OutStream * os,rdr::U32 value)217 void TightEncoder::writeCompact(rdr::OutStream* os, rdr::U32 value)
218 {
219   rdr::U8 b;
220   b = value & 0x7F;
221   if (value <= 0x7F) {
222     os->writeU8(b);
223   } else {
224     os->writeU8(b | 0x80);
225     b = value >> 7 & 0x7F;
226     if (value <= 0x3FFF) {
227       os->writeU8(b);
228     } else {
229       os->writeU8(b | 0x80);
230       os->writeU8(value >> 14 & 0xFF);
231     }
232   }
233 }
234 
getZlibOutStream(int streamId,int level,size_t length)235 rdr::OutStream* TightEncoder::getZlibOutStream(int streamId, int level, size_t length)
236 {
237   // Minimum amount of data to be compressed. This value should not be
238   // changed, doing so will break compatibility with existing clients.
239   if (length < 12)
240     return conn->getOutStream();
241 
242   assert(streamId >= 0);
243   assert(streamId < 4);
244 
245   zlibStreams[streamId].setUnderlying(&memStream);
246   zlibStreams[streamId].setCompressionLevel(level);
247   zlibStreams[streamId].cork(true);
248 
249   return &zlibStreams[streamId];
250 }
251 
flushZlibOutStream(rdr::OutStream * os_)252 void TightEncoder::flushZlibOutStream(rdr::OutStream* os_)
253 {
254   rdr::OutStream* os;
255   rdr::ZlibOutStream* zos;
256 
257   zos = dynamic_cast<rdr::ZlibOutStream*>(os_);
258   if (zos == NULL)
259     return;
260 
261   zos->cork(false);
262   zos->flush();
263   zos->setUnderlying(NULL);
264 
265   os = conn->getOutStream();
266 
267   writeCompact(os, memStream.length());
268   os->writeBytes(memStream.data(), memStream.length());
269   memStream.clear();
270 }
271 
272 //
273 // Including BPP-dependent implementation of the encoder.
274 //
275 
276 #define BPP 8
277 #include <rfb/TightEncoderBPP.cxx>
278 #undef BPP
279 #define BPP 16
280 #include <rfb/TightEncoderBPP.cxx>
281 #undef BPP
282 #define BPP 32
283 #include <rfb/TightEncoderBPP.cxx>
284 #undef BPP
285