From c4cd8880bed06b2cb12ffd11e9a6978568f7271c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 11 Mar 2012 20:16:36 -0700 Subject: [PATCH] Refactor rect, rename copyRect -> copy, add several necessary operations, remove unused functions. --- src/main/resources/guacamole.js | 30 ++---- src/main/resources/layer.js | 203 ++++++++++++++++++++++++++++----------- 2 files changed, 157 insertions(+), 76 deletions(-) diff --git a/src/main/resources/guacamole.js b/src/main/resources/guacamole.js index 88f4dba..0cbf024 100644 --- a/src/main/resources/guacamole.js +++ b/src/main/resources/guacamole.js @@ -378,7 +378,7 @@ Guacamole.Client = function(tunnel) { dstL.setChannelMask(channelMask); - dstL.copyRect( + dstL.copy( srcL, srcX, srcY, @@ -417,35 +417,21 @@ Guacamole.Client = function(tunnel) { "rect": function(parameters) { - var channelMask = parseInt(parameters[0]); - var layer = getLayer(parseInt(parameters[1])); - var x = parseInt(parameters[2]); - var y = parseInt(parameters[3]); - var w = parseInt(parameters[4]); - var h = parseInt(parameters[5]); - var r = parseInt(parameters[6]); - var g = parseInt(parameters[7]); - var b = parseInt(parameters[8]); - var a = parseInt(parameters[9]); - - layer.setChannelMask(channelMask); + var layer = getLayer(parseInt(parameters[0])); + var x = parseInt(parameters[1]); + var y = parseInt(parameters[2]); + var w = parseInt(parameters[3]); + var h = parseInt(parameters[4]); - layer.drawRect( - x, y, w, h, - r, g, b, a - ); + layer.rect(x, y, w, h); }, "clip": function(parameters) { var layer = getLayer(parseInt(parameters[0])); - var x = parseInt(parameters[1]); - var y = parseInt(parameters[2]); - var w = parseInt(parameters[3]); - var h = parseInt(parameters[4]); - layer.clipRect(x, y, w, h); + layer.clip(); }, diff --git a/src/main/resources/layer.js b/src/main/resources/layer.js index 709d3ae..faac6ff 100644 --- a/src/main/resources/layer.js +++ b/src/main/resources/layer.js @@ -72,7 +72,6 @@ Guacamole.Layer = function(width, height) { * @private */ var displayContext = display.getContext("2d"); - displayContext.save(); /** * The queue of all pending Tasks. Tasks will be run in order, with new @@ -83,6 +82,17 @@ Guacamole.Layer = function(width, height) { var tasks = new Array(); /** + * Whether a new path should be started with the next path drawing + * operations. + */ + var pathClosed = true; + + /** + * The number of states on the state stack. + */ + var stackSize = 0; + + /** * Map of all Guacamole channel masks to HTML5 canvas composite operation * names. Not all channel mask combinations are currently implemented. * @private @@ -518,12 +528,12 @@ Guacamole.Layer = function(width, height) { * @param {Number} x The destination X coordinate. * @param {Number} y The destination Y coordinate. */ - this.copyRect = function(srcLayer, srcx, srcy, srcw, srch, x, y) { + this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) { var drawComplete = false; var srcLock = null; - function doCopyRect() { + function doCopy() { if (layer.autosize != 0) fitRect(x, y, srcw, srch); var srcCanvas = srcLayer.getCanvas(); @@ -541,13 +551,13 @@ Guacamole.Layer = function(width, height) { // If we ARE the source layer, no need to sync. // Syncing would result in deadlock. if (layer === srcLayer) - scheduleTask(doCopyRect); + scheduleTask(doCopy); // Otherwise synchronize copy operation with source layer else { // Currently blocked draw task - var task = scheduleTask(doCopyRect, true); + var task = scheduleTask(doCopy, true); // Unblock draw task once source layer is ready srcLayer.sync(task.unblock); @@ -565,24 +575,32 @@ Guacamole.Layer = function(width, height) { }; /** - * Clear the specified rectangle of image data. + * Add the specified cubic bezier point to the current path. * - * @param {Number} x The X coordinate of the upper-left corner of the - * rectangle to clear. - * @param {Number} y The Y coordinate of the upper-left corner of the - * rectangle to clear. - * @param {Number} w The width of the rectangle to clear. - * @param {Number} h The height of the rectangle to clear. + * @param {Number} x The X coordinate of the point to draw. + * @param {Number} y The Y coordinate of the point to draw. + * @param {Number} cp1x The X coordinate of the first control point. + * @param {Number} cp1y The Y coordinate of the first control point. + * @param {Number} cp2x The X coordinate of the second control point. + * @param {Number} cp2y The Y coordinate of the second control point. */ - this.clearRect = function(x, y, w, h) { + this.path = function(x, y, cp1x, cp1y, cp2x, cp2y) { scheduleTask(function() { - if (layer.autosize != 0) fitRect(x, y, w, h); - displayContext.clearRect(x, y, w, h); + + // Start a new path if current path is closed + if (pathClosed) { + displayContext.beginPath(); + pathClosed = false; + } + + if (layer.autosize != 0) fitRect(x, y, 0, 0); + displayContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, y); + }); }; /** - * Fill the specified rectangle of image data with the specified color. + * Add the specified rectangle to the current path. * * @param {Number} x The X coordinate of the upper-left corner of the * rectangle to draw. @@ -590,64 +608,141 @@ Guacamole.Layer = function(width, height) { * rectangle to draw. * @param {Number} w The width of the rectangle to draw. * @param {Number} h The height of the rectangle to draw. - * @param {Number} r The red component of the color of the rectangle. - * @param {Number} g The green component of the color of the rectangle. - * @param {Number} b The blue component of the color of the rectangle. - * @param {Number} a The alpha component of the color of the rectangle. */ - this.drawRect = function(x, y, w, h, r, g, b, a) { + this.rect = function(x, y, w, h) { scheduleTask(function() { + + // Start a new path if current path is closed + if (pathClosed) { + displayContext.beginPath(); + pathClosed = false; + } + if (layer.autosize != 0) fitRect(x, y, w, h); - displayContext.fillStyle = "rgba(" - + r + "," + g + "," + b + "," + a / 255 + ")"; - displayContext.fillRect(x, y, w, h); + displayContext.rect(x, y, w, h); + }); }; /** - * Clip all future drawing operations by the specified rectangle. + * Clip all future drawing operations by the current path. The current path + * is implicitly closed. The current path can continue to be reused + * for other operations (such as fillColor()) but a new path will be started + * once a path drawing operation (path() or rect()) is used. + */ + this.clip = function() { + scheduleTask(function() { + + // Set new clipping region + displayContext.clip(); + + // Path now implicitly closed + pathClosed = true; + + }); + }; + + /** + * Stroke the current path with the specified color. The current path + * is implicitly closed. The current path can continue to be reused + * for other operations (such as clip()) but a new path will be started + * once a path drawing operation (path() or rect()) is used. * - * @param {Number} x The X coordinate of the upper-left corner of the - * rectangle to use for the clipping region. - * @param {Number} y The Y coordinate of the upper-left corner of the - * rectangle to use for the clipping region. - * @param {Number} w The width of the rectangle to use for the clipping region. - * @param {Number} h The height of the rectangle to use for the clipping region. + * @param {Number} r The red component of the color to fill. + * @param {Number} g The green component of the color to fill. + * @param {Number} b The blue component of the color to fill. + * @param {Number} a The alpha component of the color to fill. */ - this.clipRect = function(x, y, w, h) { + this.strokeColor = function(r, g, b, a) { scheduleTask(function() { - // Clear any current clipping region - displayContext.restore(); + // Stroke with color + displayContext.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; + displayContext.stroke(); + + // Path now implicitly closed + pathClosed = true; + + }); + }; + + + /** + * Fills the current path with the specified color. The current path + * is implicitly closed. The current path can continue to be reused + * for other operations (such as clip()) but a new path will be started + * once a path drawing operation (path() or rect()) is used. + * + * @param {Number} r The red component of the color to fill. + * @param {Number} g The green component of the color to fill. + * @param {Number} b The blue component of the color to fill. + * @param {Number} a The alpha component of the color to fill. + */ + this.fillColor = function(r, g, b, a) { + scheduleTask(function() { + + // Fill with color + displayContext.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; + displayContext.fill(); + + // Path now implicitly closed + pathClosed = true; + + }); + }; + + /** + * Push current layer state onto stack. + */ + this.push = function() { + scheduleTask(function() { + + // Save current state onto stack displayContext.save(); + stackSize++; - if (layer.autosize != 0) fitRect(x, y, w, h); + }); + }; - // Set new clipping region - displayContext.beginPath(); - displayContext.rect(x, y, w, h); - displayContext.clip(); + /** + * Pop layer state off stack. + */ + this.pop = function() { + scheduleTask(function() { + + // Restore current state from stack + if (stackSize > 0) { + displayContext.restore(); + stackSize--; + } }); }; /** - * Provides the given filtering function with a writable snapshot of - * image data and the current width and height of the Layer. - * - * @param {function} filter A function which accepts an array of image - * data (as returned by the canvas element's - * display context's getImageData() function), - * the width of the Layer, and the height of the - * Layer as parameters, in that order. This - * function must accomplish its filtering by - * modifying the given image data array directly. - */ - this.filter = function(filter) { + * Reset the layer, clearing the stack, the current path, and any transform + * matrix. + */ + this.reset = function() { scheduleTask(function() { - var imageData = displayContext.getImageData(0, 0, width, height); - filter(imageData.data, width, height); - displayContext.putImageData(imageData, 0, 0); + + // Clear stack + while (stackSize > 0) { + displaycontext.restore(); + stackSize--; + } + + // Clear transform + displayContext.setTransform( + 1, 0, 0, + 0, 1, 0 + /*0, 0, 1*/ + ); + + // Clear path + displayContext.beginPath(); + pathClosed = false; + }); }; -- 1.7.10.4