/* * 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( $ ){ /** * @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.Rect} 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} drawingHandler - Method for firing the drawing event. * drawingHandler({context, tile, rendered}) * where rendered is the context with the pre-drawn image. */ drawCanvas: function( context, drawingHandler ) { var position = this.position, size = this.size, rendered, canvas; if (!this.cacheImageRecord) { $.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached', this.toString()); return; } rendered = this.cacheImageRecord.getRenderedContext(); if ( !this.loaded || !( this.image || rendered) ){ $.console.warn( "Attempting to draw tile %s when it's not yet loaded.", this.toString() ); return; } context.globalAlpha = this.opacity; //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 * $.pixelDensityRatio)+1, (position.y * $.pixelDensityRatio)+1, (size.x * $.pixelDensityRatio)-2, (size.y * $.pixelDensityRatio)-2 ); } if(!rendered){ canvas = document.createElement( 'canvas' ); canvas.width = this.image.width; canvas.height = this.image.height; rendered = canvas.getContext('2d'); rendered.drawImage( this.image, 0, 0 ); this.cacheImageRecord.setRenderedContext(rendered); //since we are caching the prerendered image on a canvas //allow the image to not be held in memory this.image = null; } // This gives the application a chance to make image manipulation changes as we are rendering the image drawingHandler({context: context, tile: this, rendered: rendered}); context.drawImage( rendered.canvas, 0, 0, rendered.canvas.width, rendered.canvas.height, position.x * $.pixelDensityRatio, position.y * $.pixelDensityRatio, size.x * $.pixelDensityRatio, size.y * $.pixelDensityRatio ); }, /** * 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 ); } this.element = null; this.imgElement = null; this.image = null; this.loaded = false; this.loading = false; } }; }( OpenSeadragon ));