diff --git a/src/Modules.js b/src/Modules.js index 6a00ab6d..01536980 100644 --- a/src/Modules.js +++ b/src/Modules.js @@ -11,6 +11,7 @@ module.exports = { 'canvas-resize': require('./modules/CanvasResize'), 'channel': require('./modules/Channel'), 'colorbar': require('./modules/Colorbar'), + 'color-halftone': require('./modules/ColorHalftone'), 'color-temperature': require('./modules/ColorTemperature'), 'colormap': require('./modules/Colormap'), 'constrained-crop': require('./modules/ConstrainedCrop'), diff --git a/src/modules/ColorHalftone/Module.js b/src/modules/ColorHalftone/Module.js new file mode 100644 index 00000000..f6b7936a --- /dev/null +++ b/src/modules/ColorHalftone/Module.js @@ -0,0 +1,135 @@ +/* +* Generate halftone versions of CMYK channels and blend them with varying rotations as in analog print color separation processes. +* Simulates a CMYK halftone rendering of the image by multiplying pixel values with a four rotated 2D sine wave patterns, one each for cyan, magenta, yellow, and black. +* http://evanw.github.io/glfx.js/docs/#colorHalftone +*/ +module.exports = function ColorHalftone(options, UI) { + + var defaults = require('./../../util/getDefaults.js')(require('./info.json')); + + var output; + var fx = require('glfx'); + + var dataURItoBlob = function dataURItoBlob(dataURI) { + // convert base64/URLEncoded data component to raw binary data held in a string + var byteString; + if (dataURI.split(',')[0].indexOf('base64') >= 0) + byteString = atob(dataURI.split(',')[1]); + else + byteString = unescape(dataURI.split(',')[1]); + + // separate out the mime component + var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; + + // write the bytes of the string to a typed array + var ia = new Uint8Array(byteString.length); + for (var i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + + return new Blob([ia], { type: mimeString }); + }; + + var canvasToBlobUrl = function canvasToBlobUrl(canvas) { + + var blob = dataURItoBlob(canvas.toDataURL('image/png')); + return window.URL.createObjectURL(blob); + + }; + + var colorHalftone = function colorHalftone(id, options, download) { + + // try to create a WebGL canvas (will fail if WebGL isn't supported) + try { + var canvas = fx.canvas(1500, 1500); + } catch (e) { + alert(e); + return; + } + + // convert the image to a texture + var imageEl = document.getElementById(id); + + var image = new Image(); + + image.onload = function() { + + var texture = canvas.texture(image); + + canvas.draw(texture, + image.width, // * ratio, + image.height// * ratio + ); + + canvas.colorHalftone( + image.width / 2, + image.height / 2, + parseFloat(options.angle), + parseFloat(options.size) + ).update(); + + var burl = canvasToBlobUrl(canvas); + + if (download) { + + window.open(burl); + + } else { // replace the image + + // keep non-blob version in case we have to fall back: + // image.src = canvas.toDataURL('image/png'); + // window.location = canvas.toDataURL('image/png'); + imageEl.src = burl; + + } + + }; + + $(image).hide(); + image.src = imageEl.src; + + }; + + function draw(input, callback) { + + var step = this; + + options.angle = options.angle || defaults.angle; + options.size = options.size || defaults.size; + + if (!options.inBrowser) { + // this.output = input; + // callback(); + require('../_nomodule/gl-context')(input, callback, step, options); + } + else { + var image = document.createElement('img'); + image.onload = () => { + colorHalftone( + 'img', + options + ); + image.onload = () => { + var canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth; // or 'width' if you want a special/scaled size + canvas.height = image.naturalHeight; // or 'height' if you want a special/scaled size + canvas.getContext('2d').drawImage(image, 0, 0); + + step.output = { src: canvas.toDataURL('image/png'), format: 'png' }; + image.remove(); + callback(); + }; + }; + image.src = input.src; + image.id = 'img'; + document.body.appendChild(image); + } + } + + return { + options: options, + draw: draw, + output: output, + UI: UI + }; +}; diff --git a/src/modules/ColorHalftone/index.js b/src/modules/ColorHalftone/index.js new file mode 100644 index 00000000..71549002 --- /dev/null +++ b/src/modules/ColorHalftone/index.js @@ -0,0 +1,4 @@ +module.exports = [ + require('./Module'), + require('./info.json') +]; \ No newline at end of file diff --git a/src/modules/ColorHalftone/info.json b/src/modules/ColorHalftone/info.json new file mode 100644 index 00000000..dec0b183 --- /dev/null +++ b/src/modules/ColorHalftone/info.json @@ -0,0 +1,22 @@ +{ + "name": "color-halftone", + "requires": ["webgl"], + "description": "Generate halftone versions of CMYK channels and blend them with varying rotations as in analog print color separation processes.", + "inputs": { + "angle": { + "type": "float", + "desc": "angle of rotation of the halftone patterns in radians", + "default": "0.25", + "min": "0", + "max": "1.57" + }, + "size": { + "type": "integer", + "desc": "the diameter of a dot in pixels", + "default": "4", + "min": "3", + "max": "20" + } + }, + "docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#color-halftone-module" +} diff --git a/src/modules/WebglDistort/Module.js b/src/modules/WebglDistort/Module.js index 686edeb4..96420125 100644 --- a/src/modules/WebglDistort/Module.js +++ b/src/modules/WebglDistort/Module.js @@ -6,7 +6,7 @@ module.exports = function DoNothing(options, UI) { var defaults = require('./../../util/getDefaults.js')(require('./info.json')); var output; - var fx = require('./glfx'); + var fx = require('glfx'); var dataURItoBlob = function dataURItoBlob(dataURI) { // convert base64/URLEncoded data component to raw binary data held in a string diff --git a/src/modules/WebglDistort/glfx.js b/src/modules/WebglDistort/glfx.js deleted file mode 100644 index 8b9f3940..00000000 --- a/src/modules/WebglDistort/glfx.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * glfx.js - * http://evanw.github.com/glfx.js/ - * - * Copyright 2011 Evan Wallace - * Released under the MIT license - */ -module.exports = function() { - function q(a, d, c) { return Math.max(a, Math.min(d, c)); } function w(b) { return { _: b, loadContentsOf: function(b) { a = this._.gl; this._.loadContentsOf(b); }, destroy: function() { a = this._.gl; this._.destroy(); } }; } function A(a) { return w(r.fromElement(a)); } function B(b, d) { - var c = a.UNSIGNED_BYTE; if (a.getExtension('OES_texture_float') && a.getExtension('OES_texture_float_linear')) { var e = new r(100, 100, a.RGBA, a.FLOAT); try { e.drawTo(function() { c = a.FLOAT; }); } catch (g) { } e.destroy(); } this._.texture && this._.texture.destroy(); - this._.spareTexture && this._.spareTexture.destroy(); this.width = b; this.height = d; this._.texture = new r(b, d, a.RGBA, c); this._.spareTexture = new r(b, d, a.RGBA, c); this._.extraTexture = this._.extraTexture || new r(0, 0, a.RGBA, c); this._.flippedShader = this._.flippedShader || new h(null, 'uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}'); this._.isInitialized = !0; - } function C(a, d, c) { - this._.isInitialized && - a._.width == this.width && a._.height == this.height || B.call(this, d ? d : a._.width, c ? c : a._.height); a._.use(); this._.texture.drawTo(function() { h.getDefaultShader().drawRect(); }); return this; - } function D() { this._.texture.use(); this._.flippedShader.drawRect(); return this; } function f(a, d, c, e) { (c || this._.texture).use(); this._.spareTexture.drawTo(function() { a.uniforms(d).drawRect(); }); this._.spareTexture.swapWith(e || this._.texture); } function E(a) { a.parentNode.insertBefore(this, a); a.parentNode.removeChild(a); return this; } - function F() { var b = new r(this._.texture.width, this._.texture.height, a.RGBA, a.UNSIGNED_BYTE); this._.texture.use(); b.drawTo(function() { h.getDefaultShader().drawRect(); }); return w(b); } function G() { var b = this._.texture.width, d = this._.texture.height, c = new Uint8Array(4 * b * d); this._.texture.drawTo(function() { a.readPixels(0, 0, b, d, a.RGBA, a.UNSIGNED_BYTE, c); }); return c; } function k(b) { return function() { a = this._.gl; return b.apply(this, arguments); }; } function x(a, d, c, e, g, l, n, p) { - var m = c - g, h = e - l, f = n - g, k = p - l; g = a - c + g - n; l = - d - e + l - p; var q = m * k - f * h, f = (g * k - f * l) / q, m = (m * l - g * h) / q; return [c - a + f * c, e - d + f * e, f, n - a + m * n, p - d + m * p, m, a, d, 1]; - } function y(a) { var d = a[0], c = a[1], e = a[2], g = a[3], l = a[4], n = a[5], p = a[6], m = a[7]; a = a[8]; var f = d * l * a - d * n * m - c * g * a + c * n * p + e * g * m - e * l * p; return [(l * a - n * m) / f, (e * m - c * a) / f, (c * n - e * l) / f, (n * p - g * a) / f, (d * a - e * p) / f, (e * g - d * n) / f, (g * m - l * p) / f, (c * p - d * m) / f, (d * l - c * g) / f]; } function z(a) { - var d = a.length; this.xa = []; this.ya = []; this.u = []; this.y2 = []; a.sort(function(a, b) { return a[0] - b[0]; }); for (var c = 0; c < d; c++)this.xa.push(a[c][0]), this.ya.push(a[c][1]); - this.u[0] = 0; this.y2[0] = 0; for (c = 1; c < d - 1; ++c) { a = this.xa[c + 1] - this.xa[c - 1]; var e = (this.xa[c] - this.xa[c - 1]) / a, g = e * this.y2[c - 1] + 2; this.y2[c] = (e - 1) / g; this.u[c] = (6 * ((this.ya[c + 1] - this.ya[c]) / (this.xa[c + 1] - this.xa[c]) - (this.ya[c] - this.ya[c - 1]) / (this.xa[c] - this.xa[c - 1])) / a - e * this.u[c - 1]) / g; } this.y2[d - 1] = 0; for (c = d - 2; 0 <= c; --c)this.y2[c] = this.y2[c] * this.y2[c + 1] + this.u[c]; - } function u(a, d) { - return new h(null, a + 'uniform sampler2D texture;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 coord=texCoord*texSize;' + - d + 'gl_FragColor=texture2D(texture,coord/texSize);vec2 clampedCoord=clamp(coord,vec2(0.0),texSize);if(coord!=clampedCoord){gl_FragColor.a*=max(0.0,1.0-length(coord-clampedCoord));}}'); - } function H(b, d) { - a.brightnessContrast = a.brightnessContrast || new h(null, 'uniform sampler2D texture;uniform float brightness;uniform float contrast;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.rgb+=brightness;if(contrast>0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}'); - f.call(this, a.brightnessContrast, { brightness: q(-1, b, 1), contrast: q(-1, d, 1) }); return this; - } function t(a) { a = new z(a); for (var d = [], c = 0; 256 > c; c++)d.push(q(0, Math.floor(256 * a.interpolate(c / 255)), 255)); return d; } function I(b, d, c) { - b = t(b); 1 == arguments.length ? d = c = b : (d = t(d), c = t(c)); for (var e = [], g = 0; 256 > g; g++)e.splice(e.length, 0, b[g], d[g], c[g], 255); this._.extraTexture.initFromBytes(256, 1, e); this._.extraTexture.use(1); a.curves = a.curves || new h(null, 'uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}'); - a.curves.textures({ map: 1 }); f.call(this, a.curves, {}); return this; - } function J(b) { - a.denoise = a.denoise || new h(null, 'uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}'); - for (var d = 0; 2 > d; d++)f.call(this, a.denoise, { exponent: Math.max(0, b), texSize: [this.width, this.height] }); return this; - } function K(b, d) { - a.hueSaturation = a.hueSaturation || new h(null, 'uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}'); - f.call(this, a.hueSaturation, { hue: q(-1, b, 1), saturation: q(-1, d, 1) }); return this; - } function L(b) { - a.noise = a.noise || new h(null, 'uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}'); - f.call(this, a.noise, { amount: q(0, b, 1) }); return this; - } function M(b) { - a.sepia = a.sepia || new h(null, 'uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}'); - f.call(this, a.sepia, { amount: q(0, b, 1) }); return this; - } function N(b, d) { - a.unsharpMask = a.unsharpMask || new h(null, 'uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}'); - this._.extraTexture.ensureFormat(this._.texture); this._.texture.use(); this._.extraTexture.drawTo(function() { h.getDefaultShader().drawRect(); }); this._.extraTexture.use(1); this.triangleBlur(b); a.unsharpMask.textures({ originalTexture: 1 }); f.call(this, a.unsharpMask, { strength: d }); this._.extraTexture.unuse(1); return this; - } function O(b) { - a.vibrance = a.vibrance || new h(null, 'uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}'); - f.call(this, a.vibrance, { amount: q(-1, b, 1) }); return this; - } function P(b, d) { - a.vignette = a.vignette || new h(null, 'uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}'); - f.call(this, a.vignette, { size: q(0, b, 1), amount: q(0, d, 1) }); return this; - } function Q(b, d, c) { - a.lensBlurPrePass = a.lensBlurPrePass || new h(null, 'uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}'); var e = 'uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;' + - s + 'vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}'; - a.lensBlur0 = a.lensBlur0 || new h(null, e + 'void main(){gl_FragColor=sample(delta0);}'); a.lensBlur1 = a.lensBlur1 || new h(null, e + 'void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}'); a.lensBlur2 = a.lensBlur2 || (new h(null, e + 'void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}')).textures({ texture1: 1 }); for (var e = - [], g = 0; 3 > g; g++) { var l = c + 2 * g * Math.PI / 3; e.push([b * Math.sin(l) / this.width, b * Math.cos(l) / this.height]); } b = Math.pow(10, q(-1, d, 1)); f.call(this, a.lensBlurPrePass, { power: b }); this._.extraTexture.ensureFormat(this._.texture); f.call(this, a.lensBlur0, { delta0: e[0] }, this._.texture, this._.extraTexture); f.call(this, a.lensBlur1, { delta0: e[1], delta1: e[2] }, this._.extraTexture, this._.extraTexture); f.call(this, a.lensBlur0, { delta0: e[1] }); this._.extraTexture.use(1); f.call(this, a.lensBlur2, { power: 1 / b, delta0: e[2] }); return this; - } - function R(b, d, c, e, g, l) { - a.tiltShift = a.tiltShift || new h(null, 'uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;' + s + 'void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}'); - var n = c - b, p = e - d, m = Math.sqrt(n * n + p * p); f.call(this, a.tiltShift, { blurRadius: g, gradientRadius: l, start: [b, d], end: [c, e], delta: [n / m, p / m], texSize: [this.width, this.height] }); f.call(this, a.tiltShift, { blurRadius: g, gradientRadius: l, start: [b, d], end: [c, e], delta: [-p / m, n / m], texSize: [this.width, this.height] }); return this; - } function S(b) { - a.triangleBlur = a.triangleBlur || new h(null, 'uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;' + s + 'void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}'); - f.call(this, a.triangleBlur, { delta: [b / this.width, 0] }); f.call(this, a.triangleBlur, { delta: [0, b / this.height] }); return this; - } function T(b, d, c) { - a.zoomBlur = a.zoomBlur || new h(null, 'uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;' + s + 'void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}'); - f.call(this, a.zoomBlur, { center: [b, d], strength: c, texSize: [this.width, this.height] }); return this; - } function U(b, d, c, e) { - a.colorHalftone = a.colorHalftone || new h(null, 'uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}'); - f.call(this, a.colorHalftone, { center: [b, d], angle: c, scale: Math.PI / e, texSize: [this.width, this.height] }); return this; - } function V(b, d, c, e) { - a.dotScreen = a.dotScreen || new h(null, 'uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}'); - f.call(this, a.dotScreen, { center: [b, d], angle: c, scale: Math.PI / e, texSize: [this.width, this.height] }); return this; - } function W(b) { - a.edgeWork1 = a.edgeWork1 || new h(null, 'uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;' + s + 'void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}'); - a.edgeWork2 = a.edgeWork2 || new h(null, 'uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;' + s + 'void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}'); - f.call(this, a.edgeWork1, { delta: [b / this.width, 0] }); f.call(this, a.edgeWork2, { delta: [0, b / this.height] }); return this; - } function X(b, d, c) { - a.hexagonalPixelate = a.hexagonalPixelate || new h(null, 'uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;'); - f.call(this, a.bulgePinch, { radius: c, strength: q(-1, e, 1), center: [b, d], texSize: [this.width, this.height] }); return this; - } function $(b, d, c) { - a.matrixWarp = a.matrixWarp || u('uniform mat3 matrix;uniform bool useTextureSpace;', 'if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;'); b = Array.prototype.concat.apply([], b); if (4 == b.length) b = - [b[0], b[1], 0, b[2], b[3], 0, 0, 0, 1]; else if (9 != b.length) throw 'can only warp with 2x2 or 3x3 matrix'; f.call(this, a.matrixWarp, { matrix: d ? y(b) : b, texSize: [this.width, this.height], useTextureSpace: c | 0 }); return this; - } function aa(a, d) { - var c = x.apply(null, d), e = x.apply(null, a), c = y(c); return this.matrixWarp([c[0] * e[0] + c[1] * e[3] + c[2] * e[6], c[0] * e[1] + c[1] * e[4] + c[2] * e[7], c[0] * e[2] + c[1] * e[5] + c[2] * e[8], c[3] * e[0] + c[4] * e[3] + c[5] * e[6], c[3] * e[1] + c[4] * e[4] + c[5] * e[7], c[3] * e[2] + c[4] * e[5] + c[5] * e[8], c[6] * e[0] + c[7] * e[3] + c[8] * e[6], - c[6] * e[1] + c[7] * e[4] + c[8] * e[7], c[6] * e[2] + c[7] * e[5] + c[8] * e[8]]); - } function ba(b, d, c, e) { - a.swirl = a.swirl || u('uniform float radius;uniform float angle;uniform vec2 center;', 'coord-=center;float distance=length(coord);if(distance> 1; this.xa[e] > a ? c = e : d = e; } var e = this.xa[c] - - this.xa[d], g = (this.xa[c] - a) / e; a = (a - this.xa[d]) / e; return g * this.ya[d] + a * this.ya[c] + ((g * g * g - g) * this.y2[d] + (a * a * a - a) * this.y2[c]) * e * e / 6; - }; var r = function() { - function b(b, c, d, f) { - this.gl = a; this.id = a.createTexture(); this.width = b; this.height = c; this.format = d; this.type = f; a.bindTexture(a.TEXTURE_2D, this.id); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MAG_FILTER, a.LINEAR); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MIN_FILTER, a.LINEAR); a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_S, a.CLAMP_TO_EDGE); a.texParameteri(a.TEXTURE_2D, - a.TEXTURE_WRAP_T, a.CLAMP_TO_EDGE); b && c && a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null); - } function d(a) { null == c && (c = document.createElement('canvas')); c.width = a.width; c.height = a.height; a = c.getContext('2d'); a.clearRect(0, 0, c.width, c.height); return a; } b.fromElement = function(c) { var d = new b(0, 0, a.RGBA, a.UNSIGNED_BYTE); d.loadContentsOf(c); return d; }; b.prototype.loadContentsOf = function(b) { - this.width = b.width || b.videoWidth; this.height = b.height || b.videoHeight; a.bindTexture(a.TEXTURE_2D, - this.id); a.texImage2D(a.TEXTURE_2D, 0, this.format, this.format, this.type, b); - }; b.prototype.initFromBytes = function(b, c, d) { this.width = b; this.height = c; this.format = a.RGBA; this.type = a.UNSIGNED_BYTE; a.bindTexture(a.TEXTURE_2D, this.id); a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, b, c, 0, a.RGBA, this.type, new Uint8Array(d)); }; b.prototype.destroy = function() { a.deleteTexture(this.id); this.id = null; }; b.prototype.use = function(b) { a.activeTexture(a.TEXTURE0 + (b || 0)); a.bindTexture(a.TEXTURE_2D, this.id); }; b.prototype.unuse = function(b) { - a.activeTexture(a.TEXTURE0 + - (b || 0)); a.bindTexture(a.TEXTURE_2D, null); - }; b.prototype.ensureFormat = function(b, c, d, f) { if (1 == arguments.length) { var h = arguments[0]; b = h.width; c = h.height; d = h.format; f = h.type; } if (b != this.width || c != this.height || d != this.format || f != this.type) this.width = b, this.height = c, this.format = d, this.type = f, a.bindTexture(a.TEXTURE_2D, this.id), a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null); }; b.prototype.drawTo = function(b) { - a.framebuffer = a.framebuffer || a.createFramebuffer(); a.bindFramebuffer(a.FRAMEBUFFER, - a.framebuffer); a.framebufferTexture2D(a.FRAMEBUFFER, a.COLOR_ATTACHMENT0, a.TEXTURE_2D, this.id, 0); if (a.checkFramebufferStatus(a.FRAMEBUFFER) !== a.FRAMEBUFFER_COMPLETE) throw Error('incomplete framebuffer'); a.viewport(0, 0, this.width, this.height); b(); a.bindFramebuffer(a.FRAMEBUFFER, null); - }; var c = null; b.prototype.fillUsingCanvas = function(b) { b(d(this)); this.format = a.RGBA; this.type = a.UNSIGNED_BYTE; a.bindTexture(a.TEXTURE_2D, this.id); a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, c); return this; }; - b.prototype.toImage = function(b) { this.use(); h.getDefaultShader().drawRect(); var f = 4 * this.width * this.height, k = new Uint8Array(f), n = d(this), p = n.createImageData(this.width, this.height); a.readPixels(0, 0, this.width, this.height, a.RGBA, a.UNSIGNED_BYTE, k); for (var m = 0; m < f; m++)p.data[m] = k[m]; n.putImageData(p, 0, 0); b.src = c.toDataURL(); }; b.prototype.swapWith = function(a) { - var b; b = a.id; a.id = this.id; this.id = b; b = a.width; a.width = this.width; this.width = b; b = a.height; a.height = this.height; this.height = b; b = a.format; a.format = - this.format; this.format = b; - }; return b; - }(), s = 'float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}'; return v; -}(); \ No newline at end of file diff --git a/test/core/modules/color-halftone.js b/test/core/modules/color-halftone.js new file mode 100644 index 00000000..9f56aa8e --- /dev/null +++ b/test/core/modules/color-halftone.js @@ -0,0 +1,8 @@ +const testModule = require('../templates/module-test'), + options = { + angle: '0.5', + size: '5' + }, + benchmark = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAp0lEQVQ4T62SsQ3CMBBF3y/YgDUYAQE9IzACEvS0LAAZgRGoYQZGAXpzKFISWYmxzsCV1v/vnnTWCwy4CRaCO4WjBtDVBFvBwcsZAKJibTUXPHKwHMBl5QL0rGaCZ/tWCoitNoLj14CfDf4HqEkGS4Oz9/ZxTv1SgJNg5YUNAG3RYGpwAUbZj+TZFKASrFPZjwapsMHE4AqMuyt4DFKZAHvBrsggBXoDQmQp9u/Xo6IAAAAASUVORK5CYII='; + +//testModule('webgl-distort', options, benchmark);