GUACAMOLE-1302: Add surface/display level support for forcing lossless compression.
diff --git a/src/common/common/display.h b/src/common/common/display.h
index 6707bcb..c542890 100644
--- a/src/common/common/display.h
+++ b/src/common/common/display.h
@@ -100,6 +100,13 @@
guac_common_display_layer* buffers;
/**
+ * Non-zero if all graphical updates for this display should use lossless
+ * compression, 0 otherwise. By default, newly-created displays will use
+ * lossy compression when heuristics determine it is appropriate.
+ */
+ int lossless;
+
+ /**
* Mutex which is locked internally when access to the display must be
* synchronized. All public functions of guac_common_display should be
* considered threadsafe.
@@ -228,5 +235,27 @@
void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer);
+/**
+ * Sets the overall lossless compression policy of the given display to the
+ * given value, affecting all current and future layers/buffers maintained by
+ * the display. By default, newly-created displays will use lossy compression
+ * for graphical updates when heuristics determine that doing so is
+ * appropriate. Specifying a non-zero value here will force all graphical
+ * updates to always use lossless compression, whereas specifying zero will
+ * restore the default policy.
+ *
+ * Note that this can also be adjusted on a per-layer / per-buffer basis with
+ * guac_common_surface_set_lossless().
+ *
+ * @param display
+ * The display to modify.
+ *
+ * @param lossless
+ * Non-zero if all graphical updates for this display should use lossless
+ * compression, 0 otherwise.
+ */
+void guac_common_display_set_lossless(guac_common_display* display,
+ int lossless);
+
#endif
diff --git a/src/common/common/surface.h b/src/common/common/surface.h
index ca8b310..b43dcaa 100644
--- a/src/common/common/surface.h
+++ b/src/common/common/surface.h
@@ -127,6 +127,13 @@
int touches;
/**
+ * Non-zero if all graphical updates for this surface should use lossless
+ * compression, 0 otherwise. By default, newly-created surfaces will use
+ * lossy compression when heuristics determine it is appropriate.
+ */
+ int lossless;
+
+ /**
* The X coordinate of the upper-left corner of this layer, in pixels,
* relative to its parent layer. This is only applicable to visible
* (non-buffer) layers which are not the default layer.
@@ -510,5 +517,23 @@
void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches);
+/**
+ * Sets the lossless compression policy of the given surface to the given
+ * value. By default, newly-created surfaces will use lossy compression for
+ * graphical updates when heuristics determine that doing so is appropriate.
+ * Specifying a non-zero value here will force all graphical updates to always
+ * use lossless compression, whereas specifying zero will restore the default
+ * policy.
+ *
+ * @param surface
+ * The surface to modify.
+ *
+ * @param lossless
+ * Non-zero if all graphical updates for this surface should use lossless
+ * compression, 0 otherwise.
+ */
+void guac_common_surface_set_lossless(guac_common_surface* surface,
+ int lossless);
+
#endif
diff --git a/src/common/display.c b/src/common/display.c
index 5d8ce9f..9b4b87f 100644
--- a/src/common/display.c
+++ b/src/common/display.c
@@ -182,6 +182,30 @@
}
+void guac_common_display_set_lossless(guac_common_display* display,
+ int lossless) {
+
+ pthread_mutex_lock(&display->_lock);
+
+ /* Update lossless setting to be applied to all newly-allocated
+ * layers/buffers */
+ display->lossless = lossless;
+
+ /* Update losslessness of all allocated layers/buffers */
+ guac_common_display_layer* current = display->layers;
+ while (current != NULL) {
+ guac_common_surface_set_lossless(current->surface, lossless);
+ current = current->next;
+ }
+
+ /* Update losslessness of default display layer (not included within layers
+ * list) */
+ guac_common_surface_set_lossless(display->default_surface, lossless);
+
+ pthread_mutex_unlock(&display->_lock);
+
+}
+
void guac_common_display_flush(guac_common_display* display) {
pthread_mutex_lock(&display->_lock);
@@ -287,6 +311,9 @@
guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, layer, width, height);
+ /* Apply current display losslessness */
+ guac_common_surface_set_lossless(surface, display->lossless);
+
/* Add layer and surface to list */
guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->layers, layer, surface);
@@ -308,6 +335,9 @@
guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, buffer, width, height);
+ /* Apply current display losslessness */
+ guac_common_surface_set_lossless(surface, display->lossless);
+
/* Add buffer and surface to list */
guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->buffers, buffer, surface);
diff --git a/src/common/surface.c b/src/common/surface.c
index 183ae11..61b77c7 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -116,6 +116,15 @@
}
+void guac_common_surface_set_lossless(guac_common_surface* surface,
+ int lossless) {
+
+ pthread_mutex_lock(&surface->_lock);
+ surface->lossless = lossless;
+ pthread_mutex_unlock(&surface->_lock);
+
+}
+
void guac_common_surface_move(guac_common_surface* surface, int x, int y) {
pthread_mutex_lock(&surface->_lock);
@@ -534,6 +543,10 @@
static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface,
const guac_common_rect* rect) {
+ /* Do not use JPEG if lossless quality is required */
+ if (surface->lossless)
+ return 0;
+
/* Calculate the average framerate for the given rect */
int framerate = __guac_common_surface_calculate_framerate(surface, rect);
@@ -1806,7 +1819,8 @@
/* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect,
- guac_common_surface_suggest_quality(surface->client), 0);
+ guac_common_surface_suggest_quality(surface->client),
+ surface->lossless ? 1 : 0);
cairo_surface_destroy(rect);
surface->realized = 1;