blob: 1b0792c4dba5d454716481e99777ee70543c35a3 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
DRCcd2c5d42011-08-11 11:18:34 +00002 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossmanc0397262014-03-14 15:59:46 +01003 * Copyright 2014 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
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 */
Pierre Ossmanc0397262014-03-14 15:59:46 +010020#include <assert.h>
21
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000022#include <rdr/OutStream.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010023#include <rfb/PixelBuffer.h>
Pierre Ossmanc0397262014-03-14 15:59:46 +010024#include <rfb/Palette.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/encodings.h>
Pierre Ossman668468b2014-01-31 12:37:32 +010026#include <rfb/SConnection.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/TightEncoder.h>
Pierre Ossmanc0397262014-03-14 15:59:46 +010028#include <rfb/TightConstants.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000029
30using namespace rfb;
31
Pierre Ossmanc0397262014-03-14 15:59:46 +010032struct TightConf {
33 int idxZlibLevel, monoZlibLevel, rawZlibLevel;
34};
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035
36//
Pierre Ossmanc0397262014-03-14 15:59:46 +010037// Compression level stuff. The following array contains zlib
38// settings for each of 10 compression levels (0..9).
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039//
DRCcd2c5d42011-08-11 11:18:34 +000040// 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.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044
Pierre Ossmanc0397262014-03-14 15:59:46 +010045static 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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056};
Pierre Ossman701ad682011-11-20 15:39:17 +000057
Pierre Ossmanc0397262014-03-14 15:59:46 +010058TightEncoder::TightEncoder(SConnection* conn) :
59 Encoder(conn, encodingTight, EncoderPlain, 256)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060{
Pierre Ossmanc0397262014-03-14 15:59:46 +010061 setCompressLevel(-1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062}
63
64TightEncoder::~TightEncoder()
65{
66}
67
Pierre Ossmanc0397262014-03-14 15:59:46 +010068bool TightEncoder::isSupported()
69{
Pierre Ossman0d3ce872018-06-18 15:59:00 +020070 return conn->client.supportsEncoding(encodingTight);
Pierre Ossmanc0397262014-03-14 15:59:46 +010071}
72
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000073void TightEncoder::setCompressLevel(int level)
74{
Pierre Ossmanc0397262014-03-14 15:59:46 +010075 if (level < 0 || level > 9)
76 level = 2;
77
78 idxZlibLevel = conf[level].idxZlibLevel;
Michal Srb2a2a7bb2016-05-06 14:48:36 +030079 monoZlibLevel = conf[level].monoZlibLevel;
Pierre Ossmanc0397262014-03-14 15:59:46 +010080 rawZlibLevel = conf[level].rawZlibLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081}
82
Pierre Ossmanc0397262014-03-14 15:59:46 +010083void TightEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084{
Pierre Ossmanc0397262014-03-14 15:59:46 +010085 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;
DRCcd2c5d42011-08-11 11:18:34 +000095 default:
Pierre Ossmanc0397262014-03-14 15:59:46 +010096 writeIndexedRect(pb, palette);
DRCcd2c5d42011-08-11 11:18:34 +000097 }
98}
99
Pierre Ossmanc0397262014-03-14 15:59:46 +0100100void TightEncoder::writeSolidRect(int width, int height,
101 const PixelFormat& pf,
102 const rdr::U8* colour)
DRCcd2c5d42011-08-11 11:18:34 +0000103{
Pierre Ossmanc0397262014-03-14 15:59:46 +0100104 rdr::OutStream* os;
DRCcd2c5d42011-08-11 11:18:34 +0000105
Pierre Ossmanc0397262014-03-14 15:59:46 +0100106 os = conn->getOutStream();
DRCcd2c5d42011-08-11 11:18:34 +0000107
Pierre Ossmanc0397262014-03-14 15:59:46 +0100108 os->writeU8(tightFill << 4);
109 writePixels(colour, pf, 1, os);
DRCcd2c5d42011-08-11 11:18:34 +0000110}
111
Pierre Ossmanc0397262014-03-14 15:59:46 +0100112void TightEncoder::writeMonoRect(const PixelBuffer* pb, const Palette& palette)
DRCcd2c5d42011-08-11 11:18:34 +0000113{
Pierre Ossmanc0397262014-03-14 15:59:46 +0100114 const rdr::U8* buffer;
115 int stride;
DRCcd2c5d42011-08-11 11:18:34 +0000116
Pierre Ossmanc0397262014-03-14 15:59:46 +0100117 buffer = pb->getBuffer(pb->getRect(), &stride);
DRCcd2c5d42011-08-11 11:18:34 +0000118
Pierre Ossmanc0397262014-03-14 15:59:46 +0100119 switch (pb->getPF().bpp) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000120 case 32:
Pierre Ossmanc0397262014-03-14 15:59:46 +0100121 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
134void 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
156void 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;
Pierre Ossmaneb955322015-03-03 16:10:53 +0100165 int stride, h;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100166
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;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 }
187
Pierre Ossmanc0397262014-03-14 15:59:46 +0100188 // Finish the zlib stream
189 flushZlibOutStream(zos);
190}
191
192void 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) {
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100203 unsigned int iter_count;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100204
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 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100216
217void 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}
Pierre Ossmanc0397262014-03-14 15:59:46 +0100234
235rdr::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
248 return &zlibStreams[streamId];
249}
250
251void TightEncoder::flushZlibOutStream(rdr::OutStream* os_)
252{
253 rdr::OutStream* os;
254 rdr::ZlibOutStream* zos;
255
256 zos = dynamic_cast<rdr::ZlibOutStream*>(os_);
257 if (zos == NULL)
258 return;
259
260 zos->flush();
261 zos->setUnderlying(NULL);
262
263 os = conn->getOutStream();
264
265 writeCompact(os, memStream.length());
266 os->writeBytes(memStream.data(), memStream.length());
267 memStream.clear();
268}
269
270//
271// Including BPP-dependent implementation of the encoder.
272//
273
274#define BPP 8
275#include <rfb/TightEncoderBPP.cxx>
276#undef BPP
277#define BPP 16
278#include <rfb/TightEncoderBPP.cxx>
279#undef BPP
280#define BPP 32
281#include <rfb/TightEncoderBPP.cxx>
282#undef BPP