blob: c3f87dab8482cb4552e4ce094ef60ce09ab8858f [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.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19#include <rdr/OutStream.h>
Pierre Ossman456b2c22014-01-15 13:22:03 +010020#include <rfb/TransImageGetter.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010021#include <rfb/PixelBuffer.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000022#include <rfb/encodings.h>
23#include <rfb/ConnParams.h>
24#include <rfb/SMsgWriter.h>
Pierre Ossman668468b2014-01-31 12:37:32 +010025#include <rfb/SConnection.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/TightEncoder.h>
27
28using namespace rfb;
29
30// Minimum amount of data to be compressed. This value should not be
31// changed, doing so will break compatibility with existing clients.
32#define TIGHT_MIN_TO_COMPRESS 12
33
34// Adjustable parameters.
35// FIXME: Get rid of #defines
DRCcd2c5d42011-08-11 11:18:34 +000036#define TIGHT_MAX_SPLIT_TILE_SIZE 16
37#define TIGHT_MIN_SPLIT_RECT_SIZE 4096
38#define TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039
40//
41// Compression level stuff. The following array contains various
42// encoder parameters for each of 10 compression levels (0..9).
43// Last three parameters correspond to JPEG quality levels (0..9).
44//
DRCcd2c5d42011-08-11 11:18:34 +000045// NOTE: The parameters used in this encoder are the result of painstaking
46// research by The VirtualGL Project using RFB session captures from a variety
47// of both 2D and 3D applications. See http://www.VirtualGL.org for the full
48// reports.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
DRC773cf3c2009-03-12 19:26:44 +000050// NOTE: The JPEG quality and subsampling levels below were obtained
51// experimentally by the VirtualGL Project. They represent the approximate
52// average compression ratios listed below, as measured across the set of
53// every 10th frame in the SPECviewperf 9 benchmark suite.
54//
55// 9 = JPEG quality 100, no subsampling (ratio ~= 10:1)
56// [this should be lossless, except for round-off error]
57// 8 = JPEG quality 92, no subsampling (ratio ~= 20:1)
58// [this should be perceptually lossless, based on current research]
59// 7 = JPEG quality 86, no subsampling (ratio ~= 25:1)
60// 6 = JPEG quality 79, no subsampling (ratio ~= 30:1)
61// 5 = JPEG quality 77, 4:2:2 subsampling (ratio ~= 40:1)
62// 4 = JPEG quality 62, 4:2:2 subsampling (ratio ~= 50:1)
63// 3 = JPEG quality 42, 4:2:2 subsampling (ratio ~= 60:1)
64// 2 = JPEG quality 41, 4:2:0 subsampling (ratio ~= 70:1)
65// 1 = JPEG quality 29, 4:2:0 subsampling (ratio ~= 80:1)
66// 0 = JPEG quality 15, 4:2:0 subsampling (ratio ~= 100:1)
67
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068const TIGHT_CONF TightEncoder::conf[10] = {
Pierre Ossmanb948a912014-01-15 13:23:43 +010069 { 65536, 2048, 6, 0, 0, 0, 4, 24, 15, subsample4X }, // 0
70 { 65536, 2048, 6, 1, 1, 1, 8, 24, 29, subsample4X }, // 1
71 { 65536, 2048, 8, 3, 3, 2, 24, 96, 41, subsample4X }, // 2
72 { 65536, 2048, 12, 5, 5, 2, 32, 96, 42, subsample2X }, // 3
73 { 65536, 2048, 12, 6, 7, 3, 32, 96, 62, subsample2X }, // 4
74 { 65536, 2048, 12, 7, 8, 4, 32, 96, 77, subsample2X }, // 5
75 { 65536, 2048, 16, 7, 8, 5, 32, 96, 79, subsampleNone }, // 6
76 { 65536, 2048, 16, 8, 9, 6, 64, 96, 86, subsampleNone }, // 7
77 { 65536, 2048, 24, 9, 9, 7, 64, 96, 92, subsampleNone }, // 8
78 { 65536, 2048, 32, 9, 9, 9, 96, 96,100, subsampleNone } // 9
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079};
Pierre Ossman701ad682011-11-20 15:39:17 +000080
81const int TightEncoder::defaultCompressLevel = 2;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083//
84// Including BPP-dependent implementation of the encoder.
85//
86
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000087#define BPP 8
88#include <rfb/tightEncode.h>
89#undef BPP
90#define BPP 16
91#include <rfb/tightEncode.h>
92#undef BPP
93#define BPP 32
94#include <rfb/tightEncode.h>
95#undef BPP
96
Pierre Ossman668468b2014-01-31 12:37:32 +010097TightEncoder::TightEncoder(SConnection* conn) : Encoder(conn)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098{
99 setCompressLevel(defaultCompressLevel);
100 setQualityLevel(-1);
101}
102
103TightEncoder::~TightEncoder()
104{
105}
106
107void TightEncoder::setCompressLevel(int level)
108{
109 if (level >= 0 && level <= 9) {
110 pconf = &conf[level];
111 } else {
112 pconf = &conf[defaultCompressLevel];
113 }
114}
115
116void TightEncoder::setQualityLevel(int level)
117{
118 if (level >= 0 && level <= 9) {
DRCb4a83232011-08-19 04:57:18 +0000119 jpegQuality = conf[level].jpegQuality;
120 jpegSubsampling = conf[level].jpegSubsampling;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121 } else {
DRCb4a83232011-08-19 04:57:18 +0000122 jpegQuality = -1;
Pierre Ossmanb948a912014-01-15 13:23:43 +0100123 jpegSubsampling = subsampleUndefined;
DRCb4a83232011-08-19 04:57:18 +0000124 }
125}
126
Pierre Ossmanb948a912014-01-15 13:23:43 +0100127void TightEncoder::setFineQualityLevel(int quality, int subsampling)
DRCb4a83232011-08-19 04:57:18 +0000128{
Pierre Ossmanb948a912014-01-15 13:23:43 +0100129 jpegQuality = quality;
130 jpegSubsampling = subsampling;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000131}
132
DRCffe09d62011-08-17 02:27:59 +0000133bool TightEncoder::checkSolidTile(Rect& r, rdr::U32* colorPtr,
DRCcd2c5d42011-08-11 11:18:34 +0000134 bool needSameColor)
135{
DRCffe09d62011-08-17 02:27:59 +0000136 switch (serverpf.bpp) {
DRCcd2c5d42011-08-11 11:18:34 +0000137 case 32:
DRCffe09d62011-08-17 02:27:59 +0000138 return checkSolidTile32(r, colorPtr, needSameColor);
DRCcd2c5d42011-08-11 11:18:34 +0000139 case 16:
DRCffe09d62011-08-17 02:27:59 +0000140 return checkSolidTile16(r, colorPtr, needSameColor);
DRCcd2c5d42011-08-11 11:18:34 +0000141 default:
DRCffe09d62011-08-17 02:27:59 +0000142 return checkSolidTile8(r, colorPtr, needSameColor);
DRCcd2c5d42011-08-11 11:18:34 +0000143 }
144}
145
DRCffe09d62011-08-17 02:27:59 +0000146void TightEncoder::findBestSolidArea(Rect& r, rdr::U32 colorValue, Rect& bestr)
DRCcd2c5d42011-08-11 11:18:34 +0000147{
148 int dx, dy, dw, dh;
149 int w_prev;
150 Rect sr;
151 int w_best = 0, h_best = 0;
152
153 bestr.tl.x = bestr.br.x = r.tl.x;
154 bestr.tl.y = bestr.br.y = r.tl.y;
155
156 w_prev = r.width();
157
158 for (dy = r.tl.y; dy < r.br.y; dy += TIGHT_MAX_SPLIT_TILE_SIZE) {
159
160 dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= r.br.y) ?
161 TIGHT_MAX_SPLIT_TILE_SIZE : (r.br.y - dy);
162 dw = (w_prev > TIGHT_MAX_SPLIT_TILE_SIZE) ?
163 TIGHT_MAX_SPLIT_TILE_SIZE : w_prev;
164
165 sr.setXYWH(r.tl.x, dy, dw, dh);
DRCffe09d62011-08-17 02:27:59 +0000166 if (!checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000167 break;
168
169 for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
170 dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= r.tl.x + w_prev) ?
171 TIGHT_MAX_SPLIT_TILE_SIZE : (r.tl.x + w_prev - dx);
172 sr.setXYWH(dx, dy, dw, dh);
DRCffe09d62011-08-17 02:27:59 +0000173 if (!checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000174 break;
DRCffe09d62011-08-17 02:27:59 +0000175 dx += dw;
DRCcd2c5d42011-08-11 11:18:34 +0000176 }
177
178 w_prev = dx - r.tl.x;
179 if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
180 w_best = w_prev;
181 h_best = dy + dh - r.tl.y;
182 }
183 }
184
185 bestr.br.x = bestr.tl.x + w_best;
186 bestr.br.y = bestr.tl.y + h_best;
187}
188
DRCffe09d62011-08-17 02:27:59 +0000189void TightEncoder::extendSolidArea(const Rect& r, rdr::U32 colorValue,
190 Rect& er)
DRCcd2c5d42011-08-11 11:18:34 +0000191{
192 int cx, cy;
193 Rect sr;
194
195 // Try to extend the area upwards.
196 for (cy = er.tl.y - 1; ; cy--) {
197 sr.setXYWH(er.tl.x, cy, er.width(), 1);
DRCffe09d62011-08-17 02:27:59 +0000198 if (cy < r.tl.y || !checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000199 break;
200 }
201 er.tl.y = cy + 1;
202
203 // ... downwards.
204 for (cy = er.br.y; ; cy++) {
205 sr.setXYWH(er.tl.x, cy, er.width(), 1);
DRCffe09d62011-08-17 02:27:59 +0000206 if (cy >= r.br.y || !checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000207 break;
208 }
209 er.br.y = cy;
210
211 // ... to the left.
212 for (cx = er.tl.x - 1; ; cx--) {
213 sr.setXYWH(cx, er.tl.y, 1, er.height());
DRCffe09d62011-08-17 02:27:59 +0000214 if (cx < r.tl.x || !checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000215 break;
216 }
217 er.tl.x = cx + 1;
218
219 // ... to the right.
220 for (cx = er.br.x; ; cx++) {
221 sr.setXYWH(cx, er.tl.y, 1, er.height());
DRCffe09d62011-08-17 02:27:59 +0000222 if (cx >= r.br.x || !checkSolidTile(sr, &colorValue, true))
DRCcd2c5d42011-08-11 11:18:34 +0000223 break;
224 }
225 er.br.x = cx;
226}
227
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228int TightEncoder::getNumRects(const Rect &r)
229{
Pierre Ossman668468b2014-01-31 12:37:32 +0100230 ConnParams* cp = &conn->cp;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000231 const unsigned int w = r.width();
232 const unsigned int h = r.height();
233
DRCcd2c5d42011-08-11 11:18:34 +0000234 // If last rect. encoding is enabled, we can use the higher-performance
235 // code that pre-computes solid rectangles. In that case, we don't care
236 // about the rectangle count.
237 if (cp->supportsLastRect && w * h >= TIGHT_MIN_SPLIT_RECT_SIZE)
238 return 0;
239
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240 // Will this rectangle split into subrects?
241 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
242 if (!rectTooBig)
243 return 1;
244
245 // Compute max sub-rectangle size.
246 const unsigned int subrectMaxWidth =
247 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
248 const unsigned int subrectMaxHeight =
249 pconf->maxRectSize / subrectMaxWidth;
250
251 // Return the number of subrects.
252 return (((w - 1) / pconf->maxRectWidth + 1) *
253 ((h - 1) / subrectMaxHeight + 1));
254}
255
DRCffe09d62011-08-17 02:27:59 +0000256void TightEncoder::sendRectSimple(const Rect& r)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257{
258 // Shortcuts to rectangle coordinates and dimensions.
259 const int x = r.tl.x;
260 const int y = r.tl.y;
261 const unsigned int w = r.width();
262 const unsigned int h = r.height();
263
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264 // Encode small rects as is.
265 bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
266 if (!rectTooBig) {
DRCffe09d62011-08-17 02:27:59 +0000267 writeSubrect(r);
DRCcd2c5d42011-08-11 11:18:34 +0000268 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000269 }
270
271 // Compute max sub-rectangle size.
272 const unsigned int subrectMaxWidth =
273 (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
274 const unsigned int subrectMaxHeight =
275 pconf->maxRectSize / subrectMaxWidth;
276
277 // Split big rects into separately encoded subrects.
278 Rect sr;
279 unsigned int dx, dy, sw, sh;
280 for (dy = 0; dy < h; dy += subrectMaxHeight) {
281 for (dx = 0; dx < w; dx += pconf->maxRectWidth) {
282 sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
283 sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
284 sr.setXYWH(x + dx, y + dy, sw, sh);
DRCffe09d62011-08-17 02:27:59 +0000285 writeSubrect(sr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000286 }
287 }
DRCcd2c5d42011-08-11 11:18:34 +0000288}
289
Pierre Ossman717c07b2014-01-21 14:45:10 +0100290void TightEncoder::writeRect(const Rect& _r, TransImageGetter* _ig)
DRCcd2c5d42011-08-11 11:18:34 +0000291{
DRCffe09d62011-08-17 02:27:59 +0000292 ig = _ig;
293 serverpf = ig->getPixelBuffer()->getPF();
Pierre Ossman668468b2014-01-31 12:37:32 +0100294 ConnParams* cp = &conn->cp;
DRCffe09d62011-08-17 02:27:59 +0000295 clientpf = cp->pf();
DRCcd2c5d42011-08-11 11:18:34 +0000296
297 // Shortcuts to rectangle coordinates and dimensions.
298 Rect r = _r;
299 int x = r.tl.x;
300 int y = r.tl.y;
DRCe3ffcf72011-08-19 03:11:32 +0000301 int w = r.width();
302 int h = r.height();
DRCcd2c5d42011-08-11 11:18:34 +0000303
DRCcd2c5d42011-08-11 11:18:34 +0000304 // Encode small rects as is.
305 if (!cp->supportsLastRect || w * h < TIGHT_MIN_SPLIT_RECT_SIZE) {
DRCffe09d62011-08-17 02:27:59 +0000306 sendRectSimple(r);
Pierre Ossman717c07b2014-01-21 14:45:10 +0100307 return;
DRCcd2c5d42011-08-11 11:18:34 +0000308 }
309
310 // Split big rects into separately encoded subrects.
311 Rect sr, bestr;
DRCe3ffcf72011-08-19 03:11:32 +0000312 int dx, dy, dw, dh;
DRCcd2c5d42011-08-11 11:18:34 +0000313 rdr::U32 colorValue;
DRCffe09d62011-08-17 02:27:59 +0000314 int maxRectWidth = pconf->maxRectWidth;
DRCcd2c5d42011-08-11 11:18:34 +0000315 int nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
DRCffe09d62011-08-17 02:27:59 +0000316 int nMaxRows = pconf->maxRectSize / nMaxWidth;
DRCcd2c5d42011-08-11 11:18:34 +0000317
318 // Try to find large solid-color areas and send them separately.
319 for (dy = y; dy < y + h; dy += TIGHT_MAX_SPLIT_TILE_SIZE) {
320
321 // If a rectangle becomes too large, send its upper part now.
322 if (dy - y >= nMaxRows) {
323 sr.setXYWH(x, y, w, nMaxRows);
DRCffe09d62011-08-17 02:27:59 +0000324 sendRectSimple(sr);
DRCcd2c5d42011-08-11 11:18:34 +0000325 r.tl.y += nMaxRows;
326 y = r.tl.y;
327 h = r.height();
328 }
329
330 dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= y + h) ?
331 TIGHT_MAX_SPLIT_TILE_SIZE : (y + h - dy);
332
333 for (dx = x; dx < x + w; dx += TIGHT_MAX_SPLIT_TILE_SIZE) {
334
335 dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= x + w) ?
336 TIGHT_MAX_SPLIT_TILE_SIZE : (x + w - dx);
337
338 sr.setXYWH(dx, dy, dw, dh);
DRCffe09d62011-08-17 02:27:59 +0000339 if (checkSolidTile(sr, &colorValue, false)) {
DRCcd2c5d42011-08-11 11:18:34 +0000340
Pierre Ossmanb948a912014-01-15 13:23:43 +0100341 if (jpegSubsampling == subsampleGray && jpegQuality != -1) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100342 rdr::U16 r, g, b;
343 serverpf.rgbFromPixel(colorValue, &r, &g, &b);
344 rdr::U32 lum = ((257 * r) + (504 * g) + (98 * b)
DRCb4a83232011-08-19 04:57:18 +0000345 + 16500) / 1000;
346 colorValue = lum + (lum << 8) + (lum << 16);
347 }
348
DRCcd2c5d42011-08-11 11:18:34 +0000349 // Get dimensions of solid-color area.
350 sr.setXYWH(dx, dy, r.br.x - dx, r.br.y - dy);
DRCffe09d62011-08-17 02:27:59 +0000351 findBestSolidArea(sr, colorValue, bestr);
DRCcd2c5d42011-08-11 11:18:34 +0000352
353 // Make sure a solid rectangle is large enough
354 // (or the whole rectangle is of the same color).
355 if (bestr.area() != r.area()
356 && bestr.area() < TIGHT_MIN_SOLID_SUBRECT_SIZE)
357 continue;
358
359 // Try to extend solid rectangle to maximum size.
DRCffe09d62011-08-17 02:27:59 +0000360 extendSolidArea(r, colorValue, bestr);
DRCcd2c5d42011-08-11 11:18:34 +0000361
362 // Send rectangles at top and left to solid-color area.
363 if (bestr.tl.y != y) {
364 sr.setXYWH(x, y, w, bestr.tl.y - y);
DRCffe09d62011-08-17 02:27:59 +0000365 sendRectSimple(sr);
DRCcd2c5d42011-08-11 11:18:34 +0000366 }
367 if (bestr.tl.x != x) {
368 sr.setXYWH(x, bestr.tl.y, bestr.tl.x - x, bestr.height());
Pierre Ossman717c07b2014-01-21 14:45:10 +0100369 writeRect(sr, _ig);
DRCcd2c5d42011-08-11 11:18:34 +0000370 }
371
372 // Send solid-color rectangle.
DRCffe09d62011-08-17 02:27:59 +0000373 writeSubrect(bestr, true);
DRCcd2c5d42011-08-11 11:18:34 +0000374
375 // Send remaining rectangles (at right and bottom).
376 if (bestr.br.x != r.br.x) {
377 sr.setXYWH(bestr.br.x, bestr.tl.y, r.br.x - bestr.br.x,
378 bestr.height());
Pierre Ossman717c07b2014-01-21 14:45:10 +0100379 writeRect(sr, _ig);
DRCcd2c5d42011-08-11 11:18:34 +0000380 }
381 if (bestr.br.y != r.br.y) {
382 sr.setXYWH(x, bestr.br.y, w, r.br.y - bestr.br.y);
Pierre Ossman717c07b2014-01-21 14:45:10 +0100383 writeRect(sr, _ig);
DRCcd2c5d42011-08-11 11:18:34 +0000384 }
385
Pierre Ossman717c07b2014-01-21 14:45:10 +0100386 return;
DRCcd2c5d42011-08-11 11:18:34 +0000387 }
388 }
389 }
390
391 // No suitable solid-color rectangles found.
DRCffe09d62011-08-17 02:27:59 +0000392 sendRectSimple(r);
Pierre Ossman717c07b2014-01-21 14:45:10 +0100393 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000394}
395
DRCffe09d62011-08-17 02:27:59 +0000396void TightEncoder::writeSubrect(const Rect& r, bool forceSolid)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000397{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000398 mos.clear();
399
DRCffe09d62011-08-17 02:27:59 +0000400 switch (clientpf.bpp) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000401 case 8:
DRCffe09d62011-08-17 02:27:59 +0000402 tightEncode8(r, &mos, forceSolid); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000403 case 16:
DRCffe09d62011-08-17 02:27:59 +0000404 tightEncode16(r, &mos, forceSolid); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405 case 32:
DRCffe09d62011-08-17 02:27:59 +0000406 tightEncode32(r, &mos, forceSolid); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000407 }
408
Pierre Ossman668468b2014-01-31 12:37:32 +0100409 conn->writer()->startRect(r, encodingTight);
410 rdr::OutStream* os = conn->getOutStream();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411 os->writeBytes(mos.data(), mos.length());
Pierre Ossman668468b2014-01-31 12:37:32 +0100412 conn->writer()->endRect();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413}
Pierre Ossman7b5c0692014-03-17 14:35:51 +0100414
415void TightEncoder::writeCompact(rdr::OutStream* os, rdr::U32 value)
416{
417 rdr::U8 b;
418 b = value & 0x7F;
419 if (value <= 0x7F) {
420 os->writeU8(b);
421 } else {
422 os->writeU8(b | 0x80);
423 b = value >> 7 & 0x7F;
424 if (value <= 0x3FFF) {
425 os->writeU8(b);
426 } else {
427 os->writeU8(b | 0x80);
428 os->writeU8(value >> 14 & 0xFF);
429 }
430 }
431}