blob: fb19885ae524086cb16fdd19cb1dbdef6f909954 [file] [log] [blame]
Peter Åstrand9b0809c2005-02-10 15:13:38 +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 2048
34#define TIGHT_DETECT_SUBROW_WIDTH 7
35#define TIGHT_DETECT_MIN_WIDTH 8
36#define TIGHT_DETECT_MIN_HEIGHT 8
37
38//
39// Compression level stuff. The following array contains various
40// encoder parameters for each of 10 compression levels (0..9).
41// Last three parameters correspond to JPEG quality levels (0..9).
42//
43// NOTE: s_conf[9].maxRectSize should be >= s_conf[i].maxRectSize,
44// where i in [0..8]. RequiredBuffSize() method depends on this.
45// FIXME: Is this comment obsolete?
46//
47
48const TIGHT_CONF TightEncoder::conf[10] = {
49 { 512, 32, 6, 0, 0, 0, 4, 5, 10000, 23000 },
50 { 768, 32, 6, 1, 1, 1, 8, 10, 8000, 18000 },
51 { 1024, 32, 8, 3, 3, 2, 24, 15, 6500, 15000 },
52 { 1536, 48, 12, 5, 5, 3, 32, 25, 5000, 12000 },
53 { 2048, 48, 12, 6, 6, 4, 32, 37, 4000, 10000 },
54 { 3072, 64, 12, 7, 7, 5, 32, 50, 3000, 8000 },
55 { 4096, 64, 16, 7, 7, 6, 48, 60, 2000, 5000 },
56 { 6144, 64, 16, 8, 8, 7, 64, 70, 1000, 2500 },
57 { 8192, 128, 24, 9, 9, 8, 64, 75, 500, 1200 },
58 { 10240, 128, 32, 9, 9, 9, 96, 80, 200, 500 }
59};
60const int TightEncoder::defaultCompressLevel = 6;
61
62// FIXME: Not good to mirror TightEncoder's members here.
63static const TIGHT_CONF* s_pconf;
64static const TIGHT_CONF* s_pjconf;
65
66//
67// Including BPP-dependent implementation of the encoder.
68//
69
70#define EXTRA_ARGS ImageGetter* ig
71#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
72#define BPP 8
73#include <rfb/tightEncode.h>
74#undef BPP
75#define BPP 16
76#include <rfb/tightEncode.h>
77#undef BPP
78#define BPP 32
79#include <rfb/tightEncode.h>
80#undef BPP
81
82Encoder* TightEncoder::create(SMsgWriter* writer)
83{
84 return new TightEncoder(writer);
85}
86
87TightEncoder::TightEncoder(SMsgWriter* writer_) : writer(writer_)
88{
89 setCompressLevel(defaultCompressLevel);
90 setQualityLevel(-1);
91}
92
93TightEncoder::~TightEncoder()
94{
95}
96
97void TightEncoder::setCompressLevel(int level)
98{
99 if (level >= 0 && level <= 9) {
100 pconf = &conf[level];
101 } else {
102 pconf = &conf[defaultCompressLevel];
103 }
104}
105
106void TightEncoder::setQualityLevel(int level)
107{
108 if (level >= 0 && level <= 9) {
109 pjconf = &conf[level];
110 } else {
111 pjconf = NULL;
112 }
113}
114
Peter Åstrand67505112005-02-10 15:30:47 +0000115int TightEncoder::getNumRects(const Rect &r)
116{
117 const unsigned int w = r.width();
118 const unsigned int h = r.height();
119
120 // Will this rectangle split into subrects?
121 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
122 if (!rectTooBig)
123 return 1;
124
125 // Compute max sub-rectangle size.
126 const unsigned int subrectMaxWidth =
127 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
128 const unsigned int subrectMaxHeight =
129 pconf->maxRectSize / subrectMaxWidth;
130
131 // Return the number of subrects.
132 return (((w - 1) / pconf->maxRectWidth + 1) *
133 ((h - 1) / subrectMaxHeight + 1));
134}
135
Peter Åstrand9b0809c2005-02-10 15:13:38 +0000136bool TightEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
137{
138 // Shortcuts to rectangle coordinates and dimensions.
139 const int x = r.tl.x;
140 const int y = r.tl.y;
141 const unsigned int w = r.width();
142 const unsigned int h = r.height();
143
144 // Copy members of current TightEncoder instance to static variables.
145 s_pconf = pconf;
146 s_pjconf = pjconf;
147
148 // Encode small rects as is.
149 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
150 if (!rectTooBig) {
151 writeSubrect(r, ig);
152 return true;
153 }
154
155 // Compute max sub-rectangle size.
156 const unsigned int subrectMaxWidth =
157 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
158 const unsigned int subrectMaxHeight =
159 pconf->maxRectSize / subrectMaxWidth;
160
161 // Split big rects into separately encoded subrects.
162 Rect sr;
163 unsigned int dx, dy, sw, sh;
164 for (dy = 0; dy < h; dy += subrectMaxHeight) {
165 for (dx = 0; dx < w; dx += pconf->maxRectWidth) {
166 sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
167 sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
168 sr.setXYWH(x + dx, y + dy, sw, sh);
169 writeSubrect(sr, ig);
170 }
171 }
172 return true;
173}
174
175void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig)
176{
177 rdr::U8* imageBuf = writer->getImageBuf(r.area());
178 ConnParams* cp = writer->getConnParams();
179 mos.clear();
180
181 switch (writer->bpp()) {
182 case 8:
183 tightEncode8(r, &mos, zos, imageBuf, cp, ig); break;
184 case 16:
185 tightEncode16(r, &mos, zos, imageBuf, cp, ig); break;
186 case 32:
187 tightEncode32(r, &mos, zos, imageBuf, cp, ig); break;
188 }
189
190 writer->startRect(r, encodingTight);
191 rdr::OutStream* os = writer->getOutStream();
192 os->writeBytes(mos.data(), mos.length());
193 writer->endRect();
194}