blob: f9684b991f5acf491fb4297e6d009fdf8a6f0143 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18#include <rdr/OutStream.h>
19#include <rfb/ImageGetter.h>
20#include <rfb/encodings.h>
21#include <rfb/ConnParams.h>
22#include <rfb/SMsgWriter.h>
23#include <rfb/TightEncoder.h>
24
25using namespace rfb;
26
27// Minimum amount of data to be compressed. This value should not be
28// changed, doing so will break compatibility with existing clients.
29#define TIGHT_MIN_TO_COMPRESS 12
30
31// Adjustable parameters.
32// FIXME: Get rid of #defines
33#define TIGHT_JPEG_MIN_RECT_SIZE 1024
34#define TIGHT_DETECT_MIN_WIDTH 8
35#define TIGHT_DETECT_MIN_HEIGHT 8
36
37//
38// Compression level stuff. The following array contains various
39// encoder parameters for each of 10 compression levels (0..9).
40// Last three parameters correspond to JPEG quality levels (0..9).
41//
42// NOTE: s_conf[9].maxRectSize should be >= s_conf[i].maxRectSize,
43// where i in [0..8]. RequiredBuffSize() method depends on this.
44// FIXME: Is this comment obsolete?
45//
46
DRC773cf3c2009-03-12 19:26:44 +000047// NOTE: The JPEG quality and subsampling levels below were obtained
48// experimentally by the VirtualGL Project. They represent the approximate
49// average compression ratios listed below, as measured across the set of
50// every 10th frame in the SPECviewperf 9 benchmark suite.
51//
52// 9 = JPEG quality 100, no subsampling (ratio ~= 10:1)
53// [this should be lossless, except for round-off error]
54// 8 = JPEG quality 92, no subsampling (ratio ~= 20:1)
55// [this should be perceptually lossless, based on current research]
56// 7 = JPEG quality 86, no subsampling (ratio ~= 25:1)
57// 6 = JPEG quality 79, no subsampling (ratio ~= 30:1)
58// 5 = JPEG quality 77, 4:2:2 subsampling (ratio ~= 40:1)
59// 4 = JPEG quality 62, 4:2:2 subsampling (ratio ~= 50:1)
60// 3 = JPEG quality 42, 4:2:2 subsampling (ratio ~= 60:1)
61// 2 = JPEG quality 41, 4:2:0 subsampling (ratio ~= 70:1)
62// 1 = JPEG quality 29, 4:2:0 subsampling (ratio ~= 80:1)
63// 0 = JPEG quality 15, 4:2:0 subsampling (ratio ~= 100:1)
64
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000065const TIGHT_CONF TightEncoder::conf[10] = {
DRC773cf3c2009-03-12 19:26:44 +000066 { 512, 32, 6, 0, 0, 0, 4, 15, SUBSAMP_420 }, // 0
67 { 2048, 64, 6, 1, 1, 1, 8, 29, SUBSAMP_420 }, // 1
68 { 4096, 128, 8, 3, 3, 2, 24, 41, SUBSAMP_420 }, // 2
69 { 8192, 256, 12, 5, 5, 2, 32, 42, SUBSAMP_422 }, // 3
70 { 16384, 512, 12, 6, 7, 3, 32, 62, SUBSAMP_422 }, // 4
71 { 32768, 512, 12, 7, 8, 4, 32, 77, SUBSAMP_422 }, // 5
72 { 65536, 1024, 16, 7, 8, 5, 32, 79, SUBSAMP_NONE }, // 6
73 { 65536, 1024, 16, 8, 9, 6, 64, 86, SUBSAMP_NONE }, // 7
74 { 65536, 2048, 24, 9, 9, 7, 64, 92, SUBSAMP_NONE }, // 8
75 { 65536, 2048, 32, 9, 9, 9, 96,100, SUBSAMP_NONE } // 9
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076};
77const int TightEncoder::defaultCompressLevel = 6;
78
79// FIXME: Not good to mirror TightEncoder's members here.
80static const TIGHT_CONF* s_pconf;
81static const TIGHT_CONF* s_pjconf;
82
83//
84// Including BPP-dependent implementation of the encoder.
85//
86
87#define EXTRA_ARGS ImageGetter* ig
88#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
89#define BPP 8
90#include <rfb/tightEncode.h>
91#undef BPP
92#define BPP 16
93#include <rfb/tightEncode.h>
94#undef BPP
95#define BPP 32
96#include <rfb/tightEncode.h>
97#undef BPP
98
99Encoder* TightEncoder::create(SMsgWriter* writer)
100{
101 return new TightEncoder(writer);
102}
103
104TightEncoder::TightEncoder(SMsgWriter* writer_) : writer(writer_)
105{
106 setCompressLevel(defaultCompressLevel);
107 setQualityLevel(-1);
108}
109
110TightEncoder::~TightEncoder()
111{
112}
113
114void TightEncoder::setCompressLevel(int level)
115{
116 if (level >= 0 && level <= 9) {
117 pconf = &conf[level];
118 } else {
119 pconf = &conf[defaultCompressLevel];
120 }
121}
122
123void TightEncoder::setQualityLevel(int level)
124{
125 if (level >= 0 && level <= 9) {
126 pjconf = &conf[level];
127 } else {
128 pjconf = NULL;
129 }
130}
131
132int TightEncoder::getNumRects(const Rect &r)
133{
134 const unsigned int w = r.width();
135 const unsigned int h = r.height();
136
137 // Will this rectangle split into subrects?
138 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
139 if (!rectTooBig)
140 return 1;
141
142 // Compute max sub-rectangle size.
143 const unsigned int subrectMaxWidth =
144 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
145 const unsigned int subrectMaxHeight =
146 pconf->maxRectSize / subrectMaxWidth;
147
148 // Return the number of subrects.
149 return (((w - 1) / pconf->maxRectWidth + 1) *
150 ((h - 1) / subrectMaxHeight + 1));
151}
152
153bool TightEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
154{
155 // Shortcuts to rectangle coordinates and dimensions.
156 const int x = r.tl.x;
157 const int y = r.tl.y;
158 const unsigned int w = r.width();
159 const unsigned int h = r.height();
160
161 // Copy members of current TightEncoder instance to static variables.
162 s_pconf = pconf;
163 s_pjconf = pjconf;
164
165 // Encode small rects as is.
166 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
167 if (!rectTooBig) {
168 writeSubrect(r, ig);
169 return true;
170 }
171
172 // Compute max sub-rectangle size.
173 const unsigned int subrectMaxWidth =
174 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
175 const unsigned int subrectMaxHeight =
176 pconf->maxRectSize / subrectMaxWidth;
177
178 // Split big rects into separately encoded subrects.
179 Rect sr;
180 unsigned int dx, dy, sw, sh;
181 for (dy = 0; dy < h; dy += subrectMaxHeight) {
182 for (dx = 0; dx < w; dx += pconf->maxRectWidth) {
183 sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
184 sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
185 sr.setXYWH(x + dx, y + dy, sw, sh);
186 writeSubrect(sr, ig);
187 }
188 }
189 return true;
190}
191
192void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig)
193{
194 rdr::U8* imageBuf = writer->getImageBuf(r.area());
195 ConnParams* cp = writer->getConnParams();
196 mos.clear();
197
198 switch (writer->bpp()) {
199 case 8:
200 tightEncode8(r, &mos, zos, imageBuf, cp, ig); break;
201 case 16:
202 tightEncode16(r, &mos, zos, imageBuf, cp, ig); break;
203 case 32:
204 tightEncode32(r, &mos, zos, imageBuf, cp, ig); break;
205 }
206
207 writer->startRect(r, encodingTight);
208 rdr::OutStream* os = writer->getOutStream();
209 os->writeBytes(mos.data(), mos.length());
210 writer->endRect();
211}