tav-media
Version:
Cross platform media editing framework
189 lines (186 loc) • 7.22 kB
JavaScript
/* eslint-disable prefer-destructuring */
export function createFrameBufferAndTexture(gl) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
const targetTextureWidth = gl.canvas.width;
const targetTextureHeight = gl.canvas.height;
// define size and format of level 0
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, targetTextureWidth, targetTextureHeight, border, format, type, data);
// set the filtering so we don't need mips
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);
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
return {
framebuffer,
texture,
};
}
const PresentVertexShader = `
attribute vec4 a_position;
attribute vec2 a_texcoord;
uniform mat4 u_matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * a_position;
v_texcoord = a_texcoord;
}
`;
const PresentFragmentShader = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;
export function createTextureRenderer(gl) {
const webPresentProgram = createProgramFromSources(gl, [PresentVertexShader, PresentFragmentShader]);
const webPositionLocation = gl.getAttribLocation(webPresentProgram, 'a_position');
const webTexCoordLocation = gl.getAttribLocation(webPresentProgram, 'a_texcoord');
const webMatrixLocation = gl.getUniformLocation(webPresentProgram, 'u_matrix');
const webTextureLocation = gl.getUniformLocation(webPresentProgram, 'u_texture');
const positions = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
const texCoords = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
const positionBuffer = gl.createBuffer();
return function drawTexture(tex, width, height) {
gl.viewport(0, 0, width, height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.useProgram(webPresentProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(webPositionLocation);
gl.vertexAttribPointer(webPositionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.enableVertexAttribArray(webTexCoordLocation);
gl.vertexAttribPointer(webTexCoordLocation, 2, gl.FLOAT, false, 0, 0);
let matrix = m4.orthographic(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
matrix = m4.scale(matrix, width, height, 1);
gl.uniformMatrix4fv(webMatrixLocation, false, matrix);
gl.uniform1i(webTextureLocation, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
}
export function loadImageAndCreateTextureInfo(gl, url) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Fill the texture with a 1x1 blue pixel.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
// let's assume all images are not a power of 2
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
const textureInfo = {
width: 1,
height: 1,
texture: tex,
};
const img = new Image();
img.addEventListener('load', () => {
textureInfo.width = img.width;
textureInfo.height = img.height;
gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
});
img.src = url;
return textureInfo;
}
function loadShader(gl, shaderSource, shaderType) {
const shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
const lastError = gl.getShaderInfoLog(shader);
console.error(`Error compiling shader: ${lastError}\n${shaderSource}`);
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(gl, shaders) {
const program = gl.createProgram();
shaders.forEach((shader) => {
gl.attachShader(program, shader);
});
gl.linkProgram(program);
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
const lastError = gl.getProgramInfoLog(program);
console.error(`Error in program linking: ${lastError}\n${shaders.map((shader) => {
const src = gl.getShaderSource(shader);
const type = gl.getShaderParameter(shader, gl.SHADER_TYPE);
return `${type}:\n${src}`;
}).join('\n')}`);
gl.deleteProgram(program);
return null;
}
return program;
}
const defaultShaderType = [
'VERTEX_SHADER',
'FRAGMENT_SHADER',
];
function createProgramFromSources(gl, shaderSources) {
const shaders = [];
for (let ii = 0; ii < shaderSources.length; ++ii) {
shaders.push(loadShader(gl, shaderSources[ii], gl[defaultShaderType[ii]]));
}
return createProgram(gl, shaders);
}
class m4 {
static orthographic(left, right, bottom, top, near, far) {
const dst = new Float32Array(16);
dst[0] = 2 / (right - left);
dst[1] = 0;
dst[2] = 0;
dst[3] = 0;
dst[4] = 0;
dst[5] = 2 / (top - bottom);
dst[6] = 0;
dst[7] = 0;
dst[8] = 0;
dst[9] = 0;
dst[10] = 2 / (far - near);
dst[11] = 0;
dst[12] = (left + right) / (left - right);
dst[13] = (bottom + top) / (bottom - top);
dst[14] = (near + far) / (near - far);
dst[15] = 1;
return dst;
}
static scale(m, sx, sy, sz) {
const dst = new Float32Array(16);
dst[0] = sx * m[0 * 4 + 0];
dst[1] = sx * m[0 * 4 + 1];
dst[2] = sx * m[0 * 4 + 2];
dst[3] = sx * m[0 * 4 + 3];
dst[4] = sy * m[1 * 4 + 0];
dst[5] = sy * m[1 * 4 + 1];
dst[6] = sy * m[1 * 4 + 2];
dst[7] = sy * m[1 * 4 + 3];
dst[8] = sz * m[2 * 4 + 0];
dst[9] = sz * m[2 * 4 + 1];
dst[10] = sz * m[2 * 4 + 2];
dst[11] = sz * m[2 * 4 + 3];
dst[12] = m[12];
dst[13] = m[13];
dst[14] = m[14];
dst[15] = m[15];
return dst;
}
}