mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-29 08:36:10 +03:00
Drawers now use new cache API to draw onto a canvas. The type conversion now requires also the tile argument so that conversion can rely on the tile metadata.
This commit is contained in:
parent
3fa13570ef
commit
fcf20be8ea
@ -50,6 +50,8 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
constructor(options){
|
constructor(options){
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.declareSupportedDataFormats("context2d");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML element (canvas) that this drawer uses for drawing
|
* The HTML element (canvas) that this drawer uses for drawing
|
||||||
* @member {Element} canvas
|
* @member {Element} canvas
|
||||||
@ -255,26 +257,26 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
_drawTiles( tiledImage ) {
|
_drawTiles( tiledImage ) {
|
||||||
var lastDrawn = tiledImage.getTilesToDraw().map(info => info.tile);
|
const lastDrawn = tiledImage.getTilesToDraw().map(info => info.tile);
|
||||||
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
|
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tile = lastDrawn[0];
|
let tile = lastDrawn[0];
|
||||||
var useSketch;
|
let useSketch;
|
||||||
|
|
||||||
if (tile) {
|
if (tile) {
|
||||||
useSketch = tiledImage.opacity < 1 ||
|
useSketch = tiledImage.opacity < 1 ||
|
||||||
(tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over') ||
|
(tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over') ||
|
||||||
(!tiledImage._isBottomItem() &&
|
(!tiledImage._isBottomItem() &&
|
||||||
tiledImage.source.hasTransparency(tile.context2D, tile.getUrl(), tile.ajaxHeaders, tile.postData));
|
tiledImage.source.hasTransparency(null, tile.getUrl(), tile.ajaxHeaders, tile.postData));
|
||||||
}
|
}
|
||||||
|
|
||||||
var sketchScale;
|
let sketchScale;
|
||||||
var sketchTranslate;
|
let sketchTranslate;
|
||||||
|
|
||||||
var zoom = this.viewport.getZoom(true);
|
const zoom = this.viewport.getZoom(true);
|
||||||
var imageZoom = tiledImage.viewportToImageZoom(zoom);
|
const imageZoom = tiledImage.viewportToImageZoom(zoom);
|
||||||
|
|
||||||
if (lastDrawn.length > 1 &&
|
if (lastDrawn.length > 1 &&
|
||||||
imageZoom > tiledImage.smoothTileEdgesMinZoom &&
|
imageZoom > tiledImage.smoothTileEdgesMinZoom &&
|
||||||
@ -284,13 +286,19 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
// So we have to composite them at ~100% and scale them up together.
|
// 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
|
// Note: Disabled on iOS devices per default as it causes a native crash
|
||||||
useSketch = true;
|
useSketch = true;
|
||||||
sketchScale = tile.getScaleForEdgeSmoothing();
|
|
||||||
|
const context = tile.length && this.getCompatibleData(tile);
|
||||||
|
if (context) {
|
||||||
|
sketchScale = context.canvas.width / (tile.size.x * $.pixelDensityRatio);
|
||||||
|
} else {
|
||||||
|
sketchScale = 1;
|
||||||
|
}
|
||||||
sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
|
sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
|
||||||
this._getCanvasSize(false),
|
this._getCanvasSize(false),
|
||||||
this._getCanvasSize(true));
|
this._getCanvasSize(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
var bounds;
|
let bounds;
|
||||||
if (useSketch) {
|
if (useSketch) {
|
||||||
if (!sketchScale) {
|
if (!sketchScale) {
|
||||||
// Except when edge smoothing, we only clean the part of the
|
// Except when edge smoothing, we only clean the part of the
|
||||||
@ -337,13 +345,13 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usedClip = false;
|
let usedClip = false;
|
||||||
if ( tiledImage._clip ) {
|
if ( tiledImage._clip ) {
|
||||||
this._saveContext(useSketch);
|
this._saveContext(useSketch);
|
||||||
|
|
||||||
var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
|
let box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
|
||||||
box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
|
box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
|
||||||
var clipRect = this.viewportToDrawerRectangle(box);
|
let clipRect = this.viewportToDrawerRectangle(box);
|
||||||
if (sketchScale) {
|
if (sketchScale) {
|
||||||
clipRect = clipRect.times(sketchScale);
|
clipRect = clipRect.times(sketchScale);
|
||||||
}
|
}
|
||||||
@ -356,17 +364,17 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tiledImage._croppingPolygons) {
|
if (tiledImage._croppingPolygons) {
|
||||||
var self = this;
|
const self = this;
|
||||||
if(!usedClip){
|
if(!usedClip){
|
||||||
this._saveContext(useSketch);
|
this._saveContext(useSketch);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var polygons = tiledImage._croppingPolygons.map(function (polygon) {
|
const polygons = tiledImage._croppingPolygons.map(function (polygon) {
|
||||||
return polygon.map(function (coord) {
|
return polygon.map(function (coord) {
|
||||||
var point = tiledImage
|
const point = tiledImage
|
||||||
.imageToViewportCoordinates(coord.x, coord.y, true)
|
.imageToViewportCoordinates(coord.x, coord.y, true)
|
||||||
.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
|
.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
|
||||||
var clipPoint = self.viewportCoordToDrawerCoord(point);
|
let clipPoint = self.viewportCoordToDrawerCoord(point);
|
||||||
if (sketchScale) {
|
if (sketchScale) {
|
||||||
clipPoint = clipPoint.times(sketchScale);
|
clipPoint = clipPoint.times(sketchScale);
|
||||||
}
|
}
|
||||||
@ -384,7 +392,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
|
if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
|
||||||
var placeholderRect = this.viewportToDrawerRectangle(tiledImage.getBounds(true));
|
let placeholderRect = this.viewportToDrawerRectangle(tiledImage.getBounds(true));
|
||||||
if (sketchScale) {
|
if (sketchScale) {
|
||||||
placeholderRect = placeholderRect.times(sketchScale);
|
placeholderRect = placeholderRect.times(sketchScale);
|
||||||
}
|
}
|
||||||
@ -392,7 +400,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
placeholderRect = placeholderRect.translate(sketchTranslate);
|
placeholderRect = placeholderRect.translate(sketchTranslate);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fillStyle = null;
|
let fillStyle;
|
||||||
if ( typeof tiledImage.placeholderFillStyle === "function" ) {
|
if ( typeof tiledImage.placeholderFillStyle === "function" ) {
|
||||||
fillStyle = tiledImage.placeholderFillStyle(tiledImage, this.context);
|
fillStyle = tiledImage.placeholderFillStyle(tiledImage, this.context);
|
||||||
}
|
}
|
||||||
@ -403,19 +411,18 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
this._drawRectangle(placeholderRect, fillStyle, useSketch);
|
this._drawRectangle(placeholderRect, fillStyle, useSketch);
|
||||||
}
|
}
|
||||||
|
|
||||||
var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRoundingForTransparency);
|
const subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRoundingForTransparency);
|
||||||
|
|
||||||
var shouldRoundPositionAndSize = false;
|
let shouldRoundPositionAndSize = false;
|
||||||
|
|
||||||
if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) {
|
if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) {
|
||||||
shouldRoundPositionAndSize = true;
|
shouldRoundPositionAndSize = true;
|
||||||
} else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) {
|
} else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) {
|
||||||
var isAnimating = this.viewer && this.viewer.isAnimating();
|
shouldRoundPositionAndSize = !(this.viewer && this.viewer.isAnimating());
|
||||||
shouldRoundPositionAndSize = !isAnimating;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over the tiles to draw, and draw them
|
// Iterate over the tiles to draw, and draw them
|
||||||
for (var i = 0; i < lastDrawn.length; i++) {
|
for (let i = 0; i < lastDrawn.length; i++) {
|
||||||
tile = lastDrawn[ i ];
|
tile = lastDrawn[ i ];
|
||||||
this._drawTile( tile, tiledImage, useSketch, sketchScale,
|
this._drawTile( tile, tiledImage, useSketch, sketchScale,
|
||||||
sketchTranslate, shouldRoundPositionAndSize, tiledImage.source );
|
sketchTranslate, shouldRoundPositionAndSize, tiledImage.source );
|
||||||
@ -499,9 +506,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
this._drawDebugInfo( tiledImage, lastDrawn );
|
this._drawDebugInfo( tiledImage, lastDrawn );
|
||||||
|
|
||||||
// Fire tiled-image-drawn event.
|
// Fire tiled-image-drawn event.
|
||||||
|
|
||||||
this._raiseTiledImageDrawnEvent(tiledImage, lastDrawn);
|
this._raiseTiledImageDrawnEvent(tiledImage, lastDrawn);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -559,52 +564,25 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
$.console.assert(tile, '[Drawer._drawTile] tile is required');
|
$.console.assert(tile, '[Drawer._drawTile] tile is required');
|
||||||
$.console.assert(tiledImage, '[Drawer._drawTile] drawingHandler is required');
|
$.console.assert(tiledImage, '[Drawer._drawTile] drawingHandler is required');
|
||||||
|
|
||||||
var context = this._getContext(useSketch);
|
if ( !tile.loaded ){
|
||||||
scale = scale || 1;
|
|
||||||
this._drawTileToCanvas(tile, context, tiledImage, scale, translate, shouldRoundPositionAndSize, source);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the tile in a canvas-based context.
|
|
||||||
* @private
|
|
||||||
* @function
|
|
||||||
* @param {OpenSeadragon.Tile} tile - the tile to draw to the canvas
|
|
||||||
* @param {Canvas} context
|
|
||||||
* @param {OpenSeadragon.TiledImage} tiledImage - Method for firing the drawing event.
|
|
||||||
* drawingHandler({context, tile, rendered})
|
|
||||||
* where <code>rendered</code> 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( tile, context, tiledImage, 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(
|
$.console.warn(
|
||||||
"Attempting to draw tile %s when it's not yet loaded.",
|
"Attempting to draw tile %s when it's not yet loaded.",
|
||||||
tile.toString()
|
tile.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rendered = this.getCompatibleData(tile);
|
||||||
|
if (!rendered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = this._getContext(useSketch);
|
||||||
|
scale = scale || 1;
|
||||||
|
|
||||||
|
let position = tile.position.times($.pixelDensityRatio),
|
||||||
|
size = tile.size.times($.pixelDensityRatio);
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
// context.globalAlpha = this.options.opacity; // this was deprecated previously and should not be applied as it is set per TiledImage
|
// context.globalAlpha = this.options.opacity; // this was deprecated previously and should not be applied as it is set per TiledImage
|
||||||
|
|
||||||
@ -644,7 +622,7 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
|
|
||||||
this._raiseTileDrawingEvent(tiledImage, context, tile, rendered);
|
this._raiseTileDrawingEvent(tiledImage, context, tile, rendered);
|
||||||
|
|
||||||
var sourceWidth, sourceHeight;
|
let sourceWidth, sourceHeight;
|
||||||
if (tile.sourceBounds) {
|
if (tile.sourceBounds) {
|
||||||
sourceWidth = Math.min(tile.sourceBounds.width, rendered.canvas.width);
|
sourceWidth = Math.min(tile.sourceBounds.width, rendered.canvas.width);
|
||||||
sourceHeight = Math.min(tile.sourceBounds.height, rendered.canvas.height);
|
sourceHeight = Math.min(tile.sourceBounds.height, rendered.canvas.height);
|
||||||
@ -672,6 +650,8 @@ class CanvasDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the context of the main or sketch canvas
|
* Get the context of the main or sketch canvas
|
||||||
* @private
|
* @private
|
||||||
|
@ -148,13 +148,24 @@ class WeightedGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node on the conversion path in OpenSeadragon.converter.getConversionPath().
|
* Edge.transform function on the conversion path in OpenSeadragon.converter.getConversionPath().
|
||||||
* It can be also conversion to undefined if used as destructor implementation.
|
* It can be also conversion to undefined if used as destructor implementation.
|
||||||
*
|
*
|
||||||
* @callback TypeConvertor
|
* @callback TypeConvertor
|
||||||
* @memberof OpenSeadragon
|
* @memberof OpenSeadragon
|
||||||
* @param {?} data data in the input format
|
* @param {OpenSeadragon.Tile} tile reference tile that owns the data
|
||||||
* @return {?} data in the output format
|
* @param {any} data data in the input format
|
||||||
|
* @returns {any} data in the output format
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor called every time a data type is to be destroyed or converted to another type.
|
||||||
|
*
|
||||||
|
* @callback TypeDestructor
|
||||||
|
* @memberof OpenSeadragon
|
||||||
|
* @param {any} data data in the format the destructor is registered for
|
||||||
|
* @returns {any} can return any value that is carried over to the caller if desirable.
|
||||||
|
* Note: not used by the OSD cache system.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,13 +195,13 @@ $.DataTypeConvertor = class {
|
|||||||
this.copyings = {};
|
this.copyings = {};
|
||||||
|
|
||||||
// Teaching OpenSeadragon built-in conversions:
|
// Teaching OpenSeadragon built-in conversions:
|
||||||
const imageCreator = (url) => new $.Promise((resolve, reject) => {
|
const imageCreator = (tile, url) => new $.Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onerror = img.onabort = reject;
|
img.onerror = img.onabort = reject;
|
||||||
img.onload = () => resolve(img);
|
img.onload = () => resolve(img);
|
||||||
img.src = url;
|
img.src = url;
|
||||||
});
|
});
|
||||||
const canvasContextCreator = (imageData) => {
|
const canvasContextCreator = (tile, imageData) => {
|
||||||
const canvas = document.createElement( 'canvas' );
|
const canvas = document.createElement( 'canvas' );
|
||||||
canvas.width = imageData.width;
|
canvas.width = imageData.width;
|
||||||
canvas.height = imageData.height;
|
canvas.height = imageData.height;
|
||||||
@ -199,15 +210,25 @@ $.DataTypeConvertor = class {
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.learn("context2d", "url", ctx => ctx.canvas.toDataURL(), 1, 2);
|
this.learn("context2d", "url", (tile, ctx) => ctx.canvas.toDataURL(), 1, 2);
|
||||||
this.learn("image", "url", image => image.url);
|
this.learn("image", "url", (tile, image) => image.url);
|
||||||
this.learn("image", "context2d", canvasContextCreator, 1, 1);
|
this.learn("image", "context2d", canvasContextCreator, 1, 1);
|
||||||
this.learn("url", "image", imageCreator, 1, 1);
|
this.learn("url", "image", imageCreator, 1, 1);
|
||||||
|
|
||||||
//Copies
|
//Copies
|
||||||
this.learn("image", "image", image => imageCreator(image.src), 1, 1);
|
this.learn("image", "image", (tile, image) => imageCreator(tile, image.src), 1, 1);
|
||||||
this.learn("url", "url", url => url, 0, 1); //strings are immutable, no need to copy
|
this.learn("url", "url", (tile, url) => url, 0, 1); //strings are immutable, no need to copy
|
||||||
this.learn("context2d", "context2d", ctx => canvasContextCreator(ctx.canvas));
|
this.learn("context2d", "context2d", (tile, ctx) => canvasContextCreator(tile, ctx.canvas));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free up canvas memory
|
||||||
|
* (iOS 12 or higher on 2GB RAM device has only 224MB canvas memory,
|
||||||
|
* and Safari keeps canvas until its height and width will be set to 0).
|
||||||
|
*/
|
||||||
|
this.learnDestroy("context2d", ctx => {
|
||||||
|
ctx.canvas.width = 0;
|
||||||
|
ctx.canvas.height = 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,9 +284,9 @@ $.DataTypeConvertor = class {
|
|||||||
* Teach the system to convert data type 'from' -> 'to'
|
* Teach the system to convert data type 'from' -> 'to'
|
||||||
* @param {string} from unique ID of the data item 'from'
|
* @param {string} from unique ID of the data item 'from'
|
||||||
* @param {string} to unique ID of the data item 'to'
|
* @param {string} to unique ID of the data item 'to'
|
||||||
* @param {OpenSeadragon.TypeConvertor} callback convertor that takes type 'from', and converts to type 'to'.
|
* @param {OpenSeadragon.TypeConvertor} callback convertor that takes two arguments: a tile reference, and
|
||||||
* Callback can return function. This function returns the data in type 'to',
|
* a data object of a type 'from'; and converts this data object to type 'to'. It can return also the value
|
||||||
* it can return also the value wrapped in a Promise (returned in resolve) or it can be async function.
|
* wrapped in a Promise (returned in resolve) or it can be async function.
|
||||||
* @param {Number} [costPower=0] positive cost class of the conversion, smaller or equal than 7.
|
* @param {Number} [costPower=0] positive cost class of the conversion, smaller or equal than 7.
|
||||||
* Should reflect the actual cost of the conversion:
|
* Should reflect the actual cost of the conversion:
|
||||||
* - if nothing must be done and only reference is retrieved (or a constant operation done),
|
* - if nothing must be done and only reference is retrieved (or a constant operation done),
|
||||||
@ -298,7 +319,7 @@ $.DataTypeConvertor = class {
|
|||||||
* for example, textures loaded to GPU have to be also manually removed when not needed anymore.
|
* for example, textures loaded to GPU have to be also manually removed when not needed anymore.
|
||||||
* Needs to be defined only when the created object has extra deletion process.
|
* Needs to be defined only when the created object has extra deletion process.
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
* @param {OpenSeadragon.TypeConvertor} callback destructor, receives the object created,
|
* @param {OpenSeadragon.TypeDestructor} callback destructor, receives the object created,
|
||||||
* it is basically a type conversion to 'undefined' - thus the type.
|
* it is basically a type conversion to 'undefined' - thus the type.
|
||||||
*/
|
*/
|
||||||
learnDestroy(type, callback) {
|
learnDestroy(type, callback) {
|
||||||
@ -312,12 +333,13 @@ $.DataTypeConvertor = class {
|
|||||||
* Note: conversion DOES NOT COPY data if [to] contains type 'from' (e.g., the cheapest conversion is no conversion).
|
* Note: conversion DOES NOT COPY data if [to] contains type 'from' (e.g., the cheapest conversion is no conversion).
|
||||||
* It automatically calls destructor on immediate types, but NOT on the x and the result. You should call these
|
* It automatically calls destructor on immediate types, but NOT on the x and the result. You should call these
|
||||||
* manually if these should be destroyed.
|
* manually if these should be destroyed.
|
||||||
* @param {*} x data item to convert
|
* @param {OpenSeadragon.Tile} tile
|
||||||
|
* @param {any} data data item to convert
|
||||||
* @param {string} from data item type
|
* @param {string} from data item type
|
||||||
* @param {string} to desired type(s)
|
* @param {string} to desired type(s)
|
||||||
* @return {OpenSeadragon.Promise<?>} promise resolution with type 'to' or undefined if the conversion failed
|
* @return {OpenSeadragon.Promise<?>} promise resolution with type 'to' or undefined if the conversion failed
|
||||||
*/
|
*/
|
||||||
convert(x, from, ...to) {
|
convert(tile, data, from, ...to) {
|
||||||
const conversionPath = this.getConversionPath(from, to);
|
const conversionPath = this.getConversionPath(from, to);
|
||||||
if (!conversionPath) {
|
if (!conversionPath) {
|
||||||
$.console.error(`[OpenSeadragon.convertor.convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
$.console.error(`[OpenSeadragon.convertor.convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
||||||
@ -331,7 +353,7 @@ $.DataTypeConvertor = class {
|
|||||||
return $.Promise.resolve(x);
|
return $.Promise.resolve(x);
|
||||||
}
|
}
|
||||||
let edge = conversionPath[i];
|
let edge = conversionPath[i];
|
||||||
let y = edge.transform(x);
|
let y = edge.transform(tile, x);
|
||||||
if (!y) {
|
if (!y) {
|
||||||
$.console.warn(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting to %s)`, edge.target);
|
$.console.warn(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting to %s)`, edge.target);
|
||||||
return $.Promise.resolve();
|
return $.Promise.resolve();
|
||||||
@ -344,19 +366,20 @@ $.DataTypeConvertor = class {
|
|||||||
return result.then(res => step(res, i + 1));
|
return result.then(res => step(res, i + 1));
|
||||||
};
|
};
|
||||||
//destroy only mid-results, but not the original value
|
//destroy only mid-results, but not the original value
|
||||||
return step(x, 0, false);
|
return step(data, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the data item given.
|
* Destroy the data item given.
|
||||||
|
* @param {OpenSeadragon.Tile} tile
|
||||||
|
* @param {any} data data item to convert
|
||||||
* @param {string} type data type
|
* @param {string} type data type
|
||||||
* @param {?} data
|
|
||||||
* @return {OpenSeadragon.Promise<?>|undefined} promise resolution with data passed from constructor
|
* @return {OpenSeadragon.Promise<?>|undefined} promise resolution with data passed from constructor
|
||||||
*/
|
*/
|
||||||
copy(data, type) {
|
copy(tile, data, type) {
|
||||||
const copyTransform = this.copyings[type];
|
const copyTransform = this.copyings[type];
|
||||||
if (copyTransform) {
|
if (copyTransform) {
|
||||||
const y = copyTransform(data);
|
const y = copyTransform(tile, data);
|
||||||
return $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
return $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||||
}
|
}
|
||||||
$.console.warn(`[OpenSeadragon.convertor.copy] is not supported with type %s`, type);
|
$.console.warn(`[OpenSeadragon.convertor.copy] is not supported with type %s`, type);
|
||||||
@ -399,7 +422,7 @@ $.DataTypeConvertor = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(to)) {
|
if (Array.isArray(to)) {
|
||||||
$.console.assert(typeof to === "string" || to.length > 0, "[getConversionPath] conversion 'to' type must be defined.");
|
$.console.assert(to.length > 0, "[getConversionPath] conversion 'to' type must be defined.");
|
||||||
let bestCost = Infinity;
|
let bestCost = Infinity;
|
||||||
|
|
||||||
//FIXME: pre-compute all paths in 'to' array? could be efficient for multiple
|
//FIXME: pre-compute all paths in 'to' array? could be efficient for multiple
|
||||||
|
@ -77,7 +77,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
this.container.style.textAlign = "left";
|
this.container.style.textAlign = "left";
|
||||||
this.container.appendChild( this.canvas );
|
this.container.appendChild( this.canvas );
|
||||||
|
|
||||||
this._checkForAPIOverrides();
|
this._checkInterfaceImplementation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// protect the canvas member with a getter
|
// protect the canvas member with a getter
|
||||||
@ -98,6 +98,54 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define which data types are compatible for this drawer to work with.
|
||||||
|
* See default type list in OpenSeadragon.DataTypeConvertor
|
||||||
|
* @param formats
|
||||||
|
*/
|
||||||
|
declareSupportedDataFormats(...formats) {
|
||||||
|
this._formats = formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve data types
|
||||||
|
* @return {[string]}
|
||||||
|
*/
|
||||||
|
getSupportedDataFormats() {
|
||||||
|
if (!this._formats || this._formats.length < 1) {
|
||||||
|
$.console.error("A drawer must define its supported rendering data types using declareSupportedDataFormats!");
|
||||||
|
}
|
||||||
|
return this._formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a particular cache record is compatible.
|
||||||
|
* This function _MUST_ be called: if it returns a falsey
|
||||||
|
* value, the rendering _MUST NOT_ proceed. It should
|
||||||
|
* await next animation frames and check again for availability.
|
||||||
|
* @param {OpenSeadragon.Tile} tile
|
||||||
|
*/
|
||||||
|
getCompatibleData(tile) {
|
||||||
|
const cache = tile.getCache(tile.cacheKey);
|
||||||
|
if (!cache) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formats = this.getSupportedDataFormats();
|
||||||
|
if (!formats.includes(cache.type)) {
|
||||||
|
cache.transformTo(formats.length > 1 ? formats : formats[0]);
|
||||||
|
return false; // type is NOT compatible
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache in the process of loading, no-op
|
||||||
|
if (!cache.loaded) {
|
||||||
|
return false; // cache is NOT ready
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensured compatible
|
||||||
|
return cache.data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
* @returns {Boolean} Whether the drawer implementation is supported by the browser. Must be overridden by extending classes.
|
* @returns {Boolean} Whether the drawer implementation is supported by the browser. Must be overridden by extending classes.
|
||||||
@ -146,8 +194,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
*/
|
*/
|
||||||
minimumOverlapRequired() {
|
minimumOverlapRequired() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
@ -182,7 +229,7 @@ OpenSeadragon.DrawerBase = class DrawerBase{
|
|||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
_checkForAPIOverrides(){
|
_checkInterfaceImplementation(){
|
||||||
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
if(this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement){
|
||||||
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
constructor(options){
|
constructor(options){
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.declareSupportedDataFormats("image");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML element (div) that this drawer uses for drawing
|
* The HTML element (div) that this drawer uses for drawing
|
||||||
* @member {Element} canvas
|
* @member {Element} canvas
|
||||||
@ -210,7 +212,7 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
|||||||
// content during animation of the container size.
|
// content during animation of the container size.
|
||||||
|
|
||||||
if ( !tile.element ) {
|
if ( !tile.element ) {
|
||||||
var image = tile.getImage();
|
const image = this.getCompatibleData(tile);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -510,7 +510,7 @@ $.Tile.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
if (this.tiledImage && !this.tiledImage.__typeWarningReported) {
|
if (!this.tiledImage.__typeWarningReported) {
|
||||||
$.console.warn(this, "[Tile.setCache] called without type specification. " +
|
$.console.warn(this, "[Tile.setCache] called without type specification. " +
|
||||||
"Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
|
"Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
|
||||||
this.tiledImage.__typeWarningReported = true;
|
this.tiledImage.__typeWarningReported = true;
|
||||||
@ -520,10 +520,11 @@ $.Tile.prototype = {
|
|||||||
|
|
||||||
const writesToRenderingCache = key === this.cacheKey;
|
const writesToRenderingCache = key === this.cacheKey;
|
||||||
if (writesToRenderingCache && _safely) {
|
if (writesToRenderingCache && _safely) {
|
||||||
//todo after-merge-aiosa decide dynamically
|
// Need to get the supported type for rendering out of the active drawer.
|
||||||
const conversion = $.convertor.getConversionPath(type, "context2d");
|
const supportedTypes = this.tiledImage.viewer.drawer.getSupportedDataFormats();
|
||||||
|
const conversion = $.convertor.getConversionPath(type, supportedTypes);
|
||||||
$.console.assert(conversion, "[Tile.setCache] data was set for the default tile cache we are unable" +
|
$.console.assert(conversion, "[Tile.setCache] data was set for the default tile cache we are unable" +
|
||||||
"to render. Make sure OpenSeadragon.convertor was taught to convert type: " + type);
|
"to render. Make sure OpenSeadragon.convertor was taught to convert to (one of): " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.__cutoff) {
|
if (!this.__cutoff) {
|
||||||
|
@ -136,8 +136,9 @@
|
|||||||
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
|
* @returns {OpenSeadragon.Promise<?>} desired data type in promise, undefined if the cache was destroyed
|
||||||
*/
|
*/
|
||||||
getDataAs(type = this._type, copy = true) {
|
getDataAs(type = this._type, copy = true) {
|
||||||
|
const referenceTile = this._tiles[0];
|
||||||
if (this.loaded && type === this._type) {
|
if (this.loaded && type === this._type) {
|
||||||
return copy ? $.convertor.copy(this._data, type) : this._promise;
|
return copy ? $.convertor.copy(referenceTile, this._data, type) : this._promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._promise.then(data => {
|
return this._promise.then(data => {
|
||||||
@ -146,10 +147,10 @@
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (type !== this._type) {
|
if (type !== this._type) {
|
||||||
return $.convertor.convert(data, this._type, type);
|
return $.convertor.convert(referenceTile, data, this._type, type);
|
||||||
}
|
}
|
||||||
if (copy) { //convert does not copy data if same type, do explicitly
|
if (copy) { //convert does not copy data if same type, do explicitly
|
||||||
return $.convertor.copy(data, type);
|
return $.convertor.copy(referenceTile, data, type);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
@ -158,11 +159,15 @@
|
|||||||
/**
|
/**
|
||||||
* Transform cache to desired type and get the data after conversion.
|
* Transform cache to desired type and get the data after conversion.
|
||||||
* Does nothing if the type equals to the current type. Asynchronous.
|
* Does nothing if the type equals to the current type. Asynchronous.
|
||||||
* @param {string} type
|
* @param {string|[string]} type if array provided, the system will
|
||||||
|
* try to optimize for the best type to convert to.
|
||||||
* @return {OpenSeadragon.Promise<?>|*}
|
* @return {OpenSeadragon.Promise<?>|*}
|
||||||
*/
|
*/
|
||||||
transformTo(type = this._type) {
|
transformTo(type = this._type) {
|
||||||
if (!this.loaded || type !== this._type) {
|
if (!this.loaded ||
|
||||||
|
type !== this._type ||
|
||||||
|
(Array.isArray(type) && !type.includes(this._type))) {
|
||||||
|
|
||||||
if (!this.loaded) {
|
if (!this.loaded) {
|
||||||
this._conversionJobQueue = this._conversionJobQueue || [];
|
this._conversionJobQueue = this._conversionJobQueue || [];
|
||||||
let resolver = null;
|
let resolver = null;
|
||||||
@ -173,7 +178,8 @@
|
|||||||
if (this._destroyed) {
|
if (this._destroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type !== this._type) {
|
//must re-check types since we perform in a queue of conversion requests
|
||||||
|
if (type !== this._type || (Array.isArray(type) && !type.includes(this._type))) {
|
||||||
//ensures queue gets executed after finish
|
//ensures queue gets executed after finish
|
||||||
this._convert(this._type, type);
|
this._convert(this._type, type);
|
||||||
this._promise.then(data => resolver(data));
|
this._promise.then(data => resolver(data));
|
||||||
@ -351,10 +357,13 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Private conversion that makes sure the cache knows its data is ready
|
* Private conversion that makes sure the cache knows its data is ready
|
||||||
|
* @param to array or a string - allowed types
|
||||||
|
* @param from string - type origin
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_convert(from, to) {
|
_convert(from, to) {
|
||||||
const convertor = $.convertor,
|
const convertor = $.convertor,
|
||||||
|
referenceTile = this._tiles[0],
|
||||||
conversionPath = convertor.getConversionPath(from, to);
|
conversionPath = convertor.getConversionPath(from, to);
|
||||||
if (!conversionPath) {
|
if (!conversionPath) {
|
||||||
$.console.error(`[OpenSeadragon.convertor.convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
$.console.error(`[OpenSeadragon.convertor.convert] Conversion conversion ${from} ---> ${to} cannot be done!`);
|
||||||
@ -372,7 +381,7 @@
|
|||||||
return $.Promise.resolve(x);
|
return $.Promise.resolve(x);
|
||||||
}
|
}
|
||||||
let edge = conversionPath[i];
|
let edge = conversionPath[i];
|
||||||
return $.Promise.resolve(edge.transform(x)).then(
|
return $.Promise.resolve(edge.transform(referenceTile, x)).then(
|
||||||
y => {
|
y => {
|
||||||
if (!y) {
|
if (!y) {
|
||||||
$.console.error(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting using %s)`, edge);
|
$.console.error(`[OpenSeadragon.convertor.convert] data mid result falsey value (while converting using %s)`, edge);
|
||||||
@ -391,7 +400,8 @@
|
|||||||
|
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this._data = undefined;
|
this._data = undefined;
|
||||||
this._type = to;
|
// Read target type from the conversion path: [edge.target] = Vertex, its value=type
|
||||||
|
this._type = conversionPath[stepCount - 1].target.value;
|
||||||
this._promise = convert(originalData, 0);
|
this._promise = convert(originalData, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -657,6 +667,11 @@
|
|||||||
this._tilesLoaded.splice( deleteAtIndex, 1 );
|
this._tilesLoaded.splice( deleteAtIndex, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Possible error: it can happen that unloaded tile gets to this stage. Should it even be allowed to happen?
|
||||||
|
if (!tile.loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const tiledImage = tile.tiledImage;
|
const tiledImage = tile.tiledImage;
|
||||||
tile.unload();
|
tile.unload();
|
||||||
|
|
||||||
|
@ -2126,14 +2126,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
);
|
);
|
||||||
//make sure cache data is ready for drawing, if not, request the desired format
|
//make sure cache data is ready for drawing, if not, request the desired format
|
||||||
const cache = tile.getCache(tile.cacheKey),
|
const cache = tile.getCache(tile.cacheKey),
|
||||||
// TODO: after-merge-aiosa dynamic type declaration from the drawer base class interface
|
requiredTypes = _this.viewer.drawer.getSupportedDataFormats();
|
||||||
requiredType = _this._drawer.useCanvas ? "context2d" : "image";
|
|
||||||
if (!cache) {
|
if (!cache) {
|
||||||
$.console.warn("Tile %s not cached at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
$.console.warn("Tile %s not cached at the end of tile-loaded event: tile will not be drawn - it has no data!", tile);
|
||||||
resolver(tile);
|
resolver(tile);
|
||||||
} else if (cache.type !== requiredType) {
|
} else if (!requiredTypes.includes(cache.type)) {
|
||||||
//initiate conversion as soon as possible if incompatible with the drawer
|
//initiate conversion as soon as possible if incompatible with the drawer
|
||||||
cache.transformTo(requiredType).then(_ => {
|
cache.transformTo(requiredTypes).then(_ => {
|
||||||
tile.loading = false;
|
tile.loading = false;
|
||||||
tile.loaded = true;
|
tile.loaded = true;
|
||||||
resolver(tile);
|
resolver(tile);
|
||||||
|
@ -379,7 +379,9 @@ $.Viewer = function( options ) {
|
|||||||
//update tiles
|
//update tiles
|
||||||
item.opacity = 0; //prevent draw
|
item.opacity = 0; //prevent draw
|
||||||
item.maxTilesPerFrame = 50; //todo based on image size and also number of images!
|
item.maxTilesPerFrame = 50; //todo based on image size and also number of images!
|
||||||
item._updateViewport();
|
|
||||||
|
//TODO check if the method is used correctly
|
||||||
|
item._updateLevelsForViewport();
|
||||||
item._needsDraw = true; //we did not draw
|
item._needsDraw = true; //we did not draw
|
||||||
item.opacity = origOpacity;
|
item.opacity = origOpacity;
|
||||||
item.maxTilesPerFrame = origMaxTiles;
|
item.maxTilesPerFrame = origMaxTiles;
|
||||||
|
@ -40,23 +40,23 @@
|
|||||||
/**
|
/**
|
||||||
* @class OpenSeadragon.WebGLDrawer
|
* @class OpenSeadragon.WebGLDrawer
|
||||||
* @classdesc Default implementation of WebGLDrawer for an {@link OpenSeadragon.Viewer}. The WebGLDrawer
|
* @classdesc Default implementation of WebGLDrawer for an {@link OpenSeadragon.Viewer}. The WebGLDrawer
|
||||||
* loads tile data as textures to the graphics card as soon as it is available (via the tile-ready event),
|
* defines its own data type that ensures textures are correctly loaded to and deleted from the GPU memory.
|
||||||
* and unloads the data (via the image-unloaded event). The drawer utilizes a context-dependent two pass drawing pipeline.
|
* The drawer utilizes a context-dependent two pass drawing pipeline. For the first pass, tile composition
|
||||||
* For the first pass, tile composition for a given TiledImage is always done using a canvas with a WebGL context.
|
* for a given TiledImage is always done using a canvas with a WebGL context. This allows tiles to be stitched
|
||||||
* This allows tiles to be stitched together without seams or artifacts, without requiring a tile source with overlap. If overlap is present,
|
* together without seams or artifacts, without requiring a tile source with overlap. If overlap is present,
|
||||||
* overlapping pixels are discarded. The second pass copies all pixel data from the WebGL context onto an output canvas
|
* overlapping pixels are discarded. The second pass copies all pixel data from the WebGL context onto an output
|
||||||
* with a Context2d context. This allows applications to have access to pixel data and other functionality provided by
|
* canvas with a Context2d context. This allows applications to have access to pixel data and other functionality
|
||||||
* Context2d, regardless of whether the CanvasDrawer or the WebGLDrawer is used. Certain options, including compositeOperation,
|
* provided by Context2d, regardless of whether the CanvasDrawer or the WebGLDrawer is used. Certain options,
|
||||||
* clip, croppingPolygons, and debugMode are implemented using Context2d operations; in these scenarios, each TiledImage is
|
* including compositeOperation, clip, croppingPolygons, and debugMode are implemented using Context2d operations;
|
||||||
* drawn onto the output canvas immediately after the tile composition step (pass 1). Otherwise, for efficiency, all TiledImages
|
* in these scenarios, each TiledImage is drawn onto the output canvas immediately after the tile composition step
|
||||||
* are copied over to the output canvas at once, after all tiles have been composited for all images.
|
* (pass 1). Otherwise, for efficiency, all TiledImages are copied over to the output canvas at once, after all
|
||||||
|
* tiles have been composited for all images.
|
||||||
* @param {Object} options - Options for this Drawer.
|
* @param {Object} options - Options for this Drawer.
|
||||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||||
* @param {Element} options.element - Parent element.
|
* @param {Element} options.element - Parent element.
|
||||||
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
OpenSeadragon.WebGLDrawer = class WebGLDrawer extends OpenSeadragon.DrawerBase{
|
OpenSeadragon.WebGLDrawer = class WebGLDrawer extends OpenSeadragon.DrawerBase{
|
||||||
constructor(options){
|
constructor(options){
|
||||||
super(options);
|
super(options);
|
||||||
@ -76,24 +76,20 @@
|
|||||||
|
|
||||||
// private members
|
// private members
|
||||||
this._destroyed = false;
|
this._destroyed = false;
|
||||||
this._TextureMap = new Map();
|
|
||||||
this._TileMap = new Map();
|
|
||||||
|
|
||||||
this._gl = null;
|
this._gl = null;
|
||||||
this._firstPass = null;
|
this._firstPass = null;
|
||||||
this._secondPass = null;
|
this._secondPass = null;
|
||||||
this._glFrameBuffer = null;
|
this._glFrameBuffer = null;
|
||||||
this._renderToTexture = null;
|
this._renderToTexture = null;
|
||||||
this._glFramebufferToCanvasTransform = null;
|
|
||||||
this._outputCanvas = null;
|
this._outputCanvas = null;
|
||||||
this._outputContext = null;
|
this._outputContext = null;
|
||||||
this._clippingCanvas = null;
|
this._clippingCanvas = null;
|
||||||
this._clippingContext = null;
|
this._clippingContext = null;
|
||||||
this._renderingCanvas = null;
|
this._renderingCanvas = null;
|
||||||
|
|
||||||
// Add listeners for events that require modifying the scene or camera
|
// Unique type per drawer: uploads texture to unique webgl context.
|
||||||
this.viewer.addHandler("tile-ready", ev => this._tileReadyHandler(ev));
|
this._dataType = `${Date.now()}_TEX_2D`;
|
||||||
this.viewer.addHandler("image-unloaded", ev => this._imageUnloadedHandler(ev));
|
this._setupTextureHandlers(this._dataType);
|
||||||
|
|
||||||
// Reject listening for the tile-drawing and tile-drawn events, which this drawer does not fire
|
// Reject listening for the tile-drawing and tile-drawn events, which this drawer does not fire
|
||||||
this.viewer.rejectEventHandler("tile-drawn", "The WebGLDrawer does not raise the tile-drawn event");
|
this.viewer.rejectEventHandler("tile-drawn", "The WebGLDrawer does not raise the tile-drawn event");
|
||||||
@ -132,11 +128,6 @@
|
|||||||
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||||
|
|
||||||
let canvases = Array.from(this._TextureMap.keys());
|
|
||||||
canvases.forEach(canvas => {
|
|
||||||
this._cleanupImageData(canvas); // deletes texture, removes from _TextureMap
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete all our created resources
|
// Delete all our created resources
|
||||||
gl.deleteBuffer(this._secondPass.bufferOutputPosition);
|
gl.deleteBuffer(this._secondPass.bufferOutputPosition);
|
||||||
gl.deleteFramebuffer(this._glFrameBuffer);
|
gl.deleteFramebuffer(this._glFrameBuffer);
|
||||||
@ -316,15 +307,21 @@
|
|||||||
let tile = tilesToDraw[tileIndex].tile;
|
let tile = tilesToDraw[tileIndex].tile;
|
||||||
let indexInDrawArray = tileIndex % maxTextures;
|
let indexInDrawArray = tileIndex % maxTextures;
|
||||||
let numTilesToDraw = indexInDrawArray + 1;
|
let numTilesToDraw = indexInDrawArray + 1;
|
||||||
let tileContext = tile.getCanvasContext();
|
|
||||||
|
|
||||||
let textureInfo = tileContext ? this._TextureMap.get(tileContext.canvas) : null;
|
if ( !tile.loaded ) {
|
||||||
if(textureInfo){
|
$.console.warn(
|
||||||
this._getTileData(tile, tiledImage, textureInfo, overallMatrix, indexInDrawArray, texturePositionArray, textureDataArray, matrixArray, opacityArray);
|
"Attempting to draw tile %s when it's not yet loaded.",
|
||||||
} else {
|
tile.toString()
|
||||||
// console.log('No tile info', tile);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if( (numTilesToDraw === maxTextures) || (tileIndex === tilesToDraw.length - 1)){
|
const textureInfo = this.getCompatibleData(tile);
|
||||||
|
if (!textureInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._getTileData(tile, tiledImage, textureInfo, overallMatrix, indexInDrawArray, texturePositionArray, textureDataArray, matrixArray, opacityArray);
|
||||||
|
|
||||||
|
if ((numTilesToDraw === maxTextures) || (tileIndex === tilesToDraw.length - 1)){
|
||||||
// We've filled up the buffers: time to draw this set of tiles
|
// We've filled up the buffers: time to draw this set of tiles
|
||||||
|
|
||||||
// bind each tile's texture to the appropriate gl.TEXTURE#
|
// bind each tile's texture to the appropriate gl.TEXTURE#
|
||||||
@ -786,27 +783,12 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// private
|
_setupTextureHandlers(thisType) {
|
||||||
_makeQuadVertexBuffer(left, right, top, bottom){
|
const tex2DCompatibleLoader = (tile, data) => {
|
||||||
return new Float32Array([
|
let tiledImage = tile.tiledImage;
|
||||||
left, bottom,
|
//todo verify we are calling conversion just right amount of time!
|
||||||
right, bottom,
|
// e.g. no upload of cpu-existing texture
|
||||||
left, top,
|
|
||||||
left, top,
|
|
||||||
right, bottom,
|
|
||||||
right, top]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
_tileReadyHandler(event){
|
|
||||||
let tile = event.tile;
|
|
||||||
let tiledImage = event.tiledImage;
|
|
||||||
let tileContext = tile.getCanvasContext();
|
|
||||||
let canvas = tileContext.canvas;
|
|
||||||
let textureInfo = this._TextureMap.get(canvas);
|
|
||||||
|
|
||||||
// if this is a new image for us, create a texture
|
|
||||||
if(!textureInfo){
|
|
||||||
let gl = this._gl;
|
let gl = this._gl;
|
||||||
|
|
||||||
// create a gl Texture for this tile and bind the canvas with the image data
|
// create a gl Texture for this tile and bind the canvas with the image data
|
||||||
@ -828,13 +810,6 @@
|
|||||||
position = this._unitQuad;
|
position = this._unitQuad;
|
||||||
}
|
}
|
||||||
|
|
||||||
let textureInfo = {
|
|
||||||
texture: texture,
|
|
||||||
position: position,
|
|
||||||
};
|
|
||||||
|
|
||||||
// add it to our _TextureMap
|
|
||||||
this._TextureMap.set(canvas, textureInfo);
|
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
// Set the parameters so we can render any size image.
|
// Set the parameters so we can render any size image.
|
||||||
@ -843,11 +818,55 @@
|
|||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
|
|
||||||
// Upload the image into the texture.
|
try{
|
||||||
this._uploadImageData(tileContext);
|
// This depends on gl.TEXTURE_2D being bound to the texture
|
||||||
|
// associated with this canvas before calling this function
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
||||||
|
} catch (e){
|
||||||
|
$.console.error('Error uploading image data to WebGL', e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// TextureInfo stored in the cache
|
||||||
|
return {
|
||||||
|
texture: texture,
|
||||||
|
position: position,
|
||||||
|
cpuData: data,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const tex2DCompatibleDestructor = textureInfo => {
|
||||||
|
if (textureInfo) {
|
||||||
|
this._gl.deleteTexture(textureInfo.texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dataRetrieval = (tile, data) => {
|
||||||
|
return data.cpuData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Differentiate type also based on type used to upload data: we can support bidirectional conversion.
|
||||||
|
const c2dTexType = thisType + ":context2d",
|
||||||
|
imageTexType = thisType + ":image";
|
||||||
|
|
||||||
|
this.declareSupportedDataFormats(imageTexType, c2dTexType);
|
||||||
|
|
||||||
|
// We should be OK uploading any of these types.
|
||||||
|
$.convertor.learn("context2d", c2dTexType, tex2DCompatibleLoader, 1, 2);
|
||||||
|
$.convertor.learn("image", imageTexType, tex2DCompatibleLoader, 1, 2);
|
||||||
|
$.convertor.learn(c2dTexType, "context2d", dataRetrieval, 1, 2);
|
||||||
|
$.convertor.learn(imageTexType, "image", dataRetrieval, 1, 2);
|
||||||
|
|
||||||
|
$.convertor.learnDestroy(c2dTexType, tex2DCompatibleDestructor);
|
||||||
|
$.convertor.learnDestroy(imageTexType, tex2DCompatibleDestructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
_makeQuadVertexBuffer(left, right, top, bottom){
|
||||||
|
return new Float32Array([
|
||||||
|
left, bottom,
|
||||||
|
right, bottom,
|
||||||
|
left, top,
|
||||||
|
left, top,
|
||||||
|
right, bottom,
|
||||||
|
right, top]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private
|
// private
|
||||||
@ -865,43 +884,6 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// private
|
|
||||||
_uploadImageData(tileContext){
|
|
||||||
|
|
||||||
let gl = this._gl;
|
|
||||||
let canvas = tileContext.canvas;
|
|
||||||
|
|
||||||
try{
|
|
||||||
if(!canvas){
|
|
||||||
throw('Tile context does not have a canvas', tileContext);
|
|
||||||
}
|
|
||||||
// This depends on gl.TEXTURE_2D being bound to the texture
|
|
||||||
// associated with this canvas before calling this function
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
|
|
||||||
} catch (e){
|
|
||||||
$.console.error('Error uploading image data to WebGL', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
_imageUnloadedHandler(event){
|
|
||||||
let canvas = event.context2D.canvas;
|
|
||||||
this._cleanupImageData(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
_cleanupImageData(tileCanvas){
|
|
||||||
let textureInfo = this._TextureMap.get(tileCanvas);
|
|
||||||
//remove from the map
|
|
||||||
this._TextureMap.delete(tileCanvas);
|
|
||||||
|
|
||||||
//release the texture from the GPU
|
|
||||||
if(textureInfo){
|
|
||||||
this._gl.deleteTexture(textureInfo.texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
// private
|
||||||
_setClip(rect){
|
_setClip(rect){
|
||||||
this._clippingContext.beginPath();
|
this._clippingContext.beginPath();
|
||||||
@ -1133,9 +1115,7 @@
|
|||||||
|
|
||||||
return shaderProgram;
|
return shaderProgram;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}( OpenSeadragon ));
|
}( OpenSeadragon ));
|
||||||
|
@ -332,9 +332,10 @@
|
|||||||
} ]
|
} ]
|
||||||
} );
|
} );
|
||||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||||
assert.ok(OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
event.tiles[0].getCache().getDataAs("context2d", false).then(context =>
|
||||||
"Canvas should be tainted.");
|
assert.ok(OpenSeadragon.isCanvasTainted(context.canvas),
|
||||||
done();
|
"Canvas should be tainted.")
|
||||||
|
).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
} );
|
} );
|
||||||
@ -352,9 +353,10 @@
|
|||||||
} ]
|
} ]
|
||||||
} );
|
} );
|
||||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||||
assert.ok(!OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
event.tiles[0].getCache().getDataAs("context2d", false).then(context =>
|
||||||
"Canvas should not be tainted.");
|
assert.notOk(OpenSeadragon.isCanvasTainted(context.canvas),
|
||||||
done();
|
"Canvas should be tainted.")
|
||||||
|
).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
} );
|
} );
|
||||||
@ -376,9 +378,10 @@
|
|||||||
crossOriginPolicy : false
|
crossOriginPolicy : false
|
||||||
} );
|
} );
|
||||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||||
assert.ok(OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
event.tiles[0].getCache().getDataAs("context2d", false).then(context =>
|
||||||
"Canvas should be tainted.");
|
assert.ok(OpenSeadragon.isCanvasTainted(context.canvas),
|
||||||
done();
|
"Canvas should be tainted.")
|
||||||
|
).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
} );
|
} );
|
||||||
@ -400,9 +403,10 @@
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
viewer.addOnceHandler('tiled-image-drawn', function(event) {
|
||||||
assert.ok(!OpenSeadragon.isCanvasTainted(event.tiles[0].getCanvasContext().canvas),
|
event.tiles[0].getCache().getDataAs("context2d", false).then(context =>
|
||||||
"Canvas should not be tainted.");
|
assert.notOk(OpenSeadragon.isCanvasTainted(context.canvas),
|
||||||
done();
|
"Canvas should be tainted.")
|
||||||
|
).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
@ -33,46 +33,46 @@
|
|||||||
// other tests will interfere
|
// other tests will interfere
|
||||||
let typeAtoB = 0, typeBtoC = 0, typeCtoA = 0, typeDtoA = 0, typeCtoE = 0;
|
let typeAtoB = 0, typeBtoC = 0, typeCtoA = 0, typeDtoA = 0, typeCtoE = 0;
|
||||||
//set all same costs to get easy testing, know which path will be taken
|
//set all same costs to get easy testing, know which path will be taken
|
||||||
Convertor.learn(T_A, T_B, x => {
|
Convertor.learn(T_A, T_B, (tile, x) => {
|
||||||
typeAtoB++;
|
typeAtoB++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_B, T_C, x => {
|
Convertor.learn(T_B, T_C, (tile, x) => {
|
||||||
typeBtoC++;
|
typeBtoC++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_C, T_A, x => {
|
Convertor.learn(T_C, T_A, (tile, x) => {
|
||||||
typeCtoA++;
|
typeCtoA++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_D, T_A, x => {
|
Convertor.learn(T_D, T_A, (tile, x) => {
|
||||||
typeDtoA++;
|
typeDtoA++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_C, T_E, x => {
|
Convertor.learn(T_C, T_E, (tile, x) => {
|
||||||
typeCtoE++;
|
typeCtoE++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
//'Copy constructors'
|
//'Copy constructors'
|
||||||
let copyA = 0, copyB = 0, copyC = 0, copyD = 0, copyE = 0;
|
let copyA = 0, copyB = 0, copyC = 0, copyD = 0, copyE = 0;
|
||||||
//also learn destructors
|
//also learn destructors
|
||||||
Convertor.learn(T_A, T_A,x => {
|
Convertor.learn(T_A, T_A,(tile, x) => {
|
||||||
copyA++;
|
copyA++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_B, T_B,x => {
|
Convertor.learn(T_B, T_B,(tile, x) => {
|
||||||
copyB++;
|
copyB++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_C, T_C,x => {
|
Convertor.learn(T_C, T_C,(tile, x) => {
|
||||||
copyC++;
|
copyC++;
|
||||||
return x-1;
|
return x-1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_D, T_D,x => {
|
Convertor.learn(T_D, T_D,(tile, x) => {
|
||||||
copyD++;
|
copyD++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
Convertor.learn(T_E, T_E,x => {
|
Convertor.learn(T_E, T_E,(tile, x) => {
|
||||||
copyE++;
|
copyE++;
|
||||||
return x+1;
|
return x+1;
|
||||||
});
|
});
|
||||||
|
@ -31,23 +31,23 @@
|
|||||||
let imageToCanvas = 0, srcToImage = 0, context2DtoImage = 0, canvasToContext2D = 0, imageToUrl = 0,
|
let imageToCanvas = 0, srcToImage = 0, context2DtoImage = 0, canvasToContext2D = 0, imageToUrl = 0,
|
||||||
canvasToUrl = 0;
|
canvasToUrl = 0;
|
||||||
//set all same costs to get easy testing, know which path will be taken
|
//set all same costs to get easy testing, know which path will be taken
|
||||||
Convertor.learn("__TEST__canvas", "__TEST__url", canvas => {
|
Convertor.learn("__TEST__canvas", "__TEST__url", (tile, canvas) => {
|
||||||
canvasToUrl++;
|
canvasToUrl++;
|
||||||
return canvas.toDataURL();
|
return canvas.toDataURL();
|
||||||
}, 1, 1);
|
}, 1, 1);
|
||||||
Convertor.learn("__TEST__image", "__TEST__url", image => {
|
Convertor.learn("__TEST__image", "__TEST__url", (tile,image) => {
|
||||||
imageToUrl++;
|
imageToUrl++;
|
||||||
return image.url;
|
return image.url;
|
||||||
}, 1, 1);
|
}, 1, 1);
|
||||||
Convertor.learn("__TEST__canvas", "__TEST__context2d", canvas => {
|
Convertor.learn("__TEST__canvas", "__TEST__context2d", (tile,canvas) => {
|
||||||
canvasToContext2D++;
|
canvasToContext2D++;
|
||||||
return canvas.getContext("2d");
|
return canvas.getContext("2d");
|
||||||
}, 1, 1);
|
}, 1, 1);
|
||||||
Convertor.learn("__TEST__context2d", "__TEST__canvas", context2D => {
|
Convertor.learn("__TEST__context2d", "__TEST__canvas", (tile,context2D) => {
|
||||||
context2DtoImage++;
|
context2DtoImage++;
|
||||||
return context2D.canvas;
|
return context2D.canvas;
|
||||||
}, 1, 1);
|
}, 1, 1);
|
||||||
Convertor.learn("__TEST__image", "__TEST__canvas", image => {
|
Convertor.learn("__TEST__image", "__TEST__canvas", (tile,image) => {
|
||||||
imageToCanvas++;
|
imageToCanvas++;
|
||||||
const canvas = document.createElement( 'canvas' );
|
const canvas = document.createElement( 'canvas' );
|
||||||
canvas.width = image.width;
|
canvas.width = image.width;
|
||||||
@ -56,7 +56,7 @@
|
|||||||
context.drawImage( image, 0, 0 );
|
context.drawImage( image, 0, 0 );
|
||||||
return canvas;
|
return canvas;
|
||||||
}, 1, 1);
|
}, 1, 1);
|
||||||
Convertor.learn("__TEST__url", "__TEST__image", url => {
|
Convertor.learn("__TEST__url", "__TEST__image", (tile, url) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
srcToImage++;
|
srcToImage++;
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
@ -68,7 +68,8 @@
|
|||||||
|
|
||||||
let canvasDestroy = 0, imageDestroy = 0, contex2DDestroy = 0, urlDestroy = 0;
|
let canvasDestroy = 0, imageDestroy = 0, contex2DDestroy = 0, urlDestroy = 0;
|
||||||
//also learn destructors
|
//also learn destructors
|
||||||
Convertor.learnDestroy("__TEST__canvas", () => {
|
Convertor.learnDestroy("__TEST__canvas", canvas => {
|
||||||
|
canvas.width = canvas.height = 0;
|
||||||
canvasDestroy++;
|
canvasDestroy++;
|
||||||
});
|
});
|
||||||
Convertor.learnDestroy("__TEST__image", () => {
|
Convertor.learnDestroy("__TEST__image", () => {
|
||||||
@ -145,20 +146,20 @@
|
|||||||
context.drawImage( image, 0, 0 );
|
context.drawImage( image, 0, 0 );
|
||||||
|
|
||||||
//copy URL
|
//copy URL
|
||||||
const URL2 = await Convertor.copy(URL, "url");
|
const URL2 = await Convertor.copy(null, URL, "url");
|
||||||
//we cannot check if they are not the same object, strings are immutable (and we don't copy anyway :D )
|
//we cannot check if they are not the same object, strings are immutable (and we don't copy anyway :D )
|
||||||
test.equal(URL, URL2, "String copy is equal in data.");
|
test.equal(URL, URL2, "String copy is equal in data.");
|
||||||
test.equal(typeof URL, typeof URL2, "Type of copies equals.");
|
test.equal(typeof URL, typeof URL2, "Type of copies equals.");
|
||||||
test.equal(URL.length, URL2.length, "Data length is also equal.");
|
test.equal(URL.length, URL2.length, "Data length is also equal.");
|
||||||
|
|
||||||
//copy context
|
//copy context
|
||||||
const context2 = await Convertor.copy(context, "context2d");
|
const context2 = await Convertor.copy(null, context, "context2d");
|
||||||
test.notEqual(context, context2, "Copy is not the same as original canvas.");
|
test.notEqual(context, context2, "Copy is not the same as original canvas.");
|
||||||
test.equal(typeof context, typeof context2, "Type of copies equals.");
|
test.equal(typeof context, typeof context2, "Type of copies equals.");
|
||||||
test.equal(context.canvas.toDataURL(), context2.canvas.toDataURL(), "Data is equal.");
|
test.equal(context.canvas.toDataURL(), context2.canvas.toDataURL(), "Data is equal.");
|
||||||
|
|
||||||
//copy image
|
//copy image
|
||||||
const image2 = await Convertor.copy(image, "image");
|
const image2 = await Convertor.copy(null, image, "image");
|
||||||
test.notEqual(image, image2, "Copy is not the same as original image.");
|
test.notEqual(image, image2, "Copy is not the same as original image.");
|
||||||
test.equal(typeof image, typeof image2, "Type of copies equals.");
|
test.equal(typeof image, typeof image2, "Type of copies equals.");
|
||||||
test.equal(image.src, image2.src, "Data is equal.");
|
test.equal(image.src, image2.src, "Data is equal.");
|
||||||
@ -173,7 +174,7 @@
|
|||||||
const done = test.async();
|
const done = test.async();
|
||||||
|
|
||||||
//load image object: url -> image
|
//load image object: url -> image
|
||||||
Convertor.convert("/test/data/A.png", "__TEST__url", "__TEST__image").then(i => {
|
Convertor.convert(null, "/test/data/A.png", "__TEST__url", "__TEST__image").then(i => {
|
||||||
test.equal(OpenSeadragon.type(i), "image", "Got image object after conversion.");
|
test.equal(OpenSeadragon.type(i), "image", "Got image object after conversion.");
|
||||||
test.equal(srcToImage, 1, "Conversion happened.");
|
test.equal(srcToImage, 1, "Conversion happened.");
|
||||||
|
|
||||||
@ -182,14 +183,14 @@
|
|||||||
test.equal(urlDestroy, 1, "Url destructor called.");
|
test.equal(urlDestroy, 1, "Url destructor called.");
|
||||||
|
|
||||||
test.equal(imageDestroy, 0, "Image destructor not called.");
|
test.equal(imageDestroy, 0, "Image destructor not called.");
|
||||||
return Convertor.convert(i, "__TEST__image", "__TEST__canvas");
|
return Convertor.convert(null, i, "__TEST__image", "__TEST__canvas");
|
||||||
}).then(c => { //path image -> canvas
|
}).then(c => { //path image -> canvas
|
||||||
test.equal(OpenSeadragon.type(c), "canvas", "Got canvas object after conversion.");
|
test.equal(OpenSeadragon.type(c), "canvas", "Got canvas object after conversion.");
|
||||||
test.equal(srcToImage, 1, "Conversion ulr->image did not happen.");
|
test.equal(srcToImage, 1, "Conversion ulr->image did not happen.");
|
||||||
test.equal(imageToCanvas, 1, "Conversion image->canvas happened.");
|
test.equal(imageToCanvas, 1, "Conversion image->canvas happened.");
|
||||||
test.equal(urlDestroy, 1, "Url destructor not called.");
|
test.equal(urlDestroy, 1, "Url destructor not called.");
|
||||||
test.equal(imageDestroy, 0, "Image destructor not called unless we ask it.");
|
test.equal(imageDestroy, 0, "Image destructor not called unless we ask it.");
|
||||||
return Convertor.convert(c, "__TEST__canvas", "__TEST__image");
|
return Convertor.convert(null, c, "__TEST__canvas", "__TEST__image");
|
||||||
}).then(i => { //path canvas, image: canvas -> url -> image
|
}).then(i => { //path canvas, image: canvas -> url -> image
|
||||||
test.equal(OpenSeadragon.type(i), "image", "Got image object after conversion.");
|
test.equal(OpenSeadragon.type(i), "image", "Got image object after conversion.");
|
||||||
test.equal(srcToImage, 2, "Conversion ulr->image happened.");
|
test.equal(srcToImage, 2, "Conversion ulr->image happened.");
|
||||||
@ -314,7 +315,7 @@
|
|||||||
const done = test.async();
|
const done = test.async();
|
||||||
|
|
||||||
let conversionHappened = false;
|
let conversionHappened = false;
|
||||||
Convertor.learn("__TEST__url", "__TEST__longConversionProcessForTesting", value => {
|
Convertor.learn("__TEST__url", "__TEST__longConversionProcessForTesting", (tile, value) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
conversionHappened = true;
|
conversionHappened = true;
|
||||||
@ -358,7 +359,7 @@
|
|||||||
const done = test.async();
|
const done = test.async();
|
||||||
|
|
||||||
let conversionHappened = false;
|
let conversionHappened = false;
|
||||||
Convertor.learn("__TEST__url", "__TEST__longConversionProcessForTesting", value => {
|
Convertor.learn("__TEST__url", "__TEST__longConversionProcessForTesting", (tile, value) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
conversionHappened = true;
|
conversionHappened = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user