diff options
Diffstat (limited to 'common/rfb/EncodeManager.cxx')
-rw-r--r-- | common/rfb/EncodeManager.cxx | 140 |
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) |