StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
StackGenVis/frontend/node_modules/gl-texture2d/texture.js

562 lines
16 KiB

4 years ago
'use strict'
var ndarray = require('ndarray')
var ops = require('ndarray-ops')
var pool = require('typedarray-pool')
module.exports = createTexture2D
var linearTypes = null
var filterTypes = null
var wrapTypes = null
function lazyInitLinearTypes(gl) {
linearTypes = [
gl.LINEAR,
gl.NEAREST_MIPMAP_LINEAR,
gl.LINEAR_MIPMAP_NEAREST,
gl.LINEAR_MIPMAP_NEAREST
]
filterTypes = [
gl.NEAREST,
gl.LINEAR,
gl.NEAREST_MIPMAP_NEAREST,
gl.NEAREST_MIPMAP_LINEAR,
gl.LINEAR_MIPMAP_NEAREST,
gl.LINEAR_MIPMAP_LINEAR
]
wrapTypes = [
gl.REPEAT,
gl.CLAMP_TO_EDGE,
gl.MIRRORED_REPEAT
]
}
function acceptTextureDOM (obj) {
return (
('undefined' != typeof HTMLCanvasElement && obj instanceof HTMLCanvasElement) ||
('undefined' != typeof HTMLImageElement && obj instanceof HTMLImageElement) ||
('undefined' != typeof HTMLVideoElement && obj instanceof HTMLVideoElement) ||
('undefined' != typeof ImageData && obj instanceof ImageData))
}
var convertFloatToUint8 = function(out, inp) {
ops.muls(out, inp, 255.0)
}
function reshapeTexture(tex, w, h) {
var gl = tex.gl
var maxSize = gl.getParameter(gl.MAX_TEXTURE_SIZE)
if(w < 0 || w > maxSize || h < 0 || h > maxSize) {
throw new Error('gl-texture2d: Invalid texture size')
}
tex._shape = [w, h]
tex.bind()
gl.texImage2D(gl.TEXTURE_2D, 0, tex.format, w, h, 0, tex.format, tex.type, null)
tex._mipLevels = [0]
return tex
}
function Texture2D(gl, handle, width, height, format, type) {
this.gl = gl
this.handle = handle
this.format = format
this.type = type
this._shape = [width, height]
this._mipLevels = [0]
this._magFilter = gl.NEAREST
this._minFilter = gl.NEAREST
this._wrapS = gl.CLAMP_TO_EDGE
this._wrapT = gl.CLAMP_TO_EDGE
this._anisoSamples = 1
var parent = this
var wrapVector = [this._wrapS, this._wrapT]
Object.defineProperties(wrapVector, [
{
get: function() {
return parent._wrapS
},
set: function(v) {
return parent.wrapS = v
}
},
{
get: function() {
return parent._wrapT
},
set: function(v) {
return parent.wrapT = v
}
}
])
this._wrapVector = wrapVector
var shapeVector = [this._shape[0], this._shape[1]]
Object.defineProperties(shapeVector, [
{
get: function() {
return parent._shape[0]
},
set: function(v) {
return parent.width = v
}
},
{
get: function() {
return parent._shape[1]
},
set: function(v) {
return parent.height = v
}
}
])
this._shapeVector = shapeVector
}
var proto = Texture2D.prototype
Object.defineProperties(proto, {
minFilter: {
get: function() {
return this._minFilter
},
set: function(v) {
this.bind()
var gl = this.gl
if(this.type === gl.FLOAT && linearTypes.indexOf(v) >= 0) {
if(!gl.getExtension('OES_texture_float_linear')) {
v = gl.NEAREST
}
}
if(filterTypes.indexOf(v) < 0) {
throw new Error('gl-texture2d: Unknown filter mode ' + v)
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, v)
return this._minFilter = v
}
},
magFilter: {
get: function() {
return this._magFilter
},
set: function(v) {
this.bind()
var gl = this.gl
if(this.type === gl.FLOAT && linearTypes.indexOf(v) >= 0) {
if(!gl.getExtension('OES_texture_float_linear')) {
v = gl.NEAREST
}
}
if(filterTypes.indexOf(v) < 0) {
throw new Error('gl-texture2d: Unknown filter mode ' + v)
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, v)
return this._magFilter = v
}
},
mipSamples: {
get: function() {
return this._anisoSamples
},
set: function(i) {
var psamples = this._anisoSamples
this._anisoSamples = Math.max(i, 1)|0
if(psamples !== this._anisoSamples) {
var ext = this.gl.getExtension('EXT_texture_filter_anisotropic')
if(ext) {
this.gl.texParameterf(this.gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, this._anisoSamples)
}
}
return this._anisoSamples
}
},
wrapS: {
get: function() {
return this._wrapS
},
set: function(v) {
this.bind()
if(wrapTypes.indexOf(v) < 0) {
throw new Error('gl-texture2d: Unknown wrap mode ' + v)
}
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, v)
return this._wrapS = v
}
},
wrapT: {
get: function() {
return this._wrapT
},
set: function(v) {
this.bind()
if(wrapTypes.indexOf(v) < 0) {
throw new Error('gl-texture2d: Unknown wrap mode ' + v)
}
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, v)
return this._wrapT = v
}
},
wrap: {
get: function() {
return this._wrapVector
},
set: function(v) {
if(!Array.isArray(v)) {
v = [v,v]
}
if(v.length !== 2) {
throw new Error('gl-texture2d: Must specify wrap mode for rows and columns')
}
for(var i=0; i<2; ++i) {
if(wrapTypes.indexOf(v[i]) < 0) {
throw new Error('gl-texture2d: Unknown wrap mode ' + v)
}
}
this._wrapS = v[0]
this._wrapT = v[1]
var gl = this.gl
this.bind()
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this._wrapS)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this._wrapT)
return v
}
},
shape: {
get: function() {
return this._shapeVector
},
set: function(x) {
if(!Array.isArray(x)) {
x = [x|0,x|0]
} else {
if(x.length !== 2) {
throw new Error('gl-texture2d: Invalid texture shape')
}
}
reshapeTexture(this, x[0]|0, x[1]|0)
return [x[0]|0, x[1]|0]
}
},
width: {
get: function() {
return this._shape[0]
},
set: function(w) {
w = w|0
reshapeTexture(this, w, this._shape[1])
return w
}
},
height: {
get: function() {
return this._shape[1]
},
set: function(h) {
h = h|0
reshapeTexture(this, this._shape[0], h)
return h
}
}
})
proto.bind = function(unit) {
var gl = this.gl
if(unit !== undefined) {
gl.activeTexture(gl.TEXTURE0 + (unit|0))
}
gl.bindTexture(gl.TEXTURE_2D, this.handle)
if(unit !== undefined) {
return (unit|0)
}
return gl.getParameter(gl.ACTIVE_TEXTURE) - gl.TEXTURE0
}
proto.dispose = function() {
this.gl.deleteTexture(this.handle)
}
proto.generateMipmap = function() {
this.bind()
this.gl.generateMipmap(this.gl.TEXTURE_2D)
//Update mip levels
var l = Math.min(this._shape[0], this._shape[1])
for(var i=0; l>0; ++i, l>>>=1) {
if(this._mipLevels.indexOf(i) < 0) {
this._mipLevels.push(i)
}
}
}
proto.setPixels = function(data, x_off, y_off, mip_level) {
var gl = this.gl
this.bind()
if(Array.isArray(x_off)) {
mip_level = y_off
y_off = x_off[1]|0
x_off = x_off[0]|0
} else {
x_off = x_off || 0
y_off = y_off || 0
}
mip_level = mip_level || 0
var directData = acceptTextureDOM(data) ? data : data.raw
if(directData) {
var needsMip = this._mipLevels.indexOf(mip_level) < 0
if(needsMip) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, this.type, directData)
this._mipLevels.push(mip_level)
} else {
gl.texSubImage2D(gl.TEXTURE_2D, mip_level, x_off, y_off, this.format, this.type, directData)
}
} else if(data.shape && data.stride && data.data) {
if(data.shape.length < 2 ||
x_off + data.shape[1] > this._shape[1]>>>mip_level ||
y_off + data.shape[0] > this._shape[0]>>>mip_level ||
x_off < 0 ||
y_off < 0) {
throw new Error('gl-texture2d: Texture dimensions are out of bounds')
}
texSubImageArray(gl, x_off, y_off, mip_level, this.format, this.type, this._mipLevels, data)
} else {
throw new Error('gl-texture2d: Unsupported data type')
}
}
function isPacked(shape, stride) {
if(shape.length === 3) {
return (stride[2] === 1) &&
(stride[1] === shape[0]*shape[2]) &&
(stride[0] === shape[2])
}
return (stride[0] === 1) &&
(stride[1] === shape[0])
}
function texSubImageArray(gl, x_off, y_off, mip_level, cformat, ctype, mipLevels, array) {
var dtype = array.dtype
var shape = array.shape.slice()
if(shape.length < 2 || shape.length > 3) {
throw new Error('gl-texture2d: Invalid ndarray, must be 2d or 3d')
}
var type = 0, format = 0
var packed = isPacked(shape, array.stride.slice())
if(dtype === 'float32') {
type = gl.FLOAT
} else if(dtype === 'float64') {
type = gl.FLOAT
packed = false
dtype = 'float32'
} else if(dtype === 'uint8') {
type = gl.UNSIGNED_BYTE
} else {
type = gl.UNSIGNED_BYTE
packed = false
dtype = 'uint8'
}
var channels = 1
if(shape.length === 2) {
format = gl.LUMINANCE
shape = [shape[0], shape[1], 1]
array = ndarray(array.data, shape, [array.stride[0], array.stride[1], 1], array.offset)
} else if(shape.length === 3) {
if(shape[2] === 1) {
format = gl.ALPHA
} else if(shape[2] === 2) {
format = gl.LUMINANCE_ALPHA
} else if(shape[2] === 3) {
format = gl.RGB
} else if(shape[2] === 4) {
format = gl.RGBA
} else {
throw new Error('gl-texture2d: Invalid shape for pixel coords')
}
channels = shape[2]
} else {
throw new Error('gl-texture2d: Invalid shape for texture')
}
//For 1-channel textures allow conversion between formats
if((format === gl.LUMINANCE || format === gl.ALPHA) &&
(cformat === gl.LUMINANCE || cformat === gl.ALPHA)) {
format = cformat
}
if(format !== cformat) {
throw new Error('gl-texture2d: Incompatible texture format for setPixels')
}
var size = array.size
var needsMip = mipLevels.indexOf(mip_level) < 0
if(needsMip) {
mipLevels.push(mip_level)
}
if(type === ctype && packed) {
//Array data types are compatible, can directly copy into texture
if(array.offset === 0 && array.data.length === size) {
if(needsMip) {
gl.texImage2D(gl.TEXTURE_2D, mip_level, cformat, shape[0], shape[1], 0, cformat, ctype, array.data)
} else {
gl.texSubImage2D(gl.TEXTURE_2D, mip_level, x_off, y_off, shape[0], shape[1], cformat, ctype, array.data)
}
} else {
if(needsMip) {
gl.texImage2D(gl.TEXTURE_2D, mip_level, cformat, shape[0], shape[1], 0, cformat, ctype, array.data.subarray(array.offset, array.offset+size))
} else {
gl.texSubImage2D(gl.TEXTURE_2D, mip_level, x_off, y_off, shape[0], shape[1], cformat, ctype, array.data.subarray(array.offset, array.offset+size))
}
}
} else {
//Need to do type conversion to pack data into buffer
var pack_buffer
if(ctype === gl.FLOAT) {
pack_buffer = pool.mallocFloat32(size)
} else {
pack_buffer = pool.mallocUint8(size)
}
var pack_view = ndarray(pack_buffer, shape, [shape[2], shape[2]*shape[0], 1])
if(type === gl.FLOAT && ctype === gl.UNSIGNED_BYTE) {
convertFloatToUint8(pack_view, array)
} else {
ops.assign(pack_view, array)
}
if(needsMip) {
gl.texImage2D(gl.TEXTURE_2D, mip_level, cformat, shape[0], shape[1], 0, cformat, ctype, pack_buffer.subarray(0, size))
} else {
gl.texSubImage2D(gl.TEXTURE_2D, mip_level, x_off, y_off, shape[0], shape[1], cformat, ctype, pack_buffer.subarray(0, size))
}
if(ctype === gl.FLOAT) {
pool.freeFloat32(pack_buffer)
} else {
pool.freeUint8(pack_buffer)
}
}
}
function initTexture(gl) {
var tex = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, tex)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
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)
return tex
}
function createTextureShape(gl, width, height, format, type) {
var maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE)
if(width < 0 || width > maxTextureSize || height < 0 || height > maxTextureSize) {
throw new Error('gl-texture2d: Invalid texture shape')
}
if(type === gl.FLOAT && !gl.getExtension('OES_texture_float')) {
throw new Error('gl-texture2d: Floating point textures not supported on this platform')
}
var tex = initTexture(gl)
gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, type, null)
return new Texture2D(gl, tex, width, height, format, type)
}
function createTextureDOM(gl, directData, width, height, format, type) {
var tex = initTexture(gl)
gl.texImage2D(gl.TEXTURE_2D, 0, format, format, type, directData)
return new Texture2D(gl, tex, width, height, format, type)
}
//Creates a texture from an ndarray
function createTextureArray(gl, array) {
var dtype = array.dtype
var shape = array.shape.slice()
var maxSize = gl.getParameter(gl.MAX_TEXTURE_SIZE)
if(shape[0] < 0 || shape[0] > maxSize || shape[1] < 0 || shape[1] > maxSize) {
throw new Error('gl-texture2d: Invalid texture size')
}
var packed = isPacked(shape, array.stride.slice())
var type = 0
if(dtype === 'float32') {
type = gl.FLOAT
} else if(dtype === 'float64') {
type = gl.FLOAT
packed = false
dtype = 'float32'
} else if(dtype === 'uint8') {
type = gl.UNSIGNED_BYTE
} else {
type = gl.UNSIGNED_BYTE
packed = false
dtype = 'uint8'
}
var format = 0
if(shape.length === 2) {
format = gl.LUMINANCE
shape = [shape[0], shape[1], 1]
array = ndarray(array.data, shape, [array.stride[0], array.stride[1], 1], array.offset)
} else if(shape.length === 3) {
if(shape[2] === 1) {
format = gl.ALPHA
} else if(shape[2] === 2) {
format = gl.LUMINANCE_ALPHA
} else if(shape[2] === 3) {
format = gl.RGB
} else if(shape[2] === 4) {
format = gl.RGBA
} else {
throw new Error('gl-texture2d: Invalid shape for pixel coords')
}
} else {
throw new Error('gl-texture2d: Invalid shape for texture')
}
if(type === gl.FLOAT && !gl.getExtension('OES_texture_float')) {
type = gl.UNSIGNED_BYTE
packed = false
}
var buffer, buf_store
var size = array.size
if(!packed) {
var stride = [shape[2], shape[2]*shape[0], 1]
buf_store = pool.malloc(size, dtype)
var buf_array = ndarray(buf_store, shape, stride, 0)
if((dtype === 'float32' || dtype === 'float64') && type === gl.UNSIGNED_BYTE) {
convertFloatToUint8(buf_array, array)
} else {
ops.assign(buf_array, array)
}
buffer = buf_store.subarray(0, size)
} else if (array.offset === 0 && array.data.length === size) {
buffer = array.data
} else {
buffer = array.data.subarray(array.offset, array.offset + size)
}
var tex = initTexture(gl)
gl.texImage2D(gl.TEXTURE_2D, 0, format, shape[0], shape[1], 0, format, type, buffer)
if(!packed) {
pool.free(buf_store)
}
return new Texture2D(gl, tex, shape[0], shape[1], format, type)
}
function createTexture2D(gl) {
if(arguments.length <= 1) {
throw new Error('gl-texture2d: Missing arguments for texture2d constructor')
}
if(!linearTypes) {
lazyInitLinearTypes(gl)
}
if(typeof arguments[1] === 'number') {
return createTextureShape(gl, arguments[1], arguments[2], arguments[3]||gl.RGBA, arguments[4]||gl.UNSIGNED_BYTE)
}
if(Array.isArray(arguments[1])) {
return createTextureShape(gl, arguments[1][0]|0, arguments[1][1]|0, arguments[2]||gl.RGBA, arguments[3]||gl.UNSIGNED_BYTE)
}
if(typeof arguments[1] === 'object') {
var obj = arguments[1]
var directData = acceptTextureDOM(obj) ? obj : obj.raw
if (directData) {
return createTextureDOM(gl, directData, obj.width|0, obj.height|0, arguments[2]||gl.RGBA, arguments[3]||gl.UNSIGNED_BYTE)
} else if(obj.shape && obj.data && obj.stride) {
return createTextureArray(gl, obj)
}
}
throw new Error('gl-texture2d: Invalid arguments for texture2d constructor')
}