blob: cd80df97cca016a12c45827aad1ce8e589b14e73 [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";
94 }
95
96 return "Unknown Encoder Class";
97}
98
99static const char *encoderTypeName(EncoderType type)
100{
101 switch (type) {
102 case encoderSolid:
103 return "Solid";
104 case encoderBitmap:
105 return "Bitmap";
106 case encoderBitmapRLE:
107 return "Bitmap RLE";
108 case encoderIndexed:
109 return "Indexed";
110 case encoderIndexedRLE:
111 return "Indexed RLE";
112 case encoderFullColour:
113 return "Full Colour";
114 }
115
116 return "Unknown Encoder Type";
117}
118
Pierre Ossmanc0397262014-03-14 15:59:46 +0100119EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
120{
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100121 StatsVector::iterator iter;
122
Pierre Ossmanc0397262014-03-14 15:59:46 +0100123 encoders.resize(encoderClassMax, NULL);
124 activeEncoders.resize(encoderTypeMax, encoderRaw);
125
126 encoders[encoderRaw] = new RawEncoder(conn);
127 encoders[encoderRRE] = new RREEncoder(conn);
128 encoders[encoderHextile] = new HextileEncoder(conn);
129 encoders[encoderTight] = new TightEncoder(conn);
130 encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
131 encoders[encoderZRLE] = new ZRLEEncoder(conn);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100132
133 updates = 0;
134 stats.resize(encoderClassMax);
135 for (iter = stats.begin();iter != stats.end();++iter) {
136 StatsVector::value_type::iterator iter2;
137 iter->resize(encoderTypeMax);
138 for (iter2 = iter->begin();iter2 != iter->end();++iter2)
139 memset(&*iter2, 0, sizeof(EncoderStats));
140 }
Pierre Ossmanc0397262014-03-14 15:59:46 +0100141}
142
143EncodeManager::~EncodeManager()
144{
145 std::vector<Encoder*>::iterator iter;
146
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100147 logStats();
148
Pierre Ossmanc0397262014-03-14 15:59:46 +0100149 for (iter = encoders.begin();iter != encoders.end();iter++)
150 delete *iter;
151}
152
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100153void EncodeManager::logStats()
154{
155 int i, j;
156
157 unsigned rects;
158 unsigned long long pixels, bytes, equivalent;
159
160 double ratio;
161
162 rects = 0;
163 pixels = bytes = equivalent = 0;
164
165 vlog.info("Framebuffer updates: %u", updates);
166
167 for (i = 0;i < stats.size();i++) {
168 // Did this class do anything at all?
169 for (j = 0;j < stats[i].size();j++) {
170 if (stats[i][j].rects != 0)
171 break;
172 }
173 if (j == stats[i].size())
174 continue;
175
176 vlog.info(" %s:", encoderClassName((EncoderClass)i));
177
178 for (j = 0;j < stats[i].size();j++) {
179 if (stats[i][j].rects == 0)
180 continue;
181
182 rects += stats[i][j].rects;
183 pixels += stats[i][j].pixels;
184 bytes += stats[i][j].bytes;
185 equivalent += stats[i][j].equivalent;
186
187 ratio = (double)stats[i][j].equivalent / stats[i][j].bytes;
188
189 vlog.info(" %s: %u rects, %llu pixels",
190 encoderTypeName((EncoderType)j),
191 stats[i][j].rects, stats[i][j].pixels);
192 vlog.info(" %*s %llu bytes (%g ratio)",
193 strlen(encoderTypeName((EncoderType)j)), "",
194 stats[i][j].bytes, ratio);
195 }
196 }
197
198 ratio = (double)equivalent / bytes;
199
200 vlog.info(" Total: %u rects, %llu pixels", rects, pixels);
201 vlog.info(" %llu bytes (%g ratio)", bytes, ratio);
202}
203
Pierre Ossmanc0397262014-03-14 15:59:46 +0100204bool EncodeManager::supported(int encoding)
205{
206 switch (encoding) {
207 case encodingRaw:
208 case encodingRRE:
209 case encodingHextile:
210 case encodingZRLE:
211 case encodingTight:
212 return true;
213 default:
214 return false;
215 }
216}
217
218void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
219 const RenderedCursor* renderedCursor)
220{
221 int nRects;
222 Region changed;
223
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100224 updates++;
225
Pierre Ossmanc0397262014-03-14 15:59:46 +0100226 prepareEncoders();
227
228 if (conn->cp.supportsLastRect)
229 nRects = 0xFFFF;
230 else {
231 nRects = ui.copied.numRects();
232 nRects += computeNumRects(ui.changed);
233
234 if (renderedCursor != NULL)
235 nRects += 1;
236 }
237
238 conn->writer()->writeFramebufferUpdateStart(nRects);
239
240 writeCopyRects(ui);
241
242 /*
243 * We start by searching for solid rects, which are then removed
244 * from the changed region.
245 */
246 changed.copyFrom(ui.changed);
247
248 if (conn->cp.supportsLastRect)
249 writeSolidRects(&changed, pb);
250
251 writeRects(changed, pb);
252
253 if (renderedCursor != NULL) {
254 Rect renderedCursorRect;
255
256 renderedCursorRect = renderedCursor->getEffectiveRect();
257 writeSubRect(renderedCursorRect, renderedCursor);
258 }
259
260 conn->writer()->writeFramebufferUpdateEnd();
261}
262
263void EncodeManager::prepareEncoders()
264{
265 enum EncoderClass solid, bitmap, bitmapRLE;
266 enum EncoderClass indexed, indexedRLE, fullColour;
267
268 rdr::S32 preferred;
269
270 std::vector<int>::iterator iter;
271
272 solid = bitmap = bitmapRLE = encoderRaw;
273 indexed = indexedRLE = fullColour = encoderRaw;
274
275 // Try to respect the client's wishes
Pierre Ossman48700812014-09-17 17:11:56 +0200276 preferred = conn->getPreferredEncoding();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100277 switch (preferred) {
278 case encodingRRE:
279 // Horrible for anything high frequency and/or lots of colours
280 bitmapRLE = indexedRLE = encoderRRE;
281 break;
282 case encodingHextile:
283 // Slightly less horrible
284 bitmapRLE = indexedRLE = fullColour = encoderHextile;
285 break;
286 case encodingTight:
287 if (encoders[encoderTightJPEG]->isSupported() &&
288 (conn->cp.pf().bpp >= 16))
289 fullColour = encoderTightJPEG;
290 else
291 fullColour = encoderTight;
292 indexed = indexedRLE = encoderTight;
293 bitmap = bitmapRLE = encoderTight;
294 break;
295 case encodingZRLE:
296 fullColour = encoderZRLE;
297 bitmapRLE = indexedRLE = encoderZRLE;
298 bitmap = indexed = encoderZRLE;
299 break;
300 }
301
302 // Any encoders still unassigned?
303
304 if (fullColour == encoderRaw) {
305 if (encoders[encoderTightJPEG]->isSupported() &&
306 (conn->cp.pf().bpp >= 16))
307 fullColour = encoderTightJPEG;
308 else if (encoders[encoderZRLE]->isSupported())
309 fullColour = encoderZRLE;
310 else if (encoders[encoderTight]->isSupported())
311 fullColour = encoderTight;
312 else if (encoders[encoderHextile]->isSupported())
313 fullColour = encoderHextile;
314 }
315
316 if (indexed == encoderRaw) {
317 if (encoders[encoderZRLE]->isSupported())
318 indexed = encoderZRLE;
319 else if (encoders[encoderTight]->isSupported())
320 indexed = encoderTight;
321 else if (encoders[encoderHextile]->isSupported())
322 indexed = encoderHextile;
323 }
324
325 if (indexedRLE == encoderRaw)
326 indexedRLE = indexed;
327
328 if (bitmap == encoderRaw)
329 bitmap = indexed;
330 if (bitmapRLE == encoderRaw)
331 bitmapRLE = bitmap;
332
333 if (solid == encoderRaw) {
334 if (encoders[encoderTight]->isSupported())
335 solid = encoderTight;
336 else if (encoders[encoderRRE]->isSupported())
337 solid = encoderRRE;
338 else if (encoders[encoderZRLE]->isSupported())
339 solid = encoderZRLE;
340 else if (encoders[encoderHextile]->isSupported())
341 solid = encoderHextile;
342 }
343
344 // JPEG is the only encoder that can reduce things to grayscale
345 if ((conn->cp.subsampling == subsampleGray) &&
346 encoders[encoderTightJPEG]->isSupported()) {
347 solid = bitmap = bitmapRLE = encoderTightJPEG;
348 indexed = indexedRLE = fullColour = encoderTightJPEG;
349 }
350
351 activeEncoders[encoderSolid] = solid;
352 activeEncoders[encoderBitmap] = bitmap;
353 activeEncoders[encoderBitmapRLE] = bitmapRLE;
354 activeEncoders[encoderIndexed] = indexed;
355 activeEncoders[encoderIndexedRLE] = indexedRLE;
356 activeEncoders[encoderFullColour] = fullColour;
357
358 for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
359 Encoder *encoder;
360
361 encoder = encoders[*iter];
362
363 encoder->setCompressLevel(conn->cp.compressLevel);
364 encoder->setQualityLevel(conn->cp.qualityLevel);
365 encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
366 conn->cp.subsampling);
367 }
368}
369
370int EncodeManager::computeNumRects(const Region& changed)
371{
372 int numRects;
373 std::vector<Rect> rects;
374 std::vector<Rect>::const_iterator rect;
375
376 numRects = 0;
377 changed.get_rects(&rects);
378 for (rect = rects.begin(); rect != rects.end(); ++rect) {
379 int w, h, sw, sh;
380
381 w = rect->width();
382 h = rect->height();
383
384 // No split necessary?
385 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
386 numRects += 1;
387 continue;
388 }
389
390 if (w <= SubRectMaxWidth)
391 sw = w;
392 else
393 sw = SubRectMaxWidth;
394
395 sh = SubRectMaxArea / sw;
396
397 // ceil(w/sw) * ceil(h/sh)
398 numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
399 }
400
401 return numRects;
402}
403
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100404Encoder *EncodeManager::startRect(const Rect& rect, int type)
405{
406 Encoder *encoder;
407 int klass, equiv;
408
409 activeType = type;
410 klass = activeEncoders[activeType];
411
412 beforeLength = conn->getOutStream()->length();
413
414 stats[klass][activeType].rects++;
415 stats[klass][activeType].pixels += rect.area();
416 equiv = 12 + rect.area() * conn->cp.pf().bpp/8;
417 stats[klass][activeType].equivalent += equiv;
418
419 encoder = encoders[klass];
420 conn->writer()->startRect(rect, encoder->encoding);
421
422 return encoder;
423}
424
425void EncodeManager::endRect()
426{
427 int klass;
428 int length;
429
430 conn->writer()->endRect();
431
432 length = conn->getOutStream()->length() - beforeLength;
433
434 klass = activeEncoders[activeType];
435 stats[klass][activeType].bytes += length;
436}
437
Pierre Ossmanc0397262014-03-14 15:59:46 +0100438void EncodeManager::writeCopyRects(const UpdateInfo& ui)
439{
440 std::vector<Rect> rects;
441 std::vector<Rect>::const_iterator rect;
442
443 ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
444 for (rect = rects.begin(); rect != rects.end(); ++rect) {
445 conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
446 rect->tl.y - ui.copy_delta.y);
447 }
448}
449
450void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
451{
452 std::vector<Rect> rects;
453 std::vector<Rect>::const_iterator rect;
454
455 // FIXME: This gives up after the first rect it finds. A large update
456 // (like a whole screen refresh) might have lots of large solid
457 // areas.
458
459 changed->get_rects(&rects);
460 for (rect = rects.begin(); rect != rects.end(); ++rect) {
461 Rect sr;
462 int dx, dy, dw, dh;
463
464 // We start by finding a solid 16x16 block
465 for (dy = rect->tl.y; dy < rect->br.y; dy += SolidSearchBlock) {
466
467 dh = SolidSearchBlock;
468 if (dy + dh > rect->br.y)
469 dh = rect->br.y - dy;
470
471 for (dx = rect->tl.x; dx < rect->br.x; dx += SolidSearchBlock) {
472 // We define it like this to guarantee alignment
473 rdr::U32 _buffer;
474 rdr::U8* colourValue = (rdr::U8*)&_buffer;
475
476 dw = SolidSearchBlock;
477 if (dx + dw > rect->br.x)
478 dw = rect->br.x - dx;
479
480 pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
481
482 sr.setXYWH(dx, dy, dw, dh);
483 if (checkSolidTile(sr, colourValue, pb)) {
484 Rect erb, erp;
485
486 Encoder *encoder;
487
488 // We then try extending the area by adding more blocks
489 // in both directions and pick the combination that gives
490 // the largest area.
491 sr.setXYWH(dx, dy, rect->br.x - dx, rect->br.y - dy);
492 extendSolidAreaByBlock(sr, colourValue, pb, &erb);
493
494 // Did we end up getting the entire rectangle?
495 if (erb.equals(*rect))
496 erp = erb;
497 else {
498 // Don't bother with sending tiny rectangles
499 if (erb.area() < SolidBlockMinArea)
500 continue;
501
502 // Extend the area again, but this time one pixel
503 // row/column at a time.
504 extendSolidAreaByPixel(*rect, erb, colourValue, pb, &erp);
505 }
506
507 // Send solid-color rectangle.
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100508 encoder = startRect(erp, encoderSolid);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100509 if (encoder->flags & EncoderUseNativePF) {
510 encoder->writeSolidRect(erp.width(), erp.height(),
511 pb->getPF(), colourValue);
512 } else {
513 rdr::U32 _buffer2;
514 rdr::U8* converted = (rdr::U8*)&_buffer2;
515
516 conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
517 colourValue, 1);
518
519 encoder->writeSolidRect(erp.width(), erp.height(),
520 conn->cp.pf(), converted);
521 }
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100522 endRect();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100523
524 changed->assign_subtract(Region(erp));
525
526 break;
527 }
528 }
529
530 if (dx < rect->br.x)
531 break;
532 }
533 }
534}
535
536void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
537{
538 std::vector<Rect> rects;
539 std::vector<Rect>::const_iterator rect;
540
541 changed.get_rects(&rects);
542 for (rect = rects.begin(); rect != rects.end(); ++rect) {
543 int w, h, sw, sh;
544 Rect sr;
545
546 w = rect->width();
547 h = rect->height();
548
549 // No split necessary?
550 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
551 writeSubRect(*rect, pb);
552 continue;
553 }
554
555 if (w <= SubRectMaxWidth)
556 sw = w;
557 else
558 sw = SubRectMaxWidth;
559
560 sh = SubRectMaxArea / sw;
561
562 for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
563 sr.br.y = sr.tl.y + sh;
564 if (sr.br.y > rect->br.y)
565 sr.br.y = rect->br.y;
566
567 for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
568 sr.br.x = sr.tl.x + sw;
569 if (sr.br.x > rect->br.x)
570 sr.br.x = rect->br.x;
571
572 writeSubRect(sr, pb);
573 }
574 }
575 }
576}
577
578void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
579{
580 PixelBuffer *ppb;
581
582 Encoder *encoder;
583
584 struct RectInfo info;
585 int divisor, maxColours;
586
587 bool useRLE;
588 EncoderType type;
589
590 // FIXME: This is roughly the algorithm previously used by the Tight
591 // encoder. It seems a bit backwards though, that higher
592 // compression setting means spending less effort in building
593 // a palette. It might be that they figured the increase in
594 // zlib setting compensated for the loss.
595 if (conn->cp.compressLevel == -1)
596 divisor = 2 * 8;
597 else
598 divisor = conn->cp.compressLevel * 8;
599 if (divisor < 4)
600 divisor = 4;
601
602 maxColours = rect.area()/divisor;
603
604 // Special exception inherited from the Tight encoder
605 if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
606 if (conn->cp.compressLevel < 2)
607 maxColours = 24;
608 else
609 maxColours = 96;
610 }
611
612 if (maxColours < 2)
613 maxColours = 2;
614
615 encoder = encoders[activeEncoders[encoderIndexedRLE]];
616 if (maxColours > encoder->maxPaletteSize)
617 maxColours = encoder->maxPaletteSize;
618 encoder = encoders[activeEncoders[encoderIndexed]];
619 if (maxColours > encoder->maxPaletteSize)
620 maxColours = encoder->maxPaletteSize;
621
622 ppb = preparePixelBuffer(rect, pb, true);
623
624 if (!analyseRect(ppb, &info, maxColours))
625 info.palette.clear();
626
627 // Different encoders might have different RLE overhead, but
628 // here we do a guess at RLE being the better choice if reduces
629 // the pixel count by 50%.
630 useRLE = info.rleRuns <= (rect.area() * 2);
631
632 switch (info.palette.size()) {
633 case 0:
634 type = encoderFullColour;
635 break;
636 case 1:
637 type = encoderSolid;
638 break;
639 case 2:
640 if (useRLE)
641 type = encoderBitmapRLE;
642 else
643 type = encoderBitmap;
644 break;
645 default:
646 if (useRLE)
647 type = encoderIndexedRLE;
648 else
649 type = encoderIndexed;
650 }
651
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100652 encoder = startRect(rect, type);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100653
654 if (encoder->flags & EncoderUseNativePF)
655 ppb = preparePixelBuffer(rect, pb, false);
656
Pierre Ossmanc0397262014-03-14 15:59:46 +0100657 encoder->writeRect(ppb, info.palette);
Pierre Ossman20dd2a92015-02-11 17:43:15 +0100658
659 endRect();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100660}
661
662bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
663 const PixelBuffer *pb)
664{
665 switch (pb->getPF().bpp) {
666 case 32:
667 return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
668 case 16:
669 return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
670 default:
671 return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
672 }
673}
674
675void EncodeManager::extendSolidAreaByBlock(const Rect& r,
676 const rdr::U8* colourValue,
677 const PixelBuffer *pb, Rect* er)
678{
679 int dx, dy, dw, dh;
680 int w_prev;
681 Rect sr;
682 int w_best = 0, h_best = 0;
683
684 w_prev = r.width();
685
686 // We search width first, back off when we hit a different colour,
687 // and restart with a larger height. We keep track of the
688 // width/height combination that gives us the largest area.
689 for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
690
691 dh = SolidSearchBlock;
692 if (dy + dh > r.br.y)
693 dh = r.br.y - dy;
694
695 // We test one block here outside the x loop in order to break
696 // the y loop right away.
697 dw = SolidSearchBlock;
698 if (dw > w_prev)
699 dw = w_prev;
700
701 sr.setXYWH(r.tl.x, dy, dw, dh);
702 if (!checkSolidTile(sr, colourValue, pb))
703 break;
704
705 for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
706
707 dw = SolidSearchBlock;
708 if (dx + dw > r.tl.x + w_prev)
709 dw = r.tl.x + w_prev - dx;
710
711 sr.setXYWH(dx, dy, dw, dh);
712 if (!checkSolidTile(sr, colourValue, pb))
713 break;
714
715 dx += dw;
716 }
717
718 w_prev = dx - r.tl.x;
719 if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
720 w_best = w_prev;
721 h_best = dy + dh - r.tl.y;
722 }
723 }
724
725 er->tl.x = r.tl.x;
726 er->tl.y = r.tl.y;
727 er->br.x = er->tl.x + w_best;
728 er->br.y = er->tl.y + h_best;
729}
730
731void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
732 const rdr::U8* colourValue,
733 const PixelBuffer *pb, Rect* er)
734{
735 int cx, cy;
736 Rect tr;
737
738 // Try to extend the area upwards.
739 for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
740 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
741 if (!checkSolidTile(tr, colourValue, pb))
742 break;
743 }
744 er->tl.y = cy + 1;
745
746 // ... downwards.
747 for (cy = sr.br.y; cy < r.br.y; cy++) {
748 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
749 if (!checkSolidTile(tr, colourValue, pb))
750 break;
751 }
752 er->br.y = cy;
753
754 // ... to the left.
755 for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
756 tr.setXYWH(cx, er->tl.y, 1, er->height());
757 if (!checkSolidTile(tr, colourValue, pb))
758 break;
759 }
760 er->tl.x = cx + 1;
761
762 // ... to the right.
763 for (cx = sr.br.x; cx < r.br.x; cx++) {
764 tr.setXYWH(cx, er->tl.y, 1, er->height());
765 if (!checkSolidTile(tr, colourValue, pb))
766 break;
767 }
768 er->br.x = cx;
769}
770
771PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
772 const PixelBuffer *pb,
773 bool convert)
774{
775 const rdr::U8* buffer;
776 int stride;
777
778 // Do wo need to convert the data?
779 if (convert && !conn->cp.pf().equal(pb->getPF())) {
780 convertedPixelBuffer.setPF(conn->cp.pf());
781 convertedPixelBuffer.setSize(rect.width(), rect.height());
782
783 buffer = pb->getBuffer(rect, &stride);
784 convertedPixelBuffer.imageRect(pb->getPF(),
785 convertedPixelBuffer.getRect(),
786 buffer, stride);
787
788 return &convertedPixelBuffer;
789 }
790
791 // Otherwise we still need to shift the coordinates. We have our own
792 // abusive subclass of FullFramePixelBuffer for this.
793
794 buffer = pb->getBuffer(rect, &stride);
795
796 offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
797 buffer, stride);
798
799 return &offsetPixelBuffer;
800}
801
802bool EncodeManager::analyseRect(const PixelBuffer *pb,
803 struct RectInfo *info, int maxColours)
804{
805 const rdr::U8* buffer;
806 int stride;
807
808 buffer = pb->getBuffer(pb->getRect(), &stride);
809
810 switch (pb->getPF().bpp) {
811 case 32:
812 return analyseRect(pb->width(), pb->height(),
813 (const rdr::U32*)buffer, stride,
814 info, maxColours);
815 case 16:
816 return analyseRect(pb->width(), pb->height(),
817 (const rdr::U16*)buffer, stride,
818 info, maxColours);
819 default:
820 return analyseRect(pb->width(), pb->height(),
821 (const rdr::U8*)buffer, stride,
822 info, maxColours);
823 }
824}
825
826void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
827 int width, int height,
828 const rdr::U8* data_,
829 int stride_)
830{
831 format = pf;
832 width_ = width;
833 height_ = height;
834 // Forced cast. We never write anything though, so it should be safe.
835 data = (rdr::U8*)data_;
836 stride = stride_;
837}
838
839// Preprocessor generated, optimised methods
840
841#define BPP 8
842#include "EncodeManagerBPP.cxx"
843#undef BPP
844#define BPP 16
845#include "EncodeManagerBPP.cxx"
846#undef BPP
847#define BPP 32
848#include "EncodeManagerBPP.cxx"
849#undef BPP