blob: 0ceec8fd4a41dbea924481a332f643779911a433 [file] [log] [blame]
Pierre Ossmanc0397262014-03-14 15:59:46 +01001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossman8c3bd692018-03-22 15:58:54 +01003 * Copyright 2014-2018 Pierre Ossman for Cendio AB
Pierre Ossmanc0397262014-03-14 15:59:46 +01004 *
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 */
20#include <rfb/EncodeManager.h>
21#include <rfb/Encoder.h>
22#include <rfb/Palette.h>
23#include <rfb/SConnection.h>
24#include <rfb/SMsgWriter.h>
25#include <rfb/UpdateTracker.h>
Pierre Ossman20dd2a92015-02-11 17:43:15 +010026#include <rfb/LogWriter.h>
Pierre Ossmanc0397262014-03-14 15:59:46 +010027
28#include <rfb/RawEncoder.h>
29#include <rfb/RREEncoder.h>
30#include <rfb/HextileEncoder.h>
31#include <rfb/ZRLEEncoder.h>
32#include <rfb/TightEncoder.h>
33#include <rfb/TightJPEGEncoder.h>
34
35using namespace rfb;
36
Pierre Ossman20dd2a92015-02-11 17:43:15 +010037static LogWriter vlog("EncodeManager");
38
Pierre Ossmanc0397262014-03-14 15:59:46 +010039// Split each rectangle into smaller ones no larger than this area,
40// and no wider than this width.
41static const int SubRectMaxArea = 65536;
42static const int SubRectMaxWidth = 2048;
43
44// The size in pixels of either side of each block tested when looking
45// for solid blocks.
46static const int SolidSearchBlock = 16;
47// Don't bother with blocks smaller than this
48static const int SolidBlockMinArea = 2048;
49
50namespace rfb {
51
52enum EncoderClass {
53 encoderRaw,
54 encoderRRE,
55 encoderHextile,
56 encoderTight,
57 encoderTightJPEG,
58 encoderZRLE,
59 encoderClassMax,
60};
61
62enum EncoderType {
63 encoderSolid,
64 encoderBitmap,
65 encoderBitmapRLE,
66 encoderIndexed,
67 encoderIndexedRLE,
68 encoderFullColour,
69 encoderTypeMax,
70};
71
72struct RectInfo {
73 int rleRuns;
74 Palette palette;
75};
76
77};
78
Pierre Ossman20dd2a92015-02-11 17:43:15 +010079static const char *encoderClassName(EncoderClass klass)
80{
81 switch (klass) {
82 case encoderRaw:
83 return "Raw";
84 case encoderRRE:
85 return "RRE";
86 case encoderHextile:
87 return "Hextile";
88 case encoderTight:
89 return "Tight";
90 case encoderTightJPEG:
91 return "Tight (JPEG)";
92 case encoderZRLE:
93 return "ZRLE";
Pierre Ossman620dd952015-03-03 16:28:54 +010094 case encoderClassMax:
95 break;
Pierre Ossman20dd2a92015-02-11 17:43:15 +010096 }
97
98 return "Unknown Encoder Class";
99}
100
101static const char *encoderTypeName(EncoderType type)
102{
103 switch (type) {
104 case encoderSolid:
105 return "Solid";
106 case encoderBitmap:
107 return "Bitmap";
108 case encoderBitmapRLE:
109 return "Bitmap RLE";
110 case encoderIndexed:
111 return "Indexed";
112 case encoderIndexedRLE:
113 return "Indexed RLE";
114 case encoderFullColour:
115 return "Full Colour";
Pierre Ossman620dd952015-03-03 16:28:54 +0100116 case encoderTypeMax:
117 break;
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100118 }
119
120 return "Unknown Encoder Type";
121}
122
Pierre Ossmanc0397262014-03-14 15:59:46 +0100123EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
124{
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100125 StatsVector::iterator iter;
126
Pierre Ossmanc0397262014-03-14 15:59:46 +0100127 encoders.resize(encoderClassMax, NULL);
128 activeEncoders.resize(encoderTypeMax, encoderRaw);
129
130 encoders[encoderRaw] = new RawEncoder(conn);
131 encoders[encoderRRE] = new RREEncoder(conn);
132 encoders[encoderHextile] = new HextileEncoder(conn);
133 encoders[encoderTight] = new TightEncoder(conn);
134 encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
135 encoders[encoderZRLE] = new ZRLEEncoder(conn);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100136
137 updates = 0;
Pierre Ossmane539cb82015-09-22 11:09:00 +0200138 memset(&copyStats, 0, sizeof(copyStats));
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100139 stats.resize(encoderClassMax);
140 for (iter = stats.begin();iter != stats.end();++iter) {
141 StatsVector::value_type::iterator iter2;
142 iter->resize(encoderTypeMax);
143 for (iter2 = iter->begin();iter2 != iter->end();++iter2)
144 memset(&*iter2, 0, sizeof(EncoderStats));
145 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100146}
147
148EncodeManager::~EncodeManager()
149{
150 std::vector<Encoder*>::iterator iter;
151
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100152 logStats();
153
Pierre Ossmanc0397262014-03-14 15:59:46 +0100154 for (iter = encoders.begin();iter != encoders.end();iter++)
155 delete *iter;
156}
157
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100158void EncodeManager::logStats()
159{
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100160 size_t i, j;
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100161
162 unsigned rects;
163 unsigned long long pixels, bytes, equivalent;
164
165 double ratio;
166
Pierre Ossman64624342015-03-03 16:30:13 +0100167 char a[1024], b[1024];
168
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100169 rects = 0;
170 pixels = bytes = equivalent = 0;
171
172 vlog.info("Framebuffer updates: %u", updates);
173
Pierre Ossmane539cb82015-09-22 11:09:00 +0200174 if (copyStats.rects != 0) {
175 vlog.info(" %s:", "CopyRect");
176
177 rects += copyStats.rects;
178 pixels += copyStats.pixels;
179 bytes += copyStats.bytes;
180 equivalent += copyStats.equivalent;
181
182 ratio = (double)copyStats.equivalent / copyStats.bytes;
183
184 siPrefix(copyStats.rects, "rects", a, sizeof(a));
185 siPrefix(copyStats.pixels, "pixels", b, sizeof(b));
186 vlog.info(" %s: %s, %s", "Copies", a, b);
187 iecPrefix(copyStats.bytes, "B", a, sizeof(a));
188 vlog.info(" %*s %s (1:%g ratio)",
189 (int)strlen("Copies"), "",
190 a, ratio);
191 }
192
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100193 for (i = 0;i < stats.size();i++) {
194 // Did this class do anything at all?
195 for (j = 0;j < stats[i].size();j++) {
196 if (stats[i][j].rects != 0)
197 break;
198 }
199 if (j == stats[i].size())
200 continue;
201
202 vlog.info(" %s:", encoderClassName((EncoderClass)i));
203
204 for (j = 0;j < stats[i].size();j++) {
205 if (stats[i][j].rects == 0)
206 continue;
207
208 rects += stats[i][j].rects;
209 pixels += stats[i][j].pixels;
210 bytes += stats[i][j].bytes;
211 equivalent += stats[i][j].equivalent;
212
213 ratio = (double)stats[i][j].equivalent / stats[i][j].bytes;
214
Pierre Ossman64624342015-03-03 16:30:13 +0100215 siPrefix(stats[i][j].rects, "rects", a, sizeof(a));
216 siPrefix(stats[i][j].pixels, "pixels", b, sizeof(b));
217 vlog.info(" %s: %s, %s", encoderTypeName((EncoderType)j), a, b);
218 iecPrefix(stats[i][j].bytes, "B", a, sizeof(a));
219 vlog.info(" %*s %s (1:%g ratio)",
220 (int)strlen(encoderTypeName((EncoderType)j)), "",
221 a, ratio);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100222 }
223 }
224
225 ratio = (double)equivalent / bytes;
226
Pierre Ossman64624342015-03-03 16:30:13 +0100227 siPrefix(rects, "rects", a, sizeof(a));
228 siPrefix(pixels, "pixels", b, sizeof(b));
229 vlog.info(" Total: %s, %s", a, b);
230 iecPrefix(bytes, "B", a, sizeof(a));
231 vlog.info(" %s (1:%g ratio)", a, ratio);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100232}
233
Pierre Ossmanc0397262014-03-14 15:59:46 +0100234bool EncodeManager::supported(int encoding)
235{
236 switch (encoding) {
237 case encodingRaw:
238 case encodingRRE:
239 case encodingHextile:
240 case encodingZRLE:
241 case encodingTight:
242 return true;
243 default:
244 return false;
245 }
246}
247
248void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
249 const RenderedCursor* renderedCursor)
250{
251 int nRects;
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100252 Region changed, cursorRegion;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100253
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100254 updates++;
255
Pierre Ossmanc0397262014-03-14 15:59:46 +0100256 prepareEncoders();
257
Pierre Ossman74385d32018-03-22 16:00:18 +0100258 changed = ui.changed;
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100259
260 /*
261 * We need to render the cursor seperately as it has its own
262 * magical pixel buffer, so split it out from the changed region.
263 */
264 if (renderedCursor != NULL) {
265 cursorRegion = changed.intersect(renderedCursor->getEffectiveRect());
266 changed.assign_subtract(renderedCursor->getEffectiveRect());
267 }
268
Pierre Ossmanc0397262014-03-14 15:59:46 +0100269 if (conn->cp.supportsLastRect)
270 nRects = 0xFFFF;
271 else {
272 nRects = ui.copied.numRects();
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100273 nRects += computeNumRects(changed);
274 nRects += computeNumRects(cursorRegion);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100275 }
276
277 conn->writer()->writeFramebufferUpdateStart(nRects);
278
279 writeCopyRects(ui);
280
281 /*
282 * We start by searching for solid rects, which are then removed
283 * from the changed region.
284 */
Pierre Ossmanc0397262014-03-14 15:59:46 +0100285 if (conn->cp.supportsLastRect)
286 writeSolidRects(&changed, pb);
287
288 writeRects(changed, pb);
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100289 writeRects(cursorRegion, renderedCursor);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100290
291 conn->writer()->writeFramebufferUpdateEnd();
292}
293
294void EncodeManager::prepareEncoders()
295{
296 enum EncoderClass solid, bitmap, bitmapRLE;
297 enum EncoderClass indexed, indexedRLE, fullColour;
298
299 rdr::S32 preferred;
300
301 std::vector<int>::iterator iter;
302
303 solid = bitmap = bitmapRLE = encoderRaw;
304 indexed = indexedRLE = fullColour = encoderRaw;
305
306 // Try to respect the client's wishes
Pierre Ossman48700812014-09-17 17:11:56 +0200307 preferred = conn->getPreferredEncoding();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100308 switch (preferred) {
309 case encodingRRE:
310 // Horrible for anything high frequency and/or lots of colours
311 bitmapRLE = indexedRLE = encoderRRE;
312 break;
313 case encodingHextile:
314 // Slightly less horrible
315 bitmapRLE = indexedRLE = fullColour = encoderHextile;
316 break;
317 case encodingTight:
318 if (encoders[encoderTightJPEG]->isSupported() &&
319 (conn->cp.pf().bpp >= 16))
320 fullColour = encoderTightJPEG;
321 else
322 fullColour = encoderTight;
323 indexed = indexedRLE = encoderTight;
324 bitmap = bitmapRLE = encoderTight;
325 break;
326 case encodingZRLE:
327 fullColour = encoderZRLE;
328 bitmapRLE = indexedRLE = encoderZRLE;
329 bitmap = indexed = encoderZRLE;
330 break;
331 }
332
333 // Any encoders still unassigned?
334
335 if (fullColour == encoderRaw) {
336 if (encoders[encoderTightJPEG]->isSupported() &&
337 (conn->cp.pf().bpp >= 16))
338 fullColour = encoderTightJPEG;
339 else if (encoders[encoderZRLE]->isSupported())
340 fullColour = encoderZRLE;
341 else if (encoders[encoderTight]->isSupported())
342 fullColour = encoderTight;
343 else if (encoders[encoderHextile]->isSupported())
344 fullColour = encoderHextile;
345 }
346
347 if (indexed == encoderRaw) {
348 if (encoders[encoderZRLE]->isSupported())
349 indexed = encoderZRLE;
350 else if (encoders[encoderTight]->isSupported())
351 indexed = encoderTight;
352 else if (encoders[encoderHextile]->isSupported())
353 indexed = encoderHextile;
354 }
355
356 if (indexedRLE == encoderRaw)
357 indexedRLE = indexed;
358
359 if (bitmap == encoderRaw)
360 bitmap = indexed;
361 if (bitmapRLE == encoderRaw)
362 bitmapRLE = bitmap;
363
364 if (solid == encoderRaw) {
365 if (encoders[encoderTight]->isSupported())
366 solid = encoderTight;
367 else if (encoders[encoderRRE]->isSupported())
368 solid = encoderRRE;
369 else if (encoders[encoderZRLE]->isSupported())
370 solid = encoderZRLE;
371 else if (encoders[encoderHextile]->isSupported())
372 solid = encoderHextile;
373 }
374
375 // JPEG is the only encoder that can reduce things to grayscale
376 if ((conn->cp.subsampling == subsampleGray) &&
377 encoders[encoderTightJPEG]->isSupported()) {
378 solid = bitmap = bitmapRLE = encoderTightJPEG;
379 indexed = indexedRLE = fullColour = encoderTightJPEG;
380 }
381
382 activeEncoders[encoderSolid] = solid;
383 activeEncoders[encoderBitmap] = bitmap;
384 activeEncoders[encoderBitmapRLE] = bitmapRLE;
385 activeEncoders[encoderIndexed] = indexed;
386 activeEncoders[encoderIndexedRLE] = indexedRLE;
387 activeEncoders[encoderFullColour] = fullColour;
388
389 for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
390 Encoder *encoder;
391
392 encoder = encoders[*iter];
393
394 encoder->setCompressLevel(conn->cp.compressLevel);
395 encoder->setQualityLevel(conn->cp.qualityLevel);
396 encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
397 conn->cp.subsampling);
398 }
399}
400
401int EncodeManager::computeNumRects(const Region& changed)
402{
403 int numRects;
404 std::vector<Rect> rects;
405 std::vector<Rect>::const_iterator rect;
406
407 numRects = 0;
408 changed.get_rects(&rects);
409 for (rect = rects.begin(); rect != rects.end(); ++rect) {
410 int w, h, sw, sh;
411
412 w = rect->width();
413 h = rect->height();
414
415 // No split necessary?
416 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
417 numRects += 1;
418 continue;
419 }
420
421 if (w <= SubRectMaxWidth)
422 sw = w;
423 else
424 sw = SubRectMaxWidth;
425
426 sh = SubRectMaxArea / sw;
427
428 // ceil(w/sw) * ceil(h/sh)
429 numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
430 }
431
432 return numRects;
433}
434
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100435Encoder *EncodeManager::startRect(const Rect& rect, int type)
436{
437 Encoder *encoder;
438 int klass, equiv;
439
440 activeType = type;
441 klass = activeEncoders[activeType];
442
443 beforeLength = conn->getOutStream()->length();
444
445 stats[klass][activeType].rects++;
446 stats[klass][activeType].pixels += rect.area();
447 equiv = 12 + rect.area() * conn->cp.pf().bpp/8;
448 stats[klass][activeType].equivalent += equiv;
449
450 encoder = encoders[klass];
451 conn->writer()->startRect(rect, encoder->encoding);
452
453 return encoder;
454}
455
456void EncodeManager::endRect()
457{
458 int klass;
459 int length;
460
461 conn->writer()->endRect();
462
463 length = conn->getOutStream()->length() - beforeLength;
464
465 klass = activeEncoders[activeType];
466 stats[klass][activeType].bytes += length;
467}
468
Pierre Ossmanc0397262014-03-14 15:59:46 +0100469void EncodeManager::writeCopyRects(const UpdateInfo& ui)
470{
471 std::vector<Rect> rects;
472 std::vector<Rect>::const_iterator rect;
473
Pierre Ossmane539cb82015-09-22 11:09:00 +0200474 beforeLength = conn->getOutStream()->length();
475
Pierre Ossmanc0397262014-03-14 15:59:46 +0100476 ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
477 for (rect = rects.begin(); rect != rects.end(); ++rect) {
Pierre Ossmane539cb82015-09-22 11:09:00 +0200478 int equiv;
479
480 copyStats.rects++;
481 copyStats.pixels += rect->area();
482 equiv = 12 + rect->area() * conn->cp.pf().bpp/8;
483 copyStats.equivalent += equiv;
484
Pierre Ossmanc0397262014-03-14 15:59:46 +0100485 conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
486 rect->tl.y - ui.copy_delta.y);
487 }
Pierre Ossmane539cb82015-09-22 11:09:00 +0200488
489 copyStats.bytes += conn->getOutStream()->length() - beforeLength;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100490}
491
492void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
493{
494 std::vector<Rect> rects;
495 std::vector<Rect>::const_iterator rect;
496
Pierre Ossmanc0397262014-03-14 15:59:46 +0100497 changed->get_rects(&rects);
Pierre Ossmaneef55162015-02-12 13:44:22 +0100498 for (rect = rects.begin(); rect != rects.end(); ++rect)
499 findSolidRect(*rect, changed, pb);
500}
Pierre Ossmanc0397262014-03-14 15:59:46 +0100501
Pierre Ossmaneef55162015-02-12 13:44:22 +0100502void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
503 const PixelBuffer* pb)
504{
505 Rect sr;
506 int dx, dy, dw, dh;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100507
Pierre Ossmaneef55162015-02-12 13:44:22 +0100508 // We start by finding a solid 16x16 block
509 for (dy = rect.tl.y; dy < rect.br.y; dy += SolidSearchBlock) {
Pierre Ossmanc0397262014-03-14 15:59:46 +0100510
Pierre Ossmaneef55162015-02-12 13:44:22 +0100511 dh = SolidSearchBlock;
512 if (dy + dh > rect.br.y)
513 dh = rect.br.y - dy;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100514
Pierre Ossmaneef55162015-02-12 13:44:22 +0100515 for (dx = rect.tl.x; dx < rect.br.x; dx += SolidSearchBlock) {
516 // We define it like this to guarantee alignment
517 rdr::U32 _buffer;
518 rdr::U8* colourValue = (rdr::U8*)&_buffer;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100519
Pierre Ossmaneef55162015-02-12 13:44:22 +0100520 dw = SolidSearchBlock;
521 if (dx + dw > rect.br.x)
522 dw = rect.br.x - dx;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100523
Pierre Ossmaneef55162015-02-12 13:44:22 +0100524 pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
Pierre Ossmanc0397262014-03-14 15:59:46 +0100525
Pierre Ossmaneef55162015-02-12 13:44:22 +0100526 sr.setXYWH(dx, dy, dw, dh);
527 if (checkSolidTile(sr, colourValue, pb)) {
528 Rect erb, erp;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100529
Pierre Ossmaneef55162015-02-12 13:44:22 +0100530 Encoder *encoder;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100531
Pierre Ossmaneef55162015-02-12 13:44:22 +0100532 // We then try extending the area by adding more blocks
533 // in both directions and pick the combination that gives
534 // the largest area.
535 sr.setXYWH(dx, dy, rect.br.x - dx, rect.br.y - dy);
536 extendSolidAreaByBlock(sr, colourValue, pb, &erb);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100537
Pierre Ossmaneef55162015-02-12 13:44:22 +0100538 // Did we end up getting the entire rectangle?
539 if (erb.equals(rect))
540 erp = erb;
541 else {
542 // Don't bother with sending tiny rectangles
543 if (erb.area() < SolidBlockMinArea)
544 continue;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100545
Pierre Ossmaneef55162015-02-12 13:44:22 +0100546 // Extend the area again, but this time one pixel
547 // row/column at a time.
548 extendSolidAreaByPixel(rect, erb, colourValue, pb, &erp);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100549 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100550
Pierre Ossmaneef55162015-02-12 13:44:22 +0100551 // Send solid-color rectangle.
552 encoder = startRect(erp, encoderSolid);
553 if (encoder->flags & EncoderUseNativePF) {
554 encoder->writeSolidRect(erp.width(), erp.height(),
555 pb->getPF(), colourValue);
556 } else {
557 rdr::U32 _buffer2;
558 rdr::U8* converted = (rdr::U8*)&_buffer2;
559
560 conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
561 colourValue, 1);
562
563 encoder->writeSolidRect(erp.width(), erp.height(),
564 conn->cp.pf(), converted);
565 }
566 endRect();
567
568 changed->assign_subtract(Region(erp));
569
570 // Search remaining areas by recursion
571 // FIXME: Is this the best way to divide things up?
572
573 // Left? (Note that we've already searched a SolidSearchBlock
574 // pixels high strip here)
575 if ((erp.tl.x != rect.tl.x) && (erp.height() > SolidSearchBlock)) {
576 sr.setXYWH(rect.tl.x, erp.tl.y + SolidSearchBlock,
577 erp.tl.x - rect.tl.x, erp.height() - SolidSearchBlock);
578 findSolidRect(sr, changed, pb);
579 }
580
581 // Right?
582 if (erp.br.x != rect.br.x) {
583 sr.setXYWH(erp.br.x, erp.tl.y, rect.br.x - erp.br.x, erp.height());
584 findSolidRect(sr, changed, pb);
585 }
586
587 // Below?
588 if (erp.br.y != rect.br.y) {
589 sr.setXYWH(rect.tl.x, erp.br.y, rect.width(), rect.br.y - erp.br.y);
590 findSolidRect(sr, changed, pb);
591 }
592
593 return;
594 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100595 }
596 }
597}
598
599void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
600{
601 std::vector<Rect> rects;
602 std::vector<Rect>::const_iterator rect;
603
604 changed.get_rects(&rects);
605 for (rect = rects.begin(); rect != rects.end(); ++rect) {
606 int w, h, sw, sh;
607 Rect sr;
608
609 w = rect->width();
610 h = rect->height();
611
612 // No split necessary?
613 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
614 writeSubRect(*rect, pb);
615 continue;
616 }
617
618 if (w <= SubRectMaxWidth)
619 sw = w;
620 else
621 sw = SubRectMaxWidth;
622
623 sh = SubRectMaxArea / sw;
624
625 for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
626 sr.br.y = sr.tl.y + sh;
627 if (sr.br.y > rect->br.y)
628 sr.br.y = rect->br.y;
629
630 for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
631 sr.br.x = sr.tl.x + sw;
632 if (sr.br.x > rect->br.x)
633 sr.br.x = rect->br.x;
634
635 writeSubRect(sr, pb);
636 }
637 }
638 }
639}
640
641void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
642{
643 PixelBuffer *ppb;
644
645 Encoder *encoder;
646
647 struct RectInfo info;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100648 unsigned int divisor, maxColours;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100649
650 bool useRLE;
651 EncoderType type;
652
653 // FIXME: This is roughly the algorithm previously used by the Tight
654 // encoder. It seems a bit backwards though, that higher
655 // compression setting means spending less effort in building
656 // a palette. It might be that they figured the increase in
657 // zlib setting compensated for the loss.
658 if (conn->cp.compressLevel == -1)
659 divisor = 2 * 8;
660 else
661 divisor = conn->cp.compressLevel * 8;
662 if (divisor < 4)
663 divisor = 4;
664
665 maxColours = rect.area()/divisor;
666
667 // Special exception inherited from the Tight encoder
668 if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
Pierre Ossman4daa7b12015-02-12 14:57:05 +0100669 if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2))
Pierre Ossmanc0397262014-03-14 15:59:46 +0100670 maxColours = 24;
671 else
672 maxColours = 96;
673 }
674
675 if (maxColours < 2)
676 maxColours = 2;
677
678 encoder = encoders[activeEncoders[encoderIndexedRLE]];
679 if (maxColours > encoder->maxPaletteSize)
680 maxColours = encoder->maxPaletteSize;
681 encoder = encoders[activeEncoders[encoderIndexed]];
682 if (maxColours > encoder->maxPaletteSize)
683 maxColours = encoder->maxPaletteSize;
684
685 ppb = preparePixelBuffer(rect, pb, true);
686
687 if (!analyseRect(ppb, &info, maxColours))
688 info.palette.clear();
689
690 // Different encoders might have different RLE overhead, but
691 // here we do a guess at RLE being the better choice if reduces
692 // the pixel count by 50%.
693 useRLE = info.rleRuns <= (rect.area() * 2);
694
695 switch (info.palette.size()) {
696 case 0:
697 type = encoderFullColour;
698 break;
699 case 1:
700 type = encoderSolid;
701 break;
702 case 2:
703 if (useRLE)
704 type = encoderBitmapRLE;
705 else
706 type = encoderBitmap;
707 break;
708 default:
709 if (useRLE)
710 type = encoderIndexedRLE;
711 else
712 type = encoderIndexed;
713 }
714
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100715 encoder = startRect(rect, type);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100716
717 if (encoder->flags & EncoderUseNativePF)
718 ppb = preparePixelBuffer(rect, pb, false);
719
Pierre Ossmanc0397262014-03-14 15:59:46 +0100720 encoder->writeRect(ppb, info.palette);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100721
722 endRect();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100723}
724
725bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
726 const PixelBuffer *pb)
727{
728 switch (pb->getPF().bpp) {
729 case 32:
730 return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
731 case 16:
732 return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
733 default:
734 return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
735 }
736}
737
738void EncodeManager::extendSolidAreaByBlock(const Rect& r,
739 const rdr::U8* colourValue,
740 const PixelBuffer *pb, Rect* er)
741{
742 int dx, dy, dw, dh;
743 int w_prev;
744 Rect sr;
745 int w_best = 0, h_best = 0;
746
747 w_prev = r.width();
748
749 // We search width first, back off when we hit a different colour,
750 // and restart with a larger height. We keep track of the
751 // width/height combination that gives us the largest area.
752 for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
753
754 dh = SolidSearchBlock;
755 if (dy + dh > r.br.y)
756 dh = r.br.y - dy;
757
758 // We test one block here outside the x loop in order to break
759 // the y loop right away.
760 dw = SolidSearchBlock;
761 if (dw > w_prev)
762 dw = w_prev;
763
764 sr.setXYWH(r.tl.x, dy, dw, dh);
765 if (!checkSolidTile(sr, colourValue, pb))
766 break;
767
768 for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
769
770 dw = SolidSearchBlock;
771 if (dx + dw > r.tl.x + w_prev)
772 dw = r.tl.x + w_prev - dx;
773
774 sr.setXYWH(dx, dy, dw, dh);
775 if (!checkSolidTile(sr, colourValue, pb))
776 break;
777
778 dx += dw;
779 }
780
781 w_prev = dx - r.tl.x;
782 if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
783 w_best = w_prev;
784 h_best = dy + dh - r.tl.y;
785 }
786 }
787
788 er->tl.x = r.tl.x;
789 er->tl.y = r.tl.y;
790 er->br.x = er->tl.x + w_best;
791 er->br.y = er->tl.y + h_best;
792}
793
794void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
795 const rdr::U8* colourValue,
796 const PixelBuffer *pb, Rect* er)
797{
798 int cx, cy;
799 Rect tr;
800
801 // Try to extend the area upwards.
802 for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
803 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
804 if (!checkSolidTile(tr, colourValue, pb))
805 break;
806 }
807 er->tl.y = cy + 1;
808
809 // ... downwards.
810 for (cy = sr.br.y; cy < r.br.y; cy++) {
811 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
812 if (!checkSolidTile(tr, colourValue, pb))
813 break;
814 }
815 er->br.y = cy;
816
817 // ... to the left.
818 for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
819 tr.setXYWH(cx, er->tl.y, 1, er->height());
820 if (!checkSolidTile(tr, colourValue, pb))
821 break;
822 }
823 er->tl.x = cx + 1;
824
825 // ... to the right.
826 for (cx = sr.br.x; cx < r.br.x; cx++) {
827 tr.setXYWH(cx, er->tl.y, 1, er->height());
828 if (!checkSolidTile(tr, colourValue, pb))
829 break;
830 }
831 er->br.x = cx;
832}
833
834PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
835 const PixelBuffer *pb,
836 bool convert)
837{
838 const rdr::U8* buffer;
839 int stride;
840
841 // Do wo need to convert the data?
842 if (convert && !conn->cp.pf().equal(pb->getPF())) {
843 convertedPixelBuffer.setPF(conn->cp.pf());
844 convertedPixelBuffer.setSize(rect.width(), rect.height());
845
846 buffer = pb->getBuffer(rect, &stride);
847 convertedPixelBuffer.imageRect(pb->getPF(),
848 convertedPixelBuffer.getRect(),
849 buffer, stride);
850
851 return &convertedPixelBuffer;
852 }
853
854 // Otherwise we still need to shift the coordinates. We have our own
855 // abusive subclass of FullFramePixelBuffer for this.
856
857 buffer = pb->getBuffer(rect, &stride);
858
859 offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
860 buffer, stride);
861
862 return &offsetPixelBuffer;
863}
864
865bool EncodeManager::analyseRect(const PixelBuffer *pb,
866 struct RectInfo *info, int maxColours)
867{
868 const rdr::U8* buffer;
869 int stride;
870
871 buffer = pb->getBuffer(pb->getRect(), &stride);
872
873 switch (pb->getPF().bpp) {
874 case 32:
875 return analyseRect(pb->width(), pb->height(),
876 (const rdr::U32*)buffer, stride,
877 info, maxColours);
878 case 16:
879 return analyseRect(pb->width(), pb->height(),
880 (const rdr::U16*)buffer, stride,
881 info, maxColours);
882 default:
883 return analyseRect(pb->width(), pb->height(),
884 (const rdr::U8*)buffer, stride,
885 info, maxColours);
886 }
887}
888
889void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
890 int width, int height,
891 const rdr::U8* data_,
892 int stride_)
893{
894 format = pf;
895 width_ = width;
896 height_ = height;
897 // Forced cast. We never write anything though, so it should be safe.
898 data = (rdr::U8*)data_;
899 stride = stride_;
900}
901
902// Preprocessor generated, optimised methods
903
904#define BPP 8
905#include "EncodeManagerBPP.cxx"
906#undef BPP
907#define BPP 16
908#include "EncodeManagerBPP.cxx"
909#undef BPP
910#define BPP 32
911#include "EncodeManagerBPP.cxx"
912#undef BPP