*/
function resize(newWidth, newHeight) {
-
// Only preserve old data if width/height are both non-zero
var oldData = null;
if (width != 0 && height != 0) {
}
+ // Preserve composite operation
+ var oldCompositeOperation = displayContext.globalCompositeOperation;
+
// Resize canvas
display.width = newWidth;
display.height = newHeight;
0, 0, width, height,
0, 0, width, height);
+ // Restore composite operation
+ displayContext.globalCompositeOperation = oldCompositeOperation;
+
width = newWidth;
height = newHeight;
+
}
/**
function scheduleTask(handler, blocked) {
// If no pending tasks, just call (if available) and exit
- if (layer.isReady() && !blocked && handler != null) {
- handler();
+ if (layer.isReady() && !blocked) {
+ if (handler) handler();
return null;
}
*
* @param {function} handler The function to call once all currently
* pending operations are complete.
+ * @param {boolean} blocked Whether the task should start blocked.
+ */
+ this.sync = scheduleTask;
+
+ /**
+ * Transfer a rectangle of image data from one Layer to this Layer using the
+ * specified transfer function.
+ *
+ * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.
+ * @param {Number} srcx The X coordinate of the upper-left corner of the
+ * rectangle within the source Layer's coordinate
+ * space to copy data from.
+ * @param {Number} srcy The Y coordinate of the upper-left corner of the
+ * rectangle within the source Layer's coordinate
+ * space to copy data from.
+ * @param {Number} srcw The width of the rectangle within the source Layer's
+ * coordinate space to copy data from.
+ * @param {Number} srch The height of the rectangle within the source
+ * Layer's coordinate space to copy data from.
+ * @param {Number} x The destination X coordinate.
+ * @param {Number} y The destination Y coordinate.
+ * @param {Function} transferFunction The transfer function to use to
+ * transfer data from source to
+ * destination.
*/
- this.sync = function(handler) {
- return scheduleTask(handler);
+ this.transfer = function(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction) {
+
+ var drawComplete = false;
+ var srcLock = null;
+
+ function doTransfer() {
+ if (layer.autosize != 0) fitRect(x, y, srcw, srch);
+
+ var srcCanvas = srcLayer.getCanvas();
+ if (srcCanvas.width != 0 && srcCanvas.height != 0) {
+
+ // Get image data from src and dst
+ var src = srcLayer.getCanvas().getContext("2d").getImageData(srcx, srcy, srcw, srch);
+ var dst = displayContext.getImageData(x , y, srcw, srch);
+
+ // Apply transfer for each pixel
+ for (var i=0; i<srcw*srch*4; i+=4) {
+
+ // Get source pixel environment
+ var src_pixel = new Guacamole.Layer.Pixel(
+ src.data[i],
+ src.data[i+1],
+ src.data[i+2],
+ src.data[i+3]
+ );
+
+ // Get destination pixel environment
+ var dst_pixel = new Guacamole.Layer.Pixel(
+ dst.data[i],
+ dst.data[i+1],
+ dst.data[i+2],
+ dst.data[i+3]
+ );
+
+ // Apply transfer function
+ transferFunction(src_pixel, dst_pixel);
+
+ // Save pixel data
+ dst.data[i ] = dst_pixel.red;
+ dst.data[i+1] = dst_pixel.green;
+ dst.data[i+2] = dst_pixel.blue;
+ dst.data[i+3] = dst_pixel.alpha;
+
+ }
+
+ // Draw image data
+ displayContext.putImageData(dst, x, y);
+
+ }
+
+ // 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);
+
+ }
+
};
/**
*/
this.copyRect = function(srcLayer, srcx, srcy, srcw, srch, x, y) {
+ var drawComplete = false;
var srcLock = null;
function doCopyRect() {
if (srcCanvas.width != 0 && srcCanvas.height != 0)
displayContext.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch);
- // Unblock the copy complete task, if it exists
+ // 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.
// Otherwise synchronize copy operation with source layer
else {
- // Task which will be unblocked only when the source layer is ready
- // This task will perform the copy
+ // Currently blocked draw task
var task = scheduleTask(doCopyRect, true);
- // Task which will unblock the drawing task.
- var srcReady = srcLayer.sync(task.unblock);
+ // Unblock draw task once source layer is ready
+ srcLayer.sync(task.unblock);
- // Task which will be unblocked only after the copy has
- // occurred (this will only be created if the draw task
- // was postponed)
- if (srcReady != null) srcLock = srcLayer.sync(null, true);
+ // 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);
}
};
/**
- * Sets the channel mask for future operations on this Layer. The channel
- * mask is a Guacamole-specific compositing operation identifier with a
- * single bit representing each of four channels (in order): source image
- * where destination transparent, source where destination opaque,
+ * Sets the channel mask for future operations on this Layer.
+ *
+ * The channel mask is a Guacamole-specific compositing operation identifier
+ * with a single bit representing each of four channels (in order): source
+ * image where destination transparent, source where destination opaque,
* destination where source transparent, and destination where source
* opaque.
*
display.height = height;
};
+
+/**
+ * Channel mask for the composite operation "rout".
+ */
+Guacamole.Layer.ROUT = 0x2;
+
+/**
+ * Channel mask for the composite operation "atop".
+ */
+Guacamole.Layer.ATOP = 0x6;
+
+/**
+ * Channel mask for the composite operation "xor".
+ */
+Guacamole.Layer.XOR = 0xA;
+
+/**
+ * Channel mask for the composite operation "rover".
+ */
+Guacamole.Layer.ROVER = 0xB;
+
+/**
+ * Channel mask for the composite operation "over".
+ */
+Guacamole.Layer.OVER = 0xE;
+
+/**
+ * Channel mask for the composite operation "plus".
+ */
+Guacamole.Layer.PLUS = 0xF;
+
+/**
+ * Channel mask for the composite operation "rin".
+ * Beware that WebKit-based browsers may leave the contents of the destionation
+ * layer where the source layer is transparent, despite the definition of this
+ * operation.
+ */
+Guacamole.Layer.RIN = 0x1;
+
+/**
+ * Channel mask for the composite operation "in".
+ * Beware that WebKit-based browsers may leave the contents of the destionation
+ * layer where the source layer is transparent, despite the definition of this
+ * operation.
+ */
+Guacamole.Layer.IN = 0x4;
+
+/**
+ * Channel mask for the composite operation "out".
+ * Beware that WebKit-based browsers may leave the contents of the destionation
+ * layer where the source layer is transparent, despite the definition of this
+ * operation.
+ */
+Guacamole.Layer.OUT = 0x8;
+
+/**
+ * Channel mask for the composite operation "ratop".
+ * Beware that WebKit-based browsers may leave the contents of the destionation
+ * layer where the source layer is transparent, despite the definition of this
+ * operation.
+ */
+Guacamole.Layer.RATOP = 0x9;
+
+/**
+ * Channel mask for the composite operation "src".
+ * Beware that WebKit-based browsers may leave the contents of the destionation
+ * layer where the source layer is transparent, despite the definition of this
+ * operation.
+ */
+Guacamole.Layer.SRC = 0xC;
+
+
+/**
+ * Represents a single pixel of image data. All components have a minimum value
+ * of 0 and a maximum value of 255.
+ *
+ * @constructor
+ *
+ * @param {Number} r The red component of this pixel.
+ * @param {Number} g The green component of this pixel.
+ * @param {Number} b The blue component of this pixel.
+ * @param {Number} a The alpha component of this pixel.
+ */
+Guacamole.Layer.Pixel = function(r, g, b, a) {
+
+ /**
+ * The red component of this pixel, where 0 is the minimum value,
+ * and 255 is the maximum.
+ */
+ this.red = r;
+
+ /**
+ * The green component of this pixel, where 0 is the minimum value,
+ * and 255 is the maximum.
+ */
+ this.green = g;
+
+ /**
+ * The blue component of this pixel, where 0 is the minimum value,
+ * and 255 is the maximum.
+ */
+ this.blue = b;
+
+ /**
+ * The alpha component of this pixel, where 0 is the minimum value,
+ * and 255 is the maximum.
+ */
+ this.alpha = a;
+
+};