Add IE8 support in ImageTileSource.

This commit is contained in:
Antoine Vandecreme 2015-11-02 18:35:11 -05:00
parent 8e06eb56cf
commit 14a83e1154
3 changed files with 93 additions and 72 deletions

View File

@ -43,17 +43,23 @@
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @param {Object} options Options object. * @param {Object} options Options object.
* @property {String} options.url URL of the image * @property {String} options.url URL of the image
* @property {Boolean} options.buildPyramid If set to true, a pyramid will * @property {Boolean} options.buildPyramid If set to true (default), a
* be built internally to provide a better downsampling. * pyramid will be built internally to provide a better downsampling.
* @property {String|Boolean} options.crossOriginPolicy Valid values are * @property {String|Boolean} options.crossOriginPolicy Valid values are
* 'Anonymous', 'use-credentials', and false. If false, image requests will * 'Anonymous', 'use-credentials', and false. If false, image requests will
* not use CORS preventing internal pyramid building for images from other * not use CORS preventing internal pyramid building for images from other
* domains. * domains.
* @property {String|Boolean} options.ajaxWithCredentials Whether to set the * @property {String|Boolean} options.ajaxWithCredentials Whether to set the
* withCredentials XHR flag for AJAX requests (when loading tile sources) * withCredentials XHR flag for AJAX requests (when loading tile sources)
* @property {Boolean} options.useCanvas Set to false to prevent any use of
* the canvas API.
*/ */
$.ImageTileSource = function (options) { $.ImageTileSource = function (options) {
$.extend(options, {
buildPyramid: true,
useCanvas: true
});
this.options = options; this.options = options;
$.TileSource.apply(this, [options]); $.TileSource.apply(this, [options]);
@ -99,7 +105,7 @@
image.useCredentials = this.options.ajaxWithCredentials; image.useCredentials = this.options.ajaxWithCredentials;
} }
image.addEventListener('load', function () { $.addEvent(image, 'load', function () {
_this.width = image.naturalWidth; _this.width = image.naturalWidth;
_this.height = image.naturalHeight; _this.height = image.naturalHeight;
_this.aspectRatio = _this.width / _this.height; _this.aspectRatio = _this.width / _this.height;
@ -112,7 +118,8 @@
var pyramidMinWidth = _this.buildPyramid ? 1 : _this.width; var pyramidMinWidth = _this.buildPyramid ? 1 : _this.width;
var pyramidMinHeight = _this.buildPyramid ? 1 : _this.height; var pyramidMinHeight = _this.buildPyramid ? 1 : _this.height;
_this.levels = buildLevels(image, pyramidMinWidth, pyramidMinHeight); _this.levels = _this._buildLevels(
image, pyramidMinWidth, pyramidMinHeight);
_this.maxLevel = _this.levels.length - 1; _this.maxLevel = _this.levels.length - 1;
_this.ready = true; _this.ready = true;
@ -122,21 +129,23 @@
* @event ready * @event ready
* @memberof OpenSeadragon.TileSource * @memberof OpenSeadragon.TileSource
* @type {object} * @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event. * @property {OpenSeadragon.TileSource} eventSource - A reference
* to the TileSource which raised the event.
* @property {Object} tileSource * @property {Object} tileSource
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
_this.raiseEvent('ready', {tileSource: _this}); _this.raiseEvent('ready', {tileSource: _this});
}); });
image.addEventListener('error', function () { $.addEvent(image, 'error', function () {
/*** /***
* Raised when an error occurs loading a TileSource. * Raised when an error occurs loading a TileSource.
* *
* @event open-failed * @event open-failed
* @memberof OpenSeadragon.TileSource * @memberof OpenSeadragon.TileSource
* @type {object} * @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event. * @property {OpenSeadragon.TileSource} eventSource - A reference
* to the TileSource which raised the event.
* @property {String} message * @property {String} message
* @property {String} source * @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
@ -183,15 +192,11 @@
return new $.Point(0, 0); return new $.Point(0, 0);
}, },
/** /**
* This method is not implemented by this class other than to throw an Error * Retrieves a tile url
* announcing you have to implement it. Because of the variety of tile
* server technologies, and various specifications for building image
* pyramids, this method is here to allow easy integration.
* @function * @function
* @param {Number} level * @param {Number} level Level of the tile
* @param {Number} x * @param {Number} x x coordinate of the tile
* @param {Number} y * @param {Number} y y coordinate of the tile
* @throws {Error}
*/ */
getTileUrl: function (level, x, y) { getTileUrl: function (level, x, y) {
var url = null; var url = null;
@ -199,76 +204,71 @@
url = this.levels[level].url; url = this.levels[level].url;
} }
return url; return url;
} },
}); /**
* @private Build the differents levels of the pyramid if possible
* (canvas API enabled and no canvas tainting issue)
*/
_buildLevels: function (image, minWidth, minHeight) {
var levels = [{
url: image.src,
width: image.naturalWidth,
height: image.naturalHeight
}];
function buildLevels(image, minWidth, minHeight) { if (!$.supportsCanvas || !this.useCanvas) {
var levels = [{ return levels;
url: image.src, }
width: image.naturalWidth,
height: image.naturalHeight
}];
var currentWidth = Math.floor(image.naturalWidth / 2); var currentWidth = Math.floor(image.naturalWidth / 2);
var currentHeight = Math.floor(image.naturalHeight / 2); var currentHeight = Math.floor(image.naturalHeight / 2);
if (currentWidth < minWidth || currentHeight < minHeight) { if (currentWidth < minWidth || currentHeight < minHeight) {
return levels; return levels;
} }
var bigCanvas = document.createElement("canvas"); var bigCanvas = document.createElement("canvas");
var bigContext = bigCanvas.getContext("2d"); var bigContext = bigCanvas.getContext("2d");
bigCanvas.width = currentWidth; bigCanvas.width = currentWidth;
bigCanvas.height = currentHeight; bigCanvas.height = currentHeight;
bigContext.drawImage(image, 0, 0, currentWidth, currentHeight); bigContext.drawImage(image, 0, 0, currentWidth, currentHeight);
if (isCanvasTainted(bigContext)) {
// If the canvas is tainted, we can't compute the pyramid.
return levels;
}
levels.splice(0, 0, {
url: bigCanvas.toDataURL(),
width: currentWidth,
height: currentHeight
});
var smallCanvas = document.createElement("canvas");
var smallContext = smallCanvas.getContext("2d");
while (currentWidth >= minWidth * 2 && currentHeight >= minHeight * 2) {
currentWidth = Math.floor(currentWidth / 2);
currentHeight = Math.floor(currentHeight / 2);
smallCanvas.width = currentWidth;
smallCanvas.height = currentHeight;
smallContext.drawImage(bigCanvas, 0, 0, currentWidth, currentHeight);
if ($.isCanvasTainted(bigContext.canvas)) {
// If the canvas is tainted, we can't compute the pyramid.
return levels;
}
levels.splice(0, 0, { levels.splice(0, 0, {
url: smallCanvas.toDataURL(), url: bigCanvas.toDataURL(),
width: currentWidth, width: currentWidth,
height: currentHeight height: currentHeight
}); });
var tempCanvas = bigCanvas; var smallCanvas = document.createElement("canvas");
bigCanvas = smallCanvas; var smallContext = smallCanvas.getContext("2d");
smallCanvas = tempCanvas; while (currentWidth >= minWidth * 2 && currentHeight >= minHeight * 2) {
currentWidth = Math.floor(currentWidth / 2);
currentHeight = Math.floor(currentHeight / 2);
smallCanvas.width = currentWidth;
smallCanvas.height = currentHeight;
smallContext.drawImage(bigCanvas, 0, 0, currentWidth, currentHeight);
var tempContext = bigContext; levels.splice(0, 0, {
bigContext = smallContext; url: smallCanvas.toDataURL(),
smallContext = tempContext; width: currentWidth,
} height: currentHeight
return levels; });
}
function isCanvasTainted(context) { var tempCanvas = bigCanvas;
var isTainted = false; bigCanvas = smallCanvas;
try { smallCanvas = tempCanvas;
// We test if the canvas is tainted by retrieving data from it.
// An exception will be raised if the canvas is tainted. var tempContext = bigContext;
var data = context.getImageData(0, 0, 1, 1); bigContext = smallContext;
} catch (e) { smallContext = tempContext;
isTainted = true; }
return levels;
} }
return isTainted; });
}
}(OpenSeadragon)); }(OpenSeadragon));

