blob: ca60da488d56a92de20ecaf110fe5d3fdee68235 [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>
26
27#include <rfb/RawEncoder.h>
28#include <rfb/RREEncoder.h>
29#include <rfb/HextileEncoder.h>
30#include <rfb/ZRLEEncoder.h>
31#include <rfb/TightEncoder.h>
32#include <rfb/TightJPEGEncoder.h>
33
34using namespace rfb;
35
36// Split each rectangle into smaller ones no larger than this area,
37// and no wider than this width.
38static const int SubRectMaxArea = 65536;
39static const int SubRectMaxWidth = 2048;
40
41// The size in pixels of either side of each block tested when looking
42// for solid blocks.
43static const int SolidSearchBlock = 16;
44// Don't bother with blocks smaller than this
45static const int SolidBlockMinArea = 2048;
46
47namespace rfb {
48
49enum EncoderClass {
50 encoderRaw,
51 encoderRRE,
52 encoderHextile,
53 encoderTight,
54 encoderTightJPEG,
55 encoderZRLE,
56 encoderClassMax,
57};
58
59enum EncoderType {
60 encoderSolid,
61 encoderBitmap,
62 encoderBitmapRLE,
63 encoderIndexed,
64 encoderIndexedRLE,
65 encoderFullColour,
66 encoderTypeMax,
67};
68
69struct RectInfo {
70 int rleRuns;
71 Palette palette;
72};
73
74};
75
76EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
77{
78 encoders.resize(encoderClassMax, NULL);
79 activeEncoders.resize(encoderTypeMax, encoderRaw);
80
81 encoders[encoderRaw] = new RawEncoder(conn);
82 encoders[encoderRRE] = new RREEncoder(conn);
83 encoders[encoderHextile] = new HextileEncoder(conn);
84 encoders[encoderTight] = new TightEncoder(conn);
85 encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
86 encoders[encoderZRLE] = new ZRLEEncoder(conn);
87}
88
89EncodeManager::~EncodeManager()
90{
91 std::vector<Encoder*>::iterator iter;
92
93 for (iter = encoders.begin();iter != encoders.end();iter++)
94 delete *iter;
95}
96
97bool EncodeManager::supported(int encoding)
98{
99 switch (encoding) {
100 case encodingRaw:
101 case encodingRRE:
102 case encodingHextile:
103 case encodingZRLE:
104 case encodingTight:
105 return true;
106 default:
107 return false;
108 }
109}
110
111void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
112 const RenderedCursor* renderedCursor)
113{
114 int nRects;
115 Region changed;
116
117 prepareEncoders();
118
119 if (conn->cp.supportsLastRect)
120 nRects = 0xFFFF;
121 else {
122 nRects = ui.copied.numRects();
123 nRects += computeNumRects(ui.changed);
124
125 if (renderedCursor != NULL)
126 nRects += 1;
127 }
128
129 conn->writer()->writeFramebufferUpdateStart(nRects);
130
131 writeCopyRects(ui);
132
133 /*
134 * We start by searching for solid rects, which are then removed
135 * from the changed region.
136 */
137 changed.copyFrom(ui.changed);
138
139 if (conn->cp.supportsLastRect)
140 writeSolidRects(&changed, pb);
141
142 writeRects(changed, pb);
143
144 if (renderedCursor != NULL) {
145 Rect renderedCursorRect;
146
147 renderedCursorRect = renderedCursor->getEffectiveRect();
148 writeSubRect(renderedCursorRect, renderedCursor);
149 }
150
151 conn->writer()->writeFramebufferUpdateEnd();
152}
153
154void EncodeManager::prepareEncoders()
155{
156 enum EncoderClass solid, bitmap, bitmapRLE;
157 enum EncoderClass indexed, indexedRLE, fullColour;
158
159 rdr::S32 preferred;
160
161 std::vector<int>::iterator iter;
162
163 solid = bitmap = bitmapRLE = encoderRaw;
164 indexed = indexedRLE = fullColour = encoderRaw;
165
166 // Try to respect the client's wishes
Pierre Ossman48700812014-09-17 17:11:56 +0200167 preferred = conn->getPreferredEncoding();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100168 switch (preferred) {
169 case encodingRRE:
170 // Horrible for anything high frequency and/or lots of colours
171 bitmapRLE = indexedRLE = encoderRRE;
172 break;
173 case encodingHextile:
174 // Slightly less horrible
175 bitmapRLE = indexedRLE = fullColour = encoderHextile;
176 break;
177 case encodingTight:
178 if (encoders[encoderTightJPEG]->isSupported() &&
179 (conn->cp.pf().bpp >= 16))
180 fullColour = encoderTightJPEG;
181 else
182 fullColour = encoderTight;
183 indexed = indexedRLE = encoderTight;
184 bitmap = bitmapRLE = encoderTight;
185 break;
186 case encodingZRLE:
187 fullColour = encoderZRLE;
188 bitmapRLE = indexedRLE = encoderZRLE;
189 bitmap = indexed = encoderZRLE;
190 break;
191 }
192
193 // Any encoders still unassigned?
194
195 if (fullColour == encoderRaw) {
196 if (encoders[encoderTightJPEG]->isSupported() &&
197 (conn->cp.pf().bpp >= 16))
198 fullColour = encoderTightJPEG;
199 else if (encoders[encoderZRLE]->isSupported())
200 fullColour = encoderZRLE;
201 else if (encoders[encoderTight]->isSupported())
202 fullColour = encoderTight;
203 else if (encoders[encoderHextile]->isSupported())
204 fullColour = encoderHextile;
205 }
206
207 if (indexed == encoderRaw) {
208 if (encoders[encoderZRLE]->isSupported())
209 indexed = encoderZRLE;
210 else if (encoders[encoderTight]->isSupported())
211 indexed = encoderTight;
212 else if (encoders[encoderHextile]->isSupported())
213 indexed = encoderHextile;
214 }
215
216 if (indexedRLE == encoderRaw)
217 indexedRLE = indexed;
218
219 if (bitmap == encoderRaw)
220 bitmap = indexed;
221 if (bitmapRLE == encoderRaw)
222 bitmapRLE = bitmap;
223
224 if (solid == encoderRaw) {
225 if (encoders[encoderTight]->isSupported())
226 solid = encoderTight;
227 else if (encoders[encoderRRE]->isSupported())
228 solid = encoderRRE;
229 else if (encoders[encoderZRLE]->isSupported())
230 solid = encoderZRLE;
231 else if (encoders[encoderHextile]->isSupported())
232 solid = encoderHextile;
233 }
234
235 // JPEG is the only encoder that can reduce things to grayscale
236 if ((conn->cp.subsampling == subsampleGray) &&
237 encoders[encoderTightJPEG]->isSupported()) {
238 solid = bitmap = bitmapRLE = encoderTightJPEG;
239 indexed = indexedRLE = fullColour = encoderTightJPEG;
240 }
241
242 activeEncoders[encoderSolid] = solid;
243 activeEncoders[encoderBitmap] = bitmap;
244 activeEncoders[encoderBitmapRLE] = bitmapRLE;
245 activeEncoders[encoderIndexed] = indexed;
246 activeEncoders[encoderIndexedRLE] = indexedRLE;
247 activeEncoders[encoderFullColour] = fullColour;
248
249 for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
250 Encoder *encoder;
251
252 encoder = encoders[*iter];
253
254 encoder->setCompressLevel(conn->cp.compressLevel);
255 encoder->setQualityLevel(conn->cp.qualityLevel);
256 encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
257 conn->cp.subsampling);
258 }
259}
260
261int EncodeManager::computeNumRects(const Region& changed)
262{
263 int numRects;
264 std::vector<Rect> rects;
265 std::vector<Rect>::const_iterator rect;
266
267 numRects = 0;
268 changed.get_rects(&rects);
269 for (rect = rects.begin(); rect != rects.end(); ++rect) {
270 int w, h, sw, sh;
271
272 w = rect->width();
273 h = rect->height();
274
275 // No split necessary?
276 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
277 numRects += 1;
278 continue;
279 }
280
281 if (w <= SubRectMaxWidth)
282 sw = w;
283 else
284 sw = SubRectMaxWidth;
285
286 sh = SubRectMaxArea / sw;
287
288 // ceil(w/sw) * ceil(h/sh)
289 numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
290 }
291
292 return numRects;
293}
294
295void EncodeManager::writeCopyRects(const UpdateInfo& ui)
296{
297 std::vector<Rect> rects;
298 std::vector<Rect>::const_iterator rect;
299
300 ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
301 for (rect = rects.begin(); rect != rects.end(); ++rect) {
302 conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
303 rect->tl.y - ui.copy_delta.y);
304 }
305}
306
307void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
308{
309 std::vector<Rect> rects;
310 std::vector<Rect>::const_iterator rect;
311
312 // FIXME: This gives up after the first rect it finds. A large update
313 // (like a whole screen refresh) might have lots of large solid
314 // areas.
315
316 changed->get_rects(&rects);
317 for (rect = rects.begin(); rect != rects.end(); ++rect) {
318 Rect sr;
319 int dx, dy, dw, dh;
320
321 // We start by finding a solid 16x16 block
322 for (dy = rect->tl.y; dy < rect->br.y; dy += SolidSearchBlock) {
323
324 dh = SolidSearchBlock;
325 if (dy + dh > rect->br.y)
326 dh = rect->br.y - dy;
327
328 for (dx = rect->tl.x; dx < rect->br.x; dx += SolidSearchBlock) {
329 // We define it like this to guarantee alignment
330 rdr::U32 _buffer;
331 rdr::U8* colourValue = (rdr::U8*)&_buffer;
332
333 dw = SolidSearchBlock;
334 if (dx + dw > rect->br.x)
335 dw = rect->br.x - dx;
336
337 pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
338
339 sr.setXYWH(dx, dy, dw, dh);
340 if (checkSolidTile(sr, colourValue, pb)) {
341 Rect erb, erp;
342
343 Encoder *encoder;
344
345 // We then try extending the area by adding more blocks
346 // in both directions and pick the combination that gives
347 // the largest area.
348 sr.setXYWH(dx, dy, rect->br.x - dx, rect->br.y - dy);
349 extendSolidAreaByBlock(sr, colourValue, pb, &erb);
350
351 // Did we end up getting the entire rectangle?
352 if (erb.equals(*rect))
353 erp = erb;
354 else {
355 // Don't bother with sending tiny rectangles
356 if (erb.area() < SolidBlockMinArea)
357 continue;
358
359 // Extend the area again, but this time one pixel
360 // row/column at a time.
361 extendSolidAreaByPixel(*rect, erb, colourValue, pb, &erp);
362 }
363
364 // Send solid-color rectangle.
365 encoder = encoders[activeEncoders[encoderSolid]];
366 conn->writer()->startRect(erp, encoder->encoding);
367 if (encoder->flags & EncoderUseNativePF) {
368 encoder->writeSolidRect(erp.width(), erp.height(),
369 pb->getPF(), colourValue);
370 } else {
371 rdr::U32 _buffer2;
372 rdr::U8* converted = (rdr::U8*)&_buffer2;
373
374 conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
375 colourValue, 1);
376
377 encoder->writeSolidRect(erp.width(), erp.height(),
378 conn->cp.pf(), converted);
379 }
380 conn->writer()->endRect();
381
382 changed->assign_subtract(Region(erp));
383
384 break;
385 }
386 }
387
388 if (dx < rect->br.x)
389 break;
390 }
391 }
392}
393
394void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
395{
396 std::vector<Rect> rects;
397 std::vector<Rect>::const_iterator rect;
398
399 changed.get_rects(&rects);
400 for (rect = rects.begin(); rect != rects.end(); ++rect) {
401 int w, h, sw, sh;
402 Rect sr;
403
404 w = rect->width();
405 h = rect->height();
406
407 // No split necessary?
408 if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
409 writeSubRect(*rect, pb);
410 continue;
411 }
412
413 if (w <= SubRectMaxWidth)
414 sw = w;
415 else
416 sw = SubRectMaxWidth;
417
418 sh = SubRectMaxArea / sw;
419
420 for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
421 sr.br.y = sr.tl.y + sh;
422 if (sr.br.y > rect->br.y)
423 sr.br.y = rect->br.y;
424
425 for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
426 sr.br.x = sr.tl.x + sw;
427 if (sr.br.x > rect->br.x)
428 sr.br.x = rect->br.x;
429
430 writeSubRect(sr, pb);
431 }
432 }
433 }
434}
435
436void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
437{
438 PixelBuffer *ppb;
439
440 Encoder *encoder;
441
442 struct RectInfo info;
443 int divisor, maxColours;
444
445 bool useRLE;
446 EncoderType type;
447
448 // FIXME: This is roughly the algorithm previously used by the Tight
449 // encoder. It seems a bit backwards though, that higher
450 // compression setting means spending less effort in building
451 // a palette. It might be that they figured the increase in
452 // zlib setting compensated for the loss.
453 if (conn->cp.compressLevel == -1)
454 divisor = 2 * 8;
455 else
456 divisor = conn->cp.compressLevel * 8;
457 if (divisor < 4)
458 divisor = 4;
459
460 maxColours = rect.area()/divisor;
461
462 // Special exception inherited from the Tight encoder
463 if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
464 if (conn->cp.compressLevel < 2)
465 maxColours = 24;
466 else
467 maxColours = 96;
468 }
469
470 if (maxColours < 2)
471 maxColours = 2;
472
473 encoder = encoders[activeEncoders[encoderIndexedRLE]];
474 if (maxColours > encoder->maxPaletteSize)
475 maxColours = encoder->maxPaletteSize;
476 encoder = encoders[activeEncoders[encoderIndexed]];
477 if (maxColours > encoder->maxPaletteSize)
478 maxColours = encoder->maxPaletteSize;
479
480 ppb = preparePixelBuffer(rect, pb, true);
481
482 if (!analyseRect(ppb, &info, maxColours))
483 info.palette.clear();
484
485 // Different encoders might have different RLE overhead, but
486 // here we do a guess at RLE being the better choice if reduces
487 // the pixel count by 50%.
488 useRLE = info.rleRuns <= (rect.area() * 2);
489
490 switch (info.palette.size()) {
491 case 0:
492 type = encoderFullColour;
493 break;
494 case 1:
495 type = encoderSolid;
496 break;
497 case 2:
498 if (useRLE)
499 type = encoderBitmapRLE;
500 else
501 type = encoderBitmap;
502 break;
503 default:
504 if (useRLE)
505 type = encoderIndexedRLE;
506 else
507 type = encoderIndexed;
508 }
509
510 encoder = encoders[activeEncoders[type]];
511
512 if (encoder->flags & EncoderUseNativePF)
513 ppb = preparePixelBuffer(rect, pb, false);
514
515 conn->writer()->startRect(rect, encoder->encoding);
516 encoder->writeRect(ppb, info.palette);
517 conn->writer()->endRect();
518}
519
520bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
521 const PixelBuffer *pb)
522{
523 switch (pb->getPF().bpp) {
524 case 32:
525 return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
526 case 16:
527 return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
528 default:
529 return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
530 }
531}
532
533void EncodeManager::extendSolidAreaByBlock(const Rect& r,
534 const rdr::U8* colourValue,
535 const PixelBuffer *pb, Rect* er)
536{
537 int dx, dy, dw, dh;
538 int w_prev;
539 Rect sr;
540 int w_best = 0, h_best = 0;
541
542 w_prev = r.width();
543
544 // We search width first, back off when we hit a different colour,
545 // and restart with a larger height. We keep track of the
546 // width/height combination that gives us the largest area.
547 for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
548
549 dh = SolidSearchBlock;
550 if (dy + dh > r.br.y)
551 dh = r.br.y - dy;
552
553 // We test one block here outside the x loop in order to break
554 // the y loop right away.
555 dw = SolidSearchBlock;
556 if (dw > w_prev)
557 dw = w_prev;
558
559 sr.setXYWH(r.tl.x, dy, dw, dh);
560 if (!checkSolidTile(sr, colourValue, pb))
561 break;
562
563 for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
564
565 dw = SolidSearchBlock;
566 if (dx + dw > r.tl.x + w_prev)
567 dw = r.tl.x + w_prev - dx;
568
569 sr.setXYWH(dx, dy, dw, dh);
570 if (!checkSolidTile(sr, colourValue, pb))
571 break;
572
573 dx += dw;
574 }
575
576 w_prev = dx - r.tl.x;
577 if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
578 w_best = w_prev;
579 h_best = dy + dh - r.tl.y;
580 }
581 }
582
583 er->tl.x = r.tl.x;
584 er->tl.y = r.tl.y;
585 er->br.x = er->tl.x + w_best;
586 er->br.y = er->tl.y + h_best;
587}
588
589void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
590 const rdr::U8* colourValue,
591 const PixelBuffer *pb, Rect* er)
592{
593 int cx, cy;
594 Rect tr;
595
596 // Try to extend the area upwards.
597 for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
598 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
599 if (!checkSolidTile(tr, colourValue, pb))
600 break;
601 }
602 er->tl.y = cy + 1;
603
604 // ... downwards.
605 for (cy = sr.br.y; cy < r.br.y; cy++) {
606 tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
607 if (!checkSolidTile(tr, colourValue, pb))
608 break;
609 }
610 er->br.y = cy;
611
612 // ... to the left.
613 for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
614 tr.setXYWH(cx, er->tl.y, 1, er->height());
615 if (!checkSolidTile(tr, colourValue, pb))
616 break;
617 }
618 er->tl.x = cx + 1;
619
620 // ... to the right.
621 for (cx = sr.br.x; cx < r.br.x; cx++) {
622 tr.setXYWH(cx, er->tl.y, 1, er->height());
623 if (!checkSolidTile(tr, colourValue, pb))
624 break;
625 }
626 er->br.x = cx;
627}
628
629PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
630 const PixelBuffer *pb,
631 bool convert)
632{
633 const rdr::U8* buffer;
634 int stride;
635
636 // Do wo need to convert the data?
637 if (convert && !conn->cp.pf().equal(pb->getPF())) {
638 convertedPixelBuffer.setPF(conn->cp.pf());
639 convertedPixelBuffer.setSize(rect.width(), rect.height());
640
641 buffer = pb->getBuffer(rect, &stride);
642 convertedPixelBuffer.imageRect(pb->getPF(),
643 convertedPixelBuffer.getRect(),
644 buffer, stride);
645
646 return &convertedPixelBuffer;
647 }
648
649 // Otherwise we still need to shift the coordinates. We have our own
650 // abusive subclass of FullFramePixelBuffer for this.
651
652 buffer = pb->getBuffer(rect, &stride);
653
654 offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
655 buffer, stride);
656
657 return &offsetPixelBuffer;
658}
659
660bool EncodeManager::analyseRect(const PixelBuffer *pb,
661 struct RectInfo *info, int maxColours)
662{
663 const rdr::U8* buffer;
664 int stride;
665
666 buffer = pb->getBuffer(pb->getRect(), &stride);
667
668 switch (pb->getPF().bpp) {
669 case 32:
670 return analyseRect(pb->width(), pb->height(),
671 (const rdr::U32*)buffer, stride,
672 info, maxColours);
673 case 16:
674 return analyseRect(pb->width(), pb->height(),
675 (const rdr::U16*)buffer, stride,
676 info, maxColours);
677 default:
678 return analyseRect(pb->width(), pb->height(),
679 (const rdr::U8*)buffer, stride,
680 info, maxColours);
681 }
682}
683
684void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
685 int width, int height,
686 const rdr::U8* data_,
687 int stride_)
688{
689 format = pf;
690 width_ = width;
691 height_ = height;
692 // Forced cast. We never write anything though, so it should be safe.
693 data = (rdr::U8*)data_;
694 stride = stride_;
695}
696
697// Preprocessor generated, optimised methods
698
699#define BPP 8
700#include "EncodeManagerBPP.cxx"
701#undef BPP
702#define BPP 16
703#include "EncodeManagerBPP.cxx"
704#undef BPP
705#define BPP 32
706#include "EncodeManagerBPP.cxx"
707#undef BPP