Modular webgl2 drawer: fix small bugs. Add drawer IDs to demo page urls to allow refreshing/direct running

This commit is contained in:
Aiosa 2023-09-26 15:31:43 +02:00
parent e07745d790
commit e3024deb46
8 changed files with 156 additions and 64 deletions

View File

@ -114,6 +114,30 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase {
engine.init(size.x, size.y); engine.init(size.x, size.y);
this.viewer.addHandler("resize", this._resizeRenderer.bind(this)); this.viewer.addHandler("resize", this._resizeRenderer.bind(this));
this.renderer = engine; this.renderer = engine;
this.renderer.setDataBlendingEnabled(true);
// const gl = this.renderer.gl;
// this._renderToTexture = gl.createTexture();
// gl.activeTexture(gl.TEXTURE0);
// gl.bindTexture(gl.TEXTURE_2D, this._renderToTexture);
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.x, size.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
//
// // set up the framebuffer for render-to-texture
// this._glFrameBuffer = gl.createFramebuffer();
// gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
// gl.framebufferTexture2D(
// gl.FRAMEBUFFER,
// gl.COLOR_ATTACHMENT0, // attach texture as COLOR_ATTACHMENT0
// gl.TEXTURE_2D, // attach a 2D texture
// this._renderToTexture, // the texture to attach
// 0
// );
// gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
// gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._renderToTexture, 0);
return engine.canvas; return engine.canvas;
} }
@ -136,23 +160,19 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase {
let rotMatrix = $.Mat3.makeRotation(-viewport.rotation); let rotMatrix = $.Mat3.makeRotation(-viewport.rotation);
let viewMatrix = scaleMatrix.multiply(rotMatrix).multiply(posMatrix); let viewMatrix = scaleMatrix.multiply(rotMatrix).multiply(posMatrix);
this.renderer.clear(); // const gl = this.renderer.gl;
this.renderer.setDataBlendingEnabled(false); // gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
// clear the buffer to draw a new image
// gl.clear(gl.COLOR_BUFFER_BIT);
//iterate over tiled images and draw each one using a two-pass rendering pipeline if needed //iterate over tiled images and draw each one using a two-pass rendering pipeline if needed
let drawn = 0;
for (const tiledImage of tiledImages) { for (const tiledImage of tiledImages) {
console.log("START TILED IMAGE");
let tilesToDraw = tiledImage.getTilesToDraw(); let tilesToDraw = tiledImage.getTilesToDraw();
if (tilesToDraw.length === 0) { // if (tilesToDraw.length === 0) {
break; // break;
} // }
if (drawn === 1) {
this.renderer.setDataBlendingEnabled(true);
}
let overallMatrix = viewMatrix; let overallMatrix = viewMatrix;
let imageRotation = tiledImage.getRotation(true); let imageRotation = tiledImage.getRotation(true);
@ -168,12 +188,15 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase {
overallMatrix = viewMatrix.multiply(localMatrix); overallMatrix = viewMatrix.multiply(localMatrix);
} }
//todo better access to the rendering context //todo better access to the rendering context
const shader = this.renderer.specification(0).shaders.renderShader._renderContext; const shader = this.renderer.specification(0).shaders.renderShader._renderContext;
// iterate over tiles and add data for each one to the buffers // iterate over tiles and add data for each one to the buffers
for (let tileIndex = 0; tileIndex < tilesToDraw.length; tileIndex++){ for (let tileIndex = 0; tileIndex < tilesToDraw.length; tileIndex++){
const tile = tilesToDraw[tileIndex].tile; const tile = tilesToDraw[tileIndex].tile;
console.log("TILE " + tile.level + "-" + tile.x + "_" + tile.y);
const matrix = this._getTileMatrix(tile, tiledImage, overallMatrix); const matrix = this._getTileMatrix(tile, tiledImage, overallMatrix);
shader.opacity.set(tile.opacity * tiledImage.opacity); shader.opacity.set(tile.opacity * tiledImage.opacity);
@ -205,8 +228,6 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase {
tiles: tilesToDraw.map(info => info.tile), tiles: tilesToDraw.map(info => info.tile),
}); });
} }
drawn++;
} }
} }

View File

@ -144,7 +144,7 @@ $.WebGLModule = class extends $.EventSource {
const options = { const options = {
wrap: readGlProp("wrap", "MIRRORED_REPEAT"), wrap: readGlProp("wrap", "MIRRORED_REPEAT"),
magFilter: readGlProp("magFilter", "LINEAR"), magFilter: readGlProp("magFilter", "LINEAR"),
minFilter: readGlProp("minFilter", "NEAREST"), minFilter: readGlProp("minFilter", "LINEAR"),
dataLoader: contextOpts.dataLoader || "TEXTURE_2D" dataLoader: contextOpts.dataLoader || "TEXTURE_2D"
}; };
this.webglContext = new Context(this, glContext, options); this.webglContext = new Context(this, glContext, options);
@ -592,9 +592,11 @@ $.WebGLModule = class extends $.EventSource {
setDataBlendingEnabled(enabled) { setDataBlendingEnabled(enabled) {
if (enabled) { if (enabled) {
// this.gl.enable(this.gl.BLEND);
// this.gl.blendEquation(this.gl.FUNC_ADD);
// this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE);
this.gl.enable(this.gl.BLEND); this.gl.enable(this.gl.BLEND);
this.gl.blendEquation(this.gl.FUNC_ADD); this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE);
} else { } else {
this.gl.disable(this.gl.BLEND); this.gl.disable(this.gl.BLEND);
} }

View File

@ -185,7 +185,7 @@ $.WebGLModule.WebGL20 = class extends $.WebGLModule.WebGLImplementation {
} }
static create(canvas) { static create(canvas) {
return canvas.getContext('webgl2', { premultipliedAlpha: false, alpha: true }); return canvas.getContext('webgl2', { premultipliedAlpha: true, alpha: true });
} }
//todo try to implement on the global scope version-independntly //todo try to implement on the global scope version-independntly
@ -263,6 +263,7 @@ vec4 lid_${layer._index}_xo() {
return `#version 300 es return `#version 300 es
precision mediump float; precision mediump float;
precision mediump sampler2DArray; precision mediump sampler2DArray;
precision mediump sampler2D;
${this.texture.declare(shaderDataIndexToGlobalDataIndex)} ${this.texture.declare(shaderDataIndexToGlobalDataIndex)}
uniform float pixel_size_in_fragments; uniform float pixel_size_in_fragments;
@ -283,17 +284,7 @@ void show(vec4 color) {
vec4 fg = _last_rendered_color; vec4 fg = _last_rendered_color;
_last_rendered_color = color; _last_rendered_color = color;
vec4 pre_fg = vec4(fg.rgb * fg.a, fg.a); vec4 pre_fg = vec4(fg.rgb * fg.a, fg.a);
final_color = pre_fg + final_color * (1.0-fg.a); final_color = pre_fg + final_color;
}
void finalize() {
show(vec4(.0));
if (close(final_color.a, 0.0)) {
final_color = vec4(0.);
} else {
final_color = vec4(final_color.rgb/final_color.a, final_color.a);
}
} }
vec4 blend_equation(in vec4 foreground, in vec4 background) { vec4 blend_equation(in vec4 foreground, in vec4 background) {
@ -317,7 +308,8 @@ ${definition}
void main() { void main() {
${execution} ${execution}
finalize(); //blend last level
show(vec4(.0));
}`; }`;
} }

View File

@ -20,12 +20,23 @@
<div id="contentDiv" class="openseadragon1"></div> <div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript"> <script type="text/javascript">
const url = new URL(window.location.href);
let drawer = url.searchParams.get("drawer");
if (!drawer) {
drawer = 'canvas';
url.searchParams.set('drawer', drawer);
if ("undefined" !== typeof history.replaceState) {
history.replaceState(null, window.location.title, url.toString());
}
}
var viewer = OpenSeadragon({ var viewer = OpenSeadragon({
// debugMode: true, // debugMode: true,
id: "contentDiv", id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/", prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/testpattern.dzi", tileSources: "../data/testpattern.dzi",
showNavigator: true showNavigator: true,
drawer:drawer,
}); });
</script> </script>
</body> </body>

View File

@ -65,12 +65,12 @@
<h2>Compare behavior of <strong>Context2d</strong> and <strong>WebGL</strong> drawers</h2> <h2>Compare behavior of <strong>Context2d</strong> and <strong>WebGL</strong> drawers</h2>
<div class="mirrored"> <div class="mirrored">
<div> <div>
<h3>Context2d drawer (default in OSD &lt;= 4.1.0)</h3> <h3 id="title-w1">Loading...</h3>
<div id="canvasdrawer" class="viewer-container"></div> <div id="canvasdrawer" class="viewer-container"></div>
</div> </div>
<div> <div>
<h3>New WebGL drawer</h3> <h3 id="title-w2">Loading...</h3>
<div id="webgl" class="viewer-container"></div> <div id="webgl" class="viewer-container"></div>
</div> </div>
</div> </div>

View File

@ -13,6 +13,19 @@ const labels = {
bblue: 'Blue B', bblue: 'Blue B',
duomo: 'Duomo', duomo: 'Duomo',
} }
const drawers = {
canvas: "Context2d drawer (default in OSD &lt;= 4.1.0)",
webgl: "New WebGL drawer",
universal_webgl: "New WebGL (Modular)"
}
//Support drawer type from the url
const url = new URL(window.location.href);
const drawer1 = url.searchParams.get("left") || 'canvas';
const drawer2 = url.searchParams.get("right") || 'webgl';
$("#title-w1").html(drawers[drawer1]);
$("#title-w2").html(drawers[drawer2]);
//Double viewer setup for comparison - CanvasDrawer and WebGLDrawer //Double viewer setup for comparison - CanvasDrawer and WebGLDrawer
// viewer1: canvas drawer // viewer1: canvas drawer
@ -25,7 +38,7 @@ let viewer1 = window.viewer1 = OpenSeadragon({
crossOriginPolicy: 'Anonymous', crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false, ajaxWithCredentials: false,
// maxImageCacheCount: 30, // maxImageCacheCount: 30,
drawer:'canvas', drawer:drawer1,
blendTime:0 blendTime:0
}); });
@ -39,25 +52,23 @@ let viewer2 = window.viewer2 = OpenSeadragon({
crossOriginPolicy: 'Anonymous', crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false, ajaxWithCredentials: false,
// maxImageCacheCount: 30, // maxImageCacheCount: 30,
drawer:'webgl', drawer:drawer2,
blendTime:0, blendTime:0,
}); });
// viewer3: html drawer // // viewer3: html drawer, unused
var viewer3 = window.viewer3 = OpenSeadragon({ // var viewer3 = window.viewer3 = OpenSeadragon({
id: "htmldrawer", // id: "htmldrawer",
drawer:'html', // drawer:'html',
blendTime:2, // blendTime:2,
prefixUrl: "../../build/openseadragon/images/", // prefixUrl: "../../build/openseadragon/images/",
minZoomImageRatio:0.01, // minZoomImageRatio:0.01,
customDrawer: OpenSeadragon.HTMLDrawer, // customDrawer: OpenSeadragon.HTMLDrawer,
tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']], // tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']],
sequenceMode: true, // sequenceMode: true,
crossOriginPolicy: 'Anonymous', // crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false // ajaxWithCredentials: false
}); // });
// Sync navigation of viewer1 and viewer 2 // Sync navigation of viewer1 and viewer 2
@ -126,6 +137,8 @@ Object.keys(sources).forEach((key, index)=>{
} }
}) })
$('#image-picker').append(makeComparisonSwitcher());
$('#image-picker input.toggle').on('change',function(){ $('#image-picker input.toggle').on('change',function(){
let data = $(this).data(); let data = $(this).data();
if(this.checked){ if(this.checked){
@ -288,6 +301,30 @@ function addTileSource(viewer, image, checkbox){
} }
} }
function getAvailableDrawerSelect(name, selectedDrawer) {
return `
<select name="${name}">
${Object.entries(drawers).map(([k, v]) => {
const selected = selectedDrawer === k ? "selected" : "";
return `<option value="${k}" ${selected}>${v}</option>`;
}).join("\n")}
</select>`;
}
function makeComparisonSwitcher() {
const left = getAvailableDrawerSelect("left", drawer1),
right = getAvailableDrawerSelect("right", drawer2);
return `
<div>
Note: you can run the comparison with desired drawers like this: drawercomparison.html?left=[type]&right=[type]
<form method="get">
${left}
${right}
<button>Submit</button>
</form>
</div>`;
}
function makeImagePickerElement(key, label){ function makeImagePickerElement(key, label){
return $(`<div class="image-options"> return $(`<div class="image-options">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span> <span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
@ -306,7 +343,6 @@ function makeImagePickerElement(key, label){
<label>Composite: <select data-image="" data-field="composite"></select></label> <label>Composite: <select data-image="" data-field="composite"></select></label>
<label>Wrap: <select data-image="" data-field="wrapping"></select></label> <label>Wrap: <select data-image="" data-field="wrapping"></select></label>
</div> </div>
</div>`.replaceAll('data-image=""', `data-image="${key}"`).replace('__title__', label)); </div>`.replaceAll('data-image=""', `data-image="${key}"`).replace('__title__', label));
} }

View File

@ -27,9 +27,7 @@
<div> <div>
<label>Select a drawer: </label> <label>Select a drawer: </label>
<select id="select-drawer"> <select id="select-drawer">
<option value="canvas">Canvas</option>
<option value="webgl">WebGL</option>
<option value="html">HTML</option>
</select> </select>
<label>Num images: </label> <label>Num images: </label>
<input id="input-number" type="number" value="1" min="1" step="1"> <input id="input-number" type="number" value="1" min="1" step="1">

View File

@ -13,6 +13,14 @@ const labels = {
bblue: 'Blue B', bblue: 'Blue B',
duomo: 'Duomo', duomo: 'Duomo',
} }
const drawers = {
canvas: "Context2d drawer (default in OSD &lt;= 4.1.0)",
webgl: "New WebGL drawer",
universal_webgl: "New WebGL (Modular)",
html: ""
}
let viewer; let viewer;
( function () { ( function () {
@ -408,6 +416,10 @@ function rStats ( settings ) {
_base = document.createElement( 'div' ); _base = document.createElement( 'div' );
_base.className = 'rs-base'; _base.className = 'rs-base';
_base.style.bottom = '0px';
_base.style.right = '0px';
_base.style.top = 'initial';
_base.style.left = 'initial';
_div = document.createElement( 'div' ); _div = document.createElement( 'div' );
_div.className = 'rs-container'; _div.className = 'rs-container';
_div.style.height = 'auto'; _div.style.height = 'auto';
@ -509,6 +521,8 @@ function rStats ( settings ) {
} }
var glStats = function() { var glStats = function() {
var _rS = null; var _rS = null;
@ -898,16 +912,9 @@ Stats.Panel = function ( name, fg, bg ) {
// })(); // })();
$('#create-drawer').on('click',function(){
let drawerType = $('#select-drawer').val();
let num = Math.floor($('#input-number').val());
rS('other').start();
run(drawerType, num);
});
function run(drawerType, num) { function run(drawerType, num) {
rS('other').start();
if(viewer){ if(viewer){
viewer.destroy(); viewer.destroy();
} }
@ -969,3 +976,28 @@ function makeTileSources(num){
} }
const url = new URL(window.location.href);
const drawer = url.searchParams.get("drawer");
const numberOfSources = Number.parseInt(url.searchParams.get("sources")) || 1;
$('#create-drawer').on('click',function(){
const drawer = $('#select-drawer').val();
let num = Math.floor($('#input-number').val());
url.searchParams.set("drawer", drawer);
url.searchParams.set("sources", num);
if ("undefined" !== typeof history.replaceState) {
history.replaceState(null, window.location.title, url.toString());
}
run(drawer, num);
});
$('#input-number').val(numberOfSources);
$("#select-drawer").html(Object.entries(drawers).map(([k, v]) => {
const selected = drawer === k ? "selected" : "";
return `<option value="${k}" ${selected}>${v}</option>`;
}).join("\n"));
if (drawer) {
run(drawer, numberOfSources);
}