mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-22 05:06:09 +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.setRotation(theirItem.getRotation(), immediately);
|
||||
myItem.setClip(theirItem.getClip());
|
||||
myItem.setFlip(theirItem.getFlip());
|
||||
},
|
||||
|
||||
// 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#
|
||||
*/
|
||||
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.
|
||||
* @member {Number} blendStart
|
||||
@ -296,6 +302,10 @@ $.Tile.prototype = {
|
||||
this.style.height = this.size.y + "px";
|
||||
this.style.width = this.size.x + "px";
|
||||
|
||||
if (this.flipped) {
|
||||
this.style.transform = "scaleX(-1)";
|
||||
}
|
||||
|
||||
$.setElementOpacity( this.element, this.opacity );
|
||||
},
|
||||
|
||||
@ -376,13 +386,17 @@ $.Tile.prototype = {
|
||||
sourceHeight = rendered.canvas.height;
|
||||
}
|
||||
|
||||
context.translate(position.x + size.x / 2, 0);
|
||||
if (this.flipped) {
|
||||
context.scale(-1, 1);
|
||||
}
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
0,
|
||||
0,
|
||||
sourceWidth,
|
||||
sourceHeight,
|
||||
position.x,
|
||||
-size.x / 2,
|
||||
position.y,
|
||||
size.x,
|
||||
size.y
|
||||
|
@ -392,6 +392,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
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.
|
||||
*/
|
||||
@ -832,6 +852,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
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.
|
||||
*/
|
||||
@ -1255,24 +1292,41 @@ function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
|
||||
|
||||
var viewportCenter = tiledImage.viewport.pixelFromPoint(
|
||||
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 y = topLeftTile.y; y <= bottomRightTile.y; y++) {
|
||||
|
||||
// Optimisation disabled with wrapping because getTileBounds does not
|
||||
// work correctly with x and y outside of the number of tiles
|
||||
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
|
||||
var tileBounds = tiledImage.source.getTileBounds(level, x, y);
|
||||
if (drawArea.intersection(tileBounds) === null) {
|
||||
// This tile is outside of the viewport, no need to draw it
|
||||
continue;
|
||||
}
|
||||
var flippedX;
|
||||
if (tiledImage.getFlip()) {
|
||||
var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
|
||||
flippedX = x + numberOfTiles.x - xMod - xMod - 1;
|
||||
} else {
|
||||
flippedX = x;
|
||||
}
|
||||
|
||||
if (drawArea.intersection(tiledImage.getTileBounds(level, flippedX, y)) === null) {
|
||||
// This tile is outside of the viewport, no need to draw it
|
||||
continue;
|
||||
}
|
||||
|
||||
best = updateTile(
|
||||
tiledImage,
|
||||
drawLevel,
|
||||
haveDrawn,
|
||||
x, y,
|
||||
flippedX, y,
|
||||
level,
|
||||
levelOpacity,
|
||||
levelVisibility,
|
||||
@ -1447,10 +1501,10 @@ function getTile(
|
||||
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;
|
||||
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 );
|
||||
exists = tileSource.tileExists( level, xMod, yMod );
|
||||
url = tileSource.getTileUrl( level, xMod, yMod );
|
||||
@ -1469,9 +1523,6 @@ function getTile(
|
||||
context2D = tileSource.getContext2D ?
|
||||
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
||||
|
||||
bounds.x += ( x - xMod ) / numTiles.x;
|
||||
bounds.y += (worldHeight / worldWidth) * (( y - yMod ) / numTiles.y);
|
||||
|
||||
tile = new $.Tile(
|
||||
level,
|
||||
x,
|
||||
@ -1485,14 +1536,22 @@ function getTile(
|
||||
sourceBounds
|
||||
);
|
||||
|
||||
if (xMod === numTiles.x - 1) {
|
||||
tile.isRightMost = true;
|
||||
if (tiledImage.getFlip()) {
|
||||
if (xMod === 0) {
|
||||
tile.isRightMost = true;
|
||||
}
|
||||
} else {
|
||||
if (xMod === numTiles.x - 1) {
|
||||
tile.isRightMost = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (yMod === numTiles.y - 1) {
|
||||
tile.isBottomMost = true;
|
||||
}
|
||||
|
||||
tile.flipped = tiledImage.flipped;
|
||||
|
||||
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 {Number} [options.degrees=0] Initial rotation of the tiled image around
|
||||
* 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.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||
* overriding viewer.crossOriginPolicy.
|
||||
@ -1463,6 +1464,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
opacity: queueItem.options.opacity,
|
||||
preload: queueItem.options.preload,
|
||||
degrees: queueItem.options.degrees,
|
||||
flipped: queueItem.options.flipped,
|
||||
compositeOperation: queueItem.options.compositeOperation,
|
||||
springStiffness: _this.springStiffness,
|
||||
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