From 6d4d7cc8c6bc74c28d0a98cc863718d91b9bb229 Mon Sep 17 00:00:00 2001 From: thatcher Date: Fri, 1 Mar 2013 08:14:35 -0500 Subject: [PATCH 01/18] niave implementation of prerender for canvas, the first optimization discussed here: http://www.html5rocks.com/en/tutorials/canvas/performance/ --- src/drawer.js | 2 ++ src/tile.js | 42 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index ed9e588c..56d883de 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -1153,6 +1153,7 @@ function drawTiles( drawer, lastDrawn ){ function drawDebugInfo( drawer, tile, count, i ){ if ( USE_CANVAS ) { + drawer.context.save(); drawer.context.lineWidth = 2; drawer.context.font = 'small-caps bold 13px ariel'; drawer.context.strokeStyle = drawer.debugGridColor; @@ -1205,6 +1206,7 @@ function drawDebugInfo( drawer, tile, count, i ){ tile.position.x + 10, tile.position.y + 70 ); + drawer.context.restore(); } } diff --git a/src/tile.js b/src/tile.js index 4624ff01..8add9ede 100644 --- a/src/tile.js +++ b/src/tile.js @@ -1,5 +1,7 @@ (function( $ ){ - + var TILE_CACHE = {}, + TILE_CACHE_STACK = [], + TILE_CACHE_MAX = 256; /** * @class * @param {Number} level The zoom level this tile belongs to. @@ -140,7 +142,9 @@ $.Tile.prototype = { drawCanvas: function( context ) { var position = this.position, - size = this.size; + size = this.size, + rendered, + canvas; if ( !this.loaded || !this.image ) { $.console.warn( @@ -151,9 +155,9 @@ $.Tile.prototype = { } context.globalAlpha = this.opacity; - context.save(); + //context.save(); - //if we are supposed to b rendering fully opaque rectangle, + //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 @@ -168,10 +172,32 @@ $.Tile.prototype = { ); } - - context.drawImage( this.image, position.x, position.y, size.x, size.y ); - context.restore(); + if( !TILE_CACHE[ this.image.src ] ){ + 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.image.src ] = rendered; + } + + rendered = TILE_CACHE[ this.image.src ]; + //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(); }, /** @@ -183,7 +209,7 @@ $.Tile.prototype = { this.element.parentNode.removeChild( this.element ); } - this.element = null; + this.element = null; this.image = null; this.loaded = false; this.loading = false; From 2fc6cc3876b1686f2d08b9089981b2897263a87a Mon Sep 17 00:00:00 2001 From: thatcher Date: Fri, 1 Mar 2013 08:14:35 -0500 Subject: [PATCH 02/18] niave implementation of prerender for canvas, the first optimization discussed here: http://www.html5rocks.com/en/tutorials/canvas/performance/ --- src/drawer.js | 2 ++ src/tile.js | 42 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2fe5c4ba..102085f9 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -1153,6 +1153,7 @@ function drawTiles( drawer, lastDrawn ){ function drawDebugInfo( drawer, tile, count, i ){ if ( USE_CANVAS ) { + drawer.context.save(); drawer.context.lineWidth = 2; drawer.context.font = 'small-caps bold 13px ariel'; drawer.context.strokeStyle = drawer.debugGridColor; @@ -1205,6 +1206,7 @@ function drawDebugInfo( drawer, tile, count, i ){ tile.position.x + 10, tile.position.y + 70 ); + drawer.context.restore(); } } diff --git a/src/tile.js b/src/tile.js index 4624ff01..8add9ede 100644 --- a/src/tile.js +++ b/src/tile.js @@ -1,5 +1,7 @@ (function( $ ){ - + var TILE_CACHE = {}, + TILE_CACHE_STACK = [], + TILE_CACHE_MAX = 256; /** * @class * @param {Number} level The zoom level this tile belongs to. @@ -140,7 +142,9 @@ $.Tile.prototype = { drawCanvas: function( context ) { var position = this.position, - size = this.size; + size = this.size, + rendered, + canvas; if ( !this.loaded || !this.image ) { $.console.warn( @@ -151,9 +155,9 @@ $.Tile.prototype = { } context.globalAlpha = this.opacity; - context.save(); + //context.save(); - //if we are supposed to b rendering fully opaque rectangle, + //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 @@ -168,10 +172,32 @@ $.Tile.prototype = { ); } - - context.drawImage( this.image, position.x, position.y, size.x, size.y ); - context.restore(); + if( !TILE_CACHE[ this.image.src ] ){ + 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.image.src ] = rendered; + } + + rendered = TILE_CACHE[ this.image.src ]; + //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(); }, /** @@ -183,7 +209,7 @@ $.Tile.prototype = { this.element.parentNode.removeChild( this.element ); } - this.element = null; + this.element = null; this.image = null; this.loaded = false; this.loading = false; From 11a0f3b315bcc731412587af0ce00d05b166ddc1 Mon Sep 17 00:00:00 2001 From: thatcher Date: Tue, 5 Mar 2013 07:30:37 -0500 Subject: [PATCH 03/18] improved caching follows existing use of tile.unload to delete prerendered canvas --- Gruntfile.js | 2 +- src/tile.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index bd21d089..4c8f8006 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -136,7 +136,7 @@ module.exports = function(grunt) { grunt.registerTask("copy:release", function() { grunt.file.recurse("build", function(abspath, rootdir, subdir, filename) { var dest = "../site-build/" - + (subdir ? subdir + "/" : "") + + (subdir ? subdir + "/" : '/') + filename; grunt.file.copy(abspath, dest); diff --git a/src/tile.js b/src/tile.js index 8add9ede..4cb6d22e 100644 --- a/src/tile.js +++ b/src/tile.js @@ -146,7 +146,7 @@ $.Tile.prototype = { rendered, canvas; - if ( !this.loaded || !this.image ) { + if ( !this.loaded || !( this.image || TILE_CACHE[ this.url ] ) ){ $.console.warn( "Attempting to draw tile %s when it's not yet loaded.", this.toString() @@ -161,7 +161,7 @@ $.Tile.prototype = { //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.image.src.match('.png') ){ + if( context.globalAlpha == 1 && this.url.match('.png') ){ //clearing only the inside of the rectangle occupied //by the png prevents edge flikering context.clearRect( @@ -173,16 +173,19 @@ $.Tile.prototype = { } - if( !TILE_CACHE[ this.image.src ] ){ + 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.image.src ] = rendered; + 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.image.src ]; + rendered = TILE_CACHE[ this.url ]; //rendered.save(); context.drawImage( rendered.canvas, @@ -207,6 +210,9 @@ $.Tile.prototype = { unload: function() { 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; From 61a844bdc0fce6e430c3f738de1aa00e217a9ca6 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 05:51:31 -0500 Subject: [PATCH 04/18] what was initally a feature branch to work on just canvas prerender, evolved into a feature branch focused on broader ideas discussed in issue #4 - I've basically been able to reduce time spent in drawTile by half. good stuff, thanks to dustmoo for getting us looking into this --- src/drawer.js | 28 ++++++++++++++++++---------- src/navigator.js | 19 ++++++++++++++++++- src/openseadragon.js | 14 +++++++------- src/referencestrip.js | 6 ++++-- src/tile.js | 4 +--- src/tilesource.js | 18 ++++++++++++++++++ 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 102085f9..0792e2e6 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -515,9 +515,15 @@ function updateViewport( drawer ) { ).x; zeroRatioT = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( 0 ), + drawer.source.getPixelRatio( + Math.max( + drawer.source.getClosestLevel( drawer.viewport.containerSize ) - 1, + 0 + ) + ), false ).x; + console.log( "ZERO RATIO T %s", drawer.source.getClosestLevel( drawer.viewport.containerSize ) ); optimalRatio = drawer.immediateRender ? 1 : @@ -850,7 +856,7 @@ function blendTile( drawer, tile, x, y, level, levelOpacity, currentTime ){ } deltaTime = currentTime - tile.blendStart; - opacity = Math.min( 1, deltaTime / ( blendTimeMillis || 1 ) ); + opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1; if ( drawer.alwaysBlend ) { opacity *= levelOpacity; @@ -1080,14 +1086,16 @@ function drawTiles( drawer, lastDrawn ){ //$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position); if( tileSource ){ drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({ - element: $.makeNeutralElement( "div" ), - mouseNavEnabled: false, - showNavigator: false, - showSequenceControl: false, - showNavigationControl: false, - //visibilityRatio: 1, - //debugMode: true, - //debugGridColor: 'red', + element: $.makeNeutralElement( "div" ), + mouseNavEnabled: false, + showNavigator: false, + showSequenceControl: false, + showNavigationControl: false, + //visibilityRatio: 1, + //debugMode: true, + //debugGridColor: 'red', + animationTime: 0, + blentTime: 0.1, tileSources: [ tileSource ] diff --git a/src/navigator.js b/src/navigator.js index 04d17a4d..1a5defdd 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -37,7 +37,10 @@ $.Navigator = function( options ){ showNavigator: false, mouseNavEnabled: false, showNavigationControl: false, - showSequenceControl: false + showSequenceControl: false, + immediateRender: true, + blendTime: 0, + animationTime: 0 }); options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio; @@ -228,6 +231,20 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { }( this.displayRegion.style )); } + }, + + open: function( source ){ + var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio ); + if( source.tileSize > containerSize.x || + source.tileSize > containerSize.y ){ + this.minPixelRatio = Math.min( + containerSize.x, + containerSize.y + ) / source.tileSize; + } else { + this.minPixelRatio = thie.viewer.minPixelRatio; + } + return $.Viewer.prototype.open.apply( this, [ source ] ); } }); diff --git a/src/openseadragon.js b/src/openseadragon.js index c70d7a7b..497f006b 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -473,25 +473,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ wrapHorizontal: false, wrapVertical: false, visibilityRatio: 0.5, - minPixelRatio: 0.5, - minZoomImageRatio: 0.8, - maxZoomPixelRatio: 2, + minPixelRatio: 0.9, defaultZoomLevel: 0, minZoomLevel: null, maxZoomLevel: null, //UI RESPONSIVENESS AND FEEL - springStiffness: 5.0, + springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, zoomPerClick: 2.0, zoomPerScroll: 1.2, zoomPerSecond: 2.0, - animationTime: 1.5, - blendTime: 1.5, + animationTime: 1.0, + blendTime: 0, alwaysBlend: false, autoHideControls: true, immediateRender: false, + minZoomImageRatio: 0.8, + maxZoomPixelRatio: 1.2, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE @@ -530,7 +530,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ //PERFORMANCE SETTINGS imageLoaderLimit: 0, maxImageCacheCount: 200, - timeout: 5000, + timeout: 30000, //INTERFACE RESOURCE SETTINGS prefixUrl: "/images/", diff --git a/src/referencestrip.js b/src/referencestrip.js index 20f8ff03..80bf10d1 100644 --- a/src/referencestrip.js +++ b/src/referencestrip.js @@ -377,11 +377,13 @@ function loadPanels(strip, viewerSize, scroll){ tileSources: [ strip.viewer.tileSources[ i ] ], element: element, navigatorSizeRatio: strip.sizeRatio, - minPixelRatio: strip.minPixelRatio, showNavigator: false, mouseNavEnabled: false, showNavigationControl: false, - showSequenceControl: false + showSequenceControl: false, + immediateRender: true, + blendTime: 0, + animationTime: 0 } ); miniViewer.displayRegion = $.makeNeutralElement( "textarea" ); diff --git a/src/tile.js b/src/tile.js index 4cb6d22e..35e6821b 100644 --- a/src/tile.js +++ b/src/tile.js @@ -1,7 +1,5 @@ (function( $ ){ - var TILE_CACHE = {}, - TILE_CACHE_STACK = [], - TILE_CACHE_MAX = 256; + var TILE_CACHE = {}; /** * @class * @param {Number} level The zoom level this tile belongs to. diff --git a/src/tilesource.js b/src/tilesource.js index 51b0f03f..5f59f84a 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -180,6 +180,24 @@ $.TileSource.prototype = { return new $.Point(rx, ry); }, + + /** + * @function + * @param {Number} level + */ + getClosestLevel: function( rect ) { + var i, + tilesPerSide = Math.floor( Math.max( rect.x, rect.y ) / this.tileSize ), + tiles; + for( i = this.minLevel; i < this.maxLevel; i++ ){ + tiles = this.getNumTiles( i ); + if( Math.max( tiles.x, tiles.y ) + 1 >= tilesPerSide ){ + break; + } + } + return Math.max( 0, i - 1 ); + }, + /** * @function * @param {Number} level From e04813e47746df09fa0be2738403cfeb580828ae Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 06:44:55 -0500 Subject: [PATCH 05/18] minZoomImageRatio should not be less than minPixelRatio to avoid blurry full zoom out. Its worth noting now that several options are 'coupled' but dont have logic which addresses the coupling so as to avoid ill affects from settings that dont make sense for how they are coupled. --- src/openseadragon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 497f006b..9ee4c011 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -490,8 +490,8 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ alwaysBlend: false, autoHideControls: true, immediateRender: false, - minZoomImageRatio: 0.8, - maxZoomPixelRatio: 1.2, + minZoomImageRatio: 0.9, + maxZoomPixelRatio: 1.1, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE From 0bdc4383bde2e0fe8243d39f7715cb76895f6ed6 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 07:35:47 -0500 Subject: [PATCH 06/18] simplifying zoom settings so scroll and click arent significantly different --- src/openseadragon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 9ee4c011..1f45198d 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -482,9 +482,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, - zoomPerClick: 2.0, - zoomPerScroll: 1.2, - zoomPerSecond: 2.0, + zoomPerClick: 1.5, + zoomPerScroll: 1.5, + zoomPerSecond: 1.5, animationTime: 1.0, blendTime: 0, alwaysBlend: false, From 75f7589312e2d2472614bce6a322a486c49a423e Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 07:43:12 -0500 Subject: [PATCH 07/18] simplifying zoom settings so scroll and click arent significantly different --- src/openseadragon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 1f45198d..7c7a84f3 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -482,9 +482,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, - zoomPerClick: 1.5, - zoomPerScroll: 1.5, - zoomPerSecond: 1.5, + zoomPerClick: 2, + zoomPerScroll: 1.1, + zoomPerSecond: 1.0, animationTime: 1.0, blendTime: 0, alwaysBlend: false, From f107aaf0e85d89b90787591aacac031da4cfc664 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 21:53:20 -0500 Subject: [PATCH 08/18] removing debug console log statement per ventero's review notes --- src/drawer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/drawer.js b/src/drawer.js index 0792e2e6..bb1d2a95 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -523,7 +523,6 @@ function updateViewport( drawer ) { ), false ).x; - console.log( "ZERO RATIO T %s", drawer.source.getClosestLevel( drawer.viewport.containerSize ) ); optimalRatio = drawer.immediateRender ? 1 : From 2be165fa70f95993fecc2e94c009007c30a85473 Mon Sep 17 00:00:00 2001 From: thatcher Date: Fri, 1 Mar 2013 08:14:35 -0500 Subject: [PATCH 09/18] niave implementation of prerender for canvas, the first optimization discussed here: http://www.html5rocks.com/en/tutorials/canvas/performance/ --- src/drawer.js | 2 ++ src/tile.js | 42 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2fe5c4ba..102085f9 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -1153,6 +1153,7 @@ function drawTiles( drawer, lastDrawn ){ function drawDebugInfo( drawer, tile, count, i ){ if ( USE_CANVAS ) { + drawer.context.save(); drawer.context.lineWidth = 2; drawer.context.font = 'small-caps bold 13px ariel'; drawer.context.strokeStyle = drawer.debugGridColor; @@ -1205,6 +1206,7 @@ function drawDebugInfo( drawer, tile, count, i ){ tile.position.x + 10, tile.position.y + 70 ); + drawer.context.restore(); } } diff --git a/src/tile.js b/src/tile.js index 4624ff01..8add9ede 100644 --- a/src/tile.js +++ b/src/tile.js @@ -1,5 +1,7 @@ (function( $ ){ - + var TILE_CACHE = {}, + TILE_CACHE_STACK = [], + TILE_CACHE_MAX = 256; /** * @class * @param {Number} level The zoom level this tile belongs to. @@ -140,7 +142,9 @@ $.Tile.prototype = { drawCanvas: function( context ) { var position = this.position, - size = this.size; + size = this.size, + rendered, + canvas; if ( !this.loaded || !this.image ) { $.console.warn( @@ -151,9 +155,9 @@ $.Tile.prototype = { } context.globalAlpha = this.opacity; - context.save(); + //context.save(); - //if we are supposed to b rendering fully opaque rectangle, + //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 @@ -168,10 +172,32 @@ $.Tile.prototype = { ); } - - context.drawImage( this.image, position.x, position.y, size.x, size.y ); - context.restore(); + if( !TILE_CACHE[ this.image.src ] ){ + 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.image.src ] = rendered; + } + + rendered = TILE_CACHE[ this.image.src ]; + //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(); }, /** @@ -183,7 +209,7 @@ $.Tile.prototype = { this.element.parentNode.removeChild( this.element ); } - this.element = null; + this.element = null; this.image = null; this.loaded = false; this.loading = false; From fe0cafea21cedd952358e581ad18b3fc30f3e163 Mon Sep 17 00:00:00 2001 From: thatcher Date: Tue, 5 Mar 2013 07:30:37 -0500 Subject: [PATCH 10/18] improved caching follows existing use of tile.unload to delete prerendered canvas --- Gruntfile.js | 2 +- src/tile.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b043e067..33e45459 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -148,7 +148,7 @@ module.exports = function(grunt) { grunt.registerTask("copy:release", function() { grunt.file.recurse("build", function(abspath, rootdir, subdir, filename) { var dest = "../site-build/" - + (subdir ? subdir + "/" : "") + + (subdir ? subdir + "/" : '/') + filename; grunt.file.copy(abspath, dest); diff --git a/src/tile.js b/src/tile.js index 8add9ede..4cb6d22e 100644 --- a/src/tile.js +++ b/src/tile.js @@ -146,7 +146,7 @@ $.Tile.prototype = { rendered, canvas; - if ( !this.loaded || !this.image ) { + if ( !this.loaded || !( this.image || TILE_CACHE[ this.url ] ) ){ $.console.warn( "Attempting to draw tile %s when it's not yet loaded.", this.toString() @@ -161,7 +161,7 @@ $.Tile.prototype = { //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.image.src.match('.png') ){ + if( context.globalAlpha == 1 && this.url.match('.png') ){ //clearing only the inside of the rectangle occupied //by the png prevents edge flikering context.clearRect( @@ -173,16 +173,19 @@ $.Tile.prototype = { } - if( !TILE_CACHE[ this.image.src ] ){ + 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.image.src ] = rendered; + 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.image.src ]; + rendered = TILE_CACHE[ this.url ]; //rendered.save(); context.drawImage( rendered.canvas, @@ -207,6 +210,9 @@ $.Tile.prototype = { unload: function() { 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; From 86e27c18f70e008f2af8916f7f4fff0aa8294528 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 05:51:31 -0500 Subject: [PATCH 11/18] what was initally a feature branch to work on just canvas prerender, evolved into a feature branch focused on broader ideas discussed in issue #4 - I've basically been able to reduce time spent in drawTile by half. good stuff, thanks to dustmoo for getting us looking into this --- src/drawer.js | 28 ++++++++++++++++++---------- src/navigator.js | 19 ++++++++++++++++++- src/openseadragon.js | 14 +++++++------- src/referencestrip.js | 6 ++++-- src/tile.js | 4 +--- src/tilesource.js | 18 ++++++++++++++++++ 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 102085f9..0792e2e6 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -515,9 +515,15 @@ function updateViewport( drawer ) { ).x; zeroRatioT = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( 0 ), + drawer.source.getPixelRatio( + Math.max( + drawer.source.getClosestLevel( drawer.viewport.containerSize ) - 1, + 0 + ) + ), false ).x; + console.log( "ZERO RATIO T %s", drawer.source.getClosestLevel( drawer.viewport.containerSize ) ); optimalRatio = drawer.immediateRender ? 1 : @@ -850,7 +856,7 @@ function blendTile( drawer, tile, x, y, level, levelOpacity, currentTime ){ } deltaTime = currentTime - tile.blendStart; - opacity = Math.min( 1, deltaTime / ( blendTimeMillis || 1 ) ); + opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1; if ( drawer.alwaysBlend ) { opacity *= levelOpacity; @@ -1080,14 +1086,16 @@ function drawTiles( drawer, lastDrawn ){ //$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position); if( tileSource ){ drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({ - element: $.makeNeutralElement( "div" ), - mouseNavEnabled: false, - showNavigator: false, - showSequenceControl: false, - showNavigationControl: false, - //visibilityRatio: 1, - //debugMode: true, - //debugGridColor: 'red', + element: $.makeNeutralElement( "div" ), + mouseNavEnabled: false, + showNavigator: false, + showSequenceControl: false, + showNavigationControl: false, + //visibilityRatio: 1, + //debugMode: true, + //debugGridColor: 'red', + animationTime: 0, + blentTime: 0.1, tileSources: [ tileSource ] diff --git a/src/navigator.js b/src/navigator.js index 04d17a4d..1a5defdd 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -37,7 +37,10 @@ $.Navigator = function( options ){ showNavigator: false, mouseNavEnabled: false, showNavigationControl: false, - showSequenceControl: false + showSequenceControl: false, + immediateRender: true, + blendTime: 0, + animationTime: 0 }); options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio; @@ -228,6 +231,20 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { }( this.displayRegion.style )); } + }, + + open: function( source ){ + var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio ); + if( source.tileSize > containerSize.x || + source.tileSize > containerSize.y ){ + this.minPixelRatio = Math.min( + containerSize.x, + containerSize.y + ) / source.tileSize; + } else { + this.minPixelRatio = thie.viewer.minPixelRatio; + } + return $.Viewer.prototype.open.apply( this, [ source ] ); } }); diff --git a/src/openseadragon.js b/src/openseadragon.js index b9c0b6dc..aabbf0fb 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -475,25 +475,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ wrapHorizontal: false, wrapVertical: false, visibilityRatio: 0.5, - minPixelRatio: 0.5, - minZoomImageRatio: 0.8, - maxZoomPixelRatio: 2, + minPixelRatio: 0.9, defaultZoomLevel: 0, minZoomLevel: null, maxZoomLevel: null, //UI RESPONSIVENESS AND FEEL - springStiffness: 5.0, + springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, zoomPerClick: 2.0, zoomPerScroll: 1.2, zoomPerSecond: 2.0, - animationTime: 1.5, - blendTime: 1.5, + animationTime: 1.0, + blendTime: 0, alwaysBlend: false, autoHideControls: true, immediateRender: false, + minZoomImageRatio: 0.8, + maxZoomPixelRatio: 1.2, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE @@ -532,7 +532,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ //PERFORMANCE SETTINGS imageLoaderLimit: 0, maxImageCacheCount: 200, - timeout: 5000, + timeout: 30000, //INTERFACE RESOURCE SETTINGS prefixUrl: "/images/", diff --git a/src/referencestrip.js b/src/referencestrip.js index 20f8ff03..80bf10d1 100644 --- a/src/referencestrip.js +++ b/src/referencestrip.js @@ -377,11 +377,13 @@ function loadPanels(strip, viewerSize, scroll){ tileSources: [ strip.viewer.tileSources[ i ] ], element: element, navigatorSizeRatio: strip.sizeRatio, - minPixelRatio: strip.minPixelRatio, showNavigator: false, mouseNavEnabled: false, showNavigationControl: false, - showSequenceControl: false + showSequenceControl: false, + immediateRender: true, + blendTime: 0, + animationTime: 0 } ); miniViewer.displayRegion = $.makeNeutralElement( "textarea" ); diff --git a/src/tile.js b/src/tile.js index 4cb6d22e..35e6821b 100644 --- a/src/tile.js +++ b/src/tile.js @@ -1,7 +1,5 @@ (function( $ ){ - var TILE_CACHE = {}, - TILE_CACHE_STACK = [], - TILE_CACHE_MAX = 256; + var TILE_CACHE = {}; /** * @class * @param {Number} level The zoom level this tile belongs to. diff --git a/src/tilesource.js b/src/tilesource.js index 3b64821b..11cc499a 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -180,6 +180,24 @@ $.TileSource.prototype = { return new $.Point(rx, ry); }, + + /** + * @function + * @param {Number} level + */ + getClosestLevel: function( rect ) { + var i, + tilesPerSide = Math.floor( Math.max( rect.x, rect.y ) / this.tileSize ), + tiles; + for( i = this.minLevel; i < this.maxLevel; i++ ){ + tiles = this.getNumTiles( i ); + if( Math.max( tiles.x, tiles.y ) + 1 >= tilesPerSide ){ + break; + } + } + return Math.max( 0, i - 1 ); + }, + /** * @function * @param {Number} level From a5421917a45e7e4b32ab022ff0ea2e9c0f91e716 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 06:44:55 -0500 Subject: [PATCH 12/18] minZoomImageRatio should not be less than minPixelRatio to avoid blurry full zoom out. Its worth noting now that several options are 'coupled' but dont have logic which addresses the coupling so as to avoid ill affects from settings that dont make sense for how they are coupled. --- src/openseadragon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index aabbf0fb..f9282ebb 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -492,8 +492,8 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ alwaysBlend: false, autoHideControls: true, immediateRender: false, - minZoomImageRatio: 0.8, - maxZoomPixelRatio: 1.2, + minZoomImageRatio: 0.9, + maxZoomPixelRatio: 1.1, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE From 464cf076a64a55258aa0338de9af73cdef5b272a Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 07:35:47 -0500 Subject: [PATCH 13/18] simplifying zoom settings so scroll and click arent significantly different --- src/openseadragon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index f9282ebb..fa047b2f 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -484,9 +484,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, - zoomPerClick: 2.0, - zoomPerScroll: 1.2, - zoomPerSecond: 2.0, + zoomPerClick: 1.5, + zoomPerScroll: 1.5, + zoomPerSecond: 1.5, animationTime: 1.0, blendTime: 0, alwaysBlend: false, From 537ea0b844d6b1db20fa777382e3c440b4218938 Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 07:43:12 -0500 Subject: [PATCH 14/18] simplifying zoom settings so scroll and click arent significantly different --- src/openseadragon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index fa047b2f..3afbaf58 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -484,9 +484,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ springStiffness: 7.0, clickTimeThreshold: 300, clickDistThreshold: 5, - zoomPerClick: 1.5, - zoomPerScroll: 1.5, - zoomPerSecond: 1.5, + zoomPerClick: 2, + zoomPerScroll: 1.1, + zoomPerSecond: 1.0, animationTime: 1.0, blendTime: 0, alwaysBlend: false, From b9741daaaf11c4ce8047492178203e1e6504a3eb Mon Sep 17 00:00:00 2001 From: thatcher Date: Wed, 6 Mar 2013 21:53:20 -0500 Subject: [PATCH 15/18] removing debug console log statement per ventero's review notes --- src/drawer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/drawer.js b/src/drawer.js index 0792e2e6..bb1d2a95 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -523,7 +523,6 @@ function updateViewport( drawer ) { ), false ).x; - console.log( "ZERO RATIO T %s", drawer.source.getClosestLevel( drawer.viewport.containerSize ) ); optimalRatio = drawer.immediateRender ? 1 : From f0c7870f4eed915c072ad21573cbd8d258b99c89 Mon Sep 17 00:00:00 2001 From: thatcher Date: Thu, 7 Mar 2013 23:41:46 -0500 Subject: [PATCH 16/18] cleaning up new setting as applied to collections --- src/drawer.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index bb1d2a95..85bab42a 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -1090,11 +1090,6 @@ function drawTiles( drawer, lastDrawn ){ showNavigator: false, showSequenceControl: false, showNavigationControl: false, - //visibilityRatio: 1, - //debugMode: true, - //debugGridColor: 'red', - animationTime: 0, - blentTime: 0.1, tileSources: [ tileSource ] From 0b75a2f2598ff0f2645b247146ccfaa6e0cf9517 Mon Sep 17 00:00:00 2001 From: thatcher Date: Thu, 14 Mar 2013 07:22:15 -0400 Subject: [PATCH 17/18] tweaking default setting to find optimal performance point while preserving responsivness --- src/openseadragon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 3afbaf58..f0c1fe4e 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -485,10 +485,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ clickTimeThreshold: 300, clickDistThreshold: 5, zoomPerClick: 2, - zoomPerScroll: 1.1, + zoomPerScroll: 1.2, zoomPerSecond: 1.0, - animationTime: 1.0, - blendTime: 0, + animationTime: 1.2, + blendTime: 0.1, alwaysBlend: false, autoHideControls: true, immediateRender: false, From 10f60c88cb044f69405388903cef08342496e3ec Mon Sep 17 00:00:00 2001 From: thatcher Date: Thu, 14 Mar 2013 08:12:01 -0400 Subject: [PATCH 18/18] minImageRatio should never be set below 1 unless you want to load the Nth level tiles to level N-M, which effectively throttles network requests and draw animations but does not produce a better picture --- src/openseadragon.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index f0c1fe4e..d5ab3231 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -474,8 +474,8 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ constrainDuringPan: false, wrapHorizontal: false, wrapVertical: false, - visibilityRatio: 0.5, - minPixelRatio: 0.9, + visibilityRatio: 0.5, //-> how much of the viewer can be negative space + minPixelRatio: 1, //->closer to 0 draws tiles meant for a higher zoom at this zoom defaultZoomLevel: 0, minZoomLevel: null, maxZoomLevel: null, @@ -488,12 +488,12 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ zoomPerScroll: 1.2, zoomPerSecond: 1.0, animationTime: 1.2, - blendTime: 0.1, + blendTime: 0, alwaysBlend: false, autoHideControls: true, immediateRender: false, - minZoomImageRatio: 0.9, - maxZoomPixelRatio: 1.1, + minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity + maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE