/**
* @file filterUtils.js Interface to ask information to OSD
* @author Christophe Avenel
* @see {@link filterUtils}
*/
/**
* @namespace filterUtils
* @property {Bool} filterUtils._filtersUsed -
* @property {Object} filterUtils._filters -
* @property {Object} filterUtils._filterItems -
* @property {String} filterUtils._compositeMode -
*/
filterUtils = {
// Choose between ["Brightness", "Exposure", "Hue", "Contrast", "Vibrance", "Noise",
// "Saturation","Gamma","Invert","Greyscale","Threshold","Erosion","Dilation"]
_filtersUsed: ["Saturation","Brightness","Contrast"],
_filters: {
"Color":{
params:{
type:"color",
value:"100,100,100"
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
var valueRGB = value.split(",")
this.channels(
{red: valueRGB[0]-100, green: valueRGB[1]-100, blue: valueRGB[2]-100}
)
this.render(callback);
});
}
}
},
"Brightness":{
params:{
type:"range",
min:-50,
max:50,
step:0.1,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.brightness(value);
this.render(callback);
})
}
}
},
"Exposure":{
params:{
type:"range",
min:-100,
max:100,
step:1,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.exposure(value);
this.render(callback);
})
}
}
},
"Hue":{
params:{
type:"range",
min:0,
max:100,
step:1,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.hue(value);
this.render(callback);
})
}
}
},
"Contrast":{
params:{
type:"range",
min:0,
max:8,
step:0.01,
value:1
},
filterFunction: function (value) {
if (value == 1) { return function (context, callback) {callback();}}
return OpenSeadragon.Filters.CONTRAST(value);
}
},
"Vibrance":{
params:{
type:"range",
min:-250,
max:250,
step:10,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.vibrance(value);
this.render(callback);
})
}
}
},
"Noise":{
params:{
type:"range",
min:0,
max:100,
step:10,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.noise(value);
this.render(callback);
})
}
}
},
"Saturation":{
params:{
type:"range",
min:-100,
max:100,
step:10,
value:0
},
filterFunction: function (value) {
if (value == 0) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.saturation(value);
this.render(callback);
})
}
}
},
"Gamma":{
params:{
type:"range",
min:-0,
max:2,
step:0.1,
value:1
},
filterFunction: function (value) {
if (value == 1) { return function (context, callback) {callback();}}
return function(context, callback) {
Caman(context.canvas, function() {
this.gamma(value);
this.render(callback);
})
}
}
},
"Invert":{
params:{
type:"checkbox"
},
filterFunction: function () {
return OpenSeadragon.Filters.INVERT();
}
},
"Greyscale":{
params:{
type:"checkbox"
},
filterFunction: function () {
return OpenSeadragon.Filters.GREYSCALE();
}
},
"Threshold":{
params:{
type:"range",
min:0,
max:256,
step:1,
value:256
},
filterFunction: function (value) {
if (value == 256) { return function (context, callback) {callback();}}
return OpenSeadragon.Filters.THRESHOLDING(value);
}
},
"Erosion":{
params:{
type:"range",
min:1,
max:11,
step:2,
value:1
},
filterFunction: function (value) {
if (value == 1) { return function (context, callback) {callback();}}
return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(value, Math.min);
}
},
"Dilation":{
params:{
type:"range",
min:1,
max:11,
step:2,
value:1
},
filterFunction: function (value) {
if (value == 1) { return function (context, callback) {callback();}}
return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(value, Math.max);
}
}
},
_filterItems:{},
_compositeMode:"source-over"
}
/**
* Initialize list of filters
* */
filterUtils.initFilters = function() {
var settingsPanel = document.getElementById("filterSettings");
if (!settingsPanel) return;
for (var filter in filterUtils._filters) {
selectParams = {
eventListeners:{
"change": function (e) {
filterName = e.srcElement.getAttribute("filter");
checked = e.srcElement.checked;
if (checked)
filterUtils._filtersUsed.push(filterName);
else
filterUtils._filtersUsed = filterUtils._filtersUsed.filter(function(value, index, arr){
return value != filterName;
});
overlayUtils.addAllLayersSettings();
filterUtils.setRangesFromFilterItems();
filterUtils.getFilterItems();
}
},
"class":"filterSelection",
"checked":filterUtils._filtersUsed.filter(e => e === filter).length > 0,
id:"filterCheck_" + filter,
extraAttributes: {
"filter": filter
}
}
select = HTMLElementUtils.inputTypeCheckbox(selectParams);
select.classList.add("form-check-input");
settingsPanel.appendChild(select);
var label = document.createElement("label");
label.classList.add("form-check-label");
label.setAttribute("for", "filterCheck_" + filter);
label.innerHTML = " " + filter;
var form = document.createElement("div");
form.classList.add("form-check");
form.appendChild(select);
form.appendChild(label);
settingsPanel.appendChild(form);
}
modeParams = {
eventListeners:{
"change": function (e) {
compositeMode = e.srcElement.value;
filterUtils._compositeMode = compositeMode;
filterUtils.setCompositeOperation();
}
},
id: "filterCompositeMode",
options:[
{text:"source-over", value:"source-over"},
{text:"lighter", value:"lighter"},
{text:"darken", value:"darken"},
{text:"source-atop", value:"source-atop"},
{text:"source-in", value:"source-in"},
{text:"source-out", value:"source-out"},
{text:"destination-over", value:"destination-over"},
{text:"destination-atop", value:"destination-atop"},
{text:"destination-in", value:"destination-in"},
{text:"destination-out", value:"destination-out"},
{text:"copy", value:"copy"},
{text:"xor", value:"xor"},
{text:"multiply", value:"multiply"},
{text:"screen", value:"screen"},
{text:"overlay", value:"overlay"},
{text:"color-dodge", value:"color-dodge"},
{text:"color-burn", value:"color-burn"},
{text:"hard-light", value:"hard-light"},
{text:"soft-light", value:"soft-light"},
{text:"difference", value:"difference"},
{text:"exclusion", value:"exclusion"},
{text:"hue", value:"hue"},
{text:"saturation", value:"saturation"},
{text:"color", value:"color"},
{text:"luminosity", value:"luminosity"}
]
}
var label = document.createElement("label");
label.innerHTML = "Merging mode: ";
settingsPanel.appendChild(label);
select = HTMLElementUtils.selectTypeDropDown(modeParams);
select.classList.add("form-select", "form-select-sm");
select.value = filterUtils._compositeMode;
filterUtils.setCompositeOperation();
settingsPanel.appendChild(select);
filterUtils.getFilterItems();
}
/**
* Get params for a given filter
* @param {Number} filterName
**/
filterUtils.getFilterParams = function(filterName) {
filterParams = filterUtils._filters[filterName].params;
filterParams.eventListeners = {
"input": filterUtils.getFilterItems
};
filterParams["class"] = "filterInput";
filterParams.filter = filterName;
return filterParams;
}
/**
* Get function for a given filter
* @param {Number} filterName Index of an OSD tile source
**/
filterUtils.getFilterFunction = function(filterName) {
return filterUtils._filters[filterName].filterFunction;
}
/**
* apply all filters to all layers
* */
filterUtils.applyFilterItems = function(calledItems) {
var caman = Caman;
caman.Store.put = function() {};
var op = tmapp["object_prefix"];
overlayUtils.waitLayersReady().then(() => {
filters = [];
for (const layer in filterUtils._filterItems) {
processors = [];
for(var filterIndex=0;filterIndex<filterUtils._filterItems[layer].length;filterIndex++) {
processors.push(
filterUtils._filterItems[layer][filterIndex].filterFunction(filterUtils._filterItems[layer][filterIndex].value)
);
}
filters.push({
items: tmapp[op + "_viewer"].world.getItemAt(layer),
processors: processors
});
};
tmapp[op + "_viewer"].setFilterOptions({
filters: filters,
loadMode: "async"
});
for ( var i = 0; i < tmapp[op + "_viewer"].world._items.length; i++ ) {
tmapp[op + "_viewer"].world._items[i].tilesMatrix={};
tmapp[op + "_viewer"].world._items[i]._needsDraw = true;
}
})
}
/**
* Set html ranges and checkboxes from filter items
* */
filterUtils.setRangesFromFilterItems = function() {
var op = tmapp["object_prefix"];
for (const layer in filterUtils._filterItems) {
for(var filterIndex=0;filterIndex<filterUtils._filterItems[layer].length;filterIndex++) {
item = filterUtils._filterItems[layer][filterIndex];
filterRange = document.querySelector('[filter="'+item.name+'"][layer="'+layer+'"]');
if (filterRange) {
if (filterRange.type == "range" || filterRange.type == "select-one")
filterRange.value = item.value;
else if (filterRange.type == "checkbox")
filterRange.checked = item.value;
if (filterRange.type == "color") {
function componentToHex(c) {
var hex = Math.floor(c*255/100).toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(rgb) {
if (rgb == "0" || rgb == 0) {return "#FFFFFF";}
var array = rgb.split(',');
return "#" + componentToHex(array[0]) + componentToHex(array[1]) + componentToHex(array[2]);
}
console.log(item.value);
console.log("setRangesFromFilterItems", item.value, rgbToHex(item.value))
filterRange.value = rgbToHex(item.value);
}
}
}
};
}
/**
* Get filter functions and values from html ranges and checkboxes
* */
filterUtils.getFilterItems = function() {
var op = tmapp["object_prefix"];
overlayUtils.waitLayersReady().then(() => {
filterInputsRanges = document.getElementsByClassName("filterInput");
items = {};
for (i = 0; i < filterInputsRanges.length; i++) {
var filterLayer = filterInputsRanges[i].getAttribute("layer");
items[filterLayer] = []
}
for (i = 0; i < filterInputsRanges.length; i++) {
var filterName = filterInputsRanges[i].getAttribute("filter");
var filterLayer = filterInputsRanges[i].getAttribute("layer");
var filterFunction = filterUtils.getFilterFunction(filterName);
if (filterInputsRanges[i].type == "range" || filterInputsRanges[i].type == "select-one")
inputValue = filterInputsRanges[i].value;
else if (filterInputsRanges[i].type == "checkbox")
inputValue = filterInputsRanges[i].checked;
else if (filterInputsRanges[i].type == "color") {
function hex2RGB(hex) {
const color = hex
const r = Math.floor(100*parseInt(color.substr(1,2), 16)/255)
const g = Math.floor(100*parseInt(color.substr(3,2), 16)/255)
const b = Math.floor(100*parseInt(color.substr(5,2), 16)/255)
console.log(hex, r+","+g+","+b);
return r+","+g+","+b
}
inputValue = hex2RGB(filterInputsRanges[i].value);
}
if (inputValue) {
items[filterLayer].push(
{
filterFunction: filterFunction,
value: inputValue,
name: filterName
}
);
}
}
filterUtils._filterItems = items;
filterUtils.applyFilterItems(items);
})
}
filterUtils.setCompositeOperation = function() {
var op = tmapp["object_prefix"];
overlayUtils.waitLayersReady().then(() => {
var filterCompositeMode = document.getElementById("filterCompositeMode");
filterCompositeMode.value = filterUtils._compositeMode;
tmapp[op + "_viewer"].compositeOperation = filterUtils._compositeMode;
for (i = 0; i < tmapp[op + "_viewer"].world.getItemCount(); i++) {
tmapp[op + "_viewer"].world.getItemAt(i).setCompositeOperation(filterUtils._compositeMode);
}
})
}
/** Create an HTML filter */
filterUtils.createHTMLFilter = function (params) {
if (!params) {
return null;
}
var type = params.type || null;
if (type == "range") {
filterInput = HTMLElementUtils.inputTypeRange(params);
}
else if (type == "checkbox") {
filterInput = HTMLElementUtils.inputTypeCheckbox(params);
}
else if (type == "select") {
filterInput = HTMLElementUtils.selectTypeDropDown(params);
}
else if (type == "color") {
filterInput = HTMLElementUtils.inputTypeColor(params);
console.log(params);
if (params.value.includes(",")) {
function componentToHex(c) {
var hex = Math.floor(c*255/100).toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(rgb) {
var array = rgb.split(',');
return "#" + componentToHex(array[0]) + componentToHex(array[1]) + componentToHex(array[2]);
}
params.value = rgbToHex(params.value);
}
}
if (params.value != undefined) {
filterInput.setAttribute("value", params.value);
}
filterInput.setAttribute("layer", params.layer);
filterInput.setAttribute("filter", params.filter);
filterInput.setAttribute("id", "filterInput-" + params.filter + "-" + params.layer);
if (type != "color") {
filterInput.setAttribute("list", "filterDatalist-" + params.filter + "-" + params.layer);
datalist = document.createElement("datalist");
datalist.setAttribute("id", "filterDatalist-" + params.filter + "-" + params.layer);
option = document.createElement("option");
option.text = params.value;
datalist.appendChild(option);
filterInput.appendChild(datalist);
}else {
filterInput.setAttribute("list", "filterDatalist-" + params.filter + "-" + params.layer);
datalist = document.createElement("datalist");
datalist.setAttribute("id", "filterDatalist-" + params.filter + "-" + params.layer);
const colors = ['#FFFFFF', '#FF0000', '#00FF00','#0000FF','#FF00FF','#FFFF00','#00FFFF',"#FF007F","#7F00FF","#007FFF","#00FF7F","#7FFF00","#FF7F00"];
for (const element of colors) {
option = document.createElement("option");
option.text = element;
datalist.appendChild(option);
}
filterInput.appendChild(datalist);
}
return filterInput;
}