diff --git a/src/drawer.js b/src/drawer.js
deleted file mode 100644
index 3f6b8f0d..00000000
--- a/src/drawer.js
+++ /dev/null
@@ -1,1253 +0,0 @@
-/*
- * OpenSeadragon - Drawer
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2022 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 Drawer
- * @memberof OpenSeadragon
- * @classdesc Default implementation of Drawer for an {@link OpenSeadragon.Viewer}.
- * @param {Object} options - Options for this Drawer.
- * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
- * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
- * @param {Element} options.element - Parent element.
- * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
- */
-$.Drawer = function(options) {
-
- $.DrawerBase.call(this, options);
-
- /**
- * 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
- * @member {Object} context
- * @memberof OpenSeadragon.Drawer#
- */
- this.context = this.useCanvas ? this.canvas.getContext( '2d' ) : null;
-
- /**
- * Sketch canvas used to temporarily draw tiles which cannot be drawn directly
- * to the main canvas due to opacity. Lazily initialized.
- */
- this.sketchCanvas = null;
- this.sketchContext = null;
-
- // We force our container to ltr because our drawing math doesn't work in rtl.
- // This issue only affects our canvas renderer, but we do it always for consistency.
- // Note that this means overlays you want to be rtl need to be explicitly set to rtl.
- this.container.dir = 'ltr';
-
- // Image smoothing for canvas rendering (only if canvas is used).
- // Canvas default is "true", so this will only be changed if user specified "false".
- this._imageSmoothingEnabled = true;
-};
-
-$.extend( $.Drawer.prototype, $.DrawerBase.prototype, /** @lends OpenSeadragon.Drawer.prototype */ {
-
- /**
- * Draws the TiledImages
- */
- draw: function(tiledImages) {
- var _this = this;
- this._prepareNewFrame(); // prepare to draw a new frame
- tiledImages.forEach(function(tiledImage){
- if (tiledImage.opacity !== 0 || tiledImage._preload) {
- tiledImage._midDraw = true;
- _this._updateViewportWithTiledImage(tiledImage);
- tiledImage._midDraw = false;
- }
- else {
- tiledImage._needsDraw = false;
- }
- });
-
- },
-
- /**
- * @returns {Boolean} True if rotation is supported.
- */
- canRotate: function() {
- return this.useCanvas;
- },
-
- /**
- * Destroy the drawer (unload current loaded tiles)
- */
- destroy: function() {
- //force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
- this.canvas.width = 1;
- this.canvas.height = 1;
- this.sketchCanvas = null;
- this.sketchContext = null;
- },
-
- /**
- * Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
- *
- * @function
- * @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
- * drawn smoothly on the canvas; see imageSmoothingEnabled in
- * {@link OpenSeadragon.Options} for more explanation.
- */
- setImageSmoothingEnabled: function(imageSmoothingEnabled){
- if ( this.useCanvas ) {
- this._imageSmoothingEnabled = imageSmoothingEnabled;
- this._updateImageSmoothingEnabled(this.context);
- this.viewer.forceRedraw();
- }
- },
-
- /**
- * Draw a rectangle onto the canvas
- * @param {OpenSeadragon.Rect} rect
- */
- drawDebuggingRect: function(rect) {
- if ( this.useCanvas ) {
- var context = this.context;
- context.save();
- context.lineWidth = 2 * $.pixelDensityRatio;
- context.strokeStyle = this.debugGridColor[0];
- context.fillStyle = this.debugGridColor[0];
-
- context.strokeRect(
- rect.x * $.pixelDensityRatio,
- rect.y * $.pixelDensityRatio,
- rect.width * $.pixelDensityRatio,
- rect.height * $.pixelDensityRatio
- );
-
- context.restore();
- }
- },
-
- /**
- * @private
- * @inner
- * Clears the Drawer so it's ready to draw another frame.
- *
- */
- _prepareNewFrame: function() {
- this.canvas.innerHTML = "";
- if ( this.useCanvas ) {
- var viewportSize = this._calculateCanvasSize();
- if( this.canvas.width !== viewportSize.x ||
- this.canvas.height !== viewportSize.y ) {
- this.canvas.width = viewportSize.x;
- this.canvas.height = viewportSize.y;
- this._updateImageSmoothingEnabled(this.context);
- if ( this.sketchCanvas !== null ) {
- var sketchCanvasSize = this._calculateSketchCanvasSize();
- this.sketchCanvas.width = sketchCanvasSize.x;
- this.sketchCanvas.height = sketchCanvasSize.y;
- this._updateImageSmoothingEnabled(this.sketchContext);
- }
- }
- this._clear();
- }
- },
-
- /**
- * @private
- * @inner
- * @param {Boolean} useSketch Whether to clear sketch canvas or main canvas
- * @param {OpenSeadragon.Rect} [bounds] The rectangle to clear
- */
- _clear: function(useSketch, bounds){
- if( this.useCanvas ){
- var context = this._getContext(useSketch);
- if (bounds) {
- context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
- } else {
- var canvas = context.canvas;
- context.clearRect(0, 0, canvas.width, canvas.height);
- }
- }
- },
-
- /* Methods from TiledImage */
-
- /**
- * @private
- * @inner
- * Handles drawing a single TiledImage to the canvas
- *
- */
- _updateViewportWithTiledImage: function(tiledImage) {
- var _this = this;
- tiledImage._needsDraw = false;
- tiledImage._tilesLoading = 0;
- tiledImage.loadingCoverage = {};
-
- // Reset tile's internal drawn state
- while (tiledImage.lastDrawn.length > 0) {
- var tile = tiledImage.lastDrawn.pop();
- tile.beingDrawn = false;
- }
-
-
- var drawArea = tiledImage.getDrawArea();
- if(!drawArea){
- return;
- }
-
- function updateTile(info){
- var tile = info.tile;
- if(tile && tile.loaded){
- var needsDraw = _this._blendTile(
- tiledImage,
- tile,
- tile.x,
- tile.y,
- info.level,
- info.levelOpacity,
- info.currentTime
- );
- if(needsDraw){
- tiledImage._needsDraw = true;
- }
- }
- }
-
- var infoArray = tiledImage.getTileInfoForDrawing();
- infoArray.forEach(updateTile);
-
- this._drawTiles(tiledImage);
-
- },
-
-
-
- /**
- * @private
- * @inner
- * Updates the opacity of a tile according to the time it has been on screen
- * to perform a fade-in.
- * Updates coverage once a tile is fully opaque.
- * Returns whether the fade-in has completed.
- *
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} currentTime
- * @returns {Boolean}
- */
- _blendTile: function( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
- var blendTimeMillis = 1000 * tiledImage.blendTime,
- deltaTime,
- opacity;
-
- if ( !tile.blendStart ) {
- tile.blendStart = currentTime;
- }
-
- deltaTime = currentTime - tile.blendStart;
- opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
-
- if ( tiledImage.alwaysBlend ) {
- opacity *= levelOpacity;
- }
-
- tile.opacity = opacity;
-
- tiledImage.lastDrawn.push( tile );
-
- if ( opacity === 1 ) {
- tiledImage._setCoverage( tiledImage.coverage, level, x, y, true );
- tiledImage._hasOpaqueTile = true;
- } else if ( deltaTime < blendTimeMillis ) {
- return true;
- }
-
- return false;
- },
-
- /**
- * @private
- * @inner
- * Draws a TiledImage.
- *
- */
- _drawTiles: function( tiledImage ) {
- var lastDrawn = tiledImage.lastDrawn;
- if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
- return;
- }
-
- var tile = lastDrawn[0];
- var useSketch;
-
- if (tile) {
- useSketch = tiledImage.opacity < 1 ||
- (tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over') ||
- (!tiledImage._isBottomItem() &&
- tiledImage.source.hasTransparency(tile.context2D, tile.getUrl(), tile.ajaxHeaders, tile.postData));
- }
-
- var sketchScale;
- var sketchTranslate;
-
- var zoom = this.viewport.getZoom(true);
- var imageZoom = tiledImage.viewportToImageZoom(zoom);
-
- if (lastDrawn.length > 1 &&
- imageZoom > tiledImage.smoothTileEdgesMinZoom &&
- !tiledImage.iOSDevice &&
- tiledImage.getRotation(true) % 360 === 0 && // TO DO: support tile edge smoothing with tiled image rotation.
- $.supportsCanvas && this.viewer.useCanvas) {
- // When zoomed in a lot (>100%) the tile edges are visible.
- // So we have to composite them at ~100% and scale them up together.
- // Note: Disabled on iOS devices per default as it causes a native crash
- useSketch = true;
- sketchScale = tile.getScaleForEdgeSmoothing();
- sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
- this._getCanvasSize(false),
- this._getCanvasSize(true));
- }
-
- var bounds;
- if (useSketch) {
- if (!sketchScale) {
- // Except when edge smoothing, we only clean the part of the
- // sketch canvas we are going to use for performance reasons.
- bounds = this.viewport.viewportToViewerElementRectangle(
- tiledImage.getClippedBounds(true))
- .getIntegerBoundingBox();
-
- if(this.viewer.viewport.getFlip()) {
- if (this.viewport.getRotation(true) % 360 !== 0 ||
- tiledImage.getRotation(true) % 360 !== 0) {
- bounds.x = this.viewer.container.clientWidth - (bounds.x + bounds.width);
- }
- }
-
- bounds = bounds.times($.pixelDensityRatio);
- }
- this._clear(true, bounds);
- }
-
- // When scaling, we must rotate only when blending the sketch canvas to
- // avoid interpolation
- if (!sketchScale) {
- if (this.viewport.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: this.viewport.getRotation(true),
- useSketch: useSketch
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: this.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: useSketch
- });
- }
-
- if (this.viewport.getRotation(true) % 360 === 0 &&
- tiledImage.getRotation(true) % 360 === 0) {
- if(this.viewer.viewport.getFlip()) {
- this._flip();
- }
- }
- }
-
- var usedClip = false;
- if ( tiledImage._clip ) {
- this._saveContext(useSketch);
-
- var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
- box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
- var clipRect = this._viewportToDrawerRectangle(box);
- if (sketchScale) {
- clipRect = clipRect.times(sketchScale);
- }
- if (sketchTranslate) {
- clipRect = clipRect.translate(sketchTranslate);
- }
- this._setClip(clipRect, useSketch);
-
- usedClip = true;
- }
-
- if (tiledImage._croppingPolygons) {
- var self = this;
- this._saveContext(useSketch);
- try {
- var polygons = tiledImage._croppingPolygons.map(function (polygon) {
- return polygon.map(function (coord) {
- var point = tiledImage
- .imageToViewportCoordinates(coord.x, coord.y, true)
- .rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
- var clipPoint = self._viewportCoordToDrawerCoord(point);
- if (sketchScale) {
- clipPoint = clipPoint.times(sketchScale);
- }
- if (sketchTranslate) { // mostly fixes #2312
- clipPoint = clipPoint.plus(sketchTranslate);
- }
- return clipPoint;
- });
- });
- this._clipWithPolygons(polygons, useSketch);
- } catch (e) {
- $.console.error(e);
- }
- usedClip = true;
- }
-
- if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
- var placeholderRect = this._viewportToDrawerRectangle(tiledImage.getBounds(true));
- if (sketchScale) {
- placeholderRect = placeholderRect.times(sketchScale);
- }
- if (sketchTranslate) {
- placeholderRect = placeholderRect.translate(sketchTranslate);
- }
-
- var fillStyle = null;
- if ( typeof tiledImage.placeholderFillStyle === "function" ) {
- fillStyle = tiledImage.placeholderFillStyle(tiledImage, this.context);
- }
- else {
- fillStyle = tiledImage.placeholderFillStyle;
- }
-
- this._drawRectangle(placeholderRect, fillStyle, useSketch);
- }
-
- var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRoundingForTransparency);
-
- var shouldRoundPositionAndSize = false;
-
- if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) {
- shouldRoundPositionAndSize = true;
- } else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) {
- var isAnimating = this.viewer && this.viewer.isAnimating();
- shouldRoundPositionAndSize = !isAnimating;
- }
-
- // Iterate over the tiles to draw, and draw them
- for (var i = lastDrawn.length - 1; i >= 0; i--) {
- tile = lastDrawn[ i ];
- this._drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale,
- sketchTranslate, shouldRoundPositionAndSize, tiledImage.source );
- tile.beingDrawn = true;
-
- if( this.viewer ){
- /**
- * Raised when a tile is drawn to the canvas
- *
- * @event tile-drawn
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} tile
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent( 'tile-drawn', {
- tiledImage: tiledImage,
- tile: tile
- });
- }
- }
-
- if ( usedClip ) {
- this._restoreContext( useSketch );
- }
-
- if (!sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges(useSketch);
- }
- if (this.viewport.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges(useSketch);
- }
- }
-
- if (useSketch) {
- if (sketchScale) {
- if (this.viewport.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: this.viewport.getRotation(true),
- useSketch: false
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: this.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: false
- });
- }
- }
- this.blendSketch({
- opacity: tiledImage.opacity,
- scale: sketchScale,
- translate: sketchTranslate,
- compositeOperation: tiledImage.compositeOperation,
- bounds: bounds
- });
- if (sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges(false);
- }
- if (this.viewport.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges(false);
- }
- }
- }
-
- if (!sketchScale) {
- if (this.viewport.getRotation(true) % 360 === 0 &&
- tiledImage.getRotation(true) % 360 === 0) {
- if(this.viewer.viewport.getFlip()) {
- this._flip();
- }
- }
- }
-
- this._drawDebugInfo( tiledImage, lastDrawn );
- },
-
- /**
- * @private
- * @inner
- * Draws special debug information for a TiledImage if in debug mode.
- * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
- */
- _drawDebugInfo: function( tiledImage, lastDrawn ) {
- if( tiledImage.debugMode ) {
- for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
- var tile = lastDrawn[ i ];
- try {
- this.drawDebugInfo(tile, lastDrawn.length, i, tiledImage);
- } catch(e) {
- $.console.error(e);
- }
- }
- }
- },
-
- /* Methods from Tile */
-
- /**
- * @private
- * @inner
- * This function will create multiple polygon paths on the drawing context by provided polygons,
- * then clip the context to the paths.
- * @param {OpenSeadragon.Point[][]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point
- * @param {Boolean} useSketch - Whether to use the sketch canvas or not.
- */
- _clipWithPolygons: function (polygons, useSketch) {
- if (!this.useCanvas) {
- return;
- }
- var context = this._getContext(useSketch);
- context.beginPath();
- polygons.forEach(function (polygon) {
- polygon.forEach(function (coord, i) {
- context[i === 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y);
- });
- });
- context.clip();
- },
-
- /**
- * @private
- * @inner
- * Draws the given tile.
- * @param {OpenSeadragon.Tile} tile - The tile to draw.
- * @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
- * drawingHandler({context, tile, rendered})
- * @param {Boolean} useSketch - Whether to use the sketch canvas or not.
- * where rendered
is the context with the pre-drawn image.
- * @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.
- * @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position
- * @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
- * position and size of tiles supporting alpha channel in non-transparency
- * context.
- * @param {OpenSeadragon.TileSource} source - The source specification of the tile.
- */
- _drawTile: function( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
- $.console.assert(tile, '[Drawer._drawTile] tile is required');
- $.console.assert(drawingHandler, '[Drawer._drawTile] drawingHandler is required');
-
- if (this.useCanvas) {
- var context = this._getContext(useSketch);
- scale = scale || 1;
- this._drawTileToCanvas(tile, context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source);
- } else {
- this._drawTileToHTML( tile, this.canvas );
- }
- },
-
- /**
- * @private
- * @inner
- * Renders the tile in a canvas-based context.
- * @function
- * @param {OpenSeadragon.Tile} tile - the tile to draw to the canvas
- * @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.
- * @param {Number} [scale=1] - Apply a scale to position and size
- * @param {OpenSeadragon.Point} [translate] - A translation vector
- * @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
- * position and size of tiles supporting alpha channel in non-transparency
- * context.
- * @param {OpenSeadragon.TileSource} source - The source specification of the tile.
- */
- _drawTileToCanvas: function( tile, context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
-
- var position = tile.position.times($.pixelDensityRatio),
- size = tile.size.times($.pixelDensityRatio),
- rendered;
-
- if (!tile.context2D && !tile.cacheImageRecord) {
- $.console.warn(
- '[Drawer._drawTileToCanvas] attempting to draw tile %s when it\'s not cached',
- tile.toString());
- return;
- }
-
- rendered = tile.getCanvasContext();
-
- if ( !tile.loaded || !rendered ){
- $.console.warn(
- "Attempting to draw tile %s when it's not yet loaded.",
- tile.toString()
- );
-
- return;
- }
-
- context.save();
- context.globalAlpha = this.opacity;
-
- if (typeof scale === 'number' && scale !== 1) {
- // draw tile at a different scale
- position = position.times(scale);
- size = size.times(scale);
- }
-
- if (translate instanceof $.Point) {
- // shift tile position slightly
- position = position.plus(translate);
- }
-
- //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 && tile.hasTransparency) {
- if (shouldRoundPositionAndSize) {
- // Round to the nearest whole pixel so we don't get seams from overlap.
- position.x = Math.round(position.x);
- position.y = Math.round(position.y);
- size.x = Math.round(size.x);
- size.y = Math.round(size.y);
- }
-
- //clearing only the inside of the rectangle occupied
- //by the png prevents edge flikering
- context.clearRect(
- position.x,
- position.y,
- size.x,
- size.y
- );
- }
-
- // This gives the application a chance to make image manipulation
- // changes as we are rendering the image
- drawingHandler({context: context, tile: tile, rendered: rendered});
-
- var sourceWidth, sourceHeight;
- if (tile.sourceBounds) {
- sourceWidth = Math.min(tile.sourceBounds.width, rendered.canvas.width);
- sourceHeight = Math.min(tile.sourceBounds.height, rendered.canvas.height);
- } else {
- sourceWidth = rendered.canvas.width;
- sourceHeight = rendered.canvas.height;
- }
-
- context.translate(position.x + size.x / 2, 0);
- if (tile.flipped) {
- context.scale(-1, 1);
- }
- context.drawImage(
- rendered.canvas,
- 0,
- 0,
- sourceWidth,
- sourceHeight,
- -size.x / 2,
- position.y,
- size.x,
- size.y
- );
-
- context.restore();
- },
-
- /**
- * @private
- * @inner
- * Renders the tile in an html container.
- * @function
- * @param {OpenSeadragon.Tile} tile
- * @param {Element} container
- */
- _drawTileToHTML: function( tile, container ) {
- if (!tile.cacheImageRecord) {
- $.console.warn(
- '[Drawer._drawTileToHTML] attempting to draw tile %s when it\'s not cached',
- tile.toString());
- return;
- }
-
- if ( !tile.loaded ) {
- $.console.warn(
- "Attempting to draw tile %s when it's not yet loaded.",
- tile.toString()
- );
- return;
- }
-
- //EXPERIMENTAL - trying to figure out how to scale the container
- // content during animation of the container size.
-
- if ( !tile.element ) {
- var image = tile.getImage();
- if (!image) {
- return;
- }
-
- tile.element = $.makeNeutralElement( "div" );
- tile.imgElement = image.cloneNode();
- tile.imgElement.style.msInterpolationMode = "nearest-neighbor";
- tile.imgElement.style.width = "100%";
- tile.imgElement.style.height = "100%";
-
- tile.style = tile.element.style;
- tile.style.position = "absolute";
- }
- if ( tile.element.parentNode !== container ) {
- container.appendChild( tile.element );
- }
- if ( tile.imgElement.parentNode !== tile.element ) {
- tile.element.appendChild( tile.imgElement );
- }
-
- tile.style.top = tile.position.y + "px";
- tile.style.left = tile.position.x + "px";
- tile.style.height = tile.size.y + "px";
- tile.style.width = tile.size.x + "px";
-
- if (tile.flipped) {
- tile.style.transform = "scaleX(-1)";
- }
-
- $.setElementOpacity( tile.element, tile.opacity );
- },
-
- /**
- * @private
- * @inner
- * Get the context of the main or sketch canvas
- * @param {Boolean} useSketch
- * @returns
- */
- _getContext: function( useSketch ) {
- var context = this.context;
- if ( useSketch ) {
- if (this.sketchCanvas === null) {
- this.sketchCanvas = document.createElement( "canvas" );
- var sketchCanvasSize = this._calculateSketchCanvasSize();
- this.sketchCanvas.width = sketchCanvasSize.x;
- this.sketchCanvas.height = sketchCanvasSize.y;
- this.sketchContext = this.sketchCanvas.getContext( "2d" );
-
- // If the viewport is not currently rotated, the sketchCanvas
- // will have the same size as the main canvas. However, if
- // the viewport get rotated later on, we will need to resize it.
- if (this.viewport.getRotation() === 0) {
- var self = this;
- this.viewer.addHandler('rotate', function resizeSketchCanvas() {
- if (self.viewport.getRotation() === 0) {
- return;
- }
- self.viewer.removeHandler('rotate', resizeSketchCanvas);
- var sketchCanvasSize = self._calculateSketchCanvasSize();
- self.sketchCanvas.width = sketchCanvasSize.x;
- self.sketchCanvas.height = sketchCanvasSize.y;
- });
- }
- this._updateImageSmoothingEnabled(this.sketchContext);
- }
- context = this.sketchContext;
- }
- return context;
- },
-
- /**
- * @private
- * @inner
- * Save the context of the main or sketch canvas
- * @param {Boolean} useSketch
- * @returns
- */
- _saveContext: function( useSketch ) {
- if (!this.useCanvas) {
- return;
- }
-
- this._getContext( useSketch ).save();
- },
-
- /**
- * @private
- * @inner
- * Restore the context of the main or sketch canvas
- * @param {Boolean} useSketch
- * @returns
- */
- _restoreContext: function( useSketch ) {
- if (!this.useCanvas) {
- return;
- }
-
- this._getContext( useSketch ).restore();
- },
-
- // private
- _setClip: function(rect, useSketch) {
- if (!this.useCanvas) {
- return;
- }
-
- var context = this._getContext( useSketch );
- context.beginPath();
- context.rect(rect.x, rect.y, rect.width, rect.height);
- context.clip();
- },
-
- // private
- _drawRectangle: function(rect, fillStyle, useSketch) {
- if (!this.useCanvas) {
- return;
- }
-
- var context = this._getContext( useSketch );
- context.save();
- context.fillStyle = fillStyle;
- context.fillRect(rect.x, rect.y, rect.width, rect.height);
- context.restore();
- },
-
- /**
- * Blends the sketch canvas in the main canvas.
- * @param {Object} options The options
- * @param {Float} options.opacity The opacity of the blending.
- * @param {Float} [options.scale=1] The scale at which tiles were drawn on
- * the sketch. Default is 1.
- * Use scale to draw at a lower scale and then enlarge onto the main canvas.
- * @param {OpenSeadragon.Point} [options.translate] A translation vector
- * that was used to draw the tiles
- * @param {String} [options.compositeOperation] - How the image is
- * composited onto other images; see compositeOperation in
- * {@link OpenSeadragon.Options} for possible values.
- * @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
- * canvas to blend in the main canvas. If specified, options.scale and
- * options.translate get ignored.
- */
- blendSketch: function(opacity, scale, translate, compositeOperation) {
- var options = opacity;
- if (!$.isPlainObject(options)) {
- options = {
- opacity: opacity,
- scale: scale,
- translate: translate,
- compositeOperation: compositeOperation
- };
- }
- if (!this.useCanvas || !this.sketchCanvas) {
- return;
- }
- opacity = options.opacity;
- compositeOperation = options.compositeOperation;
- var bounds = options.bounds;
-
- this.context.save();
- this.context.globalAlpha = opacity;
- if (compositeOperation) {
- this.context.globalCompositeOperation = compositeOperation;
- }
- if (bounds) {
- // Internet Explorer, Microsoft Edge, and Safari have problems
- // when you call context.drawImage with negative x or y
- // or x + width or y + height greater than the canvas width or height respectively.
- if (bounds.x < 0) {
- bounds.width += bounds.x;
- bounds.x = 0;
- }
- if (bounds.x + bounds.width > this.canvas.width) {
- bounds.width = this.canvas.width - bounds.x;
- }
- if (bounds.y < 0) {
- bounds.height += bounds.y;
- bounds.y = 0;
- }
- if (bounds.y + bounds.height > this.canvas.height) {
- bounds.height = this.canvas.height - bounds.y;
- }
-
- this.context.drawImage(
- this.sketchCanvas,
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height,
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height
- );
- } else {
- scale = options.scale || 1;
- translate = options.translate;
- var position = translate instanceof $.Point ?
- translate : new $.Point(0, 0);
-
- var widthExt = 0;
- var heightExt = 0;
- if (translate) {
- var widthDiff = this.sketchCanvas.width - this.canvas.width;
- var heightDiff = this.sketchCanvas.height - this.canvas.height;
- widthExt = Math.round(widthDiff / 2);
- heightExt = Math.round(heightDiff / 2);
- }
- this.context.drawImage(
- this.sketchCanvas,
- position.x - widthExt * scale,
- position.y - heightExt * scale,
- (this.canvas.width + 2 * widthExt) * scale,
- (this.canvas.height + 2 * heightExt) * scale,
- -widthExt,
- -heightExt,
- this.canvas.width + 2 * widthExt,
- this.canvas.height + 2 * heightExt
- );
- }
- this.context.restore();
- },
-
- // private
- drawDebugInfo: function(tile, count, i, tiledImage) {
- if ( !this.useCanvas ) {
- return;
- }
-
- var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
- var context = this.context;
- context.save();
- context.lineWidth = 2 * $.pixelDensityRatio;
- context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
- context.strokeStyle = this.debugGridColor[colorIndex];
- context.fillStyle = this.debugGridColor[colorIndex];
-
- if (this.viewport.getRotation(true) % 360 !== 0 ) {
- this._offsetForRotation({degrees: this.viewport.getRotation(true)});
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true)
- });
- }
- if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
- tiledImage.getRotation(true) % 360 === 0) {
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
-
- context.strokeRect(
- tile.position.x * $.pixelDensityRatio,
- tile.position.y * $.pixelDensityRatio,
- tile.size.x * $.pixelDensityRatio,
- tile.size.y * $.pixelDensityRatio
- );
-
- var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio;
- var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio;
-
- // Rotate the text the right way around.
- context.translate( tileCenterX, tileCenterY );
- context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) );
- context.translate( -tileCenterX, -tileCenterY );
-
- if( tile.x === 0 && tile.y === 0 ){
- context.fillText(
- "Zoom: " + this.viewport.getZoom(),
- tile.position.x * $.pixelDensityRatio,
- (tile.position.y - 30) * $.pixelDensityRatio
- );
- context.fillText(
- "Pan: " + this.viewport.getBounds().toString(),
- tile.position.x * $.pixelDensityRatio,
- (tile.position.y - 20) * $.pixelDensityRatio
- );
- }
- context.fillText(
- "Level: " + tile.level,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 20) * $.pixelDensityRatio
- );
- context.fillText(
- "Column: " + tile.x,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 30) * $.pixelDensityRatio
- );
- context.fillText(
- "Row: " + tile.y,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 40) * $.pixelDensityRatio
- );
- context.fillText(
- "Order: " + i + " of " + count,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 50) * $.pixelDensityRatio
- );
- context.fillText(
- "Size: " + tile.size.toString(),
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 60) * $.pixelDensityRatio
- );
- context.fillText(
- "Position: " + tile.position.toString(),
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 70) * $.pixelDensityRatio
- );
-
- if (this.viewport.getRotation(true) % 360 !== 0 ) {
- this._restoreRotationChanges();
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges();
- }
-
- if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
- tiledImage.getRotation(true) % 360 === 0) {
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
-
- context.restore();
- },
-
- // private
- _updateImageSmoothingEnabled: function(context){
- context.msImageSmoothingEnabled = this._imageSmoothingEnabled;
- context.imageSmoothingEnabled = this._imageSmoothingEnabled;
- },
-
- /**
- * @private
- * @inner
- * Get the canvas size
- * @param {Boolean} sketch If set to true return the size of the sketch canvas
- * @returns {OpenSeadragon.Point} The size of the canvas
- */
- _getCanvasSize: function(sketch) {
- var canvas = this._getContext(sketch).canvas;
- return new $.Point(canvas.width, canvas.height);
- },
-
- /**
- * @private
- * @inner
- * Get the canvas center
- * @param {Boolean} sketch If set to true return the center point of the sketch canvas
- * @returns {OpenSeadragon.Point} The center point of the canvas
- */
- _getCanvasCenter: function() {
- return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
- },
-
- // private
- _offsetForRotation: function(options) {
- var point = options.point ?
- options.point.times($.pixelDensityRatio) :
- this._getCanvasCenter();
-
- var context = this._getContext(options.useSketch);
- context.save();
-
- context.translate(point.x, point.y);
- if(this.viewer.viewport.flipped){
- context.rotate(Math.PI / 180 * -options.degrees);
- context.scale(-1, 1);
- } else{
- context.rotate(Math.PI / 180 * options.degrees);
- }
- context.translate(-point.x, -point.y);
- },
-
- // private
- _flip: function(options) {
- options = options || {};
- var point = options.point ?
- options.point.times($.pixelDensityRatio) :
- this._getCanvasCenter();
- var context = this._getContext(options.useSketch);
-
- context.translate(point.x, 0);
- context.scale(-1, 1);
- context.translate(-point.x, 0);
- },
-
- // private
- _restoreRotationChanges: function(useSketch) {
- var context = this._getContext(useSketch);
- context.restore();
- },
-
- // private
- _calculateCanvasSize: function() {
- var pixelDensityRatio = $.pixelDensityRatio;
- var viewportSize = this.viewport.getContainerSize();
- return {
- // canvas width and height are integers
- x: Math.round(viewportSize.x * pixelDensityRatio),
- y: Math.round(viewportSize.y * pixelDensityRatio)
- };
- },
-
- // private
- _calculateSketchCanvasSize: function() {
- var canvasSize = this._calculateCanvasSize();
- if (this.viewport.getRotation() === 0) {
- return canvasSize;
- }
- // If the viewport is rotated, we need a larger sketch canvas in order
- // to support edge smoothing.
- var sketchCanvasSize = Math.ceil(Math.sqrt(
- canvasSize.x * canvasSize.x +
- canvasSize.y * canvasSize.y));
- return {
- x: sketchCanvasSize,
- y: sketchCanvasSize
- };
- },
-
-
-});
-
-
-
-/**
- * @private
- * @inner
- * Defines the value for subpixel rounding to fallback to in case of missing or
- * invalid value.
- */
-var DEFAULT_SUBPIXEL_ROUNDING_RULE = $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER;
-
-/**
- * @private
- * @inner
- * Checks whether the input value is an invalid subpixel rounding enum value.
- *
- * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to check.
- * @returns {Boolean} Returns true if the input value is none of the expected
- * {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} value.
- */
-function isSubPixelRoundingRuleUnknown(value) {
- return value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS &&
- value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST &&
- value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER;
-}
-
-/**
- * @private
- * @inner
- * Ensures the returned value is always a valid subpixel rounding enum value,
- * defaulting to {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} if input is missing or invalid.
- *
- * @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to normalize.
- * @returns {SUBPIXEL_ROUNDING_OCCURRENCES} Returns a valid subpixel rounding enum value.
- */
-function normalizeSubPixelRoundingRule(value) {
- if (isSubPixelRoundingRuleUnknown(value)) {
- return DEFAULT_SUBPIXEL_ROUNDING_RULE;
- }
- return value;
-}
-
-/**
- * @private
- * @inner
- * Ensures the returned value is always a valid subpixel rounding enum value,
- * defaulting to 'NEVER' if input is missing or invalid.
- *
- * @param {Object} subPixelRoundingRules - A subpixel rounding enum values dictionary [{@link BROWSERS}] --> {@link SUBPIXEL_ROUNDING_OCCURRENCES}.
- * @returns {SUBPIXEL_ROUNDING_OCCURRENCES} Returns the determined subpixel rounding enum value for the
- * current browser.
- */
-function determineSubPixelRoundingRule(subPixelRoundingRules) {
- if (typeof subPixelRoundingRules === 'number') {
- return normalizeSubPixelRoundingRule(subPixelRoundingRules);
- }
-
- if (!subPixelRoundingRules || !$.Browser) {
- return DEFAULT_SUBPIXEL_ROUNDING_RULE;
- }
-
- var subPixelRoundingRule = subPixelRoundingRules[$.Browser.vendor];
-
- if (isSubPixelRoundingRuleUnknown(subPixelRoundingRule)) {
- subPixelRoundingRule = subPixelRoundingRules['*'];
- }
-
- return normalizeSubPixelRoundingRule(subPixelRoundingRule);
-}
-
-}( OpenSeadragon ));