blob: fe2470b9f618998d9125bb8812f02e64ade6d01a [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>
26#include <rfb/ConnParams.h>
Pierre Ossman668468b2014-01-31 12:37:32 +010027#include <rfb/SConnection.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/TightEncoder.h>
Pierre Ossmanc0397262014-03-14 15:59:46 +010029#include <rfb/TightConstants.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000030
31using namespace rfb;
32
Pierre Ossmanc0397262014-03-14 15:59:46 +010033struct TightConf {
34 int idxZlibLevel, monoZlibLevel, rawZlibLevel;
35};
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036
37//
Pierre Ossmanc0397262014-03-14 15:59:46 +010038// Compression level stuff. The following array contains zlib
39// settings for each of 10 compression levels (0..9).
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040//
DRCcd2c5d42011-08-11 11:18:34 +000041// NOTE: The parameters used in this encoder are the result of painstaking
42// research by The VirtualGL Project using RFB session captures from a variety
43// of both 2D and 3D applications. See http://www.VirtualGL.org for the full
44// reports.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045
Pierre Ossmanc0397262014-03-14 15:59:46 +010046static const TightConf conf[10] = {
47 { 0, 0, 0 }, // 0
48 { 1, 1, 1 }, // 1
49 { 3, 3, 2 }, // 2
50 { 5, 5, 2 }, // 3
51 { 6, 7, 3 }, // 4
52 { 7, 8, 4 }, // 5
53 { 7, 8, 5 }, // 6
54 { 8, 9, 6 }, // 7
55 { 9, 9, 7 }, // 8
56 { 9, 9, 9 } // 9
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057};
Pierre Ossman701ad682011-11-20 15:39:17 +000058
Pierre Ossmanc0397262014-03-14 15:59:46 +010059TightEncoder::TightEncoder(SConnection* conn) :
60 Encoder(conn, encodingTight, EncoderPlain, 256)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061{
Pierre Ossmanc0397262014-03-14 15:59:46 +010062 setCompressLevel(-1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063}
64
65TightEncoder::~TightEncoder()
66{
67}
68
Pierre Ossmanc0397262014-03-14 15:59:46 +010069bool TightEncoder::isSupported()
70{
71 return conn->cp.supportsEncoding(encodingTight);
72}
73
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000074void TightEncoder::setCompressLevel(int level)
75{
Pierre Ossmanc0397262014-03-14 15:59:46 +010076 if (level < 0 || level > 9)
77 level = 2;
78
79 idxZlibLevel = conf[level].idxZlibLevel;
80 monoZlibLevel = conf[level].idxZlibLevel;
81 rawZlibLevel = conf[level].rawZlibLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082}
83
Pierre Ossmanc0397262014-03-14 15:59:46 +010084void TightEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000085{
Pierre Ossmanc0397262014-03-14 15:59:46 +010086 switch (palette.size()) {
87 case 0:
88 writeFullColourRect(pb, palette);
89 break;
90 case 1:
91 Encoder::writeSolidRect(pb, palette);
92 break;
93 case 2:
94 writeMonoRect(pb, palette);
95 break;
DRCcd2c5d42011-08-11 11:18:34 +000096 default:
Pierre Ossmanc0397262014-03-14 15:59:46 +010097 writeIndexedRect(pb, palette);
DRCcd2c5d42011-08-11 11:18:34 +000098 }
99}
100
Pierre Ossmanc0397262014-03-14 15:59:46 +0100101void TightEncoder::writeSolidRect(int width, int height,
102 const PixelFormat& pf,
103 const rdr::U8* colour)
DRCcd2c5d42011-08-11 11:18:34 +0000104{
Pierre Ossmanc0397262014-03-14 15:59:46 +0100105 rdr::OutStream* os;
DRCcd2c5d42011-08-11 11:18:34 +0000106
Pierre Ossmanc0397262014-03-14 15:59:46 +0100107 os = conn->getOutStream();
DRCcd2c5d42011-08-11 11:18:34 +0000108
Pierre Ossmanc0397262014-03-14 15:59:46 +0100109 os->writeU8(tightFill << 4);
110 writePixels(colour, pf, 1, os);
DRCcd2c5d42011-08-11 11:18:34 +0000111}
112
Pierre Ossmanc0397262014-03-14 15:59:46 +0100113void TightEncoder::writeMonoRect(const PixelBuffer* pb, const Palette& palette)
DRCcd2c5d42011-08-11 11:18:34 +0000114{
Pierre Ossmanc0397262014-03-14 15:59:46 +0100115 const rdr::U8* buffer;
116 int stride;
DRCcd2c5d42011-08-11 11:18:34 +0000117
Pierre Ossmanc0397262014-03-14 15:59:46 +0100118 buffer = pb->getBuffer(pb->getRect(), &stride);
DRCcd2c5d42011-08-11 11:18:34 +0000119
Pierre Ossmanc0397262014-03-14 15:59:46 +0100120 switch (pb->getPF().bpp) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121 case 32:
Pierre Ossmanc0397262014-03-14 15:59:46 +0100122 writeMonoRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride,
123 pb->getPF(), palette);
124 break;
125 case 16:
126 writeMonoRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride,
127 pb->getPF(), palette);
128 break;
129 default:
130 writeMonoRect(pb->width(), pb->height(), (rdr::U8*)buffer, stride,
131 pb->getPF(), palette);
132 }
133}
134
135void TightEncoder::writeIndexedRect(const PixelBuffer* pb, const Palette& palette)
136{
137 const rdr::U8* buffer;
138 int stride;
139
140 buffer = pb->getBuffer(pb->getRect(), &stride);
141
142 switch (pb->getPF().bpp) {
143 case 32:
144 writeIndexedRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride,
145 pb->getPF(), palette);
146 break;
147 case 16:
148 writeIndexedRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride,
149 pb->getPF(), palette);
150 break;
151 default:
152 // It's more efficient to just do raw pixels
153 writeFullColourRect(pb, palette);
154 }
155}
156
157void TightEncoder::writeFullColourRect(const PixelBuffer* pb, const Palette& palette)
158{
159 const int streamId = 0;
160
161 rdr::OutStream* os;
162 rdr::OutStream* zos;
163 int length;
164
165 const rdr::U8* buffer;
Pierre Ossmaneb955322015-03-03 16:10:53 +0100166 int stride, h;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100167
168 os = conn->getOutStream();
169
170 os->writeU8(streamId << 4);
171
172 // Set up compression
173 if ((pb->getPF().bpp != 32) || !pb->getPF().is888())
174 length = pb->getRect().area() * pb->getPF().bpp/8;
175 else
176 length = pb->getRect().area() * 3;
177
178 zos = getZlibOutStream(streamId, rawZlibLevel, length);
179
180 // And then just dump all the raw pixels
181 buffer = pb->getBuffer(pb->getRect(), &stride);
182 h = pb->height();
183
184 while (h--) {
185 writePixels(buffer, pb->getPF(), pb->width(), zos);
186 buffer += stride * pb->getPF().bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000187 }
188
Pierre Ossmanc0397262014-03-14 15:59:46 +0100189 // Finish the zlib stream
190 flushZlibOutStream(zos);
191}
192
193void TightEncoder::writePixels(const rdr::U8* buffer, const PixelFormat& pf,
194 unsigned int count, rdr::OutStream* os)
195{
196 rdr::U8 rgb[2048];
197
198 if ((pf.bpp != 32) || !pf.is888()) {
199 os->writeBytes(buffer, count * pf.bpp/8);
200 return;
201 }
202
203 while (count) {
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100204 unsigned int iter_count;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100205
206 iter_count = sizeof(rgb)/3;
207 if (iter_count > count)
208 iter_count = count;
209
210 pf.rgbFromBuffer(rgb, buffer, iter_count);
211 os->writeBytes(rgb, iter_count * 3);
212
213 buffer += iter_count * pf.bpp/8;
214 count -= iter_count;
215 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000216}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100217
218void TightEncoder::writeCompact(rdr::OutStream* os, rdr::U32 value)
219{
220 rdr::U8 b;
221 b = value & 0x7F;
222 if (value <= 0x7F) {
223 os->writeU8(b);
224 } else {
225 os->writeU8(b | 0x80);
226 b = value >> 7 & 0x7F;
227 if (value <= 0x3FFF) {
228 os->writeU8(b);
229 } else {
230 os->writeU8(b | 0x80);
231 os->writeU8(value >> 14 & 0xFF);
232 }
233 }
234}
Pierre Ossmanc0397262014-03-14 15:59:46 +0100235
236rdr::OutStream* TightEncoder::getZlibOutStream(int streamId, int level, size_t length)
237{
238 // Minimum amount of data to be compressed. This value should not be
239 // changed, doing so will break compatibility with existing clients.
240 if (length < 12)
241 return conn->getOutStream();
242
243 assert(streamId >= 0);
244 assert(streamId < 4);
245
246 zlibStreams[streamId].setUnderlying(&memStream);
247 zlibStreams[streamId].setCompressionLevel(level);
248
249 return &zlibStreams[streamId];
250}
251
252void 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->flush();
262 zos->setUnderlying(NULL);
263
264 os = conn->getOutStream();
265
266 writeCompact(os, memStream.length());
267 os->writeBytes(memStream.data(), memStream.length());
268 memStream.clear();
269}
270
271//
272// Including BPP-dependent implementation of the encoder.
273//
274
275#define BPP 8
276#include <rfb/TightEncoderBPP.cxx>
277#undef BPP
278#define BPP 16
279#include <rfb/TightEncoderBPP.cxx>
280#undef BPP
281#define BPP 32
282#include <rfb/TightEncoderBPP.cxx>
283#undef BPP