gl-image
Version:
image filter utils based on webgl
144 lines (143 loc) • 6.31 kB
JavaScript
function polyfill() {
// From: https://github.com/evanw/OES_texture_float_linear-polyfill
// Uploads a 2x2 floating-point texture where one pixel is 2 and the other
// three pixels are 0. Linear filtering is only supported if a sample taken
// from the center of that texture is (2 + 0 + 0 + 0) / 4 = 0.5.
function supportsOESTextureFloatLinear(gl) {
// Need floating point textures in the first place
if (!gl.getExtension('OES_texture_float')) {
return false;
}
// Create a render target
var framebuffer = gl.createFramebuffer();
var byteTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, byteTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, byteTexture, 0);
// Create a simple floating-point texture with value of 0.5 in the center
var rgba = [
2, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
];
var floatTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, floatTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array(rgba));
// Create the test shader
var program = gl.createProgram();
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!program || !vertexShader || !fragmentShader) {
throw new Error('failed to create webgl program.');
}
gl.shaderSource(vertexShader, '\
attribute vec2 vertex;\
void main() {\
gl_Position = vec4(vertex, 0.0, 1.0);\
}\
');
gl.shaderSource(fragmentShader, '\
uniform sampler2D texture;\
void main() {\
gl_FragColor = texture2D(texture, vec2(0.5));\
}\
');
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// Create a buffer containing a single point
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0]), gl.STREAM_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
// Render the point and read back the rendered pixel
var pixel = new Uint8Array(4);
gl.useProgram(program);
gl.viewport(0, 0, 1, 1);
gl.bindTexture(gl.TEXTURE_2D, floatTexture);
gl.drawArrays(gl.POINTS, 0, 1);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
// The center sample will only have a value of 0.5 if linear filtering works
return pixel[0] === 127 || pixel[0] === 128;
}
// The constructor for the returned extension object
var OESTextureFloatLinear = /** @class */ (function () {
function OESTextureFloatLinear() {
}
return OESTextureFloatLinear;
}());
// Cache the extension so it's specific to each context like extensions should be
function getOESTextureFloatLinear(gl) {
if (gl.$OES_texture_float_linear$ === void 0) {
Object.defineProperty(gl, '$OES_texture_float_linear$', {
enumerable: false,
configurable: false,
writable: false,
value: new OESTextureFloatLinear()
});
}
return gl.$OES_texture_float_linear$;
}
// This replaces the real getExtension()
function getExtension(name) {
if (!(this instanceof WebGLRenderingContext)) {
throw new Error('getExtension must be called by webgl context!');
}
return name === 'OES_texture_float_linear'
? getOESTextureFloatLinear(this)
: oldGetExtension.call(this, name);
}
// This replaces the real getSupportedExtensions()
function getSupportedExtensions() {
if (!(this instanceof WebGLRenderingContext)) {
throw new Error('getSupportedExtensions must be called by webgl context!');
}
var extensions = oldGetSupportedExtensions.call(this);
if (extensions.indexOf('OES_texture_float_linear') === -1) {
extensions.push('OES_texture_float_linear');
}
return extensions;
}
// Get a WebGL context
var oldGetExtension;
var oldGetSupportedExtensions;
var gl;
try {
var canvas = document.createElement('canvas');
gl = (canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl'));
}
catch (e) {
throw e;
}
if (!gl) {
return;
}
// Don't install the polyfill if the browser already supports it or doesn't have WebGL
var oldExtensions = gl.getSupportedExtensions();
if (Array.isArray(oldExtensions) && oldExtensions.indexOf('OES_texture_float_linear') !== -1) {
return;
}
// Install the polyfill if linear filtering works with floating-point textures
if (supportsOESTextureFloatLinear(gl)) {
oldGetExtension = WebGLRenderingContext.prototype.getExtension;
oldGetSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions;
WebGLRenderingContext.prototype.getExtension = getExtension;
WebGLRenderingContext.prototype.getSupportedExtensions = getSupportedExtensions;
}
}
polyfill();
;