aboutsummaryrefslogtreecommitdiffstats
path: root/common/rfb/EncodeManager.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'common/rfb/EncodeManager.cxx')
-rw-r--r--common/rfb/EncodeManager.cxx140
1 files changed, 114 insertions, 26 deletions
diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx
index 0cd52060..0ce611e9 100644
--- a/common/rfb/EncodeManager.cxx
+++ b/common/rfb/EncodeManager.cxx
@@ -1,6 +1,6 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+
+#include <stdlib.h>
+
#include <rfb/EncodeManager.h>
#include <rfb/Encoder.h>
#include <rfb/Palette.h>
@@ -245,52 +248,79 @@ bool EncodeManager::supported(int encoding)
}
}
+bool EncodeManager::needsLosslessRefresh(const Region& req)
+{
+ return !lossyRegion.intersect(req).is_empty();
+}
+
+void EncodeManager::pruneLosslessRefresh(const Region& limits)
+{
+ lossyRegion.assign_intersect(limits);
+}
+
void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor)
{
+ doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor);
+}
+
+void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor,
+ size_t maxUpdateSize)
+{
+ doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
+ Region(), Point(), pb, renderedCursor);
+}
+
+void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
+ const Region& copied, const Point& copyDelta,
+ const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor)
+{
int nRects;
- Region changed;
+ Region changed, cursorRegion;
updates++;
- prepareEncoders();
+ prepareEncoders(allowLossy);
+
+ changed = changed_;
+
+ /*
+ * We need to render the cursor seperately as it has its own
+ * magical pixel buffer, so split it out from the changed region.
+ */
+ if (renderedCursor != NULL) {
+ cursorRegion = changed.intersect(renderedCursor->getEffectiveRect());
+ changed.assign_subtract(renderedCursor->getEffectiveRect());
+ }
if (conn->cp.supportsLastRect)
nRects = 0xFFFF;
else {
- nRects = ui.copied.numRects();
- nRects += computeNumRects(ui.changed);
-
- if (renderedCursor != NULL)
- nRects += 1;
+ nRects = copied.numRects();
+ nRects += computeNumRects(changed);
+ nRects += computeNumRects(cursorRegion);
}
conn->writer()->writeFramebufferUpdateStart(nRects);
- writeCopyRects(ui);
+ writeCopyRects(copied, copyDelta);
/*
* We start by searching for solid rects, which are then removed
* from the changed region.
*/
- changed.copyFrom(ui.changed);
-
if (conn->cp.supportsLastRect)
writeSolidRects(&changed, pb);
writeRects(changed, pb);
-
- if (renderedCursor != NULL) {
- Rect renderedCursorRect;
-
- renderedCursorRect = renderedCursor->getEffectiveRect();
- writeSubRect(renderedCursorRect, renderedCursor);
- }
+ writeRects(cursorRegion, renderedCursor);
conn->writer()->writeFramebufferUpdateEnd();
}
-void EncodeManager::prepareEncoders()
+void EncodeManager::prepareEncoders(bool allowLossy)
{
enum EncoderClass solid, bitmap, bitmapRLE;
enum EncoderClass indexed, indexedRLE, fullColour;
@@ -315,7 +345,7 @@ void EncodeManager::prepareEncoders()
break;
case encodingTight:
if (encoders[encoderTightJPEG]->isSupported() &&
- (conn->cp.pf().bpp >= 16))
+ (conn->cp.pf().bpp >= 16) && allowLossy)
fullColour = encoderTightJPEG;
else
fullColour = encoderTight;
@@ -333,7 +363,7 @@ void EncodeManager::prepareEncoders()
if (fullColour == encoderRaw) {
if (encoders[encoderTightJPEG]->isSupported() &&
- (conn->cp.pf().bpp >= 16))
+ (conn->cp.pf().bpp >= 16) && allowLossy)
fullColour = encoderTightJPEG;
else if (encoders[encoderZRLE]->isSupported())
fullColour = encoderZRLE;
@@ -373,7 +403,7 @@ void EncodeManager::prepareEncoders()
// JPEG is the only encoder that can reduce things to grayscale
if ((conn->cp.subsampling == subsampleGray) &&
- encoders[encoderTightJPEG]->isSupported()) {
+ encoders[encoderTightJPEG]->isSupported() && allowLossy) {
solid = bitmap = bitmapRLE = encoderTightJPEG;
indexed = indexedRLE = fullColour = encoderTightJPEG;
}
@@ -397,6 +427,52 @@ void EncodeManager::prepareEncoders()
}
}
+Region EncodeManager::getLosslessRefresh(const Region& req,
+ size_t maxUpdateSize)
+{
+ std::vector<Rect> rects;
+ Region refresh;
+ size_t area;
+
+ // We make a conservative guess at the compression ratio at 2:1
+ maxUpdateSize *= 2;
+
+ area = 0;
+ lossyRegion.intersect(req).get_rects(&rects);
+ while (!rects.empty()) {
+ size_t idx;
+ Rect rect;
+
+ // Grab a random rect so we don't keep damaging and restoring the
+ // same rect over and over
+ idx = rand() % rects.size();
+
+ rect = rects[idx];
+
+ // Add rects until we exceed the threshold, then include as much as
+ // possible of the final rect
+ if ((area + rect.area()) > maxUpdateSize) {
+ // Use the narrowest axis to avoid getting to thin rects
+ if (rect.width() > rect.height()) {
+ int width = (maxUpdateSize - area) / rect.height();
+ rect.br.x = rect.tl.x + __rfbmax(1, width);
+ } else {
+ int height = (maxUpdateSize - area) / rect.width();
+ rect.br.y = rect.tl.y + __rfbmax(1, height);
+ }
+ refresh.assign_union(Region(rect));
+ break;
+ }
+
+ area += rect.area();
+ refresh.assign_union(Region(rect));
+
+ rects.erase(rects.begin() + idx);
+ }
+
+ return refresh;
+}
+
int EncodeManager::computeNumRects(const Region& changed)
{
int numRects;
@@ -449,6 +525,11 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type)
encoder = encoders[klass];
conn->writer()->startRect(rect, encoder->encoding);
+ if (encoder->flags & EncoderLossy)
+ lossyRegion.assign_union(Region(rect));
+ else
+ lossyRegion.assign_subtract(Region(rect));
+
return encoder;
}
@@ -465,14 +546,16 @@ void EncodeManager::endRect()
stats[klass][activeType].bytes += length;
}
-void EncodeManager::writeCopyRects(const UpdateInfo& ui)
+void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
{
std::vector<Rect> rects;
std::vector<Rect>::const_iterator rect;
+ Region lossyCopy;
+
beforeLength = conn->getOutStream()->length();
- ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
+ copied.get_rects(&rects, delta.x <= 0, delta.y <= 0);
for (rect = rects.begin(); rect != rects.end(); ++rect) {
int equiv;
@@ -481,11 +564,16 @@ void EncodeManager::writeCopyRects(const UpdateInfo& ui)
equiv = 12 + rect->area() * conn->cp.pf().bpp/8;
copyStats.equivalent += equiv;
- conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
- rect->tl.y - ui.copy_delta.y);
+ conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x,
+ rect->tl.y - delta.y);
}
copyStats.bytes += conn->getOutStream()->length() - beforeLength;
+
+ lossyCopy = lossyRegion;
+ lossyCopy.translate(delta);
+ lossyCopy.assign_intersect(copied);
+ lossyRegion.assign_union(lossyCopy);
}
void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)