import { assert , log , LogLevel } from './auxiliaries' ; import { byteSizeOfFormat } from './formatbytesizes' ; import { GLsizei3 } from './tuples' ; import { Bindable } from './bindable' ; import { TexImage2DData } from './gl2facade' ; import { Initializable } from './initializable' ; import { AbstractObject } from './object' ; /**
 * Wrapper for an WebGL 2D texture array providing size accessors and requiring for bind, unbind, resize, validity, and
 * initialization implementations. The texture object is created on initialization and deleted on uninitialization.
 * After being initialized, the texture can be resized, reformated, and data can set directly or via fetch:
 * ```
 * const texture = new Texture2DArray(context, 'Texture');
 * texture.initialize(1, 1, 1, gl.RGB8, gl.RGB, gl.UNSIGNED_BYTE);
 * texture.fetch('/img/webgl-operate-logo.png', true);
 * ```
 */ export class Texture2DArray extends AbstractObject < WebGLTexture > implements Bindable { /**
     * Default texture, e.g., used for unbind.
     */ static readonly DEFAULT_TEXTURE = undefined ; see width protected _width : GLsizei = 0 ; see height protected _height : GLsizei = 0 ; see depth protected _depth : GLsizei = 0 ; see internalFormat protected _internalFormat : GLenum = 0 ; see format protected _format : GLenum = 0 ; see type protected _type : GLenum = 0 ; param width param height param depth param internalFormat param format param type protected create ( width , height , depth , internalFormat , format , type ) { assert ( this . _context . supportsTexImage3D , `expected texImage3D to be supported` ) ; assert ( width > 0 && height > 0 && depth > 0 , `texture requires valid width, height, and depth of greater than zero` ) ; const gl = this . _context . gl ; const gl2facade = this . _context . gl2facade ; this . _object = gl . createTexture ( ) ; this . _width = width ; this . _height = height ; this . _depth = depth ; this . _internalFormat = internalFormat ; this . _format = format ; this . _type = type ; gl . bindTexture ( gl . TEXTURE_2D_ARRAY , this . _object ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_MAG_FILTER , gl . NEAREST ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_MIN_FILTER , gl . NEAREST ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_S , gl . CLAMP_TO_EDGE ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_T , gl . CLAMP_TO_EDGE ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_R , gl . CLAMP_TO_EDGE ) ; gl2facade . texImage3D ( gl . TEXTURE_2D_ARRAY , 0 , this . _internalFormat , this . _width , this . _height , this . _depth , 0 , this . _format , this . _type ) ; gl . bindTexture ( gl . TEXTURE_2D_ARRAY , Texture2DArray . DEFAULT_TEXTURE ) ; this . _valid = gl . isTexture ( this . _object ) ; this . reallocate ( ) ; return this . _object ; } /**
     * Delete the texture object on the GPU. This should have the reverse effect of `create`.
     */ protected delete ( ) { assert ( this . _object instanceof WebGLTexture , `expected WebGLTexture object` ) ; this . _context . gl . deleteTexture ( this . _object ) ; this . _object = undefined ; this . _valid = false ; this . _internalFormat = 0 ; this . _format = 0 ; this . _type = 0 ; this . _width = 0 ; this . _height = 0 ; this . _depth = 0 ; } protected reallocate ( ) { const gl = this . context . gl ; const gl2facade = this . _context . gl2facade ; let bytes = this . _width * this . _height * this . _depth * byteSizeOfFormat ( this . context , this . _internalFormat ) ; if ( this . _type === gl2facade . HALF_FLOAT && this . _internalFormat !== gl . RGBA16F ) { bytes *= 2 ; } else if ( this . _type === gl . FLOAT && this . _internalFormat !== gl . RGBA16F ) { bytes *= 4 ; } this . context . allocationRegister . reallocate ( this . _identifier , bytes ) ; } /**
     * Bind the texture object to a texture unit.
     */ @ Initializable . assert_initialized ( ) bind ( unit ? ) { const gl = this . context . gl ; if ( unit ) { gl . activeTexture ( unit ) ; } gl . bindTexture ( gl . TEXTURE_2D_ARRAY , this . _object ) ; } /**
     * Unbind the texture object from a texture unit.
     */ @ Initializable . assert_initialized ( ) unbind ( unit ? ) { const gl = this . context . gl ; if ( unit ) { gl . activeTexture ( unit ) ; } gl . bindTexture ( gl . TEXTURE_2D_ARRAY , Texture2DArray . DEFAULT_TEXTURE ) ; } param url param slices param crossOrigin returns @ Initializable . assert_initialized ( ) fetch ( url , slices , crossOrigin = false ) { return new Promise ( ( resolve , reject ) => { const image = new Image ( ) ; image . onerror = ( ) => { log ( LogLevel . Error , `loading image from '${ image . src }' failed` ) ; reject ( ) ; } ; image . onload = ( ) => { this . resize ( image . width , image . height / slices , slices ) ; this . data ( image ) ; resolve ( ) ; } ; if ( crossOrigin ) { image . crossOrigin = 'anonymous' ; } image . src = url ; } ) ; } param data param bind param unbind @ Initializable . assert_initialized ( ) data ( data , bind = true , unbind = true ) { const gl = this . context . gl ; const gl2facade = this . _context . gl2facade ; if ( bind ) { this . bind ( ) ; } gl2facade . texImage3D ( gl . TEXTURE_2D_ARRAY , 0 , this . _internalFormat , this . _width , this . _height , this . _depth , 0 , this . _format , this . _type , data ) ; if ( unbind ) { this . unbind ( ) ; } this . reallocate ( ) ; } param mag param min param bind param unbind @ Initializable . assert_initialized ( ) filter ( mag , min , bind = true , unbind = true ) { const gl = this . context . gl ; if ( bind ) { this . bind ( ) ; } gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_MAG_FILTER , mag ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_MIN_FILTER , min ) ; if ( unbind ) { this . unbind ( ) ; } } param wrap_s param wrap_t param wrap_r param bind param unbind @ Initializable . assert_initialized ( ) wrap ( wrap_s , wrap_t , wrap_r , bind = true , unbind = true ) { const gl = this . context . gl ; if ( bind ) { this . bind ( ) ; } gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_S , wrap_s ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_T , wrap_t ) ; gl . texParameteri ( gl . TEXTURE_2D_ARRAY , gl . TEXTURE_WRAP_R , wrap_r ) ; if ( unbind ) { this . unbind ( ) ; } } /**
     * This can be used to reformat the texture image without creating a new texture object. Please note that this
     * resets the texture's image data to undefined. @see  data for setting new image data.
     *  param internalFormat param format param type param bind param unbind @ Initializable . assert_initialized ( ) reformat ( internalFormat , format ? , type ? , bind = true , unbind = true ) { if ( internalFormat === this . _internalFormat && ( format === undefined || format === this . _format ) && ( type === undefined || type === this . _type ) ) { return ; } assert ( internalFormat !== undefined , `valid internal format expected` ) ; this . _internalFormat = internalFormat ; if ( format ) { this . _format = format ; } if ( type ) { this . _type = type ; } this . data ( undefined , bind , unbind ) ; } param width param height param depth param bind param unbind @ Initializable . assert_initialized ( ) resize ( width , height , depth , bind = true , unbind = true ) { if ( width === this . _width && height === this . _height && depth === this . _depth ) { return ; } this . _width = width ; this . _height = height ; this . _depth = depth ; this . data ( undefined , bind , unbind ) ; } /**
     * Returns the number of bytes this object approximately allocates on the GPU. The size will be zero when no
     * image data was passed to the texture object.
     */ get bytes ( ) : GLsizei { this . assertInitialized ( ) ; return this . context . allocationRegister . allocated ( this . _identifier ) ; } /**
     * Cached internal format of the texture for efficient resize. This can only be changed by re-initialization.
     */ get internalFormat ( ) : GLenum { this . assertInitialized ( ) ; return this . _internalFormat ! ; } /**
     * Cached format of the data provided to the texture object for efficient resize. This is set on initialization and
     * might change on data transfers.
     */ get format ( ) : GLenum { this . assertInitialized ( ) ; return this . _format ! ; } /**
     * Cached type of the data provided to the texture used for efficient resize. This is set on initialization and
     * might change on data transfers.
     */ get type ( ) : GLenum { this . assertInitialized ( ) ; return this . _type ! ; } /**
     * The width of the texture object in px.
     */ get width ( ) : GLsizei { this . assertInitialized ( ) ; return this . _width ; } /**
     * The height of the texture object in px.
     */ get height ( ) : GLsizei { this . assertInitialized ( ) ; return this . _height ; } /**
     * The depth (number of slices) of the texture object.
     */ get depth ( ) : GLsizei { this . assertInitialized ( ) ; return this . _depth ; } see width see height see depth get size ( ) : GLsizei3 { this . assertInitialized ( ) ; return [ this . _width , this . _height , this . _depth ] ; } }