1 /* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
2  * Copyright (C) 2011 D. R. Commander.  All Rights Reserved.
3  * Copyright 2014-2018 Pierre Ossman for Cendio AB
4  * Copyright 2018 Peter Astrand for Cendio AB
5  *
6  * This is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this software; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
19  * USA.
20  */
21 
22 #include <stdlib.h>
23 
24 #include <rfb/EncodeManager.h>
25 #include <rfb/Encoder.h>
26 #include <rfb/Palette.h>
27 #include <rfb/SConnection.h>
28 #include <rfb/SMsgWriter.h>
29 #include <rfb/UpdateTracker.h>
30 #include <rfb/LogWriter.h>
31 #include <rfb/Exception.h>
32 
33 #include <rfb/RawEncoder.h>
34 #include <rfb/RREEncoder.h>
35 #include <rfb/HextileEncoder.h>
36 #include <rfb/ZRLEEncoder.h>
37 #include <rfb/TightEncoder.h>
38 #include <rfb/TightJPEGEncoder.h>
39 
40 using namespace rfb;
41 
42 static LogWriter vlog("EncodeManager");
43 
44 // Split each rectangle into smaller ones no larger than this area,
45 // and no wider than this width.
46 static const int SubRectMaxArea = 65536;
47 static const int SubRectMaxWidth = 2048;
48 
49 // The size in pixels of either side of each block tested when looking
50 // for solid blocks.
51 static const int SolidSearchBlock = 16;
52 // Don't bother with blocks smaller than this
53 static const int SolidBlockMinArea = 2048;
54 
55 // How long we consider a region recently changed (in ms)
56 static const int RecentChangeTimeout = 50;
57 
58 namespace rfb {
59 
60 enum EncoderClass {
61   encoderRaw,
62   encoderRRE,
63   encoderHextile,
64   encoderTight,
65   encoderTightJPEG,
66   encoderZRLE,
67   encoderClassMax,
68 };
69 
70 enum EncoderType {
71   encoderSolid,
72   encoderBitmap,
73   encoderBitmapRLE,
74   encoderIndexed,
75   encoderIndexedRLE,
76   encoderFullColour,
77   encoderTypeMax,
78 };
79 
80 struct RectInfo {
81   int rleRuns;
82   Palette palette;
83 };
84 
85 };
86 
encoderClassName(EncoderClass klass)87 static const char *encoderClassName(EncoderClass klass)
88 {
89   switch (klass) {
90   case encoderRaw:
91     return "Raw";
92   case encoderRRE:
93     return "RRE";
94   case encoderHextile:
95     return "Hextile";
96   case encoderTight:
97     return "Tight";
98   case encoderTightJPEG:
99     return "Tight (JPEG)";
100   case encoderZRLE:
101     return "ZRLE";
102   case encoderClassMax:
103     break;
104   }
105 
106   return "Unknown Encoder Class";
107 }
108 
encoderTypeName(EncoderType type)109 static const char *encoderTypeName(EncoderType type)
110 {
111   switch (type) {
112   case encoderSolid:
113     return "Solid";
114   case encoderBitmap:
115     return "Bitmap";
116   case encoderBitmapRLE:
117     return "Bitmap RLE";
118   case encoderIndexed:
119     return "Indexed";
120   case encoderIndexedRLE:
121     return "Indexed RLE";
122   case encoderFullColour:
123     return "Full Colour";
124   case encoderTypeMax:
125     break;
126   }
127 
128   return "Unknown Encoder Type";
129 }
130 
EncodeManager(SConnection * conn_)131 EncodeManager::EncodeManager(SConnection* conn_)
132   : conn(conn_), recentChangeTimer(this)
133 {
134   StatsVector::iterator iter;
135 
136   encoders.resize(encoderClassMax, NULL);
137   activeEncoders.resize(encoderTypeMax, encoderRaw);
138 
139   encoders[encoderRaw] = new RawEncoder(conn);
140   encoders[encoderRRE] = new RREEncoder(conn);
141   encoders[encoderHextile] = new HextileEncoder(conn);
142   encoders[encoderTight] = new TightEncoder(conn);
143   encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
144   encoders[encoderZRLE] = new ZRLEEncoder(conn);
145 
146   updates = 0;
147   memset(&copyStats, 0, sizeof(copyStats));
148   stats.resize(encoderClassMax);
149   for (iter = stats.begin();iter != stats.end();++iter) {
150     StatsVector::value_type::iterator iter2;
151     iter->resize(encoderTypeMax);
152     for (iter2 = iter->begin();iter2 != iter->end();++iter2)
153       memset(&*iter2, 0, sizeof(EncoderStats));
154   }
155 }
156 
~EncodeManager()157 EncodeManager::~EncodeManager()
158 {
159   std::vector<Encoder*>::iterator iter;
160 
161   logStats();
162 
163   for (iter = encoders.begin();iter != encoders.end();iter++)
164     delete *iter;
165 }
166 
logStats()167 void EncodeManager::logStats()
168 {
169   size_t i, j;
170 
171   unsigned rects;
172   unsigned long long pixels, bytes, equivalent;
173 
174   double ratio;
175 
176   char a[1024], b[1024];
177 
178   rects = 0;
179   pixels = bytes = equivalent = 0;
180 
181   vlog.info("Framebuffer updates: %u", updates);
182 
183   if (copyStats.rects != 0) {
184     vlog.info("  %s:", "CopyRect");
185 
186     rects += copyStats.rects;
187     pixels += copyStats.pixels;
188     bytes += copyStats.bytes;
189     equivalent += copyStats.equivalent;
190 
191     ratio = (double)copyStats.equivalent / copyStats.bytes;
192 
193     siPrefix(copyStats.rects, "rects", a, sizeof(a));
194     siPrefix(copyStats.pixels, "pixels", b, sizeof(b));
195     vlog.info("    %s: %s, %s", "Copies", a, b);
196     iecPrefix(copyStats.bytes, "B", a, sizeof(a));
197     vlog.info("    %*s  %s (1:%g ratio)",
198               (int)strlen("Copies"), "",
199               a, ratio);
200   }
201 
202   for (i = 0;i < stats.size();i++) {
203     // Did this class do anything at all?
204     for (j = 0;j < stats[i].size();j++) {
205       if (stats[i][j].rects != 0)
206         break;
207     }
208     if (j == stats[i].size())
209       continue;
210 
211     vlog.info("  %s:", encoderClassName((EncoderClass)i));
212 
213     for (j = 0;j < stats[i].size();j++) {
214       if (stats[i][j].rects == 0)
215         continue;
216 
217       rects += stats[i][j].rects;
218       pixels += stats[i][j].pixels;
219       bytes += stats[i][j].bytes;
220       equivalent += stats[i][j].equivalent;
221 
222       ratio = (double)stats[i][j].equivalent / stats[i][j].bytes;
223 
224       siPrefix(stats[i][j].rects, "rects", a, sizeof(a));
225       siPrefix(stats[i][j].pixels, "pixels", b, sizeof(b));
226       vlog.info("    %s: %s, %s", encoderTypeName((EncoderType)j), a, b);
227       iecPrefix(stats[i][j].bytes, "B", a, sizeof(a));
228       vlog.info("    %*s  %s (1:%g ratio)",
229                 (int)strlen(encoderTypeName((EncoderType)j)), "",
230                 a, ratio);
231     }
232   }
233 
234   ratio = (double)equivalent / bytes;
235 
236   siPrefix(rects, "rects", a, sizeof(a));
237   siPrefix(pixels, "pixels", b, sizeof(b));
238   vlog.info("  Total: %s, %s", a, b);
239   iecPrefix(bytes, "B", a, sizeof(a));
240   vlog.info("         %s (1:%g ratio)", a, ratio);
241 }
242 
supported(int encoding)243 bool EncodeManager::supported(int encoding)
244 {
245   switch (encoding) {
246   case encodingRaw:
247   case encodingRRE:
248   case encodingHextile:
249   case encodingZRLE:
250   case encodingTight:
251     return true;
252   default:
253     return false;
254   }
255 }
256 
needsLosslessRefresh(const Region & req)257 bool EncodeManager::needsLosslessRefresh(const Region& req)
258 {
259   return !lossyRegion.intersect(req).is_empty();
260 }
261 
getNextLosslessRefresh(const Region & req)262 int EncodeManager::getNextLosslessRefresh(const Region& req)
263 {
264   // Do we have something we can send right away?
265   if (!pendingRefreshRegion.intersect(req).is_empty())
266     return 0;
267 
268   assert(needsLosslessRefresh(req));
269   assert(recentChangeTimer.isStarted());
270 
271   return recentChangeTimer.getNextTimeout();
272 }
273 
pruneLosslessRefresh(const Region & limits)274 void EncodeManager::pruneLosslessRefresh(const Region& limits)
275 {
276   lossyRegion.assign_intersect(limits);
277   pendingRefreshRegion.assign_intersect(limits);
278 }
279 
writeUpdate(const UpdateInfo & ui,const PixelBuffer * pb,const RenderedCursor * renderedCursor)280 void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
281                                 const RenderedCursor* renderedCursor)
282 {
283   doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor);
284 
285   recentlyChangedRegion.assign_union(ui.changed);
286   recentlyChangedRegion.assign_union(ui.copied);
287   if (!recentChangeTimer.isStarted())
288     recentChangeTimer.start(RecentChangeTimeout);
289 }
290 
writeLosslessRefresh(const Region & req,const PixelBuffer * pb,const RenderedCursor * renderedCursor,size_t maxUpdateSize)291 void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
292                                          const RenderedCursor* renderedCursor,
293                                          size_t maxUpdateSize)
294 {
295   doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
296            Region(), Point(), pb, renderedCursor);
297 }
298 
handleTimeout(Timer * t)299 bool EncodeManager::handleTimeout(Timer* t)
300 {
301   if (t == &recentChangeTimer) {
302     // Any lossy region that wasn't recently updated can
303     // now be scheduled for a refresh
304     pendingRefreshRegion.assign_union(lossyRegion.subtract(recentlyChangedRegion));
305     recentlyChangedRegion.clear();
306 
307     // Will there be more to do? (i.e. do we need another round)
308     if (!lossyRegion.subtract(pendingRefreshRegion).is_empty())
309       return true;
310   }
311 
312   return false;
313 }
314 
doUpdate(bool allowLossy,const Region & changed_,const Region & copied,const Point & copyDelta,const PixelBuffer * pb,const RenderedCursor * renderedCursor)315 void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
316                              const Region& copied, const Point& copyDelta,
317                              const PixelBuffer* pb,
318                              const RenderedCursor* renderedCursor)
319 {
320     int nRects;
321     Region changed, cursorRegion;
322 
323     updates++;
324 
325     prepareEncoders(allowLossy);
326 
327     changed = changed_;
328 
329     if (!conn->client.supportsEncoding(encodingCopyRect))
330       changed.assign_union(copied);
331 
332     /*
333      * We need to render the cursor seperately as it has its own
334      * magical pixel buffer, so split it out from the changed region.
335      */
336     if (renderedCursor != NULL) {
337       cursorRegion = changed.intersect(renderedCursor->getEffectiveRect());
338       changed.assign_subtract(renderedCursor->getEffectiveRect());
339     }
340 
341     if (conn->client.supportsEncoding(pseudoEncodingLastRect))
342       nRects = 0xFFFF;
343     else {
344       nRects = 0;
345       if (conn->client.supportsEncoding(encodingCopyRect))
346         nRects += copied.numRects();
347       nRects += computeNumRects(changed);
348       nRects += computeNumRects(cursorRegion);
349     }
350 
351     conn->writer()->writeFramebufferUpdateStart(nRects);
352 
353     if (conn->client.supportsEncoding(encodingCopyRect))
354       writeCopyRects(copied, copyDelta);
355 
356     /*
357      * We start by searching for solid rects, which are then removed
358      * from the changed region.
359      */
360     if (conn->client.supportsEncoding(pseudoEncodingLastRect))
361       writeSolidRects(&changed, pb);
362 
363     writeRects(changed, pb);
364     writeRects(cursorRegion, renderedCursor);
365 
366     conn->writer()->writeFramebufferUpdateEnd();
367 }
368 
prepareEncoders(bool allowLossy)369 void EncodeManager::prepareEncoders(bool allowLossy)
370 {
371   enum EncoderClass solid, bitmap, bitmapRLE;
372   enum EncoderClass indexed, indexedRLE, fullColour;
373 
374   bool allowJPEG;
375 
376   rdr::S32 preferred;
377 
378   std::vector<int>::iterator iter;
379 
380   solid = bitmap = bitmapRLE = encoderRaw;
381   indexed = indexedRLE = fullColour = encoderRaw;
382 
383   allowJPEG = conn->client.pf().bpp >= 16;
384   if (!allowLossy) {
385     if (encoders[encoderTightJPEG]->losslessQuality == -1)
386       allowJPEG = false;
387   }
388 
389   // Try to respect the client's wishes
390   preferred = conn->getPreferredEncoding();
391   switch (preferred) {
392   case encodingRRE:
393     // Horrible for anything high frequency and/or lots of colours
394     bitmapRLE = indexedRLE = encoderRRE;
395     break;
396   case encodingHextile:
397     // Slightly less horrible
398     bitmapRLE = indexedRLE = fullColour = encoderHextile;
399     break;
400   case encodingTight:
401     if (encoders[encoderTightJPEG]->isSupported() && allowJPEG)
402       fullColour = encoderTightJPEG;
403     else
404       fullColour = encoderTight;
405     indexed = indexedRLE = encoderTight;
406     bitmap = bitmapRLE = encoderTight;
407     break;
408   case encodingZRLE:
409     fullColour = encoderZRLE;
410     bitmapRLE = indexedRLE = encoderZRLE;
411     bitmap = indexed = encoderZRLE;
412     break;
413   }
414 
415   // Any encoders still unassigned?
416 
417   if (fullColour == encoderRaw) {
418     if (encoders[encoderTightJPEG]->isSupported() && allowJPEG)
419       fullColour = encoderTightJPEG;
420     else if (encoders[encoderZRLE]->isSupported())
421       fullColour = encoderZRLE;
422     else if (encoders[encoderTight]->isSupported())
423       fullColour = encoderTight;
424     else if (encoders[encoderHextile]->isSupported())
425       fullColour = encoderHextile;
426   }
427 
428   if (indexed == encoderRaw) {
429     if (encoders[encoderZRLE]->isSupported())
430       indexed = encoderZRLE;
431     else if (encoders[encoderTight]->isSupported())
432       indexed = encoderTight;
433     else if (encoders[encoderHextile]->isSupported())
434       indexed = encoderHextile;
435   }
436 
437   if (indexedRLE == encoderRaw)
438     indexedRLE = indexed;
439 
440   if (bitmap == encoderRaw)
441     bitmap = indexed;
442   if (bitmapRLE == encoderRaw)
443     bitmapRLE = bitmap;
444 
445   if (solid == encoderRaw) {
446     if (encoders[encoderTight]->isSupported())
447       solid = encoderTight;
448     else if (encoders[encoderRRE]->isSupported())
449       solid = encoderRRE;
450     else if (encoders[encoderZRLE]->isSupported())
451       solid = encoderZRLE;
452     else if (encoders[encoderHextile]->isSupported())
453       solid = encoderHextile;
454   }
455 
456   // JPEG is the only encoder that can reduce things to grayscale
457   if ((conn->client.subsampling == subsampleGray) &&
458       encoders[encoderTightJPEG]->isSupported() && allowLossy) {
459     solid = bitmap = bitmapRLE = encoderTightJPEG;
460     indexed = indexedRLE = fullColour = encoderTightJPEG;
461   }
462 
463   activeEncoders[encoderSolid] = solid;
464   activeEncoders[encoderBitmap] = bitmap;
465   activeEncoders[encoderBitmapRLE] = bitmapRLE;
466   activeEncoders[encoderIndexed] = indexed;
467   activeEncoders[encoderIndexedRLE] = indexedRLE;
468   activeEncoders[encoderFullColour] = fullColour;
469 
470   for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
471     Encoder *encoder;
472 
473     encoder = encoders[*iter];
474 
475     encoder->setCompressLevel(conn->client.compressLevel);
476 
477     if (allowLossy) {
478       encoder->setQualityLevel(conn->client.qualityLevel);
479       encoder->setFineQualityLevel(conn->client.fineQualityLevel,
480                                    conn->client.subsampling);
481     } else {
482       int level = __rfbmax(conn->client.qualityLevel,
483                            encoder->losslessQuality);
484       encoder->setQualityLevel(level);
485       encoder->setFineQualityLevel(-1, subsampleUndefined);
486     }
487   }
488 }
489 
getLosslessRefresh(const Region & req,size_t maxUpdateSize)490 Region EncodeManager::getLosslessRefresh(const Region& req,
491                                          size_t maxUpdateSize)
492 {
493   std::vector<Rect> rects;
494   Region refresh;
495   size_t area;
496 
497   // We make a conservative guess at the compression ratio at 2:1
498   maxUpdateSize *= 2;
499 
500   // We will measure pixels, not bytes (assume 32 bpp)
501   maxUpdateSize /= 4;
502 
503   area = 0;
504   pendingRefreshRegion.intersect(req).get_rects(&rects);
505   while (!rects.empty()) {
506     size_t idx;
507     Rect rect;
508 
509     // Grab a random rect so we don't keep damaging and restoring the
510     // same rect over and over
511     idx = rand() % rects.size();
512 
513     rect = rects[idx];
514 
515     // Add rects until we exceed the threshold, then include as much as
516     // possible of the final rect
517     if ((area + rect.area()) > maxUpdateSize) {
518       // Use the narrowest axis to avoid getting to thin rects
519       if (rect.width() > rect.height()) {
520         int width = (maxUpdateSize - area) / rect.height();
521         rect.br.x = rect.tl.x + __rfbmax(1, width);
522       } else {
523         int height = (maxUpdateSize - area) / rect.width();
524         rect.br.y = rect.tl.y + __rfbmax(1, height);
525       }
526       refresh.assign_union(Region(rect));
527       break;
528     }
529 
530     area += rect.area();
531     refresh.assign_union(Region(rect));
532 
533     rects.erase(rects.begin() + idx);
534   }
535 
536   return refresh;
537 }
538 
computeNumRects(const Region & changed)539 int EncodeManager::computeNumRects(const Region& changed)
540 {
541   int numRects;
542   std::vector<Rect> rects;
543   std::vector<Rect>::const_iterator rect;
544 
545   numRects = 0;
546   changed.get_rects(&rects);
547   for (rect = rects.begin(); rect != rects.end(); ++rect) {
548     int w, h, sw, sh;
549 
550     w = rect->width();
551     h = rect->height();
552 
553     // No split necessary?
554     if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
555       numRects += 1;
556       continue;
557     }
558 
559     if (w <= SubRectMaxWidth)
560       sw = w;
561     else
562       sw = SubRectMaxWidth;
563 
564     sh = SubRectMaxArea / sw;
565 
566     // ceil(w/sw) * ceil(h/sh)
567     numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
568   }
569 
570   return numRects;
571 }
572 
startRect(const Rect & rect,int type)573 Encoder *EncodeManager::startRect(const Rect& rect, int type)
574 {
575   Encoder *encoder;
576   int klass, equiv;
577 
578   activeType = type;
579   klass = activeEncoders[activeType];
580 
581   beforeLength = conn->getOutStream()->length();
582 
583   stats[klass][activeType].rects++;
584   stats[klass][activeType].pixels += rect.area();
585   equiv = 12 + rect.area() * (conn->client.pf().bpp/8);
586   stats[klass][activeType].equivalent += equiv;
587 
588   encoder = encoders[klass];
589   conn->writer()->startRect(rect, encoder->encoding);
590 
591   if ((encoder->flags & EncoderLossy) &&
592       ((encoder->losslessQuality == -1) ||
593        (encoder->getQualityLevel() < encoder->losslessQuality)))
594     lossyRegion.assign_union(Region(rect));
595   else
596     lossyRegion.assign_subtract(Region(rect));
597 
598   // This was either a rect getting refreshed, or a rect that just got
599   // new content. Either way we should not try to refresh it anymore.
600   pendingRefreshRegion.assign_subtract(Region(rect));
601 
602   return encoder;
603 }
604 
endRect()605 void EncodeManager::endRect()
606 {
607   int klass;
608   int length;
609 
610   conn->writer()->endRect();
611 
612   length = conn->getOutStream()->length() - beforeLength;
613 
614   klass = activeEncoders[activeType];
615   stats[klass][activeType].bytes += length;
616 }
617 
writeCopyRects(const Region & copied,const Point & delta)618 void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
619 {
620   std::vector<Rect> rects;
621   std::vector<Rect>::const_iterator rect;
622 
623   Region lossyCopy;
624 
625   beforeLength = conn->getOutStream()->length();
626 
627   copied.get_rects(&rects, delta.x <= 0, delta.y <= 0);
628   for (rect = rects.begin(); rect != rects.end(); ++rect) {
629     int equiv;
630 
631     copyStats.rects++;
632     copyStats.pixels += rect->area();
633     equiv = 12 + rect->area() * (conn->client.pf().bpp/8);
634     copyStats.equivalent += equiv;
635 
636     conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x,
637                                    rect->tl.y - delta.y);
638   }
639 
640   copyStats.bytes += conn->getOutStream()->length() - beforeLength;
641 
642   lossyCopy = lossyRegion;
643   lossyCopy.translate(delta);
644   lossyCopy.assign_intersect(copied);
645   lossyRegion.assign_union(lossyCopy);
646 
647   // Stop any pending refresh as a copy is enough that we consider
648   // this region to be recently changed
649   pendingRefreshRegion.assign_subtract(copied);
650 }
651 
writeSolidRects(Region * changed,const PixelBuffer * pb)652 void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
653 {
654   std::vector<Rect> rects;
655   std::vector<Rect>::const_iterator rect;
656 
657   changed->get_rects(&rects);
658   for (rect = rects.begin(); rect != rects.end(); ++rect)
659     findSolidRect(*rect, changed, pb);
660 }
661 
findSolidRect(const Rect & rect,Region * changed,const PixelBuffer * pb)662 void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
663                                   const PixelBuffer* pb)
664 {
665   Rect sr;
666   int dx, dy, dw, dh;
667 
668   // We start by finding a solid 16x16 block
669   for (dy = rect.tl.y; dy < rect.br.y; dy += SolidSearchBlock) {
670 
671     dh = SolidSearchBlock;
672     if (dy + dh > rect.br.y)
673       dh = rect.br.y - dy;
674 
675     for (dx = rect.tl.x; dx < rect.br.x; dx += SolidSearchBlock) {
676       // We define it like this to guarantee alignment
677       rdr::U32 _buffer;
678       rdr::U8* colourValue = (rdr::U8*)&_buffer;
679 
680       dw = SolidSearchBlock;
681       if (dx + dw > rect.br.x)
682         dw = rect.br.x - dx;
683 
684       pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
685 
686       sr.setXYWH(dx, dy, dw, dh);
687       if (checkSolidTile(sr, colourValue, pb)) {
688         Rect erb, erp;
689 
690         Encoder *encoder;
691 
692         // We then try extending the area by adding more blocks
693         // in both directions and pick the combination that gives
694         // the largest area.
695         sr.setXYWH(dx, dy, rect.br.x - dx, rect.br.y - dy);
696         extendSolidAreaByBlock(sr, colourValue, pb, &erb);
697 
698         // Did we end up getting the entire rectangle?
699         if (erb.equals(rect))
700           erp = erb;
701         else {
702           // Don't bother with sending tiny rectangles
703           if (erb.area() < SolidBlockMinArea)
704             continue;
705 
706           // Extend the area again, but this time one pixel
707           // row/column at a time.
708           extendSolidAreaByPixel(rect, erb, colourValue, pb, &erp);
709         }
710 
711         // Send solid-color rectangle.
712         encoder = startRect(erp, encoderSolid);
713         if (encoder->flags & EncoderUseNativePF) {
714           encoder->writeSolidRect(erp.width(), erp.height(),
715                                   pb->getPF(), colourValue);
716         } else {
717           rdr::U32 _buffer2;
718           rdr::U8* converted = (rdr::U8*)&_buffer2;
719 
720           conn->client.pf().bufferFromBuffer(converted, pb->getPF(),
721                                          colourValue, 1);
722 
723           encoder->writeSolidRect(erp.width(), erp.height(),
724                                   conn->client.pf(), converted);
725         }
726         endRect();
727 
728         changed->assign_subtract(Region(erp));
729 
730         // Search remaining areas by recursion
731         // FIXME: Is this the best way to divide things up?
732 
733         // Left? (Note that we've already searched a SolidSearchBlock
734         //        pixels high strip here)
735         if ((erp.tl.x != rect.tl.x) && (erp.height() > SolidSearchBlock)) {
736           sr.setXYWH(rect.tl.x, erp.tl.y + SolidSearchBlock,
737                      erp.tl.x - rect.tl.x, erp.height() - SolidSearchBlock);
738           findSolidRect(sr, changed, pb);
739         }
740 
741         // Right?
742         if (erp.br.x != rect.br.x) {
743           sr.setXYWH(erp.br.x, erp.tl.y, rect.br.x - erp.br.x, erp.height());
744           findSolidRect(sr, changed, pb);
745         }
746 
747         // Below?
748         if (erp.br.y != rect.br.y) {
749           sr.setXYWH(rect.tl.x, erp.br.y, rect.width(), rect.br.y - erp.br.y);
750           findSolidRect(sr, changed, pb);
751         }
752 
753         return;
754       }
755     }
756   }
757 }
758 
writeRects(const Region & changed,const PixelBuffer * pb)759 void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
760 {
761   std::vector<Rect> rects;
762   std::vector<Rect>::const_iterator rect;
763 
764   changed.get_rects(&rects);
765   for (rect = rects.begin(); rect != rects.end(); ++rect) {
766     int w, h, sw, sh;
767     Rect sr;
768 
769     w = rect->width();
770     h = rect->height();
771 
772     // No split necessary?
773     if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
774       writeSubRect(*rect, pb);
775       continue;
776     }
777 
778     if (w <= SubRectMaxWidth)
779       sw = w;
780     else
781       sw = SubRectMaxWidth;
782 
783     sh = SubRectMaxArea / sw;
784 
785     for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
786       sr.br.y = sr.tl.y + sh;
787       if (sr.br.y > rect->br.y)
788         sr.br.y = rect->br.y;
789 
790       for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
791         sr.br.x = sr.tl.x + sw;
792         if (sr.br.x > rect->br.x)
793           sr.br.x = rect->br.x;
794 
795         writeSubRect(sr, pb);
796       }
797     }
798   }
799 }
800 
writeSubRect(const Rect & rect,const PixelBuffer * pb)801 void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
802 {
803   PixelBuffer *ppb;
804 
805   Encoder *encoder;
806 
807   struct RectInfo info;
808   unsigned int divisor, maxColours;
809 
810   bool useRLE;
811   EncoderType type;
812 
813   // FIXME: This is roughly the algorithm previously used by the Tight
814   //        encoder. It seems a bit backwards though, that higher
815   //        compression setting means spending less effort in building
816   //        a palette. It might be that they figured the increase in
817   //        zlib setting compensated for the loss.
818   if (conn->client.compressLevel == -1)
819     divisor = 2 * 8;
820   else
821     divisor = conn->client.compressLevel * 8;
822   if (divisor < 4)
823     divisor = 4;
824 
825   maxColours = rect.area()/divisor;
826 
827   // Special exception inherited from the Tight encoder
828   if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
829     if ((conn->client.compressLevel != -1) && (conn->client.compressLevel < 2))
830       maxColours = 24;
831     else
832       maxColours = 96;
833   }
834 
835   if (maxColours < 2)
836     maxColours = 2;
837 
838   encoder = encoders[activeEncoders[encoderIndexedRLE]];
839   if (maxColours > encoder->maxPaletteSize)
840     maxColours = encoder->maxPaletteSize;
841   encoder = encoders[activeEncoders[encoderIndexed]];
842   if (maxColours > encoder->maxPaletteSize)
843     maxColours = encoder->maxPaletteSize;
844 
845   ppb = preparePixelBuffer(rect, pb, true);
846 
847   if (!analyseRect(ppb, &info, maxColours))
848     info.palette.clear();
849 
850   // Different encoders might have different RLE overhead, but
851   // here we do a guess at RLE being the better choice if reduces
852   // the pixel count by 50%.
853   useRLE = info.rleRuns <= (rect.area() * 2);
854 
855   switch (info.palette.size()) {
856   case 0:
857     type = encoderFullColour;
858     break;
859   case 1:
860     type = encoderSolid;
861     break;
862   case 2:
863     if (useRLE)
864       type = encoderBitmapRLE;
865     else
866       type = encoderBitmap;
867     break;
868   default:
869     if (useRLE)
870       type = encoderIndexedRLE;
871     else
872       type = encoderIndexed;
873   }
874 
875   encoder = startRect(rect, type);
876 
877   if (encoder->flags & EncoderUseNativePF)
878     ppb = preparePixelBuffer(rect, pb, false);
879 
880   encoder->writeRect(ppb, info.palette);
881 
882   endRect();
883 }
884 
checkSolidTile(const Rect & r,const rdr::U8 * colourValue,const PixelBuffer * pb)885 bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
886                                    const PixelBuffer *pb)
887 {
888   switch (pb->getPF().bpp) {
889   case 32:
890     return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
891   case 16:
892     return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
893   default:
894     return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
895   }
896 }
897 
extendSolidAreaByBlock(const Rect & r,const rdr::U8 * colourValue,const PixelBuffer * pb,Rect * er)898 void EncodeManager::extendSolidAreaByBlock(const Rect& r,
899                                            const rdr::U8* colourValue,
900                                            const PixelBuffer *pb, Rect* er)
901 {
902   int dx, dy, dw, dh;
903   int w_prev;
904   Rect sr;
905   int w_best = 0, h_best = 0;
906 
907   w_prev = r.width();
908 
909   // We search width first, back off when we hit a different colour,
910   // and restart with a larger height. We keep track of the
911   // width/height combination that gives us the largest area.
912   for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
913 
914     dh = SolidSearchBlock;
915     if (dy + dh > r.br.y)
916       dh = r.br.y - dy;
917 
918     // We test one block here outside the x loop in order to break
919     // the y loop right away.
920     dw = SolidSearchBlock;
921     if (dw > w_prev)
922       dw = w_prev;
923 
924     sr.setXYWH(r.tl.x, dy, dw, dh);
925     if (!checkSolidTile(sr, colourValue, pb))
926       break;
927 
928     for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
929 
930       dw = SolidSearchBlock;
931       if (dx + dw > r.tl.x + w_prev)
932         dw = r.tl.x + w_prev - dx;
933 
934       sr.setXYWH(dx, dy, dw, dh);
935       if (!checkSolidTile(sr, colourValue, pb))
936         break;
937 
938       dx += dw;
939     }
940 
941     w_prev = dx - r.tl.x;
942     if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
943       w_best = w_prev;
944       h_best = dy + dh - r.tl.y;
945     }
946   }
947 
948   er->tl.x = r.tl.x;
949   er->tl.y = r.tl.y;
950   er->br.x = er->tl.x + w_best;
951   er->br.y = er->tl.y + h_best;
952 }
953 
extendSolidAreaByPixel(const Rect & r,const Rect & sr,const rdr::U8 * colourValue,const PixelBuffer * pb,Rect * er)954 void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
955                                            const rdr::U8* colourValue,
956                                            const PixelBuffer *pb, Rect* er)
957 {
958   int cx, cy;
959   Rect tr;
960 
961   // Try to extend the area upwards.
962   for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
963     tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
964     if (!checkSolidTile(tr, colourValue, pb))
965       break;
966   }
967   er->tl.y = cy + 1;
968 
969   // ... downwards.
970   for (cy = sr.br.y; cy < r.br.y; cy++) {
971     tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
972     if (!checkSolidTile(tr, colourValue, pb))
973       break;
974   }
975   er->br.y = cy;
976 
977   // ... to the left.
978   for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
979     tr.setXYWH(cx, er->tl.y, 1, er->height());
980     if (!checkSolidTile(tr, colourValue, pb))
981       break;
982   }
983   er->tl.x = cx + 1;
984 
985   // ... to the right.
986   for (cx = sr.br.x; cx < r.br.x; cx++) {
987     tr.setXYWH(cx, er->tl.y, 1, er->height());
988     if (!checkSolidTile(tr, colourValue, pb))
989       break;
990   }
991   er->br.x = cx;
992 }
993 
preparePixelBuffer(const Rect & rect,const PixelBuffer * pb,bool convert)994 PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
995                                                const PixelBuffer *pb,
996                                                bool convert)
997 {
998   const rdr::U8* buffer;
999   int stride;
1000 
1001   // Do wo need to convert the data?
1002   if (convert && !conn->client.pf().equal(pb->getPF())) {
1003     convertedPixelBuffer.setPF(conn->client.pf());
1004     convertedPixelBuffer.setSize(rect.width(), rect.height());
1005 
1006     buffer = pb->getBuffer(rect, &stride);
1007     convertedPixelBuffer.imageRect(pb->getPF(),
1008                                    convertedPixelBuffer.getRect(),
1009                                    buffer, stride);
1010 
1011     return &convertedPixelBuffer;
1012   }
1013 
1014   // Otherwise we still need to shift the coordinates. We have our own
1015   // abusive subclass of FullFramePixelBuffer for this.
1016 
1017   buffer = pb->getBuffer(rect, &stride);
1018 
1019   offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
1020                            buffer, stride);
1021 
1022   return &offsetPixelBuffer;
1023 }
1024 
analyseRect(const PixelBuffer * pb,struct RectInfo * info,int maxColours)1025 bool EncodeManager::analyseRect(const PixelBuffer *pb,
1026                                 struct RectInfo *info, int maxColours)
1027 {
1028   const rdr::U8* buffer;
1029   int stride;
1030 
1031   buffer = pb->getBuffer(pb->getRect(), &stride);
1032 
1033   switch (pb->getPF().bpp) {
1034   case 32:
1035     return analyseRect(pb->width(), pb->height(),
1036                        (const rdr::U32*)buffer, stride,
1037                        info, maxColours);
1038   case 16:
1039     return analyseRect(pb->width(), pb->height(),
1040                        (const rdr::U16*)buffer, stride,
1041                        info, maxColours);
1042   default:
1043     return analyseRect(pb->width(), pb->height(),
1044                        (const rdr::U8*)buffer, stride,
1045                        info, maxColours);
1046   }
1047 }
1048 
update(const PixelFormat & pf,int width,int height,const rdr::U8 * data_,int stride_)1049 void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
1050                                               int width, int height,
1051                                               const rdr::U8* data_,
1052                                               int stride_)
1053 {
1054   format = pf;
1055   // Forced cast. We never write anything though, so it should be safe.
1056   setBuffer(width, height, (rdr::U8*)data_, stride_);
1057 }
1058 
getBufferRW(const Rect & r,int * stride)1059 rdr::U8* EncodeManager::OffsetPixelBuffer::getBufferRW(const Rect& r, int* stride)
1060 {
1061   throw rfb::Exception("Invalid write attempt to OffsetPixelBuffer");
1062 }
1063 
1064 // Preprocessor generated, optimised methods
1065 
1066 #define BPP 8
1067 #include "EncodeManagerBPP.cxx"
1068 #undef BPP
1069 #define BPP 16
1070 #include "EncodeManagerBPP.cxx"
1071 #undef BPP
1072 #define BPP 32
1073 #include "EncodeManagerBPP.cxx"
1074 #undef BPP
1075