Set fully loaded for reset() call on tiled image to false. Add old plugins demo to see how they behave. Remove basic2 demo as it was added by accident.

This commit is contained in:
Aiosa 2024-10-07 11:18:36 +02:00
parent 2033814227
commit 3d21ec897b
9 changed files with 621 additions and 35 deletions

View File

@ -303,6 +303,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this._tileCache.clearTilesFor(this);
this.lastResetTime = $.now();
this._needsDraw = true;
this._fullyLoaded = false;
},
/**

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon maxTilesPerFrame 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: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div>
Simple demo page to show an OpenSeadragon viewer with a higher maxTilesPerFrame.
</div>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "https://openseadragon.github.io/example-images/duomo/duomo.dzi",
showNavigator:true,
debugMode:true,
maxTilesPerFrame:3,
});
</script>
</body>
</html>

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<!--
This software was developed at the National Institute of Standards and
Technology by employees of the Federal Government in the course of
their official duties. Pursuant to title 17 Section 105 of the United
States Code this software is not subject to copyright protection and is
in the public domain. This software is an experimental system. NIST assumes
no responsibility whatsoever for its use by other parties, and makes no
guarantees, expressed or implied, about its quality, reliability, or
any other characteristic. We would appreciate acknowledgement if the
software is used.
-->
<html>
<head>
<meta charset="UTF-8">
<title>OpenSeadragon Filtering</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src='../../../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../../../lib/jquery-1.9.1.min.js'></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section class="home home-title">
<h1>OpenSeadragon filtering plugin demo.</h1>
</section>
<section class="home-about">
<p>
Demo of the OpenSeadragon filtering plugin.
Code and documentation are available on
<a href="https://github.com/usnistgov/OpenSeadragonFiltering">GitHub</a>.
</p>
<p>
Add/remove filters to visualize the effects.
</p>
</section>
<section class="demo">
<div class="wdzt-table-layout wdzt-full-width">
<div class="wdzt-row-layout">
<div class="wdzt-cell-layout column-2">
<div id="openseadragon"></div>
</div>
<div class="wdzt-cell-layout column-2">
<h3>Available filters</h3>
<ul id="available">
</ul>
<h3>Selected filters</h3>
<ul id="selected"></ul>
<p>Drag and drop the selected filters to set their order.</p>
</div>
</div>
</div>
</section>
<footer class="site-footer">
<div class="section-container">
<div class="section-content">
This plugin and demo are maintained by the
<a href="http://www.nist.gov/itl/ssd/is/">Information System Group</a>
within the
<a href="http://www.nist.gov/itl/ssd/">Software and Systems Division</a>.
</div>
</div>
</footer>
<script src="//cdnjs.cloudflare.com/ajax/libs/camanjs/4.1.2/caman.full.min.js"></script>
<script src="demo-bundle.js" charset="utf-8"></script>
</body>
</html>

View File

@ -0,0 +1,83 @@
/*
This software was developed at the National Institute of Standards and
Technology by employees of the Federal Government in the course of
their official duties. Pursuant to title 17 Section 105 of the United
States Code this software is not subject to copyright protection and is
in the public domain. This software is an experimental system. NIST assumes
no responsibility whatsoever for its use by other parties, and makes no
guarantees, expressed or implied, about its quality, reliability, or
any other characteristic. We would appreciate acknowledgement if the
software is used.
*/
.demo {
line-height: normal;
}
.demo h3 {
margin-top: 5px;
margin-bottom: 5px;
}
#openseadragon {
width: 100%;
height: 700px;
background-color: black;
}
.wdzt-table-layout {
display: table;
}
.wdzt-row-layout {
display: table-row;
}
.wdzt-cell-layout {
display: table-cell;
}
.wdzt-full-width {
width: 100%;
}
.wdzt-menu-slider {
margin-left: 10px;
margin-right: 10px;
}
.column-2 {
width: 50%;
vertical-align: top;
padding: 3px;
}
#available {
list-style-type: none;
}
ul {
padding: 0;
border: 1px solid black;
min-height: 25px;
}
li {
padding: 3px;
}
#selected {
list-style-type: none;
}
.button {
cursor: pointer;
vertical-align: text-top;
}
.filterLabel {
min-width: 120px;
}
#selected .filterLabel {
cursor: move;
}

View File

