blob: 159ebbfb492efd3111d3ee0a4be27981494eac8c [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
47const TIGHT_CONF TightEncoder::conf[10] = {
Pierre Ossmanc6a445f2009-03-11 16:15:29 +000048 { 512, 32, 6, 0, 0, 0, 4, 10, 1 }, // 0
49 { 2048, 64, 6, 1, 1, 1, 8, 25, 1 }, // 1
50 { 4096, 128, 8, 3, 3, 2, 24, 25, 0 }, // 2
51 { 8192, 256, 12, 5, 5, 2, 32, 50, 1 }, // 3
52 { 16384, 512, 12, 6, 7, 3, 32, 50, 0 }, // 4
53 { 32768, 512, 12, 7, 8, 4, 32, 75, 1 }, // 5
54 { 65536, 1024, 16, 7, 8, 5, 32, 75, 0 }, // 6
55 { 65536, 1024, 16, 8, 9, 6, 64, 83, 0 }, // 7
56 { 65536, 2048, 24, 9, 9, 7, 64, 92, 0 }, // 8
57 { 65536, 2048, 32, 9, 9, 9, 96,100, 0 } // 9
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058};
59const int TightEncoder::defaultCompressLevel = 6;
60
61// FIXME: Not good to mirror TightEncoder's members here.
62static const TIGHT_CONF* s_pconf;
63static const TIGHT_CONF* s_pjconf;
64
65//
66// Including BPP-dependent implementation of the encoder.
67//
68
69#define EXTRA_ARGS ImageGetter* ig
70#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
71#define BPP 8
72#include <rfb/tightEncode.h>
73#undef BPP
74#define BPP 16
75#include <rfb/tightEncode.h>
76#undef BPP
77#define BPP 32
78#include <rfb/tightEncode.h>
79#undef BPP
80
81Encoder* TightEncoder::create(SMsgWriter* writer)
82{
83 return new TightEncoder(writer);
84}
85
86TightEncoder::TightEncoder(SMsgWriter* writer_) : writer(writer_)
87{
88 setCompressLevel(defaultCompressLevel);
89 setQualityLevel(-1);
90}
91
92TightEncoder::~TightEncoder()
93{
94}
95
96void TightEncoder::setCompressLevel(int level)
97{
98 if (level >= 0 && level <= 9) {
99 pconf = &conf[level];
100 } else {
101 pconf = &conf[defaultCompressLevel];
102 }
103}
104
105void TightEncoder::setQualityLevel(int level)
106{
107 if (level >= 0 && level <= 9) {
108 pjconf = &conf[level];
109 } else {
110 pjconf = NULL;
111 }
112}
113
114int TightEncoder::getNumRects(const Rect &r)
115{
116 const unsigned int w = r.width();
117 const unsigned int h = r.height();
118
119 // Will this rectangle split into subrects?
120 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
121 if (!rectTooBig)
122 return 1;
123
124 // Compute max sub-rectangle size.
125 const unsigned int subrectMaxWidth =
126 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
127 const unsigned int subrectMaxHeight =
128 pconf->maxRectSize / subrectMaxWidth;
129
130 // Return the number of subrects.
131 return (((w - 1) / pconf->maxRectWidth + 1) *
132 ((h - 1) / subrectMaxHeight + 1));
133}
134
135bool TightEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
136{
137 // Shortcuts to rectangle coordinates and dimensions.
138 const int x = r.tl.x;
139 const int y = r.tl.y;
140 const unsigned int w = r.width();
141 const unsigned int h = r.height();
142
143 // Copy members of current TightEncoder instance to static variables.
144 s_pconf = pconf;
145 s_pjconf = pjconf;
146
147 // Encode small rects as is.
148 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
149 if (!rectTooBig) {
150 writeSubrect(r, ig);
151 return true;
152 }
153
154 // Compute max sub-rectangle size.
155 const unsigned int subrectMaxWidth =
156 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
157 const unsigned int subrectMaxHeight =
158 pconf->maxRectSize / subrectMaxWidth;
159
160 // Split big rects into separately encoded subrects.
161 Rect sr;
162 unsigned int dx, dy, sw, sh;
163 for (dy = 0; dy < h; dy += subrectMaxHeight) {
164 for (dx = 0; dx < w; dx += pconf->maxRectWidth) {
165 sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
166 sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
167 sr.setXYWH(x + dx, y + dy, sw, sh);
168 writeSubrect(sr, ig);
169 }
170 }
171 return true;
172}
173
174void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig)
175{
176 rdr::U8* imageBuf = writer->getImageBuf(r.area());
177 ConnParams* cp = writer->getConnParams();
178 mos.clear();
179
180 switch (writer->bpp()) {
181 case 8:
182 tightEncode8(r, &mos, zos, imageBuf, cp, ig); break;
183 case 16:
184 tightEncode16(r, &mos, zos, imageBuf, cp, ig); break;
185 case 32:
186 tightEncode32(r, &mos, zos, imageBuf, cp, ig); break;
187 }
188
189 writer->startRect(r, encodingTight);
190 rdr::OutStream* os = writer->getOutStream();
191 os->writeBytes(mos.data(), mos.length());
192 writer->endRect();
193}