2021-09-25 17:51:15 +02:00
/ * *
* Image Filters in WebGL algoritm implementation
* Based on : [ WebGLImageFilter ] ( https : //github.com/phoboslab/WebGLImageFilter)
* /
2020-10-18 18:12:09 +02:00
2022-08-21 19:34:51 +02:00
/* eslint-disable func-names */
2021-10-12 15:48:00 +02:00
import * as shaders from './imagefxshaders' ;
2021-10-21 16:26:44 +02:00
import { canvas } from './image' ;
2021-11-14 17:22:52 +01:00
import { log } from '../util/util' ;
2021-10-12 15:48:00 +02:00
2022-08-21 21:23:03 +02:00
const collect = ( source , prefix : string , collection ) = > {
2021-10-12 20:17:33 +02:00
const r = new RegExp ( '\\b' + prefix + ' \\w+ (\\w+)' , 'ig' ) ;
source . replace ( r , ( match , name ) = > {
collection [ name ] = 0 ;
return match ;
} ) ;
} ;
2021-10-12 15:48:00 +02:00
class GLProgram {
uniform = { } ;
attribute = { } ;
gl : WebGLRenderingContext ;
id : WebGLProgram ;
2021-11-14 17:22:52 +01:00
2021-10-12 15:48:00 +02:00
constructor ( gl , vertexSource , fragmentSource ) {
this . gl = gl ;
2021-10-12 17:39:18 +02:00
const vertexShader = this . compile ( vertexSource , this . gl . VERTEX_SHADER ) ;
const fragmentShader = this . compile ( fragmentSource , this . gl . FRAGMENT_SHADER ) ;
2025-02-05 15:29:47 +01:00
this . id = this . gl . createProgram ( ) ;
2021-11-14 17:22:52 +01:00
if ( ! vertexShader || ! fragmentShader ) return ;
if ( ! this . id ) {
log ( 'filter: could not create webgl program' ) ;
return ;
}
2021-10-12 17:39:18 +02:00
this . gl . attachShader ( this . id , vertexShader ) ;
this . gl . attachShader ( this . id , fragmentShader ) ;
2021-10-12 15:48:00 +02:00
this . gl . linkProgram ( this . id ) ;
2021-11-14 17:22:52 +01:00
if ( ! this . gl . getProgramParameter ( this . id , this . gl . LINK_STATUS ) ) {
2022-08-21 21:23:03 +02:00
log ( ` filter: gl link failed: ${ this . gl . getProgramInfoLog ( this . id ) || 'unknown' } ` ) ;
2021-11-14 17:22:52 +01:00
return ;
}
2021-10-12 15:48:00 +02:00
this . gl . useProgram ( this . id ) ;
2021-10-12 20:17:33 +02:00
collect ( vertexSource , 'attribute' , this . attribute ) ; // Collect attributes
2021-10-12 15:48:00 +02:00
for ( const a in this . attribute ) this . attribute [ a ] = this . gl . getAttribLocation ( this . id , a ) ;
2021-10-12 20:17:33 +02:00
collect ( vertexSource , 'uniform' , this . uniform ) ; // Collect uniforms
collect ( fragmentSource , 'uniform' , this . uniform ) ;
2021-10-12 15:48:00 +02:00
for ( const u in this . uniform ) this . uniform [ u ] = this . gl . getUniformLocation ( this . id , u ) ;
}
2021-11-14 17:22:52 +01:00
compile = ( source , type ) : WebGLShader | null = > {
2022-08-21 19:34:51 +02:00
const shader = this . gl . createShader ( type ) ;
2021-11-14 17:22:52 +01:00
if ( ! shader ) {
log ( 'filter: could not create shader' ) ;
return null ;
}
2021-10-12 15:48:00 +02:00
this . gl . shaderSource ( shader , source ) ;
this . gl . compileShader ( shader ) ;
2021-11-14 17:22:52 +01:00
if ( ! this . gl . getShaderParameter ( shader , this . gl . COMPILE_STATUS ) ) {
2022-08-21 21:23:03 +02:00
log ( ` filter: gl compile failed: ${ this . gl . getShaderInfoLog ( shader ) || 'unknown' } ` ) ;
2021-11-14 17:22:52 +01:00
return null ;
}
2020-10-18 18:12:09 +02:00
return shader ;
} ;
2021-02-19 14:35:41 +01:00
}
2020-10-18 18:12:09 +02:00
2021-10-12 17:39:18 +02:00
// function that is instantiated as class so it has private this members
/ * *
* @class GLImageFilter
* @property { function } reset reset current filter chain
* @property { function } add add specified filter to filter chain
* @property { function } apply execute filter chain and draw result
* @property { function } draw just draw input to result
* /
2021-10-21 16:26:44 +02:00
export function GLImageFilter() {
2021-10-12 17:39:18 +02:00
let drawCount = 0 ;
let sourceTexture : WebGLTexture | null = null ;
let lastInChain = false ;
let currentFramebufferIndex = - 1 ;
let tempFramebuffers : [ null , null ] | [ { fbo : WebGLFramebuffer | null , texture : WebGLTexture | null } ] = [ null , null ] ;
let filterChain : Record < string , unknown > [ ] = [ ] ;
let vertexBuffer : WebGLBuffer | null = null ;
let currentProgram : GLProgram | null = null ;
2022-11-11 22:19:27 +01:00
const fxcanvas = canvas ( 100 , 100 ) as HTMLCanvasElement ;
2021-10-12 17:39:18 +02:00
const shaderProgramCache = { } ; // key is the shader program source, value is the compiled program
2021-02-19 14:35:41 +01:00
const DRAW = { INTERMEDIATE : 1 } ;
2021-10-21 16:26:44 +02:00
const gl = fxcanvas . getContext ( 'webgl' ) as WebGLRenderingContext ;
2021-11-14 17:22:52 +01:00
if ( ! gl ) {
log ( 'filter: cannot get webgl context' ) ;
return ;
}
2022-04-11 17:45:24 +02:00
// @ts-ignore used for sanity checks outside of imagefx
this . gl = gl ;
2020-10-18 18:12:09 +02:00
2021-10-12 17:39:18 +02:00
function resize ( width , height ) {
2021-10-21 16:26:44 +02:00
if ( width === fxcanvas . width && height === fxcanvas . height ) return ; // Same width/height? Nothing to do here
fxcanvas . width = width ;
fxcanvas . height = height ;
2021-10-12 17:39:18 +02:00
if ( ! vertexBuffer ) { // Create the context if we don't have it yet
2021-10-12 15:48:00 +02:00
const vertices = new Float32Array ( [ - 1 , - 1 , 0 , 1 , 1 , - 1 , 1 , 1 , - 1 , 1 , 0 , 0 , - 1 , 1 , 0 , 0 , 1 , - 1 , 1 , 1 , 1 , 1 , 1 , 0 ] ) ; // Create the vertex buffer for the two triangles [x, y, u, v] * 6
2021-10-12 17:39:18 +02:00
vertexBuffer = gl . createBuffer ( ) ;
gl . bindBuffer ( gl . ARRAY_BUFFER , vertexBuffer ) ;
2020-10-18 18:12:09 +02:00
gl . bufferData ( gl . ARRAY_BUFFER , vertices , gl . STATIC_DRAW ) ;
gl . pixelStorei ( gl . UNPACK_PREMULTIPLY_ALPHA_WEBGL , true ) ;
}
2021-10-21 16:26:44 +02:00
gl . viewport ( 0 , 0 , fxcanvas . width , fxcanvas . height ) ;
2021-10-12 17:39:18 +02:00
tempFramebuffers = [ null , null ] ; // Delete old temp framebuffers
}
2020-10-18 18:12:09 +02:00
2021-10-12 17:39:18 +02:00
function createFramebufferTexture ( width , height ) {
2022-08-21 19:34:51 +02:00
const fbo = gl . createFramebuffer ( ) ;
2020-10-18 18:12:09 +02:00
gl . bindFramebuffer ( gl . FRAMEBUFFER , fbo ) ;
const renderbuffer = gl . createRenderbuffer ( ) ;
gl . bindRenderbuffer ( gl . RENDERBUFFER , renderbuffer ) ;
2022-08-21 19:34:51 +02:00
const texture = gl . createTexture ( ) ;
2020-10-18 18:12:09 +02:00
gl . bindTexture ( gl . TEXTURE_2D , texture ) ;
gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , width , height , 0 , gl . RGBA , gl . UNSIGNED_BYTE , null ) ;
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 . framebufferTexture2D ( gl . FRAMEBUFFER , gl . COLOR_ATTACHMENT0 , gl . TEXTURE_2D , texture , 0 ) ;
gl . bindTexture ( gl . TEXTURE_2D , null ) ;
gl . bindFramebuffer ( gl . FRAMEBUFFER , null ) ;
return { fbo , texture } ;
2021-10-12 17:39:18 +02:00
}
2020-10-18 18:12:09 +02:00
2021-11-14 17:22:52 +01:00
function getTempFramebuffer ( index ) : { fbo : WebGLFramebuffer | null , texture : WebGLTexture | null } {
2021-10-21 16:26:44 +02:00
tempFramebuffers [ index ] = tempFramebuffers [ index ] || createFramebufferTexture ( fxcanvas . width , fxcanvas . height ) ;
2021-11-04 11:34:13 +01:00
return tempFramebuffers [ index ] as { fbo : WebGLFramebuffer , texture : WebGLTexture } ;
2021-10-12 17:39:18 +02:00
}
2021-02-19 14:35:41 +01:00
2021-10-12 17:39:18 +02:00
function draw ( flags = 0 ) {
if ( ! currentProgram ) return ;
let source : WebGLTexture | null = null ;
let target : WebGLFramebuffer | null = null ;
2020-10-18 18:12:09 +02:00
let flipY = false ;
2021-10-12 17:39:18 +02:00
if ( drawCount === 0 ) source = sourceTexture ; // First draw call - use the source texture
2021-11-04 11:34:13 +01:00
else source = getTempFramebuffer ( currentFramebufferIndex ) . texture || null ; // All following draw calls use the temp buffer last drawn to
2021-10-12 17:39:18 +02:00
drawCount ++ ;
if ( lastInChain && ! ( flags & DRAW . INTERMEDIATE ) ) { // Last filter in our chain - draw directly to the WebGL Canvas. We may also have to flip the image vertically now
2020-10-18 18:12:09 +02:00
target = null ;
2021-10-12 17:39:18 +02:00
flipY = drawCount % 2 === 0 ;
2020-10-18 18:12:09 +02:00
} else {
2021-10-12 17:39:18 +02:00
currentFramebufferIndex = ( currentFramebufferIndex + 1 ) % 2 ;
2021-11-04 11:34:13 +01:00
target = getTempFramebuffer ( currentFramebufferIndex ) . fbo || null ; // Intermediate draw call - get a temp buffer to draw to
2020-10-18 18:12:09 +02:00
}
2021-10-12 15:48:00 +02:00
gl . bindTexture ( gl . TEXTURE_2D , source ) ; // Bind the source and target and draw the two triangles
2020-10-18 18:12:09 +02:00
gl . bindFramebuffer ( gl . FRAMEBUFFER , target ) ;
2021-10-12 17:39:18 +02:00
gl . uniform1f ( currentProgram . uniform [ 'flipY' ] , ( flipY ? - 1 : 1 ) ) ;
2020-10-18 18:12:09 +02:00
gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
2021-10-12 17:39:18 +02:00
}
2021-02-19 14:35:41 +01:00
2021-11-14 17:22:52 +01:00
function compileShader ( fragmentSource ) : GLProgram | null {
2021-10-12 17:39:18 +02:00
if ( shaderProgramCache [ fragmentSource ] ) {
currentProgram = shaderProgramCache [ fragmentSource ] ;
2021-11-04 11:34:13 +01:00
gl . useProgram ( ( currentProgram ? currentProgram.id : null ) || null ) ;
2022-08-21 19:34:51 +02:00
return currentProgram ;
2020-10-18 18:12:09 +02:00
}
2021-10-12 17:39:18 +02:00
currentProgram = new GLProgram ( gl , shaders . vertexIdentity , fragmentSource ) ;
2021-11-14 17:22:52 +01:00
if ( ! currentProgram ) {
log ( 'filter: could not get webgl program' ) ;
return null ;
}
2020-10-18 18:12:09 +02:00
const floatSize = Float32Array . BYTES_PER_ELEMENT ;
const vertSize = 4 * floatSize ;
2021-10-12 17:39:18 +02:00
gl . enableVertexAttribArray ( currentProgram . attribute [ 'pos' ] ) ;
gl . vertexAttribPointer ( currentProgram . attribute [ 'pos' ] , 2 , gl . FLOAT , false , vertSize , 0 * floatSize ) ;
gl . enableVertexAttribArray ( currentProgram . attribute [ 'uv' ] ) ;
gl . vertexAttribPointer ( currentProgram . attribute [ 'uv' ] , 2 , gl . FLOAT , false , vertSize , 2 * floatSize ) ;
shaderProgramCache [ fragmentSource ] = currentProgram ;
2022-08-21 19:34:51 +02:00
return currentProgram ;
2021-10-12 17:39:18 +02:00
}
2020-10-18 18:12:09 +02:00
2021-10-12 17:39:18 +02:00
const filter = {
2022-08-21 21:23:03 +02:00
colorMatrix : ( matrix : number [ ] ) = > { // general color matrix filter
2021-10-12 17:39:18 +02:00
const m = new Float32Array ( matrix ) ;
2021-10-12 15:48:00 +02:00
m [ 4 ] /= 255 ;
m [ 9 ] /= 255 ;
m [ 14 ] /= 255 ;
m [ 19 ] /= 255 ;
const shader = ( m [ 18 ] === 1 && m [ 3 ] === 0 && m [ 8 ] === 0 && m [ 13 ] === 0 && m [ 15 ] === 0 && m [ 16 ] === 0 && m [ 17 ] === 0 && m [ 19 ] === 0 ) // Can we ignore the alpha value? Makes things a bit faster.
? shaders . colorMatrixWithoutAlpha
: shaders . colorMatrixWithAlpha ;
2021-10-12 17:39:18 +02:00
const program = compileShader ( shader ) ;
2021-11-14 17:22:52 +01:00
if ( ! program ) return ;
2021-11-04 11:34:13 +01:00
gl . uniform1fv ( program . uniform [ 'm' ] , m ) ;
2021-10-12 17:39:18 +02:00
draw ( ) ;
2021-10-12 15:48:00 +02:00
} ,
2022-08-21 21:23:03 +02:00
brightness : ( brightness : number ) = > {
2021-10-12 15:48:00 +02:00
const b = ( brightness || 0 ) + 1 ;
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
b , 0 , 0 , 0 , 0 ,
0 , b , 0 , 0 , 0 ,
0 , 0 , b , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
2022-08-21 21:23:03 +02:00
saturation : ( amount : number ) = > {
2021-10-12 15:48:00 +02:00
const x = ( amount || 0 ) * 2 / 3 + 1 ;
const y = ( ( x - 1 ) * - 0.5 ) ;
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
x , y , y , 0 , 0 ,
y , x , y , 0 , 0 ,
y , y , x , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
desaturate : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . saturation ( - 1 ) ;
2021-10-12 15:48:00 +02:00
} ,
2022-08-21 21:23:03 +02:00
contrast : ( amount : number ) = > {
2021-10-12 15:48:00 +02:00
const v = ( amount || 0 ) + 1 ;
const o = - 128 * ( v - 1 ) ;
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
v , 0 , 0 , 0 , o ,
0 , v , 0 , 0 , o ,
0 , 0 , v , 0 , o ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
negative : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . contrast ( - 2 ) ;
2021-10-12 15:48:00 +02:00
} ,
2022-08-21 21:23:03 +02:00
hue : ( rotation : number ) = > {
2021-10-12 15:48:00 +02:00
rotation = ( rotation || 0 ) / 180 * Math . PI ;
const cos = Math . cos ( rotation ) ;
const sin = Math . sin ( rotation ) ;
const lumR = 0.213 ;
const lumG = 0.715 ;
const lumB = 0.072 ;
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
lumR + cos * ( 1 - lumR ) + sin * ( - lumR ) , lumG + cos * ( - lumG ) + sin * ( - lumG ) , lumB + cos * ( - lumB ) + sin * ( 1 - lumB ) , 0 , 0 ,
lumR + cos * ( - lumR ) + sin * ( 0.143 ) , lumG + cos * ( 1 - lumG ) + sin * ( 0.140 ) , lumB + cos * ( - lumB ) + sin * ( - 0.283 ) , 0 , 0 ,
lumR + cos * ( - lumR ) + sin * ( - ( 1 - lumR ) ) , lumG + cos * ( - lumG ) + sin * ( lumG ) , lumB + cos * ( 1 - lumB ) + sin * ( lumB ) , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
desaturateLuminance : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
0.2764723 , 0.9297080 , 0.0938197 , 0 , - 37.1 ,
0.2764723 , 0.9297080 , 0.0938197 , 0 , - 37.1 ,
0.2764723 , 0.9297080 , 0.0938197 , 0 , - 37.1 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
sepia : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
0.393 , 0.7689999 , 0.18899999 , 0 , 0 ,
0.349 , 0.6859999 , 0.16799999 , 0 , 0 ,
0.272 , 0.5339999 , 0.13099999 , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
brownie : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
0.5997023498159715 , 0.34553243048391263 , - 0.2708298674538042 , 0 , 47.43192855600873 ,
- 0.037703249837783157 , 0.8609577587992641 , 0.15059552388459913 , 0 , - 36.96841498319127 ,
0.24113635128153335 , - 0.07441037908422492 , 0.44972182064877153 , 0 , - 7.562075277591283 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
vintagePinhole : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
0.6279345635605994 , 0.3202183420819367 , - 0.03965408211312453 , 0 , 9.651285835294123 ,
0.02578397704808868 , 0.6441188644374771 , 0.03259127616149294 , 0 , 7.462829176470591 ,
0.0466055556782719 , - 0.0851232987247891 , 0.5241648018700465 , 0 , 5.159190588235296 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
kodachrome : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
1.1285582396593525 , - 0.3967382283601348 , - 0.03992559172921793 , 0 , 63.72958762196502 ,
- 0.16404339962244616 , 1.0835251566291304 , - 0.05498805115633132 , 0 , 24.732407896706203 ,
- 0.16786010706155763 , - 0.5603416277695248 , 1.6014850761964943 , 0 , 35.62982807460946 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
technicolor : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
1.9125277891456083 , - 0.8545344976951645 , - 0.09155508482755585 , 0 , 11.793603434377337 ,
- 0.3087833385928097 , 1.7658908555458428 , - 0.10601743074722245 , 0 , - 70.35205161461398 ,
- 0.231103377548616 , - 0.7501899197440212 , 1.847597816108189 , 0 , 30.950940869491138 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
polaroid : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
1.438 , - 0.062 , - 0.062 , 0 , 0 ,
- 0.122 , 1.378 , - 0.122 , 0 , 0 ,
- 0.016 , - 0.016 , 1.483 , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
shiftToBGR : ( ) = > {
2021-10-12 17:39:18 +02:00
filter . colorMatrix ( [
2021-10-12 15:48:00 +02:00
0 , 0 , 1 , 0 , 0 ,
0 , 1 , 0 , 0 , 0 ,
1 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 1 , 0 ,
] ) ;
} ,
2022-08-21 21:23:03 +02:00
convolution : ( matrix : number [ ] ) = > { // general convolution Filter
2021-10-12 15:48:00 +02:00
const m = new Float32Array ( matrix ) ;
2021-10-21 16:26:44 +02:00
const pixelSizeX = 1 / fxcanvas . width ;
const pixelSizeY = 1 / fxcanvas . height ;
2021-10-12 17:39:18 +02:00
const program = compileShader ( shaders . convolution ) ;
2021-11-14 17:22:52 +01:00
if ( ! program ) return ;
2021-11-04 11:34:13 +01:00
gl . uniform1fv ( program . uniform [ 'm' ] , m ) ;
gl . uniform2f ( program . uniform [ 'px' ] , pixelSizeX , pixelSizeY ) ;
2021-10-12 17:39:18 +02:00
draw ( ) ;
2021-10-12 15:48:00 +02:00
} ,
2020-10-18 18:12:09 +02:00
2021-10-12 15:48:00 +02:00
detectEdges : ( ) = > {
2021-10-12 17:39:18 +02:00
// @ts-ignore this
filter . convolution . call ( this , [
2021-10-12 15:48:00 +02:00
0 , 1 , 0 ,
1 , - 4 , 1 ,
0 , 1 , 0 ,
] ) ;
} ,
2020-10-18 18:12:09 +02:00
2021-10-12 15:48:00 +02:00
sobelX : ( ) = > {
2021-10-12 17:39:18 +02:00
// @ts-ignore this
filter . convolution . call ( this , [
2021-10-12 15:48:00 +02:00
- 1 , 0 , 1 ,
- 2 , 0 , 2 ,
- 1 , 0 , 1 ,
] ) ;
} ,
2020-10-18 18:12:09 +02:00
2021-10-12 15:48:00 +02:00
sobelY : ( ) = > {
2021-10-12 17:39:18 +02:00
// @ts-ignore this
filter . convolution . call ( this , [
2021-10-12 15:48:00 +02:00
- 1 , - 2 , - 1 ,
0 , 0 , 0 ,
1 , 2 , 1 ,
] ) ;
} ,
sharpen : ( amount ) = > {
const a = amount || 1 ;
2021-10-12 17:39:18 +02:00
// @ts-ignore this
filter . convolution . call ( this , [
2021-10-12 15:48:00 +02:00
0 , - 1 * a , 0 ,
- 1 * a , 1 + 4 * a , - 1 * a ,
0 , - 1 * a , 0 ,
] ) ;
} ,
2022-08-21 21:23:03 +02:00
emboss : ( size : number ) = > {
2021-10-12 15:48:00 +02:00
const s = size || 1 ;
2021-10-12 17:39:18 +02:00
// @ts-ignore this
filter . convolution . call ( this , [
2021-10-12 15:48:00 +02:00
- 2 * s , - 1 * s , 0 ,
- 1 * s , 1 , 1 * s ,
0 , 1 * s , 2 * s ,
] ) ;
} ,
2022-08-21 21:23:03 +02:00
blur : ( size : number ) = > {
2021-10-21 16:26:44 +02:00
const blurSizeX = ( size / 7 ) / fxcanvas . width ;
const blurSizeY = ( size / 7 ) / fxcanvas . height ;
2021-10-12 17:39:18 +02:00
const program = compileShader ( shaders . blur ) ;
2021-11-14 17:22:52 +01:00
if ( ! program ) return ;
2021-10-12 15:48:00 +02:00
// Vertical
2021-11-04 11:34:13 +01:00
gl . uniform2f ( program . uniform [ 'px' ] , 0 , blurSizeY ) ;
2021-10-12 17:39:18 +02:00
draw ( DRAW . INTERMEDIATE ) ;
2021-10-12 15:48:00 +02:00
// Horizontal
2021-11-04 11:34:13 +01:00
gl . uniform2f ( program . uniform [ 'px' ] , blurSizeX , 0 ) ;
2021-10-12 17:39:18 +02:00
draw ( ) ;
2021-10-12 15:48:00 +02:00
} ,
2022-08-21 21:23:03 +02:00
pixelate : ( size : number ) = > {
2021-10-21 16:26:44 +02:00
const blurSizeX = ( size ) / fxcanvas . width ;
const blurSizeY = ( size ) / fxcanvas . height ;
2021-10-12 17:39:18 +02:00
const program = compileShader ( shaders . pixelate ) ;
2021-11-14 17:22:52 +01:00
if ( ! program ) return ;
2021-11-04 11:34:13 +01:00
gl . uniform2f ( program . uniform [ 'size' ] , blurSizeX , blurSizeY ) ;
2021-10-12 17:39:18 +02:00
draw ( ) ;
2021-10-12 15:48:00 +02:00
} ,
2020-10-18 18:12:09 +02:00
} ;
2021-10-12 17:39:18 +02:00
// @ts-ignore this
this . add = function ( name ) {
2022-08-21 19:34:51 +02:00
const args = Array . prototype . slice . call ( arguments , 1 ) ; // eslint-disable-line prefer-rest-params
2021-10-12 17:39:18 +02:00
const func = filter [ name ] ;
filterChain . push ( { func , args } ) ;
} ;
// @ts-ignore this
this . reset = function ( ) {
filterChain = [ ] ;
} ;
// @ts-ignore this
this . get = function ( ) {
return filterChain ;
} ;
// @ts-ignore this
this . apply = function ( image ) {
resize ( image . width , image . height ) ;
drawCount = 0 ;
if ( ! sourceTexture ) sourceTexture = gl . createTexture ( ) ; // Create the texture for the input image if we haven't yet
gl . bindTexture ( gl . TEXTURE_2D , sourceTexture ) ;
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 . NEAREST ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MAG_FILTER , gl . NEAREST ) ;
gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , image ) ;
for ( let i = 0 ; i < filterChain . length ; i ++ ) {
lastInChain = ( i === filterChain . length - 1 ) ;
const f = filterChain [ i ] ;
// @ts-ignore function assigment
f . func . apply ( this , f . args || [ ] ) ;
}
2021-10-21 16:26:44 +02:00
return fxcanvas ;
2021-10-12 17:39:18 +02:00
} ;
// @ts-ignore this
this . draw = function ( image ) {
this . add ( 'brightness' , 0 ) ;
return this . apply ( image ) ;
} ;
2021-02-19 14:35:41 +01:00
}