@ -0,0 +1,71 @@
precision mediump float;
uniform sampler2D u_tile;
uniform vec2 u_tile_size;
varying vec2 v_tile_pos;
// Sum a vector
float sum3(vec3 v) {
return dot(v,vec3(1));
}
// Weight of a matrix
float weigh3(mat3 m) {
return sum3(m[0])+sum3(m[1])+sum3(m[2]);
}
// Take the outer product
mat3 outer3(vec3 c, vec3 r) {
mat3 goal;
for (int i =0; i<3; i++) {
goal[i] = r*c[i];
}
return goal;
}
//*~*~*~*~*~*~*~*~*~*~*~*~*~
// Now for the Sobel Program
//*~
// Sample the color at offset
vec3 color(float dx, float dy) {
// calculate the color of sampler at an offset from position
return texture2D(u_tile, v_tile_pos+vec2(dx,dy)).rgb;
}
float sobel(mat3 kernel, vec3 near_in[9]) {
// nearest pixels
mat3 near_out[3];
// Get all near_in pixels
for (int i = 0; i < 3; i++) {
near_out[i][0] = kernel[0]*vec3(near_in[0][i],near_in[1][i],near_in[2][i]);
near_out[i][1] = kernel[1]*vec3(near_in[3][i],near_in[4][i],near_in[5][i]);
near_out[i][2] = kernel[2]*vec3(near_in[6][i],near_in[7][i],near_in[8][i]);
}
// convolve the kernel with the nearest pixels
return length(vec3(weigh3(near_out[0]),weigh3(near_out[1]),weigh3(near_out[2])));
}
void main() {
// Prep work
vec3 near_in[9];
vec3 mean = vec3(1,2,1);
vec3 slope = vec3(-1,0,1);
mat3 sobelX = outer3(mean,slope);
mat3 sobelY = outer3(slope,mean);
vec2 u = vec2(1./u_tile_size.x, 1./u_tile_size.y);
// Calculate coordinates of nearest points
for (int i = 0; i < 9; i++) {
near_in[i] = color(mod(float(i),3.)*u.x, float(i/3-1)*u.y);
}
// Show the mixed XY contrast
float edgeX = sobel(sobelX, near_in);
float edgeY = sobel(sobelY, near_in);
float mixed = length(vec2(edgeX,edgeY));
// mixed = (max(mixed,0.5)-0.5);
gl_FragColor = vec4(vec3(mixed),1);
}

View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="author" content="viaWebGL">
<title>GLSL shaders for zoomable DZI images: openSeadragonGL</title>
<meta name="description" content="GLSL shaders for zoomable DZI images: openSeadragonGL">
<style rel = "stylesheet">
#viaWebGL {
background-color:black;
position:absolute;
height:100%;
width:100%;
left:0px;
top:0px;
}
</style>
<script type="text/javascript" src='../../../../build/openseadragon/openseadragon.js'></script>
<script type='text/javascript' src='osd-gl.js'></script>
<script type='text/javascript' src='viawebgl.js'></script>
<script type='text/javascript'>
var DEMO = {};
/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~
// DEMO for a viewer with custom shaders
*/
window.onload = function(e){
DEMO.view = new SOBEL.Viewer().init();
};
</script>
<script type='text/javascript'>
var SOBEL = {};
/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~
/* SOBEL Viewer - Set a Sobel Shader for OpenSeaDragon
*/
SOBEL.Viewer = function() {
// Needed constants
this.tile_mode = 'tile-drawing';
this.iconPrefix = '../../../../build/openseadragon/images/';
this.source = "https://openseadragon.github.io/example-images/duomo/duomo.dzi";
this.vShader = 'vs.glsl';
this.fShader = 'fs.glsl';
}
SOBEL.Viewer.prototype.init = function() {
// Open a seadragon with two layers
this.openSD = OpenSeadragon({
tileSources: this.source,
prefixUrl: this.iconPrefix,
crossOriginPolicy: 'Anonymous',
id: 'viaWebGL',
drawer: 'canvas',
});
// Make a link to webGL
var seaGL = new openSeadragonGL(this.openSD);
seaGL.addHandler(this.tile_mode);
seaGL.vShader = this.vShader;
seaGL.fShader = this.fShader;
// Add a custom button
seaGL.button({
tooltip: 'Toggle shaders',
prefix: this.iconPrefix,
name: 'shade'
});
seaGL.init();
return this;
}
</script>
</head>
<body>
<div id='viaWebGL'></div>
</body>
</html>

View File

