From e1d36ffb1458f734a92237e7132f8c881a94fdc7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 18 Mar 2015 10:03:44 -0700 Subject: [PATCH 1/4] First version of clip feature --- src/drawer.js | 28 ++++++++++++++++++++++++++++ src/tiledimage.js | 23 +++++++++++++++++++++++ src/viewer.js | 1 + test/demo/m2/js/main.js | 9 ++++++--- test/demo/m2/js/page.js | 7 +++++++ 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 77f078ad..52044375 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -258,6 +258,34 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ } }, + // private + saveContext: function() { + if (!this.useCanvas) { + return; + } + + this.context.save(); + }, + + // private + restoreContext: function() { + if (!this.useCanvas) { + return; + } + + this.context.restore(); + }, + + // private + setClip: function(rect) { + if (!this.useCanvas) { + return; + } + + this.context.rect(rect.x, rect.y, rect.width, rect.height); + this.context.clip(); + }, + // private drawDebugInfo: function( tile, count, i ){ if ( this.useCanvas ) { diff --git a/src/tiledimage.js b/src/tiledimage.js index 9431502a..41c06e21 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -72,6 +72,8 @@ $.TiledImage = function( options ) { $.console.assert( options.viewer, "[TiledImage] options.viewer is required" ); $.console.assert( options.imageLoader, "[TiledImage] options.imageLoader is required" ); $.console.assert( options.source, "[TiledImage] options.source is required" ); + $.console.assert(!options.clip || options.clip instanceof $.Rect, + "[TiledImage] options.clip must be an OpenSeadragon.Rect if present"); $.EventSource.call( this ); @@ -84,6 +86,9 @@ $.TiledImage = function( options ) { this._imageLoader = options.imageLoader; delete options.imageLoader; + this._clip = options.clip; + delete options.clip; + var x = options.x || 0; delete options.x; var y = options.y || 0; @@ -1116,6 +1121,20 @@ function drawTiles( tiledImage, lastDrawn ){ position, tileSource; + var usedClip = false; + if (tiledImage._clip) { + tiledImage._drawer.saveContext(); + var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); + var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); + var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); + box = new OpenSeadragon.Rect(Math.round(topLeft.x * $.pixelDensityRatio), + Math.round(topLeft.y * $.pixelDensityRatio), + Math.round(size.x * $.pixelDensityRatio), + Math.round(size.y * $.pixelDensityRatio)); + tiledImage._drawer.setClip(box); + usedClip = true; + } + for ( i = lastDrawn.length - 1; i >= 0; i-- ) { tile = lastDrawn[ i ]; tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler ); @@ -1147,6 +1166,10 @@ function drawTiles( tiledImage, lastDrawn ){ }); } } + + if (usedClip) { + tiledImage._drawer.restoreContext(); + } } }( OpenSeadragon )); diff --git a/src/viewer.js b/src/viewer.js index 155174d4..a585b936 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1280,6 +1280,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, y: queueItem.options.y, width: queueItem.options.width, height: queueItem.options.height, + clip: queueItem.options.clip, springStiffness: _this.springStiffness, animationTime: _this.animationTime, minZoomImageRatio: _this.minZoomImageRatio, diff --git a/test/demo/m2/js/main.js b/test/demo/m2/js/main.js index 75337fdb..9f15857a 100644 --- a/test/demo/m2/js/main.js +++ b/test/demo/m2/js/main.js @@ -22,7 +22,10 @@ this.pages = this.createPages(); var tileSources = $.map(this.pages, function(v, i) { - return v.starter.tileSource; + return { + tileSource: v.starter.tileSource, + clip: v.starter.clip + }; }); this.viewer = OpenSeadragon({ @@ -543,11 +546,11 @@ } } + this.panBounds = null; + box = new OpenSeadragon.Rect(x, y, width, height); this.viewer.viewport.fitBounds(box, config.immediately); - this.panBounds = null; - var setPanBounds = function() { if (self.mode === 'page' || self.mode === 'book') { self.panBounds = box; diff --git a/test/demo/m2/js/page.js b/test/demo/m2/js/page.js index 736cb2f1..1294d3f5 100644 --- a/test/demo/m2/js/page.js +++ b/test/demo/m2/js/page.js @@ -19,6 +19,11 @@ tileSource: config.tileSource }; + if (config.clip) { + this.starter.clip = new OpenSeadragon.Rect(config.clip.x, config.clip.y, + config.clip.width, config.clip.height); + } + this.main = {}; this.alternateIndex = -1; @@ -56,6 +61,7 @@ App.viewer.world.removeItem(this.main.tiledImage); App.viewer.addTiledImage({ tileSource: itemInfo.tileSource, + clip: itemInfo.clip, index: this.pageIndex, success: function(event) { self.setDetail(self.main, itemInfo, event.item); @@ -81,6 +87,7 @@ App.viewer.addTiledImage({ tileSource: v.tileSource, + clip: v.clip, success: function(event) { v.tiledImage = event.item; self.placeDetail(v, true); From c27f68640f9c10f3f998950da1b84ac2857059db Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 19 Mar 2015 16:38:52 -0700 Subject: [PATCH 2/4] Docs for clip feature, as well as get and set --- changelog.txt | 1 + src/drawer.js | 1 + src/tiledimage.js | 38 +++++++++++++++++++++++++++++++++++++- src/viewer.js | 3 +++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 3ddcd4bd..e416df23 100644 --- a/changelog.txt +++ b/changelog.txt @@ -36,6 +36,7 @@ OPENSEADRAGON CHANGELOG * New Viewport method for managing homeBounds as well as constraints: setHomeBounds * Viewport.open supports positioning config properties * For multi-image open, drawing isn't started until all tileSources have been opened + * You can specify a clip area for each image (only works on browsers that support the HTML5 canvas) (#594) * Margins option to push the home region in from the edges of the Viewer (#505) * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Overlays appear in the DOM immediately on open or addOverlay (#507) diff --git a/src/drawer.js b/src/drawer.js index 52044375..1f18f102 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -282,6 +282,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } + this.context.beginPath(); this.context.rect(rect.x, rect.y, rect.width, rect.height); this.context.clip(); }, diff --git a/src/tiledimage.js b/src/tiledimage.js index 41c06e21..48e2773b 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -52,6 +52,9 @@ * @param {Number} [options.y=0] - Top position, in viewport coordinates. * @param {Number} [options.width=1] - Width, in viewport coordinates. * @param {Number} [options.height] - Height, in viewport coordinates. + * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to + * (portions of the image outside of this area will not be visible). Only works on + * browsers that support the HTML5 canvas. * @param {Number} [options.springStiffness] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.animationTime] - See {@link OpenSeadragon.Options}. * @param {Number} [options.minZoomImageRatio] - See {@link OpenSeadragon.Options}. @@ -86,7 +89,10 @@ $.TiledImage = function( options ) { this._imageLoader = options.imageLoader; delete options.imageLoader; - this._clip = options.clip; + if (options.clip instanceof $.Rect) { + this._clip = options.clip.clone(); + } + delete options.clip; var x = options.x || 0; @@ -448,6 +454,36 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._setScale(height / this.normHeight, immediately); }, + /** + * @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle, + * in image pixels, or null if none. + */ + getClip: function() { + if (this._clip) { + return this._clip.clone(); + } + + return null; + }, + + /** + * @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to + * (portions of the image outside of this area will not be visible). Only works on + * browsers that support the HTML5 canvas. + */ + setClip: function(newClip) { + $.console.assert(!newClip || newClip instanceof $.Rect, + "[TiledImage.setClip] newClip must be an OpenSeadragon.Rect or null"); + + if (newClip instanceof $.Rect) { + this._clip = newClip.clone(); + } else { + this._clip = null; + } + + this._needsDraw = true; + }, + // private _setScale: function(scale, immediately) { var sameTarget = (this._scaleSpring.target.value === scale); diff --git a/src/viewer.js b/src/viewer.js index a585b936..7b269350 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1195,6 +1195,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @param {Number} [options.y=0] The Y position for the image in viewport coordinates. * @param {Number} [options.width=1] The width for the image in viewport coordinates. * @param {Number} [options.height] The height for the image in viewport coordinates. + * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to + * (portions of the image outside of this area will not be visible). Only works on + * browsers that support the HTML5 canvas. * @param {Function} [options.success] A function that gets called when the image is * successfully added. It's passed the event object which contains a single property: * "item", the resulting TiledImage. From e433863f096fb0fdca2fd6915b7bad4f48869edb Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 20 Mar 2015 10:09:33 -0700 Subject: [PATCH 3/4] Test for clip feature --- src/tiledimage.js | 8 ++++---- test/modules/tiledimage.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 48e2773b..405989b7 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1163,10 +1163,10 @@ function drawTiles( tiledImage, lastDrawn ){ var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); - box = new OpenSeadragon.Rect(Math.round(topLeft.x * $.pixelDensityRatio), - Math.round(topLeft.y * $.pixelDensityRatio), - Math.round(size.x * $.pixelDensityRatio), - Math.round(size.y * $.pixelDensityRatio)); + box = new OpenSeadragon.Rect(topLeft.x * $.pixelDensityRatio, + topLeft.y * $.pixelDensityRatio, + size.x * $.pixelDensityRatio, + size.y * $.pixelDensityRatio); tiledImage._drawer.setClip(box); usedClip = true; } diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 44dc9327..704f4773 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -191,4 +191,33 @@ viewer.open('/test/data/testpattern.dzi'); }); + // ---------- + asyncTest('clip', function() { + var clip = new OpenSeadragon.Rect(100, 100, 800, 800); + + viewer.addHandler('open', function() { + var image = viewer.world.getItemAt(0); + propEqual(image.getClip(), clip, 'image has correct clip'); + + image.setClip(null); + equal(image.getClip(), null, 'clip is cleared'); + + image.setClip(clip); + propEqual(image.getClip(), clip, 'clip is set correctly'); + + Util.spyOnce(viewer.drawer, 'setClip', function(rect) { + ok(true, 'drawer.setClip is called'); + var pixelRatio = viewer.viewport.getContainerSize().x / image.getContentSize().x; + var canvasClip = clip.times(pixelRatio * OpenSeadragon.pixelDensityRatio); + propEqual(rect, canvasClip, 'clipping to correct rect'); + start(); + }); + }); + + viewer.open({ + tileSource: '/test/data/testpattern.dzi', + clip: clip + }); + }); + })(); From 823e9c3d223c1a53dda222eeb2c52faec91c415e Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 20 Mar 2015 10:17:27 -0700 Subject: [PATCH 4/4] Non-Harvard version of M2 clip demo --- test/demo/m2/index.html | 2 +- test/demo/m2/js/main.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/demo/m2/index.html b/test/demo/m2/index.html index ca46fedd..fc838579 100644 --- a/test/demo/m2/index.html +++ b/test/demo/m2/index.html @@ -7,7 +7,7 @@ - +