blob: 0cd5206023b82b50ac865a4e063214340ebcfd01 [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.
3 * Copyright 2014 Pierre Ossman for Cendio AB
4 *
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;
252 Region changed;
253
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100254 updates++;
255
Pierre Ossmanc0397262014-03-14 15:59:46 +0100256 prepareEncoders();
257
258 if (conn->cp.supportsLastRect)
259 nRects = 0xFFFF;
260 else {
261 nRects = ui.copied.numRects();
262 nRects += computeNumRects(ui.changed);
263
264 if (renderedCursor != NULL)
265 nRects += 1;
266 }
267
268 conn->writer()->writeFramebufferUpdateStart(nRects);
269
270 writeCopyRects(ui);
271
272 /*
273 * We start by searching for solid rects, which are then removed
274 * from the changed region.
275 */
276 changed.copyFrom(ui.changed);
277
278 if (conn->cp.supportsLastRect)
279 writeSolidRects(&changed, pb);
280
281 writeRects(changed, pb);
282
283 if (renderedCursor != NULL) {
284 Rect renderedCursorRect;
285
286 renderedCursorRect = renderedCursor->getEffectiveRect();
287 writeSubRect(renderedCursorRect, renderedCursor);
288 }
289
290 conn->writer()->writeFramebufferUpdateEnd();
291}
292
293void EncodeManager::prepareEncoders()
294{
295 enum EncoderClass solid, bitmap, bitmapRLE;
296 enum EncoderClass indexed, indexedRLE, fullColour;
297
298 rdr::S32 preferred;
299
300 std::vector<int>::iterator iter;
301
302 solid = bitmap = bitmapRLE = encoderRaw;
303 indexed = indexedRLE = fullColour = encoderRaw;
304
305 // Try to respect the client's wishes
Pierre Ossman48700812014-09-17 17:11:56 +0200306 preferred = conn->getPreferredEncoding();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100307 switch (preferred) {
308 case encodingRRE:
309 // Horrible for anything high frequency and/or lots of colours
310 bitmapRLE = indexedRLE = encoderRRE;
311 break;
312 case encodingHextile:
313 // Slightly less horrible
314 bitmapRLE = indexedRLE = fullColour = encoderHextile;
315 break;
316 case encodingTight:
317 if (encoders[encoderTightJPEG]->isSupported() &&
318 (conn->cp.pf().bpp >= 16))
319 fullColour = encoderTightJPEG;
320 else
321 fullColour = encoderTight;
322 indexed = indexedRLE = encoderTight;
323 bitmap = bitmapRLE = encoderTight;
324 break;
325 case encodingZRLE:
326 fullColour = encoderZRLE;
327 bitmapRLE = indexedRLE = encoderZRLE;
328 bitmap = indexed = encoderZRLE;
329 break;
330 }
331
332 // Any encoders still unassigned?
333
334 if (fullColour == encoderRaw) {
335 if (encoders[encoderTightJPEG]->isSupported() &&
336 (conn->cp.pf().bpp >= 16))
337 fullColour = encoderTightJPEG;
338 else if (encoders[encoderZRLE]->isSupported())
339 fullColour = encoderZRLE;
340 else if (encoders[encoderTight]->isSupported())
341 fullColour = encoderTight;
342 else if (encoders[encoderHextile]->isSupported())
343 fullColour = encoderHextile;
344 }
345
346 if (indexed == encoderRaw) {
347 if (encoders[encoderZRLE]->isSupported())
348 indexed = encoderZRLE;
349 else if (encoders[encoderTight]->isSupported())
350 indexed = encoderTight;
351 else if (encoders[encoderHextile]->isSupported())
352 indexed = encoderHextile;
353 }
354
355 if (indexedRLE == encoderRaw)
356 indexedRLE = indexed;
357
358 if (bitmap == encoderRaw)
359 bitmap = indexed;
360 if (bitmapRLE == encoderRaw)
361 bitmapRLE = bitmap;
362
363 if (solid == encoderRaw) {
364 if (encoders[encoderTight]->isSupported())
365 solid = encoderTight;
366 else if (encoders[encoderRRE]->isSupported())
367 solid = encoderRRE;
368 else if (encoders[encoderZRLE]->isSupported())
369 solid = encoderZRLE;
370 else if (encoders[encoderHextile]->isSupported())
371 solid = encoderHextile;
372 }
373
374 // JPEG is the only encoder that can reduce things to grayscale
375 if ((conn->cp.subsampling == subsampleGray) &&
376 encoders[encoderTightJPEG]->isSupported()) {
377 solid = bitmap = bitmapRLE = encoderTightJPEG;
378 indexed = indexedRLE = fullColour = encoderTightJPEG;
379 }
380
381 activeEncoders[encoderSolid] = solid;
382 activeEncoders[encoderBitmap] = bitmap;
383 activeEncoders[encoderBitmapRLE] = bitmapRLE;
384 activeEncoders[encoderIndexed] = indexed;
385 activeEncoders[encoderIndexedRLE] = indexedRLE;
386 activeEncoders[encoderFullColour] = fullColour;
387
388 for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
389 Encoder *encoder;
390
391 encoder = encoders[*iter];
392
393 encoder->setCompressLevel(conn->cp.compressLevel);
394 encoder->setQualityLevel(conn->cp.qualityLevel);
395 encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
396 conn->cp.subsampling);
397 }
398}
399
400int EncodeManager::computeNumRects(const Region& changed)
401{
402 int numRects;
403 std::vector<Rect> rects;
404 std::vector<Rect>::const_iterator rect;
405
406 numRects = 0;
407 changed.get_rects(&rects);
408 for (rect = rects.begin(); rect != rects.end(); ++rect) {
409 int w, h, sw, sh;
410
411 w = rect->width();
412 h = rect->height();
413
414 // No split necessary?
415 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
416 numRects += 1;
417 continue;
418 }
419
420 if (w <= SubRectMaxWidth)
421 sw = w;
422 else
423 sw = SubRectMaxWidth;
424
425 sh = SubRectMaxArea / sw;
426
427 // ceil(w/sw) * ceil(h/sh)
428 numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
429 }
430
431 return numRects;
432}
433
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100434Encoder *EncodeManager::startRect(const Rect& rect, int type)
435{
436 Encoder *encoder;
437 int klass, equiv;
438
439 activeType = type;
440 klass = activeEncoders[activeType];
441
442 beforeLength = conn->getOutStream()->length();
443
444 stats[klass][activeType].rects++;
445 stats[klass][activeType].pixels += rect.area();
446 equiv = 12 + rect.area() * conn->cp.pf().bpp/8;
447 stats[klass][activeType].equivalent += equiv;
448
449 encoder = encoders[klass];
450 conn->writer()->startRect(rect, encoder->encoding);
451
452 return encoder;
453}
454
455void EncodeManager::endRect()
456{
457 int klass;
458 int length;
459
460 conn->writer()->endRect();
461
462 length = conn->getOutStream()->length() - beforeLength;
463
464 klass = activeEncoders[activeType];
465 stats[klass][activeType].bytes += length;
466}
467
Pierre Ossmanc0397262014-03-14 15:59:46 +0100468void EncodeManager::writeCopyRects(const UpdateInfo& ui)
469{
470 std::vector<Rect> rects;
471 std::vector<Rect>::const_iterator rect;
472
Pierre Ossmane539cb82015-09-22 11:09:00 +0200473 beforeLength = conn->getOutStream()->length();
474
Pierre Ossmanc0397262014-03-14 15:59:46 +0100475 ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
476 for (rect = rects.begin(); rect != rects.end(); ++rect) {
Pierre Ossmane539cb82015-09-22 11:09:00 +0200477 int equiv;
478
479 copyStats.rects++;
480 copyStats.pixels += rect->area();
481 equiv = 12 + rect->area() * conn->cp.pf().bpp/8;
482 copyStats.equivalent += equiv;
483
Pierre Ossmanc0397262014-03-14 15:59:46 +0100484 conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
485 rect->tl.y - ui.copy_delta.y);
486 }
Pierre Ossmane539cb82015-09-22 11:09:00 +0200487
488 copyStats.bytes += conn->getOutStream()->length() - beforeLength;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100489}
490
491void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
492{
493 std::vector<Rect> rects;
494 std::vector<Rect>::const_iterator rect;
495
Pierre Ossmanc0397262014-03-14 15:59:46 +0100496 changed->get_rects(&rects);
Pierre Ossmaneef55162015-02-12 13:44:22 +0100497 for (rect = rects.begin(); rect != rects.end(); ++rect)
498 findSolidRect(*rect, changed, pb);
499}
Pierre Ossmanc0397262014-03-14 15:59:46 +0100500
Pierre Ossmaneef55162015-02-12 13:44:22 +0100501void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
502 const PixelBuffer* pb)
503{
504 Rect sr;
505 int dx, dy, dw, dh;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100506
Pierre Ossmaneef55162015-02-12 13:44:22 +0100507 // We start by finding a solid 16x16 block
508 for (dy = rect.tl.y; dy < rect.br.y; dy += SolidSearchBlock) {
Pierre Ossmanc0397262014-03-14 15:59:46 +0100509
Pierre Ossmaneef55162015-02-12 13:44:22 +0100510 dh = SolidSearchBlock;
511 if (dy + dh > rect.br.y)
512 dh = rect.br.y - dy;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100513
Pierre Ossmaneef55162015-02-12 13:44:22 +0100514 for (dx = rect.tl.x; dx < rect.br.x; dx += SolidSearchBlock) {
515 // We define it like this to guarantee alignment
516 rdr::U32 _buffer;
517 rdr::U8* colourValue = (rdr::U8*)&_buffer;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100518
Pierre Ossmaneef55162015-02-12 13:44:22 +0100519 dw = SolidSearchBlock;
520 if (dx + dw > rect.br.x)
521 dw = rect.br.x - dx;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100522
Pierre Ossmaneef55162015-02-12 13:44:22 +0100523 pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
Pierre Ossmanc0397262014-03-14 15:59:46 +0100524
Pierre Ossmaneef55162015-02-12 13:44:22 +0100525 sr.setXYWH(dx, dy, dw, dh);
526 if (checkSolidTile(sr, colourValue, pb)) {
527 Rect erb, erp;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100528
Pierre Ossmaneef55162015-02-12 13:44:22 +0100529 Encoder *encoder;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100530
Pierre Ossmaneef55162015-02-12 13:44:22 +0100531 // We then try extending the area by adding more blocks
532 // in both directions and pick the combination that gives
533 // the largest area.
534 sr.setXYWH(dx, dy, rect.br.x - dx, rect.br.y - dy);
535 extendSolidAreaByBlock(sr, colourValue, pb, &erb);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100536
Pierre Ossmaneef55162015-02-12 13:44:22 +0100537 // Did we end up getting the entire rectangle?
538 if (erb.equals(rect))
539 erp = erb;
540 else {
541 // Don't bother with sending tiny rectangles
542 if (erb.area() < SolidBlockMinArea)
543 continue;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100544
Pierre Ossmaneef55162015-02-12 13:44:22 +0100545 // Extend the area again, but this time one pixel
546 // row/column at a time.
547 extendSolidAreaByPixel(rect, erb, colourValue, pb, &erp);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100548 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100549
Pierre Ossmaneef55162015-02-12 13:44:22 +0100550 // Send solid-color rectangle.
551 encoder = startRect(erp, encoderSolid);
552 if (encoder->flags & EncoderUseNativePF) {
553 encoder->writeSolidRect(erp.width(), erp.height(),
554 pb->getPF(), colourValue);
555 } else {
556 rdr::U32 _buffer2;
557 rdr::U8* converted = (rdr::U8*)&_buffer2;
558
559 conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
560 colourValue, 1);
561
562 encoder->writeSolidRect(erp.width(), erp.height(),
563 conn->cp.pf(), converted);
564 }
565 endRect();
566
567 changed->assign_subtract(Region(erp));
568
569 // Search remaining areas by recursion
570 // FIXME: Is this the best way to divide things up?
571
572 // Left? (Note that we've already searched a SolidSearchBlock
573 // pixels high strip here)
574 if ((erp.tl.x != rect.tl.x) && (erp.height() > SolidSearchBlock)) {
575 sr.setXYWH(rect.tl.x, erp.tl.y + SolidSearchBlock,
576 erp.tl.x - rect.tl.x, erp.height() - SolidSearchBlock);
577 findSolidRect(sr, changed, pb);
578 }
579
580 // Right?
581 if (erp.br.x != rect.br.x) {
582 sr.setXYWH(erp.br.x, erp.tl.y, rect.br.x - erp.br.x, erp.height());
583 findSolidRect(sr, changed, pb);
584 }
585
586 // Below?
587 if (erp.br.y != rect.br.y) {
588 sr.setXYWH(rect.tl.x, erp.br.y, rect.width(), rect.br.y - erp.br.y);
589 findSolidRect(sr, changed, pb);
590 }
591
592 return;
593 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100594 }
595 }
596}
597
598void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
599{
600 std::vector<Rect> rects;
601 std::vector<Rect>::const_iterator rect;
602
603 changed.get_rects(&rects);
604 for (rect = rects.begin(); rect != rects.end(); ++rect) {
605 int w, h, sw, sh;
606 Rect sr;
607
608 w = rect->width();
609 h = rect->height();
610
611 // No split necessary?
612 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
613 writeSubRect(*rect, pb);
614 continue;
615 }
616
617 if (w <= SubRectMaxWidth)
618 sw = w;
619 else
620 sw = SubRectMaxWidth;
621
622 sh = SubRectMaxArea / sw;
623
624 for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
625 sr.br.y = sr.tl.y + sh;
626 if (sr.br.y > rect->br.y)
627 sr.br.y = rect->br.y;
628
629 for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
630 sr.br.x = sr.tl.x + sw;
631 if (sr.br.x > rect->br.x)
632 sr.br.x = rect->br.x;
633
634 writeSubRect(sr, pb);
635 }
636 }
637 }
638}
639
640void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
641{
642 PixelBuffer *ppb;
643
644 Encoder *encoder;
645
646 struct RectInfo info;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100647 unsigned int divisor, maxColours;
Pierre Ossmanc0397262014-03-14 15:59:46 +0100648
649 bool useRLE;
650 EncoderType type;
651
652 // FIXME: This is roughly the algorithm previously used by the Tight
653 // encoder. It seems a bit backwards though, that higher
654 // compression setting means spending less effort in building
655 // a palette. It might be that they figured the increase in
656 // zlib setting compensated for the loss.
657 if (conn->cp.compressLevel == -1)
658 divisor = 2 * 8;
659 else
660 divisor = conn->cp.compressLevel * 8;
661 if (divisor < 4)
662 divisor = 4;
663
664 maxColours = rect.area()/divisor;
665
666 // Special exception inherited from the Tight encoder
667 if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
Pierre Ossman4daa7b12015-02-12 14:57:05 +0100668 if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2))
Pierre Ossmanc0397262014-03-14 15:59:46 +0100669 maxColours = 24;
670 else
671 maxColours = 96;
672 }
673
674 if (maxColours < 2)
675 maxColours = 2;
676
677 encoder = encoders[activeEncoders[encoderIndexedRLE]];
678 if (maxColours > encoder->maxPaletteSize)
679 maxColours = encoder->maxPaletteSize;
680 encoder = encoders[activeEncoders[encoderIndexed]];
681 if (maxColours > encoder->maxPaletteSize)
682 maxColours = encoder->maxPaletteSize;
683
684 ppb = preparePixelBuffer(rect, pb, true);
685
686 if (!analyseRect(ppb, &info, maxColours))
687 info.palette.clear();
688
689 // Different encoders might have different RLE overhead, but
690 // here we do a guess at RLE being the better choice if reduces
691 // the pixel count by 50%.
692 useRLE = info.rleRuns <= (rect.area() * 2);
693
694 switch (info.palette.size()) {
695 case 0:
696 type = encoderFullColour;
697 break;
698 case 1:
699 type = encoderSolid;
700 break;
701 case 2:
702 if (useRLE)
703 type = encoderBitmapRLE;
704 else
705 type = encoderBitmap;
706 break;
707 default:
708 if (useRLE)
709 type = encoderIndexedRLE;
710 else
711 type = encoderIndexed;
712 }
713
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100714 encoder = startRect(rect, type);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100715
716 if (encoder->flags & EncoderUseNativePF)
717 ppb = preparePixelBuffer(rect, pb, false);
718
Pierre Ossmanc0397262014-03-14 15:59:46 +0100719 encoder->writeRect(ppb, info.palette);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100720
721 endRect();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100722}
723
724bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
725 const PixelBuffer *pb)
726{
727 switch (pb->getPF().bpp) {
728 case 32:
729 return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
730 case 16:
731 return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
732 default:
733 return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
734 }
735}
736
737void EncodeManager::extendSolidAreaByBlock(const Rect& r,
738 const rdr::U8* colourValue,
739 const PixelBuffer *pb, Rect* er)
740{
741 int dx, dy, dw, dh;
742 int w_prev;
743 Rect sr;
744 int w_best = 0, h_best = 0;
745
746 w_prev = r.width();
747
748 // We search width first, back off when we hit a different colour,
749 // and restart with a larger height. We keep track of the
750 // width/height combination that gives us the largest area.
751 for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
752
753 dh = SolidSearchBlock;
754 if (dy + dh > r.br.y)
755 dh = r.br.y - dy;
756
757 // We test one block here outside the x loop in order to break
758 // the y loop right away.
759 dw = SolidSearchBlock;
760 if (dw > w_prev)
761 dw = w_prev;
762
763 sr.setXYWH(r.tl.x, dy, dw, dh);
764 if (!checkSolidTile(sr, colourValue, pb))
765 break;
766
767 for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
768
769 dw = SolidSearchBlock;
770 if (dx + dw > r.tl.x + w_prev)
771 dw = r.tl.x + w_prev - dx;
772
773 sr.setXYWH(dx, dy, dw, dh);
774 if (!checkSolidTile(sr, colourValue, pb))
775 break;
776
777 dx += dw;
778 }
779
780 w_prev = dx - r.tl.x;
781 if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
782 w_best = w_prev;
783 h_best = dy + dh - r.tl.y;
784 }
785 }
786
787 er->tl.x = r.tl.x;
788 er->tl.y = r.tl.y;
789 er->br.x = er->tl.x + w_best;
790 er->br.y = er->tl.y + h_best;
791}
792
793void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
794 const rdr::U8* colourValue,
795 const PixelBuffer *pb, Rect* er)
796{
797 int cx, cy;
798 Rect tr;
799
800 // Try to extend the area upwards.
801 for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
802 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
803 if (!checkSolidTile(tr, colourValue, pb))
804 break;
805 }
806 er->tl.y = cy + 1;
807
808 // ... downwards.
809 for (cy = sr.br.y; cy < r.br.y; cy++) {
810 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
811 if (!checkSolidTile(tr, colourValue, pb))
812 break;
813 }
814 er->br.y = cy;
815
816 // ... to the left.
817 for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
818 tr.setXYWH(cx, er->tl.y, 1, er->height());
819 if (!checkSolidTile(tr, colourValue, pb))
820 break;
821 }
822 er->tl.x = cx + 1;
823
824 // ... to the right.
825 for (cx = sr.br.x; cx < r.br.x; cx++) {
826 tr.setXYWH(cx, er->tl.y, 1, er->height());
827 if (!checkSolidTile(tr, colourValue, pb))
828 break;
829 }
830 er->br.x = cx;
831}
832
833PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
834 const PixelBuffer *pb,
835 bool convert)
836{
837 const rdr::U8* buffer;
838 int stride;
839
840 // Do wo need to convert the data?
841 if (convert && !conn->cp.pf().equal(pb->getPF())) {
842 convertedPixelBuffer.setPF(conn->cp.pf());
843 convertedPixelBuffer.setSize(rect.width(), rect.height());
844
845 buffer = pb->getBuffer(rect, &stride);
846 convertedPixelBuffer.imageRect(pb->getPF(),
847 convertedPixelBuffer.getRect(),
848 buffer, stride);
849
850 return &convertedPixelBuffer;
851 }
852
853 // Otherwise we still need to shift the coordinates. We have our own
854 // abusive subclass of FullFramePixelBuffer for this.
855
856 buffer = pb->getBuffer(rect, &stride);
857
858 offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
859 buffer, stride);
860
861 return &offsetPixelBuffer;
862}
863
864bool EncodeManager::analyseRect(const PixelBuffer *pb,
865 struct RectInfo *info, int maxColours)
866{
867 const rdr::U8* buffer;
868 int stride;
869
870 buffer = pb->getBuffer(pb->getRect(), &stride);
871
872 switch (pb->getPF().bpp) {
873 case 32:
874 return analyseRect(pb->width(), pb->height(),
875 (const rdr::U32*)buffer, stride,
876 info, maxColours);
877 case 16:
878 return analyseRect(pb->width(), pb->height(),
879 (const rdr::U16*)buffer, stride,
880 info, maxColours);
881 default:
882 return analyseRect(pb->width(), pb->height(),
883 (const rdr::U8*)buffer, stride,
884 info, maxColours);
885 }
886}
887
888void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
889 int width, int height,
890 const rdr::U8* data_,
891 int stride_)
892{
893 format = pf;
894 width_ = width;
895 height_ = height;
896 // Forced cast. We never write anything though, so it should be safe.
897 data = (rdr::U8*)data_;
898 stride = stride_;
899}
900
901// Preprocessor generated, optimised methods
902
903#define BPP 8
904#include "EncodeManagerBPP.cxx"
905#undef BPP
906#define BPP 16
907#include "EncodeManagerBPP.cxx"
908#undef BPP
909#define BPP 32
910#include "EncodeManagerBPP.cxx"
911#undef BPP