/* * 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 * @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. * * @property {Number} level The zoom level this tile belongs to. * @property {Number} x The vector component 'x'. * @property {Number} y The vector component 'y'. * @property {OpenSeadragon.Point} bounds Where this tile fits, in normalized * coordinates * @property {Boolean} exists Is this tile a part of a sparse image? ( Also has * this tile failed to load? * @property {String} url The URL of this tile's image. * @property {Boolean} loaded Is this tile loaded? * @property {Boolean} loading Is this tile loading * @property {Element} element The HTML div element for this tile * @property {Element} imgElement The HTML img element for this tile * @property {Image} image The Image object for this tile * @property {String} style The alias of this.element.style. * @property {String} position This tile's position on screen, in pixels. * @property {String} size This tile's size on screen, in pixels * @property {String} blendStart The start time of this tile's blending * @property {String} opacity The current opacity this tile should be. * @property {String} distance The distance of this tile to the viewport center * @property {String} visibility The visibility score of this tile. * @property {Boolean} beingDrawn Whether this tile is currently being drawn * @property {Number} lastTouchTime Timestamp the tile was last touched. */ $.Tile = function(level, x, y, bounds, exists, url) { this.level = level; this.x = x; this.y = y; this.bounds = bounds; this.exists = exists; this.url = url; this.loaded = false; this.loading = false; this.element = null; this.imgElement = null; this.image = null; this.style = null; this.position = null; this.size = null; this.blendStart = null; this.opacity = null; this.distance = null; this.visibility = null; this.beingDrawn = false; this.lastTouchTime = 0; }; $.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 */ drawCanvas: function( context ) { 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 ]; //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 it's contianer. * @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 ));