@ -0,0 +1,102 @@
/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~
/* openSeadragonGL - Set Shaders in OpenSeaDragon with viaWebGL
*/
openSeadragonGL = function(openSD) {
/* OpenSeaDragon API calls
~*~*~*~*~*~*~*~*~*~*~*~*/
this.interface = {
'tile-loaded': function(e) {
// Set the imageSource as a data URL and then complete
var output = this.viaGL.toCanvas(e.image);
e.image.onload = e.getCompletionCallback();
e.image.src = output.toDataURL();
},
'tile-drawing': function(e) {
// Render a webGL canvas to an input canvas
var input = e.rendered.canvas;
e.rendered.drawImage(this.viaGL.toCanvas(input), 0, 0, input.width, input.height);
}
};
this.defaults = {
'tile-loaded': function(callback, e) {
callback(e);
},
'tile-drawing': function(callback, e) {
if (e.tile.loaded !==1) {
e.tile.loaded = 1;
callback(e);
}
}
};
this.openSD = openSD;
this.viaGL = new ViaWebGL();
};
openSeadragonGL.prototype = {
// Map to viaWebGL and openSeadragon
init: function() {
var open = this.merger.bind(this);
this.openSD.addHandler('open',open);
return this;
},
// User adds events
addHandler: function(key,custom) {
if (key in this.defaults){
this[key] = this.defaults[key];
}
if (typeof custom == 'function') {
this[key] = custom;
}
},
// Merge with viaGL
merger: function(e) {
// Take GL height and width from OpenSeaDragon
this.width = this.openSD.source.getTileWidth();
this.height = this.openSD.source.getTileHeight();
// Add all viaWebGL properties
for (var key of this.and(this.viaGL)) {
this.viaGL[key] = this[key];
}
this.viaGL.init().then(this.adder.bind(this));
},
// Add all seadragon properties
adder: function(e) {
for (var key of this.and(this.defaults)) {
var handler = this[key].bind(this);
var interface = this.interface[key].bind(this);
// Add all openSeadragon event handlers
this.openSD.addHandler(key, function(e) {
handler.call(this, interface, e);
});
}
},
// Joint keys
and: function(obj) {
return Object.keys(obj).filter(Object.hasOwnProperty,this);
},
// Add your own button to OSD controls
button: function(terms) {
var name = terms.name || 'tool';
var prefix = terms.prefix || this.openSD.prefixUrl;
if (!terms.hasOwnProperty('onClick')){
terms.onClick = this.shade;
}
terms.onClick = terms.onClick.bind(this);
terms.srcRest = terms.srcRest || prefix+name+'_rest.png';
terms.srcHover = terms.srcHover || prefix+name+'_hover.png';
terms.srcDown = terms.srcDown || prefix+name+'_pressed.png';
terms.srcGroup = terms.srcGroup || prefix+name+'_grouphover.png';
// Replace the current controls with the same controls plus a new button
this.openSD.clearControls().buttons.buttons.push(new OpenSeadragon.Button(terms));
var toolbar = new OpenSeadragon.ButtonGroup({buttons: this.openSD.buttons.buttons});
this.openSD.addControl(toolbar.element,{anchor: OpenSeadragon.ControlAnchor.TOP_LEFT});
},
// Switch Shaders on or off
shade: function() {
this.viaGL.on++;
this.openSD.world.resetItems();
}
}

View File

