@razorpay/blade
Version:
The Design System that powers Razorpay
201 lines (188 loc) • 7.16 kB
JavaScript
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
import _createClass from '@babel/runtime/helpers/createClass';
/**
* WebGL utility functions for shader compilation, program creation,
* geometry setup, and texture handling.
*/
// Fullscreen quad vertices (two triangles covering clip space -1 to 1)
// prettier-ignore
var FULLSCREEN_QUAD_POSITIONS = new Float32Array([-1, -1,
// bottom-left
1, -1,
// bottom-right
-1, 1,
// top-left
-1, 1,
// top-left
1, -1,
// bottom-right
1, 1 // top-right
]);
// Standard UV coordinates for fullscreen quad
// prettier-ignore
var FULLSCREEN_QUAD_UVS = new Float32Array([0, 0,
// bottom-left
1, 0,
// bottom-right
0, 1,
// top-left
0, 1,
// top-left
1, 0,
// bottom-right
1, 1 // top-right
]);
/**
* Creates and compiles a WebGL shader
*/
function createShader(gl, type, source) {
var shader = gl.createShader(type);
if (!shader) return null;
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compilation error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
/**
* Creates a WebGL program from vertex and fragment shaders
*/
function createProgram(gl, vertexSource, fragmentSource) {
// Check shader precision and upgrade if needed
var format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);
var precision = format ? format.precision : null;
// MEDIUM_FLOAT precision can be 10, 16 or 23 bits depending on device
// Shaders fail on 10 bit => we force 23-bit by switching to highp
if (precision && precision < 23) {
vertexSource = vertexSource.replace(/precision\s+(lowp|mediump)\s+float;/g, 'precision highp float;');
fragmentSource = fragmentSource.replace(/precision\s+(lowp|mediump)\s+float/g, 'precision highp float').replace(/\b(uniform|varying|attribute)\s+(lowp|mediump)\s+(\w+)/g, '$1 highp $3');
}
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
if (!vertexShader || !fragmentShader) return null;
var program = gl.createProgram();
if (!program) return null;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return null;
}
// Clean up shaders after successful linking
gl.detachShader(program, vertexShader);
gl.detachShader(program, fragmentShader);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
}
/**
* Sets up a fullscreen quad with position and UV attributes.
* This is the standard geometry for post-processing shaders.
*
* @param gl - WebGL rendering context
* @param program - WebGL program to get attribute locations from
* @param positionAttr - Name of position attribute (default: 'position')
* @param uvAttr - Name of UV attribute (default: 'uv')
* @returns The created buffers for cleanup, or null if setup failed
*/
function setupFullscreenQuad(gl, program) {
var positionAttr = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'position';
var uvAttr = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'uv';
// Position attribute
var positionLocation = gl.getAttribLocation(program, positionAttr);
var positionBuffer = gl.createBuffer();
if (!positionBuffer) return null;
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_QUAD_POSITIONS, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// UV attribute
var uvLocation = gl.getAttribLocation(program, uvAttr);
var uvBuffer = gl.createBuffer();
if (!uvBuffer) return null;
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_QUAD_UVS, gl.STATIC_DRAW);
gl.enableVertexAttribArray(uvLocation);
gl.vertexAttribPointer(uvLocation, 2, gl.FLOAT, false, 0, 0);
return {
positionBuffer: positionBuffer,
uvBuffer: uvBuffer
};
}
/**
* OGL-style Texture class for WebGL texture management.
* Supports images, videos, and canvas elements as sources.
*
* @see https://github.com/oframe/ogl/blob/master/src/core/Texture.js
*/
var Texture = /*#__PURE__*/function () {
function Texture(gl) {
var _params$textureUnit, _params$minFilter, _params$magFilter, _params$wrapS, _params$wrapT, _params$flipY;
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, Texture);
this.gl = gl;
this.textureUnit = (_params$textureUnit = params.textureUnit) !== null && _params$textureUnit !== void 0 ? _params$textureUnit : 0;
this.minFilter = (_params$minFilter = params.minFilter) !== null && _params$minFilter !== void 0 ? _params$minFilter : gl.NEAREST;
this.magFilter = (_params$magFilter = params.magFilter) !== null && _params$magFilter !== void 0 ? _params$magFilter : gl.NEAREST;
this.wrapS = (_params$wrapS = params.wrapS) !== null && _params$wrapS !== void 0 ? _params$wrapS : gl.CLAMP_TO_EDGE;
this.wrapT = (_params$wrapT = params.wrapT) !== null && _params$wrapT !== void 0 ? _params$wrapT : gl.CLAMP_TO_EDGE;
this.flipY = (_params$flipY = params.flipY) !== null && _params$flipY !== void 0 ? _params$flipY : true;
this.texture = gl.createTexture();
this.bind();
this.setParameters();
}
return _createClass(Texture, [{
key: "bind",
value: function bind() {
var gl = this.gl;
gl.activeTexture(gl.TEXTURE0 + this.textureUnit);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
}
}, {
key: "setParameters",
value: function setParameters() {
var gl = this.gl;
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.minFilter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.magFilter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrapS);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrapT);
}
/**
* Upload image data to the texture (OGL-style)
*/
}, {
key: "image",
value: function image(source) {
var gl = this.gl;
if (!source) return;
this.bind();
if (this.flipY) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
}
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
}
/**
* Update texture from video frame (call each frame for video textures)
*/
}, {
key: "update",
value: function update(source) {
this.image(source);
}
}, {
key: "destroy",
value: function destroy() {
this.gl.deleteTexture(this.texture);
this.texture = null;
}
}]);
}();
export { Texture, createProgram, createShader, setupFullscreenQuad };
//# sourceMappingURL=webgl-utils.js.map