* 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>
// Don't bother with blocks smaller than this
static const int SolidBlockMinArea = 2048;
+static const int LosslessRefreshMaxArea = 4096;
+
namespace rfb {
enum EncoderClass {
}
}
+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)
+{
+ doUpdate(false, getLosslessRefresh(req),
+ 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, cursorRegion;
updates++;
- prepareEncoders();
+ prepareEncoders(allowLossy);
- changed = ui.changed;
+ changed = changed_;
/*
* We need to render the cursor seperately as it has its own
if (conn->cp.supportsLastRect)
nRects = 0xFFFF;
else {
- nRects = ui.copied.numRects();
+ 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
conn->writer()->writeFramebufferUpdateEnd();
}
-void EncodeManager::prepareEncoders()
+void EncodeManager::prepareEncoders(bool allowLossy)
{
enum EncoderClass solid, bitmap, bitmapRLE;
enum EncoderClass indexed, indexedRLE, fullColour;
break;
case encodingTight:
if (encoders[encoderTightJPEG]->isSupported() &&
- (conn->cp.pf().bpp >= 16))
+ (conn->cp.pf().bpp >= 16) && allowLossy)
fullColour = encoderTightJPEG;
else
fullColour = encoderTight;
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;
// 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;
}
}
}
+Region EncodeManager::getLosslessRefresh(const Region& req)
+{
+ std::vector<Rect> rects;
+ Region refresh;
+ size_t area;
+
+ 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()) > LosslessRefreshMaxArea) {
+ // Use the narrowest axis to avoid getting to thin rects
+ if (rect.width() > rect.height()) {
+ int width = (LosslessRefreshMaxArea - area) / rect.height();
+ rect.br.x = rect.tl.x + __rfbmax(1, width);
+ } else {
+ int height = (LosslessRefreshMaxArea - 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;
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;
}
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;
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)
/* 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
#include <rdr/types.h>
#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
namespace rfb {
class SConnection;
class UpdateInfo;
class PixelBuffer;
class RenderedCursor;
- class Region;
struct Rect;
struct RectInfo;
// Hack to let ConnParams calculate the client's preferred encoding
static bool supported(int encoding);
+ bool needsLosslessRefresh(const Region& req);
+ void pruneLosslessRefresh(const Region& limits);
+
void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
+ void writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor);
+
protected:
- void prepareEncoders();
+ void doUpdate(bool allowLossy, const Region& changed,
+ const Region& copied, const Point& copy_delta,
+ const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor);
+ void prepareEncoders(bool allowLossy);
+
+ Region getLosslessRefresh(const Region& req);
int computeNumRects(const Region& changed);
Encoder *startRect(const Rect& rect, int type);
void endRect();
- void writeCopyRects(const UpdateInfo& ui);
+ void writeCopyRects(const Region& copied, const Point& delta);
void writeSolidRects(Region *changed, const PixelBuffer* pb);
void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb);
void writeRects(const Region& changed, const PixelBuffer* pb);
std::vector<Encoder*> encoders;
std::vector<int> activeEncoders;
+ Region lossyRegion;
+
struct EncoderStats {
unsigned rects;
unsigned long long bytes;