@ -0,0 +1,201 @@
/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~
/* viaWebGL
/* Set shaders on Image or Canvas with WebGL
/* Built on 2016-9-9
/* http://via.hoff.in
*/
ViaWebGL = function(incoming) {
/* Custom WebGL API calls
~*~*~*~*~*~*~*~*~*~*~*~*/
this['gl-drawing'] = function(e) { return e; };
this['gl-loaded'] = function(e) { return e; };
this.ready = function(e) { return e; };
var gl = this.maker();
this.flat = document.createElement('canvas').getContext('2d');
this.tile_size = 'u_tile_size';
this.vShader = 'vShader.glsl';
this.fShader = 'fShader.glsl';
this.wrap = gl.CLAMP_TO_EDGE;
this.tile_pos = 'a_tile_pos';
this.filter = gl.NEAREST;
this.pos = 'a_pos';
this.height = 128;
this.width = 128;
this.on = 0;
this.gl = gl;
// Assign from incoming terms
for (var key in incoming) {
this[key] = incoming[key];
}
};
ViaWebGL.prototype = {
init: function(source) {
var ready = this.ready;
// Allow for mouse actions on click
if (this.hasOwnProperty('container') && this.hasOwnProperty('onclick')) {
this.container.onclick = this[this.onclick].bind(this);
}
if (source && source.height && source.width) {
this.ready = this.toCanvas.bind(this,source);
this.height = source.height;
this.width = source.width;
}
this.source = source;
this.gl.canvas.width = this.width;
this.gl.canvas.height = this.height;
this.gl.viewport(0, 0, this.width, this.height);
// Load the shaders when ready and return the promise
var step = [[this.vShader, this.fShader].map(this.getter)];
step.push(this.toProgram.bind(this), this.toBuffers.bind(this));
return Promise.all(step[0]).then(step[1]).then(step[2]).then(this.ready);
},
// Make a canvas
maker: function(options){
return this.context(document.createElement('canvas'));
},
context: function(a){
return a.getContext('experimental-webgl') || a.getContext('webgl');
},
// Get a file as a promise
getter: function(where) {
return new Promise(function(done){
// Return if not a valid filename
if (where.slice(-4) != 'glsl') {
return done(where);
}
var bid = new XMLHttpRequest();
var win = function(){
if (bid.status == 200) {
return done(bid.response);
}
return done(where);
};
bid.open('GET', where, true);
bid.onerror = bid.onload = win;
bid.send();
});
},
// Link shaders from strings
toProgram: function(files) {
var gl = this.gl;
var program = gl.createProgram();
var ok = function(kind,status,value,sh) {
if (!gl['get'+kind+'Parameter'](value, gl[status+'_STATUS'])){
console.log((sh||'LINK')+':\n'+gl['get'+kind+'InfoLog'](value));
}
return value;
}
// 1st is vertex; 2nd is fragment
files.map(function(given,i) {
var sh = ['VERTEX_SHADER', 'FRAGMENT_SHADER'][i];
var shader = gl.createShader(gl[sh]);
gl.shaderSource(shader, given);
gl.compileShader(shader);
gl.attachShader(program, shader);
ok('Shader','COMPILE',shader,sh);
});
gl.linkProgram(program);
return ok('Program','LINK',program);
},
// Load data to the buffers
toBuffers: function(program) {
// Allow for custom loading
this.gl.useProgram(program);
this['gl-loaded'].call(this, program);
// Unchangeable square array buffer fills viewport with texture
var boxes = [[-1, 1,-1,-1, 1, 1, 1,-1], [0, 1, 0, 0, 1, 1, 1, 0]];
var buffer = new Float32Array([].concat.apply([], boxes));
var bytes = buffer.BYTES_PER_ELEMENT;
var gl = this.gl;
var count = 4;
// Get uniform term
var tile_size = gl.getUniformLocation(program, this.tile_size);
gl.uniform2f(tile_size, gl.canvas.height, gl.canvas.width);
// Get attribute terms
this.att = [this.pos, this.tile_pos].map(function(name, number) {
var index = Math.min(number, boxes.length-1);
var vec = Math.floor(boxes[index].length/count);
var vertex = gl.getAttribLocation(program, name);
return [vertex, vec, gl.FLOAT, 0, vec*bytes, count*index*vec*bytes];
});
// Get texture
this.tex = {
texParameteri: [
[gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrap],
[gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrap],
[gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.filter],
[gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.filter]
],
texImage2D: [gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE],
bindTexture: [gl.TEXTURE_2D, gl.createTexture()],
drawArrays: [gl.TRIANGLE_STRIP, 0, count],
pixelStorei: [gl.UNPACK_FLIP_Y_WEBGL, 1]
};
// Build the position and texture buffer
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
},
// Turns image or canvas into a rendered canvas
toCanvas: function(tile) {
// Stop Rendering
if (this.on%2 !== 0) {
if(tile.nodeName == 'IMG') {
this.flat.canvas.width = tile.width;
this.flat.canvas.height = tile.height;
this.flat.drawImage(tile,0,0,tile.width,tile.height);
return this.flat.canvas;
}
return tile;
}
// Allow for custom drawing in webGL
this['gl-drawing'].call(this,tile);
var gl = this.gl;
// Set Attributes for GLSL
this.att.map(function(x){
gl.enableVertexAttribArray(x.slice(0,1));
gl.vertexAttribPointer.apply(gl, x);
});
// Set Texture for GLSL
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture.apply(gl, this.tex.bindTexture);
gl.pixelStorei.apply(gl, this.tex.pixelStorei);
// Apply texture parameters
this.tex.texParameteri.map(function(x){
gl.texParameteri.apply(gl, x);
});
// Send the tile into the texture.
var output = this.tex.texImage2D.concat([tile]);
gl.texImage2D.apply(gl, output);
// Draw everything needed to canvas
gl.drawArrays.apply(gl, this.tex.drawArrays);
// Apply to container if needed
if (this.container) {
this.container.appendChild(this.gl.canvas);
}
return this.gl.canvas;
},
toggle: function() {
this.on ++;
this.container.innerHTML = '';
this.container.appendChild(this.toCanvas(this.source));
}
}

View File

@ -0,0 +1,9 @@
attribute vec4 a_pos;
attribute vec2 a_tile_pos;
varying vec2 v_tile_pos;
void main() {
// Pass the overlay tiles
v_tile_pos = a_tile_pos;
gl_Position = a_pos;
}