View File

@ -839,6 +839,23 @@ if (typeof define === 'function' && define.amd) {
canvasElement.getContext( '2d' ) ); canvasElement.getContext( '2d' ) );
}()); }());
/**
* Test whether the submitted canvas is tainted or not.
* @argument {Canvas} canvas The canvas to test.
* @returns {Boolean} True if the canvas is tainted.
*/
$.isCanvasTainted = function(canvas) {
var isTainted = false;
try {
// We test if the canvas is tainted by retrieving data from it.
// An exception will be raised if the canvas is tainted.
var data = canvas.getContext('2d').getImageData(0, 0, 1, 1);
} catch (e) {
isTainted = true;
}
return isTainted;
};
/** /**
* A ratio comparing the device screen's pixel density to the canvas's backing store pixel density. Defaults to 1 if canvas isn't supported by the browser. * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density. Defaults to 1 if canvas isn't supported by the browser.
* @member {Number} pixelDensityRatio * @member {Number} pixelDensityRatio

View File

@ -2047,6 +2047,7 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
url: tileSource, url: tileSource,
crossOriginPolicy: viewer.crossOriginPolicy, crossOriginPolicy: viewer.crossOriginPolicy,
ajaxWithCredentials: viewer.ajaxWithCredentials, ajaxWithCredentials: viewer.ajaxWithCredentials,
useCanvas: viewer.useCanvas,
success: function( event ) { success: function( event ) {
successCallback( event.tileSource ); successCallback( event.tileSource );
} }
@ -2062,6 +2063,9 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
if (tileSource.ajaxWithCredentials === undefined) { if (tileSource.ajaxWithCredentials === undefined) {
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials; tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
} }
if (tileSource.useCanvas === undefined) {
tileSource.useCanvas = viewer.useCanvas;
}
if ( $.isFunction( tileSource.getTileUrl ) ) { if ( $.isFunction( tileSource.getTileUrl ) ) {
//Custom tile source //Custom tile source