GUACAMOLE-884: Leverage createImageBitmap() for reading image data where supported.

Some browsers suffer from a memory leak when reading image data
repeatedly using the Image object. Reading from Blobs does not exhibit
the same behavior. While reading from Blobs has previously been seen to
perform poorly compared to data URIs, this was observed when reading
using createObjectURL(). The createImageBitmap() function appears to
perform identically to reading data URIs using Image.
This commit is contained in:
Michael Jumper
2019-09-29 21:57:02 -07:00
parent 5ff81f7735
commit 4e130d2afd
2 changed files with 86 additions and 21 deletions

View File

@@ -1190,13 +1190,10 @@ Guacamole.Client = function(tunnel) {
// Create stream
var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
var reader = new Guacamole.DataURIReader(stream, mimetype);
// Draw image when stream is complete
reader.onend = function drawImageBlob() {
// Draw received contents once decoded
display.setChannelMask(layer, channelMask);
display.draw(layer, x, y, reader.getURI());
};
display.drawStream(layer, x, y, stream, mimetype);
},

View File

@@ -543,11 +543,35 @@ Guacamole.Display = function() {
*/
this.drawBlob = function(layer, x, y, blob) {
var task;
// Prefer createImageBitmap() over blob URLs if available
if (window.createImageBitmap) {
var bitmap;
// Draw image once loaded
task = scheduleTask(function drawImageBitmap() {
layer.drawImage(x, y, bitmap);
}, true);
// Load image from provided blob
window.createImageBitmap(blob).then(function bitmapLoaded(decoded) {
bitmap = decoded;
task.unblock();
});
}
// Use blob URLs and the Image object if createImageBitmap() is
// unavailable
else {
// Create URL for blob
var url = URL.createObjectURL(blob);
// Draw and free blob URL when ready
var task = scheduleTask(function __display_drawBlob() {
task = scheduleTask(function __display_drawBlob() {
// Draw the image only if it loaded without errors
if (image.width && image.height)
@@ -564,6 +588,50 @@ Guacamole.Display = function() {
image.onerror = task.unblock;
image.src = url;
}
};
/**
* Draws the image within the given stream at the given coordinates. The
* image will be loaded automatically, and this and any future operations
* will wait for the image to finish loading.
*
* @param {Guacamole.Layer} layer
* The layer to draw upon.
*
* @param {Number} x
* The destination X coordinate.
*
* @param {Number} y
* The destination Y coordinate.
*
* @param {Guacamole.InputStream} stream
* The stream along which image data will be received.
*
* @param {String} mimetype
* The mimetype of the image within the stream.
*/
this.drawStream = function drawStream(layer, x, y, stream, mimetype) {
// If createImageBitmap() is available, load the image as a blob so
// that function can be used
if (window.createImageBitmap) {
var reader = new Guacamole.BlobReader(stream, mimetype);
reader.onend = function drawImageBlob() {
guac_display.drawBlob(layer, x, y, reader.getBlob());
};
}
// Lacking createImageBitmap(), fall back to data URIs and the Image
// object
else {
var reader = new Guacamole.DataURIReader(stream, mimetype);
reader.onend = function drawImageDataURI() {
guac_display.draw(layer, x, y, reader.getURI());
};
}
};
/**