From 9ef2d46e7573745e5ba12eb9987c015500727d4d Mon Sep 17 00:00:00 2001 From: Aiosa <469130@mail.muni.cz> Date: Mon, 5 Feb 2024 09:42:26 +0100 Subject: [PATCH] Fix tests: always fetch up-to-date pixel values, prevent adding loaded tile to the 'bestTiles' array. Enforce _needsDraw check to be based on lastDrawn - we are async now. --- src/tiledimage.js | 6 ++++-- src/webgldrawer.js | 30 +++++++++++++------------- src/world.js | 4 ++-- test/modules/multi-image.js | 42 ++++++++++++++++--------------------- test/modules/tilecache.js | 32 ++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 20911365..b0d9cc54 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -352,7 +352,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @returns {Boolean} whether the item still needs to be drawn due to blending */ setDrawn: function(){ - this._needsDraw = this._isBlending || this._wasBlending; + this._needsDraw = this._isBlending || this._wasBlending || + (this.opacity > 0 && this._lastDrawn.length < 1); return this._needsDraw; }, @@ -1825,7 +1826,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag if ( tile.loading ) { // the tile is already in the download queue this._tilesLoading++; - } else if (!loadingCoverage) { + } else if (!tile.loaded && !loadingCoverage) { + // add tile to best tiles to load only when not loaded already best = this._compareTiles( best, tile, this.maxTilesPerFrame ); } diff --git a/src/webgldrawer.js b/src/webgldrawer.js index 620496dc..f8918f20 100644 --- a/src/webgldrawer.js +++ b/src/webgldrawer.js @@ -87,10 +87,6 @@ this._clippingContext = null; this._renderingCanvas = null; - // Unique type per drawer: uploads texture to unique webgl context. - this._dataType = `${Date.now()}_TEX_2D`; - this._setupTextureHandlers(this._dataType); - // 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-drawing", "The WebGLDrawer does not raise the tile-drawing event"); @@ -101,6 +97,10 @@ this._setupCanvases(); this._setupRenderer(); + // Unique type per drawer: uploads texture to unique webgl context. + this._dataType = `${Date.now()}_TEX_2D`; + this._setupTextureHandlers(this._dataType); + this.context = this._outputContext; // API required by tests } @@ -757,7 +757,6 @@ this._renderingCanvas.height = this._clippingCanvas.height = this._outputCanvas.height; this._gl = this._renderingCanvas.getContext('webgl'); - //make the additional canvas elements mirror size changes to the output canvas this.viewer.addHandler("resize", function(){ @@ -784,12 +783,14 @@ } _setupTextureHandlers(thisType) { + const _this = this; const tex2DCompatibleLoader = (tile, data) => { let tiledImage = tile.tiledImage; //todo verify we are calling conversion just right amount of time! // e.g. no upload of cpu-existing texture + // also check textures are really getting destroyed (it is tested, but also do this with demos) - let gl = this._gl; + let gl = _this._gl; // create a gl Texture for this tile and bind the canvas with the image data let texture = gl.createTexture(); @@ -798,16 +799,16 @@ if( overlap > 0){ // calculate the normalized position of the rect to actually draw // discarding overlap. - let overlapFraction = this._calculateOverlapFraction(tile, tiledImage); + let overlapFraction = _this._calculateOverlapFraction(tile, tiledImage); let left = tile.x === 0 ? 0 : overlapFraction.x; let top = tile.y === 0 ? 0 : overlapFraction.y; let right = tile.isRightMost ? 1 : 1 - overlapFraction.x; let bottom = tile.isBottomMost ? 1 : 1 - overlapFraction.y; - position = this._makeQuadVertexBuffer(left, right, top, bottom); + position = _this._makeQuadVertexBuffer(left, right, top, bottom); } else { // no overlap: this texture can use the unit quad as its position data - position = this._unitQuad; + position = _this._unitQuad; } gl.activeTexture(gl.TEXTURE0); @@ -848,11 +849,12 @@ 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); + // We should be OK uploading any of these types. The complexity is selected to be O(3n), should be + // more than linear pass over pixels + $.convertor.learn("context2d", c2dTexType, tex2DCompatibleLoader, 1, 3); + $.convertor.learn("image", imageTexType, tex2DCompatibleLoader, 1, 3); + $.convertor.learn(c2dTexType, "context2d", dataRetrieval, 1, 3); + $.convertor.learn(imageTexType, "image", dataRetrieval, 1, 3); $.convertor.learnDestroy(c2dTexType, tex2DCompatibleDestructor); $.convertor.learnDestroy(imageTexType, tex2DCompatibleDestructor); diff --git a/src/world.js b/src/world.js index 264e276b..db0604e2 100644 --- a/src/world.js +++ b/src/world.js @@ -284,9 +284,9 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W draw: function() { this.viewer.drawer.draw(this._items); this._needsDraw = false; - this._items.forEach(function(item){ + for (let item of this._items) { this._needsDraw = item.setDrawn() || this._needsDraw; - }); + } }, /** diff --git a/test/modules/multi-image.js b/test/modules/multi-image.js index 5e1a8891..27dc92a4 100644 --- a/test/modules/multi-image.js +++ b/test/modules/multi-image.js @@ -220,17 +220,23 @@ var done = assert.async(); viewer.open('/test/data/testpattern.dzi'); - var density = OpenSeadragon.pixelDensityRatio; + function getPixelFromViewerScreenCoords(x, y) { + const density = OpenSeadragon.pixelDensityRatio; + const imageData = viewer.drawer.context.getImageData(x * density, y * density, 1, 1); + return { + r: imageData.data[0], + g: imageData.data[1], + b: imageData.data[2], + a: imageData.data[3] + }; + } viewer.addHandler('open', function() { var firstImage = viewer.world.getItemAt(0); firstImage.addHandler('fully-loaded-change', function() { viewer.addOnceHandler('update-viewport', function(){ - var imageData = viewer.drawer.context.getImageData(0, 0, - 500 * density, 500 * density); - // Pixel 250,250 will be in the hole of the A - var expectedVal = getPixelValue(imageData, 250 * density, 250 * density); + var expectedVal = getPixelFromViewerScreenCoords(250, 250); assert.notEqual(expectedVal.r, 0, 'Red channel should not be 0'); assert.notEqual(expectedVal.g, 0, 'Green channel should not be 0'); @@ -241,10 +247,9 @@ url: '/test/data/A.png', success: function() { var secondImage = viewer.world.getItemAt(1); - secondImage.addHandler('fully-loaded-change', function() { - viewer.addOnceHandler('update-viewport',function(){ - var imageData = viewer.drawer.context.getImageData(0, 0, 500 * density, 500 * density); - var actualVal = getPixelValue(imageData, 250 * density, 250 * density); + secondImage.addHandler('fully-loaded-change', function() { + viewer.addOnceHandler('update-viewport', function(){ + var actualVal = getPixelFromViewerScreenCoords(250, 250); assert.equal(actualVal.r, expectedVal.r, 'Red channel should not change in transparent part of the A'); @@ -255,10 +260,10 @@ assert.equal(actualVal.a, expectedVal.a, 'Alpha channel should not change in transparent part of the A'); - var onAVal = getPixelValue(imageData, 333 * density, 250 * density); - assert.equal(onAVal.r, 0, 'Red channel should be null on the A'); - assert.equal(onAVal.g, 0, 'Green channel should be null on the A'); - assert.equal(onAVal.b, 0, 'Blue channel should be null on the A'); + var onAVal = getPixelFromViewerScreenCoords(333 , 250); + assert.equal(onAVal.r, 0, 'Red channel should be 0 on the A'); + assert.equal(onAVal.g, 0, 'Green channel should be 0 on the A'); + assert.equal(onAVal.b, 0, 'Blue channel should be 0 on the A'); assert.equal(onAVal.a, 255, 'Alpha channel should be 255 on the A'); done(); @@ -271,17 +276,6 @@ }); }); }); - - function getPixelValue(imageData, x, y) { - var offset = 4 * (y * imageData.width + x); - return { - r: imageData.data[offset], - g: imageData.data[offset + 1], - b: imageData.data[offset + 2], - a: imageData.data[offset + 3] - }; - } }); } - })(); diff --git a/test/modules/tilecache.js b/test/modules/tilecache.js index 35586beb..3c9663fb 100644 --- a/test/modules/tilecache.js +++ b/test/modules/tilecache.js @@ -123,7 +123,13 @@ QUnit.test('basics', function(assert) { const done = assert.async(); const fakeViewer = { - raiseEvent: function() {} + raiseEvent: function() {}, + drawer: { + // tile in safe mode inspects the supported formats upon cache set + getSupportedDataFormats() { + return [T_A, T_B, T_C, T_D, T_E]; + } + } }; const fakeTiledImage0 = { viewer: fakeViewer, @@ -170,7 +176,13 @@ QUnit.test('maxImageCacheCount', function(assert) { const done = assert.async(); const fakeViewer = { - raiseEvent: function() {} + raiseEvent: function() {}, + drawer: { + // tile in safe mode inspects the supported formats upon cache set + getSupportedDataFormats() { + return [T_A, T_B, T_C, T_D, T_E]; + } + } }; const fakeTiledImage0 = { viewer: fakeViewer, @@ -218,7 +230,13 @@ QUnit.test('Tile API: basic conversion', function(test) { const done = test.async(); const fakeViewer = { - raiseEvent: function() {} + raiseEvent: function() {}, + drawer: { + // tile in safe mode inspects the supported formats upon cache set + getSupportedDataFormats() { + return [T_A, T_B, T_C, T_D, T_E]; + } + } }; const tileCache = new OpenSeadragon.TileCache(); const fakeTiledImage0 = { @@ -406,7 +424,13 @@ QUnit.test('Tile API Cache Interaction', function(test) { const done = test.async(); const fakeViewer = { - raiseEvent: function() {} + raiseEvent: function() {}, + drawer: { + // tile in safe mode inspects the supported formats upon cache set + getSupportedDataFormats() { + return [T_A, T_B, T_C, T_D, T_E]; + } + } }; const tileCache = new OpenSeadragon.TileCache(); const fakeTiledImage0 = {