let FLOATING_POINT_AVAILABLE = false; /** * Helper class to create a WebGL Texture * * @class * @memberof PIXI.glCore * @param gl {WebGLRenderingContext} The current WebGL context * @param width {number} the width of the texture * @param height {number} the height of the texture * @param format {number} the pixel format of the texture. defaults to gl.RGBA * @param type {number} the gl type of the texture. defaults to gl.UNSIGNED_BYTE */ export default class Texture { constructor(gl, width, height, format, type) { /** * The current WebGL rendering context * * @member {WebGLRenderingContext} */ this.gl = gl; /** * The WebGL texture * * @member {WebGLTexture} */ this.texture = gl.createTexture(); /** * If mipmapping was used for this texture, enable and disable with enableMipmap() * * @member {Boolean} */ // some settings.. this.mipmap = false; /** * Set to true to enable pre-multiplied alpha * * @member {Boolean} */ this.premultiplyAlpha = false; /** * The width of texture * * @member {Number} */ this.width = width || -1; /** * The height of texture * * @member {Number} */ this.height = height || -1; /** * The pixel format of the texture. defaults to gl.RGBA * * @member {Number} */ this.format = format || gl.RGBA; /** * The gl type of the texture. defaults to gl.UNSIGNED_BYTE * * @member {Number} */ this.type = type || gl.UNSIGNED_BYTE; } /** * Uploads this texture to the GPU * @param source {HTMLImageElement|ImageData|HTMLVideoElement} the source image of the texture */ upload(source) { this.bind(); const gl = this.gl; gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); const newWidth = source.videoWidth || source.width; const newHeight = source.videoHeight || source.height; if (newHeight !== this.height || newWidth !== this.width) { gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, this.type, source); } else { gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.format, this.type, source); } // if the source is a video, we need to use the videoWidth / videoHeight // properties as width / height will be incorrect. this.width = newWidth; this.height = newHeight; } /** * Use a data source and uploads this texture to the GPU * @param data {TypedArray} the data to upload to the texture * @param width {number} the new width of the texture * @param height {number} the new height of the texture */ uploadData(data, width, height) { this.bind(); const gl = this.gl; if (data instanceof Float32Array) { if (!FLOATING_POINT_AVAILABLE) { const ext = gl.getExtension('OES_texture_float'); if (ext) { FLOATING_POINT_AVAILABLE = true; } else { throw new Error('floating point textures not available'); } } this.type = gl.FLOAT; } else { // TODO support for other types this.type = this.type || gl.UNSIGNED_BYTE; } // what type of data? gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); if (width !== this.width || height !== this.height) { gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, this.type, data || null); } else { gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, this.format, this.type, data || null); } this.width = width; this.height = height; } /** * Binds the texture * @param location */ bind(location) { const gl = this.gl; if (location !== undefined) { gl.activeTexture(gl.TEXTURE0 + location); } gl.bindTexture(gl.TEXTURE_2D, this.texture); } /** * Unbinds the texture */ unbind() { const gl = this.gl; gl.bindTexture(gl.TEXTURE_2D, null); } /** * @param linear {Boolean} if we want to use linear filtering or nearest neighbour interpolation */ minFilter(linear) { const gl = this.gl; this.bind(); if (this.mipmap) { /* eslint-disable max-len */ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linear ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); /* eslint-disable max-len */ } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linear ? gl.LINEAR : gl.NEAREST); } } /** * @param linear {Boolean} if we want to use linear filtering or nearest neighbour interpolation */ magFilter(linear) { const gl = this.gl; this.bind(); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, linear ? gl.LINEAR : gl.NEAREST); } /** * Enables mipmapping */ enableMipmap() { const gl = this.gl; this.bind(); this.mipmap = true; gl.generateMipmap(gl.TEXTURE_2D); } /** * Enables linear filtering */ enableLinearScaling() { this.minFilter(true); this.magFilter(true); } /** * Enables nearest neighbour interpolation */ enableNearestScaling() { this.minFilter(false); this.magFilter(false); } /** * Enables clamping on the texture so WebGL will not repeat it */ enableWrapClamp() { const gl = this.gl; this.bind(); 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); } enableWrapRepeat() { const gl = this.gl; this.bind(); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } enableWrapMirrorRepeat() { const gl = this.gl; this.bind(); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); } /** * Destroys this texture */ destroy() { const gl = this.gl; // TODO gl.deleteTexture(this.texture); } /** * @static * @param gl {WebGLRenderingContext} The current WebGL context * @param source {HTMLImageElement|ImageData} the source image of the texture * @param premultiplyAlpha {Boolean} If we want to use pre-multiplied alpha */ static fromSource(gl, source, premultiplyAlpha) { const texture = new Texture(gl); texture.premultiplyAlpha = premultiplyAlpha || false; texture.upload(source); return texture; } /** * @static * @param gl {WebGLRenderingContext} The current WebGL context * @param data {TypedArray} the data to upload to the texture * @param width {number} the new width of the texture * @param height {number} the new height of the texture */ static fromData(gl, data, width, height) { // console.log(data, width, height); const texture = new Texture(gl); texture.uploadData(data, width, height); return texture; } }