From 4be56cdb524fed468f0acb36e1df147856ad265c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 20 Mar 2014 15:39:31 -0400 Subject: [PATCH 1/3] Overlays performance optimization. See #359 --- src/overlay.js | 39 +++++++++++++++++++++++++-------------- src/viewer.js | 3 ++- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/overlay.js b/src/overlay.js index accb6ea4..c59d8c35 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -78,7 +78,11 @@ * @param {OpenSeadragon.OverlayPlacement} [options.placement=OpenSeadragon.OverlayPlacement.TOP_LEFT] * Relative position to the viewport. * Only used if location is a {@link OpenSeadragon.Point}. - * @param {OpenSeadragon.Overlay.OnDrawCallback} options.onDraw + * @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw] + * @param {Boolean} [options.checkResize=true] Set to false to avoid to + * check the size of the overlay everytime it is drawn when using a + * {@link OpenSeadragon.Point} as options.location. It will improve + * performances but will cause a misalignment if the overlay size changes. */ $.Overlay = function( element, location, placement ) { @@ -125,6 +129,8 @@ options.placement : $.OverlayPlacement.TOP_LEFT; this.onDraw = options.onDraw; + this.checkResize = options.checkResize === undefined ? + true : options.checkResize; }; $.Overlay.prototype = /** @lends OpenSeadragon.Overlay.prototype */{ @@ -212,19 +218,12 @@ var element = this.element, style = this.style, scales = this.scales, - drawerCenter = new $.Point( - viewport.viewer.drawer.canvas.width / 2, - viewport.viewer.drawer.canvas.height / 2 - ), degrees = viewport.degrees, position = viewport.pixelFromPoint( this.bounds.getTopLeft(), true ), - size = viewport.deltaPixelsFromPoints( - this.bounds.getSize(), - true - ), + size, overlayCenter; if ( element.parentNode != container ) { @@ -232,10 +231,18 @@ element.prevElementParent = element.parentNode; element.prevNextSibling = element.nextSibling; container.appendChild( element ); + this.size = $.getElementSize( element ); } - if ( !scales ) { - this.size = $.getElementSize( element ); + if ( scales ) { + size = viewport.deltaPixelsFromPoints( + this.bounds.getSize(), + true + ); + } else if ( this.checkResize ) { + size = $.getElementSize( element ); + } else { + size = this.size; } this.position = position; @@ -254,6 +261,10 @@ if( degrees !== 0 && this.scales ) { overlayCenter = new $.Point( size.x / 2, size.y / 2 ); + var drawerCenter = new $.Point( + viewport.viewer.drawer.canvas.width / 2, + viewport.viewer.drawer.canvas.height / 2 + ); position = position.plus( overlayCenter ).rotate( degrees, drawerCenter @@ -263,10 +274,10 @@ size = new $.Point( Math.abs( size.x ), Math.abs( size.y ) ); } - // call the onDraw callback if there is one to allow, this allows someone to overwrite + // call the onDraw callback if it exists to allow one to overwrite // the drawing/positioning/sizing of the overlay - if (this.onDraw) { - this.onDraw(position, size, element); + if ( this.onDraw ) { + this.onDraw( position, size, element ); } else { style.left = position.x + "px"; style.top = position.y + "px"; diff --git a/src/viewer.js b/src/viewer.js index 157e6ef1..afd7881f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1811,7 +1811,8 @@ function getOverlayObject( viewer, overlay ) { element: element, location: location, placement: placement, - onDraw: overlay.onDraw + onDraw: overlay.onDraw, + checkResize: overlay.checkResize }); } From 4a5ccea2f71e6d14810714295e07aab8098ec63b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Fri, 21 Mar 2014 15:55:04 -0400 Subject: [PATCH 2/3] Update overlays unit tests. --- src/viewer.js | 48 +++++++---- test/overlays.js | 217 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 237 insertions(+), 28 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index afd7881f..05cdbb8d 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1331,6 +1331,15 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return this; }, + /** + * @function Force the viewer to redraw its content. + * @return {OpenSeadragon.Viewer} Chainable. + */ + forceRedraw: function() { + THIS[ this.hash ].forceRedraw = true; + return this; + }, + /** * Adds an html element as an overlay to the current viewport. Useful for * highlighting words or areas of interest on an image or other zoomable @@ -1784,22 +1793,31 @@ function getOverlayObject( viewer, overlay ) { var location = overlay.location; if ( !location ) { - var rect = ( overlay.height && overlay.width ) ? new $.Rect( - overlay.x || overlay.px, - overlay.y || overlay.py, - overlay.width, - overlay.height - ) : new $.Point( - overlay.x || overlay.px, - overlay.y || overlay.py - ); - if( overlay.px !== undefined ) { - //if they specified 'px' so it's in pixel coordinates so - //we need to translate to viewport coordinates - rect = viewer.viewport.imageToViewportRectangle( rect ); + if ( overlay.width && overlay.height ) { + location = overlay.px !== undefined ? + viewer.viewport.imageToViewportRectangle( new $.Rect( + overlay.px, + overlay.py, + overlay.width, + overlay.height + ) ) : + new $.Rect( + overlay.x, + overlay.y, + overlay.width, + overlay.height + ); + } else { + location = overlay.px !== undefined ? + viewer.viewport.imageToViewportCoordinates( new $.Point( + overlay.px, + overlay.py + ) ) : + new $.Point( + overlay.x, + overlay.y + ); } - location = overlay.placement ? viewer.viewport.pointFromPixel( rect ) : - rect; } var placement = overlay.placement; diff --git a/test/overlays.js b/test/overlays.js index 47e0af89..223f99de 100644 --- a/test/overlays.js +++ b/test/overlays.js @@ -6,9 +6,9 @@ module( "Overlays", { setup: function() { var example = $( '
' ).appendTo( "#qunit-fixture" ); - var fixedOverlay = $( '
' ).appendTo(example); - fixedOverlay.width(70); - fixedOverlay.height(60); + var fixedOverlay = $( '
' ).appendTo( example ); + fixedOverlay.width( 70 ); + fixedOverlay.height( 60 ); testLog.reset(); }, @@ -186,7 +186,7 @@ equal( viewer.currentOverlays.length, 0, "No overlay should be open." ); var rect = new OpenSeadragon.Rect( 0.1, 0.1, 0.1, 0.1 ); - var overlay = $( "
" ).prop("id", "overlay").get( 0 ); + var overlay = $( "
" ).prop( "id", "overlay" ).get( 0 ); viewer.addOverlay( overlay, rect ); equal( viewer.overlays.length, 0, "No manual overlay should be added as global overlay." ); equal( viewer.currentOverlays.length, 1, "A manual overlay should be open." ); @@ -221,7 +221,7 @@ viewer = OpenSeadragon( { id: 'example-overlays', prefixUrl: '/build/openseadragon/images/', - tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + tileSources: '/test/data/testpattern.dzi', springStiffness: 100, // Faster animation = faster tests overlays: [ { px: 13, @@ -232,8 +232,9 @@ }, { px: 400, py: 500, - id: "fixed-overlay" - }] + id: "fixed-overlay", + placement: "TOP_LEFT" + } ] } ); function checkOverlayPosition( contextMessage ) { @@ -284,7 +285,7 @@ viewer = OpenSeadragon( { id: 'example-overlays', prefixUrl: '/build/openseadragon/images/', - tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + tileSources: '/test/data/testpattern.dzi', springStiffness: 100, // Faster animation = faster tests overlays: [ { x: 0.2, @@ -292,10 +293,11 @@ width: 0.5, height: 0.1, id: "overlay" - },{ + }, { x: 0.5, y: 0.6, - id: "fixed-overlay" + id: "fixed-overlay", + placement: "TOP_LEFT" } ] } ); @@ -309,9 +311,9 @@ equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); var expectedSize = viewport.deltaPixelsFromPoints( - new OpenSeadragon.Point(0.5, 0.1)); + new OpenSeadragon.Point( 0.5, 0.1 ) ); equal( $( "#overlay" ).width(), expectedSize.x, "Width mismatch " + contextMessage ); - equal( $( "#overlay" ).height( ), expectedSize.y, "Height mismatch " + contextMessage ); + equal( $( "#overlay" ).height(), expectedSize.y, "Height mismatch " + contextMessage ); expPosition = viewport.viewportToViewerElementCoordinates( @@ -341,4 +343,193 @@ } ); } ); -} )(); + asyncTest( 'Overlays placement', function() { + + var scalableOverlayLocation = new OpenSeadragon.Rect( 0.2, 0.1, 0.5, 0.1 ); + var fixedOverlayLocation = new OpenSeadragon.Point( 0.5, 0.6 ); + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + x: scalableOverlayLocation.x, + y: scalableOverlayLocation.y, + width: scalableOverlayLocation.width, + height: scalableOverlayLocation.height, + id: "overlay", + placement: "TOP_LEFT" + }, { + x: fixedOverlayLocation.x, + y: fixedOverlayLocation.y, + id: "fixed-overlay", + placement: "TOP_LEFT" + } ] + } ); + + // Scalable overlays are always TOP_LEFT + function checkScalableOverlayPosition( contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.2, 0.1 ) ).apply( Math.floor ); + var actPosition = $( "#overlay" ).position(); + equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); + } + + function checkFixedOverlayPosition( expectedOffset, contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.5, 0.6 ) ) + .apply( Math.floor ) + .plus( expectedOffset ); + var actPosition = $( "#fixed-overlay" ).position(); + equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); + } + + waitForViewer( function() { + + checkScalableOverlayPosition( "with TOP_LEFT placement." ); + checkFixedOverlayPosition( new OpenSeadragon.Point( 0, 0 ), + "with TOP_LEFT placement." ); + + viewer.updateOverlay( "overlay", scalableOverlayLocation, + OpenSeadragon.OverlayPlacement.CENTER ); + viewer.updateOverlay( "fixed-overlay", fixedOverlayLocation, + OpenSeadragon.OverlayPlacement.CENTER ); + + setTimeout( function() { + checkScalableOverlayPosition( "with CENTER placement." ); + checkFixedOverlayPosition( new OpenSeadragon.Point( -35, -30 ), + "with CENTER placement." ); + + viewer.updateOverlay( "overlay", scalableOverlayLocation, + OpenSeadragon.OverlayPlacement.BOTTOM_RIGHT ); + viewer.updateOverlay( "fixed-overlay", fixedOverlayLocation, + OpenSeadragon.OverlayPlacement.BOTTOM_RIGHT ); + setTimeout( function() { + checkScalableOverlayPosition( "with BOTTOM_RIGHT placement." ); + checkFixedOverlayPosition( new OpenSeadragon.Point( -70, -60 ), + "with BOTTOM_RIGHT placement." ); + + start(); + }, 100 ); + + }, 100 ); + + } ); + } ); + + asyncTest( 'Overlays placement and resizing check', function() { + + var fixedOverlayLocation = new OpenSeadragon.Point( 0.5, 0.6 ); + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + x: fixedOverlayLocation.x, + y: fixedOverlayLocation.y, + id: "fixed-overlay", + placement: "CENTER", + checkResize: true + } ] + } ); + + function checkFixedOverlayPosition( expectedOffset, contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.5, 0.6 ) ) + .apply( Math.floor ) + .plus( expectedOffset ); + var actPosition = $( "#fixed-overlay" ).position(); + equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); + } + + waitForViewer( function() { + checkFixedOverlayPosition( new OpenSeadragon.Point( -35, -30 ), + "with overlay of size 70,60." ); + + $( "#fixed-overlay" ).width( 50 ); + $( "#fixed-overlay" ).height( 40 ); + + // The resizing of the overlays is not detected by the viewer's loop. + viewer.forceRedraw(); + + setTimeout( function() { + checkFixedOverlayPosition( new OpenSeadragon.Point( -25, -20 ), + "with overlay of size 50,40." ); + + // Restore original size + $( "#fixed-overlay" ).width( 70 ); + $( "#fixed-overlay" ).height( 60 ); + + start(); + }, 100 ); + } ); + + } ); + + asyncTest( 'Overlays placement and no resizing check', function() { + + var fixedOverlayLocation = new OpenSeadragon.Point( 0.5, 0.6 ); + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + x: fixedOverlayLocation.x, + y: fixedOverlayLocation.y, + id: "fixed-overlay", + placement: "CENTER", + checkResize: false + } ] + } ); + + function checkFixedOverlayPosition( expectedOffset, contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.5, 0.6 ) ) + .apply( Math.floor ) + .plus( expectedOffset ); + var actPosition = $( "#fixed-overlay" ).position(); + equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); + } + + waitForViewer( function() { + checkFixedOverlayPosition( new OpenSeadragon.Point( -35, -30 ), + "with overlay of size 70,60." ); + + $( "#fixed-overlay" ).width( 50 ); + $( "#fixed-overlay" ).height( 40 ); + + // The resizing of the overlays is not detected by the viewer's loop. + viewer.forceRedraw(); + + setTimeout( function() { + checkFixedOverlayPosition( new OpenSeadragon.Point( -35, -30 ), + "with overlay of size 50,40." ); + + // Restore original size + $( "#fixed-overlay" ).width( 70 ); + $( "#fixed-overlay" ).height( 60 ); + + start(); + }, 100 ); + } ); + + } ); + +} )( ); From 639af65ea9ed47116dbfb787fb9fbe03b99dd14c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Fri, 21 Mar 2014 16:15:08 -0400 Subject: [PATCH 3/3] Remove duplicate method --- src/viewer.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index cb802e83..86c84403 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1535,15 +1535,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return this; }, - /** - * @function Force the viewer to redraw its content. - * @return {OpenSeadragon.Viewer} Chainable. - */ - forceRedraw: function() { - THIS[ this.hash ].forceRedraw = true; - return this; - }, - /** * Adds an html element as an overlay to the current viewport. Useful for * highlighting words or areas of interest on an image or other zoomable