import { compileShader , buildShaderProgram , createTextureWithType , TextureDataType } from "./gl" ; export enum TextureComponents { RGBA = "RGBA" , RGB = "RGB" , R = "R" , G = "G" , B = "B" , A = "A" } export class TextureDataConverter { gl : WebGL2RenderingContext ; readFbo : WebGLFramebuffer ; shaderProgram : WebGLProgram ; texture : WebGLTexture ; posBuffer : WebGLBuffer ; texCoordBuffer : WebGLBuffer ; constructor ( gl ) { this . gl = gl ; this . readFbo = this . gl . createFramebuffer ( ) ; this . createVertexBuffers ( ) ; this . buildShader ( ) ; } getData ( sourceTexture , width , height , dataType , components = TextureComponents . RGBA , flipY = false ) { const gl = this . gl ; this . texture = createTextureWithType ( this . gl , dataType , width , height ) ; gl . bindFramebuffer ( gl . FRAMEBUFFER , this . readFbo ) ; gl . activeTexture ( gl . TEXTURE0 ) ; gl . framebufferTexture2D ( gl . FRAMEBUFFER , gl . COLOR_ATTACHMENT0 , gl . TEXTURE_2D , this . texture , 0 ) ; gl . viewport ( 0 , 0 , width , height ) ; gl . disable ( gl . DEPTH_TEST ) ; gl . clearColor ( 0 , 0 , 0 , 1 ) ; gl . clear ( gl . COLOR_BUFFER_BIT ) ; gl . useProgram ( this . shaderProgram ) ; gl . activeTexture ( gl . TEXTURE0 ) ; gl . bindTexture ( gl . TEXTURE_2D , sourceTexture ) ; gl . uniform1i ( gl . getUniformLocation ( this . shaderProgram , "tex" ) , 0 ) ; gl . uniform1i ( gl . getUniformLocation ( this . shaderProgram , "flipY" ) , flipY ? 1 : 0 ) ; let outputChannel = 0 ; switch ( components ) { case TextureComponents . R : outputChannel = 1 ; break ; case TextureComponents . G : outputChannel = 2 ; break ; case TextureComponents . B : outputChannel = 3 ; break ; case TextureComponents . A : outputChannel = 4 ; break ; } gl . uniform1i ( gl . getUniformLocation ( this . shaderProgram , "channel" ) , outputChannel ) ; const posLoc = gl . getAttribLocation ( this . shaderProgram , "a_pos" ) ; const texCoordLoc = gl . getAttribLocation ( this . shaderProgram , "a_texCoord" ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , this . posBuffer ) ; gl . enableVertexAttribArray ( posLoc ) ; gl . vertexAttribPointer ( posLoc , 3 , gl . FLOAT , false , 0 , 0 ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , this . texCoordBuffer ) ; gl . enableVertexAttribArray ( texCoordLoc ) ; gl . vertexAttribPointer ( texCoordLoc , 2 , gl . FLOAT , false , 0 , 0 ) ; gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ; gl . disableVertexAttribArray ( posLoc ) ; gl . disableVertexAttribArray ( texCoordLoc ) ; let readDataType = gl . UNSIGNED_BYTE ; let arrayBufferView = null ; let arrayBuffer = null ; let format = gl . RGBA ; let numComponents = 4 ; switch ( dataType ) { case TextureDataType . Uint8 : readDataType = gl . UNSIGNED_BYTE ; arrayBuffer = new ArrayBuffer ( width * height * numComponents * 1 ) ; arrayBufferView = new Uint8Array ( arrayBuffer ) ; break ; case TextureDataType . Uint16 : readDataType = gl . UNSIGNED_SHORT ; arrayBuffer = new ArrayBuffer ( width * height * numComponents * 2 ) ; arrayBufferView = new Uint16Array ( arrayBuffer ) ; break ; case TextureDataType . Float16 : readDataType = gl . HALF_FLOAT ; arrayBuffer = new ArrayBuffer ( width * height * numComponents * 2 ) ; arrayBufferView = new Uint16Array ( arrayBuffer ) ; break ; case TextureDataType . Float32 : readDataType = gl . FLOAT ; arrayBuffer = new ArrayBuffer ( width * height * numComponents * 4 ) ; arrayBufferView = new Float32Array ( arrayBuffer ) ; break ; } if ( gl . checkFramebufferStatus ( gl . FRAMEBUFFER ) === gl . FRAMEBUFFER_COMPLETE ) { gl . readPixels ( 0 , 0 , width , height , format , readDataType , arrayBufferView ) ; } else { alert ( "getPixelData: unable to read from FBO" ) ; } gl . enable ( gl . DEPTH_TEST ) ; gl . bindFramebuffer ( gl . FRAMEBUFFER , null ) ; if ( dataType === TextureDataType . Uint8 ) { if ( components === TextureComponents . RGB ) { arrayBuffer = rgbaToRgbUint8 ( arrayBuffer , width , height ) ; } else if ( components === TextureComponents . R || components === TextureComponents . G || components === TextureComponents . B || components === TextureComponents . A ) { arrayBuffer = rgbaToRUint8 ( arrayBuffer , width , height ) ; } } else if ( dataType === TextureDataType . Uint16 ) { if ( components === TextureComponents . RGB ) { arrayBuffer = rgbaToRgbUint16 ( arrayBuffer , width , height ) ; } if ( components === TextureComponents . R || components === TextureComponents . G || components === TextureComponents . B || components === TextureComponents . A ) { arrayBuffer = rgbaToRUint16 ( arrayBuffer , width , height ) ; } } return arrayBuffer ; } createVertexBuffers ( ) { const gl = this . gl ; this . texCoordBuffer = gl . createBuffer ( ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , this . texCoordBuffer ) ; gl . bufferData ( gl . ARRAY_BUFFER , new Float32Array ( [ 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 1.0 , 1.0 , 0.0 , 1.0 , 1.0 ] ) , gl . STATIC_DRAW ) ; this . posBuffer = gl . createBuffer ( ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , this . posBuffer ) ; gl . bufferData ( gl . ARRAY_BUFFER , new Float32Array ( [ - 1.0 , - 1.0 , 0.0 , 1.0 , - 1.0 , 0.0 , - 1.0 , 1.0 , 0.0 , - 1.0 , 1.0 , 0.0 , 1.0 , - 1.0 , 0.0 , 1.0 , 1.0 , 0.0 ] ) , gl . STATIC_DRAW ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , null ) ; } buildShader ( ) { const vertSource = `#version 300 es
        precision highp float;

        in vec3 a_pos;
        in vec2 a_texCoord;
            
        // the texCoords passed in from the vertex shader.
        out vec2 v_texCoord;
            
        void main() {
            gl_Position = vec4(a_pos,1.0);
            v_texCoord = a_texCoord;
        }` ; let fragSource = `#version 300 es
        precision highp float;
        in vec2 v_texCoord;

        uniform sampler2D tex;
        // uniform vec2 _textureSize;
        uniform bool flipY;

		uniform int channel;

        // out uvec4 fragColor;
        out vec4 fragColor;
            
        void main() {
            vec2 texCoords = vec2(v_texCoord.x, flipY? 1.0 - v_texCoord.y:v_texCoord.y);
			vec4 result = texture(tex, texCoords);

			// determine which channels to extract if any
			switch(channel){
				// 0 - do nothing

				// red
				case 1:
					result = vec4(result.r);
					break;
				// green
				case 2:
					result = vec4(result.g);
					break;
				// blue
				case 3:
					result = vec4(result.b);
					break;
				// alpha
				case 4:
					result = vec4(result.a);
					break;
			}

			fragColor = result;
			// fragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }

        ` ; this . shaderProgram = buildShaderProgram ( this . gl , vertSource , fragSource ) ; } } function rgbaToRgbUint8 ( buffer , width , height ) { const totalPixels = width * height ; const numComponents = 3 ; const inputArray = new Uint8Array ( buffer ) ; const outputArray = new Uint8Array ( totalPixels * numComponents ) ; for ( let i = 0 ; i < totalPixels ; i ++ ) { outputArray [ i * numComponents + 0 ] = inputArray [ i * 4 + 0 ] ; outputArray [ i * numComponents + 1 ] = inputArray [ i * 4 + 1 ] ; outputArray [ i * numComponents + 2 ] = inputArray [ i * 4 + 2 ] ; } return outputArray . buffer ; } function rgbaToRUint8 ( buffer , width , height ) { const totalPixels = width * height ; const numComponents = 1 ; const inputArray = new Uint8Array ( buffer ) ; const outputArray = new Uint8Array ( totalPixels * numComponents ) ; for ( let i = 0 ; i < totalPixels ; i ++ ) { outputArray [ i * numComponents ] = inputArray [ i * 4 ] ; } return outputArray . buffer ; } function rgbaToRgbUint16 ( buffer , width , height ) { const totalPixels = width * height ; const numComponents = 3 ; const inputArray = new Uint16Array ( buffer ) ; const outputArray = new Uint16Array ( totalPixels * numComponents ) ; for ( let i = 0 ; i < totalPixels ; i ++ ) { outputArray [ i * numComponents + 0 ] = inputArray [ i * 4 + 0 ] ; outputArray [ i * numComponents + 1 ] = inputArray [ i * 4 + 1 ] ; outputArray [ i * numComponents + 2 ] = inputArray [ i * 4 + 2 ] ; } return outputArray . buffer ; } function rgbaToRUint16 ( buffer , width , height ) { const totalPixels = width * height ; const numComponents = 1 ; const inputArray = new Uint16Array ( buffer ) ; const outputArray = new Uint16Array ( totalPixels * numComponents ) ; for ( let i = 0 ; i < totalPixels ; i ++ ) { outputArray [ i * numComponents ] = inputArray [ i * 4 ] ; } return outputArray . buffer ; }