mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 14:46:10 +03:00
Merge pull request #1903 from ali1234/flipmode
Implement per-image flipping
This commit is contained in:
commit
e7d4f87ca9
@ -452,6 +452,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||||||
myItem.setWidth(bounds.width, immediately);
|
myItem.setWidth(bounds.width, immediately);
|
||||||
myItem.setRotation(theirItem.getRotation(), immediately);
|
myItem.setRotation(theirItem.getRotation(), immediately);
|
||||||
myItem.setClip(theirItem.getClip());
|
myItem.setClip(theirItem.getClip());
|
||||||
|
myItem.setFlip(theirItem.getFlip());
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
|
16
src/tile.js
16
src/tile.js
@ -176,6 +176,12 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
|||||||
* @memberof OpenSeadragon.Tile#
|
* @memberof OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.size = null;
|
this.size = null;
|
||||||
|
/**
|
||||||
|
* Whether to flip the tile when rendering.
|
||||||
|
* @member {Boolean} flipped
|
||||||
|
* @memberof OpenSeadragon.Tile#
|
||||||
|
*/
|
||||||
|
this.flipped = false;
|
||||||
/**
|
/**
|
||||||
* The start time of this tile's blending.
|
* The start time of this tile's blending.
|
||||||
* @member {Number} blendStart
|
* @member {Number} blendStart
|
||||||
@ -296,6 +302,10 @@ $.Tile.prototype = {
|
|||||||
this.style.height = this.size.y + "px";
|
this.style.height = this.size.y + "px";
|
||||||
this.style.width = this.size.x + "px";
|
this.style.width = this.size.x + "px";
|
||||||
|
|
||||||
|
if (this.flipped) {
|
||||||
|
this.style.transform = "scaleX(-1)";
|
||||||
|
}
|
||||||
|
|
||||||
$.setElementOpacity( this.element, this.opacity );
|
$.setElementOpacity( this.element, this.opacity );
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -376,13 +386,17 @@ $.Tile.prototype = {
|
|||||||
sourceHeight = rendered.canvas.height;
|
sourceHeight = rendered.canvas.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.translate(position.x + size.x / 2, 0);
|
||||||
|
if (this.flipped) {
|
||||||
|
context.scale(-1, 1);
|
||||||
|
}
|
||||||
context.drawImage(
|
context.drawImage(
|
||||||
rendered.canvas,
|
rendered.canvas,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
sourceWidth,
|
sourceWidth,
|
||||||
sourceHeight,
|
sourceHeight,
|
||||||
position.x,
|
-size.x / 2,
|
||||||
position.y,
|
position.y,
|
||||||
size.x,
|
size.x,
|
||||||
size.y
|
size.y
|
||||||
|
@ -392,6 +392,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
return bounds.rotate(this.getRotation(current), this._getRotationPoint(current));
|
return bounds.rotate(this.getRotation(current), this._getRotationPoint(current));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* @param {Number} level
|
||||||
|
* @param {Number} x
|
||||||
|
* @param {Number} y
|
||||||
|
* @returns {OpenSeadragon.Rect} Where this tile fits (in normalized coordinates).
|
||||||
|
*/
|
||||||
|
getTileBounds: function( level, x, y ) {
|
||||||
|
var numTiles = this.source.getNumTiles(level);
|
||||||
|
var xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
|
||||||
|
var yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
||||||
|
var bounds = this.source.getTileBounds(level, xMod, yMod);
|
||||||
|
if (this.getFlip()) {
|
||||||
|
bounds.x = 1 - bounds.x - bounds.width;
|
||||||
|
}
|
||||||
|
bounds.x += (x - xMod) / numTiles.x;
|
||||||
|
bounds.y += (this._worldHeightCurrent / this._worldWidthCurrent) * ((y - yMod) / numTiles.y);
|
||||||
|
return bounds;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
||||||
*/
|
*/
|
||||||
@ -832,6 +852,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
this.raiseEvent('clip-change');
|
this.raiseEvent('clip-change');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Boolean} Whether the TiledImage should be flipped before rendering.
|
||||||
|
*/
|
||||||
|
getFlip: function() {
|
||||||
|
return !!this.flipped;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Boolean} flip Whether the TiledImage should be flipped before rendering.
|
||||||
|
* @fires OpenSeadragon.TiledImage.event:bounds-change
|
||||||
|
*/
|
||||||
|
setFlip: function(flip) {
|
||||||
|
this.flipped = !!flip;
|
||||||
|
this._needsDraw = true;
|
||||||
|
this._raiseBoundsChange();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Number} The TiledImage's current opacity.
|
* @returns {Number} The TiledImage's current opacity.
|
||||||
*/
|
*/
|
||||||
@ -1255,24 +1292,41 @@ function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
|
|||||||
|
|
||||||
var viewportCenter = tiledImage.viewport.pixelFromPoint(
|
var viewportCenter = tiledImage.viewport.pixelFromPoint(
|
||||||
tiledImage.viewport.getCenter());
|
tiledImage.viewport.getCenter());
|
||||||
|
|
||||||
|
if (tiledImage.getFlip()) {
|
||||||
|
// The right-most tile can be narrower than the others. When flipped,
|
||||||
|
// this tile is now on the left. Because it is narrower than the normal
|
||||||
|
// left-most tile, the subsequent tiles may not be wide enough to completely
|
||||||
|
// fill the viewport. Fix this by rendering an extra column of tiles. If we
|
||||||
|
// are not wrapping, make sure we never render more than the number of tiles
|
||||||
|
// in the image.
|
||||||
|
bottomRightTile.x += 1;
|
||||||
|
if (!tiledImage.wrapHorizontal) {
|
||||||
|
bottomRightTile.x = Math.min(bottomRightTile.x, numberOfTiles.x - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
|
for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
|
||||||
for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
|
for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
|
||||||
|
|
||||||
// Optimisation disabled with wrapping because getTileBounds does not
|
var flippedX;
|
||||||
// work correctly with x and y outside of the number of tiles
|
if (tiledImage.getFlip()) {
|
||||||
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
|
var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
|
||||||
var tileBounds = tiledImage.source.getTileBounds(level, x, y);
|
flippedX = x + numberOfTiles.x - xMod - xMod - 1;
|
||||||
if (drawArea.intersection(tileBounds) === null) {
|
} else {
|
||||||
|
flippedX = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawArea.intersection(tiledImage.getTileBounds(level, flippedX, y)) === null) {
|
||||||
// This tile is outside of the viewport, no need to draw it
|
// This tile is outside of the viewport, no need to draw it
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
best = updateTile(
|
best = updateTile(
|
||||||
tiledImage,
|
tiledImage,
|
||||||
drawLevel,
|
drawLevel,
|
||||||
haveDrawn,
|
haveDrawn,
|
||||||
x, y,
|
flippedX, y,
|
||||||
level,
|
level,
|
||||||
levelOpacity,
|
levelOpacity,
|
||||||
levelVisibility,
|
levelVisibility,
|
||||||
@ -1447,10 +1501,10 @@ function getTile(
|
|||||||
tilesMatrix[ level ][ x ] = {};
|
tilesMatrix[ level ][ x ] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !tilesMatrix[ level ][ x ][ y ] ) {
|
if ( !tilesMatrix[ level ][ x ][ y ] || !tilesMatrix[ level ][ x ][ y ].flipped !== !tiledImage.flipped ) {
|
||||||
xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
|
xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
|
||||||
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
|
||||||
bounds = tileSource.getTileBounds( level, xMod, yMod );
|
bounds = tiledImage.getTileBounds( level, x, y );
|
||||||
sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
|
sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
|
||||||
exists = tileSource.tileExists( level, xMod, yMod );
|
exists = tileSource.tileExists( level, xMod, yMod );
|
||||||
url = tileSource.getTileUrl( level, xMod, yMod );
|
url = tileSource.getTileUrl( level, xMod, yMod );
|
||||||
@ -1469,9 +1523,6 @@ function getTile(
|
|||||||
context2D = tileSource.getContext2D ?
|
context2D = tileSource.getContext2D ?
|
||||||
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
||||||
|
|
||||||
bounds.x += ( x - xMod ) / numTiles.x;
|
|
||||||
bounds.y += (worldHeight / worldWidth) * (( y - yMod ) / numTiles.y);
|
|
||||||
|
|
||||||
tile = new $.Tile(
|
tile = new $.Tile(
|
||||||
level,
|
level,
|
||||||
x,
|
x,
|
||||||
@ -1485,14 +1536,22 @@ function getTile(
|
|||||||
sourceBounds
|
sourceBounds
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tiledImage.getFlip()) {
|
||||||
|
if (xMod === 0) {
|
||||||
|
tile.isRightMost = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (xMod === numTiles.x - 1) {
|
if (xMod === numTiles.x - 1) {
|
||||||
tile.isRightMost = true;
|
tile.isRightMost = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (yMod === numTiles.y - 1) {
|
if (yMod === numTiles.y - 1) {
|
||||||
tile.isBottomMost = true;
|
tile.isBottomMost = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tile.flipped = tiledImage.flipped;
|
||||||
|
|
||||||
tilesMatrix[ level ][ x ][ y ] = tile;
|
tilesMatrix[ level ][ x ][ y ] = tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,6 +1301,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* @param {Boolean} [options.preload=false] Default switch for loading hidden images (true loads, false blocks)
|
* @param {Boolean} [options.preload=false] Default switch for loading hidden images (true loads, false blocks)
|
||||||
* @param {Number} [options.degrees=0] Initial rotation of the tiled image around
|
* @param {Number} [options.degrees=0] Initial rotation of the tiled image around
|
||||||
* its top left corner in degrees.
|
* its top left corner in degrees.
|
||||||
|
* @param {Boolean} [options.flipped=false] Whether to horizontally flip the image.
|
||||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||||
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||||
* overriding viewer.crossOriginPolicy.
|
* overriding viewer.crossOriginPolicy.
|
||||||
@ -1463,6 +1464,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
opacity: queueItem.options.opacity,
|
opacity: queueItem.options.opacity,
|
||||||
preload: queueItem.options.preload,
|
preload: queueItem.options.preload,
|
||||||
degrees: queueItem.options.degrees,
|
degrees: queueItem.options.degrees,
|
||||||
|
flipped: queueItem.options.flipped,
|
||||||
compositeOperation: queueItem.options.compositeOperation,
|
compositeOperation: queueItem.options.compositeOperation,
|
||||||
springStiffness: _this.springStiffness,
|
springStiffness: _this.springStiffness,
|
||||||
animationTime: _this.animationTime,
|
animationTime: _this.animationTime,
|
||||||
|
113
test/demo/flipping.html
Normal file
113
test/demo/flipping.html
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenSeadragon Flipping Demo</title>
|
||||||
|
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
|
||||||
|
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.openseadragon1 {
|
||||||
|
width: 800px;
|
||||||
|
height: 600px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
Simple demo page to show image flipping.
|
||||||
|
</div>
|
||||||
|
<div id="contentDiv" class="openseadragon1">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="options">
|
||||||
|
First
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="ffirst" onchange="flip(0, this.checked)">
|
||||||
|
<label for="ffirst">Flip</label>
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="rfirst" onchange="rotate(0, this.checked * 45)">
|
||||||
|
<label for="rfirst">Rotate</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options">
|
||||||
|
Second
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="fsecond" onchange="flip(1, this.checked)" checked>
|
||||||
|
<label for="fsecond">Flip</label>
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="rsecond" onchange="rotate(1, this.checked * 45)">
|
||||||
|
<label for="rsecond">Rotate</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options">
|
||||||
|
Viewport
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="fview" onchange="flipViewport(this.checked)">
|
||||||
|
<label for="fview">Flip Viewport</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button">
|
||||||
|
<input type="checkbox" id="debug" onchange="debug(this.checked)">
|
||||||
|
<label for="debug">Debug Mode</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var viewer = OpenSeadragon({
|
||||||
|
// debugMode: true,
|
||||||
|
id: "contentDiv",
|
||||||
|
prefixUrl: "../../build/openseadragon/images/",
|
||||||
|
showNavigator:true,
|
||||||
|
tileSources: [
|
||||||
|
{
|
||||||
|
tileSource: "../data/testpattern.dzi",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
flipped: document.getElementById("ffirst").checked,
|
||||||
|
degrees: document.getElementById("rfirst").checked * 45,
|
||||||
|
}, {
|
||||||
|
tileSource: "../data/testpattern.dzi",
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
flipped: document.getElementById("fsecond").checked,
|
||||||
|
degrees: document.getElementById("rsecond").checked * 45,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.viewport.setFlip(document.getElementById("fview").checked);
|
||||||
|
|
||||||
|
function debug(v) {
|
||||||
|
viewer.setDebugMode(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip(n, v) {
|
||||||
|
viewer.world.getItemAt(n).setFlip(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rotate(n, v) {
|
||||||
|
viewer.world.getItemAt(n).setRotation(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flipViewport(v) {
|
||||||
|
viewer.viewport.setFlip(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user