texturity.js
Version:
WebGL based library for drawing textures
1,166 lines (918 loc) • 32.5 kB
JavaScript
var Texturity = (function (exports) {
'use strict';
var colorModelsChunk = function () {
return `
vec3 hsl2rgb( in vec3 c )
{
vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));
}
vec3 HueShift (in vec3 Color, in float Shift)
{
vec3 P = vec3(0.55735)*dot(vec3(0.55735),Color);
vec3 U = Color-P;
vec3 V = cross(vec3(0.55735),U);
Color = U*cos(Shift*6.2832) + V*sin(Shift*6.2832) + P;
return vec3(Color);
}
vec3 rgb2hsl( in vec3 c ){
float h = 0.0;
float s = 0.0;
float l = 0.0;
float r = c.r;
float g = c.g;
float b = c.b;
float cMin = min( r, min( g, b ) );
float cMax = max( r, max( g, b ) );
l = ( cMax + cMin ) / 2.0;
if ( cMax > cMin ) {
float cDelta = cMax - cMin;
//s = l < .05 ? cDelta / ( cMax + cMin ) : cDelta / ( 2.0 - ( cMax + cMin ) ); Original
s = l < .0 ? cDelta / ( cMax + cMin ) : cDelta / ( 2.0 - ( cMax + cMin ) );
if ( r == cMax ) {
h = ( g - b ) / cDelta;
} else if ( g == cMax ) {
h = 2.0 + ( b - r ) / cDelta;
} else {
h = 4.0 + ( r - g ) / cDelta;
}
if ( h < 0.0) {
h += 6.0;
}
h = h / 6.0;
}
return vec3( h, s, l );
}
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}`
};
var grayChunk = function() {
return `float gray(vec3 c){
return dot(c,vec3(0.299, 0.587, 0.114));
}`;
};
var blend = function (b, expression, preExpressions = '') {
return {
vertex: `
attribute vec2 position;
varying mediump vec2 texcoord;
void main(void) {
texcoord = (position+1.0)/2.0;
gl_Position = vec4(position, 0.0, 1.0);
}
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
varying mediump vec2 texcoord;
uniform sampler2D texture1;
${b instanceof WebGLTexture ?
'uniform sampler2D texture2'
: (b instanceof Array ?
'uniform mediump vec3 color'
: 'uniform mediump float value')};
${colorModelsChunk()}
${grayChunk()}
void main(void) {
vec3 a = texture2D(texture1, texcoord).rgb;
vec3 b = ${b instanceof WebGLTexture ?
'texture2D(texture2, texcoord).rgb'
: (b instanceof Array ?
'color'
: 'vec3(value)')};
${preExpressions}
gl_FragColor = vec4(${expression}, 1.0);
}`
}
};
var fourier = function (w, h, inverse) {
var assign = `
float gray = texture2D(texture, (coord+offset)/screen).r;
real += gray * cos(-angle);
imag += gray * sin(-angle);
`;
var fragColor = `
gl_FragColor = vec4(real,imag,magnitude(real,imag),1.0);
`;
var inverseAssign = `vec2 source = texture2D(texture, (coord+offset)/screen).rg;
real += source.r * cos(angle) - source.g * sin(angle);
imag += source.r * sin(angle) + source.g * cos(angle);`;
var inverseFragColor = `
real = real / screen.x / screen.y;
gl_FragColor = vec4(vec3(real),1.0);`;
return {
vertex: `
precision highp float;
attribute vec2 position;
uniform vec2 resolution;
void main(void) {
vec2 np = position/resolution;
np.x=np.x*2.0-1.0;
np.y=1.0-np.y*2.0;
gl_Position = vec4(np, 0.0, 1.0);
}`,
fragment: `
precision highp float;
const int W = ${w};
const int H = ${h};
const float M_PI = 3.1415926535897932384626433832795;
const vec2 screen = vec2(${w},${h});
uniform sampler2D texture;
uniform vec2 resolution;
float magnitude(float real, float imag){
return sqrt(real*real+imag*imag);
}
float phase(float real, float imag){
return atan(real*real+imag*imag);
}
${grayChunk()}
void main(void) {
float real = 0.0;
float imag = 0.0;
vec2 offset = vec2(0.5,0.5);
float x = gl_FragCoord.x-offset.x;
float y = gl_FragCoord.y-offset.y;
for(int j = 0; j < H; j++)
for(int i = 0; i < W; i++){
vec2 coord = vec2(float(i),float(j));
float angle = 2.0*M_PI*(x*coord.x/screen.x+y*coord.y/screen.y);
${inverse ? inverseAssign : assign}
}
${inverse ? inverseFragColor : fragColor}
}`
}
};
var neighbors = function(expression) {
return {
vertex: `#version 300 es
precision mediump float;
in vec2 position;
uniform vec2 resolution;
void main(void) {
gl_Position = vec4(position, 0.0, 1.0);
}
`,
fragment: `#version 300 es
precision mediump float;
uniform sampler2D tex;
uniform vec2 resolution;
uniform float matrix[9];
void getDataMatrix(sampler2D t, vec2 c, out vec3 arr[9]){
int i=0;
for(int x=-1;x<=1;x++)
for(int y=-1;y<=1;y++){
vec2 offset = vec2(x,y)/resolution;
arr[i] = texture(t,c+offset).rgb;
i=i+1;
}
}
out vec4 FragColor;
void main(void) {
vec2 texcoord = gl_FragCoord.xy/resolution;
vec3 arr[9];
getDataMatrix(tex, texcoord, arr);
vec3 color = vec3(0.0);
${expression};
FragColor = vec4(color, 1.0);
}`
}
};
var circle = {
vertex: `
attribute vec2 position;
uniform vec2 resolution;
varying lowp vec2 coord;
void main(void) {
vec2 np = position/resolution;
np.x=np.x*2.0-1.0;
np.y=1.0-np.y*2.0;
coord = np;
gl_Position = vec4(np, 0.0, 1.0);
}`,
fragment: `
varying lowp vec2 coord;
uniform mediump float r;
void main(void) {
lowp float inten = sqrt(coord.x*coord.x+coord.y*coord.y);
lowp vec3 color = vec3(distance(coord,vec2(0.0,0.0))<r?1.0:0.0);
gl_FragColor = vec4(color,1.0);
}`
};
var convolution = {
vertex: `#version 300 es
precision mediump float;
in vec2 position;
uniform vec2 resolution;
void main(void) {
gl_Position = vec4(position, 0.0, 1.0);
}
`,
fragment: `#version 300 es
precision mediump float;
uniform sampler2D tex;
uniform vec2 resolution;
uniform float matrix[9];
void getDataMatrix(sampler2D t, vec2 c, out vec3 arr[9]){
int i=0;
for(int x=-1;x<=1;x++)
for(int y=-1;y<=1;y++){
vec2 offset = vec2(x,y)/resolution;
arr[i] = texture(t,c+offset).rgb;
i=i+1;
}
}
out vec4 FragColor;
void main(void) {
vec2 texcoord = gl_FragCoord.xy/resolution;
vec3 arr[9];
getDataMatrix(tex, texcoord, arr);
vec3 color = vec3(0.0);
for(int i=0;i<9;i++)
color = color + arr[i]*matrix[i];
FragColor = vec4(color, 1.0);
}`
};
var image = {
vertex: `
attribute vec2 position;
attribute vec2 uv;
uniform vec2 resolution;
varying mediump vec2 texcoord;
void main(void) {
vec2 np = position/resolution;
np.x=np.x*2.0-1.0;
np.y=1.0-np.y*2.0;
texcoord = uv;
gl_Position = vec4(np, 0.0, 1.0);
}`,
fragment: `
varying mediump vec2 texcoord;
uniform sampler2D texture;
void main(void) {
gl_FragColor = vec4(texture2D(texture, texcoord).rgba);
}`
};
var noise = {
vertex: `
attribute vec2 position;
varying lowp vec2 pos;
void main(void) {
pos = position;
gl_Position = vec4(position, 0.0, 1.0);
}
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 resolution;
varying lowp vec2 pos;
uniform lowp float seed;
vec4 mod289(vec4 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x)
{
return mod289(((x*34.0)+2.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec2 fade(vec2 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
// Classic Perlin noise
float cnoise(vec2 P)
{
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
Pi = mod289(Pi); // To avoid truncation effects in permutation
vec4 ix = Pi.xzxz;
vec4 iy = Pi.yyww;
vec4 fx = Pf.xzxz;
vec4 fy = Pf.yyww;
vec4 i = permute(permute(ix) + iy);
vec4 gx = fract((seed/4.0+0.4)*i * (1.0 / 41.0)) * 2.0 - 1.0 ;
vec4 gy = abs(gx) - 0.5 ;
vec4 tx = floor(gx + 0.5);
gx = gx - tx;
vec2 g00 = vec2(gx.x,gy.x);
vec2 g10 = vec2(gx.y,gy.y);
vec2 g01 = vec2(gx.z,gy.z);
vec2 g11 = vec2(gx.w,gy.w);
vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
g00 *= norm.x;
g01 *= norm.y;
g10 *= norm.z;
g11 *= norm.w;
float n00 = dot(g00, vec2(fx.x, fy.x));
float n10 = dot(g10, vec2(fx.y, fy.y));
float n01 = dot(g01, vec2(fx.z, fy.z));
float n11 = dot(g11, vec2(fx.w, fy.w));
vec2 fade_xy = fade(Pf.xy);
vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
return 2.3 * n_xy;
}
void main(void){
float noise = cnoise(pos*10.0);
gl_FragColor = vec4(noise,noise,noise,1.0);
}
`
};
var normal = {
vertex: `
attribute vec2 position;
varying mediump vec2 texcoord;
void main(void) {
texcoord = (position+1.0)/2.0;
gl_Position = vec4(position, 0.0, 1.0);
}`,
fragment:`
uniform lowp vec2 resolution;
varying mediump vec2 texcoord;
uniform sampler2D texture;
uniform mediump float scale;
lowp float grey(lowp vec3 color){
return dot(color.rgb, vec3(0.299, 0.587, 0.114));
}
void main(void) {
lowp float PIXEL_WIDTH = 1.0/resolution.x;
lowp float PIXEL_HEIGHT = 1.0/resolution.y;
lowp float c = grey(texture2D(texture, texcoord).rgb);
lowp float cx = grey(texture2D(texture, texcoord+vec2(PIXEL_WIDTH, 0.0)).rgb);
lowp float cy = grey(texture2D(texture, texcoord+vec2(0.0, PIXEL_HEIGHT)).rgb);
lowp float dx = (c - cx) * scale;
lowp float dy = (c - cy) * scale;
lowp float nz = 1.0;
lowp float len = sqrt(dx * dx + dy * dy + nz * nz);
lowp float nx = dx / len;
lowp float ny = -dy / len;
nz = nz / len;
lowp vec3 r = (vec3(nx, ny, nz)+1.0)/2.0;
gl_FragColor = vec4(r,1.0);
}`
};
var radialGradient = {
vertex: `
attribute vec2 position;
uniform vec2 resolution;
varying lowp vec2 coord;
void main(void) {
vec2 np = position/resolution;
np.x=np.x*2.0-1.0;
np.y=1.0-np.y*2.0;
coord = np;
gl_Position = vec4(np, 0.0, 1.0);
}`,
fragment: `
varying lowp vec2 coord;
uniform mediump vec3 color1;
uniform mediump vec3 color2;
void main(void) {
lowp float inten = sqrt(coord.x*coord.x+coord.y*coord.y);
mediump vec3 color = color1 * inten + color2 * (1.0 - inten);
gl_FragColor = vec4(color,1.0);
}`
};
var simple = {
vertex: `
attribute vec2 position;
uniform vec2 resolution;
void main(void) {
vec2 np = position/resolution;
np.x=np.x*2.0-1.0;
np.y=1.0-np.y*2.0;
gl_Position = vec4(np, 0.0, 1.0);
}`,
fragment: `
uniform mediump vec4 color;
void main(void) {
gl_FragColor = color;
}`
};
var ShaderManager = class {
constructor(gl) {
this.gl = gl;
this.cache = new Map();
}
createPrecompiledShaders() {
var precompiled = {
simple,
normal,
convolution,
circle,
image,
noise,
radialGradient
};
Object.keys(precompiled).forEach(key => {
var sources = precompiled[key];
try {
precompiled[key] = this.createShaderProgram(sources);
} catch (e) {
console.warn(e);
}
});
return precompiled;
}
createShaderProgram(sources) {
const hash = `${sources.vertex}_${sources.fragment}`;
if (this.cache.has(hash)) return this.cache.get(hash)
var gl = this.gl;
var vertShader = this.createShader(sources.vertex, gl.VERTEX_SHADER);
var fragShader = this.createShader(sources.fragment, gl.FRAGMENT_SHADER);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.validateProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
var info = gl.getProgramInfoLog(shaderProgram);
throw new Error('Could not compile WebGL program. \n\n' + info);
}
this.cache.set(hash, shaderProgram);
return shaderProgram;
}
createShader(source, type) {
var gl = this.gl;
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!success)
throw `could not compile
${type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'}
shader: ${gl.getShaderInfoLog(shader)}\n ${source}`;
return shader;
}
createShaderRuntime(name, ...args) {
if (name === 'blend')
return this.createShaderProgram(blend(...args));
if (name === 'fourier')
return this.createShaderProgram(fourier(...args));
if (name === 'neighbors')
return this.createShaderProgram(neighbors(...args));
throw new Error('Shader program not registered');
}
clear() {
var gl = this.gl;
for (const shaderProgram of this.cache.values()) {
gl.deleteProgram(shaderProgram);
}
}
};
class FloatFrameBuffer {
constructor(gl, w, h) {
this.gl = gl;
this.w = w;
this.h = h;
this.framebuffer = this.initFB();
}
result() {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.deleteFramebuffer(this.framebuffer.buffer);
return this.framebuffer.texture;
}
initFB() {
var gl = this.gl;
if (!(gl instanceof WebGL2RenderingContext)) throw new Error('Need webgl 2');
//gl.getExtension('OES_texture_float_linear');
var ext = gl.getExtension('EXT_color_buffer_float');
if (!ext)
throw new Error('no EXT_color_buffer_float');
var buffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer);
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, this.w, this.h, 0, gl.RGBA, gl.FLOAT, null);
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.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status != gl.FRAMEBUFFER_COMPLETE)
alert('can not render to floating point textures');
return { buffer, texture };
}
}
var BrickGenerator = class {
constructor(count, margin) {
this.count = count;
this.margin = margin;
}
getBricks(w, h) {
var bricks = [];
var ly = this.count * 2;
var m = this.margin / ly;
var mw = Math.round(m * w);
var mh = Math.round(m * h);
for (var y = 0; y < ly * 2; y++)
for (var x = 0, lx = ly / 2 + y % 2; x < lx; x++) {
var hd = h / ly;
var wd = w / ly * 2;
var oy = y * hd;
var ox = y % 2 * (-wd / 2) + x * wd;
bricks.push([ox + mw, oy + mh, wd - 2 * mw, hd - 2 * mh]);
}
return bricks;
}
};
var TransformGenerator = class {
constructor(tx, ty, repeat) {
this.tx = tx;
this.ty = ty;
this.repeat = repeat;
}
getRects(w, h) {
var rects = [];
var tx = this.tx % 1;
var ty = this.ty % 1;
var dw = w / (this.repeat + 1);
var dh = h / (this.repeat + 1);
for (var x = -1; x <= this.repeat; x++)
for (var y = -1; y <= this.repeat; y++) {
var px = x * dw + tx * dw;
var py = y * dh + ty * dh;
rects.push([px, py, dw, dh]);
}
return rects;
}
};
var gl = null;
var element = null;
var programs = null;
var vertexBuffer = null;
var textures = [];
var format = null;
var shaderManager = null;
class Canvas {
constructor(w, h, clearColor = [0.0, 0.0, 0.0, 1.0]) {
this.w = w;
this.h = h;
this.backup = null;
gl = initGL();
gl.clearColor(...clearColor);
resize(w, h);
gl.clear(gl.COLOR_BUFFER_BIT);
}
toCanvas(w, h) {
const destinationCanvas = document.createElement('canvas');
destinationCanvas.width = w;
destinationCanvas.height = h;
const ctx = destinationCanvas.getContext('2d');
ctx.drawImage(element, 0, 0, w, h);
return destinationCanvas
}
save() {
this.backup = {
texture: this.toTexture(),
width: element.width,
height: element.height
};
return this;
}
restore() {
if (!this.backup) throw 'nothing to restore';
var w = this.backup.width;
var h = this.backup.height;
resize(w, h);
this.drawTexture(this.backup.texture, 0, 0, w, h);
this.backup = null;
return this;
}
drawBuffer(vertices) {
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 2);
return this;
}
transform(texture, tx, ty, repeat) {
var generator = new TransformGenerator(tx, ty, repeat);
var rects = generator.getRects(this.w, this.h);
this.drawTexture(texture, rects);
return this;
}
drawRect(x, y, w, h) {
this.drawBuffer([x, y,
x + w, y,
x + w, y + h,
x + w, y + h,
x, y + h,
x, y]);
return this;
}
drawRadialGradient(color1, color2) {
useProgram(programs.radialGradient);
gl.uniform3fv(gl.getUniformLocation(programs.radialGradient, 'color1'), color1);
gl.uniform3fv(gl.getUniformLocation(programs.radialGradient, 'color2'), color2);
this.drawRect(0, 0, this.w, this.h);
useProgram(programs.simple);
return this;
}
drawBricks(count, margin) {
this.fillStyle([0, 0, 0, 1]);
this.drawRect(0, 0, this.w, this.h);
this.fillStyle([1, 1, 1, 1]);
var generator = new BrickGenerator(count, margin);
var bricks = generator.getBricks(this.w, this.h);
bricks.forEach(r => this.drawRect(...r));
return this;
}
drawCircle(r) {
useProgram(programs.circle);
gl.uniform1f(gl.getUniformLocation(programs.circle, 'r'), r);
this.drawRect(0, 0, this.w, this.h);
useProgram(programs.simple);
return this;
}
drawTexture(texture, x, y, w, h, params = [], uvs = null) {
/* 1__2
6|\ |
| \|3
5 4
*/
uvs = uvs || [0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1];
useProgram(programs.image);
var uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvs), gl.STATIC_DRAW);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.bindTexture(gl.TEXTURE_2D, texture);
params.forEach(param => {
gl.texParameteri(gl.TEXTURE_2D, param[0], param[1]);
});
if (x instanceof Array)
x.forEach(r => this.drawRect(...r));
else
this.drawRect(x, y, w, h);
gl.disableVertexAttribArray(1);
useProgram(programs.simple);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.deleteBuffer(uvBuffer);
return this;
}
cropTexture(texture, [origw, origh], x, y, w, h) {
resize(w, h);
this.drawTexture(texture, -x, -y, origw, origh);
}
fillStyle(color) {
var colorLoc = gl.getUniformLocation(programs.simple, 'color');
gl.uniform4fv(colorLoc, color);
return this;
}
noise() {
useProgram(programs.noise);
gl.uniform1f(gl.getUniformLocation(programs.noise, 'seed'), Math.random());
this.drawBuffer([-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]);
useProgram(programs.simple);
return this;
}
blur(texture, iterations) {
iterations = Math.min(iterations, 100);
var kernel = new Array(9).fill(1 / 9);
var currentTexture = texture;
for (var i = 0; i < iterations; i++) {
this.convolution(currentTexture, kernel);
if (i > 0)
disposeTexture(currentTexture);
if (i + 1 < iterations)
currentTexture = this.toTexture();
}
return this;
}
convolution(texture, matrix) {
if (!(gl instanceof WebGL2RenderingContext)) throw new Error('Supported only in webgl 2');
useProgram(programs.convolution);
gl.bindTexture(gl.TEXTURE_2D, texture);
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.uniform1i(gl.getUniformLocation(programs.convolution, 'texture'), texture);
gl.uniform1fv(gl.getUniformLocation(programs.convolution, 'matrix'),
new Float32Array(matrix));
this.drawBuffer([-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]);
useProgram(programs.simple);
return this;
}
blend(texture, b, expression, preExpressions = '') {
var blendProgram = shaderManager.createShaderRuntime('blend', b, expression, preExpressions);
useProgram(blendProgram);
if (b instanceof WebGLTexture) {
var tex2 = b;
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, tex2);
gl.uniform1i(gl.getUniformLocation(blendProgram, 'texture1'), 0);
gl.uniform1i(gl.getUniformLocation(blendProgram, 'texture2'), 1);
} else if (b instanceof Array) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(blendProgram, 'texture1'), 0);
gl.uniform3fv(gl.getUniformLocation(blendProgram, 'color'), b);
} else {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(blendProgram, 'texture1'), 0);
gl.uniform1f(gl.getUniformLocation(blendProgram, 'value'), b);
}
this.drawBuffer([-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]);
useProgram(programs.simple);
gl.activeTexture(gl.TEXTURE0);
return this;
}
neighbors(texture, expression) {
var neighborsProgram = shaderManager.createShaderRuntime('neighbors', expression);
useProgram(neighborsProgram);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(neighborsProgram, 'tex'), 0);
this.drawBuffer([-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]);
useProgram(programs.simple);
}
drawFourierSpectrum(texture) {
this.blend(texture, element.width * element.height, 'vec3(a.z/sqrt(b+a.z*a.z))');
var uv =
[
-0.5, 0.5,
0.5, 0.5,
0.5, -0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5
];
var params = [[gl.TEXTURE_WRAP_T, gl.REPEAT], [gl.TEXTURE_WRAP_S, gl.REPEAT]];
texture = this.toTexture();
this.drawTexture(texture, 0, 0, element.width, element.height, params, uv);
gl.deleteTexture(texture);
return this;
}
fourierFilter(texture, mask) {
var fb = new FloatFrameBuffer(gl, element.width, element.height);
if (mask instanceof WebGLTexture) { /// shift mask
var uv =
[
-0.5, 0.5,
0.5, 0.5,
0.5, -0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5
];
var params = [[gl.TEXTURE_WRAP_T, gl.REPEAT], [gl.TEXTURE_WRAP_S, gl.REPEAT]];
this.drawTexture(mask, 0, 0, element.width, element.height, params, uv);
gl.deleteTexture(mask);
mask = this.toTexture();
}
this.blend(texture, mask, 'vec3(a.rg*b.r,a.b)');
return fb.result();
}
fourierTransform(texture, inverse = false) {
var fb = new FloatFrameBuffer(gl, element.width, element.height);
var fourier = shaderManager.createShaderRuntime('fourier', this.w, this.h, inverse);
useProgram(fourier);
gl.bindTexture(gl.TEXTURE_2D, texture);
this.drawRect(0, 0, this.w, this.h);
return fb.result();
}
normalMap(texture, scale) {
useProgram(programs.normal);
gl.uniform1f(gl.getUniformLocation(programs.normal, 'scale'), scale);
gl.bindTexture(gl.TEXTURE_2D, texture);
this.drawBuffer([-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]);
useProgram(programs.simple);
return this;
}
toTexture() {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, format, element.width, element.height, 0, format, gl.UNSIGNED_BYTE, null);
gl.copyTexImage2D(gl.TEXTURE_2D, 0, format, 0, 0, element.width, element.height, 0);
textures.push(texture);
return texture;
}
toSrc() {
return element.toDataURL('image/png');
}
async toSrcAsync() {
return new Promise(resolve => {
element.toBlob((blob) => {
resolve(URL.createObjectURL(blob));
}, 'image/png');
});
}
async toImage() {
var canv = this;
return new Promise(function (resolve) {
var img = new Image();
img.onload = () => {
resolve(img);
};
img.src = canv.toSrc();
});
}
toImageSync() {
var img = new Image();
img.src = this.toSrc();
return img;
}
}
function disposeTexture(texture) {
var index = textures.indexOf(texture);
if (index !== -1)
textures.splice(index, 1);
gl.deleteTexture(texture);
}
function disposeTextures() {
textures.forEach(disposeTexture);
}
function getGL() {
return gl;
}
function createShader(vertex, fragment) {
return shaderManager.createShaderProgram({ vertex, fragment });
}
function initGL(contextName = 'webgl', params = {}) {
if (gl) return gl;
params = Object.assign({
alpha: false,
antialias: false,
depth: false
}, params);
element = document.createElement('canvas');
gl = element.getContext(contextName, params);
format = params.alpha ? gl.RGBA : gl.RGB;
shaderManager = new ShaderManager(gl);
programs = shaderManager.createPrecompiledShaders();
vertexBuffer = createBuffer();
return gl;
}
function createBuffer() {
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var posAttr = gl.getAttribLocation(programs.simple, 'position');
gl.enableVertexAttribArray(posAttr);
gl.vertexAttribPointer(posAttr, 2, gl.FLOAT, false, 0, 0);
return buffer;
}
function resize(w, h) {
element.width = w;
element.height = h;
gl.viewport(0, 0, w, h);
useProgram(programs.simple);
}
function loadTexture(img) {
if (!(img instanceof Image)) throw 'argument should be instance of Image';
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, format, format, gl.UNSIGNED_BYTE, img);
gl.bindTexture(gl.TEXTURE_2D, null);
return tex;
}
function useProgram(program) {
gl.useProgram(program);
var resolLoc = gl.getUniformLocation(program, 'resolution');
gl.uniform2fv(resolLoc, [element.width, element.height]);
}
function clearCache() {
if (shaderManager) {
shaderManager.clear();
}
}
exports.Canvas = Canvas;
exports.disposeTexture = disposeTexture;
exports.disposeTextures = disposeTextures;
exports.getGL = getGL;
exports.createShader = createShader;
exports.initGL = initGL;
exports.createBuffer = createBuffer;
exports.resize = resize;
exports.loadTexture = loadTexture;
exports.useProgram = useProgram;
exports.clearCache = clearCache;
return exports;
}({}));
//# sourceMappingURL=texturity.js.map