From f2682f54e6e522252767510410cdba957f69f0b0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 11 Mar 2012 21:00:08 -0700 Subject: [PATCH] Implemented remaining operations as possible. --- src/main/resources/layer.js | 250 ++++++++++++++++++++++++++++++------------- 1 file changed, 175 insertions(+), 75 deletions(-) diff --git a/src/main/resources/layer.js b/src/main/resources/layer.js index faac6ff..f78e519 100644 --- a/src/main/resources/layer.js +++ b/src/main/resources/layer.js @@ -301,6 +301,68 @@ Guacamole.Layer = function(width, height) { } /** + * Schedules a task within the current layer just as scheduleTast() does, + * except that another specified layer will be blocked until this task + * completes, and this task will not start until the other layer is + * ready. + * + * Essentially, a task is scheduled in both layers, and the specified task + * will only be performed once both layers are ready, and neither layer may + * proceed until this task completes. + * + * Note that there is no way to specify whether the task starts blocked, + * as whether the task is blocked depends completely on whether the + * other layer is currently ready. + * + * @param {Guacamole.Layer} otherLayer The other layer which must be blocked + * until this task completes. + * @param {function} handler The function to call when possible. + */ + function scheduleTaskSynced(otherLayer, handler) { + + // If we ARE the other layer, no need to sync. + // Syncing would result in deadlock. + if (layer === otherLayer) + scheduleTask(handler); + + // Otherwise synchronize operation with other layer + else { + + var drawComplete = false; + var layerLock = null; + + function performTask() { + + // Perform task + handler(); + + // Unblock the other layer now that draw is complete + if (layerLock != null) + layerLock.unblock(); + + // Flag operation as done + drawComplete = true; + + } + + // Currently blocked draw task + var task = scheduleTask(performTask, true); + + // Unblock draw task once source layer is ready + otherLayer.sync(task.unblock); + + // Block other layer until draw completes + // Note that the draw MAY have already been performed at this point, + // in which case creating a lock on the other layer will lead to + // deadlock (the draw task has already run and will thus never + // clear the lock) + if (!drawComplete) + layerLock = otherLayer.sync(null, true); + + } + } + + /** * Set to true if this Layer should resize itself to accomodate the * dimensions of any drawing operation, and false (the default) otherwise. * @@ -424,11 +486,8 @@ Guacamole.Layer = function(width, height) { * destination. */ this.transfer = function(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction) { + scheduleTaskSynced(srcLayer, function() { - var drawComplete = false; - var srcLock = null; - - function doTransfer() { if (layer.autosize != 0) fitRect(x, y, srcw, srch); var srcCanvas = srcLayer.getCanvas(); @@ -473,38 +532,7 @@ Guacamole.Layer = function(width, height) { } - // Unblock the source layer now that draw is complete - if (srcLock != null) - srcLock.unblock(); - - // Flag operation as done - drawComplete = true; - } - - // If we ARE the source layer, no need to sync. - // Syncing would result in deadlock. - if (layer === srcLayer) - scheduleTask(doTransfer); - - // Otherwise synchronize copy operation with source layer - else { - - // Currently blocked draw task - var task = scheduleTask(doTransfer, true); - - // Unblock draw task once source layer is ready - srcLayer.sync(task.unblock); - - // Block source layer until draw completes - // Note that the draw MAY have already been performed at this point, - // in which case creating a lock on the source layer will lead to - // deadlock (the draw task has already run and will thus never - // clear the lock) - if (!drawComplete) - srcLock = srcLayer.sync(null, true); - - } - + }); }; /** @@ -529,49 +557,14 @@ Guacamole.Layer = function(width, height) { * @param {Number} y The destination Y coordinate. */ this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) { - - var drawComplete = false; - var srcLock = null; - - function doCopy() { + scheduleTaskSynced(srcLayer, function() { if (layer.autosize != 0) fitRect(x, y, srcw, srch); var srcCanvas = srcLayer.getCanvas(); if (srcCanvas.width != 0 && srcCanvas.height != 0) displayContext.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch); - // Unblock the source layer now that draw is complete - if (srcLock != null) - srcLock.unblock(); - - // Flag operation as done - drawComplete = true; - } - - // If we ARE the source layer, no need to sync. - // Syncing would result in deadlock. - if (layer === srcLayer) - scheduleTask(doCopy); - - // Otherwise synchronize copy operation with source layer - else { - - // Currently blocked draw task - var task = scheduleTask(doCopy, true); - - // Unblock draw task once source layer is ready - srcLayer.sync(task.unblock); - - // Block source layer until draw completes - // Note that the draw MAY have already been performed at this point, - // in which case creating a lock on the source layer will lead to - // deadlock (the draw task has already run and will thus never - // clear the lock) - if (!drawComplete) - srcLock = srcLayer.sync(null, true); - - } - + }); }; /** @@ -648,15 +641,23 @@ Guacamole.Layer = function(width, height) { * for other operations (such as clip()) but a new path will be started * once a path drawing operation (path() or rect()) is used. * + * @param {String} cap The line cap style. Can be "round", "square", + * or "butt". + * @param {String} join The line join style. Can be "round", "bevel", + * or "miter". + * @param {Number} thickness The line thickness in pixels. * @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.strokeColor = function(r, g, b, a) { + this.strokeColor = function(cap, join, thickness, r, g, b, a) { scheduleTask(function() { // Stroke with color + displayContext.lineCap = cap; + displayContext.lineJoin = join; + displayContext.lineWidth = thickness; displayContext.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; displayContext.stroke(); @@ -666,7 +667,6 @@ Guacamole.Layer = function(width, height) { }); }; - /** * Fills the current path with the specified color. The current path * is implicitly closed. The current path can continue to be reused @@ -692,6 +692,66 @@ Guacamole.Layer = function(width, height) { }; /** + * Stroke the current path with the image within the specified layer. The + * image data will be tiled infinitely within the stroke. 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 {String} cap The line cap style. Can be "round", "square", + * or "butt". + * @param {String} join The line join style. Can be "round", "bevel", + * or "miter". + * @param {Number} thickness The line thickness in pixels. + * @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern + * within the stroke. + */ + this.strokeLayer = function(cap, join, thickness, srcLayer) { + scheduleTaskSynced(srcLayer, function() { + + // Stroke with image data + displayContext.lineCap = cap; + displayContext.lineJoin = join; + displayContext.lineWidth = thickness; + displayContext.strokeStyle = displayContext.createPattern( + srcLayer.getCanvas(), + "repeat" + ); + displayContext.stroke(); + + // Path now implicitly closed + pathClosed = true; + + }); + }; + + /** + * Fills the current path with the image within the specified layer. The + * image data will be tiled infinitely within the stroke. 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 {Guacamole.Layer} srcLayer The layer to use as a repeating pattern + * within the fill. + */ + this.fillLayer = function(srcLayer) { + scheduleTask(function() { + + // Fill with image data + displayContext.fillStyle = displayContext.createPattern( + srcLayer.getCanvas(), + "repeat" + ); + displayContext.fill(); + + // Path now implicitly closed + pathClosed = true; + + }); + }; + + /** * Push current layer state onto stack. */ this.push = function() { @@ -747,6 +807,31 @@ Guacamole.Layer = function(width, height) { }; /** + * Applies the given affine transform (defined with three values from the + * transform's matrix). + * + * @param {Number} a The first value in the affine transform's matrix. + * @param {Number} b The second value in the affine transform's matrix. + * @param {Number} c The third value in the affine transform's matrix. + * @param {Number} d The fourth value in the affine transform's matrix. + * @param {Number} e The fifth value in the affine transform's matrix. + * @param {Number} f The sixth value in the affine transform's matrix. + */ + this.transform = function(a, b, c, d, e, f) { + scheduleTask(function() { + + // Clear transform + displayContext.transform( + a, b, c, + d, e, f + /*0, 0, 1*/ + ); + + }); + }; + + + /** * Sets the channel mask for future operations on this Layer. * * The channel mask is a Guacamole-specific compositing operation identifier @@ -764,6 +849,21 @@ Guacamole.Layer = function(width, height) { }); }; + /** + * Sets the miter limit for stroke operations using the miter join. This + * limit is the maximum ratio of the size of the miter join to the stroke + * width. If this ratio is exceeded, the miter will not be drawn for that + * joint of the path. + * + * @param {Number} limit The miter limit for stroke operations using the + * miter join. + */ + this.setMiterLimit = function(limit) { + scheduleTask(function() { + displayContext.miterLimit = limit; + }); + }; + // Initialize canvas dimensions display.width = width; display.height = height; -- 1.7.10.4