Implemented remaining operations as possible.

This commit is contained in:
Michael Jumper
2012-03-11 21:00:08 -07:00
parent aa1e467791
commit 56facdb6b2

View File

@@ -300,6 +300,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 * Set to true if this Layer should resize itself to accomodate the
* dimensions of any drawing operation, and false (the default) otherwise. * dimensions of any drawing operation, and false (the default) otherwise.
@@ -424,11 +486,8 @@ Guacamole.Layer = function(width, height) {
* destination. * destination.
*/ */
this.transfer = function(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction) { 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); if (layer.autosize != 0) fitRect(x, y, srcw, srch);
var srcCanvas = srcLayer.getCanvas(); 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. * @param {Number} y The destination Y coordinate.
*/ */
this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) { this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) {
scheduleTaskSynced(srcLayer, function() {
var drawComplete = false;
var srcLock = null;
function doCopy() {
if (layer.autosize != 0) fitRect(x, y, srcw, srch); if (layer.autosize != 0) fitRect(x, y, srcw, srch);
var srcCanvas = srcLayer.getCanvas(); var srcCanvas = srcLayer.getCanvas();
if (srcCanvas.width != 0 && srcCanvas.height != 0) if (srcCanvas.width != 0 && srcCanvas.height != 0)
displayContext.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch); 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 * for other operations (such as clip()) but a new path will be started
* once a path drawing operation (path() or rect()) is used. * 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} r The red component of the color to fill.
* @param {Number} g The green 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} b The blue component of the color to fill.
* @param {Number} a The alpha 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() { scheduleTask(function() {
// Stroke with color // Stroke with color
displayContext.lineCap = cap;
displayContext.lineJoin = join;
displayContext.lineWidth = thickness;
displayContext.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; displayContext.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")";
displayContext.stroke(); displayContext.stroke();
@@ -666,7 +667,6 @@ Guacamole.Layer = function(width, height) {
}); });
}; };
/** /**
* Fills the current path with the specified color. The current path * Fills the current path with the specified color. The current path
* is implicitly closed. The current path can continue to be reused * is implicitly closed. The current path can continue to be reused
@@ -691,6 +691,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. * Push current layer state onto stack.
*/ */
@@ -746,6 +806,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. * Sets the channel mask for future operations on this Layer.
* *
@@ -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 // Initialize canvas dimensions
display.width = width; display.width = width;
display.height = height; display.height = height;