openseadragon/src/tile.js
Grant Echols be79249c9c Added pre-draw event for tiles to allow applications to alter the image just prior to its rendering. This should enable invert, brightness, contrast, sharpen and other transformations to occur naturally through the 'tile-drawing' event listener.
The listener is given the 'rendered' object which is the pre-drawn image on a canvas context object. By altering the 'rendered' object the listener can alter the display results. Since this event gets fired multiple times for the tile, it is wise for the handler to track what their desired modifications are and to tag the 'tile' element to keep track of the modifications already applied.
2014-02-27 15:35:00 -07:00

329 lines
10 KiB
JavaScript

/*
* OpenSeadragon - Tile
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){
var TILE_CACHE = {};
/**
* @class Tile
* @memberof OpenSeadragon
* @param {Number} level The zoom level this tile belongs to.
* @param {Number} x The vector component 'x'.
* @param {Number} y The vector component 'y'.
* @param {OpenSeadragon.Point} bounds Where this tile fits, in normalized
* coordinates.
* @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
* this tile failed to load? )
* @param {String} url The URL of this tile's image.
*/
$.Tile = function(level, x, y, bounds, exists, url) {
/**
* The zoom level this tile belongs to.
* @member {Number} level
* @memberof OpenSeadragon.Tile#
*/
this.level = level;
/**
* The vector component 'x'.
* @member {Number} x
* @memberof OpenSeadragon.Tile#
*/
this.x = x;
/**
* The vector component 'y'.
* @member {Number} y
* @memberof OpenSeadragon.Tile#
*/
this.y = y;
/**
* Where this tile fits, in normalized coordinates
* @member {OpenSeadragon.Point} bounds
* @memberof OpenSeadragon.Tile#
*/
this.bounds = bounds;
/**
* Is this tile a part of a sparse image? Also has this tile failed to load?
* @member {Boolean} exists
* @memberof OpenSeadragon.Tile#
*/
this.exists = exists;
/**
* The URL of this tile's image.
* @member {String} url
* @memberof OpenSeadragon.Tile#
*/
this.url = url;
/**
* Is this tile loaded?
* @member {Boolean} loaded
* @memberof OpenSeadragon.Tile#
*/
this.loaded = false;
/**
* Is this tile loading?
* @member {Boolean} loading
* @memberof OpenSeadragon.Tile#
*/
this.loading = false;
/**
* The HTML div element for this tile
* @member {Element} element
* @memberof OpenSeadragon.Tile#
*/
this.element = null;
/**
* The HTML img element for this tile.
* @member {Element} imgElement
* @memberof OpenSeadragon.Tile#
*/
this.imgElement = null;
/**
* The Image object for this tile.
* @member {Object} image
* @memberof OpenSeadragon.Tile#
*/
this.image = null;
/**
* The alias of this.element.style.
* @member {String} style
* @memberof OpenSeadragon.Tile#
*/
this.style = null;
/**
* This tile's position on screen, in pixels.
* @member {OpenSeadragon.Point} position
* @memberof OpenSeadragon.Tile#
*/
this.position = null;
/**
* This tile's size on screen, in pixels.
* @member {OpenSeadragon.Point} size
* @memberof OpenSeadragon.Tile#
*/
this.size = null;
/**
* The start time of this tile's blending.
* @member {Number} blendStart
* @memberof OpenSeadragon.Tile#
*/
this.blendStart = null;
/**
* The current opacity this tile should be.
* @member {Number} opacity
* @memberof OpenSeadragon.Tile#
*/
this.opacity = null;
/**
* The distance of this tile to the viewport center.
* @member {Number} distance
* @memberof OpenSeadragon.Tile#
*/
this.distance = null;
/**
* The visibility score of this tile.
* @member {Number} visibility
* @memberof OpenSeadragon.Tile#
*/
this.visibility = null;
/**
* Whether this tile is currently being drawn.
* @member {Boolean} beingDrawn
* @memberof OpenSeadragon.Tile#
*/
this.beingDrawn = false;
/**
* Timestamp the tile was last touched.
* @member {Number} lastTouchTime
* @memberof OpenSeadragon.Tile#
*/
this.lastTouchTime = 0;
};
$.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
/**
* Provides a string representation of this tiles level and (x,y)
* components.
* @function
* @returns {String}
*/
toString: function() {
return this.level + "/" + this.x + "_" + this.y;
},
/**
* Renders the tile in an html container.
* @function
* @param {Element} container
*/
drawHTML: function( container ) {
if ( !this.loaded || !this.image ) {
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
this.toString()
);
return;
}
//EXPERIMENTAL - trying to figure out how to scale the container
// content during animation of the container size.
if ( !this.element ) {
this.element = $.makeNeutralElement( "div" );
this.imgElement = $.makeNeutralElement( "img" );
this.imgElement.src = this.url;
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
this.imgElement.style.width = "100%";
this.imgElement.style.height = "100%";
this.style = this.element.style;
this.style.position = "absolute";
}
if ( this.element.parentNode != container ) {
container.appendChild( this.element );
}
if ( this.imgElement.parentNode != this.element ) {
this.element.appendChild( this.imgElement );
}
this.style.top = this.position.y + "px";
this.style.left = this.position.x + "px";
this.style.height = this.size.y + "px";
this.style.width = this.size.x + "px";
$.setElementOpacity( this.element, this.opacity );
},
/**
* Renders the tile in a canvas-based context.
* @function
* @param {Canvas} context
* @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered})
* where <code>rendered</code> is the context with the pre-drawn image.
*/
drawCanvas: function( context, drawingHandler ) {
var position = this.position,
size = this.size,
rendered,
canvas;
if ( !this.loaded || !( this.image || TILE_CACHE[ this.url ] ) ){
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
this.toString()
);
return;
}
context.globalAlpha = this.opacity;
//context.save();
//if we are supposed to be rendering fully opaque rectangle,
//ie its done fading or fading is turned off, and if we are drawing
//an image with an alpha channel, then the only way
//to avoid seeing the tile underneath is to clear the rectangle
if( context.globalAlpha == 1 && this.url.match('.png') ){
//clearing only the inside of the rectangle occupied
//by the png prevents edge flikering
context.clearRect(
position.x+1,
position.y+1,
size.x-2,
size.y-2
);
}
if( !TILE_CACHE[ this.url ] ){
canvas = document.createElement( 'canvas' );
canvas.width = this.image.width;
canvas.height = this.image.height;
rendered = canvas.getContext('2d');
rendered.drawImage( this.image, 0, 0 );
TILE_CACHE[ this.url ] = rendered;
//since we are caching the prerendered image on a canvas
//allow the image to not be held in memory
this.image = null;
}
rendered = TILE_CACHE[ this.url ];
// This gives the application a chance to make image manipulation changes as we are rendering the image
drawingHandler({context: context, tile: this, rendered: rendered});
//rendered.save();
context.drawImage(
rendered.canvas,
0,
0,
rendered.canvas.width,
rendered.canvas.height,
position.x,
position.y,
size.x,
size.y
);
//rendered.restore();
//context.restore();
},
/**
* Removes tile from its container.
* @function
*/
unload: function() {
if ( this.imgElement && this.imgElement.parentNode ) {
this.imgElement.parentNode.removeChild( this.imgElement );
}
if ( this.element && this.element.parentNode ) {
this.element.parentNode.removeChild( this.element );
}
if ( TILE_CACHE[ this.url ]){
delete TILE_CACHE[ this.url ];
}
this.element = null;
this.imgElement = null;
this.image = null;
this.loaded = false;
this.loading = false;
}
};
}( OpenSeadragon ));