GUACAMOLE-187: Merge layer resize optimizations.

This commit is contained in:
James Muehlner
2017-01-29 11:53:11 -08:00

View File

@@ -42,6 +42,17 @@ Guacamole.Layer = function(width, height) {
*/ */
var layer = this; var layer = this;
/**
* The number of pixels the width or height of a layer must change before
* the underlying canvas is resized. The underlying canvas will be kept at
* dimensions which are integer multiples of this factor.
*
* @private
* @constant
* @type Number
*/
var CANVAS_SIZE_FACTOR = 64;
/** /**
* The canvas element backing this Layer. * The canvas element backing this Layer.
* @private * @private
@@ -55,6 +66,16 @@ Guacamole.Layer = function(width, height) {
var context = canvas.getContext("2d"); var context = canvas.getContext("2d");
context.save(); context.save();
/**
* Whether the layer has not yet been drawn to. Once any draw operation
* which affects the underlying canvas is invoked, this flag will be set to
* false.
*
* @private
* @type Boolean
*/
var empty = true;
/** /**
* Whether a new path should be started with the next path drawing * Whether a new path should be started with the next path drawing
* operations. * operations.
@@ -98,30 +119,44 @@ Guacamole.Layer = function(width, height) {
}; };
/** /**
* Resizes the canvas element backing this Layer without testing the * Resizes the canvas element backing this Layer. This function should only
* new size. This function should only be used internally. * be used internally.
* *
* @private * @private
* @param {Number} newWidth The new width to assign to this Layer. * @param {Number} [newWidth=0]
* @param {Number} newHeight The new height to assign to this Layer. * The new width to assign to this Layer.
*
* @param {Number} [newHeight=0]
* The new height to assign to this Layer.
*/ */
function resize(newWidth, newHeight) { var resize = function resize(newWidth, newHeight) {
// Only preserve old data if width/height are both non-zero // Default size to zero
newWidth = newWidth || 0;
newHeight = newHeight || 0;
// Calculate new dimensions of internal canvas
var canvasWidth = Math.ceil(newWidth / CANVAS_SIZE_FACTOR) * CANVAS_SIZE_FACTOR;
var canvasHeight = Math.ceil(newHeight / CANVAS_SIZE_FACTOR) * CANVAS_SIZE_FACTOR;
// Resize only if canvas dimensions are actually changing
if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) {
// Copy old data only if relevant and non-empty
var oldData = null; var oldData = null;
if (layer.width !== 0 && layer.height !== 0) { if (!empty && canvas.width !== 0 && canvas.height !== 0) {
// Create canvas and context for holding old data // Create canvas and context for holding old data
oldData = document.createElement("canvas"); oldData = document.createElement("canvas");
oldData.width = layer.width; oldData.width = Math.min(layer.width, newWidth);
oldData.height = layer.height; oldData.height = Math.min(layer.height, newHeight);
var oldDataContext = oldData.getContext("2d"); var oldDataContext = oldData.getContext("2d");
// Copy image data from current // Copy image data from current
oldDataContext.drawImage(canvas, oldDataContext.drawImage(canvas,
0, 0, layer.width, layer.height, 0, 0, oldData.width, oldData.height,
0, 0, layer.width, layer.height); 0, 0, oldData.width, oldData.height);
} }
@@ -129,27 +164,34 @@ Guacamole.Layer = function(width, height) {
var oldCompositeOperation = context.globalCompositeOperation; var oldCompositeOperation = context.globalCompositeOperation;
// Resize canvas // Resize canvas
canvas.width = newWidth; canvas.width = canvasWidth;
canvas.height = newHeight; canvas.height = canvasHeight;
// Redraw old data, if any // Redraw old data, if any
if (oldData) if (oldData)
context.drawImage(oldData, context.drawImage(oldData,
0, 0, layer.width, layer.height, 0, 0, oldData.width, oldData.height,
0, 0, layer.width, layer.height); 0, 0, oldData.width, oldData.height);
// Restore composite operation // Restore composite operation
context.globalCompositeOperation = oldCompositeOperation; context.globalCompositeOperation = oldCompositeOperation;
layer.width = newWidth;
layer.height = newHeight;
// Acknowledge reset of stack (happens on resize of canvas) // Acknowledge reset of stack (happens on resize of canvas)
stackSize = 0; stackSize = 0;
context.save(); context.save();
} }
// If the canvas size is not changing, manually force state reset
else
layer.reset();
// Assign new layer dimensions
layer.width = newWidth;
layer.height = newHeight;
};
/** /**
* Given the X and Y coordinates of the upper-left corner of a rectangle * Given the X and Y coordinates of the upper-left corner of a rectangle
* and the rectangle's width and height, resize the backing canvas element * and the rectangle's width and height, resize the backing canvas element
@@ -257,6 +299,7 @@ Guacamole.Layer = function(width, height) {
this.drawImage = function(x, y, image) { this.drawImage = function(x, y, image) {
if (layer.autosize) fitRect(x, y, image.width, image.height); if (layer.autosize) fitRect(x, y, image.width, image.height);
context.drawImage(image, x, y); context.drawImage(image, x, y);
empty = false;
}; };
/** /**
@@ -335,6 +378,7 @@ Guacamole.Layer = function(width, height) {
// Draw image data // Draw image data
context.putImageData(dst, x, y); context.putImageData(dst, x, y);
empty = false;
}; };
@@ -378,6 +422,7 @@ Guacamole.Layer = function(width, height) {
// Get image data from src and dst // Get image data from src and dst
var src = srcLayer.getCanvas().getContext("2d").getImageData(srcx, srcy, srcw, srch); var src = srcLayer.getCanvas().getContext("2d").getImageData(srcx, srcy, srcw, srch);
context.putImageData(src, x, y); context.putImageData(src, x, y);
empty = false;
}; };
@@ -421,6 +466,7 @@ Guacamole.Layer = function(width, height) {
if (layer.autosize) fitRect(x, y, srcw, srch); if (layer.autosize) fitRect(x, y, srcw, srch);
context.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch); context.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch);
empty = false;
}; };
@@ -583,6 +629,7 @@ Guacamole.Layer = function(width, height) {
context.lineWidth = thickness; context.lineWidth = thickness;
context.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; context.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")";
context.stroke(); context.stroke();
empty = false;
// Path now implicitly closed // Path now implicitly closed
pathClosed = true; pathClosed = true;
@@ -605,6 +652,7 @@ Guacamole.Layer = function(width, height) {
// Fill with color // Fill with color
context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")";
context.fill(); context.fill();
empty = false;
// Path now implicitly closed // Path now implicitly closed
pathClosed = true; pathClosed = true;
@@ -637,6 +685,7 @@ Guacamole.Layer = function(width, height) {
"repeat" "repeat"
); );
context.stroke(); context.stroke();
empty = false;
// Path now implicitly closed // Path now implicitly closed
pathClosed = true; pathClosed = true;
@@ -661,6 +710,7 @@ Guacamole.Layer = function(width, height) {
"repeat" "repeat"
); );
context.fill(); context.fill();
empty = false;
// Path now implicitly closed // Path now implicitly closed
pathClosed = true; pathClosed = true;
@@ -781,8 +831,7 @@ Guacamole.Layer = function(width, height) {
}; };
// Initialize canvas dimensions // Initialize canvas dimensions
canvas.width = width; resize(width, height);
canvas.height = height;
// Explicitly render canvas below other elements in the layer (such as // Explicitly render canvas below other elements in the layer (such as
// child layers). Chrome and others may fail to render layers properly // child layers). Chrome and others may fail to render layers properly