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