/**
* HTML module.
* @module html
*/
var dwv = dwv || {};
dwv.html = dwv.html || {};
/**
* Window layer.
* @class Layer
* @namespace dwv.html
* @constructor
* @param {String} name The name of the layer.
*/
dwv.html.Layer = function(name)
{
/**
* The associated HTMLCanvasElement.
* @property canvas
* @private
* @type Object
*/
var canvas = null;
/**
* A cache of the initial canvas.
* @property cacheCanvas
* @private
* @type Object
*/
var cacheCanvas = null;
/**
* The associated CanvasRenderingContext2D.
* @property context
* @private
* @type Object
*/
var context = null;
/**
* Get the layer name.
* @method getName
* @return {String} The layer name.
*/
this.getName = function() { return name; };
/**
* Get the layer canvas.
* @method getCanvas
* @return {Object} The layer canvas.
*/
this.getCanvas = function() { return canvas; };
/**
* Get the layer context.
* @method getContext
* @return {Object} The layer context.
*/
this.getContext = function() { return context; };
/**
* Get the layer offset on page.
* @method getOffset
* @return {Number} The layer offset on page.
*/
this.getOffset = function() { return $('#'+name).offset(); };
/**
* The image data array.
* @property imageData
* @private
* @type Array
*/
var imageData = null;
/**
* The layer origin.
* @property origin
* @private
* @type {Object}
*/
var origin = {'x': 0, 'y': 0};
/**
* Get the layer origin.
* @method getOrigin
* @returns {Object} The layer origin as {'x','y'}.
*/
this.getOrigin = function () {
return origin;
};
/**
* The image zoom.
* @property zoom
* @private
* @type {Object}
*/
var zoom = {'x': 1, 'y': 1};
/**
* Get the layer zoom.
* @method getZoom
* @returns {Object} The layer zoom as {'x','y'}.
*/
this.getZoom = function () {
return zoom;
};
/**
* Set the canvas width.
* @method setWidth
* @param {Number} width The new width.
*/
this.setWidth = function ( width ) {
canvas.width = width;
};
/**
* Set the canvas height.
* @method setHeight
* @param {Number} height The new height.
*/
this.setHeight = function ( height ) {
canvas.height = height;
};
/**
* Set the layer zoom.
* @method setZoom
* @param {Number} newZoomX The zoom in the X direction.
* @param {Number} newZoomY The zoom in the Y direction.
* @param {Number} centerX The zoom center in the X direction.
* @param {Number} centerY The zoom center in the Y direction.
*/
this.zoom = function(newZoomX,newZoomY,centerX,centerY)
{
// The zoom is the ratio between the differences from the center
// to the origins:
// centerX - originX = ( centerX - originX0 ) * zoomX
// (center in ~world coordinate system)
//originX = (centerX / zoomX) + originX - (centerX / newZoomX);
//originY = (centerY / zoomY) + originY - (centerY / newZoomY);
// center in image coordinate system
origin.x = centerX - (centerX - origin.x) * (newZoomX / zoom.x);
origin.y = centerY - (centerY - origin.y) * (newZoomY / zoom.y);
// save zoom
zoom.x = newZoomX;
zoom.y = newZoomY;
};
/**
* Set the layer translation.
* Translation is according to the last one.
* @method setTranslate
* @param {Number} tx The translation in the X direction.
* @param {Number} ty The translation in the Y direction.
*/
this.translate = function(tx,ty)
{
// new origin
origin.x += tx * zoom.x;
origin.y += ty * zoom.y;
};
/**
* Set the image data array.
* @method setImageData
* @param {Array} data The data array.
*/
this.setImageData = function(data)
{
imageData = data;
// update the cached canvas
cacheCanvas.getContext("2d").putImageData(imageData, 0, 0);
};
/**
* Reset the layout.
* @method resetLayout
*/
this.resetLayout = function(izoom)
{
origin.x = 0;
origin.y = 0;
zoom.x = izoom;
zoom.y = izoom;
};
/**
* Transform a display position to an index.
* @method displayToIndex
*/
this.displayToIndex = function ( point2D ) {
return {'x': (point2D.x - origin.x) / zoom.x,
'y': (point2D.y - origin.y) / zoom.y };
};
/**
* Draw the content (imageData) of the layer.
* The imageData variable needs to be set
* @method draw
*/
this.draw = function ()
{
// clear the context: reset the transform first
// store the current transformation matrix
context.save();
// use the identity matrix while clearing the canvas
context.setTransform( 1, 0, 0, 1, 0, 0 );
context.clearRect( 0, 0, canvas.width, canvas.height );
// restore the transform
context.restore();
// draw the cached canvas on the context
// transform takes as input a, b, c, d, e, f to create
// the transform matrix (column-major order):
// [ a c e ]
// [ b d f ]
// [ 0 0 1 ]
context.setTransform( zoom.x, 0, 0, zoom.y, origin.x, origin.y );
context.drawImage( cacheCanvas, 0, 0 );
};
/**
* Initialise the layer: set the canvas and context
* @method initialise
* @input {Number} inputWidth The width of the canvas.
* @input {Number} inputHeight The height of the canvas.
*/
this.initialise = function(inputWidth, inputHeight)
{
// find the canvas element
canvas = document.getElementById(name);
if (!canvas)
{
alert("Error: cannot find the canvas element for '" + name + "'.");
return;
}
// check that the getContext method exists
if (!canvas.getContext)
{
alert("Error: no canvas.getContext method for '" + name + "'.");
return;
}
// get the 2D context
context = canvas.getContext('2d');
if (!context)
{
alert("Error: failed to get the 2D context for '" + name + "'.");
return;
}
// canvas sizes
canvas.width = inputWidth;
canvas.height = inputHeight;
// original empty image data array
context.clearRect (0, 0, canvas.width, canvas.height);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// cached canvas
cacheCanvas = document.createElement("canvas");
cacheCanvas.width = inputWidth;
cacheCanvas.height = inputHeight;
};
/**
* Fill the full context with the current style.
* @method fillContext
*/
this.fillContext = function()
{
context.fillRect( 0, 0, canvas.width, canvas.height );
};
/**
* Clear the context and reset the image data.
* @method clear
*/
this.clear = function()
{
context.clearRect(0, 0, canvas.width, canvas.height);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
this.resetLayout();
};
/**
* Merge two layers.
* @method merge
* @input {Layer} layerToMerge The layer to merge. It will also be emptied.
*/
this.merge = function(layerToMerge)
{
// basic resampling of the merge data to put it at zoom 1:1
var mergeImageData = layerToMerge.getContext().getImageData(
0, 0, canvas.width, canvas.height);
var offMerge = 0;
var offMergeJ = 0;
var offThis = 0;
var offThisJ = 0;
var alpha = 0;
for( var j=0; j < canvas.height; ++j ) {
offMergeJ = parseInt( (origin.y + j * zoom.y), 10 ) * canvas.width;
offThisJ = j * canvas.width;
for( var i=0; i < canvas.width; ++i ) {
// 4 component data: RGB + alpha
offMerge = 4 * ( parseInt( (origin.x + i * zoom.x), 10 ) + offMergeJ );
offThis = 4 * ( i + offThisJ );
// merge non transparent
alpha = mergeImageData.data[offMerge+3];
if( alpha !== 0 ) {
imageData.data[offThis] = mergeImageData.data[offMerge];
imageData.data[offThis+1] = mergeImageData.data[offMerge+1];
imageData.data[offThis+2] = mergeImageData.data[offMerge+2];
imageData.data[offThis+3] = alpha;
}
}
}
// empty and reset merged layer
layerToMerge.clear();
// draw the layer
this.draw();
};
/**
* Set the line color for the layer.
* @method setLineColor
* @input {String} color The line color.
*/
this.setLineColor = function(color)
{
context.fillStyle = color;
context.strokeStyle = color;
};
/**
* Display the layer.
* @method setStyleDisplay
* @input {Boolean} val Whether to display the layer or not.
*/
this.setStyleDisplay = function(val)
{
if( val === true )
{
canvas.style.display = '';
}
else
{
canvas.style.display = "none";
}
};
/**
* Check if the layer is visible.
* @method isVisible
* @return {Boolean} True if the layer is visible.
*/
this.isVisible = function()
{
if( canvas.style.display === "none" ) {
return false;
}
else {
return true;
}
};
/**
* Align on another layer.
* @method align
* @param {Layer} rhs The layer to align on.
*/
this.align = function(rhs)
{
canvas.style.top = rhs.getCanvas().offsetTop;
canvas.style.left = rhs.getCanvas().offsetLeft;
};
}; // Layer class
/**
* Get the offset of an input event.
* @method getEventOffset
* @static
* @param {Object} event The event to get the offset from.
* @return {Array} The array of offsets.
*/
dwv.html.getEventOffset = function (event) {
var positions = [];
var ex = 0;
var ey = 0;
if ( event.targetTouches ) {
var touch = null;
for ( var i = 0 ; i < event.targetTouches.length; ++i ) {
touch = event.targetTouches[i];
ex = touch.pageX - app.getImageLayer().getOffset().left;
ey = touch.pageY - app.getImageLayer().getOffset().top;
positions.push({'x': ex, 'y': ey});
}
}
else {
// layerX is used by Firefox
ex = event.offsetX === undefined ? event.layerX : event.offsetX;
ey = event.offsetY === undefined ? event.layerY : event.offsetY;
positions.push({'x': ex, 'y': ey});
}
return positions;
};