diff --git a/src/tile.js b/src/tile.js index 94f58cc2..56fe1824 100644 --- a/src/tile.js +++ b/src/tile.js @@ -193,6 +193,11 @@ $.Tile.prototype = { return this.level + "/" + this.x + "_" + this.y; }, + // private + _hasTransparencyChannel: function() { + return this.context2D || this.url.match('.png'); + }, + /** * Renders the tile in an html container. * @function @@ -295,8 +300,7 @@ $.Tile.prototype = { //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 && - (this.context2D || this.url.match('.png'))) { + if (context.globalAlpha === 1 && this._hasTransparencyChannel()) { //clearing only the inside of the rectangle occupied //by the png prevents edge flikering context.clearRect( @@ -305,7 +309,6 @@ $.Tile.prototype = { size.x - 2, size.y - 2 ); - } // This gives the application a chance to make image manipulation diff --git a/src/tiledimage.js b/src/tiledimage.js index 7ad9d7f2..9f3acf46 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -650,6 +650,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.raiseEvent('bounds-change'); + }, + + // private + _isBottomItem: function() { + return this.viewer.world.getItemAt(0) === this; } }); @@ -1324,22 +1329,22 @@ function compareTiles( previousBest, tile ) { } function drawTiles( tiledImage, lastDrawn ) { - var i, - tile = lastDrawn[0]; - - if ( tiledImage.opacity <= 0 ) { - drawDebugInfo( tiledImage, lastDrawn ); + if (lastDrawn.length === 0) { return; } + var tile = lastDrawn[0]; + var useSketch = tiledImage.opacity < 1 || - (tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over'); + (tiledImage.compositeOperation && + tiledImage.compositeOperation !== 'source-over') || + (!tiledImage._isBottomItem() && tile._hasTransparencyChannel()); var sketchScale; var sketchTranslate; var zoom = tiledImage.viewport.getZoom(true); var imageZoom = tiledImage.viewportToImageZoom(zoom); - if (imageZoom > tiledImage.smoothTileEdgesMinZoom && tile) { + if (imageZoom > tiledImage.smoothTileEdgesMinZoom) { // When zoomed in a lot (>100%) the tile edges are visible. // So we have to composite them at ~100% and scale them up together. useSketch = true; @@ -1396,7 +1401,7 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch); } - for ( i = lastDrawn.length - 1; i >= 0; i-- ) { + for (var i = lastDrawn.length - 1; i >= 0; i--) { tile = lastDrawn[ i ]; tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate ); tile.beingDrawn = true; diff --git a/test/modules/multi-image.js b/test/modules/multi-image.js index f3710429..1cfd0515 100644 --- a/test/modules/multi-image.js +++ b/test/modules/multi-image.js @@ -5,12 +5,12 @@ module( 'Multi-Image', { setup: function() { - $( '
' ).appendTo( "#qunit-fixture" ); + $( '
' ).appendTo( "#qunit-fixture" ); testLog.reset(); viewer = OpenSeadragon( { - id: 'itemsexample', + id: 'example', prefixUrl: '/build/openseadragon/images/', springStiffness: 100 // Faster animation = faster tests }); @@ -21,7 +21,7 @@ } viewer = null; - $( "#itemsexample" ).remove(); + $("#example").remove(); } } ); @@ -208,4 +208,57 @@ viewer.open('/test/data/testpattern.dzi'); }); + asyncTest('Transparent image on top of others', function() { + viewer.open('/test/data/testpattern.dzi'); + + // TODO: replace with fully-loaded event listener when available. + setTimeout(function() { + var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500); + // Pixel 250,250 will be in the hole of the A + var expectedVal = getPixelValue(imageData, 250, 250); + + notEqual(expectedVal.r, 0, 'Red channel should not be 0'); + notEqual(expectedVal.g, 0, 'Green channel should not be 0'); + notEqual(expectedVal.b, 0, 'Blue channel should not be 0'); + notEqual(expectedVal.a, 0, 'Alpha channel should not be 0'); + + viewer.addSimpleImage({ + url: '/test/data/A.png' + }); + + // TODO: replace with fully-loaded event listener when available. + setTimeout(function() { + var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500); + var actualVal = getPixelValue(imageData, 250, 250); + + equal(actualVal.r, expectedVal.r, + 'Red channel should not change in transparent part of the A'); + equal(actualVal.g, expectedVal.g, + 'Green channel should not change in transparent part of the A'); + equal(actualVal.b, expectedVal.b, + 'Blue channel should not change in transparent part of the A'); + equal(actualVal.a, expectedVal.a, + 'Alpha channel should not change in transparent part of the A'); + + var onAVal = getPixelValue(imageData, 333, 250); + equal(onAVal.r, 0, 'Red channel should be null on the A'); + equal(onAVal.g, 0, 'Green channel should be null on the A'); + equal(onAVal.b, 0, 'Blue channel should be null on the A'); + equal(onAVal.a, 255, 'Alpha channel should be 255 on the A'); + + start(); + }, 500); + }, 500); + + 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] + }; + } + }); + })();