From 7bc280f4e8bc88e73976abc3f24586bc43cb39f7 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Sat, 12 Sep 2015 15:52:06 -0700 Subject: [PATCH 1/8] Added support for Node.js / Node Package Manager. Modified README.md, main file, minified file, and added package.json --- README.md | 4 ++ script/jsmanipulate.js | 8 ++- script/minified/jsmanipulate.min.js | 87 +---------------------------- script/package.json | 15 +++++ 4 files changed, 28 insertions(+), 86 deletions(-) create mode 100644 script/package.json diff --git a/README.md b/README.md index dab0914..be30610 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) Copyright (c) 2011, Joel Besada +Installation +------ + npm install jsmanipulate + Usage ------ diff --git a/script/jsmanipulate.js b/script/jsmanipulate.js index f10acdb..5fd1e47 100644 --- a/script/jsmanipulate.js +++ b/script/jsmanipulate.js @@ -2307,4 +2307,10 @@ var JSManipulate = { twirl : new TwirlFilter(), vignette : new VignetteFilter(), waterripple : new WaterRippleFilter() -}; \ No newline at end of file +}; + +// node.js +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){ + module.exports = JSManipulate; +} + diff --git a/script/minified/jsmanipulate.min.js b/script/minified/jsmanipulate.min.js index 6d2ddb5..b67424a 100644 --- a/script/minified/jsmanipulate.min.js +++ b/script/minified/jsmanipulate.min.js @@ -11,88 +11,5 @@ MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) Copyright (c) 2011, Joel Besada ========================================================================= */ -function FilterUtils(){this.HSVtoRGB=function(a,b,f){var d,g,c,e=Math.floor(a*6),h=a*6-e,a=f*(1-b),k=f*(1-h*b),b=f*(1-(1-h)*b);switch(e%6){case 0:d=f;g=b;c=a;break;case 1:d=k;g=f;c=a;break;case 2:d=a;g=f;c=b;break;case 3:d=a;g=k;c=f;break;case 4:d=b;g=a;c=f;break;case 5:d=f,g=a,c=k}return[d*255,g*255,c*255]};this.RGBtoHSV=function(a,b,f){a/=255;b/=255;f/=255;var d=Math.max(a,b,f),g=Math.min(a,b,f),c,e=d-g;if(d===g)c=0;else{switch(d){case a:c=(b-f)/e+(b=d||f<0||f>=g)return[a[(this.clampPixel(f,0,g-1)*d+this.clampPixel(b,0,d-1))*4],a[(this.clampPixel(f,0,g-1)*d+this.clampPixel(b,0,d-1))*4+1],a[(this.clampPixel(f,0,g-1)*d+this.clampPixel(b,0,d-1))*4+2],a[(this.clampPixel(f,0,g-1)*d+this.clampPixel(b,0,d-1))*4+3]];return[a[c],a[c+1],a[c+2],a[c+3]]};var h=!1,e;this.gaussianRandom=function(){if(h)return h=!1,e;else{var a,b,f;do a=2*Math.random()-1,b=2* -Math.random()-1,f=a*a+b*b;while(f>=1||f===0);f=Math.sqrt(-2*Math.log(f)/f);e=b*f;h=!0;return a*f}};this.clampPixel=function(a,b,f){return af?f:a};this.triangle=function(a){a=this.mod(a,1);return 2*(a<0.5?a:1-a)};this.mod=function(a,b){var f=parseInt(a/b,10);a-=f*b;if(a<0)return a+b;return a};this.mixColors=function(a,b,f){var d=this.linearInterpolate(a,b[0],f[0]),g=this.linearInterpolate(a,b[1],f[1]),c=this.linearInterpolate(a,b[2],f[2]),a=this.linearInterpolate(a,b[3],f[3]);return[d,g,c,a]}; -this.linearInterpolate=function(a,b,f){return b+a*(f-b)};this.bilinearInterpolate=function(a,b,f,d,g,c){var e=f[0],h=f[1],k=f[2],i=d[0],o=d[1],p=d[2],s=g[0],n=g[1],m=g[2],q=g[3],t=c[0],r=c[1],g=c[2],u=c[3],c=1-a,v=1-b,f=c*f[3]+a*d[3],f=v*f+b*(c*q+a*u),e=v*(c*e+a*i)+b*(c*s+a*t),h=v*(c*h+a*o)+b*(c*n+a*r);return[e,h,v*(c*k+a*p)+b*(c*m+a*g),f]};this.tableFilter=function(a,b,f,d){for(var g=0;g=0&&i=0&&o=2.5?0.98711*g-0.9633:g>=0.5?3.97156-4.14554*Math.sqrt(1-0.26891*g):2*g*(3.97156-4.14554*Math.sqrt(0.865545));var c=g*g,j=c*g,l=1.57825+2.44413*g+1.4281*c+0.422205*j;g=(2.44413*g+2.85619*c+1.26661*j)/l;for(var c=-(1.4281*c+1.26661*j)/l,j=0.422205*j/l,l=1-(g+c+j),k=0,i,o,p, -s,n,m,k=0;k<3;k++)for(var q=0;q=o;i-=4)p=l*d[i]+g*s+c*n+j*m,d[i]=p,m=n,n=s,s=p}for(k=0;k<3;k++)for(q=0;q=o;i-=b)p=l*d[i]+g*s+c*n+j*m,d[i]=p,m=n,n=s,s=p}}} -function BrightnessFilter(){this.name="Brightness";this.isDirAnimatable=!0;this.defaultValues={amount:0};this.valueRanges={amount:{min:-1,max:1}};var h=new FilterUtils;this.filter=function(e,a){var b=e.width,f=e.height,d=e.data;if(a===void 0)a=this.defaultValues;for(var g=a.amount===void 0?this.defaultValues.amount:a.amount,c=0;c1&&(k[2]=1);for(var k=h.HSVtoRGB(k[0],k[1],k[2]),i=0;i<3;i++)d[l+i]= -k[i]}}}function BumpFilter(){this.name="Bump";this.isDirAnimatable=!0;this.defaultValues={};this.valueRanges={};var h=new FilterUtils;this.filter=function(e){h.convolveFilter(e.data,[-1,-1,0,-1,1,1,0,1,1],e.width,e.height)}} -function CircleSmearFilter(){this.name="Circle Smear";this.isDirAnimatable=!1;this.defaultValues={size:4,density:0.5,mix:0.5};this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var h=new FilterUtils;this.filter=function(e,a){for(var b=e.width,f=e.height,d=e.data,g=[],c=0;c=0&&n=0&&m=0&&q=0&&q=0&&o=0&&n=j-j*e/j)c[0]=a,c[1]=b;else{var t=1/g,r=Math.sqrt((1-e/j-h/j)*j),u=r*r,d=Math.acos(d/Math.sqrt(e+u)),e=Math.PI/2-d,e=Math.asin(Math.sin(e)*t),e=Math.PI/2-d-e;c[0]=a-Math.tan(e)*r;a=Math.acos(f/Math.sqrt(h+u));e=Math.PI/2-a;e=Math.asin(Math.sin(e)*t);e=Math.PI/2-a-e;c[1]=b-Math.tan(e)*r}},b,f)}} -function LineSmearFilter(){this.name="Line Smear";this.isDirAnimatable=!1;this.defaultValues={distance:8,density:0.5,angle:0,mix:0.5};this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var h=new FilterUtils;this.filter=function(e,a){var b=e.width,f=e.height,d=e.data,g=[],c;for(c=0;c=0&&u=0){c=[g[(u*b+r)*4],g[(u*b+r)*4+1],g[(u*b+r)*4+2],g[(u*b+r)*4+3]];z=h.mixColors(i,c,q);for(c=0;c<3;c++)g[(u*b+r)*4+c]=z[c]}if(Math.abs(t)>Math.abs(m)){v=2*m-t;y=2*m;for(t=2*(m-t);r!=s;)if(v<=0?v+=y:(v+=t,u+=x),r+=w,r=0&&u=0){c=[g[(u*b+r)*4],g[(u*b+r)*4+1],g[(u*b+r)*4+2],g[(u*b+r)*4+3]];z=h.mixColors(i,c,q);for(c=0;c<3;c++)g[(u*b+r)*4+c]=z[c]}}else{v=2*t-m;y=2*t;for(t=2*(t-m);u!=n;)if(v<=0?v+=y:(v+= -t,r+=w),u+=x,r=0&&u=0){c=[g[(u*b+r)*4],g[(u*b+r)*4+1],g[(u*b+r)*4+2],g[(u*b+r)*4+3]];z=h.mixColors(i,c,q);for(c=0;c<3;c++)g[(u*b+r)*4+c]=z[c]}}}for(c=0;c=0&&i=0&&p=0&&i=0&&p=0&&i=0&&pc[m]&&(m=u),j[u]>j[q]&&(q=u),l[u]>l[t]&&(t=u);m=k[m]/c[m];q=i[q]/j[q];t=o[t]/l[t];d[n]=m;d[n+1]=q;d[n+2]=t;d[n+3]=f[n+3]}for(a=0;ai||h===0?(d[0]=a,d[1]=b):(a=Math.sqrt(h/i),b=Math.pow(Math.sin(Math.PI*0.5*a),-g),e*=b,f*=b,a=1-a,b=c*a*a,a=Math.sin(b),b=Math.cos(b),d[0]=o+b*e-a*f,d[1]=p+a*e+b*f)},b,f)}} -function PixelationFilter(){this.name="Pixelation";this.isDirAnimatable=!1;this.defaultValues={size:5};this.valueRanges={size:{min:1,max:50}};this.filter=function(h,e){var a=h.width,b=h.height,f=h.data;if(e===void 0)e=this.defaultValues;for(var d=e.size===void 0?this.defaultValues.size:e.size,d=parseInt(d,10),g,c,j,l=0;l0.5?2*(d/255-0.5):2*(0.5-d/255)),10);h.tableFilter(e,f,a,b)}} -function SparkleFilter(){this.name="Sparkle";this.isDirAnimatable=!1;this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:0.5,centerY:0.5};this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var h=new FilterUtils;this.filter=function(e,a){var b=e.width,f=e.height,d=e.data;if(a===void 0)a=this.defaultValues;for(var g=a.rays===void 0?this.defaultValues.rays:a.rays,g=parseInt(g,10),c= -a.size===void 0?this.defaultValues.size:a.size,j=a.amount===void 0?this.defaultValues.amount:a.amount,l=a.randomness===void 0?this.defaultValues.randomness:a.randomness,k=(a.centerX===void 0?this.defaultValues.centerX:a.centerX)*b,i=(a.centerY===void 0?this.defaultValues.centerY:a.centerY)*f,o=[],p=0;p=0&&n=0&&md&&(l=255);f[j]=f[j+1]=f[j+2]=l}}} -function TriangleRippleFilter(){this.name="Triangle Ripples";this.isDirAnimatable=!1;this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16};this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var h=new FilterUtils;this.filter=function(e,a){var b=e.width,f=e.height,d=e.data;if(a===void 0)a=this.defaultValues;var g=a.xAmplitude===void 0?this.defaultValues.xAmplitude:a.xAmplitude,c=a.yAmplitude===void 0?this.defaultValues.yAmplitude: -a.yAmplitude,j=a.xWavelength===void 0?this.defaultValues.xWavelength:a.xWavelength,l=a.yWavelength===void 0?this.defaultValues.yWavelength:a.yWavelength;h.transformFilter(d,function(a,b,d){var e=a/l,f=h.triangle(b/j,1),e=h.triangle(e,1);d[0]=a+g*f;d[1]=b+c*e},b,f)}} -function TwirlFilter(){this.name="Twirl";this.isDirAnimatable=!1;this.defaultValues={radius:100,angle:180,centerX:0.5,centerY:0.5};this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var h=new FilterUtils;this.filter=function(e,a){var b=e.width,f=e.height,d=e.data;if(a===void 0)a=this.defaultValues;var g=a.angle===void 0?this.defaultValues.angle:a.angle,c=a.centerX===void 0?this.defaultValues.centerX:a.centerX,j=a.centerY===void 0?this.defaultValues.centerY: -a.centerY,l=a.radius===void 0?this.defaultValues.radius:a.radius,k=l*l,g=g/180*Math.PI,i=b*c,o=f*j;h.transformFilter(d,function(a,b,c){var d=a-i,e=b-o,f=d*d+e*e;f>k?(c[0]=a,c[1]=b):(f=Math.sqrt(f),a=Math.atan2(e,d)+g*(l-f)/l,c[0]=i+f*Math.cos(a),c[1]=o+f*Math.sin(a))},b,f)}} -function VignetteFilter(){this.name="Vignette";this.isDirAnimatable=!1;this.defaultValues={amount:0.3};this.valueRanges={amount:{min:0,max:1}};this.filter=function(h,e){var a=h.width,b=h.height,f=h.data,d=[];if(e===void 0)e=this.defaultValues;var d=e.amount===void 0?this.defaultValues.amount:e.amount,g=document.createElement("canvas");g.width=a;g.height=b;var g=g.getContext("2d"),c;c=Math.sqrt(Math.pow(a/2,2)+Math.pow(b/2,2));g.putImageData(h,0,0);g.globalCompositeOperation="source-over";c=g.createRadialGradient(a/ -2,b/2,0,a/2,b/2,c);c.addColorStop(0,"rgba(0,0,0,0)");c.addColorStop(0.5,"rgba(0,0,0,0)");c.addColorStop(1,"rgba(0,0,0,"+d+")");g.fillStyle=c;g.fillRect(0,0,a,b);d=g.getImageData(0,0,a,b).data;for(a=0;ak)d[0]=a,d[1]=b;else{var h=Math.sqrt(h),r=c*Math.sin(h/g*Math.PI*2-j);r*=(l-h)/l;h!==0&&(r*=g/h);d[0]=a+e*r;d[1]=b+f*r}}, -b,f)}} -var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter, -maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter, -triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter}; +function FilterUtils(){this.HSVtoRGB=function(t,a,e){var i,r,n,s=Math.floor(6*t),l=6*t-s,h=e*(1-a),o=e*(1-l*a),u=e*(1-(1-l)*a);switch(s%6){case 0:i=e,r=u,n=h;break;case 1:i=o,r=e,n=h;break;case 2:i=h,r=e,n=u;break;case 3:i=h,r=o,n=e;break;case 4:i=u,r=h,n=e;break;case 5:i=e,r=h,n=o}return[255*i,255*r,255*n]},this.RGBtoHSV=function(t,a,e){t/=255,a/=255,e/=255;var i,r,n=Math.max(t,a,e),s=Math.min(t,a,e),l=n,h=n-s;if(r=0===n?0:h/n,n===s)i=0;else{switch(n){case t:i=(a-e)/h+(e>a?6:0);break;case a:i=(e-t)/h+2;break;case e:i=(t-a)/h+4}i/=6}return[i,r,l]},this.getPixel=function(t,a,e,i,r){var n=4*(e*i+a);return 0>a||a>=i||0>e||e>=r?[t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+1],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+2],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+3]]:[t[n],t[n+1],t[n+2],t[n+3]]};var t,a=!1;this.gaussianRandom=function(){if(a)return a=!1,t;var e,i,r;do e=2*Math.random()-1,i=2*Math.random()-1,r=e*e+i*i;while(r>=1||0===r);var n=Math.sqrt(-2*Math.log(r)/r);return t=i*n,a=!0,e*n},this.clampPixel=function(t,a,e){return a>t?a:t>e?e:t},this.triangle=function(t){var a=this.mod(t,1);return 2*(.5>a?a:1-a)},this.mod=function(t,a){var e=parseInt(t/a,10);return t-=e*a,0>t?t+a:t},this.mixColors=function(t,a,e){var i=this.linearInterpolate(t,a[0],e[0]),r=this.linearInterpolate(t,a[1],e[1]),n=this.linearInterpolate(t,a[2],e[2]),s=this.linearInterpolate(t,a[3],e[3]);return[i,r,n,s]},this.linearInterpolate=function(t,a,e){return a+t*(e-a)},this.bilinearInterpolate=function(t,a,e,i,r,n){var s,l,h=e[0],o=e[1],u=e[2],d=e[3],f=i[0],v=i[1],m=i[2],c=i[3],g=r[0],p=r[1],x=r[2],V=r[3],w=n[0],F=n[1],M=n[2],b=n[3],I=1-t,A=1-a;s=I*d+t*c,l=I*V+t*b;var y=A*s+a*l;s=I*h+t*f,l=I*g+t*w;var R=A*s+a*l;s=I*o+t*v,l=I*p+t*F;var D=A*s+a*l;s=I*u+t*m,l=I*x+t*M;var S=A*s+a*l;return[R,D,S,y]},this.tableFilter=function(t,a,e,i){for(var r=0;i>r;r++)for(var n=0;e>n;n++)for(var s=4*(r*e+n),l=0;3>l;l++)t[s+l]=a[t[s+l]]},this.convolveFilter=function(t,a,e,i){var r,n,s=[];r=n=Math.sqrt(a.length);for(var l=parseInt(r/2,10),h=parseInt(n/2,10),o=0;i>o;o++)for(var u=0;e>u;u++){for(var d=4*(o*e+u),f=0,v=0,m=0,c=-l;l>=c;c++){var g,p=o+c;g=p>=0&&i>p?p*e:o*e;for(var x=n*(c+l)+h,V=-h;h>=V;V++){var w=a[x+V];if(0!==w){var F=u+V;F>=0&&e>F||(F=u);var M=4*(g+F);f+=w*t[M],v+=w*t[M+1],m+=w*t[M+2]}}}s[d]=parseInt(f+.5,10),s[d+1]=parseInt(v+.5,10),s[d+2]=parseInt(m+.5,10),s[d+3]=t[d+3]}for(var b=0;bl;l++)for(var h=0;e>h;h++){var o=4*(l*e+h);a.apply(this,[h,l,r]);var u,d,f,v,m=Math.floor(r[0]),c=Math.floor(r[1]),g=r[0]-m,p=r[1]-c;if(m>=0&&e-1>m&&c>=0&&i-1>c){var x=4*(e*c+m);u=[t[x],t[x+1],t[x+2],t[x+3]],d=[t[x+4],t[x+5],t[x+6],t[x+7]],f=[t[x+4*e],t[x+4*e+1],t[x+4*e+2],t[x+4*e+3]],v=[t[x+4*(e+1)],t[x+4*(e+1)+1],t[x+4*(e+1)+2],t[x+4*(e+1)+3]]}else u=this.getPixel(t,m,c,e,i),d=this.getPixel(t,m+1,c,e,i),f=this.getPixel(t,m,c+1,e,i),v=this.getPixel(t,m+1,c+1,e,i);var V=this.bilinearInterpolate(g,p,u,d,f,v);n[o]=V[0],n[o+1]=V[1],n[o+2]=V[2],n[o+3]=V[3]}for(var w=0;wl&&(l=0),e=l>=2.5?.98711*l-.9633:l>=.5?3.97156-4.14554*Math.sqrt(1-.26891*l):2*l*(3.97156-4.14554*Math.sqrt(.865545));var h,o,u,d,f,v,m=e*e,c=m*e,g=1.57825+2.44413*e+1.4281*m+.422205*c,p=(2.44413*e+2.85619*m+1.26661*c)/g,x=-(1.4281*m+1.26661*c)/g,V=.422205*c/g,w=1-(p+x+V),F=0;for(F=0;3>F;F++)for(var M=0;n>M;M++){for(h=M*r+F,o=M*r+(i-1<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=M*r+(i-1<<2)+F,o=M*r+F,u=s[h],d=u,f=d,v=f;h>=o;h-=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}for(F=0;3>F;F++)for(var b=0;i>b;b++){for(h=(b<<2)+F,o=(n-1)*r+(b<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=(n-1)*r+(b<<2)+F,o=(b<<2)+F,u=s[h],d=u,f=d,v=f;h>=o;h-=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}}}function BrightnessFilter(){this.name="Brightness",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);u[2]+=s,u[2]<0?u[2]=0:u[2]>1&&(u[2]=1);for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function BumpFilter(){this.name="Bump",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[-1,-1,0,-1,1,1,0,1,1];t.convolveFilter(r,n,e,i)}}function CircleSmearFilter(){this.name="Circle Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=d*d,v=parseInt(2*o/30*i*r/2,10),m=0;v>m;m++)for(var c=(Math.random()*Math.pow(2,32)&2147483647)%i,g=(Math.random()*Math.pow(2,32)&2147483647)%r,p=[n[4*(g*i+c)],n[4*(g*i+c)+1],n[4*(g*i+c)+2],n[4*(g*i+c)+3]],x=c-d;c+d+1>x;x++)for(var V=g-d;g+d+1>V;V++){var w=(x-c)*(x-c)+(V-g)*(V-g);if(x>=0&&i>x&&V>=0&&r>V&&f>=w)for(var F=[s[4*(V*i+x)],s[4*(V*i+x)+1],s[4*(V*i+x)+2],s[4*(V*i+x)+3]],M=t.mixColors(u,F,p),b=0;3>b;b++)s[4*(V*i+x)+b]=M[b]}for(var I=0;Is&&(s=0);for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*((h/255-.5)*s+.5),10);t.tableFilter(n,l,i,r)}}function CrossSmearFilter(){this.name="Cross Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,mix:.5},this.valueRanges={distance:{min:0,max:30},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=0),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=parseInt(2*o*i*r/(h+1),10),f=0;d>f;f++){for(var v,m,c,g=(Math.random()*Math.pow(2,32)&2147483647)%i,p=(Math.random()*Math.pow(2,32)&2147483647)%r,x=Math.random()*Math.pow(2,32)%h+1,V=[n[4*(p*i+g)],n[4*(p*i+g)+1],n[4*(p*i+g)+2],n[4*(p*i+g)+3]],w=g-x;g+x+1>w;w++)if(w>=0&&i>w)for(v=[s[4*(p*i+w)],s[4*(p*i+w)+1],s[4*(p*i+w)+2],s[4*(p*i+w)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(p*i+w)+c]=m[c];for(var F=p-x;p+x+1>F;F++)if(F>=0&&r>F)for(v=[s[4*(F*i+g)],s[4*(F*i+g)+1],s[4*(F*i+g)+2],s[4*(F*i+g)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(F*i+g)+c]=m[c]}for(var M=0;Mo;o++){var u=2*Math.PI*o/256;l[o]=s*Math.sin(u),h[o]=s*Math.cos(u)}transInverse=function(t,a,e){var i=parseInt(255*Math.random(),10),r=Math.random();e[0]=t+r*l[i],e[1]=a+r*h[i]},t.transformFilter(n,transInverse,i,r)}}function DitherFilter(){this.name="Dither",this.isDirAnimatable=!1,this.defaultValues={levels:3,color:!0},this.valueRanges={levels:{min:2,max:30},color:{min:!1,max:!0}};new FilterUtils;this.filter=function(t,a){var e,i,r=t.width,n=t.height,s=t.data,l=[];for(i=0;i=h&&(h=1);var u=[0,0,0,0,0,7,3,5,1],d=16,f=0,v=[];for(e=0;h>e;e++)v[e]=parseInt(255*e/(h-1),10);var m=[];for(e=0;256>e;e++)m[e]=parseInt(h*e/256,10);for(var c=0;n>c;c++){var g,p=1==(1&c);p?(f=4*(c*r+r-1),g=-1):(f=c*r*4,g=1);for(var x=0;r>x;x++){var V=s[f],w=s[f+1],F=s[f+2];o||(V=w=F=parseInt((V+w+F)/3,10));var M=v[m[V]],b=v[m[w]],I=v[m[F]];l[f]=M,l[f+1]=b,l[f+2]=I,l[f+3]=s[f+3];var A=V-M,y=w-b,R=F-I;for(e=-1;1>=e;e++){var D=e+c;if(D>=0&&n>D)for(i=-1;1>=i;i++){var S=i+x;if(S>=0&&r>S){var P;if(P=p?u[3*(e+1)-i+1]:u[3*(e+1)+i+1],0!==P){var W=p?f-4*i:f+4*i;V=s[W],w=s[W+1],F=s[W+2];var U=P/d;V+=A*U,w+=y*U,F+=R*U,s[W]=V,s[W+1]=w,s[W+2]=F}}}}f+=4*g}}for(i=0;il;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=0;gh=0,bh=0;var d=0;gv=0,bv=0;for(var f=-1;1>=f;f++){var v,m=l+f;v=m>=0&&r>m?m*i*4:l*i*4;for(var c=3*(f+1)+1,g=-1;1>=g;g++){var p=h+g;p>=0&&i>p||(p=h),p*=4;var x=n[v+p],V=n[v+p+1],w=n[v+p+2],F=t[c+g],M=a[c+g];u+=parseInt(F*x,10),bh+=parseInt(F*V,10),gh+=parseInt(F*w,10),d+=parseInt(M*x,10),gv+=parseInt(M*V,10),bv+=parseInt(M*w,10)}}x=parseInt(Math.sqrt(u*u+d*d)/1.8,10),V=parseInt(Math.sqrt(gh*gh+gv*gv)/1.8,10),w=parseInt(Math.sqrt(bh*bh+bv*bv)/1.8,10),s[o]=x,s[o+1]=V,s[o+2]=w,s[o+3]=n[o+3]}for(var b=0;bA;A++,I+=d)for(var y=I,R=y+d,D=R+d,S=0;e>S;S++,y++,R++,D++){var P=4*(A*e+S);0!==A&&i-2>A&&0!==S&&e-2>S?(v=u[y-1]+u[R-1]+u[D-1]-u[y+1]-u[R+1]-u[D+1],m=u[D-1]+u[D]+u[D+1]-u[y-1]-u[y]-u[y+1],M=0===v&&0===m?b:(F=v*g+m*p+w)<0?0:parseInt(F/Math.sqrt(v*v+m*m+V),10)):M=b,r[P]=r[P+1]=r[P+2]=M}}}function ExposureFilter(){this.name="Exposure",this.isDirAnimatable=!0,this.defaultValues={exposure:1},this.valueRanges={exposure:{min:0,max:5}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.exposure?this.defaultValues.exposure:e.exposure,l=[],h=0;256>h;h++)l[h]=parseInt(255*(1-Math.exp(-(h/255)*s)),10);t.tableFilter(n,l,i,r)}}function GainFilter(){this.name="Gain/Bias",this.isDirAnimatable=!0,this.defaultValues={gain:.5,bias:.5},this.valueRanges={gain:{min:0,max:1},bias:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.gain?this.defaultValues.gain:e.gain,l=void 0===e.bias?this.defaultValues.bias:e.bias,h=[],o=0;256>o;o++){var u=o/255,d=(1/s-2)*(1-2*u);u=.5>u?u/(d+1):(d-u)/(d-1),u/=(1/l-2)*(1-u)+1,h[o]=parseInt(255*u,10)}t.tableFilter(n,h,i,r)}}function GammaFilter(){this.name="Gamma",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;if(0>n&&(n=0),!FilterUtils)return void(console&&console.error("Unable to find filterutils.js, please include this file! (Required by "+this.name+" filter)"));for(var s=new FilterUtils,l=[],h=0;256>h;h++)l[h]=255*Math.pow(h/255,1/n)+.5;s.tableFilter(r,l,e,i)}}function GrayscaleFilter(){this.name="Grayscale",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++){var s=4*(r*a+n),l=.3*i[s]+.59*i[s+1]+.11*i[s+2];i[s]=i[s+1]=i[s+2]=l}}}function HueFilter(){this.name="Hue",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);for(u[0]+=s;u[0]<0;)u[0]+=360;for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function InvertFilter(){this.name="Invert",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++)for(var s=4*(r*a+n),l=0;3>l;l++)i[s+l]=255-i[s+l]}}function KaleidoscopeFilter(){this.name="Kaleidoscope",this.isDirAnimatable=!1,this.defaultValues={angle:0,rotation:0,sides:3,centerX:.5,centerY:.5},this.valueRanges={angle:{min:0,max:360},rotation:{min:0,max:360},sides:{min:1,max:30},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.rotation?this.defaultValues.rotation:e.rotation,h=void 0===e.sides?this.defaultValues.sides:e.sides,o=void 0===e.centerX?this.defaultValues.centerX:e.centerX,u=void 0===e.centerY?this.defaultValues.centerY:e.centerY,d=i*o,f=r*u;s=s/180*Math.PI,l=l/180*Math.PI;var v=function(a,e,i){var r=a-d,n=e-f,o=Math.sqrt(r*r+n*n),u=Math.atan2(n,r)-s-l;u=t.triangle(u/Math.PI*h*.5),u+=s,i[0]=d+o*Math.cos(u),i[1]=f+o*Math.sin(u)};t.transformFilter(n,v,i,r)}}function LensDistortionFilter(){this.name="Lens Distortion",this.isDirAnimatable=!1,this.defaultValues={refraction:1.5,radius:50,centerX:.5,centerY:.5},this.valueRanges={refraction:{min:1,max:10},radius:{min:1,max:200},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.refraction?this.defaultValues.refraction:e.refraction,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o,d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i,l=r*r;if(l>=u-u*n/u)e[0]=t,e[1]=a;else{var h=1/s,o=Math.sqrt((1-n/u-l/u)*u),v=o*o,m=Math.acos(i/Math.sqrt(n+v)),c=Math.PI/2-m,g=Math.asin(Math.sin(c)*h);g=Math.PI/2-m-g,e[0]=t-Math.tan(g)*o;var p=Math.acos(r/Math.sqrt(l+v));c=Math.PI/2-p,g=Math.asin(Math.sin(c)*h),g=Math.PI/2-p-g,e[1]=a-Math.tan(g)*o}};t.transformFilter(n,v,i,r)}}function LineSmearFilter(){this.name="Line Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,angle:0,mix:.5},this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.angle?this.defaultValues.angle:e.angle,d=void 0===e.mix?this.defaultValues.mix:e.mix;u=u/180*Math.PI;for(var f=Math.sin(u),v=Math.cos(u),m=parseInt(2*o*r*n/2,10),c=0;m>c;c++){var g,p,x,V,w,F,M,b=(Math.random()*Math.pow(2,32)&2147483647)%r,I=(Math.random()*Math.pow(2,32)&2147483647)%n,A=(Math.random()*Math.pow(2,32)&2147483647)%h+1,y=[s[4*(I*r+b)],s[4*(I*r+b)+1],s[4*(I*r+b)+2],s[4*(I*r+b)+3]],R=parseInt(A*v,10),D=parseInt(A*f,10),S=b-R,P=I-D,W=b+R,U=I+D;F=S>W?-1:1,M=P>U?-1:1,R=W-S,D=U-P,R=Math.abs(R),D=Math.abs(D),g=S,p=P;var X,Y;if(r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i];if(Math.abs(R)>Math.abs(D)){for(x=2*D-R,V=2*D,w=2*(D-R);g!=W;)if(0>=x?x+=V:(x+=w,p+=M),g+=F,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}else for(x=2*R-D,V=2*R,w=2*(R-D);p!=U;)if(0>=x?x+=V:(x+=w,g+=F),p+=M,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}for(i=0;in;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=0,o=0,u=0,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.max(h,i[c]),o=Math.max(o,i[c+1]),u=Math.max(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=[],o=[],u=[],d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h.push(i[c]),o.push(i[c+1]),u.push(i[c+2])}}}var g=function(t,a){return t-a};h.sort(g),o.sort(g),u.sort(g),r[l]=h[4],r[l+1]=o[4],r[l+2]=u[4],r[l+3]=i[l+3]}for(var p=0;pn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=255,o=255,u=255,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.min(h,i[c]),o=Math.min(o,i[c+1]),u=Math.min(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gh;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);if(Math.random()<=s){var d;if(l)d=parseInt((2*Math.random()-1)*n,10),r[u]+=d,r[u+1]+=d,r[u+2]+=d;else for(var f=0;3>f;f++)d=parseInt((2*Math.random()-1)*n,10),r[u+f]+=d}}}}function OilFilter(){this.name="Oil Painting",this.isDirAnimatable=!1,this.defaultValues={range:3},this.valueRanges={range:{min:0,max:5}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.range?this.defaultValues.range:a.range;s=parseInt(s,10);for(var l=[],h=[],o=[],u=[],d=[],f=[],v=256,m=0;i>m;m++)for(var c=0;e>c;c++){for(var g=4*(m*e+c),p=0;v>p;p++)l[p]=h[p]=o[p]=u[p]=d[p]=f[p]=0;for(var x=-s;s>=x;x++){var V,w=m+x;if(w>=0&&i>w){V=w*e;for(var F=-s;s>=F;F++){var M=c+F;if(M>=0&&e>M){var b=r[4*(V+M)],I=r[4*(V+M)+1],A=r[4*(V+M)+2],y=b*v/256,R=I*v/256,D=A*v/256;u[y]+=b,d[R]+=I,f[D]+=A,l[y]++,h[R]++,o[D]++}}}}for(var S=0,P=0,W=0,U=1;v>U;U++)l[U]>l[S]&&(S=U),h[U]>h[P]&&(P=U),o[U]>o[W]&&(W=U);S=u[S]/l[S],P=d[P]/h[P],W=f[W]/o[W],n[g]=S,n[g+1]=P,n[g+2]=W,n[g+3]=r[g+3]}for(var X=0;Xs;s++)for(var l=0;e>l;l++){var h=4*(s*e+l);r[h+3]=255*n}}}function PinchFilter(){this.name="Pinch/Whirl",this.isDirAnimatable=!1,this.defaultValues={amount:.5,radius:100,angle:0,centerX:.5,centerY:.5},this.valueRanges={amount:{min:-1,max:1},radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=void 0===e.angle?this.defaultValues.angle:e.angle,h=void 0===e.centerX?this.defaultValues.centerX:e.centerX,o=void 0===e.centerY?this.defaultValues.centerY:e.centerY,u=void 0===e.radius?this.defaultValues.radius:e.radius,d=u*u;l=l/180*Math.PI;var f=i*h,v=r*o,m=function(t,a,e){var i=t-f,r=a-v,n=i*i+r*r;if(n>d||0===n)e[0]=t,e[1]=a;else{var h=Math.sqrt(n/d),o=Math.pow(Math.sin(.5*Math.PI*h),-s);i*=o,r*=o;var u=1-h,m=l*u*u,c=Math.sin(m),g=Math.cos(m);e[0]=f+g*i-c*r,e[1]=v+c*i+g*r}};t.transformFilter(n,m,i,r)}}function PixelationFilter(){this.name="Pixelation",this.isDirAnimatable=!1,this.defaultValues={size:5},this.valueRanges={size:{min:1,max:50}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.size?this.defaultValues.size:a.size;n=parseInt(n,10);for(var s,l,h,o=0;i>o;o+=n)for(var u=0;e>u;u+=n){var d=Math.min(n,e-u),f=Math.min(n,i-o),v=d*f,m=0,c=0,g=0;for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),m+=r[h],c+=r[h+1],g+=r[h+2];for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),r[h]=m/v,r[h+1]=c/v,r[h+2]=g/v}}}function PosterizeFilter(){this.name="Posterize",this.isDirAnimatable=!1,this.defaultValues={levels:6},this.valueRanges={levels:{min:2,max:30}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.levels?this.defaultValues.levels:parseInt(e.levels,10);if(!(1>=s)){for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*parseInt(h*s/256,10)/(s-1),10);t.tableFilter(n,l,i,r)}}}function RGBAdjustFilter(){this.name="RGBAdjust",this.isDirAnimatable=!0,this.defaultValues={red:1,green:1,blue:1},this.valueRanges={red:{min:0,max:2},green:{min:0,max:2},blue:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.red?this.defaultValues.red:a.red,s=void 0===a.green?this.defaultValues.green:a.green,l=void 0===a.blue?this.defaultValues.blue:a.blue;0>n&&(n=0),0>s&&(s=0),0>l&&(l=0);for(var h=0;i>h;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);r[u]*=n,r[u+1]*=s,r[u+2]*=l}}}function SaturationFilter(){this.name="Saturation",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);for(var n=void 0===a.amount?this.defaultValues.amount:a.amount,s=.3,l=.59,h=.11,o=(1-n)*s+n,u=(1-n)*s,d=(1-n)*s,f=(1-n)*l,v=(1-n)*l+n,m=(1-n)*l,c=(1-n)*h,g=(1-n)*h,p=(1-n)*h+n,x=0;i>x;x++)for(var V=0;e>V;V++){var w=4*(x*e+V),F=r[w],M=r[w+1],b=r[w+2];r[w]=o*F+f*M+c*b,r[w+1]=u*F+v*M+g*b,r[w+2]=d*F+m*M+p*b}}}function SawtoothRippleFilter(){this.name="Sawtooth Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.mod(r,1),d=t.mod(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function SepiaFilter(){this.name="Sepia",this.isDirAnimatable=!0,this.defaultValues={amount:10},this.valueRanges={amount:{min:0,max:30}};new FilterUtils;this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;n*=2.55;for(var s=0;i>s;s++)for(var l=0;e>l;l++){var h,o,u,d=4*(s*e+l),f=.3*r[d]+.59*r[d+1]+.11*r[d+2];h=o=u=f,h+=40,o+=20,u-=n,r[d]=h,r[d+1]=o,r[d+2]=u}}}function SharpenFilter(){this.name="Sharpen",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[0,-.2,0,-.2,1.8,-.2,0,-.2,0];t.convolveFilter(r,n,e,i)}}function SineRippleFilter(){this.name="Sine Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(t,a,e){var i=a/h,r=t/o,n=Math.sin(i),u=Math.sin(r);e[0]=t+s*n,e[1]=a+l*u};t.transformFilter(n,u,i,r)}}function SolarizeFilter(){this.name="Solarize",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){for(var e=a.width,i=a.height,r=a.data,n=[],s=0;256>s;s++){var l=s/255>.5?2*(s/255-.5):2*(.5-s/255);n[s]=parseInt(255*l,10)}t.tableFilter(r,n,e,i)}}function SparkleFilter(){this.name="Sparkle",this.isDirAnimatable=!1,this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:.5,centerY:.5},this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.rays?this.defaultValues.rays:e.rays;s=parseInt(s,10);for(var l=void 0===e.size?this.defaultValues.size:e.size,h=void 0===e.amount?this.defaultValues.amount:e.amount,o=void 0===e.randomness?this.defaultValues.randomness:e.randomness,u=void 0===e.centerX?this.defaultValues.centerX:e.centerX,d=void 0===e.centerY?this.defaultValues.centerY:e.centerY,f=u*i,v=d*r,m=[],c=0;s>c;c++)m[c]=l+o/100*l*t.gaussianRandom();for(var g=0;r>g;g++)for(var p=0;i>p;p++){var x=4*(g*i+p),V=p-f,w=g-v,F=V*V+w*w,M=Math.atan2(w,V),b=(M+Math.PI)/(2*Math.PI)*s,I=parseInt(b,10),A=b-I;if(0!==l){var y=t.linearInterpolate(A,m[I%s],m[(I+1)%s]),R=y*y/(F+1e-4);R=Math.pow(R,(100-h)/50),A-=.5,A=1-A*A,A*=R}A=t.clampPixel(A,0,1);for(var D=t.mixColors(A,[n[x],n[x+1],n[x+2],n[x+3]],[255,255,255,255]),S=0;3>S;S++)n[x+S]=D[S]}}}function SquareSmearFilter(){this.name="Square Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=parseInt(2*o/30*r*n/2,10),v=0;f>v;v++)for(var m=(Math.random()*Math.pow(2,32)&2147483647)%r,c=(Math.random()*Math.pow(2,32)&2147483647)%n,g=[s[4*(c*r+m)],s[4*(c*r+m)+1],s[4*(c*r+m)+2],s[4*(c*r+m)+3]],p=m-d;m+d+1>p;p++)for(var x=c-d;c+d+1>x;x++)if(p>=0&&r>p&&x>=0&&n>x){var V=[l[4*(x*r+p)],l[4*(x*r+p)+1],l[4*(x*r+p)+2],l[4*(x*r+p)+3]],w=t.mixColors(u,V,g);for(i=0;3>i;i++)l[4*(x*r+p)+i]=w[i]}for(i=0;is;s++)for(var l=0;e>l;l++){var h=4*(s*e+l),o=(r[h]+r[h+1]+r[h+2])/3,u=0;o>n&&(u=255),r[h]=r[h+1]=r[h+2]=u}}}function TriangleRippleFilter(){this.name="Triangle Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.triangle(r,1),d=t.triangle(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function TwirlFilter(){this.name="Twirl",this.isDirAnimatable=!1,this.defaultValues={radius:100,angle:180,centerX:.5,centerY:.5},this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o;s=s/180*Math.PI;var d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i+r*r;if(n>u)e[0]=t,e[1]=a;else{n=Math.sqrt(n);var l=Math.atan2(r,i)+s*(o-n)/o;e[0]=d+n*Math.cos(l),e[1]=f+n*Math.sin(l)}};t.transformFilter(n,v,i,r)}}function VignetteFilter(){this.name="Vignette",this.isDirAnimatable=!1,this.defaultValues={amount:.3},this.valueRanges={amount:{min:0,max:1}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.amount?this.defaultValues.amount:a.amount,l=document.createElement("canvas");l.width=e,l.height=i;var h,o=l.getContext("2d"),u=Math.sqrt(Math.pow(e/2,2)+Math.pow(i/2,2));o.putImageData(t,0,0),o.globalCompositeOperation="source-over",h=o.createRadialGradient(e/2,i/2,0,e/2,i/2,u),h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(.5,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,"+s+")"),o.fillStyle=h,o.fillRect(0,0,e,i),n=o.getImageData(0,0,e,i).data;for(var d=0;df)e[0]=t,e[1]=a;else{var o=Math.sqrt(n),u=l*Math.sin(o/s*Math.PI*2-h);u*=(d-o)/d,0!==o&&(u*=s/o),e[0]=t+i*u,e[1]=a+r*u}};t.transformFilter(n,c,i,r)}}var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter,maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter,triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter};"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=JSManipulate); diff --git a/script/package.json b/script/package.json new file mode 100644 index 0000000..7285e10 --- /dev/null +++ b/script/package.json @@ -0,0 +1,15 @@ +{ + "author": "Joel Besada", + "name": "jsmanipulate", + "version": "1.0.0", + "description": "JSManipulate is an image filter and effects library written in Javascript for client-side manipulation of images on a web page.", + "main": "jsmanipulate.js", + "license": "MIT", + "keywords": ["canvas", "image", "images", "graphics", "filter", "filters", "manipulation", "manipulate", "distortion", "distort", "effects", "blur", "sharpen", "emboss", "invert"], + "homepage": "http://joelb.me/jsmanipulate/", + "repository": "git://github.com/JoelBesada/JSManipulate.git", + "dependencies": { + "canvas": "1.2.7" + }, + "engines": { "node": "*" } +} From a3eb5196ea0e262601422e211998ab9e39f03080 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Tue, 15 Sep 2015 15:35:20 -0700 Subject: [PATCH 2/8] Changed file hierarchy --- filters/blur.js | 97 ++ filters/brightness.js | 42 + filters/bump.js | 27 + filters/circlesmear.js | 62 + filters/contrast.js | 34 + filters/crosssmear.js | 68 + filters/diffusion.js | 41 + filters/dither.js | 99 ++ filters/edge.js | 63 + filters/emboss.js | 72 ++ filters/exposure.js | 30 + filters/filterutils.js | 214 ++++ filters/gain.js | 41 + filters/gamma.js | 33 + filters/grayscale.js | 21 + filters/hue.js | 39 + filters/invert.js | 22 + filters/kaleidoscope.js | 52 + filters/lensdistortion.js | 66 + filters/linesmear.js | 130 ++ filters/maximum.js | 45 + filters/median.js | 49 + filters/minimum.js | 45 + filters/noise.js | 42 + filters/oil.js | 83 ++ filters/opacity.js | 25 + filters/pinch.js | 67 + filters/pixelation.js | 45 + filters/posterize.js | 33 + filters/rgbadjust.js | 35 + filters/saturation.js | 39 + filters/sawtoothripple.js | 44 + filters/sepia.js | 41 + filters/sharpen.js | 25 + filters/sineripple.js | 44 + filters/solarize.js | 27 + filters/sparkle.js | 72 ++ filters/squaresmear.js | 62 + filters/threshold.js | 30 + filters/triangleripple.js | 44 + filters/twirl.js | 54 + filters/vignette.js | 38 + filters/waterripple.js | 63 + jsmanipulate.js | 2316 ++++++++++++++++++++++++++++++++++ minified/jsmanipulate.min.js | 15 + package.json | 18 + 46 files changed, 4654 insertions(+) create mode 100644 filters/blur.js create mode 100644 filters/brightness.js create mode 100644 filters/bump.js create mode 100644 filters/circlesmear.js create mode 100644 filters/contrast.js create mode 100644 filters/crosssmear.js create mode 100644 filters/diffusion.js create mode 100644 filters/dither.js create mode 100644 filters/edge.js create mode 100644 filters/emboss.js create mode 100644 filters/exposure.js create mode 100644 filters/filterutils.js create mode 100644 filters/gain.js create mode 100644 filters/gamma.js create mode 100644 filters/grayscale.js create mode 100644 filters/hue.js create mode 100644 filters/invert.js create mode 100644 filters/kaleidoscope.js create mode 100644 filters/lensdistortion.js create mode 100644 filters/linesmear.js create mode 100644 filters/maximum.js create mode 100644 filters/median.js create mode 100644 filters/minimum.js create mode 100644 filters/noise.js create mode 100644 filters/oil.js create mode 100644 filters/opacity.js create mode 100644 filters/pinch.js create mode 100644 filters/pixelation.js create mode 100644 filters/posterize.js create mode 100644 filters/rgbadjust.js create mode 100644 filters/saturation.js create mode 100644 filters/sawtoothripple.js create mode 100644 filters/sepia.js create mode 100644 filters/sharpen.js create mode 100644 filters/sineripple.js create mode 100644 filters/solarize.js create mode 100644 filters/sparkle.js create mode 100644 filters/squaresmear.js create mode 100644 filters/threshold.js create mode 100644 filters/triangleripple.js create mode 100644 filters/twirl.js create mode 100644 filters/vignette.js create mode 100644 filters/waterripple.js create mode 100644 jsmanipulate.js create mode 100644 minified/jsmanipulate.min.js create mode 100644 package.json diff --git a/filters/blur.js b/filters/blur.js new file mode 100644 index 0000000..0834c11 --- /dev/null +++ b/filters/blur.js @@ -0,0 +1,97 @@ +/** + * Blurs the image with Gaussian blur. + */ +function BlurFilter(){ + this.name = "Blur"; + this.defaultValues = { + amount : 3 + }; + this.valueRanges = { + amount : {min:0, max:10} + }; + this.filter = function(input,values){ + var width = input.width; + var width4 = width << 2; + var height = input.height; + var inputData = input.data; + var q; + var amount = values.amount; + if (amount < 0.0) { + amount = 0.0; + } + if (amount >= 2.5) { + q = 0.98711 * amount - 0.96330; + } else if (amount >= 0.5) { + q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * amount); + } else { + q = 2 * amount * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5)); + } + var qq = q * q; + var qqq = qq * q; + var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq); + var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0; + var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0; + var b3 = (0.422205 * qqq) / b0; + var bigB = 1.0 - (b1 + b2 + b3); + for (var c = 0; c < 3; c++) { + for (var y = 0; y < height; y++) { + var index = y * width4 + c; + var indexLast = y * width4 + ((width - 1) << 2) + c; + var pixel = inputData[index]; + var ppixel = pixel; + var pppixel = ppixel; + var ppppixel = pppixel; + for (; index <= indexLast; index += 4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + index = y * width4 + ((width - 1) << 2) + c; + indexLast = y * width4 + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index >= indexLast; index -= 4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + } + } + for (var c = 0; c < 3; c++) { + for (var x = 0; x < width; x++) { + var index = (x << 2) + c; + var indexLast = (height - 1) * width4 + (x << 2) + c; + var pixel = inputData[index]; + var ppixel = pixel; + var pppixel = ppixel; + var ppppixel = pppixel; + for (; index <= indexLast; index += width4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + index = (height - 1) * width4 + (x << 2) + c; + indexLast = (x << 2) + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index >= indexLast; index -= width4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + } + } + } +} diff --git a/filters/brightness.js b/filters/brightness.js new file mode 100644 index 0000000..c456acb --- /dev/null +++ b/filters/brightness.js @@ -0,0 +1,42 @@ +/** + * Adjusts the brightness of the image by going over to HSV values. + * Negative values decrease brightness while positive values increase brightness. + */ +function BrightnessFilter(){ + this.name = "Brightness"; + this.defaultValues = { + amount : 0.0 + }; + this.valueRanges = { + amount : {min:-1.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); + hsv[2] += amount; + if(hsv[2] < 0){ + hsv[2] = 0; + } else if (hsv[2] > 1){ + hsv[2] = 1; + } + var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = rgb[i]; + } + } + } + } +} diff --git a/filters/bump.js b/filters/bump.js new file mode 100644 index 0000000..9318f34 --- /dev/null +++ b/filters/bump.js @@ -0,0 +1,27 @@ +/** + * Embosses the edges of the image. + * This filter takes no parameters but can be applied several times for + * further effect. + */ +function BumpFilter(){ + this.name = "Bump"; + this.defaultValues = { + }; + this.valueRanges = { + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var matrix = [-1,-1, 0, + -1, 1, 1, + 0, 1, 1]; + filterUtils.convolveFilter(inputData,matrix,width,height); + } +} diff --git a/filters/circlesmear.js b/filters/circlesmear.js new file mode 100644 index 0000000..9b13293 --- /dev/null +++ b/filters/circlesmear.js @@ -0,0 +1,62 @@ +/** + * Smears out the image with circular shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. + */ +function CircleSmearFilter(){ + this.name = "Circle Smear"; + this.defaultValues = { + size : 4, + density : 0.5, + mix : 0.5, + }; + this.valueRanges = { + size : {min:1, max:10}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0}, + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + if(size < 1){ size = 1;} + size = parseInt(size); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var radius = size+1; + var radius2 = radius*radius; + var numShapes = parseInt(2*density/30*width*height / 2); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + for(var x = sx - radius; x < sx + radius + 1; x++){ + for(var y = sy - radius; y < sy + radius + 1; y++){ + var f = (x - sx) * (x - sx) + (y - sy) * (y - sy); + if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/contrast.js b/filters/contrast.js new file mode 100644 index 0000000..e7ea429 --- /dev/null +++ b/filters/contrast.js @@ -0,0 +1,34 @@ +/** + * Adjusts the contrast of the image. + */ +function ContrastFilter(){ + this.name = "Contrast"; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + if(amount < 0){ + amount = 0.0; + } + var table = []; + + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 * (((i/255)-0.5)*amount+0.5)); + } + filterUtils.tableFilter(inputData,table,width,height); + } +} diff --git a/filters/crosssmear.js b/filters/crosssmear.js new file mode 100644 index 0000000..6b06544 --- /dev/null +++ b/filters/crosssmear.js @@ -0,0 +1,68 @@ +/** + * Smears out the image with cross shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + */ +function CrossSmearFilter(){ + this.name = "Cross Smear"; + this.defaultValues = { + distance : 8, + density : 0.5, + mix : 0.5, + }; + this.valueRanges = { + distance : {min:0, max:30}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0}, + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; + if(distance < 0){ distance = 0;} + distance = parseInt(distance); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var numShapes = parseInt(2*density*width * height / (distance + 1)); + for(var i = 0; i < numShapes; i++){ + var x = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var y = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var length = (Math.random()*Math.pow(2,32)) % distance + 1; + var rgb2 = [inputData[(y*width+x)*4],inputData[(y*width+x)*4+1],inputData[(y*width+x)*4+2],inputData[(y*width+x)*4+3]]; + for (var x1 = x-length; x1 < x+length+1; x1++) { + if(x1 >= 0 && x1 < width){ + var rgb1 = [outputData[(y*width+x1)*4],outputData[(y*width+x1)*4+1],outputData[(y*width+x1)*4+2],outputData[(y*width+x1)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x1)*4+k] = mixedRGB[k]; + } + } + + } + for (var y1 = y-length; y1 < y+length+1; y1++) { + if(y1 >= 0 && y1 < height){ + var rgb1 = [outputData[(y1*width+x)*4],outputData[(y1*width+x)*4+1],outputData[(y1*width+x)*4+2],outputData[(y1*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y1*width+x)*4+k] = mixedRGB[k]; + } + } + + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/diffusion.js b/filters/diffusion.js new file mode 100644 index 0000000..9ba8594 --- /dev/null +++ b/filters/diffusion.js @@ -0,0 +1,41 @@ +/** + * Diffuses the image creating a frosted glass effect. + */ +function DiffusionFilter(){ + this.name = "Diffusion"; + this.defaultValues = { + scale: 4 + }; + this.valueRanges = { + scale: {min: 1, max: 100} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var scale = (values.scale === undefined) ? this.defaultValues.scale : values.scale; + var out = []; + var outputData = []; + var sinTable = []; + var cosTable = []; + for(var i = 0; i < 256; i++){ + var angle = Math.PI*2*i/256; + sinTable[i] = scale*Math.sin(angle); + cosTable[i] = scale*Math.cos(angle); + } + transInverse = function (x,y,out){ + var angle = parseInt(Math.random() * 255); + var distance = Math.random(); + out[0] = x + distance * sinTable[angle]; + out[1] = y + distance * cosTable[angle]; + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/dither.js b/filters/dither.js new file mode 100644 index 0000000..ae3fbff --- /dev/null +++ b/filters/dither.js @@ -0,0 +1,99 @@ +/** + * Dithers the image to the specified number of colors. Setting color to false + * grayscales the image. + */ +function DitherFilter(){ + this.name = "Dither"; + this.defaultValues = { + levels : 3, + color : true + }; + this.valueRanges = { + levels : {min:2, max:30}, + color : {min:false, max:true} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var i=0; i < inputData.length; i++) { + outputData[i] = 0; + }; + if(values === undefined){ values = this.defaultValues; } + var levels = (values.levels === undefined) ? this.defaultValues.levels : values.levels; + var color = (values.color === undefined) ? this.defaultValues.color : values.color; + if(levels <= 1){ + levels = 1; + } + var matrix = [0,0,0, + 0,0,7, + 3,5,1]; + var sum = 7+3+5+1; + var index = 0; + var map = []; + for (var i=0; i < levels; i++) { + map[i] = parseInt(255* i / (levels-1)); + }; + var div = []; + for (var i=0; i < 256; i++) { + div[i] = parseInt(levels*i / 256); + }; + for (var y = 0; y < height; y++) { + var reverse = ((y & 1) == 1); + var direction; + if(reverse){ + index = (y*width+width-1)*4; + direction = -1 + } else { + index = y*width*4; + direction = 1; + } + for (var x = 0; x < width; x++) { + var r1 = inputData[index]; var g1 = inputData[index+1]; var b1 = inputData[index+2]; + if(!color){ + r1 = g1 = b1 = parseInt((r1+g1+b1) / 3); + } + var r2 = map[div[r1]];var g2 = map[div[g1]];var b2 = map[div[b1]]; + + outputData[index] = r2; outputData[index + 1] = g2; outputData[index+2] = b2; outputData[index+3] = inputData[index+3]; + + var er = r1-r2; var eg = g1-g2; var eb = b1-b2; + + for (var i = -1; i <= 1; i++) { + var iy = i+y; + if (0 <= iy && iy < height) { + for (var j = -1; j <= 1; j++) { + var jx = j+x; + if (0 <= jx && jx < width) { + var w; + if (reverse){ + w = matrix[(i+1)*3-j+1]; + } else{ + w = matrix[(i+1)*3+j+1]; + } + if (w != 0) { + var k = (reverse) ? index - j*4 : index + j*4; + r1 = inputData[k]; g1 = inputData[k+1]; b1 = inputData[k+2]; + var factor = w/sum; + r1 += er * factor; g1 += eg * factor; b1 += eb * factor; + inputData[k] = r1; inputData[k+1] = g1 ;inputData[k+2] = b1; + } + } + } + } + } + index += direction*4; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/edge.js b/filters/edge.js new file mode 100644 index 0000000..6d3e50f --- /dev/null +++ b/filters/edge.js @@ -0,0 +1,63 @@ +/** + * Highlights the edges of the image. + */ +function EdgeFilter(){ + this.name = "Edge Detection"; + var matrixH = [-1,-2,-1, + 0, 0, 0, + 1, 2, 1]; + var matrixV = [-1, 0, 1, + -2, 0, 2, + -1, 0, 1]; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var rh = 0; gh = 0; bh = 0; + var rv = 0; gv = 0; bv = 0; + for(var row = -1; row <= 1; row++){ + var iy = y+row; + var ioffset; + if(iy >= 0 && iy < height){ + ioffset = iy*width*4; + } else { + ioffset = y*width*4; + } + var moffset = 3*(row+1)+1; + for(var col = -1; col <= 1; col++){ + var ix = x+col; + if(!(ix >= 0 && ix < width)){ + ix = x; + } + ix *= 4; + var r = inputData[ioffset+ix]; + var g = inputData[ioffset+ix+1]; + var b = inputData[ioffset+ix+2]; + var h = matrixH[moffset+col]; + var v = matrixV[moffset+col]; + rh += parseInt(h*r); + bh += parseInt(h*g); + gh += parseInt(h*b); + rv += parseInt(v*r); + gv += parseInt(v*g); + bv += parseInt(v*b); + } + } + r = parseInt(Math.sqrt(rh*rh + rv*rv) / 1.8); + g = parseInt(Math.sqrt(gh*gh + gv*gv) / 1.8); + b = parseInt(Math.sqrt(bh*bh + bv*bv) / 1.8); + + outputData[pixel] = r; + outputData[pixel+1] = g; + outputData[pixel+2] = b; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/emboss.js b/filters/emboss.js new file mode 100644 index 0000000..c4b673d --- /dev/null +++ b/filters/emboss.js @@ -0,0 +1,72 @@ +/** + * Embosses the image with a simulated light source. + * Angle and elevation sets the position of the light. + */ +function EmbossFilter(){ + this.name = "Emboss"; + this.defaultValues = { + height : 1, + angle : 135, + elevation : 30 + }; + this.valueRanges = { + height : {min:1, max:10}, + angle : {min:0, max:360}, + elevation : {min:0, max:180} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var bumpHeight = (values.height === undefined) ? this.defaultValues.height : values.height; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var elevation = (values.elevation === undefined) ? this.defaultValues.elevation : values.elevation; + angle = angle / 180 * Math.PI; + elevation = elevation / 180 * Math.PI; + var width45 = 3 * bumpHeight; + var pixelScale = 255.9; + + var bumpPixels = []; + var bumpMapWidth = width; + var bumpMapHeight = height; + for(var i = 0; i < inputData.length; i+=4){ + bumpPixels[i/4] = (inputData[i] + inputData[i+1] + inputData[i+2])/3 + } + var Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL; + var shade, background; + + Lx = parseInt(Math.cos(angle) * Math.cos(elevation) * pixelScale); + Ly = parseInt(Math.sin(angle) * Math.cos(elevation) * pixelScale); + Lz = parseInt(Math.sin(elevation) * pixelScale); + + Nz = parseInt(6 * 255 / width45); + Nz2 = Nz * Nz; + NzLz = Nz * Lz; + background = Lz; + + var bumpIndex = 0; + + for (var y = 0; y < height; y++, bumpIndex += bumpMapWidth) { + var s1 = bumpIndex; + var s2 = s1 + bumpMapWidth; + var s3 = s2 + bumpMapWidth; + for (var x = 0; x < width; x++, s1++, s2++, s3++) { + var pixel = (y*width + x)*4; + if (y != 0 && y < height-2 && x != 0 && x < width-2) { + Nx = bumpPixels[s1-1] + bumpPixels[s2-1] + bumpPixels[s3-1] - bumpPixels[s1+1] - bumpPixels[s2+1] - bumpPixels[s3+1]; + Ny = bumpPixels[s3-1] + bumpPixels[s3] + bumpPixels[s3+1] - bumpPixels[s1-1] - bumpPixels[s1] - bumpPixels[s1+1]; + if (Nx == 0 && Ny == 0){ + shade = background; + } else if ((NdotL = Nx*Lx + Ny*Ly + NzLz) < 0){ + shade = 0; + } else { + shade = parseInt(NdotL / Math.sqrt(Nx*Nx + Ny*Ny + Nz2)); + } + } else { + shade = background; + } + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = shade; + } + } + } +} diff --git a/filters/exposure.js b/filters/exposure.js new file mode 100644 index 0000000..1b3f079 --- /dev/null +++ b/filters/exposure.js @@ -0,0 +1,30 @@ +/** + * Adjust simulated exposure values on the image. + */ +function ExposureFilter(){ + this.name = "Exposure"; + this.defaultValues = { + exposure : 1.0 + }; + this.valueRanges = { + exposure : {min:0, max:5} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var exposure = (values.exposure === undefined) ? this.defaultValues.exposure : values.exposure; + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 *(1-Math.exp(-(i/255) * exposure))); + } + filterUtils.tableFilter(inputData, table, width, height); + } +} diff --git a/filters/filterutils.js b/filters/filterutils.js new file mode 100644 index 0000000..293e1e5 --- /dev/null +++ b/filters/filterutils.js @@ -0,0 +1,214 @@ +/** + * Contains common filter functions. + */ +function FilterUtils(){ + this.HSVtoRGB = function (h, s, v){ + var r, g, b; + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + switch(i % 6){ + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + return [r * 255, g * 255, b * 255]; + } + this.RGBtoHSV = function (r, g, b){ + r = r/255, g = g/255, b = b/255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + var d = max - min; + s = max == 0 ? 0 : d / max; + if(max == min){ + h = 0; + }else{ + switch(max){ + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return [h, s, v]; + } + this.getPixel = function (pixels,x,y,width,height){ + var pix = (y*width + x)*4; + if (x < 0 || x >= width || y < 0 || y >= height) { + return [pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 1], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 2], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 3]]; + } + return [pixels[pix],pixels[pix+1],pixels[pix+2],pixels[pix+3]] + } + var haveNextGaussian = false; + var nextGaussian; + this.gaussianRandom = function(){ + if(haveNextGaussian){ + haveNextGaussian = false; + return nextGaussian; + } else { + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + var mult = Math.sqrt(-2 * Math.log(s)/s); + nextGaussian = v2 * mult; + haveNextGaussian = true; + return v1 * mult; + } + } + this.clampPixel = function (x,a,b){ + return (x < a) ? a : (x > b) ? b : x; + } + this.triangle = function(x){ + var r = this.mod(x, 1); + return 2*(r < 0.5 ? r : 1-r); + } + this.mod = function(a,b){ + var n = parseInt(a/b); + a -= n*b; + if(a < 0){ + return a + b; + } + return a; + } + this.mixColors = function(t, rgb1, rgb2){ + var r = this.linearInterpolate(t,rgb1[0],rgb2[0]); + var g = this.linearInterpolate(t,rgb1[1],rgb2[1]); + var b = this.linearInterpolate(t,rgb1[2],rgb2[2]); + var a = this.linearInterpolate(t,rgb1[3],rgb2[3]); + return [r,g,b,a]; + } + + this.linearInterpolate = function(t,a,b){ + return a + t * (b-a); + } + this.bilinearInterpolate = function (x,y,nw,ne,sw,se){ + var m0, m1; + var r0 = nw[0]; var g0 = nw[1]; var b0 = nw[2]; var a0 = nw[3]; + var r1 = ne[0]; var g1 = ne[1]; var b1 = ne[2]; var a1 = ne[3]; + var r2 = sw[0]; var g2 = sw[1]; var b2 = sw[2]; var a2 = sw[3]; + var r3 = se[0]; var g3 = se[1]; var b3 = se[2]; var a3 = se[3]; + var cx = 1.0 - x; var cy = 1.0 - y; + + m0 = cx * a0 + x * a1; + m1 = cx * a2 + x * a3; + var a = cy * m0 + y * m1; + + m0 = cx * r0 + x * r1; + m1 = cx * r2 + x * r3; + var r = cy * m0 + y * m1; + + m0 = cx * g0 + x * g1; + m1 = cx * g2 + x * g3; + var g = cy * m0 + y * m1; + + m0 = cx * b0 + x * b1; + m1 = cx * b2 + x * b3; + var b =cy * m0 + y * m1; + return [r,g,b,a]; + } + this.tableFilter = function (inputData, table, width, height){ + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = table[inputData[pixel+i]]; + } + } + } + } + this.convolveFilter = function(inputData, matrix, width, height){ + var outputData = []; + var rows, cols; + rows = cols = Math.sqrt(matrix.length); + var rows2 = parseInt(rows/2); + var cols2 = parseInt(cols/2); + var trace = true; + for(var y = 0; y < height; y++){ + for (var x = 0; x < width; x++){ + var pixel = (y*width + x)*4; + var r = 0, g = 0, b = 0; + for(var row = -rows2; row <= rows2; row++){ + var iy = y+row; + var ioffset; + if (0 <= iy && iy < height) { + ioffset = iy*width; + } else { + ioffset = y*width; + } + var moffset = cols*(row+rows2)+cols2; + for (var col = -cols2; col <= cols2; col++) { + var f = matrix[moffset+col]; + + if (f != 0) { + var ix = x+col; + if (!(0 <= ix && ix < width)) { + ix = x; + } + var iPixel = (ioffset+ix)*4; + r += f * inputData[iPixel]; + g += f * inputData[iPixel+1]; + b += f * inputData[iPixel+2]; + } + } + } + outputData[pixel] = parseInt(r+0.5); + outputData[pixel+1] = parseInt(g+0.5); + outputData[pixel+2] = parseInt(b+0.5); + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } + this.transformFilter = function(inputData, transformInverse, width, height){ + var out = []; + var outputData = []; + for(var j = 0; j < inputData.length; j++){ + outputData[j] = inputData[j]; + } + for(var y = 0; y < height; y++){ + for (var x = 0; x < width; x++){ + var pixel = (y*width + x)*4; + transformInverse.apply(this,[x,y,out]); + var srcX = Math.floor(out[0]); + var srcY = Math.floor(out[1]); + var xWeight = out[0]-srcX; + var yWeight = out[1]-srcY; + var nw,ne,sw,se; + if(srcX >= 0 && srcX < width-1 && srcY >= 0 && srcY < height-1){ + var i = (width*srcY + srcX)*4; + nw = [inputData[i],inputData[i+1],inputData[i+2],inputData[i+3]]; + ne = [inputData[i+4],inputData[i+5],inputData[i+6],inputData[i+7]]; + sw = [inputData[i+width*4],inputData[i+width*4+1],inputData[i+width*4+2],inputData[i+width*4+3]]; + se = [inputData[i+(width + 1)*4],inputData[i+(width + 1)*4+1],inputData[i+(width + 1)*4+2],inputData[i+(width + 1)*4+3]]; + + } else { + nw = this.getPixel( inputData, srcX, srcY, width, height ); + ne = this.getPixel( inputData, srcX+1, srcY, width, height ); + sw = this.getPixel( inputData, srcX, srcY+1, width, height ); + se = this.getPixel( inputData, srcX+1, srcY+1, width, height ); + } + var rgba = this.bilinearInterpolate(xWeight,yWeight,nw,ne,sw,se); + outputData[pixel] = rgba[0]; + outputData[pixel + 1] = rgba[1]; + outputData[pixel + 2] = rgba[2]; + outputData[pixel + 3] = rgba[3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/gain.js b/filters/gain.js new file mode 100644 index 0000000..e3f31f8 --- /dev/null +++ b/filters/gain.js @@ -0,0 +1,41 @@ +/** + * Adjusts the gain and bias of the image. Gain alters the contrast while bias biases + * colors towards lighter or darker. + */ +function GainFilter(){ + this.name = "Gain/Bias"; + this.defaultValues = { + gain: 0.5, + bias: 0.5 + }; + this.valueRanges = { + gain: {min:0.0, max:1.0}, + bias: {min:0.0, max:1.0} + }; + var table = []; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var gain = (values.gain === undefined) ? this.defaultValues.gain : values.gain; + var bias = (values.bias === undefined) ? this.defaultValues.bias : values.bias; + + var table = []; + + for(var i = 0; i < 256; i++){ + var val = i/255; + var k = (1/gain-2) * (1-2*val); + val = (val < 0.5) ? val/(k+1) : (k-val)/(k-1); + val /= (1/bias-2)*(1-val)+1; + table[i] = parseInt(255 * val); + } + filterUtils.tableFilter(inputData,table,width,height); + } +} diff --git a/filters/gamma.js b/filters/gamma.js new file mode 100644 index 0000000..aab1c1d --- /dev/null +++ b/filters/gamma.js @@ -0,0 +1,33 @@ +/** + * Adjusts the gamma values of the image. Values over 1 increase the gamma while values over 0 decrease gamma. + */ +function GammaFilter(){ + this.name = "Gamma"; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + if(amount < 0){ + amount = 0.0; + } + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = 255 * Math.pow(i/255, 1/amount) + 0.5; + } + filterUtils.tableFilter(inputData,table,width,height); + } +} diff --git a/filters/grayscale.js b/filters/grayscale.js new file mode 100644 index 0000000..d53954d --- /dev/null +++ b/filters/grayscale.js @@ -0,0 +1,21 @@ +/** + * Sets the image to grayscale. + */ +function GrayscaleFilter(){ + this.name = "Grayscale"; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = luma; + } + } + } +} diff --git a/filters/hue.js b/filters/hue.js new file mode 100644 index 0000000..0b25787 --- /dev/null +++ b/filters/hue.js @@ -0,0 +1,39 @@ +/** + * Adjusts the hue of the image by going over to HSV values. + */ +function HueFilter(){ + this.name = "Hue"; + this.defaultValues = { + amount : 0.0 + }; + this.valueRanges = { + amount : {min:-1.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); + hsv[0] += amount; + while(hsv[0] < 0){ + hsv[0] += 360; + } + var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = rgb[i]; + } + } + } + } +} diff --git a/filters/invert.js b/filters/invert.js new file mode 100644 index 0000000..5711ee5 --- /dev/null +++ b/filters/invert.js @@ -0,0 +1,22 @@ +/** + * Inverts the colors of the image. + */ +function InvertFilter(){ + this.name = "Invert"; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = 255 - inputData[pixel+i]; + } + } + } + } +} diff --git a/filters/kaleidoscope.js b/filters/kaleidoscope.js new file mode 100644 index 0000000..7322519 --- /dev/null +++ b/filters/kaleidoscope.js @@ -0,0 +1,52 @@ +/** + * Creates a kaleidoscope effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function KaleidoscopeFilter(){ + this.name = "Kaleidoscope"; + this.defaultValues = { + angle : 0, + rotation : 0, + sides : 3, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + angle : {min: 0, max: 360}, + rotation : {min: 0, max: 360}, + sides : {min: 1, max: 30}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var rotation = (values.rotation === undefined) ? this.defaultValues.rotation : values.rotation; + var sides = (values.sides === undefined) ? this.defaultValues.sides : values.sides; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var iCenterX = width * centerX; var iCenterY = height * centerY; + angle = angle/180 * Math.PI; + rotation = rotation/180 * Math.PI; + var transInverse = function(x,y,out){ + var dx = x - iCenterX; + var dy = y - iCenterY; + var r = Math.sqrt(dx*dx + dy*dy); + var theta = Math.atan2(dy,dx) - angle - rotation; + theta = filterUtils.triangle(theta/Math.PI*sides*0.5); + theta += angle; + out[0] = iCenterX + r*Math.cos(theta); + out[1] = iCenterY + r*Math.sin(theta); + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/lensdistortion.js b/filters/lensdistortion.js new file mode 100644 index 0000000..a7f45ed --- /dev/null +++ b/filters/lensdistortion.js @@ -0,0 +1,66 @@ +/** + * Applies a fisheye lens distortion effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function LensDistortionFilter(){ + this.name = "Lens Distortion"; + this.defaultValues = { + refraction : 1.5, + radius : 50, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + refraction : {min: 1, max: 10}, + radius : {min: 1, max: 200}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var refraction = (values.refraction === undefined) ? this.defaultValues.refraction : values.refraction; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var x2 = dx*dx; + var y2 = dy*dy; + if (y2 >= (radius2 - (radius2*x2)/radius2)) { + out[0] = x; + out[1] = y; + } else { + var rRefraction = 1.0 / refraction; + + var z = Math.sqrt((1.0 - x2/radius2 - y2/radius2) * radius2); + var z2 = z*z; + + var xAngle = Math.acos(dx / Math.sqrt(x2+z2)); + var angle1 = Math.PI/2 - xAngle; + var angle2 = Math.asin(Math.sin(angle1)*rRefraction); + angle2 = Math.PI/2 - xAngle - angle2; + out[0] = x - Math.tan(angle2)*z; + + var yAngle = Math.acos(dy / Math.sqrt(y2+z2)); + angle1 = Math.PI/2 - yAngle; + angle2 = Math.asin(Math.sin(angle1)*rRefraction); + angle2 = Math.PI/2 - yAngle - angle2; + out[1] = y - Math.tan(angle2)*z; + } + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/linesmear.js b/filters/linesmear.js new file mode 100644 index 0000000..4b81c64 --- /dev/null +++ b/filters/linesmear.js @@ -0,0 +1,130 @@ +/** + * Smears out the image with line shapes to create a painting style effect. Mix specifies + * the intensity of the effect. + */ +function LineSmearFilter(){ + this.name = "Line Smear"; + this.defaultValues = { + distance : 8, + density : 0.5, + angle : 0, + mix : 0.5, + }; + this.valueRanges = { + distance : {min:1, max:30}, + density : {min:0.0, max:1.0}, + angle : {min:0, max:360}, + mix : {min:0.0, max:1.0}, + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; + if(distance < 1){ distance = 1;} + distance = parseInt(distance); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + angle = angle/180*Math.PI; + var sinAngle = Math.sin(angle); + var cosAngle = Math.cos(angle); + var numShapes = parseInt(2*density*width*height / 2); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var length = (Math.random()*Math.pow(2,32) & 0x7fffffff) % distance + 1; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + var dx = parseInt(length*cosAngle); + var dy = parseInt(length*sinAngle); + + var x0 = sx-dx; + var y0 = sy-dy; + var x1 = sx+dx; + var y1 = sy+dy; + var x, y, d, incrE, incrNE, ddx, ddy; + + if (x1 < x0){ + ddx = -1; + } else { + ddx = 1; + } + if (y1 < y0){ + ddy = -1; + } else { + ddy = 1; + } + dx = x1-x0; + dy = y1-y0; + dx = Math.abs(dx); + dy = Math.abs(dy); + x = x0; + y = y0; + + if (x < width && x >= 0 && y < height && y >= 0) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + if (Math.abs(dx) > Math.abs(dy)) { + d = 2*dy-dx; + incrE = 2*dy; + incrNE = 2*(dy-dx); + + while (x != x1) { + if (d <= 0) + d += incrE; + else { + d += incrNE; + y += ddy; + } + x += ddx; + if (x < width && x >= 0 && y < height && y >= 0) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } else { + d = 2*dx-dy; + incrE = 2*dx; + incrNE = 2*(dx-dy); + + while (y != y1) { + if (d <= 0) + d += incrE; + else { + d += incrNE; + x += ddx; + } + y += ddy; + if (x < width && x >= 0 && y < height && y >= 0) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/maximum.js b/filters/maximum.js new file mode 100644 index 0000000..3d22357 --- /dev/null +++ b/filters/maximum.js @@ -0,0 +1,45 @@ +/** + * Replaces every pixel with the maximum RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MaximumFilter(){ + this.name = "Maximum"; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var maxR = 0; + var maxG = 0; + var maxB = 0; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + maxR = Math.max(maxR,inputData[iPixel]); + maxG = Math.max(maxG,inputData[iPixel+1]); + maxB = Math.max(maxB,inputData[iPixel+2]); + } + } + } + } + outputData[pixel] = maxR; + outputData[pixel+1] = maxG; + outputData[pixel+2] = maxB; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/median.js b/filters/median.js new file mode 100644 index 0000000..e4afbd0 --- /dev/null +++ b/filters/median.js @@ -0,0 +1,49 @@ +/** + * Replaces every pixel with the median RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MedianFilter(){ + this.name = "Median"; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var rList = []; + var gList = []; + var bList = []; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + rList.push(inputData[iPixel]); + gList.push(inputData[iPixel+1]); + bList.push(inputData[iPixel+2]); + + } + } + } + } + rList.sort(function(a,b){return a-b}); + gList.sort(function(a,b){return a-b}); + bList.sort(function(a,b){return a-b}); + outputData[pixel] = rList[4]; + outputData[pixel+1] = gList[4]; + outputData[pixel+2] = bList[4]; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/minimum.js b/filters/minimum.js new file mode 100644 index 0000000..bce3b28 --- /dev/null +++ b/filters/minimum.js @@ -0,0 +1,45 @@ +/** + * Replaces every pixel with the minimum RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MinimumFilter(){ + this.name = "Minimum"; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var minR = 255; + var minG = 255; + var minB = 255; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + minR = Math.min(minR,inputData[iPixel]); + minG = Math.min(minG,inputData[iPixel+1]); + minB = Math.min(minB,inputData[iPixel+2]); + } + } + } + } + outputData[pixel] = minR; + outputData[pixel+1] = minG; + outputData[pixel+2] = minB; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/noise.js b/filters/noise.js new file mode 100644 index 0000000..90ec11d --- /dev/null +++ b/filters/noise.js @@ -0,0 +1,42 @@ +/** + * Creates random noise on the image, with or without color. + */ +function NoiseFilter(){ + this.name = "Noise"; + this.defaultValues = { + amount : 25, + density : 1, + monochrome : true + }; + this.valueRanges = { + amount : {min:0, max:100}, + density : {min:0, max:1.0}, + monochrome : {min:false, max:true} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var monochrome = (values.monochrome === undefined) ? this.defaultValues.monochrome : values.monochrome; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + if(Math.random() <= density){ + if(monochrome){ + var n = parseInt((2*Math.random()-1) * amount); + inputData[pixel] += n; + inputData[pixel+1] += n; + inputData[pixel+2] += n; + } else { + for(var i = 0; i < 3; i++){ + var n = parseInt((2*Math.random()-1) * amount); + inputData[pixel+i] += n; + } + } + } + } + } + } +} diff --git a/filters/oil.js b/filters/oil.js new file mode 100644 index 0000000..486c121 --- /dev/null +++ b/filters/oil.js @@ -0,0 +1,83 @@ +/** + * Produces an oil painting effect on the image. + * NOTE: This filter can be very slow, especially at higher ranges. Use with caution. + */ +function OilFilter(){ + this.name = "Oil Painting"; + this.defaultValues = { + range : 3, + }; + this.valueRanges = { + range : {min:0, max:5}, + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + if(values === undefined){ values = this.defaultValues; } + var range = (values.range === undefined) ? this.defaultValues.range : values.range; + range = parseInt(range); + var index = 0; + var rHistogram = []; + var gHistogram = []; + var bHistogram = []; + var rTotal = []; + var gTotal = []; + var bTotal = []; + var levels = 256; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for (var i = 0; i < levels; i++){ + rHistogram[i] = gHistogram[i] = bHistogram[i] = rTotal[i] = gTotal[i] = bTotal[i] = 0; + } + for (var row = -range; row <= range; row++) { + var iy = y+row; + var ioffset; + if (0 <= iy && iy < height) { + ioffset = iy*width; + for (var col = -range; col <= range; col++) { + var ix = x+col; + if (0 <= ix && ix < width) { + var r = inputData[(ioffset+ix)*4] + var g = inputData[(ioffset+ix)*4+1] + var b = inputData[(ioffset+ix)*4+2] + var ri = r*levels/256; + var gi = g*levels/256; + var bi = b*levels/256; + rTotal[ri] += r; + gTotal[gi] += g; + bTotal[bi] += b; + rHistogram[ri]++; + gHistogram[gi]++; + bHistogram[bi]++; + } + } + } + } + var r = 0, g = 0, b = 0; + for (var i = 1; i < levels; i++) { + if (rHistogram[i] > rHistogram[r]){ + r = i; + } + if (gHistogram[i] > gHistogram[g]){ + g = i; + } + if (bHistogram[i] > bHistogram[b]){ + b = i; + } + } + r = rTotal[r] / rHistogram[r]; + g = gTotal[g] / gHistogram[g]; + b = bTotal[b] / bHistogram[b]; + outputData[pixel] = r; + outputData[pixel+1] = g; + outputData[pixel+2] = b; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/opacity.js b/filters/opacity.js new file mode 100644 index 0000000..4f87dd3 --- /dev/null +++ b/filters/opacity.js @@ -0,0 +1,25 @@ +/** + * Changes the opacity of the image. + */ +function OpacityFilter(){ + this.name = "Opacity"; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:1.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + inputData[pixel+3] = 255*amount; + } + } + } + +} diff --git a/filters/pinch.js b/filters/pinch.js new file mode 100644 index 0000000..abe7c67 --- /dev/null +++ b/filters/pinch.js @@ -0,0 +1,67 @@ +/** + * Pinches and whirls the image toward the center point. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function PinchFilter(){ + this.name = "Pinch/Whirl"; + this.defaultValues = { + amount : 0.5, + radius : 100, + angle : 0, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + amount : {min: -1.0, max: 1.0}, + radius : {min: 1, max: 200}, + angle : {min: 0, max: 360}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + angle = angle/180 * Math.PI; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + if(distance > radius2 || distance == 0){ + out[0] = x; + out[1] = y; + } else { + var d = Math.sqrt( distance / radius2 ); + var t = Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); + + dx *= t; + dy *= t; + + var e = 1 - d; + var a = angle * e * e; + + var s = Math.sin(a); + var c = Math.cos(a); + + out[0] = iCenterX + c*dx - s*dy; + out[1] = iCenterY + s*dx + c*dy; + } + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/pixelation.js b/filters/pixelation.js new file mode 100644 index 0000000..58d2a3d --- /dev/null +++ b/filters/pixelation.js @@ -0,0 +1,45 @@ +/** + * Pixelates the image i.e. divides the image into blocks of color. + */ +function PixelationFilter(){ + this.name = "Pixelation"; + this.defaultValues = { + size : 5 + }; + this.valueRanges = { + size : {min:1, max:50} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + size = parseInt(size); + var pixels = []; + for (var y = 0; y < height; y+=size) { + for (var x = 0; x < width; x+=size) { + var pixel = (y*width + x)*4; + var w = Math.min(size, width-x); + var h = Math.min(size, height-y); + var t = w*h; + var r = 0, g = 0, b = 0; + for(var by = y; by < y+h; by++){ + for(var bx = x; bx < x+w; bx++){ + var bPixel = (by*width + bx)*4; + r += inputData[bPixel]; + g += inputData[bPixel+1]; + b += inputData[bPixel+2]; + } + } + for(var by = y; by < y+h; by++){ + for(var bx = x; bx < x+w; bx++){ + var bPixel = (by*width + bx)*4; + inputData[bPixel] = r/t; + inputData[bPixel+1] = g/t; + inputData[bPixel+2] = b/t; + } + } + } + } + } +} diff --git a/filters/posterize.js b/filters/posterize.js new file mode 100644 index 0000000..b536052 --- /dev/null +++ b/filters/posterize.js @@ -0,0 +1,33 @@ +/** + * Posterizes the image, i.e. restricts the color values to a set amount of levels. + */ +function PosterizeFilter(){ + this.name = "Posterize"; + this.defaultValues = { + levels : 6 + }; + this.valueRanges = { + levels : {min:2, max:30 } + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var levels = (values.levels === undefined) ? this.defaultValues.levels : parseInt(values.levels); + if(levels <= 1){ + return; + } + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 * parseInt(i*levels/256) / (levels-1)); + } + filterUtils.tableFilter(inputData,table,width,height); + } +} diff --git a/filters/rgbadjust.js b/filters/rgbadjust.js new file mode 100644 index 0000000..99c3bf7 --- /dev/null +++ b/filters/rgbadjust.js @@ -0,0 +1,35 @@ +/** + * Adjust the factor of each RGB color value in the image. + */ +function RGBAdjustFilter(){ + this.name = "RGBAdjust"; + this.defaultValues = { + red: 1.0, + green: 1.0, + blue: 1.0 + }; + this.valueRanges = { + red: {min: 0.0, max: 2.0}, + green: {min: 0.0, max: 2.0}, + blue: {min: 0.0, max: 2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var red = (values.red === undefined) ? this.defaultValues.red : values.red; + var green = (values.green === undefined) ? this.defaultValues.green : values.green; + var blue = (values.blue === undefined) ? this.defaultValues.blue : values.blue; + if(red < 0){ red = 0; } + if(green < 0){ green = 0; } + if(blue < 0){ blue = 0; } + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + inputData[pixel] *= red; + inputData[pixel+1] *= green; + inputData[pixel+2] *= blue; + } + } + } +} diff --git a/filters/saturation.js b/filters/saturation.js new file mode 100644 index 0000000..1f46a42 --- /dev/null +++ b/filters/saturation.js @@ -0,0 +1,39 @@ +/** + * Adjusts the saturation value of the image. Values over 1 increase saturation while values below decrease saturation. + * For a true grayscale effect, use the grayscale filter instead. + */ +function SaturationFilter(){ + this.name = "Saturation"; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var RW = 0.3086; + var RG = 0.6084; + var RB = 0.0820; + var a = (1 - amount) * RW + amount; + var b = (1 - amount) * RW; + var c = (1 - amount) * RW; + var d = (1 - amount) * RG; + var e = (1 - amount) * RG + amount; + var f = (1 - amount) * RG; + var g = (1 - amount) * RB; + var h = (1 - amount) * RB; + var i = (1 - amount) * RB + amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + inputData[pixel] = a*inputData[pixel] + d*inputData[pixel+1] + g*inputData[pixel+2]; + inputData[pixel+1] = b*inputData[pixel] + e*inputData[pixel+1] + h*inputData[pixel+2]; + inputData[pixel+2] = c*inputData[pixel] + f*inputData[pixel+1] + i*inputData[pixel+2]; + } + } + } +} diff --git a/filters/sawtoothripple.js b/filters/sawtoothripple.js new file mode 100644 index 0000000..6c2002f --- /dev/null +++ b/filters/sawtoothripple.js @@ -0,0 +1,44 @@ +/** + * Creates ripples on the image horizontally/vertically in a sawtooth pattern. + */ +function SawtoothRippleFilter(){ + this.name = "Sawtooth Ripples"; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = filterUtils.mod(nx,1) + var fy = filterUtils.mod(ny,1) + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/sepia.js b/filters/sepia.js new file mode 100644 index 0000000..c8db3f3 --- /dev/null +++ b/filters/sepia.js @@ -0,0 +1,41 @@ +/** + * Creates a sepia effect on the image i.e. gives the image a yellow-brownish tone. + */ +function SepiaFilter(){ + this.name = "Sepia"; + this.defaultValues = { + amount : 10 + }; + this.valueRanges = { + amount : {min:0, max:30} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + amount *= 255/100; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; + var r,g,b; + r = g = b = luma; + r += 40; + g += 20; + b -= amount; + + inputData[pixel] = r; + inputData[pixel+1] = g; + inputData[pixel+2] = b; + } + } + } +} diff --git a/filters/sharpen.js b/filters/sharpen.js new file mode 100644 index 0000000..9669798 --- /dev/null +++ b/filters/sharpen.js @@ -0,0 +1,25 @@ +/** + * Sharpens the image slightly. For increased effect, apply the filter multiple times. + */ +function SharpenFilter(){ + this.name = "Sharpen"; + this.defaultValues = { + }; + this.valueRanges = { + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var matrix = [ 0,-0.2, 0, + -0.2,1.8,-0.2, + 0, -0.2, 0]; + filterUtils.convolveFilter(inputData,matrix,width,height); + } +} diff --git a/filters/sineripple.js b/filters/sineripple.js new file mode 100644 index 0000000..df179f1 --- /dev/null +++ b/filters/sineripple.js @@ -0,0 +1,44 @@ +/** + * Creates ripples on the image horizontally/vertically in a sine pattern. + */ +function SineRippleFilter(){ + this.name = "Sine Ripples"; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = Math.sin(nx); + var fy = Math.sin(ny); + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/solarize.js b/filters/solarize.js new file mode 100644 index 0000000..badc522 --- /dev/null +++ b/filters/solarize.js @@ -0,0 +1,27 @@ +/** + * Produces a solarization effect on the image. + */ +function SolarizeFilter(){ + this.name = "Solarize"; + this.defaultValues = { + }; + this.valueRanges = { + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var table = []; + for(var i = 0; i < 256; i++){ + var val = (i/255 > 0.5) ? 2*(i/255-0.5) : 2*(0.5-i/255) + table[i] = parseInt(255 * val); + } + filterUtils.tableFilter(inputData, table, width, height); + } +} diff --git a/filters/sparkle.js b/filters/sparkle.js new file mode 100644 index 0000000..8e8c921 --- /dev/null +++ b/filters/sparkle.js @@ -0,0 +1,72 @@ +/** + * Generates a sparkle/sunburst effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function SparkleFilter(){ + this.name = "Sparkle"; + this.defaultValues = { + rays : 50, + radius : 25, + amount : 50, + randomness : 25, + centerX : 0.5, + centerY : 0.5, + }; + this.valueRanges = { + rays : {min:1, max:100}, + radius : {min:1, max:200}, + amount : {min:0, max:100}, + randomness : {min:0, max:50}, + centerX : {min:0, max:1.0}, + centerY : {min:0, max:1.0}, + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var rays = (values.rays === undefined) ? this.defaultValues.rays : values.rays; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var randomness = (values.randomness === undefined) ? this.defaultValues.randomness : values.randomness; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var iCenterX = centerX * width; + var iCenterY = centerY * height; + var rayLengths = []; + for(var i = 0; i < rays; i++){ + rayLengths[i]= radius + randomness / 100 * radius * filterUtils.gaussianRandom(); + } + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + var angle = Math.atan2(dy,dx); + var d = (angle+Math.PI) / (Math.PI*2) * rays; + var i = parseInt(d); + var f = d - i; + if(radius != 0){ + var length = filterUtils.linearInterpolate(f, rayLengths[i % rays], rayLengths[(i+1) % rays]); + var g = length*length / (distance+0.0001); + g = Math.pow(g, (100-amount) / 50); + f -= 0.5; + f = 1 - f*f; + f *= g; + } + f = filterUtils.clampPixel(f,0,1); + var mixedRGB = filterUtils.mixColors(f,[inputData[pixel],inputData[pixel+1],inputData[pixel+2],inputData[pixel+3]],[255,255,255,255]); + for(var k = 0; k < 3; k++){ + inputData[pixel+k] = mixedRGB[k]; + } + } + } + } +} diff --git a/filters/squaresmear.js b/filters/squaresmear.js new file mode 100644 index 0000000..f3e299c --- /dev/null +++ b/filters/squaresmear.js @@ -0,0 +1,62 @@ +/** + * Smears out the image with square shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. + */ +function SquareSmearFilter(){ + this.name = "Square Smear"; + this.defaultValues = { + size : 4, + density : 0.5, + mix : 0.5, + }; + this.valueRanges = { + size : {min:1, max:10}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0}, + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + if(size < 1){ size = 1;} + size = parseInt(size); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var radius = size+1; + var radius2 = radius*radius; + var numShapes = parseInt(2*density/30*width*height / 2); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + for(var x = sx - radius; x < sx + radius + 1; x++){ + + for(var y = sy - radius; y < sy + radius + 1; y++){ + if (x >= 0 && x < width && y >= 0 && y < height) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/threshold.js b/filters/threshold.js new file mode 100644 index 0000000..7572077 --- /dev/null +++ b/filters/threshold.js @@ -0,0 +1,30 @@ +/** + * Divides the colors into black and white following the treshold value. Brightnesses above the threshold + * sets the color to white while values below the threshold sets the color to black. + */ +function ThresholdFilter(){ + this.name = "Black & White"; + this.defaultValues = { + threshold : 127 + }; + this.valueRanges = { + threshold : {min:0, max:255} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var threshold = (values.threshold === undefined) ? this.defaultValues.threshold : values.threshold; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var brightness = (inputData[pixel] + inputData[pixel+1] + inputData[pixel+2])/3 + var colorVal = 0; + if(brightness > threshold){ + colorVal = 255; + } + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = colorVal; + } + } + } +} diff --git a/filters/triangleripple.js b/filters/triangleripple.js new file mode 100644 index 0000000..c075dae --- /dev/null +++ b/filters/triangleripple.js @@ -0,0 +1,44 @@ +/** + * Creates ripples on the image horizontally/vertically in a sine pattern. + */ +function TriangleRippleFilter(){ + this.name = "Triangle Ripples"; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = filterUtils.triangle(nx,1) + var fy = filterUtils.triangle(ny,1) + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/twirl.js b/filters/twirl.js new file mode 100644 index 0000000..836a0d0 --- /dev/null +++ b/filters/twirl.js @@ -0,0 +1,54 @@ +/** + * Twists the image around a given center point. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function TwirlFilter(){ + this.name = "Twirl"; + this.defaultValues = { + radius : 100, + angle : 180, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + radius : {min: 1, max: 200}, + angle : {min: 0, max: 360}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + angle = angle/180 * Math.PI; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + if(distance > radius2){ + out[0] = x; + out[1] = y; + } else { + distance = Math.sqrt(distance); + var a = Math.atan2(dy, dx) + angle * (radius-distance) / radius; + out[0] = iCenterX + distance*Math.cos(a); + out[1] = iCenterY + distance*Math.sin(a); + } + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/filters/vignette.js b/filters/vignette.js new file mode 100644 index 0000000..7ae5020 --- /dev/null +++ b/filters/vignette.js @@ -0,0 +1,38 @@ +/** + * Creates a classical vignette effect on the image i.e. darkens the corners. + */ +function VignetteFilter(){ + this.name = "Vignette"; + this.defaultValues = { + amount : 0.3, + }; + this.valueRanges = { + amount : {min:0.0, max:1.0}, + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var context = canvas.getContext("2d"); + var gradient; + var radius = Math.sqrt( Math.pow(width/2, 2) + Math.pow(height/2, 2) ); + context.putImageData(input,0,0); + context.globalCompositeOperation = 'source-over'; + + gradient = context.createRadialGradient(width/2, height/2, 0, width/2, height/2, radius); + gradient.addColorStop(0, 'rgba(0,0,0,0)'); + gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); + gradient.addColorStop(1, 'rgba(0,0,0,' + amount + ')'); + context.fillStyle = gradient; + context.fillRect(0, 0, width, height); + outputData = context.getImageData(0,0,width,height).data; + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + } +} diff --git a/filters/waterripple.js b/filters/waterripple.js new file mode 100644 index 0000000..3717986 --- /dev/null +++ b/filters/waterripple.js @@ -0,0 +1,63 @@ +/** + * Produces a water ripple/waves on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function WaterRippleFilter(){ + this.name = "Water Ripples"; + this.defaultValues = { + phase : 0, + radius : 50, + wavelength : 16, + amplitude : 10, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + phase : {min: 0, max: 100}, + radius : {min: 1, max: 200}, + wavelength : {min: 1, max: 100}, + amplitude : {min: 1, max: 100}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var wavelength = (values.wavelength === undefined) ? this.defaultValues.wavelength : values.wavelength; + var amplitude = (values.amplitude === undefined) ? this.defaultValues.amplitude : values.amplitude; + var phase = (values.phase === undefined) ? this.defaultValues.phase : values.phase; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance2 = dx*dx + dy*dy; + if(distance2 > radius2){ + out[0] = x; + out[1] = y; + } else { + var distance = Math.sqrt(distance2); + var amount = amplitude * Math.sin(distance/wavelength * Math.PI * 2 - phase); + amount *= (radius-distance)/radius; + if(distance != 0){ + amount *= wavelength/distance; + } + out[0] = x + dx*amount; + out[1] = y + dy*amount; + } + } + filterUtils.transformFilter(inputData,transInverse,width,height); + } +} diff --git a/jsmanipulate.js b/jsmanipulate.js new file mode 100644 index 0000000..5fd1e47 --- /dev/null +++ b/jsmanipulate.js @@ -0,0 +1,2316 @@ +/* +========================================================================= + JSManipulate v1.0 (2011-08-01) + +Javascript image filter & effect library + +Developed by Joel Besada (http://www.joelb.me) +Demo page: http://www.joelb.me/jsmanipulate + +MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2011, Joel Besada +========================================================================= +*/ + + +/** + * Contains common filter functions. + */ +function FilterUtils(){ + this.HSVtoRGB = function (h, s, v){ + var r, g, b; + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + switch(i % 6){ + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + default: break; + } + return [r * 255, g * 255, b * 255]; + }; + this.RGBtoHSV = function (r, g, b){ + r = r/255; g = g/255; b = b/255; + var max = Math.max(r, g, b); + var min = Math.min(r, g, b); + var h, s, v = max; + var d = max - min; + s = max === 0 ? 0 : d / max; + if(max === min){ + h = 0; + }else{ + switch(max){ + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + default: break; + } + h /= 6; + } + return [h, s, v]; + }; + this.getPixel = function (pixels,x,y,width,height){ + var pix = (y*width + x)*4; + if (x < 0 || x >= width || y < 0 || y >= height) { + return [pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 1], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 2], + pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 3]]; + } + return [pixels[pix],pixels[pix+1],pixels[pix+2],pixels[pix+3]]; + }; + var haveNextGaussian = false; + var nextGaussian; + this.gaussianRandom = function(){ + if(haveNextGaussian){ + haveNextGaussian = false; + return nextGaussian; + } else { + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s === 0); + var mult = Math.sqrt(-2 * Math.log(s)/s); + nextGaussian = v2 * mult; + haveNextGaussian = true; + return v1 * mult; + } + }; + this.clampPixel = function (x,a,b){ + return (x < a) ? a : (x > b) ? b : x; + }; + this.triangle = function(x){ + var r = this.mod(x, 1); + return 2*(r < 0.5 ? r : 1-r); + }; + this.mod = function(a,b){ + var n = parseInt(a/b,10); + a -= n*b; + if(a < 0){ + return a + b; + } + return a; + }; + this.mixColors = function(t, rgb1, rgb2){ + var r = this.linearInterpolate(t,rgb1[0],rgb2[0]); + var g = this.linearInterpolate(t,rgb1[1],rgb2[1]); + var b = this.linearInterpolate(t,rgb1[2],rgb2[2]); + var a = this.linearInterpolate(t,rgb1[3],rgb2[3]); + return [r,g,b,a]; + }; + + this.linearInterpolate = function(t,a,b){ + return a + t * (b-a); + }; + this.bilinearInterpolate = function (x,y,nw,ne,sw,se){ + var m0, m1; + var r0 = nw[0]; var g0 = nw[1]; var b0 = nw[2]; var a0 = nw[3]; + var r1 = ne[0]; var g1 = ne[1]; var b1 = ne[2]; var a1 = ne[3]; + var r2 = sw[0]; var g2 = sw[1]; var b2 = sw[2]; var a2 = sw[3]; + var r3 = se[0]; var g3 = se[1]; var b3 = se[2]; var a3 = se[3]; + var cx = 1.0 - x; var cy = 1.0 - y; + + m0 = cx * a0 + x * a1; + m1 = cx * a2 + x * a3; + var a = cy * m0 + y * m1; + + m0 = cx * r0 + x * r1; + m1 = cx * r2 + x * r3; + var r = cy * m0 + y * m1; + + m0 = cx * g0 + x * g1; + m1 = cx * g2 + x * g3; + var g = cy * m0 + y * m1; + + m0 = cx * b0 + x * b1; + m1 = cx * b2 + x * b3; + var b =cy * m0 + y * m1; + return [r,g,b,a]; + }; + this.tableFilter = function (inputData, table, width, height){ + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = table[inputData[pixel+i]]; + } + } + } + }; + this.convolveFilter = function(inputData, matrix, width, height){ + var outputData = []; + var rows, cols; + rows = cols = Math.sqrt(matrix.length); + var rows2 = parseInt(rows/2,10); + var cols2 = parseInt(cols/2,10); + var trace = true; + for(var y = 0; y < height; y++){ + for (var x = 0; x < width; x++){ + var pixel = (y*width + x)*4; + var r = 0, g = 0, b = 0; + for(var row = -rows2; row <= rows2; row++){ + var iy = y+row; + var ioffset; + if (0 <= iy && iy < height) { + ioffset = iy*width; + } else { + ioffset = y*width; + } + var moffset = cols*(row+rows2)+cols2; + for (var col = -cols2; col <= cols2; col++) { + var f = matrix[moffset+col]; + if (f !== 0) { + var ix = x+col; + if (!(0 <= ix && ix < width)) { + ix = x; + } + var iPixel = (ioffset+ix)*4; + r += f * inputData[iPixel]; + g += f * inputData[iPixel+1]; + b += f * inputData[iPixel+2]; + } + } + } + outputData[pixel] = parseInt(r+0.5,10); + outputData[pixel+1] = parseInt(g+0.5,10); + outputData[pixel+2] = parseInt(b+0.5,10); + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; + this.transformFilter = function(inputData, transformInverse, width, height){ + var out = []; + var outputData = []; + for(var j = 0; j < inputData.length; j++){ + outputData[j] = inputData[j]; + } + for(var y = 0; y < height; y++){ + for (var x = 0; x < width; x++){ + var pixel = (y*width + x)*4; + transformInverse.apply(this,[x,y,out]); + var srcX = Math.floor(out[0]); + var srcY = Math.floor(out[1]); + var xWeight = out[0]-srcX; + var yWeight = out[1]-srcY; + var nw,ne,sw,se; + if(srcX >= 0 && srcX < width-1 && srcY >= 0 && srcY < height-1){ + var i = (width*srcY + srcX)*4; + nw = [inputData[i],inputData[i+1],inputData[i+2],inputData[i+3]]; + ne = [inputData[i+4],inputData[i+5],inputData[i+6],inputData[i+7]]; + sw = [inputData[i+width*4],inputData[i+width*4+1],inputData[i+width*4+2],inputData[i+width*4+3]]; + se = [inputData[i+(width + 1)*4],inputData[i+(width + 1)*4+1],inputData[i+(width + 1)*4+2],inputData[i+(width + 1)*4+3]]; + } else { + nw = this.getPixel( inputData, srcX, srcY, width, height ); + ne = this.getPixel( inputData, srcX+1, srcY, width, height ); + sw = this.getPixel( inputData, srcX, srcY+1, width, height ); + se = this.getPixel( inputData, srcX+1, srcY+1, width, height ); + } + var rgba = this.bilinearInterpolate(xWeight,yWeight,nw,ne,sw,se); + outputData[pixel] = rgba[0]; + outputData[pixel + 1] = rgba[1]; + outputData[pixel + 2] = rgba[2]; + outputData[pixel + 3] = rgba[3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Blurs the image with Gaussian blur. + */ +function BlurFilter(){ + this.name = "Blur"; + this.isDirAnimatable = false; + this.defaultValues = { + amount : 3 + }; + this.valueRanges = { + amount : {min:0, max:10} + }; + this.filter = function(input,values){ + var width = input.width; + var width4 = width << 2; + var height = input.height; + var inputData = input.data; + var q; + var amount = values.amount; + if (amount < 0.0) { + amount = 0.0; + } + if (amount >= 2.5) { + q = 0.98711 * amount - 0.96330; + } else if (amount >= 0.5) { + q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * amount); + } else { + q = 2 * amount * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5)); + } + var qq = q * q; + var qqq = qq * q; + var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq); + var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0; + var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0; + var b3 = (0.422205 * qqq) / b0; + var bigB = 1.0 - (b1 + b2 + b3); + var c = 0; + var index; + var indexLast; + var pixel; + var ppixel; + var pppixel; + var ppppixel; + for (c = 0; c < 3; c++) { + for (var y = 0; y < height; y++) { + index = y * width4 + c; + indexLast = y * width4 + ((width - 1) << 2) + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index <= indexLast; index += 4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + index = y * width4 + ((width - 1) << 2) + c; + indexLast = y * width4 + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index >= indexLast; index -= 4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + } + } + for (c = 0; c < 3; c++) { + for (var x = 0; x < width; x++) { + index = (x << 2) + c; + indexLast = (height - 1) * width4 + (x << 2) + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index <= indexLast; index += width4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + index = (height - 1) * width4 + (x << 2) + c; + indexLast = (x << 2) + c; + pixel = inputData[index]; + ppixel = pixel; + pppixel = ppixel; + ppppixel = pppixel; + for (; index >= indexLast; index -= width4) { + pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; + inputData[index] = pixel; + ppppixel = pppixel; + pppixel = ppixel; + ppixel = pixel; + } + } + } + }; +} +/** + * Adjusts the brightness of the image by going over to HSV values. + * Negative values decrease brightness while positive values increase brightness. + */ +function BrightnessFilter(){ + this.name = "Brightness"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 0.0 + }; + this.valueRanges = { + amount : {min:-1.0, max:1.0} + }; + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); + hsv[2] += amount; + if(hsv[2] < 0){ + hsv[2] = 0; + } else if (hsv[2] > 1){ + hsv[2] = 1; + } + var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = rgb[i]; + } + } + } + }; +} +/** + * Embosses the edges of the image. + * This filter takes no parameters but can be applied several times for + * further effect. + */ +function BumpFilter(){ + this.name = "Bump"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var matrix = [-1,-1, 0, + -1, 1, 1, + 0, 1, 1]; + filterUtils.convolveFilter(inputData,matrix,width,height); + }; +} +/** + * Smears out the image with circular shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. + */ +function CircleSmearFilter(){ + this.name = "Circle Smear"; + this.isDirAnimatable = false; + this.defaultValues = { + size : 4, + density : 0.5, + mix : 0.5 + }; + this.valueRanges = { + size : {min:1, max:10}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var j = 0; j < inputData.length; j++){ + outputData[j] = inputData[j]; + } + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + if(size < 1){ size = 1;} + size = parseInt(size,10); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var radius = size+1; + var radius2 = radius*radius; + var numShapes = parseInt(2*density/30*width*height / 2,10); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + for(var x = sx - radius; x < sx + radius + 1; x++){ + for(var y = sy - radius; y < sy + radius + 1; y++){ + var f = (x - sx) * (x - sx) + (y - sy) * (y - sy); + if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(var k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(var l = 0; l < outputData.length; l++){ + inputData[l] = outputData[l]; + } + }; +} +/** + * Adjusts the contrast of the image. + */ +function ContrastFilter(){ + this.name = "Contrast"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + if(amount < 0){ + amount = 0.0; + } + var table = []; + + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 * (((i/255)-0.5)*amount+0.5),10); + } + filterUtils.tableFilter(inputData,table,width,height); + }; +} +/** + * Smears out the image with cross shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + */ +function CrossSmearFilter(){ + this.name = "Cross Smear"; + this.isDirAnimatable = false; + this.defaultValues = { + distance : 8, + density : 0.5, + mix : 0.5 + }; + this.valueRanges = { + distance : {min:0, max:30}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for(var j = 0; j < inputData.length; j++){ + outputData[j] = inputData[j]; + } + if(values === undefined){ values = this.defaultValues; } + var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; + if(distance < 0){ distance = 0;} + distance = parseInt(distance,10); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var numShapes = parseInt(2*density*width * height / (distance + 1),10); + for(var i = 0; i < numShapes; i++){ + var x = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var y = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var length = (Math.random()*Math.pow(2,32)) % distance + 1; + var rgb2 = [inputData[(y*width+x)*4],inputData[(y*width+x)*4+1],inputData[(y*width+x)*4+2],inputData[(y*width+x)*4+3]]; + var rgb1; + var mixedRGB; + var k; + for (var x1 = x-length; x1 < x+length+1; x1++) { + if(x1 >= 0 && x1 < width){ + rgb1 = [outputData[(y*width+x1)*4],outputData[(y*width+x1)*4+1],outputData[(y*width+x1)*4+2],outputData[(y*width+x1)*4+3]]; + mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y*width+x1)*4+k] = mixedRGB[k]; + } + } + + } + for (var y1 = y-length; y1 < y+length+1; y1++) { + if(y1 >= 0 && y1 < height){ + rgb1 = [outputData[(y1*width+x)*4],outputData[(y1*width+x)*4+1],outputData[(y1*width+x)*4+2],outputData[(y1*width+x)*4+3]]; + mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y1*width+x)*4+k] = mixedRGB[k]; + } + } + + } + } + for(var l = 0; l < outputData.length; l++){ + inputData[l] = outputData[l]; + } + }; +} +/** + * Diffuses the image creating a frosted glass effect. + */ +function DiffusionFilter(){ + this.name = "Diffusion"; + this.isDirAnimatable = false; + this.defaultValues = { + scale: 4 + }; + this.valueRanges = { + scale: {min: 1, max: 100} + }; + + var filterUtils = new FilterUtils(); + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var scale = (values.scale === undefined) ? this.defaultValues.scale : values.scale; + var out = []; + var outputData = []; + var sinTable = []; + var cosTable = []; + for(var i = 0; i < 256; i++){ + var angle = Math.PI*2*i/256; + sinTable[i] = scale*Math.sin(angle); + cosTable[i] = scale*Math.cos(angle); + } + transInverse = function (x,y,out){ + var angle = parseInt(Math.random() * 255,10); + var distance = Math.random(); + out[0] = x + distance * sinTable[angle]; + out[1] = y + distance * cosTable[angle]; + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Dithers the image to the specified number of colors. Setting color to false + * grayscales the image. + */ +function DitherFilter(){ + this.name = "Dither"; + this.isDirAnimatable = false; + this.defaultValues = { + levels : 3, + color : true + }; + this.valueRanges = { + levels : {min:2, max:30}, + color : {min:false, max:true} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + var i, j; + for (j=0; j < inputData.length; j++) { + outputData[j] = 0; + } + if(values === undefined){ values = this.defaultValues; } + var levels = (values.levels === undefined) ? this.defaultValues.levels : values.levels; + var color = (values.color === undefined) ? this.defaultValues.color : values.color; + if(levels <= 1){ + levels = 1; + } + var matrix = [0,0,0, + 0,0,7, + 3,5,1]; + var sum = 7+3+5+1; + var index = 0; + var map = []; + + for (i=0; i < levels; i++) { + map[i] = parseInt(255* i / (levels-1),10); + } + var div = []; + for (i=0; i < 256; i++) { + div[i] = parseInt(levels*i / 256,10); + } + for (var y = 0; y < height; y++) { + var reverse = ((y & 1) == 1); + var direction; + if(reverse){ + index = (y*width+width-1)*4; + direction = -1; + } else { + index = y*width*4; + direction = 1; + } + for (var x = 0; x < width; x++) { + var r1 = inputData[index]; var g1 = inputData[index+1]; var b1 = inputData[index+2]; + if(!color){ + r1 = g1 = b1 = parseInt((r1+g1+b1) / 3,10); + } + var r2 = map[div[r1]];var g2 = map[div[g1]];var b2 = map[div[b1]]; + + outputData[index] = r2; outputData[index + 1] = g2; outputData[index+2] = b2; outputData[index+3] = inputData[index+3]; + + var er = r1-r2; var eg = g1-g2; var eb = b1-b2; + + for (i = -1; i <= 1; i++) { + var iy = i+y; + if (0 <= iy && iy < height) { + for (j = -1; j <= 1; j++) { + var jx = j+x; + if (0 <= jx && jx < width) { + var w; + if (reverse){ + w = matrix[(i+1)*3-j+1]; + } else{ + w = matrix[(i+1)*3+j+1]; + } + if (w !== 0) { + var k = (reverse) ? index - j*4 : index + j*4; + r1 = inputData[k]; g1 = inputData[k+1]; b1 = inputData[k+2]; + var factor = w/sum; + r1 += er * factor; g1 += eg * factor; b1 += eb * factor; + inputData[k] = r1; inputData[k+1] = g1 ;inputData[k+2] = b1; + } + } + } + } + } + index += direction*4; + } + } + for(j = 0; j < outputData.length; j++){ + inputData[j] = outputData[j]; + } + }; +} +/** + * Highlights the edges of the image. + */ +function EdgeFilter(){ + this.name = "Edge Detection"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + var matrixH = [-1,-2,-1, + 0, 0, 0, + 1, 2, 1]; + var matrixV = [-1, 0, 1, + -2, 0, 2, + -1, 0, 1]; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var rh = 0; gh = 0; bh = 0; + var rv = 0; gv = 0; bv = 0; + for(var row = -1; row <= 1; row++){ + var iy = y+row; + var ioffset; + if(iy >= 0 && iy < height){ + ioffset = iy*width*4; + } else { + ioffset = y*width*4; + } + var moffset = 3*(row+1)+1; + for(var col = -1; col <= 1; col++){ + var ix = x+col; + if(!(ix >= 0 && ix < width)){ + ix = x; + } + ix *= 4; + var r = inputData[ioffset+ix]; + var g = inputData[ioffset+ix+1]; + var b = inputData[ioffset+ix+2]; + var h = matrixH[moffset+col]; + var v = matrixV[moffset+col]; + rh += parseInt(h*r,10); + bh += parseInt(h*g,10); + gh += parseInt(h*b,10); + rv += parseInt(v*r,10); + gv += parseInt(v*g,10); + bv += parseInt(v*b,10); + } + } + r = parseInt(Math.sqrt(rh*rh + rv*rv) / 1.8,10); + g = parseInt(Math.sqrt(gh*gh + gv*gv) / 1.8,10); + b = parseInt(Math.sqrt(bh*bh + bv*bv) / 1.8,10); + + outputData[pixel] = r; + outputData[pixel+1] = g; + outputData[pixel+2] = b; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Embosses the image with a simulated light source. + * Angle and elevation sets the position of the light. + */ +function EmbossFilter(){ + this.name = "Emboss"; + this.isDirAnimatable = false; + this.defaultValues = { + height : 1, + angle : 135, + elevation : 30 + }; + this.valueRanges = { + height : {min:1, max:10}, + angle : {min:0, max:360}, + elevation : {min:0, max:180} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var bumpHeight = (values.height === undefined) ? this.defaultValues.height : values.height; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var elevation = (values.elevation === undefined) ? this.defaultValues.elevation : values.elevation; + angle = angle / 180 * Math.PI; + elevation = elevation / 180 * Math.PI; + var width45 = 3 * bumpHeight; + var pixelScale = 255.9; + + var bumpPixels = []; + var bumpMapWidth = width; + var bumpMapHeight = height; + for(var i = 0; i < inputData.length; i+=4){ + bumpPixels[i/4] = (inputData[i] + inputData[i+1] + inputData[i+2])/3; + } + var Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL; + var shade, background; + + Lx = parseInt(Math.cos(angle) * Math.cos(elevation) * pixelScale,10); + Ly = parseInt(Math.sin(angle) * Math.cos(elevation) * pixelScale,10); + Lz = parseInt(Math.sin(elevation) * pixelScale,10); + + Nz = parseInt(6 * 255 / width45,10); + Nz2 = Nz * Nz; + NzLz = Nz * Lz; + background = Lz; + + var bumpIndex = 0; + + for (var y = 0; y < height; y++, bumpIndex += bumpMapWidth) { + var s1 = bumpIndex; + var s2 = s1 + bumpMapWidth; + var s3 = s2 + bumpMapWidth; + for (var x = 0; x < width; x++, s1++, s2++, s3++) { + var pixel = (y*width + x)*4; + if (y !== 0 && y < height-2 && x !== 0 && x < width-2) { + Nx = bumpPixels[s1-1] + bumpPixels[s2-1] + bumpPixels[s3-1] - bumpPixels[s1+1] - bumpPixels[s2+1] - bumpPixels[s3+1]; + Ny = bumpPixels[s3-1] + bumpPixels[s3] + bumpPixels[s3+1] - bumpPixels[s1-1] - bumpPixels[s1] - bumpPixels[s1+1]; + if (Nx === 0 && Ny === 0){ + shade = background; + } else if ((NdotL = Nx*Lx + Ny*Ly + NzLz) < 0){ + shade = 0; + } else { + shade = parseInt(NdotL / Math.sqrt(Nx*Nx + Ny*Ny + Nz2),10); + } + } else { + shade = background; + } + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = shade; + } + } + }; +} +/** + * Adjust simulated exposure values on the image. + */ +function ExposureFilter(){ + this.name = "Exposure"; + this.isDirAnimatable = true; + this.defaultValues = { + exposure : 1.0 + }; + this.valueRanges = { + exposure : {min:0, max:5} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var exposure = (values.exposure === undefined) ? this.defaultValues.exposure : values.exposure; + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 *(1-Math.exp(-(i/255) * exposure)),10); + } + filterUtils.tableFilter(inputData, table, width, height); + }; +} +/** + * Adjusts the gain and bias of the image. Gain alters the contrast while bias biases + * colors towards lighter or darker. + */ +function GainFilter(){ + this.name = "Gain/Bias"; + this.isDirAnimatable = true; + this.defaultValues = { + gain: 0.5, + bias: 0.5 + }; + this.valueRanges = { + gain: {min:0.0, max:1.0}, + bias: {min:0.0, max:1.0} + }; + var table = []; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var gain = (values.gain === undefined) ? this.defaultValues.gain : values.gain; + var bias = (values.bias === undefined) ? this.defaultValues.bias : values.bias; + + var table = []; + + for(var i = 0; i < 256; i++){ + var val = i/255; + var k = (1/gain-2) * (1-2*val); + val = (val < 0.5) ? val/(k+1) : (k-val)/(k-1); + val /= (1/bias-2)*(1-val)+1; + table[i] = parseInt(255 * val,10); + } + filterUtils.tableFilter(inputData,table,width,height); + }; +} +/** + * Adjusts the gamma values of the image. Values over 1 increase the gamma while values over 0 decrease gamma. + */ +function GammaFilter(){ + this.name = "Gamma"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + if(amount < 0){ + amount = 0.0; + } + if(!FilterUtils){ + if(console){ + console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); + } + return; + } + var filterUtils = new FilterUtils(); + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = 255 * Math.pow(i/255, 1/amount) + 0.5; + } + filterUtils.tableFilter(inputData,table,width,height); + }; +} +/** + * Sets the image to grayscale. + */ +function GrayscaleFilter(){ + this.name = "Grayscale"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = luma; + } + } + }; +} +/** + * Adjusts the hue of the image by going over to HSV values. + */ +function HueFilter(){ + this.name = "Hue"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 0.0 + }; + this.valueRanges = { + amount : {min:-1.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); + hsv[0] += amount; + while(hsv[0] < 0){ + hsv[0] += 360; + } + var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = rgb[i]; + } + } + } + }; +} +/** + * Inverts the colors of the image. + */ +function InvertFilter(){ + this.name = "Invert"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for(var i = 0; i < 3; i++){ + inputData[pixel+i] = 255 - inputData[pixel+i]; + } + } + } + }; +} +/** + * Creates a kaleidoscope effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function KaleidoscopeFilter(){ + this.name = "Kaleidoscope"; + this.isDirAnimatable = false; + this.defaultValues = { + angle : 0, + rotation : 0, + sides : 3, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + angle : {min: 0, max: 360}, + rotation : {min: 0, max: 360}, + sides : {min: 1, max: 30}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var rotation = (values.rotation === undefined) ? this.defaultValues.rotation : values.rotation; + var sides = (values.sides === undefined) ? this.defaultValues.sides : values.sides; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var iCenterX = width * centerX; var iCenterY = height * centerY; + angle = angle/180 * Math.PI; + rotation = rotation/180 * Math.PI; + var transInverse = function(x,y,out){ + var dx = x - iCenterX; + var dy = y - iCenterY; + var r = Math.sqrt(dx*dx + dy*dy); + var theta = Math.atan2(dy,dx) - angle - rotation; + theta = filterUtils.triangle(theta/Math.PI*sides*0.5); + theta += angle; + out[0] = iCenterX + r*Math.cos(theta); + out[1] = iCenterY + r*Math.sin(theta); + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Applies a fisheye lens distortion effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function LensDistortionFilter(){ + this.name = "Lens Distortion"; + this.isDirAnimatable = false; + this.defaultValues = { + refraction : 1.5, + radius : 50, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + refraction : {min: 1, max: 10}, + radius : {min: 1, max: 200}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var refraction = (values.refraction === undefined) ? this.defaultValues.refraction : values.refraction; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var x2 = dx*dx; + var y2 = dy*dy; + if (y2 >= (radius2 - (radius2*x2)/radius2)) { + out[0] = x; + out[1] = y; + } else { + var rRefraction = 1.0 / refraction; + + var z = Math.sqrt((1.0 - x2/radius2 - y2/radius2) * radius2); + var z2 = z*z; + + var xAngle = Math.acos(dx / Math.sqrt(x2+z2)); + var angle1 = Math.PI/2 - xAngle; + var angle2 = Math.asin(Math.sin(angle1)*rRefraction); + angle2 = Math.PI/2 - xAngle - angle2; + out[0] = x - Math.tan(angle2)*z; + + var yAngle = Math.acos(dy / Math.sqrt(y2+z2)); + angle1 = Math.PI/2 - yAngle; + angle2 = Math.asin(Math.sin(angle1)*rRefraction); + angle2 = Math.PI/2 - yAngle - angle2; + out[1] = y - Math.tan(angle2)*z; + } + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Smears out the image with line shapes to create a painting style effect. Mix specifies + * the intensity of the effect. + */ +function LineSmearFilter(){ + this.name = "Line Smear"; + this.isDirAnimatable = false; + this.defaultValues = { + distance : 8, + density : 0.5, + angle : 0, + mix : 0.5 + }; + this.valueRanges = { + distance : {min:1, max:30}, + density : {min:0.0, max:1.0}, + angle : {min:0, max:360}, + mix : {min:0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + var k; + for(k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; + if(distance < 1){ distance = 1;} + distance = parseInt(distance,10); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + angle = angle/180*Math.PI; + var sinAngle = Math.sin(angle); + var cosAngle = Math.cos(angle); + var numShapes = parseInt(2*density*width*height / 2,10); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var length = (Math.random()*Math.pow(2,32) & 0x7fffffff) % distance + 1; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + var dx = parseInt(length*cosAngle,10); + var dy = parseInt(length*sinAngle,10); + + var x0 = sx-dx; + var y0 = sy-dy; + var x1 = sx+dx; + var y1 = sy+dy; + var x, y, d, incrE, incrNE, ddx, ddy; + + if (x1 < x0){ + ddx = -1; + } else { + ddx = 1; + } + if (y1 < y0){ + ddy = -1; + } else { + ddy = 1; + } + dx = x1-x0; + dy = y1-y0; + dx = Math.abs(dx); + dy = Math.abs(dy); + x = x0; + y = y0; + var rgb1; + var mixedRGB; + if (x < width && x >= 0 && y < height && y >= 0) { + rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + if (Math.abs(dx) > Math.abs(dy)) { + d = 2*dy-dx; + incrE = 2*dy; + incrNE = 2*(dy-dx); + + while (x != x1) { + if (d <= 0){ + d += incrE; + } else { + d += incrNE; + y += ddy; + } + x += ddx; + if (x < width && x >= 0 && y < height && y >= 0) { + rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } else { + d = 2*dx-dy; + incrE = 2*dx; + incrNE = 2*(dx-dy); + + while (y != y1) { + if (d <= 0) { + d += incrE; + }else { + d += incrNE; + x += ddx; + } + y += ddy; + if (x < width && x >= 0 && y < height && y >= 0) { + rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Replaces every pixel with the maximum RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MaximumFilter(){ + this.name = "Maximum"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var maxR = 0; + var maxG = 0; + var maxB = 0; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + maxR = Math.max(maxR,inputData[iPixel]); + maxG = Math.max(maxG,inputData[iPixel+1]); + maxB = Math.max(maxB,inputData[iPixel+2]); + } + } + } + } + outputData[pixel] = maxR; + outputData[pixel+1] = maxG; + outputData[pixel+2] = maxB; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Replaces every pixel with the median RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MedianFilter(){ + this.name = "Median"; + this.isDirAnimatable = false; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var rList = []; + var gList = []; + var bList = []; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + rList.push(inputData[iPixel]); + gList.push(inputData[iPixel+1]); + bList.push(inputData[iPixel+2]); + + } + } + } + } + var sortFunc = function(a,b){ + return a-b; + }; + rList.sort(sortFunc); + gList.sort(sortFunc); + bList.sort(sortFunc); + outputData[pixel] = rList[4]; + outputData[pixel+1] = gList[4]; + outputData[pixel+2] = bList[4]; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Replaces every pixel with the minimum RGB value of the neighboring pixels. Each color is + * considered separately. + */ +function MinimumFilter(){ + this.name = "Minimum"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var minR = 255; + var minG = 255; + var minB = 255; + for (var dy = -1; dy <= 1; dy++){ + var iy = y+dy; + if(iy >= 0 && iy < height){ + for (var dx = -1; dx <= 1; dx++){ + var ix = x+dx; + if(ix >= 0 && ix < width){ + var iPixel = (iy*width + ix)*4; + minR = Math.min(minR,inputData[iPixel]); + minG = Math.min(minG,inputData[iPixel+1]); + minB = Math.min(minB,inputData[iPixel+2]); + } + } + } + } + outputData[pixel] = minR; + outputData[pixel+1] = minG; + outputData[pixel+2] = minB; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Creates random noise on the image, with or without color. + */ +function NoiseFilter(){ + this.name = "Noise"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 25, + density : 1, + monochrome : true + }; + this.valueRanges = { + amount : {min:0, max:100}, + density : {min:0, max:1.0}, + monochrome : {min:false, max:true} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var monochrome = (values.monochrome === undefined) ? this.defaultValues.monochrome : values.monochrome; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + if(Math.random() <= density){ + var n; + if(monochrome){ + n = parseInt((2*Math.random()-1) * amount,10); + inputData[pixel] += n; + inputData[pixel+1] += n; + inputData[pixel+2] += n; + } else { + for(var i = 0; i < 3; i++){ + n = parseInt((2*Math.random()-1) * amount,10); + inputData[pixel+i] += n; + } + } + } + } + } + }; +} +/** + * Produces an oil painting effect on the image. + * NOTE: This filter can be very slow, especially at higher ranges. Use with caution. + */ +function OilFilter(){ + this.name = "Oil Painting"; + this.isDirAnimatable = false; + this.defaultValues = { + range : 3 + }; + this.valueRanges = { + range : {min:0, max:5} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + if(values === undefined){ values = this.defaultValues; } + var range = (values.range === undefined) ? this.defaultValues.range : values.range; + range = parseInt(range,10); + var index = 0; + var rHistogram = []; + var gHistogram = []; + var bHistogram = []; + var rTotal = []; + var gTotal = []; + var bTotal = []; + var levels = 256; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + for (var j = 0; j < levels; j++){ + rHistogram[j] = gHistogram[j] = bHistogram[j] = rTotal[j] = gTotal[j] = bTotal[j] = 0; + } + for (var row = -range; row <= range; row++) { + var iy = y+row; + var ioffset; + if (0 <= iy && iy < height) { + ioffset = iy*width; + for (var col = -range; col <= range; col++) { + var ix = x+col; + if (0 <= ix && ix < width) { + var ro = inputData[(ioffset+ix)*4]; + var go = inputData[(ioffset+ix)*4+1]; + var bo = inputData[(ioffset+ix)*4+2]; + var ri = ro*levels/256; + var gi = go*levels/256; + var bi = bo*levels/256; + rTotal[ri] += ro; + gTotal[gi] += go; + bTotal[bi] += bo; + rHistogram[ri]++; + gHistogram[gi]++; + bHistogram[bi]++; + } + } + } + } + var r = 0, g = 0, b = 0; + for (var i = 1; i < levels; i++) { + if (rHistogram[i] > rHistogram[r]){ + r = i; + } + if (gHistogram[i] > gHistogram[g]){ + g = i; + } + if (bHistogram[i] > bHistogram[b]){ + b = i; + } + } + r = rTotal[r] / rHistogram[r]; + g = gTotal[g] / gHistogram[g]; + b = bTotal[b] / bHistogram[b]; + outputData[pixel] = r; + outputData[pixel+1] = g; + outputData[pixel+2] = b; + outputData[pixel+3] = inputData[pixel+3]; + } + } + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Changes the opacity of the image. + */ +function OpacityFilter(){ + this.name = "Opacity"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:1.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + inputData[pixel+3] = 255*amount; + } + } + }; +} +/** + * Pinches and whirls the image toward the center point. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function PinchFilter(){ + this.name = "Pinch/Whirl"; + this.isDirAnimatable = false; + this.defaultValues = { + amount : 0.5, + radius : 100, + angle : 0, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + amount : {min: -1.0, max: 1.0}, + radius : {min: 1, max: 200}, + angle : {min: 0, max: 360}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + angle = angle/180 * Math.PI; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + if(distance > radius2 || distance === 0){ + out[0] = x; + out[1] = y; + } else { + var d = Math.sqrt( distance / radius2 ); + var t = Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); + dx *= t; + dy *= t; + var e = 1 - d; + var a = angle * e * e; + var s = Math.sin(a); + var c = Math.cos(a); + out[0] = iCenterX + c*dx - s*dy; + out[1] = iCenterY + s*dx + c*dy; + } + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Pixelates the image i.e. divides the image into blocks of color. + */ +function PixelationFilter(){ + this.name = "Pixelation"; + this.isDirAnimatable = false; + this.defaultValues = { + size : 5 + }; + this.valueRanges = { + size : {min:1, max:50} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + size = parseInt(size,10); + var pixels = []; + var by, bx, bPixel; + for (var y = 0; y < height; y+=size) { + for (var x = 0; x < width; x+=size) { + var pixel = (y*width + x)*4; + var w = Math.min(size, width-x); + var h = Math.min(size, height-y); + var t = w*h; + var r = 0, g = 0, b = 0; + for(by = y; by < y+h; by++){ + for(bx = x; bx < x+w; bx++){ + bPixel = (by*width + bx)*4; + r += inputData[bPixel]; + g += inputData[bPixel+1]; + b += inputData[bPixel+2]; + } + } + for(by = y; by < y+h; by++){ + for(bx = x; bx < x+w; bx++){ + bPixel = (by*width + bx)*4; + inputData[bPixel] = r/t; + inputData[bPixel+1] = g/t; + inputData[bPixel+2] = b/t; + } + } + } + } + }; +} +/** + * Posterizes the image, i.e. restricts the color values to a set amount of levels. + */ +function PosterizeFilter(){ + this.name = "Posterize"; + this.isDirAnimatable = false; + this.defaultValues = { + levels : 6 + }; + this.valueRanges = { + levels : {min:2, max:30 } + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var levels = (values.levels === undefined) ? this.defaultValues.levels : parseInt(values.levels,10); + if(levels <= 1){ + return; + } + var table = []; + for(var i = 0; i < 256; i++){ + table[i] = parseInt(255 * parseInt(i*levels/256,10) / (levels-1),10); + } + filterUtils.tableFilter(inputData,table,width,height); + }; +} +/** + * Adjust the factor of each RGB color value in the image. + */ +function RGBAdjustFilter(){ + this.name = "RGBAdjust"; + this.isDirAnimatable = true; + this.defaultValues = { + red: 1.0, + green: 1.0, + blue: 1.0 + }; + this.valueRanges = { + red: {min: 0.0, max: 2.0}, + green: {min: 0.0, max: 2.0}, + blue: {min: 0.0, max: 2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var red = (values.red === undefined) ? this.defaultValues.red : values.red; + var green = (values.green === undefined) ? this.defaultValues.green : values.green; + var blue = (values.blue === undefined) ? this.defaultValues.blue : values.blue; + if(red < 0){ red = 0; } + if(green < 0){ green = 0; } + if(blue < 0){ blue = 0; } + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + inputData[pixel] *= red; + inputData[pixel+1] *= green; + inputData[pixel+2] *= blue; + } + } + }; +} +/** + * Adjusts the saturation value of the image. Values over 1 increase saturation while values below decrease saturation. + * For a true grayscale effect, use the grayscale filter instead. + */ +function SaturationFilter(){ + this.name = "Saturation"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 1.0 + }; + this.valueRanges = { + amount : {min:0.0, max:2.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var RW = 0.3; + var RG = 0.59; + var RB = 0.11; + var a = (1 - amount) * RW + amount; + var b = (1 - amount) * RW; + var c = (1 - amount) * RW; + var d = (1 - amount) * RG; + var e = (1 - amount) * RG + amount; + var f = (1 - amount) * RG; + var g = (1 - amount) * RB; + var h = (1 - amount) * RB; + var i = (1 - amount) * RB + amount; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var pR = inputData[pixel]; + var pG = inputData[pixel+1]; + var pB = inputData[pixel+2]; + inputData[pixel] = a*pR + d*pG + g*pB; + inputData[pixel+1] = b*pR + e*pG + h*pB; + inputData[pixel+2] = c*pR + f*pG + i*pB; + } + } + }; +} +/** + * Creates ripples on the image horizontally/vertically in a sawtooth pattern. + */ +function SawtoothRippleFilter(){ + this.name = "Sawtooth Ripples"; + this.isDirAnimatable = false; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = filterUtils.mod(nx,1); + var fy = filterUtils.mod(ny,1); + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Creates a sepia effect on the image i.e. gives the image a yellow-brownish tone. + */ +function SepiaFilter(){ + this.name = "Sepia"; + this.isDirAnimatable = true; + this.defaultValues = { + amount : 10 + }; + this.valueRanges = { + amount : {min:0, max:30} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + amount *= 255/100; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; + var r,g,b; + r = g = b = luma; + r += 40; + g += 20; + b -= amount; + + inputData[pixel] = r; + inputData[pixel+1] = g; + inputData[pixel+2] = b; + } + } + }; +} +/** + * Sharpens the image slightly. For increased effect, apply the filter multiple times. + */ +function SharpenFilter(){ + this.name = "Sharpen"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var matrix = [ 0.0,-0.2, 0.0, + -0.2, 1.8,-0.2, + 0.0, -0.2, 0.0]; + filterUtils.convolveFilter(inputData,matrix,width,height); + }; +} +/** + * Creates ripples on the image horizontally/vertically in a sine pattern. + */ +function SineRippleFilter(){ + this.name = "Sine Ripples"; + this.isDirAnimatable = false; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = Math.sin(nx); + var fy = Math.sin(ny); + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Produces a solarization effect on the image. + */ +function SolarizeFilter(){ + this.name = "Solarize"; + this.isDirAnimatable = true; + this.defaultValues = { + }; + this.valueRanges = { + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var table = []; + for(var i = 0; i < 256; i++){ + var val = (i/255 > 0.5) ? 2*(i/255-0.5) : 2*(0.5-i/255); + table[i] = parseInt(255 * val,10); + } + filterUtils.tableFilter(inputData, table, width, height); + }; +} +/** + * Generates a sparkle/sunburst effect on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function SparkleFilter(){ + this.name = "Sparkle"; + this.isDirAnimatable = false; + this.defaultValues = { + rays : 50, + size : 25, + amount : 50, + randomness : 25, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + rays : {min:1, max:100}, + size : {min:1, max:200}, + amount : {min:0, max:100}, + randomness : {min:0, max:50}, + centerX : {min:0, max:1.0}, + centerY : {min:0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var rays = (values.rays === undefined) ? this.defaultValues.rays : values.rays; + rays = parseInt(rays, 10); + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var randomness = (values.randomness === undefined) ? this.defaultValues.randomness : values.randomness; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var iCenterX = centerX * width; + var iCenterY = centerY * height; + var rayLengths = []; + for(var j = 0; j < rays; j++){ + rayLengths[j]= size + randomness / 100 * size * filterUtils.gaussianRandom(); + } + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + var angle = Math.atan2(dy,dx); + var d = (angle+Math.PI) / (Math.PI*2) * rays; + var i = parseInt(d,10); + var f = d - i; + if(size !== 0){ + var length = filterUtils.linearInterpolate(f, rayLengths[i % rays], rayLengths[(i+1) % rays]); + var g = length*length / (distance+0.0001); + g = Math.pow(g, (100-amount) / 50); + f -= 0.5; + f = 1 - f*f; + f *= g; + } + f = filterUtils.clampPixel(f,0,1); + var mixedRGB = filterUtils.mixColors(f,[inputData[pixel],inputData[pixel+1],inputData[pixel+2],inputData[pixel+3]],[255,255,255,255]); + for(var k = 0; k < 3; k++){ + inputData[pixel+k] = mixedRGB[k]; + } + } + } + }; +} +/** + * Smears out the image with square shapes to create a painting style effect. + * The mix values sets the intensity of the effect. + * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. + */ +function SquareSmearFilter(){ + this.name = "Square Smear"; + this.isDirAnimatable = false; + this.defaultValues = { + size : 4, + density : 0.5, + mix : 0.5 + }; + this.valueRanges = { + size : {min:1, max:10}, + density : {min:0.0, max:1.0}, + mix : {min:0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + var k; + for(k = 0; k < inputData.length; k++){ + outputData[k] = inputData[k]; + } + if(values === undefined){ values = this.defaultValues; } + var size = (values.size === undefined) ? this.defaultValues.size : values.size; + if(size < 1){ size = 1;} + size = parseInt(size,10); + var density = (values.density === undefined) ? this.defaultValues.density : values.density; + var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; + var radius = size+1; + var radius2 = radius*radius; + var numShapes = parseInt(2*density/30*width*height / 2,10); + for(var i = 0; i < numShapes; i++){ + var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; + var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; + var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; + for(var x = sx - radius; x < sx + radius + 1; x++){ + + for(var y = sy - radius; y < sy + radius + 1; y++){ + if (x >= 0 && x < width && y >= 0 && y < height) { + var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; + var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); + for(k = 0; k < 3; k++){ + outputData[(y*width+x)*4+k] = mixedRGB[k]; + } + } + } + } + } + for(k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Divides the colors into black and white following the treshold value. Brightnesses above the threshold + * sets the color to white while values below the threshold sets the color to black. + */ +function ThresholdFilter(){ + this.name = "Black & White"; + this.isDirAnimatable = true; + this.defaultValues = { + threshold : 127 + }; + this.valueRanges = { + threshold : {min:0, max:255} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var threshold = (values.threshold === undefined) ? this.defaultValues.threshold : values.threshold; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var pixel = (y*width + x)*4; + var brightness = (inputData[pixel] + inputData[pixel+1] + inputData[pixel+2])/3; + var colorVal = 0; + if(brightness > threshold){ + colorVal = 255; + } + inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = colorVal; + } + } + }; +} +/** + * Creates ripples on the image horizontally/vertically in a sine pattern. + */ +function TriangleRippleFilter(){ + this.name = "Triangle Ripples"; + this.isDirAnimatable = false; + this.defaultValues = { + xAmplitude : 5, + yAmplitude : 5, + xWavelength : 16, + yWavelength : 16 + }; + this.valueRanges = { + xAmplitude : {min:0, max:30}, + yAmplitude : {min:0, max:30}, + xWavelength : {min:1, max:50}, + yWavelength : {min:1, max:50} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; + var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; + var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; + var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; + var transInverse = function(x,y,out){ + var nx = y/xWavelength; + var ny = x/yWavelength; + var fx = filterUtils.triangle(nx,1); + var fy = filterUtils.triangle(ny,1); + out[0] = x + xAmplitude * fx; + out[1] = y + yAmplitude * fy; + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Twists the image around a given center point. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function TwirlFilter(){ + this.name = "Twirl"; + this.isDirAnimatable = false; + this.defaultValues = { + radius : 100, + angle : 180, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + radius : {min: 1, max: 200}, + angle : {min: 0, max: 360}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + angle = angle/180 * Math.PI; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance = dx*dx + dy*dy; + if(distance > radius2){ + out[0] = x; + out[1] = y; + } else { + distance = Math.sqrt(distance); + var a = Math.atan2(dy, dx) + angle * (radius-distance) / radius; + out[0] = iCenterX + distance*Math.cos(a); + out[1] = iCenterY + distance*Math.sin(a); + } + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * Creates a classical vignette effect on the image i.e. darkens the corners. + */ +function VignetteFilter(){ + this.name = "Vignette"; + this.isDirAnimatable = false; + this.defaultValues = { + amount : 0.3 + }; + this.valueRanges = { + amount : {min:0.0, max:1.0} + }; + this.filter = function(input,values){ + var width = input.width, height = input.height; + var inputData = input.data; + var outputData = []; + if(values === undefined){ values = this.defaultValues; } + var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var context = canvas.getContext("2d"); + var gradient; + var radius = Math.sqrt( Math.pow(width/2, 2) + Math.pow(height/2, 2) ); + context.putImageData(input,0,0); + context.globalCompositeOperation = 'source-over'; + + gradient = context.createRadialGradient(width/2, height/2, 0, width/2, height/2, radius); + gradient.addColorStop(0, 'rgba(0,0,0,0)'); + gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); + gradient.addColorStop(1, 'rgba(0,0,0,' + amount + ')'); + context.fillStyle = gradient; + context.fillRect(0, 0, width, height); + outputData = context.getImageData(0,0,width,height).data; + for(var k = 0; k < outputData.length; k++){ + inputData[k] = outputData[k]; + } + }; +} +/** + * Produces a water ripple/waves on the image. CenterX and CenterY specify the + * position in terms of ratios of width and height. + */ +function WaterRippleFilter(){ + this.name = "Water Ripples"; + this.isDirAnimatable = false; + this.defaultValues = { + phase : 0, + radius : 50, + wavelength : 16, + amplitude : 10, + centerX : 0.5, + centerY : 0.5 + }; + this.valueRanges = { + phase : {min: 0, max: 100}, + radius : {min: 1, max: 200}, + wavelength : {min: 1, max: 100}, + amplitude : {min: 1, max: 100}, + centerX : {min: 0.0, max:1.0}, + centerY : {min: 0.0, max:1.0} + }; + var filterUtils = new FilterUtils(); + + this.filter = function (input, values){ + var width = input.width, height = input.height; + var inputData = input.data; + if(values === undefined){ values = this.defaultValues; } + var wavelength = (values.wavelength === undefined) ? this.defaultValues.wavelength : values.wavelength; + var amplitude = (values.amplitude === undefined) ? this.defaultValues.amplitude : values.amplitude; + var phase = (values.phase === undefined) ? this.defaultValues.phase : values.phase; + var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; + var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; + var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; + var radius2 = radius*radius; + var iCenterX = width * centerX; var iCenterY = height * centerY; + var transInverse = function(x,y,out){ + var dx = x-iCenterX; + var dy = y-iCenterY; + var distance2 = dx*dx + dy*dy; + if(distance2 > radius2){ + out[0] = x; + out[1] = y; + } else { + var distance = Math.sqrt(distance2); + var amount = amplitude * Math.sin(distance/wavelength * Math.PI * 2 - phase); + amount *= (radius-distance)/radius; + if(distance !== 0){ + amount *= wavelength/distance; + } + out[0] = x + dx*amount; + out[1] = y + dy*amount; + } + }; + filterUtils.transformFilter(inputData,transInverse,width,height); + }; +} +/** + * A collection of all the filters. + */ +var JSManipulate = { + blur : new BlurFilter(), + brightness : new BrightnessFilter(), + bump : new BumpFilter(), + circlesmear : new CircleSmearFilter(), + contrast : new ContrastFilter(), + crosssmear : new CrossSmearFilter(), + diffusion : new DiffusionFilter(), + dither : new DitherFilter(), + edge : new EdgeFilter(), + emboss : new EmbossFilter(), + exposure : new ExposureFilter(), + gain : new GainFilter(), + gamma : new GammaFilter(), + grayscale : new GrayscaleFilter(), + hue : new HueFilter(), + invert : new InvertFilter(), + kaleidoscope : new KaleidoscopeFilter(), + lensdistortion : new LensDistortionFilter(), + linesmear : new LineSmearFilter(), + maximum : new MaximumFilter(), + median : new MedianFilter(), + minimum : new MinimumFilter(), + noise : new NoiseFilter(), + oil : new OilFilter(), + opacity : new OpacityFilter(), + pinch : new PinchFilter(), + pixelate : new PixelationFilter(), + posterize : new PosterizeFilter(), + rgbadjust : new RGBAdjustFilter(), + saturation : new SaturationFilter(), + sawtoothripple : new SawtoothRippleFilter(), + sepia : new SepiaFilter(), + sharpen : new SharpenFilter(), + sineripple : new SineRippleFilter(), + solarize : new SolarizeFilter(), + sparkle : new SparkleFilter(), + squaresmear : new SquareSmearFilter(), + threshold : new ThresholdFilter(), + triangleripple : new TriangleRippleFilter(), + twirl : new TwirlFilter(), + vignette : new VignetteFilter(), + waterripple : new WaterRippleFilter() +}; + +// node.js +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){ + module.exports = JSManipulate; +} + diff --git a/minified/jsmanipulate.min.js b/minified/jsmanipulate.min.js new file mode 100644 index 0000000..b67424a --- /dev/null +++ b/minified/jsmanipulate.min.js @@ -0,0 +1,15 @@ +/* +========================================================================= + JSManipulate v1.0 (2011-08-01) + +Javascript image filter & effect library + +Developed by Joel Besada (http://www.joelb.me) +Demo page: http://www.joelb.me/jsmanipulate + +MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2011, Joel Besada +========================================================================= +*/ +function FilterUtils(){this.HSVtoRGB=function(t,a,e){var i,r,n,s=Math.floor(6*t),l=6*t-s,h=e*(1-a),o=e*(1-l*a),u=e*(1-(1-l)*a);switch(s%6){case 0:i=e,r=u,n=h;break;case 1:i=o,r=e,n=h;break;case 2:i=h,r=e,n=u;break;case 3:i=h,r=o,n=e;break;case 4:i=u,r=h,n=e;break;case 5:i=e,r=h,n=o}return[255*i,255*r,255*n]},this.RGBtoHSV=function(t,a,e){t/=255,a/=255,e/=255;var i,r,n=Math.max(t,a,e),s=Math.min(t,a,e),l=n,h=n-s;if(r=0===n?0:h/n,n===s)i=0;else{switch(n){case t:i=(a-e)/h+(e>a?6:0);break;case a:i=(e-t)/h+2;break;case e:i=(t-a)/h+4}i/=6}return[i,r,l]},this.getPixel=function(t,a,e,i,r){var n=4*(e*i+a);return 0>a||a>=i||0>e||e>=r?[t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+1],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+2],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+3]]:[t[n],t[n+1],t[n+2],t[n+3]]};var t,a=!1;this.gaussianRandom=function(){if(a)return a=!1,t;var e,i,r;do e=2*Math.random()-1,i=2*Math.random()-1,r=e*e+i*i;while(r>=1||0===r);var n=Math.sqrt(-2*Math.log(r)/r);return t=i*n,a=!0,e*n},this.clampPixel=function(t,a,e){return a>t?a:t>e?e:t},this.triangle=function(t){var a=this.mod(t,1);return 2*(.5>a?a:1-a)},this.mod=function(t,a){var e=parseInt(t/a,10);return t-=e*a,0>t?t+a:t},this.mixColors=function(t,a,e){var i=this.linearInterpolate(t,a[0],e[0]),r=this.linearInterpolate(t,a[1],e[1]),n=this.linearInterpolate(t,a[2],e[2]),s=this.linearInterpolate(t,a[3],e[3]);return[i,r,n,s]},this.linearInterpolate=function(t,a,e){return a+t*(e-a)},this.bilinearInterpolate=function(t,a,e,i,r,n){var s,l,h=e[0],o=e[1],u=e[2],d=e[3],f=i[0],v=i[1],m=i[2],c=i[3],g=r[0],p=r[1],x=r[2],V=r[3],w=n[0],F=n[1],M=n[2],b=n[3],I=1-t,A=1-a;s=I*d+t*c,l=I*V+t*b;var y=A*s+a*l;s=I*h+t*f,l=I*g+t*w;var R=A*s+a*l;s=I*o+t*v,l=I*p+t*F;var D=A*s+a*l;s=I*u+t*m,l=I*x+t*M;var S=A*s+a*l;return[R,D,S,y]},this.tableFilter=function(t,a,e,i){for(var r=0;i>r;r++)for(var n=0;e>n;n++)for(var s=4*(r*e+n),l=0;3>l;l++)t[s+l]=a[t[s+l]]},this.convolveFilter=function(t,a,e,i){var r,n,s=[];r=n=Math.sqrt(a.length);for(var l=parseInt(r/2,10),h=parseInt(n/2,10),o=0;i>o;o++)for(var u=0;e>u;u++){for(var d=4*(o*e+u),f=0,v=0,m=0,c=-l;l>=c;c++){var g,p=o+c;g=p>=0&&i>p?p*e:o*e;for(var x=n*(c+l)+h,V=-h;h>=V;V++){var w=a[x+V];if(0!==w){var F=u+V;F>=0&&e>F||(F=u);var M=4*(g+F);f+=w*t[M],v+=w*t[M+1],m+=w*t[M+2]}}}s[d]=parseInt(f+.5,10),s[d+1]=parseInt(v+.5,10),s[d+2]=parseInt(m+.5,10),s[d+3]=t[d+3]}for(var b=0;bl;l++)for(var h=0;e>h;h++){var o=4*(l*e+h);a.apply(this,[h,l,r]);var u,d,f,v,m=Math.floor(r[0]),c=Math.floor(r[1]),g=r[0]-m,p=r[1]-c;if(m>=0&&e-1>m&&c>=0&&i-1>c){var x=4*(e*c+m);u=[t[x],t[x+1],t[x+2],t[x+3]],d=[t[x+4],t[x+5],t[x+6],t[x+7]],f=[t[x+4*e],t[x+4*e+1],t[x+4*e+2],t[x+4*e+3]],v=[t[x+4*(e+1)],t[x+4*(e+1)+1],t[x+4*(e+1)+2],t[x+4*(e+1)+3]]}else u=this.getPixel(t,m,c,e,i),d=this.getPixel(t,m+1,c,e,i),f=this.getPixel(t,m,c+1,e,i),v=this.getPixel(t,m+1,c+1,e,i);var V=this.bilinearInterpolate(g,p,u,d,f,v);n[o]=V[0],n[o+1]=V[1],n[o+2]=V[2],n[o+3]=V[3]}for(var w=0;wl&&(l=0),e=l>=2.5?.98711*l-.9633:l>=.5?3.97156-4.14554*Math.sqrt(1-.26891*l):2*l*(3.97156-4.14554*Math.sqrt(.865545));var h,o,u,d,f,v,m=e*e,c=m*e,g=1.57825+2.44413*e+1.4281*m+.422205*c,p=(2.44413*e+2.85619*m+1.26661*c)/g,x=-(1.4281*m+1.26661*c)/g,V=.422205*c/g,w=1-(p+x+V),F=0;for(F=0;3>F;F++)for(var M=0;n>M;M++){for(h=M*r+F,o=M*r+(i-1<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=M*r+(i-1<<2)+F,o=M*r+F,u=s[h],d=u,f=d,v=f;h>=o;h-=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}for(F=0;3>F;F++)for(var b=0;i>b;b++){for(h=(b<<2)+F,o=(n-1)*r+(b<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=(n-1)*r+(b<<2)+F,o=(b<<2)+F,u=s[h],d=u,f=d,v=f;h>=o;h-=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}}}function BrightnessFilter(){this.name="Brightness",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);u[2]+=s,u[2]<0?u[2]=0:u[2]>1&&(u[2]=1);for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function BumpFilter(){this.name="Bump",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[-1,-1,0,-1,1,1,0,1,1];t.convolveFilter(r,n,e,i)}}function CircleSmearFilter(){this.name="Circle Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=d*d,v=parseInt(2*o/30*i*r/2,10),m=0;v>m;m++)for(var c=(Math.random()*Math.pow(2,32)&2147483647)%i,g=(Math.random()*Math.pow(2,32)&2147483647)%r,p=[n[4*(g*i+c)],n[4*(g*i+c)+1],n[4*(g*i+c)+2],n[4*(g*i+c)+3]],x=c-d;c+d+1>x;x++)for(var V=g-d;g+d+1>V;V++){var w=(x-c)*(x-c)+(V-g)*(V-g);if(x>=0&&i>x&&V>=0&&r>V&&f>=w)for(var F=[s[4*(V*i+x)],s[4*(V*i+x)+1],s[4*(V*i+x)+2],s[4*(V*i+x)+3]],M=t.mixColors(u,F,p),b=0;3>b;b++)s[4*(V*i+x)+b]=M[b]}for(var I=0;Is&&(s=0);for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*((h/255-.5)*s+.5),10);t.tableFilter(n,l,i,r)}}function CrossSmearFilter(){this.name="Cross Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,mix:.5},this.valueRanges={distance:{min:0,max:30},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=0),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=parseInt(2*o*i*r/(h+1),10),f=0;d>f;f++){for(var v,m,c,g=(Math.random()*Math.pow(2,32)&2147483647)%i,p=(Math.random()*Math.pow(2,32)&2147483647)%r,x=Math.random()*Math.pow(2,32)%h+1,V=[n[4*(p*i+g)],n[4*(p*i+g)+1],n[4*(p*i+g)+2],n[4*(p*i+g)+3]],w=g-x;g+x+1>w;w++)if(w>=0&&i>w)for(v=[s[4*(p*i+w)],s[4*(p*i+w)+1],s[4*(p*i+w)+2],s[4*(p*i+w)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(p*i+w)+c]=m[c];for(var F=p-x;p+x+1>F;F++)if(F>=0&&r>F)for(v=[s[4*(F*i+g)],s[4*(F*i+g)+1],s[4*(F*i+g)+2],s[4*(F*i+g)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(F*i+g)+c]=m[c]}for(var M=0;Mo;o++){var u=2*Math.PI*o/256;l[o]=s*Math.sin(u),h[o]=s*Math.cos(u)}transInverse=function(t,a,e){var i=parseInt(255*Math.random(),10),r=Math.random();e[0]=t+r*l[i],e[1]=a+r*h[i]},t.transformFilter(n,transInverse,i,r)}}function DitherFilter(){this.name="Dither",this.isDirAnimatable=!1,this.defaultValues={levels:3,color:!0},this.valueRanges={levels:{min:2,max:30},color:{min:!1,max:!0}};new FilterUtils;this.filter=function(t,a){var e,i,r=t.width,n=t.height,s=t.data,l=[];for(i=0;i=h&&(h=1);var u=[0,0,0,0,0,7,3,5,1],d=16,f=0,v=[];for(e=0;h>e;e++)v[e]=parseInt(255*e/(h-1),10);var m=[];for(e=0;256>e;e++)m[e]=parseInt(h*e/256,10);for(var c=0;n>c;c++){var g,p=1==(1&c);p?(f=4*(c*r+r-1),g=-1):(f=c*r*4,g=1);for(var x=0;r>x;x++){var V=s[f],w=s[f+1],F=s[f+2];o||(V=w=F=parseInt((V+w+F)/3,10));var M=v[m[V]],b=v[m[w]],I=v[m[F]];l[f]=M,l[f+1]=b,l[f+2]=I,l[f+3]=s[f+3];var A=V-M,y=w-b,R=F-I;for(e=-1;1>=e;e++){var D=e+c;if(D>=0&&n>D)for(i=-1;1>=i;i++){var S=i+x;if(S>=0&&r>S){var P;if(P=p?u[3*(e+1)-i+1]:u[3*(e+1)+i+1],0!==P){var W=p?f-4*i:f+4*i;V=s[W],w=s[W+1],F=s[W+2];var U=P/d;V+=A*U,w+=y*U,F+=R*U,s[W]=V,s[W+1]=w,s[W+2]=F}}}}f+=4*g}}for(i=0;il;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=0;gh=0,bh=0;var d=0;gv=0,bv=0;for(var f=-1;1>=f;f++){var v,m=l+f;v=m>=0&&r>m?m*i*4:l*i*4;for(var c=3*(f+1)+1,g=-1;1>=g;g++){var p=h+g;p>=0&&i>p||(p=h),p*=4;var x=n[v+p],V=n[v+p+1],w=n[v+p+2],F=t[c+g],M=a[c+g];u+=parseInt(F*x,10),bh+=parseInt(F*V,10),gh+=parseInt(F*w,10),d+=parseInt(M*x,10),gv+=parseInt(M*V,10),bv+=parseInt(M*w,10)}}x=parseInt(Math.sqrt(u*u+d*d)/1.8,10),V=parseInt(Math.sqrt(gh*gh+gv*gv)/1.8,10),w=parseInt(Math.sqrt(bh*bh+bv*bv)/1.8,10),s[o]=x,s[o+1]=V,s[o+2]=w,s[o+3]=n[o+3]}for(var b=0;bA;A++,I+=d)for(var y=I,R=y+d,D=R+d,S=0;e>S;S++,y++,R++,D++){var P=4*(A*e+S);0!==A&&i-2>A&&0!==S&&e-2>S?(v=u[y-1]+u[R-1]+u[D-1]-u[y+1]-u[R+1]-u[D+1],m=u[D-1]+u[D]+u[D+1]-u[y-1]-u[y]-u[y+1],M=0===v&&0===m?b:(F=v*g+m*p+w)<0?0:parseInt(F/Math.sqrt(v*v+m*m+V),10)):M=b,r[P]=r[P+1]=r[P+2]=M}}}function ExposureFilter(){this.name="Exposure",this.isDirAnimatable=!0,this.defaultValues={exposure:1},this.valueRanges={exposure:{min:0,max:5}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.exposure?this.defaultValues.exposure:e.exposure,l=[],h=0;256>h;h++)l[h]=parseInt(255*(1-Math.exp(-(h/255)*s)),10);t.tableFilter(n,l,i,r)}}function GainFilter(){this.name="Gain/Bias",this.isDirAnimatable=!0,this.defaultValues={gain:.5,bias:.5},this.valueRanges={gain:{min:0,max:1},bias:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.gain?this.defaultValues.gain:e.gain,l=void 0===e.bias?this.defaultValues.bias:e.bias,h=[],o=0;256>o;o++){var u=o/255,d=(1/s-2)*(1-2*u);u=.5>u?u/(d+1):(d-u)/(d-1),u/=(1/l-2)*(1-u)+1,h[o]=parseInt(255*u,10)}t.tableFilter(n,h,i,r)}}function GammaFilter(){this.name="Gamma",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;if(0>n&&(n=0),!FilterUtils)return void(console&&console.error("Unable to find filterutils.js, please include this file! (Required by "+this.name+" filter)"));for(var s=new FilterUtils,l=[],h=0;256>h;h++)l[h]=255*Math.pow(h/255,1/n)+.5;s.tableFilter(r,l,e,i)}}function GrayscaleFilter(){this.name="Grayscale",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++){var s=4*(r*a+n),l=.3*i[s]+.59*i[s+1]+.11*i[s+2];i[s]=i[s+1]=i[s+2]=l}}}function HueFilter(){this.name="Hue",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);for(u[0]+=s;u[0]<0;)u[0]+=360;for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function InvertFilter(){this.name="Invert",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++)for(var s=4*(r*a+n),l=0;3>l;l++)i[s+l]=255-i[s+l]}}function KaleidoscopeFilter(){this.name="Kaleidoscope",this.isDirAnimatable=!1,this.defaultValues={angle:0,rotation:0,sides:3,centerX:.5,centerY:.5},this.valueRanges={angle:{min:0,max:360},rotation:{min:0,max:360},sides:{min:1,max:30},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.rotation?this.defaultValues.rotation:e.rotation,h=void 0===e.sides?this.defaultValues.sides:e.sides,o=void 0===e.centerX?this.defaultValues.centerX:e.centerX,u=void 0===e.centerY?this.defaultValues.centerY:e.centerY,d=i*o,f=r*u;s=s/180*Math.PI,l=l/180*Math.PI;var v=function(a,e,i){var r=a-d,n=e-f,o=Math.sqrt(r*r+n*n),u=Math.atan2(n,r)-s-l;u=t.triangle(u/Math.PI*h*.5),u+=s,i[0]=d+o*Math.cos(u),i[1]=f+o*Math.sin(u)};t.transformFilter(n,v,i,r)}}function LensDistortionFilter(){this.name="Lens Distortion",this.isDirAnimatable=!1,this.defaultValues={refraction:1.5,radius:50,centerX:.5,centerY:.5},this.valueRanges={refraction:{min:1,max:10},radius:{min:1,max:200},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.refraction?this.defaultValues.refraction:e.refraction,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o,d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i,l=r*r;if(l>=u-u*n/u)e[0]=t,e[1]=a;else{var h=1/s,o=Math.sqrt((1-n/u-l/u)*u),v=o*o,m=Math.acos(i/Math.sqrt(n+v)),c=Math.PI/2-m,g=Math.asin(Math.sin(c)*h);g=Math.PI/2-m-g,e[0]=t-Math.tan(g)*o;var p=Math.acos(r/Math.sqrt(l+v));c=Math.PI/2-p,g=Math.asin(Math.sin(c)*h),g=Math.PI/2-p-g,e[1]=a-Math.tan(g)*o}};t.transformFilter(n,v,i,r)}}function LineSmearFilter(){this.name="Line Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,angle:0,mix:.5},this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.angle?this.defaultValues.angle:e.angle,d=void 0===e.mix?this.defaultValues.mix:e.mix;u=u/180*Math.PI;for(var f=Math.sin(u),v=Math.cos(u),m=parseInt(2*o*r*n/2,10),c=0;m>c;c++){var g,p,x,V,w,F,M,b=(Math.random()*Math.pow(2,32)&2147483647)%r,I=(Math.random()*Math.pow(2,32)&2147483647)%n,A=(Math.random()*Math.pow(2,32)&2147483647)%h+1,y=[s[4*(I*r+b)],s[4*(I*r+b)+1],s[4*(I*r+b)+2],s[4*(I*r+b)+3]],R=parseInt(A*v,10),D=parseInt(A*f,10),S=b-R,P=I-D,W=b+R,U=I+D;F=S>W?-1:1,M=P>U?-1:1,R=W-S,D=U-P,R=Math.abs(R),D=Math.abs(D),g=S,p=P;var X,Y;if(r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i];if(Math.abs(R)>Math.abs(D)){for(x=2*D-R,V=2*D,w=2*(D-R);g!=W;)if(0>=x?x+=V:(x+=w,p+=M),g+=F,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}else for(x=2*R-D,V=2*R,w=2*(R-D);p!=U;)if(0>=x?x+=V:(x+=w,g+=F),p+=M,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}for(i=0;in;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=0,o=0,u=0,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.max(h,i[c]),o=Math.max(o,i[c+1]),u=Math.max(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=[],o=[],u=[],d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h.push(i[c]),o.push(i[c+1]),u.push(i[c+2])}}}var g=function(t,a){return t-a};h.sort(g),o.sort(g),u.sort(g),r[l]=h[4],r[l+1]=o[4],r[l+2]=u[4],r[l+3]=i[l+3]}for(var p=0;pn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=255,o=255,u=255,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.min(h,i[c]),o=Math.min(o,i[c+1]),u=Math.min(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gh;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);if(Math.random()<=s){var d;if(l)d=parseInt((2*Math.random()-1)*n,10),r[u]+=d,r[u+1]+=d,r[u+2]+=d;else for(var f=0;3>f;f++)d=parseInt((2*Math.random()-1)*n,10),r[u+f]+=d}}}}function OilFilter(){this.name="Oil Painting",this.isDirAnimatable=!1,this.defaultValues={range:3},this.valueRanges={range:{min:0,max:5}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.range?this.defaultValues.range:a.range;s=parseInt(s,10);for(var l=[],h=[],o=[],u=[],d=[],f=[],v=256,m=0;i>m;m++)for(var c=0;e>c;c++){for(var g=4*(m*e+c),p=0;v>p;p++)l[p]=h[p]=o[p]=u[p]=d[p]=f[p]=0;for(var x=-s;s>=x;x++){var V,w=m+x;if(w>=0&&i>w){V=w*e;for(var F=-s;s>=F;F++){var M=c+F;if(M>=0&&e>M){var b=r[4*(V+M)],I=r[4*(V+M)+1],A=r[4*(V+M)+2],y=b*v/256,R=I*v/256,D=A*v/256;u[y]+=b,d[R]+=I,f[D]+=A,l[y]++,h[R]++,o[D]++}}}}for(var S=0,P=0,W=0,U=1;v>U;U++)l[U]>l[S]&&(S=U),h[U]>h[P]&&(P=U),o[U]>o[W]&&(W=U);S=u[S]/l[S],P=d[P]/h[P],W=f[W]/o[W],n[g]=S,n[g+1]=P,n[g+2]=W,n[g+3]=r[g+3]}for(var X=0;Xs;s++)for(var l=0;e>l;l++){var h=4*(s*e+l);r[h+3]=255*n}}}function PinchFilter(){this.name="Pinch/Whirl",this.isDirAnimatable=!1,this.defaultValues={amount:.5,radius:100,angle:0,centerX:.5,centerY:.5},this.valueRanges={amount:{min:-1,max:1},radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=void 0===e.angle?this.defaultValues.angle:e.angle,h=void 0===e.centerX?this.defaultValues.centerX:e.centerX,o=void 0===e.centerY?this.defaultValues.centerY:e.centerY,u=void 0===e.radius?this.defaultValues.radius:e.radius,d=u*u;l=l/180*Math.PI;var f=i*h,v=r*o,m=function(t,a,e){var i=t-f,r=a-v,n=i*i+r*r;if(n>d||0===n)e[0]=t,e[1]=a;else{var h=Math.sqrt(n/d),o=Math.pow(Math.sin(.5*Math.PI*h),-s);i*=o,r*=o;var u=1-h,m=l*u*u,c=Math.sin(m),g=Math.cos(m);e[0]=f+g*i-c*r,e[1]=v+c*i+g*r}};t.transformFilter(n,m,i,r)}}function PixelationFilter(){this.name="Pixelation",this.isDirAnimatable=!1,this.defaultValues={size:5},this.valueRanges={size:{min:1,max:50}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.size?this.defaultValues.size:a.size;n=parseInt(n,10);for(var s,l,h,o=0;i>o;o+=n)for(var u=0;e>u;u+=n){var d=Math.min(n,e-u),f=Math.min(n,i-o),v=d*f,m=0,c=0,g=0;for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),m+=r[h],c+=r[h+1],g+=r[h+2];for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),r[h]=m/v,r[h+1]=c/v,r[h+2]=g/v}}}function PosterizeFilter(){this.name="Posterize",this.isDirAnimatable=!1,this.defaultValues={levels:6},this.valueRanges={levels:{min:2,max:30}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.levels?this.defaultValues.levels:parseInt(e.levels,10);if(!(1>=s)){for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*parseInt(h*s/256,10)/(s-1),10);t.tableFilter(n,l,i,r)}}}function RGBAdjustFilter(){this.name="RGBAdjust",this.isDirAnimatable=!0,this.defaultValues={red:1,green:1,blue:1},this.valueRanges={red:{min:0,max:2},green:{min:0,max:2},blue:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.red?this.defaultValues.red:a.red,s=void 0===a.green?this.defaultValues.green:a.green,l=void 0===a.blue?this.defaultValues.blue:a.blue;0>n&&(n=0),0>s&&(s=0),0>l&&(l=0);for(var h=0;i>h;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);r[u]*=n,r[u+1]*=s,r[u+2]*=l}}}function SaturationFilter(){this.name="Saturation",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);for(var n=void 0===a.amount?this.defaultValues.amount:a.amount,s=.3,l=.59,h=.11,o=(1-n)*s+n,u=(1-n)*s,d=(1-n)*s,f=(1-n)*l,v=(1-n)*l+n,m=(1-n)*l,c=(1-n)*h,g=(1-n)*h,p=(1-n)*h+n,x=0;i>x;x++)for(var V=0;e>V;V++){var w=4*(x*e+V),F=r[w],M=r[w+1],b=r[w+2];r[w]=o*F+f*M+c*b,r[w+1]=u*F+v*M+g*b,r[w+2]=d*F+m*M+p*b}}}function SawtoothRippleFilter(){this.name="Sawtooth Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.mod(r,1),d=t.mod(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function SepiaFilter(){this.name="Sepia",this.isDirAnimatable=!0,this.defaultValues={amount:10},this.valueRanges={amount:{min:0,max:30}};new FilterUtils;this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;n*=2.55;for(var s=0;i>s;s++)for(var l=0;e>l;l++){var h,o,u,d=4*(s*e+l),f=.3*r[d]+.59*r[d+1]+.11*r[d+2];h=o=u=f,h+=40,o+=20,u-=n,r[d]=h,r[d+1]=o,r[d+2]=u}}}function SharpenFilter(){this.name="Sharpen",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[0,-.2,0,-.2,1.8,-.2,0,-.2,0];t.convolveFilter(r,n,e,i)}}function SineRippleFilter(){this.name="Sine Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(t,a,e){var i=a/h,r=t/o,n=Math.sin(i),u=Math.sin(r);e[0]=t+s*n,e[1]=a+l*u};t.transformFilter(n,u,i,r)}}function SolarizeFilter(){this.name="Solarize",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){for(var e=a.width,i=a.height,r=a.data,n=[],s=0;256>s;s++){var l=s/255>.5?2*(s/255-.5):2*(.5-s/255);n[s]=parseInt(255*l,10)}t.tableFilter(r,n,e,i)}}function SparkleFilter(){this.name="Sparkle",this.isDirAnimatable=!1,this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:.5,centerY:.5},this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.rays?this.defaultValues.rays:e.rays;s=parseInt(s,10);for(var l=void 0===e.size?this.defaultValues.size:e.size,h=void 0===e.amount?this.defaultValues.amount:e.amount,o=void 0===e.randomness?this.defaultValues.randomness:e.randomness,u=void 0===e.centerX?this.defaultValues.centerX:e.centerX,d=void 0===e.centerY?this.defaultValues.centerY:e.centerY,f=u*i,v=d*r,m=[],c=0;s>c;c++)m[c]=l+o/100*l*t.gaussianRandom();for(var g=0;r>g;g++)for(var p=0;i>p;p++){var x=4*(g*i+p),V=p-f,w=g-v,F=V*V+w*w,M=Math.atan2(w,V),b=(M+Math.PI)/(2*Math.PI)*s,I=parseInt(b,10),A=b-I;if(0!==l){var y=t.linearInterpolate(A,m[I%s],m[(I+1)%s]),R=y*y/(F+1e-4);R=Math.pow(R,(100-h)/50),A-=.5,A=1-A*A,A*=R}A=t.clampPixel(A,0,1);for(var D=t.mixColors(A,[n[x],n[x+1],n[x+2],n[x+3]],[255,255,255,255]),S=0;3>S;S++)n[x+S]=D[S]}}}function SquareSmearFilter(){this.name="Square Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=parseInt(2*o/30*r*n/2,10),v=0;f>v;v++)for(var m=(Math.random()*Math.pow(2,32)&2147483647)%r,c=(Math.random()*Math.pow(2,32)&2147483647)%n,g=[s[4*(c*r+m)],s[4*(c*r+m)+1],s[4*(c*r+m)+2],s[4*(c*r+m)+3]],p=m-d;m+d+1>p;p++)for(var x=c-d;c+d+1>x;x++)if(p>=0&&r>p&&x>=0&&n>x){var V=[l[4*(x*r+p)],l[4*(x*r+p)+1],l[4*(x*r+p)+2],l[4*(x*r+p)+3]],w=t.mixColors(u,V,g);for(i=0;3>i;i++)l[4*(x*r+p)+i]=w[i]}for(i=0;is;s++)for(var l=0;e>l;l++){var h=4*(s*e+l),o=(r[h]+r[h+1]+r[h+2])/3,u=0;o>n&&(u=255),r[h]=r[h+1]=r[h+2]=u}}}function TriangleRippleFilter(){this.name="Triangle Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.triangle(r,1),d=t.triangle(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function TwirlFilter(){this.name="Twirl",this.isDirAnimatable=!1,this.defaultValues={radius:100,angle:180,centerX:.5,centerY:.5},this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o;s=s/180*Math.PI;var d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i+r*r;if(n>u)e[0]=t,e[1]=a;else{n=Math.sqrt(n);var l=Math.atan2(r,i)+s*(o-n)/o;e[0]=d+n*Math.cos(l),e[1]=f+n*Math.sin(l)}};t.transformFilter(n,v,i,r)}}function VignetteFilter(){this.name="Vignette",this.isDirAnimatable=!1,this.defaultValues={amount:.3},this.valueRanges={amount:{min:0,max:1}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.amount?this.defaultValues.amount:a.amount,l=document.createElement("canvas");l.width=e,l.height=i;var h,o=l.getContext("2d"),u=Math.sqrt(Math.pow(e/2,2)+Math.pow(i/2,2));o.putImageData(t,0,0),o.globalCompositeOperation="source-over",h=o.createRadialGradient(e/2,i/2,0,e/2,i/2,u),h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(.5,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,"+s+")"),o.fillStyle=h,o.fillRect(0,0,e,i),n=o.getImageData(0,0,e,i).data;for(var d=0;df)e[0]=t,e[1]=a;else{var o=Math.sqrt(n),u=l*Math.sin(o/s*Math.PI*2-h);u*=(d-o)/d,0!==o&&(u*=s/o),e[0]=t+i*u,e[1]=a+r*u}};t.transformFilter(n,c,i,r)}}var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter,maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter,triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter};"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=JSManipulate); diff --git a/package.json b/package.json new file mode 100644 index 0000000..d70f05e --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "author": "Joel Besada", + "name": "jsmanipulate", + "version": "1.0.0", + "description": "JSManipulate is an image filter and effects library written in Javascript for client-side manipulation of images on a web page.", + "main": "jsmanipulate.js", + "license": "MIT", + "keywords": ["canvas", "image", "images", "graphics", "filter", "filters", "manipulation", "manipulate", "distortion", "distort", "effects", "blur", "sharpen", "emboss", "invert"], + "homepage": "http://joelb.me/jsmanipulate/", + "repository": { + "type": "git", + "url": "https://github.com/dholtzmann/JSManipulate.git", + }, + "dependencies": { + "canvas": "1.2.7" + }, + "engines": { "node": "*" } +} From bcdd04b2d9a2893e94308a0edbec5ca345616c8f Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Tue, 15 Sep 2015 15:41:05 -0700 Subject: [PATCH 3/8] Changed file hierarchy --- filter list/index.html | 15 - filter list/style/style.css | 35 - script/jsmanipulate.js | 2316 --------------------- script/minified/jsmanipulate.min.js | 15 - script/package.json | 15 - script/separate filters/blur.js | 97 - script/separate filters/brightness.js | 42 - script/separate filters/bump.js | 27 - script/separate filters/circlesmear.js | 62 - script/separate filters/contrast.js | 34 - script/separate filters/crosssmear.js | 68 - script/separate filters/diffusion.js | 41 - script/separate filters/dither.js | 99 - script/separate filters/edge.js | 63 - script/separate filters/emboss.js | 72 - script/separate filters/exposure.js | 30 - script/separate filters/filterutils.js | 214 -- script/separate filters/gain.js | 41 - script/separate filters/gamma.js | 33 - script/separate filters/grayscale.js | 21 - script/separate filters/hue.js | 39 - script/separate filters/invert.js | 22 - script/separate filters/kaleidoscope.js | 52 - script/separate filters/lensdistortion.js | 66 - script/separate filters/linesmear.js | 130 -- script/separate filters/maximum.js | 45 - script/separate filters/median.js | 49 - script/separate filters/minimum.js | 45 - script/separate filters/noise.js | 42 - script/separate filters/oil.js | 83 - script/separate filters/opacity.js | 25 - script/separate filters/pinch.js | 67 - script/separate filters/pixelation.js | 45 - script/separate filters/posterize.js | 33 - script/separate filters/rgbadjust.js | 35 - script/separate filters/saturation.js | 39 - script/separate filters/sawtoothripple.js | 44 - script/separate filters/sepia.js | 41 - script/separate filters/sharpen.js | 25 - script/separate filters/sineripple.js | 44 - script/separate filters/solarize.js | 27 - script/separate filters/sparkle.js | 72 - script/separate filters/squaresmear.js | 62 - script/separate filters/threshold.js | 30 - script/separate filters/triangleripple.js | 44 - script/separate filters/twirl.js | 54 - script/separate filters/vignette.js | 38 - script/separate filters/waterripple.js | 63 - 48 files changed, 4701 deletions(-) delete mode 100644 filter list/index.html delete mode 100644 filter list/style/style.css delete mode 100644 script/jsmanipulate.js delete mode 100644 script/minified/jsmanipulate.min.js delete mode 100644 script/package.json delete mode 100644 script/separate filters/blur.js delete mode 100644 script/separate filters/brightness.js delete mode 100644 script/separate filters/bump.js delete mode 100644 script/separate filters/circlesmear.js delete mode 100644 script/separate filters/contrast.js delete mode 100644 script/separate filters/crosssmear.js delete mode 100644 script/separate filters/diffusion.js delete mode 100644 script/separate filters/dither.js delete mode 100644 script/separate filters/edge.js delete mode 100644 script/separate filters/emboss.js delete mode 100644 script/separate filters/exposure.js delete mode 100644 script/separate filters/filterutils.js delete mode 100644 script/separate filters/gain.js delete mode 100644 script/separate filters/gamma.js delete mode 100644 script/separate filters/grayscale.js delete mode 100644 script/separate filters/hue.js delete mode 100644 script/separate filters/invert.js delete mode 100644 script/separate filters/kaleidoscope.js delete mode 100644 script/separate filters/lensdistortion.js delete mode 100644 script/separate filters/linesmear.js delete mode 100644 script/separate filters/maximum.js delete mode 100644 script/separate filters/median.js delete mode 100644 script/separate filters/minimum.js delete mode 100644 script/separate filters/noise.js delete mode 100644 script/separate filters/oil.js delete mode 100644 script/separate filters/opacity.js delete mode 100644 script/separate filters/pinch.js delete mode 100644 script/separate filters/pixelation.js delete mode 100644 script/separate filters/posterize.js delete mode 100644 script/separate filters/rgbadjust.js delete mode 100644 script/separate filters/saturation.js delete mode 100644 script/separate filters/sawtoothripple.js delete mode 100644 script/separate filters/sepia.js delete mode 100644 script/separate filters/sharpen.js delete mode 100644 script/separate filters/sineripple.js delete mode 100644 script/separate filters/solarize.js delete mode 100644 script/separate filters/sparkle.js delete mode 100644 script/separate filters/squaresmear.js delete mode 100644 script/separate filters/threshold.js delete mode 100644 script/separate filters/triangleripple.js delete mode 100644 script/separate filters/twirl.js delete mode 100644 script/separate filters/vignette.js delete mode 100644 script/separate filters/waterripple.js diff --git a/filter list/index.html b/filter list/index.html deleted file mode 100644 index 68c877f..0000000 --- a/filter list/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - JSManipulate - Filter List - - -
-

JSManipulate Filter List

-
-

Blur

filter: blur

Default Values

amount: 3

Brightness

filter: brightness

Default Values

amount: 0

Bump

filter: bump

Default Values

N/A

Circle Smear

filter: circlesmear

Default Values

size: 4
density: 0.5
mix: 0.5

Contrast

filter: contrast

Default Values

amount: 1

Cross Smear

filter: crosssmear

Default Values

distance: 8
density: 0.5
mix: 0.5

Diffusion

filter: diffusion

Default Values

scale: 4

Dither

filter: dither

Default Values

levels: 3
color: true

Edge Detection

filter: edge

Default Values

N/A

Emboss

filter: emboss

Default Values

height: 1
angle: 135
elevation: 30

Exposure

filter: exposure

Default Values

exposure: 1

Gain/Bias

filter: gain

Default Values

gain: 0.5
bias: 0.5

Gamma

filter: gamma

Default Values

amount: 1

Grayscale

filter: grayscale

Default Values

N/A

Hue

filter: hue

Default Values

amount: 0

Invert

filter: invert

Default Values

N/A

Kaleidoscope

filter: kaleidoscope

Default Values

angle: 0
rotation: 0
sides: 3
centerX: 0.5
centerY: 0.5

Lens Distortion

filter: lensdistortion

Default Values

refraction: 1.5
radius: 50
centerX: 0.5
centerY: 0.5

Line Smear

filter: linesmear

Default Values

distance: 8
density: 0.5
angle: 0
mix: 0.5

Maximum

filter: maximum

Default Values

N/A

Median

filter: median

Default Values

N/A

Minimum

filter: minimum

Default Values

N/A

Noise

filter: noise

Default Values

amount: 25
density: 1
monochrome: true

Oil Painting

filter: oil

Default Values

range: 3

Opacity

filter: opacity

Default Values

amount: 1

Pinch/Whirl

filter: pinch

Default Values

amount: 0.5
radius: 100
angle: 0
centerX: 0.5
centerY: 0.5

Pixelation

filter: pixelate

Default Values

size: 5

Posterize

filter: posterize

Default Values

levels: 6

RGBAdjust

filter: rgbadjust

Default Values

red: 1
green: 1
blue: 1

Saturation

filter: saturation

Default Values

amount: 1

Sawtooth Ripples

filter: sawtoothripple

Default Values

xAmplitude: 5
yAmplitude: 5
xWavelength: 16
yWavelength: 16

Sepia

filter: sepia

Default Values

amount: 10

Sharpen

filter: sharpen

Default Values

N/A

Sine Ripples

filter: sineripple

Default Values

xAmplitude: 5
yAmplitude: 5
xWavelength: 16
yWavelength: 16

Solarize

filter: solarize

Default Values

N/A

Sparkle

filter: sparkle

Default Values

rays: 50
size: 25
amount: 50
randomness: 25
centerX: 0.5
centerY: 0.5

Square Smear

filter: squaresmear

Default Values

size: 4
density: 0.5
mix: 0.5

Black & White

filter: threshold

Default Values

threshold: 127

Triangle Ripples

filter: triangleripple

Default Values

xAmplitude: 5
yAmplitude: 5
xWavelength: 16
yWavelength: 16

Twirl

filter: twirl

Default Values

radius: 100
angle: 180
centerX: 0.5
centerY: 0.5

Vignette

filter: vignette

Default Values

amount: 0.3

Water Ripples

filter: waterripple

Default Values

phase: 0
radius: 50
wavelength: 16
amplitude: 10
centerX: 0.5
centerY: 0.5
-
- - diff --git a/filter list/style/style.css b/filter list/style/style.css deleted file mode 100644 index 4c003bb..0000000 --- a/filter list/style/style.css +++ /dev/null @@ -1,35 +0,0 @@ -body { - background-color: #EEEEEE; - color: #232323; - font: 14px Helvetica, Arial, sans-serif; - line-height: 1.5em; -} - -.filter { - display:block; - color: #EEEEEE; - background-color: #232323; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - overflow: auto; - padding: 10px 20px 10px 20px; -} -.default-values, .recommended-values { - float: left; - display: block; - margin-right: 50px; - -} -h2 { - clear: both; -} -h3 { - color: #FFFFFF; - font-size: 14px; - margin-bottom: 5px; -} -#wrapper { - width: 580px; - margin: 0 auto; -} \ No newline at end of file diff --git a/script/jsmanipulate.js b/script/jsmanipulate.js deleted file mode 100644 index 5fd1e47..0000000 --- a/script/jsmanipulate.js +++ /dev/null @@ -1,2316 +0,0 @@ -/* -========================================================================= - JSManipulate v1.0 (2011-08-01) - -Javascript image filter & effect library - -Developed by Joel Besada (http://www.joelb.me) -Demo page: http://www.joelb.me/jsmanipulate - -MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) -Copyright (c) 2011, Joel Besada -========================================================================= -*/ - - -/** - * Contains common filter functions. - */ -function FilterUtils(){ - this.HSVtoRGB = function (h, s, v){ - var r, g, b; - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - switch(i % 6){ - case 0: r = v; g = t; b = p; break; - case 1: r = q; g = v; b = p; break; - case 2: r = p; g = v; b = t; break; - case 3: r = p; g = q; b = v; break; - case 4: r = t; g = p; b = v; break; - case 5: r = v; g = p; b = q; break; - default: break; - } - return [r * 255, g * 255, b * 255]; - }; - this.RGBtoHSV = function (r, g, b){ - r = r/255; g = g/255; b = b/255; - var max = Math.max(r, g, b); - var min = Math.min(r, g, b); - var h, s, v = max; - var d = max - min; - s = max === 0 ? 0 : d / max; - if(max === min){ - h = 0; - }else{ - switch(max){ - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - default: break; - } - h /= 6; - } - return [h, s, v]; - }; - this.getPixel = function (pixels,x,y,width,height){ - var pix = (y*width + x)*4; - if (x < 0 || x >= width || y < 0 || y >= height) { - return [pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 1], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 2], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 3]]; - } - return [pixels[pix],pixels[pix+1],pixels[pix+2],pixels[pix+3]]; - }; - var haveNextGaussian = false; - var nextGaussian; - this.gaussianRandom = function(){ - if(haveNextGaussian){ - haveNextGaussian = false; - return nextGaussian; - } else { - var v1, v2, s; - do { - v1 = 2 * Math.random() - 1; - v2 = 2 * Math.random() - 1; - s = v1 * v1 + v2 * v2; - } while (s >= 1 || s === 0); - var mult = Math.sqrt(-2 * Math.log(s)/s); - nextGaussian = v2 * mult; - haveNextGaussian = true; - return v1 * mult; - } - }; - this.clampPixel = function (x,a,b){ - return (x < a) ? a : (x > b) ? b : x; - }; - this.triangle = function(x){ - var r = this.mod(x, 1); - return 2*(r < 0.5 ? r : 1-r); - }; - this.mod = function(a,b){ - var n = parseInt(a/b,10); - a -= n*b; - if(a < 0){ - return a + b; - } - return a; - }; - this.mixColors = function(t, rgb1, rgb2){ - var r = this.linearInterpolate(t,rgb1[0],rgb2[0]); - var g = this.linearInterpolate(t,rgb1[1],rgb2[1]); - var b = this.linearInterpolate(t,rgb1[2],rgb2[2]); - var a = this.linearInterpolate(t,rgb1[3],rgb2[3]); - return [r,g,b,a]; - }; - - this.linearInterpolate = function(t,a,b){ - return a + t * (b-a); - }; - this.bilinearInterpolate = function (x,y,nw,ne,sw,se){ - var m0, m1; - var r0 = nw[0]; var g0 = nw[1]; var b0 = nw[2]; var a0 = nw[3]; - var r1 = ne[0]; var g1 = ne[1]; var b1 = ne[2]; var a1 = ne[3]; - var r2 = sw[0]; var g2 = sw[1]; var b2 = sw[2]; var a2 = sw[3]; - var r3 = se[0]; var g3 = se[1]; var b3 = se[2]; var a3 = se[3]; - var cx = 1.0 - x; var cy = 1.0 - y; - - m0 = cx * a0 + x * a1; - m1 = cx * a2 + x * a3; - var a = cy * m0 + y * m1; - - m0 = cx * r0 + x * r1; - m1 = cx * r2 + x * r3; - var r = cy * m0 + y * m1; - - m0 = cx * g0 + x * g1; - m1 = cx * g2 + x * g3; - var g = cy * m0 + y * m1; - - m0 = cx * b0 + x * b1; - m1 = cx * b2 + x * b3; - var b =cy * m0 + y * m1; - return [r,g,b,a]; - }; - this.tableFilter = function (inputData, table, width, height){ - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = table[inputData[pixel+i]]; - } - } - } - }; - this.convolveFilter = function(inputData, matrix, width, height){ - var outputData = []; - var rows, cols; - rows = cols = Math.sqrt(matrix.length); - var rows2 = parseInt(rows/2,10); - var cols2 = parseInt(cols/2,10); - var trace = true; - for(var y = 0; y < height; y++){ - for (var x = 0; x < width; x++){ - var pixel = (y*width + x)*4; - var r = 0, g = 0, b = 0; - for(var row = -rows2; row <= rows2; row++){ - var iy = y+row; - var ioffset; - if (0 <= iy && iy < height) { - ioffset = iy*width; - } else { - ioffset = y*width; - } - var moffset = cols*(row+rows2)+cols2; - for (var col = -cols2; col <= cols2; col++) { - var f = matrix[moffset+col]; - if (f !== 0) { - var ix = x+col; - if (!(0 <= ix && ix < width)) { - ix = x; - } - var iPixel = (ioffset+ix)*4; - r += f * inputData[iPixel]; - g += f * inputData[iPixel+1]; - b += f * inputData[iPixel+2]; - } - } - } - outputData[pixel] = parseInt(r+0.5,10); - outputData[pixel+1] = parseInt(g+0.5,10); - outputData[pixel+2] = parseInt(b+0.5,10); - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; - this.transformFilter = function(inputData, transformInverse, width, height){ - var out = []; - var outputData = []; - for(var j = 0; j < inputData.length; j++){ - outputData[j] = inputData[j]; - } - for(var y = 0; y < height; y++){ - for (var x = 0; x < width; x++){ - var pixel = (y*width + x)*4; - transformInverse.apply(this,[x,y,out]); - var srcX = Math.floor(out[0]); - var srcY = Math.floor(out[1]); - var xWeight = out[0]-srcX; - var yWeight = out[1]-srcY; - var nw,ne,sw,se; - if(srcX >= 0 && srcX < width-1 && srcY >= 0 && srcY < height-1){ - var i = (width*srcY + srcX)*4; - nw = [inputData[i],inputData[i+1],inputData[i+2],inputData[i+3]]; - ne = [inputData[i+4],inputData[i+5],inputData[i+6],inputData[i+7]]; - sw = [inputData[i+width*4],inputData[i+width*4+1],inputData[i+width*4+2],inputData[i+width*4+3]]; - se = [inputData[i+(width + 1)*4],inputData[i+(width + 1)*4+1],inputData[i+(width + 1)*4+2],inputData[i+(width + 1)*4+3]]; - } else { - nw = this.getPixel( inputData, srcX, srcY, width, height ); - ne = this.getPixel( inputData, srcX+1, srcY, width, height ); - sw = this.getPixel( inputData, srcX, srcY+1, width, height ); - se = this.getPixel( inputData, srcX+1, srcY+1, width, height ); - } - var rgba = this.bilinearInterpolate(xWeight,yWeight,nw,ne,sw,se); - outputData[pixel] = rgba[0]; - outputData[pixel + 1] = rgba[1]; - outputData[pixel + 2] = rgba[2]; - outputData[pixel + 3] = rgba[3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Blurs the image with Gaussian blur. - */ -function BlurFilter(){ - this.name = "Blur"; - this.isDirAnimatable = false; - this.defaultValues = { - amount : 3 - }; - this.valueRanges = { - amount : {min:0, max:10} - }; - this.filter = function(input,values){ - var width = input.width; - var width4 = width << 2; - var height = input.height; - var inputData = input.data; - var q; - var amount = values.amount; - if (amount < 0.0) { - amount = 0.0; - } - if (amount >= 2.5) { - q = 0.98711 * amount - 0.96330; - } else if (amount >= 0.5) { - q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * amount); - } else { - q = 2 * amount * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5)); - } - var qq = q * q; - var qqq = qq * q; - var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq); - var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0; - var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0; - var b3 = (0.422205 * qqq) / b0; - var bigB = 1.0 - (b1 + b2 + b3); - var c = 0; - var index; - var indexLast; - var pixel; - var ppixel; - var pppixel; - var ppppixel; - for (c = 0; c < 3; c++) { - for (var y = 0; y < height; y++) { - index = y * width4 + c; - indexLast = y * width4 + ((width - 1) << 2) + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index <= indexLast; index += 4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - index = y * width4 + ((width - 1) << 2) + c; - indexLast = y * width4 + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index >= indexLast; index -= 4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - } - } - for (c = 0; c < 3; c++) { - for (var x = 0; x < width; x++) { - index = (x << 2) + c; - indexLast = (height - 1) * width4 + (x << 2) + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index <= indexLast; index += width4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - index = (height - 1) * width4 + (x << 2) + c; - indexLast = (x << 2) + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index >= indexLast; index -= width4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - } - } - }; -} -/** - * Adjusts the brightness of the image by going over to HSV values. - * Negative values decrease brightness while positive values increase brightness. - */ -function BrightnessFilter(){ - this.name = "Brightness"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 0.0 - }; - this.valueRanges = { - amount : {min:-1.0, max:1.0} - }; - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); - hsv[2] += amount; - if(hsv[2] < 0){ - hsv[2] = 0; - } else if (hsv[2] > 1){ - hsv[2] = 1; - } - var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = rgb[i]; - } - } - } - }; -} -/** - * Embosses the edges of the image. - * This filter takes no parameters but can be applied several times for - * further effect. - */ -function BumpFilter(){ - this.name = "Bump"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var matrix = [-1,-1, 0, - -1, 1, 1, - 0, 1, 1]; - filterUtils.convolveFilter(inputData,matrix,width,height); - }; -} -/** - * Smears out the image with circular shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. - */ -function CircleSmearFilter(){ - this.name = "Circle Smear"; - this.isDirAnimatable = false; - this.defaultValues = { - size : 4, - density : 0.5, - mix : 0.5 - }; - this.valueRanges = { - size : {min:1, max:10}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var j = 0; j < inputData.length; j++){ - outputData[j] = inputData[j]; - } - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - if(size < 1){ size = 1;} - size = parseInt(size,10); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var radius = size+1; - var radius2 = radius*radius; - var numShapes = parseInt(2*density/30*width*height / 2,10); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - for(var x = sx - radius; x < sx + radius + 1; x++){ - for(var y = sy - radius; y < sy + radius + 1; y++){ - var f = (x - sx) * (x - sx) + (y - sy) * (y - sy); - if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(var l = 0; l < outputData.length; l++){ - inputData[l] = outputData[l]; - } - }; -} -/** - * Adjusts the contrast of the image. - */ -function ContrastFilter(){ - this.name = "Contrast"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - if(amount < 0){ - amount = 0.0; - } - var table = []; - - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 * (((i/255)-0.5)*amount+0.5),10); - } - filterUtils.tableFilter(inputData,table,width,height); - }; -} -/** - * Smears out the image with cross shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - */ -function CrossSmearFilter(){ - this.name = "Cross Smear"; - this.isDirAnimatable = false; - this.defaultValues = { - distance : 8, - density : 0.5, - mix : 0.5 - }; - this.valueRanges = { - distance : {min:0, max:30}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var j = 0; j < inputData.length; j++){ - outputData[j] = inputData[j]; - } - if(values === undefined){ values = this.defaultValues; } - var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; - if(distance < 0){ distance = 0;} - distance = parseInt(distance,10); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var numShapes = parseInt(2*density*width * height / (distance + 1),10); - for(var i = 0; i < numShapes; i++){ - var x = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var y = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var length = (Math.random()*Math.pow(2,32)) % distance + 1; - var rgb2 = [inputData[(y*width+x)*4],inputData[(y*width+x)*4+1],inputData[(y*width+x)*4+2],inputData[(y*width+x)*4+3]]; - var rgb1; - var mixedRGB; - var k; - for (var x1 = x-length; x1 < x+length+1; x1++) { - if(x1 >= 0 && x1 < width){ - rgb1 = [outputData[(y*width+x1)*4],outputData[(y*width+x1)*4+1],outputData[(y*width+x1)*4+2],outputData[(y*width+x1)*4+3]]; - mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y*width+x1)*4+k] = mixedRGB[k]; - } - } - - } - for (var y1 = y-length; y1 < y+length+1; y1++) { - if(y1 >= 0 && y1 < height){ - rgb1 = [outputData[(y1*width+x)*4],outputData[(y1*width+x)*4+1],outputData[(y1*width+x)*4+2],outputData[(y1*width+x)*4+3]]; - mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y1*width+x)*4+k] = mixedRGB[k]; - } - } - - } - } - for(var l = 0; l < outputData.length; l++){ - inputData[l] = outputData[l]; - } - }; -} -/** - * Diffuses the image creating a frosted glass effect. - */ -function DiffusionFilter(){ - this.name = "Diffusion"; - this.isDirAnimatable = false; - this.defaultValues = { - scale: 4 - }; - this.valueRanges = { - scale: {min: 1, max: 100} - }; - - var filterUtils = new FilterUtils(); - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var scale = (values.scale === undefined) ? this.defaultValues.scale : values.scale; - var out = []; - var outputData = []; - var sinTable = []; - var cosTable = []; - for(var i = 0; i < 256; i++){ - var angle = Math.PI*2*i/256; - sinTable[i] = scale*Math.sin(angle); - cosTable[i] = scale*Math.cos(angle); - } - transInverse = function (x,y,out){ - var angle = parseInt(Math.random() * 255,10); - var distance = Math.random(); - out[0] = x + distance * sinTable[angle]; - out[1] = y + distance * cosTable[angle]; - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Dithers the image to the specified number of colors. Setting color to false - * grayscales the image. - */ -function DitherFilter(){ - this.name = "Dither"; - this.isDirAnimatable = false; - this.defaultValues = { - levels : 3, - color : true - }; - this.valueRanges = { - levels : {min:2, max:30}, - color : {min:false, max:true} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - var i, j; - for (j=0; j < inputData.length; j++) { - outputData[j] = 0; - } - if(values === undefined){ values = this.defaultValues; } - var levels = (values.levels === undefined) ? this.defaultValues.levels : values.levels; - var color = (values.color === undefined) ? this.defaultValues.color : values.color; - if(levels <= 1){ - levels = 1; - } - var matrix = [0,0,0, - 0,0,7, - 3,5,1]; - var sum = 7+3+5+1; - var index = 0; - var map = []; - - for (i=0; i < levels; i++) { - map[i] = parseInt(255* i / (levels-1),10); - } - var div = []; - for (i=0; i < 256; i++) { - div[i] = parseInt(levels*i / 256,10); - } - for (var y = 0; y < height; y++) { - var reverse = ((y & 1) == 1); - var direction; - if(reverse){ - index = (y*width+width-1)*4; - direction = -1; - } else { - index = y*width*4; - direction = 1; - } - for (var x = 0; x < width; x++) { - var r1 = inputData[index]; var g1 = inputData[index+1]; var b1 = inputData[index+2]; - if(!color){ - r1 = g1 = b1 = parseInt((r1+g1+b1) / 3,10); - } - var r2 = map[div[r1]];var g2 = map[div[g1]];var b2 = map[div[b1]]; - - outputData[index] = r2; outputData[index + 1] = g2; outputData[index+2] = b2; outputData[index+3] = inputData[index+3]; - - var er = r1-r2; var eg = g1-g2; var eb = b1-b2; - - for (i = -1; i <= 1; i++) { - var iy = i+y; - if (0 <= iy && iy < height) { - for (j = -1; j <= 1; j++) { - var jx = j+x; - if (0 <= jx && jx < width) { - var w; - if (reverse){ - w = matrix[(i+1)*3-j+1]; - } else{ - w = matrix[(i+1)*3+j+1]; - } - if (w !== 0) { - var k = (reverse) ? index - j*4 : index + j*4; - r1 = inputData[k]; g1 = inputData[k+1]; b1 = inputData[k+2]; - var factor = w/sum; - r1 += er * factor; g1 += eg * factor; b1 += eb * factor; - inputData[k] = r1; inputData[k+1] = g1 ;inputData[k+2] = b1; - } - } - } - } - } - index += direction*4; - } - } - for(j = 0; j < outputData.length; j++){ - inputData[j] = outputData[j]; - } - }; -} -/** - * Highlights the edges of the image. - */ -function EdgeFilter(){ - this.name = "Edge Detection"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - var matrixH = [-1,-2,-1, - 0, 0, 0, - 1, 2, 1]; - var matrixV = [-1, 0, 1, - -2, 0, 2, - -1, 0, 1]; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var rh = 0; gh = 0; bh = 0; - var rv = 0; gv = 0; bv = 0; - for(var row = -1; row <= 1; row++){ - var iy = y+row; - var ioffset; - if(iy >= 0 && iy < height){ - ioffset = iy*width*4; - } else { - ioffset = y*width*4; - } - var moffset = 3*(row+1)+1; - for(var col = -1; col <= 1; col++){ - var ix = x+col; - if(!(ix >= 0 && ix < width)){ - ix = x; - } - ix *= 4; - var r = inputData[ioffset+ix]; - var g = inputData[ioffset+ix+1]; - var b = inputData[ioffset+ix+2]; - var h = matrixH[moffset+col]; - var v = matrixV[moffset+col]; - rh += parseInt(h*r,10); - bh += parseInt(h*g,10); - gh += parseInt(h*b,10); - rv += parseInt(v*r,10); - gv += parseInt(v*g,10); - bv += parseInt(v*b,10); - } - } - r = parseInt(Math.sqrt(rh*rh + rv*rv) / 1.8,10); - g = parseInt(Math.sqrt(gh*gh + gv*gv) / 1.8,10); - b = parseInt(Math.sqrt(bh*bh + bv*bv) / 1.8,10); - - outputData[pixel] = r; - outputData[pixel+1] = g; - outputData[pixel+2] = b; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Embosses the image with a simulated light source. - * Angle and elevation sets the position of the light. - */ -function EmbossFilter(){ - this.name = "Emboss"; - this.isDirAnimatable = false; - this.defaultValues = { - height : 1, - angle : 135, - elevation : 30 - }; - this.valueRanges = { - height : {min:1, max:10}, - angle : {min:0, max:360}, - elevation : {min:0, max:180} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var bumpHeight = (values.height === undefined) ? this.defaultValues.height : values.height; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var elevation = (values.elevation === undefined) ? this.defaultValues.elevation : values.elevation; - angle = angle / 180 * Math.PI; - elevation = elevation / 180 * Math.PI; - var width45 = 3 * bumpHeight; - var pixelScale = 255.9; - - var bumpPixels = []; - var bumpMapWidth = width; - var bumpMapHeight = height; - for(var i = 0; i < inputData.length; i+=4){ - bumpPixels[i/4] = (inputData[i] + inputData[i+1] + inputData[i+2])/3; - } - var Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL; - var shade, background; - - Lx = parseInt(Math.cos(angle) * Math.cos(elevation) * pixelScale,10); - Ly = parseInt(Math.sin(angle) * Math.cos(elevation) * pixelScale,10); - Lz = parseInt(Math.sin(elevation) * pixelScale,10); - - Nz = parseInt(6 * 255 / width45,10); - Nz2 = Nz * Nz; - NzLz = Nz * Lz; - background = Lz; - - var bumpIndex = 0; - - for (var y = 0; y < height; y++, bumpIndex += bumpMapWidth) { - var s1 = bumpIndex; - var s2 = s1 + bumpMapWidth; - var s3 = s2 + bumpMapWidth; - for (var x = 0; x < width; x++, s1++, s2++, s3++) { - var pixel = (y*width + x)*4; - if (y !== 0 && y < height-2 && x !== 0 && x < width-2) { - Nx = bumpPixels[s1-1] + bumpPixels[s2-1] + bumpPixels[s3-1] - bumpPixels[s1+1] - bumpPixels[s2+1] - bumpPixels[s3+1]; - Ny = bumpPixels[s3-1] + bumpPixels[s3] + bumpPixels[s3+1] - bumpPixels[s1-1] - bumpPixels[s1] - bumpPixels[s1+1]; - if (Nx === 0 && Ny === 0){ - shade = background; - } else if ((NdotL = Nx*Lx + Ny*Ly + NzLz) < 0){ - shade = 0; - } else { - shade = parseInt(NdotL / Math.sqrt(Nx*Nx + Ny*Ny + Nz2),10); - } - } else { - shade = background; - } - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = shade; - } - } - }; -} -/** - * Adjust simulated exposure values on the image. - */ -function ExposureFilter(){ - this.name = "Exposure"; - this.isDirAnimatable = true; - this.defaultValues = { - exposure : 1.0 - }; - this.valueRanges = { - exposure : {min:0, max:5} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var exposure = (values.exposure === undefined) ? this.defaultValues.exposure : values.exposure; - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 *(1-Math.exp(-(i/255) * exposure)),10); - } - filterUtils.tableFilter(inputData, table, width, height); - }; -} -/** - * Adjusts the gain and bias of the image. Gain alters the contrast while bias biases - * colors towards lighter or darker. - */ -function GainFilter(){ - this.name = "Gain/Bias"; - this.isDirAnimatable = true; - this.defaultValues = { - gain: 0.5, - bias: 0.5 - }; - this.valueRanges = { - gain: {min:0.0, max:1.0}, - bias: {min:0.0, max:1.0} - }; - var table = []; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var gain = (values.gain === undefined) ? this.defaultValues.gain : values.gain; - var bias = (values.bias === undefined) ? this.defaultValues.bias : values.bias; - - var table = []; - - for(var i = 0; i < 256; i++){ - var val = i/255; - var k = (1/gain-2) * (1-2*val); - val = (val < 0.5) ? val/(k+1) : (k-val)/(k-1); - val /= (1/bias-2)*(1-val)+1; - table[i] = parseInt(255 * val,10); - } - filterUtils.tableFilter(inputData,table,width,height); - }; -} -/** - * Adjusts the gamma values of the image. Values over 1 increase the gamma while values over 0 decrease gamma. - */ -function GammaFilter(){ - this.name = "Gamma"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - if(amount < 0){ - amount = 0.0; - } - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = 255 * Math.pow(i/255, 1/amount) + 0.5; - } - filterUtils.tableFilter(inputData,table,width,height); - }; -} -/** - * Sets the image to grayscale. - */ -function GrayscaleFilter(){ - this.name = "Grayscale"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = luma; - } - } - }; -} -/** - * Adjusts the hue of the image by going over to HSV values. - */ -function HueFilter(){ - this.name = "Hue"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 0.0 - }; - this.valueRanges = { - amount : {min:-1.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); - hsv[0] += amount; - while(hsv[0] < 0){ - hsv[0] += 360; - } - var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = rgb[i]; - } - } - } - }; -} -/** - * Inverts the colors of the image. - */ -function InvertFilter(){ - this.name = "Invert"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = 255 - inputData[pixel+i]; - } - } - } - }; -} -/** - * Creates a kaleidoscope effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function KaleidoscopeFilter(){ - this.name = "Kaleidoscope"; - this.isDirAnimatable = false; - this.defaultValues = { - angle : 0, - rotation : 0, - sides : 3, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - angle : {min: 0, max: 360}, - rotation : {min: 0, max: 360}, - sides : {min: 1, max: 30}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var rotation = (values.rotation === undefined) ? this.defaultValues.rotation : values.rotation; - var sides = (values.sides === undefined) ? this.defaultValues.sides : values.sides; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var iCenterX = width * centerX; var iCenterY = height * centerY; - angle = angle/180 * Math.PI; - rotation = rotation/180 * Math.PI; - var transInverse = function(x,y,out){ - var dx = x - iCenterX; - var dy = y - iCenterY; - var r = Math.sqrt(dx*dx + dy*dy); - var theta = Math.atan2(dy,dx) - angle - rotation; - theta = filterUtils.triangle(theta/Math.PI*sides*0.5); - theta += angle; - out[0] = iCenterX + r*Math.cos(theta); - out[1] = iCenterY + r*Math.sin(theta); - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Applies a fisheye lens distortion effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function LensDistortionFilter(){ - this.name = "Lens Distortion"; - this.isDirAnimatable = false; - this.defaultValues = { - refraction : 1.5, - radius : 50, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - refraction : {min: 1, max: 10}, - radius : {min: 1, max: 200}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var refraction = (values.refraction === undefined) ? this.defaultValues.refraction : values.refraction; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var x2 = dx*dx; - var y2 = dy*dy; - if (y2 >= (radius2 - (radius2*x2)/radius2)) { - out[0] = x; - out[1] = y; - } else { - var rRefraction = 1.0 / refraction; - - var z = Math.sqrt((1.0 - x2/radius2 - y2/radius2) * radius2); - var z2 = z*z; - - var xAngle = Math.acos(dx / Math.sqrt(x2+z2)); - var angle1 = Math.PI/2 - xAngle; - var angle2 = Math.asin(Math.sin(angle1)*rRefraction); - angle2 = Math.PI/2 - xAngle - angle2; - out[0] = x - Math.tan(angle2)*z; - - var yAngle = Math.acos(dy / Math.sqrt(y2+z2)); - angle1 = Math.PI/2 - yAngle; - angle2 = Math.asin(Math.sin(angle1)*rRefraction); - angle2 = Math.PI/2 - yAngle - angle2; - out[1] = y - Math.tan(angle2)*z; - } - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Smears out the image with line shapes to create a painting style effect. Mix specifies - * the intensity of the effect. - */ -function LineSmearFilter(){ - this.name = "Line Smear"; - this.isDirAnimatable = false; - this.defaultValues = { - distance : 8, - density : 0.5, - angle : 0, - mix : 0.5 - }; - this.valueRanges = { - distance : {min:1, max:30}, - density : {min:0.0, max:1.0}, - angle : {min:0, max:360}, - mix : {min:0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - var k; - for(k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; - if(distance < 1){ distance = 1;} - distance = parseInt(distance,10); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - angle = angle/180*Math.PI; - var sinAngle = Math.sin(angle); - var cosAngle = Math.cos(angle); - var numShapes = parseInt(2*density*width*height / 2,10); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var length = (Math.random()*Math.pow(2,32) & 0x7fffffff) % distance + 1; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - var dx = parseInt(length*cosAngle,10); - var dy = parseInt(length*sinAngle,10); - - var x0 = sx-dx; - var y0 = sy-dy; - var x1 = sx+dx; - var y1 = sy+dy; - var x, y, d, incrE, incrNE, ddx, ddy; - - if (x1 < x0){ - ddx = -1; - } else { - ddx = 1; - } - if (y1 < y0){ - ddy = -1; - } else { - ddy = 1; - } - dx = x1-x0; - dy = y1-y0; - dx = Math.abs(dx); - dy = Math.abs(dy); - x = x0; - y = y0; - var rgb1; - var mixedRGB; - if (x < width && x >= 0 && y < height && y >= 0) { - rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - if (Math.abs(dx) > Math.abs(dy)) { - d = 2*dy-dx; - incrE = 2*dy; - incrNE = 2*(dy-dx); - - while (x != x1) { - if (d <= 0){ - d += incrE; - } else { - d += incrNE; - y += ddy; - } - x += ddx; - if (x < width && x >= 0 && y < height && y >= 0) { - rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } else { - d = 2*dx-dy; - incrE = 2*dx; - incrNE = 2*(dx-dy); - - while (y != y1) { - if (d <= 0) { - d += incrE; - }else { - d += incrNE; - x += ddx; - } - y += ddy; - if (x < width && x >= 0 && y < height && y >= 0) { - rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Replaces every pixel with the maximum RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MaximumFilter(){ - this.name = "Maximum"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var maxR = 0; - var maxG = 0; - var maxB = 0; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - maxR = Math.max(maxR,inputData[iPixel]); - maxG = Math.max(maxG,inputData[iPixel+1]); - maxB = Math.max(maxB,inputData[iPixel+2]); - } - } - } - } - outputData[pixel] = maxR; - outputData[pixel+1] = maxG; - outputData[pixel+2] = maxB; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Replaces every pixel with the median RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MedianFilter(){ - this.name = "Median"; - this.isDirAnimatable = false; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var rList = []; - var gList = []; - var bList = []; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - rList.push(inputData[iPixel]); - gList.push(inputData[iPixel+1]); - bList.push(inputData[iPixel+2]); - - } - } - } - } - var sortFunc = function(a,b){ - return a-b; - }; - rList.sort(sortFunc); - gList.sort(sortFunc); - bList.sort(sortFunc); - outputData[pixel] = rList[4]; - outputData[pixel+1] = gList[4]; - outputData[pixel+2] = bList[4]; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Replaces every pixel with the minimum RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MinimumFilter(){ - this.name = "Minimum"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var minR = 255; - var minG = 255; - var minB = 255; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - minR = Math.min(minR,inputData[iPixel]); - minG = Math.min(minG,inputData[iPixel+1]); - minB = Math.min(minB,inputData[iPixel+2]); - } - } - } - } - outputData[pixel] = minR; - outputData[pixel+1] = minG; - outputData[pixel+2] = minB; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Creates random noise on the image, with or without color. - */ -function NoiseFilter(){ - this.name = "Noise"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 25, - density : 1, - monochrome : true - }; - this.valueRanges = { - amount : {min:0, max:100}, - density : {min:0, max:1.0}, - monochrome : {min:false, max:true} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var monochrome = (values.monochrome === undefined) ? this.defaultValues.monochrome : values.monochrome; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - if(Math.random() <= density){ - var n; - if(monochrome){ - n = parseInt((2*Math.random()-1) * amount,10); - inputData[pixel] += n; - inputData[pixel+1] += n; - inputData[pixel+2] += n; - } else { - for(var i = 0; i < 3; i++){ - n = parseInt((2*Math.random()-1) * amount,10); - inputData[pixel+i] += n; - } - } - } - } - } - }; -} -/** - * Produces an oil painting effect on the image. - * NOTE: This filter can be very slow, especially at higher ranges. Use with caution. - */ -function OilFilter(){ - this.name = "Oil Painting"; - this.isDirAnimatable = false; - this.defaultValues = { - range : 3 - }; - this.valueRanges = { - range : {min:0, max:5} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - if(values === undefined){ values = this.defaultValues; } - var range = (values.range === undefined) ? this.defaultValues.range : values.range; - range = parseInt(range,10); - var index = 0; - var rHistogram = []; - var gHistogram = []; - var bHistogram = []; - var rTotal = []; - var gTotal = []; - var bTotal = []; - var levels = 256; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for (var j = 0; j < levels; j++){ - rHistogram[j] = gHistogram[j] = bHistogram[j] = rTotal[j] = gTotal[j] = bTotal[j] = 0; - } - for (var row = -range; row <= range; row++) { - var iy = y+row; - var ioffset; - if (0 <= iy && iy < height) { - ioffset = iy*width; - for (var col = -range; col <= range; col++) { - var ix = x+col; - if (0 <= ix && ix < width) { - var ro = inputData[(ioffset+ix)*4]; - var go = inputData[(ioffset+ix)*4+1]; - var bo = inputData[(ioffset+ix)*4+2]; - var ri = ro*levels/256; - var gi = go*levels/256; - var bi = bo*levels/256; - rTotal[ri] += ro; - gTotal[gi] += go; - bTotal[bi] += bo; - rHistogram[ri]++; - gHistogram[gi]++; - bHistogram[bi]++; - } - } - } - } - var r = 0, g = 0, b = 0; - for (var i = 1; i < levels; i++) { - if (rHistogram[i] > rHistogram[r]){ - r = i; - } - if (gHistogram[i] > gHistogram[g]){ - g = i; - } - if (bHistogram[i] > bHistogram[b]){ - b = i; - } - } - r = rTotal[r] / rHistogram[r]; - g = gTotal[g] / gHistogram[g]; - b = bTotal[b] / bHistogram[b]; - outputData[pixel] = r; - outputData[pixel+1] = g; - outputData[pixel+2] = b; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Changes the opacity of the image. - */ -function OpacityFilter(){ - this.name = "Opacity"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:1.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - inputData[pixel+3] = 255*amount; - } - } - }; -} -/** - * Pinches and whirls the image toward the center point. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function PinchFilter(){ - this.name = "Pinch/Whirl"; - this.isDirAnimatable = false; - this.defaultValues = { - amount : 0.5, - radius : 100, - angle : 0, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - amount : {min: -1.0, max: 1.0}, - radius : {min: 1, max: 200}, - angle : {min: 0, max: 360}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - angle = angle/180 * Math.PI; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - if(distance > radius2 || distance === 0){ - out[0] = x; - out[1] = y; - } else { - var d = Math.sqrt( distance / radius2 ); - var t = Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); - dx *= t; - dy *= t; - var e = 1 - d; - var a = angle * e * e; - var s = Math.sin(a); - var c = Math.cos(a); - out[0] = iCenterX + c*dx - s*dy; - out[1] = iCenterY + s*dx + c*dy; - } - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Pixelates the image i.e. divides the image into blocks of color. - */ -function PixelationFilter(){ - this.name = "Pixelation"; - this.isDirAnimatable = false; - this.defaultValues = { - size : 5 - }; - this.valueRanges = { - size : {min:1, max:50} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - size = parseInt(size,10); - var pixels = []; - var by, bx, bPixel; - for (var y = 0; y < height; y+=size) { - for (var x = 0; x < width; x+=size) { - var pixel = (y*width + x)*4; - var w = Math.min(size, width-x); - var h = Math.min(size, height-y); - var t = w*h; - var r = 0, g = 0, b = 0; - for(by = y; by < y+h; by++){ - for(bx = x; bx < x+w; bx++){ - bPixel = (by*width + bx)*4; - r += inputData[bPixel]; - g += inputData[bPixel+1]; - b += inputData[bPixel+2]; - } - } - for(by = y; by < y+h; by++){ - for(bx = x; bx < x+w; bx++){ - bPixel = (by*width + bx)*4; - inputData[bPixel] = r/t; - inputData[bPixel+1] = g/t; - inputData[bPixel+2] = b/t; - } - } - } - } - }; -} -/** - * Posterizes the image, i.e. restricts the color values to a set amount of levels. - */ -function PosterizeFilter(){ - this.name = "Posterize"; - this.isDirAnimatable = false; - this.defaultValues = { - levels : 6 - }; - this.valueRanges = { - levels : {min:2, max:30 } - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var levels = (values.levels === undefined) ? this.defaultValues.levels : parseInt(values.levels,10); - if(levels <= 1){ - return; - } - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 * parseInt(i*levels/256,10) / (levels-1),10); - } - filterUtils.tableFilter(inputData,table,width,height); - }; -} -/** - * Adjust the factor of each RGB color value in the image. - */ -function RGBAdjustFilter(){ - this.name = "RGBAdjust"; - this.isDirAnimatable = true; - this.defaultValues = { - red: 1.0, - green: 1.0, - blue: 1.0 - }; - this.valueRanges = { - red: {min: 0.0, max: 2.0}, - green: {min: 0.0, max: 2.0}, - blue: {min: 0.0, max: 2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var red = (values.red === undefined) ? this.defaultValues.red : values.red; - var green = (values.green === undefined) ? this.defaultValues.green : values.green; - var blue = (values.blue === undefined) ? this.defaultValues.blue : values.blue; - if(red < 0){ red = 0; } - if(green < 0){ green = 0; } - if(blue < 0){ blue = 0; } - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - inputData[pixel] *= red; - inputData[pixel+1] *= green; - inputData[pixel+2] *= blue; - } - } - }; -} -/** - * Adjusts the saturation value of the image. Values over 1 increase saturation while values below decrease saturation. - * For a true grayscale effect, use the grayscale filter instead. - */ -function SaturationFilter(){ - this.name = "Saturation"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var RW = 0.3; - var RG = 0.59; - var RB = 0.11; - var a = (1 - amount) * RW + amount; - var b = (1 - amount) * RW; - var c = (1 - amount) * RW; - var d = (1 - amount) * RG; - var e = (1 - amount) * RG + amount; - var f = (1 - amount) * RG; - var g = (1 - amount) * RB; - var h = (1 - amount) * RB; - var i = (1 - amount) * RB + amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var pR = inputData[pixel]; - var pG = inputData[pixel+1]; - var pB = inputData[pixel+2]; - inputData[pixel] = a*pR + d*pG + g*pB; - inputData[pixel+1] = b*pR + e*pG + h*pB; - inputData[pixel+2] = c*pR + f*pG + i*pB; - } - } - }; -} -/** - * Creates ripples on the image horizontally/vertically in a sawtooth pattern. - */ -function SawtoothRippleFilter(){ - this.name = "Sawtooth Ripples"; - this.isDirAnimatable = false; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = filterUtils.mod(nx,1); - var fy = filterUtils.mod(ny,1); - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Creates a sepia effect on the image i.e. gives the image a yellow-brownish tone. - */ -function SepiaFilter(){ - this.name = "Sepia"; - this.isDirAnimatable = true; - this.defaultValues = { - amount : 10 - }; - this.valueRanges = { - amount : {min:0, max:30} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - amount *= 255/100; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; - var r,g,b; - r = g = b = luma; - r += 40; - g += 20; - b -= amount; - - inputData[pixel] = r; - inputData[pixel+1] = g; - inputData[pixel+2] = b; - } - } - }; -} -/** - * Sharpens the image slightly. For increased effect, apply the filter multiple times. - */ -function SharpenFilter(){ - this.name = "Sharpen"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var matrix = [ 0.0,-0.2, 0.0, - -0.2, 1.8,-0.2, - 0.0, -0.2, 0.0]; - filterUtils.convolveFilter(inputData,matrix,width,height); - }; -} -/** - * Creates ripples on the image horizontally/vertically in a sine pattern. - */ -function SineRippleFilter(){ - this.name = "Sine Ripples"; - this.isDirAnimatable = false; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = Math.sin(nx); - var fy = Math.sin(ny); - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Produces a solarization effect on the image. - */ -function SolarizeFilter(){ - this.name = "Solarize"; - this.isDirAnimatable = true; - this.defaultValues = { - }; - this.valueRanges = { - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var table = []; - for(var i = 0; i < 256; i++){ - var val = (i/255 > 0.5) ? 2*(i/255-0.5) : 2*(0.5-i/255); - table[i] = parseInt(255 * val,10); - } - filterUtils.tableFilter(inputData, table, width, height); - }; -} -/** - * Generates a sparkle/sunburst effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function SparkleFilter(){ - this.name = "Sparkle"; - this.isDirAnimatable = false; - this.defaultValues = { - rays : 50, - size : 25, - amount : 50, - randomness : 25, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - rays : {min:1, max:100}, - size : {min:1, max:200}, - amount : {min:0, max:100}, - randomness : {min:0, max:50}, - centerX : {min:0, max:1.0}, - centerY : {min:0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var rays = (values.rays === undefined) ? this.defaultValues.rays : values.rays; - rays = parseInt(rays, 10); - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var randomness = (values.randomness === undefined) ? this.defaultValues.randomness : values.randomness; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var iCenterX = centerX * width; - var iCenterY = centerY * height; - var rayLengths = []; - for(var j = 0; j < rays; j++){ - rayLengths[j]= size + randomness / 100 * size * filterUtils.gaussianRandom(); - } - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - var angle = Math.atan2(dy,dx); - var d = (angle+Math.PI) / (Math.PI*2) * rays; - var i = parseInt(d,10); - var f = d - i; - if(size !== 0){ - var length = filterUtils.linearInterpolate(f, rayLengths[i % rays], rayLengths[(i+1) % rays]); - var g = length*length / (distance+0.0001); - g = Math.pow(g, (100-amount) / 50); - f -= 0.5; - f = 1 - f*f; - f *= g; - } - f = filterUtils.clampPixel(f,0,1); - var mixedRGB = filterUtils.mixColors(f,[inputData[pixel],inputData[pixel+1],inputData[pixel+2],inputData[pixel+3]],[255,255,255,255]); - for(var k = 0; k < 3; k++){ - inputData[pixel+k] = mixedRGB[k]; - } - } - } - }; -} -/** - * Smears out the image with square shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. - */ -function SquareSmearFilter(){ - this.name = "Square Smear"; - this.isDirAnimatable = false; - this.defaultValues = { - size : 4, - density : 0.5, - mix : 0.5 - }; - this.valueRanges = { - size : {min:1, max:10}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - var k; - for(k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - if(size < 1){ size = 1;} - size = parseInt(size,10); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var radius = size+1; - var radius2 = radius*radius; - var numShapes = parseInt(2*density/30*width*height / 2,10); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - for(var x = sx - radius; x < sx + radius + 1; x++){ - - for(var y = sy - radius; y < sy + radius + 1; y++){ - if (x >= 0 && x < width && y >= 0 && y < height) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2); - for(k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Divides the colors into black and white following the treshold value. Brightnesses above the threshold - * sets the color to white while values below the threshold sets the color to black. - */ -function ThresholdFilter(){ - this.name = "Black & White"; - this.isDirAnimatable = true; - this.defaultValues = { - threshold : 127 - }; - this.valueRanges = { - threshold : {min:0, max:255} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var threshold = (values.threshold === undefined) ? this.defaultValues.threshold : values.threshold; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var brightness = (inputData[pixel] + inputData[pixel+1] + inputData[pixel+2])/3; - var colorVal = 0; - if(brightness > threshold){ - colorVal = 255; - } - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = colorVal; - } - } - }; -} -/** - * Creates ripples on the image horizontally/vertically in a sine pattern. - */ -function TriangleRippleFilter(){ - this.name = "Triangle Ripples"; - this.isDirAnimatable = false; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = filterUtils.triangle(nx,1); - var fy = filterUtils.triangle(ny,1); - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Twists the image around a given center point. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function TwirlFilter(){ - this.name = "Twirl"; - this.isDirAnimatable = false; - this.defaultValues = { - radius : 100, - angle : 180, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - radius : {min: 1, max: 200}, - angle : {min: 0, max: 360}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - angle = angle/180 * Math.PI; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - if(distance > radius2){ - out[0] = x; - out[1] = y; - } else { - distance = Math.sqrt(distance); - var a = Math.atan2(dy, dx) + angle * (radius-distance) / radius; - out[0] = iCenterX + distance*Math.cos(a); - out[1] = iCenterY + distance*Math.sin(a); - } - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * Creates a classical vignette effect on the image i.e. darkens the corners. - */ -function VignetteFilter(){ - this.name = "Vignette"; - this.isDirAnimatable = false; - this.defaultValues = { - amount : 0.3 - }; - this.valueRanges = { - amount : {min:0.0, max:1.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var canvas = document.createElement("canvas"); - canvas.width = width; - canvas.height = height; - var context = canvas.getContext("2d"); - var gradient; - var radius = Math.sqrt( Math.pow(width/2, 2) + Math.pow(height/2, 2) ); - context.putImageData(input,0,0); - context.globalCompositeOperation = 'source-over'; - - gradient = context.createRadialGradient(width/2, height/2, 0, width/2, height/2, radius); - gradient.addColorStop(0, 'rgba(0,0,0,0)'); - gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); - gradient.addColorStop(1, 'rgba(0,0,0,' + amount + ')'); - context.fillStyle = gradient; - context.fillRect(0, 0, width, height); - outputData = context.getImageData(0,0,width,height).data; - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - }; -} -/** - * Produces a water ripple/waves on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function WaterRippleFilter(){ - this.name = "Water Ripples"; - this.isDirAnimatable = false; - this.defaultValues = { - phase : 0, - radius : 50, - wavelength : 16, - amplitude : 10, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - phase : {min: 0, max: 100}, - radius : {min: 1, max: 200}, - wavelength : {min: 1, max: 100}, - amplitude : {min: 1, max: 100}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var wavelength = (values.wavelength === undefined) ? this.defaultValues.wavelength : values.wavelength; - var amplitude = (values.amplitude === undefined) ? this.defaultValues.amplitude : values.amplitude; - var phase = (values.phase === undefined) ? this.defaultValues.phase : values.phase; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance2 = dx*dx + dy*dy; - if(distance2 > radius2){ - out[0] = x; - out[1] = y; - } else { - var distance = Math.sqrt(distance2); - var amount = amplitude * Math.sin(distance/wavelength * Math.PI * 2 - phase); - amount *= (radius-distance)/radius; - if(distance !== 0){ - amount *= wavelength/distance; - } - out[0] = x + dx*amount; - out[1] = y + dy*amount; - } - }; - filterUtils.transformFilter(inputData,transInverse,width,height); - }; -} -/** - * A collection of all the filters. - */ -var JSManipulate = { - blur : new BlurFilter(), - brightness : new BrightnessFilter(), - bump : new BumpFilter(), - circlesmear : new CircleSmearFilter(), - contrast : new ContrastFilter(), - crosssmear : new CrossSmearFilter(), - diffusion : new DiffusionFilter(), - dither : new DitherFilter(), - edge : new EdgeFilter(), - emboss : new EmbossFilter(), - exposure : new ExposureFilter(), - gain : new GainFilter(), - gamma : new GammaFilter(), - grayscale : new GrayscaleFilter(), - hue : new HueFilter(), - invert : new InvertFilter(), - kaleidoscope : new KaleidoscopeFilter(), - lensdistortion : new LensDistortionFilter(), - linesmear : new LineSmearFilter(), - maximum : new MaximumFilter(), - median : new MedianFilter(), - minimum : new MinimumFilter(), - noise : new NoiseFilter(), - oil : new OilFilter(), - opacity : new OpacityFilter(), - pinch : new PinchFilter(), - pixelate : new PixelationFilter(), - posterize : new PosterizeFilter(), - rgbadjust : new RGBAdjustFilter(), - saturation : new SaturationFilter(), - sawtoothripple : new SawtoothRippleFilter(), - sepia : new SepiaFilter(), - sharpen : new SharpenFilter(), - sineripple : new SineRippleFilter(), - solarize : new SolarizeFilter(), - sparkle : new SparkleFilter(), - squaresmear : new SquareSmearFilter(), - threshold : new ThresholdFilter(), - triangleripple : new TriangleRippleFilter(), - twirl : new TwirlFilter(), - vignette : new VignetteFilter(), - waterripple : new WaterRippleFilter() -}; - -// node.js -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){ - module.exports = JSManipulate; -} - diff --git a/script/minified/jsmanipulate.min.js b/script/minified/jsmanipulate.min.js deleted file mode 100644 index b67424a..0000000 --- a/script/minified/jsmanipulate.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -========================================================================= - JSManipulate v1.0 (2011-08-01) - -Javascript image filter & effect library - -Developed by Joel Besada (http://www.joelb.me) -Demo page: http://www.joelb.me/jsmanipulate - -MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) -Copyright (c) 2011, Joel Besada -========================================================================= -*/ -function FilterUtils(){this.HSVtoRGB=function(t,a,e){var i,r,n,s=Math.floor(6*t),l=6*t-s,h=e*(1-a),o=e*(1-l*a),u=e*(1-(1-l)*a);switch(s%6){case 0:i=e,r=u,n=h;break;case 1:i=o,r=e,n=h;break;case 2:i=h,r=e,n=u;break;case 3:i=h,r=o,n=e;break;case 4:i=u,r=h,n=e;break;case 5:i=e,r=h,n=o}return[255*i,255*r,255*n]},this.RGBtoHSV=function(t,a,e){t/=255,a/=255,e/=255;var i,r,n=Math.max(t,a,e),s=Math.min(t,a,e),l=n,h=n-s;if(r=0===n?0:h/n,n===s)i=0;else{switch(n){case t:i=(a-e)/h+(e>a?6:0);break;case a:i=(e-t)/h+2;break;case e:i=(t-a)/h+4}i/=6}return[i,r,l]},this.getPixel=function(t,a,e,i,r){var n=4*(e*i+a);return 0>a||a>=i||0>e||e>=r?[t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+1],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+2],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+3]]:[t[n],t[n+1],t[n+2],t[n+3]]};var t,a=!1;this.gaussianRandom=function(){if(a)return a=!1,t;var e,i,r;do e=2*Math.random()-1,i=2*Math.random()-1,r=e*e+i*i;while(r>=1||0===r);var n=Math.sqrt(-2*Math.log(r)/r);return t=i*n,a=!0,e*n},this.clampPixel=function(t,a,e){return a>t?a:t>e?e:t},this.triangle=function(t){var a=this.mod(t,1);return 2*(.5>a?a:1-a)},this.mod=function(t,a){var e=parseInt(t/a,10);return t-=e*a,0>t?t+a:t},this.mixColors=function(t,a,e){var i=this.linearInterpolate(t,a[0],e[0]),r=this.linearInterpolate(t,a[1],e[1]),n=this.linearInterpolate(t,a[2],e[2]),s=this.linearInterpolate(t,a[3],e[3]);return[i,r,n,s]},this.linearInterpolate=function(t,a,e){return a+t*(e-a)},this.bilinearInterpolate=function(t,a,e,i,r,n){var s,l,h=e[0],o=e[1],u=e[2],d=e[3],f=i[0],v=i[1],m=i[2],c=i[3],g=r[0],p=r[1],x=r[2],V=r[3],w=n[0],F=n[1],M=n[2],b=n[3],I=1-t,A=1-a;s=I*d+t*c,l=I*V+t*b;var y=A*s+a*l;s=I*h+t*f,l=I*g+t*w;var R=A*s+a*l;s=I*o+t*v,l=I*p+t*F;var D=A*s+a*l;s=I*u+t*m,l=I*x+t*M;var S=A*s+a*l;return[R,D,S,y]},this.tableFilter=function(t,a,e,i){for(var r=0;i>r;r++)for(var n=0;e>n;n++)for(var s=4*(r*e+n),l=0;3>l;l++)t[s+l]=a[t[s+l]]},this.convolveFilter=function(t,a,e,i){var r,n,s=[];r=n=Math.sqrt(a.length);for(var l=parseInt(r/2,10),h=parseInt(n/2,10),o=0;i>o;o++)for(var u=0;e>u;u++){for(var d=4*(o*e+u),f=0,v=0,m=0,c=-l;l>=c;c++){var g,p=o+c;g=p>=0&&i>p?p*e:o*e;for(var x=n*(c+l)+h,V=-h;h>=V;V++){var w=a[x+V];if(0!==w){var F=u+V;F>=0&&e>F||(F=u);var M=4*(g+F);f+=w*t[M],v+=w*t[M+1],m+=w*t[M+2]}}}s[d]=parseInt(f+.5,10),s[d+1]=parseInt(v+.5,10),s[d+2]=parseInt(m+.5,10),s[d+3]=t[d+3]}for(var b=0;bl;l++)for(var h=0;e>h;h++){var o=4*(l*e+h);a.apply(this,[h,l,r]);var u,d,f,v,m=Math.floor(r[0]),c=Math.floor(r[1]),g=r[0]-m,p=r[1]-c;if(m>=0&&e-1>m&&c>=0&&i-1>c){var x=4*(e*c+m);u=[t[x],t[x+1],t[x+2],t[x+3]],d=[t[x+4],t[x+5],t[x+6],t[x+7]],f=[t[x+4*e],t[x+4*e+1],t[x+4*e+2],t[x+4*e+3]],v=[t[x+4*(e+1)],t[x+4*(e+1)+1],t[x+4*(e+1)+2],t[x+4*(e+1)+3]]}else u=this.getPixel(t,m,c,e,i),d=this.getPixel(t,m+1,c,e,i),f=this.getPixel(t,m,c+1,e,i),v=this.getPixel(t,m+1,c+1,e,i);var V=this.bilinearInterpolate(g,p,u,d,f,v);n[o]=V[0],n[o+1]=V[1],n[o+2]=V[2],n[o+3]=V[3]}for(var w=0;wl&&(l=0),e=l>=2.5?.98711*l-.9633:l>=.5?3.97156-4.14554*Math.sqrt(1-.26891*l):2*l*(3.97156-4.14554*Math.sqrt(.865545));var h,o,u,d,f,v,m=e*e,c=m*e,g=1.57825+2.44413*e+1.4281*m+.422205*c,p=(2.44413*e+2.85619*m+1.26661*c)/g,x=-(1.4281*m+1.26661*c)/g,V=.422205*c/g,w=1-(p+x+V),F=0;for(F=0;3>F;F++)for(var M=0;n>M;M++){for(h=M*r+F,o=M*r+(i-1<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=M*r+(i-1<<2)+F,o=M*r+F,u=s[h],d=u,f=d,v=f;h>=o;h-=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}for(F=0;3>F;F++)for(var b=0;i>b;b++){for(h=(b<<2)+F,o=(n-1)*r+(b<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=(n-1)*r+(b<<2)+F,o=(b<<2)+F,u=s[h],d=u,f=d,v=f;h>=o;h-=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}}}function BrightnessFilter(){this.name="Brightness",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);u[2]+=s,u[2]<0?u[2]=0:u[2]>1&&(u[2]=1);for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function BumpFilter(){this.name="Bump",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[-1,-1,0,-1,1,1,0,1,1];t.convolveFilter(r,n,e,i)}}function CircleSmearFilter(){this.name="Circle Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=d*d,v=parseInt(2*o/30*i*r/2,10),m=0;v>m;m++)for(var c=(Math.random()*Math.pow(2,32)&2147483647)%i,g=(Math.random()*Math.pow(2,32)&2147483647)%r,p=[n[4*(g*i+c)],n[4*(g*i+c)+1],n[4*(g*i+c)+2],n[4*(g*i+c)+3]],x=c-d;c+d+1>x;x++)for(var V=g-d;g+d+1>V;V++){var w=(x-c)*(x-c)+(V-g)*(V-g);if(x>=0&&i>x&&V>=0&&r>V&&f>=w)for(var F=[s[4*(V*i+x)],s[4*(V*i+x)+1],s[4*(V*i+x)+2],s[4*(V*i+x)+3]],M=t.mixColors(u,F,p),b=0;3>b;b++)s[4*(V*i+x)+b]=M[b]}for(var I=0;Is&&(s=0);for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*((h/255-.5)*s+.5),10);t.tableFilter(n,l,i,r)}}function CrossSmearFilter(){this.name="Cross Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,mix:.5},this.valueRanges={distance:{min:0,max:30},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=0),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=parseInt(2*o*i*r/(h+1),10),f=0;d>f;f++){for(var v,m,c,g=(Math.random()*Math.pow(2,32)&2147483647)%i,p=(Math.random()*Math.pow(2,32)&2147483647)%r,x=Math.random()*Math.pow(2,32)%h+1,V=[n[4*(p*i+g)],n[4*(p*i+g)+1],n[4*(p*i+g)+2],n[4*(p*i+g)+3]],w=g-x;g+x+1>w;w++)if(w>=0&&i>w)for(v=[s[4*(p*i+w)],s[4*(p*i+w)+1],s[4*(p*i+w)+2],s[4*(p*i+w)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(p*i+w)+c]=m[c];for(var F=p-x;p+x+1>F;F++)if(F>=0&&r>F)for(v=[s[4*(F*i+g)],s[4*(F*i+g)+1],s[4*(F*i+g)+2],s[4*(F*i+g)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(F*i+g)+c]=m[c]}for(var M=0;Mo;o++){var u=2*Math.PI*o/256;l[o]=s*Math.sin(u),h[o]=s*Math.cos(u)}transInverse=function(t,a,e){var i=parseInt(255*Math.random(),10),r=Math.random();e[0]=t+r*l[i],e[1]=a+r*h[i]},t.transformFilter(n,transInverse,i,r)}}function DitherFilter(){this.name="Dither",this.isDirAnimatable=!1,this.defaultValues={levels:3,color:!0},this.valueRanges={levels:{min:2,max:30},color:{min:!1,max:!0}};new FilterUtils;this.filter=function(t,a){var e,i,r=t.width,n=t.height,s=t.data,l=[];for(i=0;i=h&&(h=1);var u=[0,0,0,0,0,7,3,5,1],d=16,f=0,v=[];for(e=0;h>e;e++)v[e]=parseInt(255*e/(h-1),10);var m=[];for(e=0;256>e;e++)m[e]=parseInt(h*e/256,10);for(var c=0;n>c;c++){var g,p=1==(1&c);p?(f=4*(c*r+r-1),g=-1):(f=c*r*4,g=1);for(var x=0;r>x;x++){var V=s[f],w=s[f+1],F=s[f+2];o||(V=w=F=parseInt((V+w+F)/3,10));var M=v[m[V]],b=v[m[w]],I=v[m[F]];l[f]=M,l[f+1]=b,l[f+2]=I,l[f+3]=s[f+3];var A=V-M,y=w-b,R=F-I;for(e=-1;1>=e;e++){var D=e+c;if(D>=0&&n>D)for(i=-1;1>=i;i++){var S=i+x;if(S>=0&&r>S){var P;if(P=p?u[3*(e+1)-i+1]:u[3*(e+1)+i+1],0!==P){var W=p?f-4*i:f+4*i;V=s[W],w=s[W+1],F=s[W+2];var U=P/d;V+=A*U,w+=y*U,F+=R*U,s[W]=V,s[W+1]=w,s[W+2]=F}}}}f+=4*g}}for(i=0;il;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=0;gh=0,bh=0;var d=0;gv=0,bv=0;for(var f=-1;1>=f;f++){var v,m=l+f;v=m>=0&&r>m?m*i*4:l*i*4;for(var c=3*(f+1)+1,g=-1;1>=g;g++){var p=h+g;p>=0&&i>p||(p=h),p*=4;var x=n[v+p],V=n[v+p+1],w=n[v+p+2],F=t[c+g],M=a[c+g];u+=parseInt(F*x,10),bh+=parseInt(F*V,10),gh+=parseInt(F*w,10),d+=parseInt(M*x,10),gv+=parseInt(M*V,10),bv+=parseInt(M*w,10)}}x=parseInt(Math.sqrt(u*u+d*d)/1.8,10),V=parseInt(Math.sqrt(gh*gh+gv*gv)/1.8,10),w=parseInt(Math.sqrt(bh*bh+bv*bv)/1.8,10),s[o]=x,s[o+1]=V,s[o+2]=w,s[o+3]=n[o+3]}for(var b=0;bA;A++,I+=d)for(var y=I,R=y+d,D=R+d,S=0;e>S;S++,y++,R++,D++){var P=4*(A*e+S);0!==A&&i-2>A&&0!==S&&e-2>S?(v=u[y-1]+u[R-1]+u[D-1]-u[y+1]-u[R+1]-u[D+1],m=u[D-1]+u[D]+u[D+1]-u[y-1]-u[y]-u[y+1],M=0===v&&0===m?b:(F=v*g+m*p+w)<0?0:parseInt(F/Math.sqrt(v*v+m*m+V),10)):M=b,r[P]=r[P+1]=r[P+2]=M}}}function ExposureFilter(){this.name="Exposure",this.isDirAnimatable=!0,this.defaultValues={exposure:1},this.valueRanges={exposure:{min:0,max:5}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.exposure?this.defaultValues.exposure:e.exposure,l=[],h=0;256>h;h++)l[h]=parseInt(255*(1-Math.exp(-(h/255)*s)),10);t.tableFilter(n,l,i,r)}}function GainFilter(){this.name="Gain/Bias",this.isDirAnimatable=!0,this.defaultValues={gain:.5,bias:.5},this.valueRanges={gain:{min:0,max:1},bias:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.gain?this.defaultValues.gain:e.gain,l=void 0===e.bias?this.defaultValues.bias:e.bias,h=[],o=0;256>o;o++){var u=o/255,d=(1/s-2)*(1-2*u);u=.5>u?u/(d+1):(d-u)/(d-1),u/=(1/l-2)*(1-u)+1,h[o]=parseInt(255*u,10)}t.tableFilter(n,h,i,r)}}function GammaFilter(){this.name="Gamma",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;if(0>n&&(n=0),!FilterUtils)return void(console&&console.error("Unable to find filterutils.js, please include this file! (Required by "+this.name+" filter)"));for(var s=new FilterUtils,l=[],h=0;256>h;h++)l[h]=255*Math.pow(h/255,1/n)+.5;s.tableFilter(r,l,e,i)}}function GrayscaleFilter(){this.name="Grayscale",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++){var s=4*(r*a+n),l=.3*i[s]+.59*i[s+1]+.11*i[s+2];i[s]=i[s+1]=i[s+2]=l}}}function HueFilter(){this.name="Hue",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);for(u[0]+=s;u[0]<0;)u[0]+=360;for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function InvertFilter(){this.name="Invert",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++)for(var s=4*(r*a+n),l=0;3>l;l++)i[s+l]=255-i[s+l]}}function KaleidoscopeFilter(){this.name="Kaleidoscope",this.isDirAnimatable=!1,this.defaultValues={angle:0,rotation:0,sides:3,centerX:.5,centerY:.5},this.valueRanges={angle:{min:0,max:360},rotation:{min:0,max:360},sides:{min:1,max:30},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.rotation?this.defaultValues.rotation:e.rotation,h=void 0===e.sides?this.defaultValues.sides:e.sides,o=void 0===e.centerX?this.defaultValues.centerX:e.centerX,u=void 0===e.centerY?this.defaultValues.centerY:e.centerY,d=i*o,f=r*u;s=s/180*Math.PI,l=l/180*Math.PI;var v=function(a,e,i){var r=a-d,n=e-f,o=Math.sqrt(r*r+n*n),u=Math.atan2(n,r)-s-l;u=t.triangle(u/Math.PI*h*.5),u+=s,i[0]=d+o*Math.cos(u),i[1]=f+o*Math.sin(u)};t.transformFilter(n,v,i,r)}}function LensDistortionFilter(){this.name="Lens Distortion",this.isDirAnimatable=!1,this.defaultValues={refraction:1.5,radius:50,centerX:.5,centerY:.5},this.valueRanges={refraction:{min:1,max:10},radius:{min:1,max:200},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.refraction?this.defaultValues.refraction:e.refraction,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o,d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i,l=r*r;if(l>=u-u*n/u)e[0]=t,e[1]=a;else{var h=1/s,o=Math.sqrt((1-n/u-l/u)*u),v=o*o,m=Math.acos(i/Math.sqrt(n+v)),c=Math.PI/2-m,g=Math.asin(Math.sin(c)*h);g=Math.PI/2-m-g,e[0]=t-Math.tan(g)*o;var p=Math.acos(r/Math.sqrt(l+v));c=Math.PI/2-p,g=Math.asin(Math.sin(c)*h),g=Math.PI/2-p-g,e[1]=a-Math.tan(g)*o}};t.transformFilter(n,v,i,r)}}function LineSmearFilter(){this.name="Line Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,angle:0,mix:.5},this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.angle?this.defaultValues.angle:e.angle,d=void 0===e.mix?this.defaultValues.mix:e.mix;u=u/180*Math.PI;for(var f=Math.sin(u),v=Math.cos(u),m=parseInt(2*o*r*n/2,10),c=0;m>c;c++){var g,p,x,V,w,F,M,b=(Math.random()*Math.pow(2,32)&2147483647)%r,I=(Math.random()*Math.pow(2,32)&2147483647)%n,A=(Math.random()*Math.pow(2,32)&2147483647)%h+1,y=[s[4*(I*r+b)],s[4*(I*r+b)+1],s[4*(I*r+b)+2],s[4*(I*r+b)+3]],R=parseInt(A*v,10),D=parseInt(A*f,10),S=b-R,P=I-D,W=b+R,U=I+D;F=S>W?-1:1,M=P>U?-1:1,R=W-S,D=U-P,R=Math.abs(R),D=Math.abs(D),g=S,p=P;var X,Y;if(r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i];if(Math.abs(R)>Math.abs(D)){for(x=2*D-R,V=2*D,w=2*(D-R);g!=W;)if(0>=x?x+=V:(x+=w,p+=M),g+=F,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}else for(x=2*R-D,V=2*R,w=2*(R-D);p!=U;)if(0>=x?x+=V:(x+=w,g+=F),p+=M,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}for(i=0;in;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=0,o=0,u=0,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.max(h,i[c]),o=Math.max(o,i[c+1]),u=Math.max(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=[],o=[],u=[],d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h.push(i[c]),o.push(i[c+1]),u.push(i[c+2])}}}var g=function(t,a){return t-a};h.sort(g),o.sort(g),u.sort(g),r[l]=h[4],r[l+1]=o[4],r[l+2]=u[4],r[l+3]=i[l+3]}for(var p=0;pn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=255,o=255,u=255,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.min(h,i[c]),o=Math.min(o,i[c+1]),u=Math.min(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gh;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);if(Math.random()<=s){var d;if(l)d=parseInt((2*Math.random()-1)*n,10),r[u]+=d,r[u+1]+=d,r[u+2]+=d;else for(var f=0;3>f;f++)d=parseInt((2*Math.random()-1)*n,10),r[u+f]+=d}}}}function OilFilter(){this.name="Oil Painting",this.isDirAnimatable=!1,this.defaultValues={range:3},this.valueRanges={range:{min:0,max:5}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.range?this.defaultValues.range:a.range;s=parseInt(s,10);for(var l=[],h=[],o=[],u=[],d=[],f=[],v=256,m=0;i>m;m++)for(var c=0;e>c;c++){for(var g=4*(m*e+c),p=0;v>p;p++)l[p]=h[p]=o[p]=u[p]=d[p]=f[p]=0;for(var x=-s;s>=x;x++){var V,w=m+x;if(w>=0&&i>w){V=w*e;for(var F=-s;s>=F;F++){var M=c+F;if(M>=0&&e>M){var b=r[4*(V+M)],I=r[4*(V+M)+1],A=r[4*(V+M)+2],y=b*v/256,R=I*v/256,D=A*v/256;u[y]+=b,d[R]+=I,f[D]+=A,l[y]++,h[R]++,o[D]++}}}}for(var S=0,P=0,W=0,U=1;v>U;U++)l[U]>l[S]&&(S=U),h[U]>h[P]&&(P=U),o[U]>o[W]&&(W=U);S=u[S]/l[S],P=d[P]/h[P],W=f[W]/o[W],n[g]=S,n[g+1]=P,n[g+2]=W,n[g+3]=r[g+3]}for(var X=0;Xs;s++)for(var l=0;e>l;l++){var h=4*(s*e+l);r[h+3]=255*n}}}function PinchFilter(){this.name="Pinch/Whirl",this.isDirAnimatable=!1,this.defaultValues={amount:.5,radius:100,angle:0,centerX:.5,centerY:.5},this.valueRanges={amount:{min:-1,max:1},radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=void 0===e.angle?this.defaultValues.angle:e.angle,h=void 0===e.centerX?this.defaultValues.centerX:e.centerX,o=void 0===e.centerY?this.defaultValues.centerY:e.centerY,u=void 0===e.radius?this.defaultValues.radius:e.radius,d=u*u;l=l/180*Math.PI;var f=i*h,v=r*o,m=function(t,a,e){var i=t-f,r=a-v,n=i*i+r*r;if(n>d||0===n)e[0]=t,e[1]=a;else{var h=Math.sqrt(n/d),o=Math.pow(Math.sin(.5*Math.PI*h),-s);i*=o,r*=o;var u=1-h,m=l*u*u,c=Math.sin(m),g=Math.cos(m);e[0]=f+g*i-c*r,e[1]=v+c*i+g*r}};t.transformFilter(n,m,i,r)}}function PixelationFilter(){this.name="Pixelation",this.isDirAnimatable=!1,this.defaultValues={size:5},this.valueRanges={size:{min:1,max:50}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.size?this.defaultValues.size:a.size;n=parseInt(n,10);for(var s,l,h,o=0;i>o;o+=n)for(var u=0;e>u;u+=n){var d=Math.min(n,e-u),f=Math.min(n,i-o),v=d*f,m=0,c=0,g=0;for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),m+=r[h],c+=r[h+1],g+=r[h+2];for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),r[h]=m/v,r[h+1]=c/v,r[h+2]=g/v}}}function PosterizeFilter(){this.name="Posterize",this.isDirAnimatable=!1,this.defaultValues={levels:6},this.valueRanges={levels:{min:2,max:30}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.levels?this.defaultValues.levels:parseInt(e.levels,10);if(!(1>=s)){for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*parseInt(h*s/256,10)/(s-1),10);t.tableFilter(n,l,i,r)}}}function RGBAdjustFilter(){this.name="RGBAdjust",this.isDirAnimatable=!0,this.defaultValues={red:1,green:1,blue:1},this.valueRanges={red:{min:0,max:2},green:{min:0,max:2},blue:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.red?this.defaultValues.red:a.red,s=void 0===a.green?this.defaultValues.green:a.green,l=void 0===a.blue?this.defaultValues.blue:a.blue;0>n&&(n=0),0>s&&(s=0),0>l&&(l=0);for(var h=0;i>h;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);r[u]*=n,r[u+1]*=s,r[u+2]*=l}}}function SaturationFilter(){this.name="Saturation",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);for(var n=void 0===a.amount?this.defaultValues.amount:a.amount,s=.3,l=.59,h=.11,o=(1-n)*s+n,u=(1-n)*s,d=(1-n)*s,f=(1-n)*l,v=(1-n)*l+n,m=(1-n)*l,c=(1-n)*h,g=(1-n)*h,p=(1-n)*h+n,x=0;i>x;x++)for(var V=0;e>V;V++){var w=4*(x*e+V),F=r[w],M=r[w+1],b=r[w+2];r[w]=o*F+f*M+c*b,r[w+1]=u*F+v*M+g*b,r[w+2]=d*F+m*M+p*b}}}function SawtoothRippleFilter(){this.name="Sawtooth Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.mod(r,1),d=t.mod(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function SepiaFilter(){this.name="Sepia",this.isDirAnimatable=!0,this.defaultValues={amount:10},this.valueRanges={amount:{min:0,max:30}};new FilterUtils;this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;n*=2.55;for(var s=0;i>s;s++)for(var l=0;e>l;l++){var h,o,u,d=4*(s*e+l),f=.3*r[d]+.59*r[d+1]+.11*r[d+2];h=o=u=f,h+=40,o+=20,u-=n,r[d]=h,r[d+1]=o,r[d+2]=u}}}function SharpenFilter(){this.name="Sharpen",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[0,-.2,0,-.2,1.8,-.2,0,-.2,0];t.convolveFilter(r,n,e,i)}}function SineRippleFilter(){this.name="Sine Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(t,a,e){var i=a/h,r=t/o,n=Math.sin(i),u=Math.sin(r);e[0]=t+s*n,e[1]=a+l*u};t.transformFilter(n,u,i,r)}}function SolarizeFilter(){this.name="Solarize",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){for(var e=a.width,i=a.height,r=a.data,n=[],s=0;256>s;s++){var l=s/255>.5?2*(s/255-.5):2*(.5-s/255);n[s]=parseInt(255*l,10)}t.tableFilter(r,n,e,i)}}function SparkleFilter(){this.name="Sparkle",this.isDirAnimatable=!1,this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:.5,centerY:.5},this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.rays?this.defaultValues.rays:e.rays;s=parseInt(s,10);for(var l=void 0===e.size?this.defaultValues.size:e.size,h=void 0===e.amount?this.defaultValues.amount:e.amount,o=void 0===e.randomness?this.defaultValues.randomness:e.randomness,u=void 0===e.centerX?this.defaultValues.centerX:e.centerX,d=void 0===e.centerY?this.defaultValues.centerY:e.centerY,f=u*i,v=d*r,m=[],c=0;s>c;c++)m[c]=l+o/100*l*t.gaussianRandom();for(var g=0;r>g;g++)for(var p=0;i>p;p++){var x=4*(g*i+p),V=p-f,w=g-v,F=V*V+w*w,M=Math.atan2(w,V),b=(M+Math.PI)/(2*Math.PI)*s,I=parseInt(b,10),A=b-I;if(0!==l){var y=t.linearInterpolate(A,m[I%s],m[(I+1)%s]),R=y*y/(F+1e-4);R=Math.pow(R,(100-h)/50),A-=.5,A=1-A*A,A*=R}A=t.clampPixel(A,0,1);for(var D=t.mixColors(A,[n[x],n[x+1],n[x+2],n[x+3]],[255,255,255,255]),S=0;3>S;S++)n[x+S]=D[S]}}}function SquareSmearFilter(){this.name="Square Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=parseInt(2*o/30*r*n/2,10),v=0;f>v;v++)for(var m=(Math.random()*Math.pow(2,32)&2147483647)%r,c=(Math.random()*Math.pow(2,32)&2147483647)%n,g=[s[4*(c*r+m)],s[4*(c*r+m)+1],s[4*(c*r+m)+2],s[4*(c*r+m)+3]],p=m-d;m+d+1>p;p++)for(var x=c-d;c+d+1>x;x++)if(p>=0&&r>p&&x>=0&&n>x){var V=[l[4*(x*r+p)],l[4*(x*r+p)+1],l[4*(x*r+p)+2],l[4*(x*r+p)+3]],w=t.mixColors(u,V,g);for(i=0;3>i;i++)l[4*(x*r+p)+i]=w[i]}for(i=0;is;s++)for(var l=0;e>l;l++){var h=4*(s*e+l),o=(r[h]+r[h+1]+r[h+2])/3,u=0;o>n&&(u=255),r[h]=r[h+1]=r[h+2]=u}}}function TriangleRippleFilter(){this.name="Triangle Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.triangle(r,1),d=t.triangle(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function TwirlFilter(){this.name="Twirl",this.isDirAnimatable=!1,this.defaultValues={radius:100,angle:180,centerX:.5,centerY:.5},this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o;s=s/180*Math.PI;var d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i+r*r;if(n>u)e[0]=t,e[1]=a;else{n=Math.sqrt(n);var l=Math.atan2(r,i)+s*(o-n)/o;e[0]=d+n*Math.cos(l),e[1]=f+n*Math.sin(l)}};t.transformFilter(n,v,i,r)}}function VignetteFilter(){this.name="Vignette",this.isDirAnimatable=!1,this.defaultValues={amount:.3},this.valueRanges={amount:{min:0,max:1}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.amount?this.defaultValues.amount:a.amount,l=document.createElement("canvas");l.width=e,l.height=i;var h,o=l.getContext("2d"),u=Math.sqrt(Math.pow(e/2,2)+Math.pow(i/2,2));o.putImageData(t,0,0),o.globalCompositeOperation="source-over",h=o.createRadialGradient(e/2,i/2,0,e/2,i/2,u),h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(.5,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,"+s+")"),o.fillStyle=h,o.fillRect(0,0,e,i),n=o.getImageData(0,0,e,i).data;for(var d=0;df)e[0]=t,e[1]=a;else{var o=Math.sqrt(n),u=l*Math.sin(o/s*Math.PI*2-h);u*=(d-o)/d,0!==o&&(u*=s/o),e[0]=t+i*u,e[1]=a+r*u}};t.transformFilter(n,c,i,r)}}var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter,maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter,triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter};"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=JSManipulate); diff --git a/script/package.json b/script/package.json deleted file mode 100644 index 7285e10..0000000 --- a/script/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "author": "Joel Besada", - "name": "jsmanipulate", - "version": "1.0.0", - "description": "JSManipulate is an image filter and effects library written in Javascript for client-side manipulation of images on a web page.", - "main": "jsmanipulate.js", - "license": "MIT", - "keywords": ["canvas", "image", "images", "graphics", "filter", "filters", "manipulation", "manipulate", "distortion", "distort", "effects", "blur", "sharpen", "emboss", "invert"], - "homepage": "http://joelb.me/jsmanipulate/", - "repository": "git://github.com/JoelBesada/JSManipulate.git", - "dependencies": { - "canvas": "1.2.7" - }, - "engines": { "node": "*" } -} diff --git a/script/separate filters/blur.js b/script/separate filters/blur.js deleted file mode 100644 index 0834c11..0000000 --- a/script/separate filters/blur.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Blurs the image with Gaussian blur. - */ -function BlurFilter(){ - this.name = "Blur"; - this.defaultValues = { - amount : 3 - }; - this.valueRanges = { - amount : {min:0, max:10} - }; - this.filter = function(input,values){ - var width = input.width; - var width4 = width << 2; - var height = input.height; - var inputData = input.data; - var q; - var amount = values.amount; - if (amount < 0.0) { - amount = 0.0; - } - if (amount >= 2.5) { - q = 0.98711 * amount - 0.96330; - } else if (amount >= 0.5) { - q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * amount); - } else { - q = 2 * amount * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5)); - } - var qq = q * q; - var qqq = qq * q; - var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq); - var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0; - var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0; - var b3 = (0.422205 * qqq) / b0; - var bigB = 1.0 - (b1 + b2 + b3); - for (var c = 0; c < 3; c++) { - for (var y = 0; y < height; y++) { - var index = y * width4 + c; - var indexLast = y * width4 + ((width - 1) << 2) + c; - var pixel = inputData[index]; - var ppixel = pixel; - var pppixel = ppixel; - var ppppixel = pppixel; - for (; index <= indexLast; index += 4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - index = y * width4 + ((width - 1) << 2) + c; - indexLast = y * width4 + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index >= indexLast; index -= 4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - } - } - for (var c = 0; c < 3; c++) { - for (var x = 0; x < width; x++) { - var index = (x << 2) + c; - var indexLast = (height - 1) * width4 + (x << 2) + c; - var pixel = inputData[index]; - var ppixel = pixel; - var pppixel = ppixel; - var ppppixel = pppixel; - for (; index <= indexLast; index += width4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - index = (height - 1) * width4 + (x << 2) + c; - indexLast = (x << 2) + c; - pixel = inputData[index]; - ppixel = pixel; - pppixel = ppixel; - ppppixel = pppixel; - for (; index >= indexLast; index -= width4) { - pixel = bigB * inputData[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel; - inputData[index] = pixel; - ppppixel = pppixel; - pppixel = ppixel; - ppixel = pixel; - } - } - } - } -} diff --git a/script/separate filters/brightness.js b/script/separate filters/brightness.js deleted file mode 100644 index c456acb..0000000 --- a/script/separate filters/brightness.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Adjusts the brightness of the image by going over to HSV values. - * Negative values decrease brightness while positive values increase brightness. - */ -function BrightnessFilter(){ - this.name = "Brightness"; - this.defaultValues = { - amount : 0.0 - }; - this.valueRanges = { - amount : {min:-1.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); - hsv[2] += amount; - if(hsv[2] < 0){ - hsv[2] = 0; - } else if (hsv[2] > 1){ - hsv[2] = 1; - } - var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = rgb[i]; - } - } - } - } -} diff --git a/script/separate filters/bump.js b/script/separate filters/bump.js deleted file mode 100644 index 9318f34..0000000 --- a/script/separate filters/bump.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Embosses the edges of the image. - * This filter takes no parameters but can be applied several times for - * further effect. - */ -function BumpFilter(){ - this.name = "Bump"; - this.defaultValues = { - }; - this.valueRanges = { - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var matrix = [-1,-1, 0, - -1, 1, 1, - 0, 1, 1]; - filterUtils.convolveFilter(inputData,matrix,width,height); - } -} diff --git a/script/separate filters/circlesmear.js b/script/separate filters/circlesmear.js deleted file mode 100644 index 9b13293..0000000 --- a/script/separate filters/circlesmear.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Smears out the image with circular shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. - */ -function CircleSmearFilter(){ - this.name = "Circle Smear"; - this.defaultValues = { - size : 4, - density : 0.5, - mix : 0.5, - }; - this.valueRanges = { - size : {min:1, max:10}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0}, - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - if(size < 1){ size = 1;} - size = parseInt(size); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var radius = size+1; - var radius2 = radius*radius; - var numShapes = parseInt(2*density/30*width*height / 2); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - for(var x = sx - radius; x < sx + radius + 1; x++){ - for(var y = sy - radius; y < sy + radius + 1; y++){ - var f = (x - sx) * (x - sx) + (y - sy) * (y - sy); - if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/contrast.js b/script/separate filters/contrast.js deleted file mode 100644 index e7ea429..0000000 --- a/script/separate filters/contrast.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Adjusts the contrast of the image. - */ -function ContrastFilter(){ - this.name = "Contrast"; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - if(amount < 0){ - amount = 0.0; - } - var table = []; - - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 * (((i/255)-0.5)*amount+0.5)); - } - filterUtils.tableFilter(inputData,table,width,height); - } -} diff --git a/script/separate filters/crosssmear.js b/script/separate filters/crosssmear.js deleted file mode 100644 index 6b06544..0000000 --- a/script/separate filters/crosssmear.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Smears out the image with cross shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - */ -function CrossSmearFilter(){ - this.name = "Cross Smear"; - this.defaultValues = { - distance : 8, - density : 0.5, - mix : 0.5, - }; - this.valueRanges = { - distance : {min:0, max:30}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0}, - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; - if(distance < 0){ distance = 0;} - distance = parseInt(distance); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var numShapes = parseInt(2*density*width * height / (distance + 1)); - for(var i = 0; i < numShapes; i++){ - var x = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var y = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var length = (Math.random()*Math.pow(2,32)) % distance + 1; - var rgb2 = [inputData[(y*width+x)*4],inputData[(y*width+x)*4+1],inputData[(y*width+x)*4+2],inputData[(y*width+x)*4+3]]; - for (var x1 = x-length; x1 < x+length+1; x1++) { - if(x1 >= 0 && x1 < width){ - var rgb1 = [outputData[(y*width+x1)*4],outputData[(y*width+x1)*4+1],outputData[(y*width+x1)*4+2],outputData[(y*width+x1)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x1)*4+k] = mixedRGB[k]; - } - } - - } - for (var y1 = y-length; y1 < y+length+1; y1++) { - if(y1 >= 0 && y1 < height){ - var rgb1 = [outputData[(y1*width+x)*4],outputData[(y1*width+x)*4+1],outputData[(y1*width+x)*4+2],outputData[(y1*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y1*width+x)*4+k] = mixedRGB[k]; - } - } - - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/diffusion.js b/script/separate filters/diffusion.js deleted file mode 100644 index 9ba8594..0000000 --- a/script/separate filters/diffusion.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Diffuses the image creating a frosted glass effect. - */ -function DiffusionFilter(){ - this.name = "Diffusion"; - this.defaultValues = { - scale: 4 - }; - this.valueRanges = { - scale: {min: 1, max: 100} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var scale = (values.scale === undefined) ? this.defaultValues.scale : values.scale; - var out = []; - var outputData = []; - var sinTable = []; - var cosTable = []; - for(var i = 0; i < 256; i++){ - var angle = Math.PI*2*i/256; - sinTable[i] = scale*Math.sin(angle); - cosTable[i] = scale*Math.cos(angle); - } - transInverse = function (x,y,out){ - var angle = parseInt(Math.random() * 255); - var distance = Math.random(); - out[0] = x + distance * sinTable[angle]; - out[1] = y + distance * cosTable[angle]; - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/dither.js b/script/separate filters/dither.js deleted file mode 100644 index ae3fbff..0000000 --- a/script/separate filters/dither.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Dithers the image to the specified number of colors. Setting color to false - * grayscales the image. - */ -function DitherFilter(){ - this.name = "Dither"; - this.defaultValues = { - levels : 3, - color : true - }; - this.valueRanges = { - levels : {min:2, max:30}, - color : {min:false, max:true} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var i=0; i < inputData.length; i++) { - outputData[i] = 0; - }; - if(values === undefined){ values = this.defaultValues; } - var levels = (values.levels === undefined) ? this.defaultValues.levels : values.levels; - var color = (values.color === undefined) ? this.defaultValues.color : values.color; - if(levels <= 1){ - levels = 1; - } - var matrix = [0,0,0, - 0,0,7, - 3,5,1]; - var sum = 7+3+5+1; - var index = 0; - var map = []; - for (var i=0; i < levels; i++) { - map[i] = parseInt(255* i / (levels-1)); - }; - var div = []; - for (var i=0; i < 256; i++) { - div[i] = parseInt(levels*i / 256); - }; - for (var y = 0; y < height; y++) { - var reverse = ((y & 1) == 1); - var direction; - if(reverse){ - index = (y*width+width-1)*4; - direction = -1 - } else { - index = y*width*4; - direction = 1; - } - for (var x = 0; x < width; x++) { - var r1 = inputData[index]; var g1 = inputData[index+1]; var b1 = inputData[index+2]; - if(!color){ - r1 = g1 = b1 = parseInt((r1+g1+b1) / 3); - } - var r2 = map[div[r1]];var g2 = map[div[g1]];var b2 = map[div[b1]]; - - outputData[index] = r2; outputData[index + 1] = g2; outputData[index+2] = b2; outputData[index+3] = inputData[index+3]; - - var er = r1-r2; var eg = g1-g2; var eb = b1-b2; - - for (var i = -1; i <= 1; i++) { - var iy = i+y; - if (0 <= iy && iy < height) { - for (var j = -1; j <= 1; j++) { - var jx = j+x; - if (0 <= jx && jx < width) { - var w; - if (reverse){ - w = matrix[(i+1)*3-j+1]; - } else{ - w = matrix[(i+1)*3+j+1]; - } - if (w != 0) { - var k = (reverse) ? index - j*4 : index + j*4; - r1 = inputData[k]; g1 = inputData[k+1]; b1 = inputData[k+2]; - var factor = w/sum; - r1 += er * factor; g1 += eg * factor; b1 += eb * factor; - inputData[k] = r1; inputData[k+1] = g1 ;inputData[k+2] = b1; - } - } - } - } - } - index += direction*4; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/edge.js b/script/separate filters/edge.js deleted file mode 100644 index 6d3e50f..0000000 --- a/script/separate filters/edge.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Highlights the edges of the image. - */ -function EdgeFilter(){ - this.name = "Edge Detection"; - var matrixH = [-1,-2,-1, - 0, 0, 0, - 1, 2, 1]; - var matrixV = [-1, 0, 1, - -2, 0, 2, - -1, 0, 1]; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var rh = 0; gh = 0; bh = 0; - var rv = 0; gv = 0; bv = 0; - for(var row = -1; row <= 1; row++){ - var iy = y+row; - var ioffset; - if(iy >= 0 && iy < height){ - ioffset = iy*width*4; - } else { - ioffset = y*width*4; - } - var moffset = 3*(row+1)+1; - for(var col = -1; col <= 1; col++){ - var ix = x+col; - if(!(ix >= 0 && ix < width)){ - ix = x; - } - ix *= 4; - var r = inputData[ioffset+ix]; - var g = inputData[ioffset+ix+1]; - var b = inputData[ioffset+ix+2]; - var h = matrixH[moffset+col]; - var v = matrixV[moffset+col]; - rh += parseInt(h*r); - bh += parseInt(h*g); - gh += parseInt(h*b); - rv += parseInt(v*r); - gv += parseInt(v*g); - bv += parseInt(v*b); - } - } - r = parseInt(Math.sqrt(rh*rh + rv*rv) / 1.8); - g = parseInt(Math.sqrt(gh*gh + gv*gv) / 1.8); - b = parseInt(Math.sqrt(bh*bh + bv*bv) / 1.8); - - outputData[pixel] = r; - outputData[pixel+1] = g; - outputData[pixel+2] = b; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/emboss.js b/script/separate filters/emboss.js deleted file mode 100644 index c4b673d..0000000 --- a/script/separate filters/emboss.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Embosses the image with a simulated light source. - * Angle and elevation sets the position of the light. - */ -function EmbossFilter(){ - this.name = "Emboss"; - this.defaultValues = { - height : 1, - angle : 135, - elevation : 30 - }; - this.valueRanges = { - height : {min:1, max:10}, - angle : {min:0, max:360}, - elevation : {min:0, max:180} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var bumpHeight = (values.height === undefined) ? this.defaultValues.height : values.height; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var elevation = (values.elevation === undefined) ? this.defaultValues.elevation : values.elevation; - angle = angle / 180 * Math.PI; - elevation = elevation / 180 * Math.PI; - var width45 = 3 * bumpHeight; - var pixelScale = 255.9; - - var bumpPixels = []; - var bumpMapWidth = width; - var bumpMapHeight = height; - for(var i = 0; i < inputData.length; i+=4){ - bumpPixels[i/4] = (inputData[i] + inputData[i+1] + inputData[i+2])/3 - } - var Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL; - var shade, background; - - Lx = parseInt(Math.cos(angle) * Math.cos(elevation) * pixelScale); - Ly = parseInt(Math.sin(angle) * Math.cos(elevation) * pixelScale); - Lz = parseInt(Math.sin(elevation) * pixelScale); - - Nz = parseInt(6 * 255 / width45); - Nz2 = Nz * Nz; - NzLz = Nz * Lz; - background = Lz; - - var bumpIndex = 0; - - for (var y = 0; y < height; y++, bumpIndex += bumpMapWidth) { - var s1 = bumpIndex; - var s2 = s1 + bumpMapWidth; - var s3 = s2 + bumpMapWidth; - for (var x = 0; x < width; x++, s1++, s2++, s3++) { - var pixel = (y*width + x)*4; - if (y != 0 && y < height-2 && x != 0 && x < width-2) { - Nx = bumpPixels[s1-1] + bumpPixels[s2-1] + bumpPixels[s3-1] - bumpPixels[s1+1] - bumpPixels[s2+1] - bumpPixels[s3+1]; - Ny = bumpPixels[s3-1] + bumpPixels[s3] + bumpPixels[s3+1] - bumpPixels[s1-1] - bumpPixels[s1] - bumpPixels[s1+1]; - if (Nx == 0 && Ny == 0){ - shade = background; - } else if ((NdotL = Nx*Lx + Ny*Ly + NzLz) < 0){ - shade = 0; - } else { - shade = parseInt(NdotL / Math.sqrt(Nx*Nx + Ny*Ny + Nz2)); - } - } else { - shade = background; - } - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = shade; - } - } - } -} diff --git a/script/separate filters/exposure.js b/script/separate filters/exposure.js deleted file mode 100644 index 1b3f079..0000000 --- a/script/separate filters/exposure.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Adjust simulated exposure values on the image. - */ -function ExposureFilter(){ - this.name = "Exposure"; - this.defaultValues = { - exposure : 1.0 - }; - this.valueRanges = { - exposure : {min:0, max:5} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var exposure = (values.exposure === undefined) ? this.defaultValues.exposure : values.exposure; - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 *(1-Math.exp(-(i/255) * exposure))); - } - filterUtils.tableFilter(inputData, table, width, height); - } -} diff --git a/script/separate filters/filterutils.js b/script/separate filters/filterutils.js deleted file mode 100644 index 293e1e5..0000000 --- a/script/separate filters/filterutils.js +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Contains common filter functions. - */ -function FilterUtils(){ - this.HSVtoRGB = function (h, s, v){ - var r, g, b; - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - switch(i % 6){ - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; - } - return [r * 255, g * 255, b * 255]; - } - this.RGBtoHSV = function (r, g, b){ - r = r/255, g = g/255, b = b/255; - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - var d = max - min; - s = max == 0 ? 0 : d / max; - if(max == min){ - h = 0; - }else{ - switch(max){ - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return [h, s, v]; - } - this.getPixel = function (pixels,x,y,width,height){ - var pix = (y*width + x)*4; - if (x < 0 || x >= width || y < 0 || y >= height) { - return [pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 1], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 2], - pixels[((this.clampPixel(y, 0, height-1) * width) + this.clampPixel(x, 0, width-1))*4 + 3]]; - } - return [pixels[pix],pixels[pix+1],pixels[pix+2],pixels[pix+3]] - } - var haveNextGaussian = false; - var nextGaussian; - this.gaussianRandom = function(){ - if(haveNextGaussian){ - haveNextGaussian = false; - return nextGaussian; - } else { - var v1, v2, s; - do { - v1 = 2 * Math.random() - 1; - v2 = 2 * Math.random() - 1; - s = v1 * v1 + v2 * v2; - } while (s >= 1 || s == 0); - var mult = Math.sqrt(-2 * Math.log(s)/s); - nextGaussian = v2 * mult; - haveNextGaussian = true; - return v1 * mult; - } - } - this.clampPixel = function (x,a,b){ - return (x < a) ? a : (x > b) ? b : x; - } - this.triangle = function(x){ - var r = this.mod(x, 1); - return 2*(r < 0.5 ? r : 1-r); - } - this.mod = function(a,b){ - var n = parseInt(a/b); - a -= n*b; - if(a < 0){ - return a + b; - } - return a; - } - this.mixColors = function(t, rgb1, rgb2){ - var r = this.linearInterpolate(t,rgb1[0],rgb2[0]); - var g = this.linearInterpolate(t,rgb1[1],rgb2[1]); - var b = this.linearInterpolate(t,rgb1[2],rgb2[2]); - var a = this.linearInterpolate(t,rgb1[3],rgb2[3]); - return [r,g,b,a]; - } - - this.linearInterpolate = function(t,a,b){ - return a + t * (b-a); - } - this.bilinearInterpolate = function (x,y,nw,ne,sw,se){ - var m0, m1; - var r0 = nw[0]; var g0 = nw[1]; var b0 = nw[2]; var a0 = nw[3]; - var r1 = ne[0]; var g1 = ne[1]; var b1 = ne[2]; var a1 = ne[3]; - var r2 = sw[0]; var g2 = sw[1]; var b2 = sw[2]; var a2 = sw[3]; - var r3 = se[0]; var g3 = se[1]; var b3 = se[2]; var a3 = se[3]; - var cx = 1.0 - x; var cy = 1.0 - y; - - m0 = cx * a0 + x * a1; - m1 = cx * a2 + x * a3; - var a = cy * m0 + y * m1; - - m0 = cx * r0 + x * r1; - m1 = cx * r2 + x * r3; - var r = cy * m0 + y * m1; - - m0 = cx * g0 + x * g1; - m1 = cx * g2 + x * g3; - var g = cy * m0 + y * m1; - - m0 = cx * b0 + x * b1; - m1 = cx * b2 + x * b3; - var b =cy * m0 + y * m1; - return [r,g,b,a]; - } - this.tableFilter = function (inputData, table, width, height){ - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = table[inputData[pixel+i]]; - } - } - } - } - this.convolveFilter = function(inputData, matrix, width, height){ - var outputData = []; - var rows, cols; - rows = cols = Math.sqrt(matrix.length); - var rows2 = parseInt(rows/2); - var cols2 = parseInt(cols/2); - var trace = true; - for(var y = 0; y < height; y++){ - for (var x = 0; x < width; x++){ - var pixel = (y*width + x)*4; - var r = 0, g = 0, b = 0; - for(var row = -rows2; row <= rows2; row++){ - var iy = y+row; - var ioffset; - if (0 <= iy && iy < height) { - ioffset = iy*width; - } else { - ioffset = y*width; - } - var moffset = cols*(row+rows2)+cols2; - for (var col = -cols2; col <= cols2; col++) { - var f = matrix[moffset+col]; - - if (f != 0) { - var ix = x+col; - if (!(0 <= ix && ix < width)) { - ix = x; - } - var iPixel = (ioffset+ix)*4; - r += f * inputData[iPixel]; - g += f * inputData[iPixel+1]; - b += f * inputData[iPixel+2]; - } - } - } - outputData[pixel] = parseInt(r+0.5); - outputData[pixel+1] = parseInt(g+0.5); - outputData[pixel+2] = parseInt(b+0.5); - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } - this.transformFilter = function(inputData, transformInverse, width, height){ - var out = []; - var outputData = []; - for(var j = 0; j < inputData.length; j++){ - outputData[j] = inputData[j]; - } - for(var y = 0; y < height; y++){ - for (var x = 0; x < width; x++){ - var pixel = (y*width + x)*4; - transformInverse.apply(this,[x,y,out]); - var srcX = Math.floor(out[0]); - var srcY = Math.floor(out[1]); - var xWeight = out[0]-srcX; - var yWeight = out[1]-srcY; - var nw,ne,sw,se; - if(srcX >= 0 && srcX < width-1 && srcY >= 0 && srcY < height-1){ - var i = (width*srcY + srcX)*4; - nw = [inputData[i],inputData[i+1],inputData[i+2],inputData[i+3]]; - ne = [inputData[i+4],inputData[i+5],inputData[i+6],inputData[i+7]]; - sw = [inputData[i+width*4],inputData[i+width*4+1],inputData[i+width*4+2],inputData[i+width*4+3]]; - se = [inputData[i+(width + 1)*4],inputData[i+(width + 1)*4+1],inputData[i+(width + 1)*4+2],inputData[i+(width + 1)*4+3]]; - - } else { - nw = this.getPixel( inputData, srcX, srcY, width, height ); - ne = this.getPixel( inputData, srcX+1, srcY, width, height ); - sw = this.getPixel( inputData, srcX, srcY+1, width, height ); - se = this.getPixel( inputData, srcX+1, srcY+1, width, height ); - } - var rgba = this.bilinearInterpolate(xWeight,yWeight,nw,ne,sw,se); - outputData[pixel] = rgba[0]; - outputData[pixel + 1] = rgba[1]; - outputData[pixel + 2] = rgba[2]; - outputData[pixel + 3] = rgba[3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/gain.js b/script/separate filters/gain.js deleted file mode 100644 index e3f31f8..0000000 --- a/script/separate filters/gain.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Adjusts the gain and bias of the image. Gain alters the contrast while bias biases - * colors towards lighter or darker. - */ -function GainFilter(){ - this.name = "Gain/Bias"; - this.defaultValues = { - gain: 0.5, - bias: 0.5 - }; - this.valueRanges = { - gain: {min:0.0, max:1.0}, - bias: {min:0.0, max:1.0} - }; - var table = []; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var gain = (values.gain === undefined) ? this.defaultValues.gain : values.gain; - var bias = (values.bias === undefined) ? this.defaultValues.bias : values.bias; - - var table = []; - - for(var i = 0; i < 256; i++){ - var val = i/255; - var k = (1/gain-2) * (1-2*val); - val = (val < 0.5) ? val/(k+1) : (k-val)/(k-1); - val /= (1/bias-2)*(1-val)+1; - table[i] = parseInt(255 * val); - } - filterUtils.tableFilter(inputData,table,width,height); - } -} diff --git a/script/separate filters/gamma.js b/script/separate filters/gamma.js deleted file mode 100644 index aab1c1d..0000000 --- a/script/separate filters/gamma.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Adjusts the gamma values of the image. Values over 1 increase the gamma while values over 0 decrease gamma. - */ -function GammaFilter(){ - this.name = "Gamma"; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - if(amount < 0){ - amount = 0.0; - } - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = 255 * Math.pow(i/255, 1/amount) + 0.5; - } - filterUtils.tableFilter(inputData,table,width,height); - } -} diff --git a/script/separate filters/grayscale.js b/script/separate filters/grayscale.js deleted file mode 100644 index d53954d..0000000 --- a/script/separate filters/grayscale.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Sets the image to grayscale. - */ -function GrayscaleFilter(){ - this.name = "Grayscale"; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = luma; - } - } - } -} diff --git a/script/separate filters/hue.js b/script/separate filters/hue.js deleted file mode 100644 index 0b25787..0000000 --- a/script/separate filters/hue.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Adjusts the hue of the image by going over to HSV values. - */ -function HueFilter(){ - this.name = "Hue"; - this.defaultValues = { - amount : 0.0 - }; - this.valueRanges = { - amount : {min:-1.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var hsv = filterUtils.RGBtoHSV(inputData[pixel],inputData[pixel+1],inputData[pixel+2]); - hsv[0] += amount; - while(hsv[0] < 0){ - hsv[0] += 360; - } - var rgb = filterUtils.HSVtoRGB(hsv[0],hsv[1],hsv[2]); - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = rgb[i]; - } - } - } - } -} diff --git a/script/separate filters/invert.js b/script/separate filters/invert.js deleted file mode 100644 index 5711ee5..0000000 --- a/script/separate filters/invert.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Inverts the colors of the image. - */ -function InvertFilter(){ - this.name = "Invert"; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for(var i = 0; i < 3; i++){ - inputData[pixel+i] = 255 - inputData[pixel+i]; - } - } - } - } -} diff --git a/script/separate filters/kaleidoscope.js b/script/separate filters/kaleidoscope.js deleted file mode 100644 index 7322519..0000000 --- a/script/separate filters/kaleidoscope.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Creates a kaleidoscope effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function KaleidoscopeFilter(){ - this.name = "Kaleidoscope"; - this.defaultValues = { - angle : 0, - rotation : 0, - sides : 3, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - angle : {min: 0, max: 360}, - rotation : {min: 0, max: 360}, - sides : {min: 1, max: 30}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var rotation = (values.rotation === undefined) ? this.defaultValues.rotation : values.rotation; - var sides = (values.sides === undefined) ? this.defaultValues.sides : values.sides; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var iCenterX = width * centerX; var iCenterY = height * centerY; - angle = angle/180 * Math.PI; - rotation = rotation/180 * Math.PI; - var transInverse = function(x,y,out){ - var dx = x - iCenterX; - var dy = y - iCenterY; - var r = Math.sqrt(dx*dx + dy*dy); - var theta = Math.atan2(dy,dx) - angle - rotation; - theta = filterUtils.triangle(theta/Math.PI*sides*0.5); - theta += angle; - out[0] = iCenterX + r*Math.cos(theta); - out[1] = iCenterY + r*Math.sin(theta); - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/lensdistortion.js b/script/separate filters/lensdistortion.js deleted file mode 100644 index a7f45ed..0000000 --- a/script/separate filters/lensdistortion.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Applies a fisheye lens distortion effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function LensDistortionFilter(){ - this.name = "Lens Distortion"; - this.defaultValues = { - refraction : 1.5, - radius : 50, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - refraction : {min: 1, max: 10}, - radius : {min: 1, max: 200}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var refraction = (values.refraction === undefined) ? this.defaultValues.refraction : values.refraction; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var x2 = dx*dx; - var y2 = dy*dy; - if (y2 >= (radius2 - (radius2*x2)/radius2)) { - out[0] = x; - out[1] = y; - } else { - var rRefraction = 1.0 / refraction; - - var z = Math.sqrt((1.0 - x2/radius2 - y2/radius2) * radius2); - var z2 = z*z; - - var xAngle = Math.acos(dx / Math.sqrt(x2+z2)); - var angle1 = Math.PI/2 - xAngle; - var angle2 = Math.asin(Math.sin(angle1)*rRefraction); - angle2 = Math.PI/2 - xAngle - angle2; - out[0] = x - Math.tan(angle2)*z; - - var yAngle = Math.acos(dy / Math.sqrt(y2+z2)); - angle1 = Math.PI/2 - yAngle; - angle2 = Math.asin(Math.sin(angle1)*rRefraction); - angle2 = Math.PI/2 - yAngle - angle2; - out[1] = y - Math.tan(angle2)*z; - } - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/linesmear.js b/script/separate filters/linesmear.js deleted file mode 100644 index 4b81c64..0000000 --- a/script/separate filters/linesmear.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Smears out the image with line shapes to create a painting style effect. Mix specifies - * the intensity of the effect. - */ -function LineSmearFilter(){ - this.name = "Line Smear"; - this.defaultValues = { - distance : 8, - density : 0.5, - angle : 0, - mix : 0.5, - }; - this.valueRanges = { - distance : {min:1, max:30}, - density : {min:0.0, max:1.0}, - angle : {min:0, max:360}, - mix : {min:0.0, max:1.0}, - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var distance = (values.distance === undefined) ? this.defaultValues.distance : values.distance; - if(distance < 1){ distance = 1;} - distance = parseInt(distance); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - angle = angle/180*Math.PI; - var sinAngle = Math.sin(angle); - var cosAngle = Math.cos(angle); - var numShapes = parseInt(2*density*width*height / 2); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var length = (Math.random()*Math.pow(2,32) & 0x7fffffff) % distance + 1; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - var dx = parseInt(length*cosAngle); - var dy = parseInt(length*sinAngle); - - var x0 = sx-dx; - var y0 = sy-dy; - var x1 = sx+dx; - var y1 = sy+dy; - var x, y, d, incrE, incrNE, ddx, ddy; - - if (x1 < x0){ - ddx = -1; - } else { - ddx = 1; - } - if (y1 < y0){ - ddy = -1; - } else { - ddy = 1; - } - dx = x1-x0; - dy = y1-y0; - dx = Math.abs(dx); - dy = Math.abs(dy); - x = x0; - y = y0; - - if (x < width && x >= 0 && y < height && y >= 0) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - if (Math.abs(dx) > Math.abs(dy)) { - d = 2*dy-dx; - incrE = 2*dy; - incrNE = 2*(dy-dx); - - while (x != x1) { - if (d <= 0) - d += incrE; - else { - d += incrNE; - y += ddy; - } - x += ddx; - if (x < width && x >= 0 && y < height && y >= 0) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } else { - d = 2*dx-dy; - incrE = 2*dx; - incrNE = 2*(dx-dy); - - while (y != y1) { - if (d <= 0) - d += incrE; - else { - d += incrNE; - x += ddx; - } - y += ddy; - if (x < width && x >= 0 && y < height && y >= 0) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/maximum.js b/script/separate filters/maximum.js deleted file mode 100644 index 3d22357..0000000 --- a/script/separate filters/maximum.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Replaces every pixel with the maximum RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MaximumFilter(){ - this.name = "Maximum"; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var maxR = 0; - var maxG = 0; - var maxB = 0; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - maxR = Math.max(maxR,inputData[iPixel]); - maxG = Math.max(maxG,inputData[iPixel+1]); - maxB = Math.max(maxB,inputData[iPixel+2]); - } - } - } - } - outputData[pixel] = maxR; - outputData[pixel+1] = maxG; - outputData[pixel+2] = maxB; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/median.js b/script/separate filters/median.js deleted file mode 100644 index e4afbd0..0000000 --- a/script/separate filters/median.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Replaces every pixel with the median RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MedianFilter(){ - this.name = "Median"; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var rList = []; - var gList = []; - var bList = []; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - rList.push(inputData[iPixel]); - gList.push(inputData[iPixel+1]); - bList.push(inputData[iPixel+2]); - - } - } - } - } - rList.sort(function(a,b){return a-b}); - gList.sort(function(a,b){return a-b}); - bList.sort(function(a,b){return a-b}); - outputData[pixel] = rList[4]; - outputData[pixel+1] = gList[4]; - outputData[pixel+2] = bList[4]; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/minimum.js b/script/separate filters/minimum.js deleted file mode 100644 index bce3b28..0000000 --- a/script/separate filters/minimum.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Replaces every pixel with the minimum RGB value of the neighboring pixels. Each color is - * considered separately. - */ -function MinimumFilter(){ - this.name = "Minimum"; - this.defaultValues = { - }; - this.valueRanges = { - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var minR = 255; - var minG = 255; - var minB = 255; - for (var dy = -1; dy <= 1; dy++){ - var iy = y+dy; - if(iy >= 0 && iy < height){ - for (var dx = -1; dx <= 1; dx++){ - var ix = x+dx; - if(ix >= 0 && ix < width){ - var iPixel = (iy*width + ix)*4; - minR = Math.min(minR,inputData[iPixel]); - minG = Math.min(minG,inputData[iPixel+1]); - minB = Math.min(minB,inputData[iPixel+2]); - } - } - } - } - outputData[pixel] = minR; - outputData[pixel+1] = minG; - outputData[pixel+2] = minB; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/noise.js b/script/separate filters/noise.js deleted file mode 100644 index 90ec11d..0000000 --- a/script/separate filters/noise.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Creates random noise on the image, with or without color. - */ -function NoiseFilter(){ - this.name = "Noise"; - this.defaultValues = { - amount : 25, - density : 1, - monochrome : true - }; - this.valueRanges = { - amount : {min:0, max:100}, - density : {min:0, max:1.0}, - monochrome : {min:false, max:true} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var monochrome = (values.monochrome === undefined) ? this.defaultValues.monochrome : values.monochrome; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - if(Math.random() <= density){ - if(monochrome){ - var n = parseInt((2*Math.random()-1) * amount); - inputData[pixel] += n; - inputData[pixel+1] += n; - inputData[pixel+2] += n; - } else { - for(var i = 0; i < 3; i++){ - var n = parseInt((2*Math.random()-1) * amount); - inputData[pixel+i] += n; - } - } - } - } - } - } -} diff --git a/script/separate filters/oil.js b/script/separate filters/oil.js deleted file mode 100644 index 486c121..0000000 --- a/script/separate filters/oil.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Produces an oil painting effect on the image. - * NOTE: This filter can be very slow, especially at higher ranges. Use with caution. - */ -function OilFilter(){ - this.name = "Oil Painting"; - this.defaultValues = { - range : 3, - }; - this.valueRanges = { - range : {min:0, max:5}, - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - if(values === undefined){ values = this.defaultValues; } - var range = (values.range === undefined) ? this.defaultValues.range : values.range; - range = parseInt(range); - var index = 0; - var rHistogram = []; - var gHistogram = []; - var bHistogram = []; - var rTotal = []; - var gTotal = []; - var bTotal = []; - var levels = 256; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - for (var i = 0; i < levels; i++){ - rHistogram[i] = gHistogram[i] = bHistogram[i] = rTotal[i] = gTotal[i] = bTotal[i] = 0; - } - for (var row = -range; row <= range; row++) { - var iy = y+row; - var ioffset; - if (0 <= iy && iy < height) { - ioffset = iy*width; - for (var col = -range; col <= range; col++) { - var ix = x+col; - if (0 <= ix && ix < width) { - var r = inputData[(ioffset+ix)*4] - var g = inputData[(ioffset+ix)*4+1] - var b = inputData[(ioffset+ix)*4+2] - var ri = r*levels/256; - var gi = g*levels/256; - var bi = b*levels/256; - rTotal[ri] += r; - gTotal[gi] += g; - bTotal[bi] += b; - rHistogram[ri]++; - gHistogram[gi]++; - bHistogram[bi]++; - } - } - } - } - var r = 0, g = 0, b = 0; - for (var i = 1; i < levels; i++) { - if (rHistogram[i] > rHistogram[r]){ - r = i; - } - if (gHistogram[i] > gHistogram[g]){ - g = i; - } - if (bHistogram[i] > bHistogram[b]){ - b = i; - } - } - r = rTotal[r] / rHistogram[r]; - g = gTotal[g] / gHistogram[g]; - b = bTotal[b] / bHistogram[b]; - outputData[pixel] = r; - outputData[pixel+1] = g; - outputData[pixel+2] = b; - outputData[pixel+3] = inputData[pixel+3]; - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/opacity.js b/script/separate filters/opacity.js deleted file mode 100644 index 4f87dd3..0000000 --- a/script/separate filters/opacity.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Changes the opacity of the image. - */ -function OpacityFilter(){ - this.name = "Opacity"; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:1.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - inputData[pixel+3] = 255*amount; - } - } - } - -} diff --git a/script/separate filters/pinch.js b/script/separate filters/pinch.js deleted file mode 100644 index abe7c67..0000000 --- a/script/separate filters/pinch.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Pinches and whirls the image toward the center point. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function PinchFilter(){ - this.name = "Pinch/Whirl"; - this.defaultValues = { - amount : 0.5, - radius : 100, - angle : 0, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - amount : {min: -1.0, max: 1.0}, - radius : {min: 1, max: 200}, - angle : {min: 0, max: 360}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - angle = angle/180 * Math.PI; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - if(distance > radius2 || distance == 0){ - out[0] = x; - out[1] = y; - } else { - var d = Math.sqrt( distance / radius2 ); - var t = Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); - - dx *= t; - dy *= t; - - var e = 1 - d; - var a = angle * e * e; - - var s = Math.sin(a); - var c = Math.cos(a); - - out[0] = iCenterX + c*dx - s*dy; - out[1] = iCenterY + s*dx + c*dy; - } - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/pixelation.js b/script/separate filters/pixelation.js deleted file mode 100644 index 58d2a3d..0000000 --- a/script/separate filters/pixelation.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Pixelates the image i.e. divides the image into blocks of color. - */ -function PixelationFilter(){ - this.name = "Pixelation"; - this.defaultValues = { - size : 5 - }; - this.valueRanges = { - size : {min:1, max:50} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - size = parseInt(size); - var pixels = []; - for (var y = 0; y < height; y+=size) { - for (var x = 0; x < width; x+=size) { - var pixel = (y*width + x)*4; - var w = Math.min(size, width-x); - var h = Math.min(size, height-y); - var t = w*h; - var r = 0, g = 0, b = 0; - for(var by = y; by < y+h; by++){ - for(var bx = x; bx < x+w; bx++){ - var bPixel = (by*width + bx)*4; - r += inputData[bPixel]; - g += inputData[bPixel+1]; - b += inputData[bPixel+2]; - } - } - for(var by = y; by < y+h; by++){ - for(var bx = x; bx < x+w; bx++){ - var bPixel = (by*width + bx)*4; - inputData[bPixel] = r/t; - inputData[bPixel+1] = g/t; - inputData[bPixel+2] = b/t; - } - } - } - } - } -} diff --git a/script/separate filters/posterize.js b/script/separate filters/posterize.js deleted file mode 100644 index b536052..0000000 --- a/script/separate filters/posterize.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Posterizes the image, i.e. restricts the color values to a set amount of levels. - */ -function PosterizeFilter(){ - this.name = "Posterize"; - this.defaultValues = { - levels : 6 - }; - this.valueRanges = { - levels : {min:2, max:30 } - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var levels = (values.levels === undefined) ? this.defaultValues.levels : parseInt(values.levels); - if(levels <= 1){ - return; - } - var table = []; - for(var i = 0; i < 256; i++){ - table[i] = parseInt(255 * parseInt(i*levels/256) / (levels-1)); - } - filterUtils.tableFilter(inputData,table,width,height); - } -} diff --git a/script/separate filters/rgbadjust.js b/script/separate filters/rgbadjust.js deleted file mode 100644 index 99c3bf7..0000000 --- a/script/separate filters/rgbadjust.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Adjust the factor of each RGB color value in the image. - */ -function RGBAdjustFilter(){ - this.name = "RGBAdjust"; - this.defaultValues = { - red: 1.0, - green: 1.0, - blue: 1.0 - }; - this.valueRanges = { - red: {min: 0.0, max: 2.0}, - green: {min: 0.0, max: 2.0}, - blue: {min: 0.0, max: 2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var red = (values.red === undefined) ? this.defaultValues.red : values.red; - var green = (values.green === undefined) ? this.defaultValues.green : values.green; - var blue = (values.blue === undefined) ? this.defaultValues.blue : values.blue; - if(red < 0){ red = 0; } - if(green < 0){ green = 0; } - if(blue < 0){ blue = 0; } - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - inputData[pixel] *= red; - inputData[pixel+1] *= green; - inputData[pixel+2] *= blue; - } - } - } -} diff --git a/script/separate filters/saturation.js b/script/separate filters/saturation.js deleted file mode 100644 index 1f46a42..0000000 --- a/script/separate filters/saturation.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Adjusts the saturation value of the image. Values over 1 increase saturation while values below decrease saturation. - * For a true grayscale effect, use the grayscale filter instead. - */ -function SaturationFilter(){ - this.name = "Saturation"; - this.defaultValues = { - amount : 1.0 - }; - this.valueRanges = { - amount : {min:0.0, max:2.0} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var RW = 0.3086; - var RG = 0.6084; - var RB = 0.0820; - var a = (1 - amount) * RW + amount; - var b = (1 - amount) * RW; - var c = (1 - amount) * RW; - var d = (1 - amount) * RG; - var e = (1 - amount) * RG + amount; - var f = (1 - amount) * RG; - var g = (1 - amount) * RB; - var h = (1 - amount) * RB; - var i = (1 - amount) * RB + amount; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - inputData[pixel] = a*inputData[pixel] + d*inputData[pixel+1] + g*inputData[pixel+2]; - inputData[pixel+1] = b*inputData[pixel] + e*inputData[pixel+1] + h*inputData[pixel+2]; - inputData[pixel+2] = c*inputData[pixel] + f*inputData[pixel+1] + i*inputData[pixel+2]; - } - } - } -} diff --git a/script/separate filters/sawtoothripple.js b/script/separate filters/sawtoothripple.js deleted file mode 100644 index 6c2002f..0000000 --- a/script/separate filters/sawtoothripple.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Creates ripples on the image horizontally/vertically in a sawtooth pattern. - */ -function SawtoothRippleFilter(){ - this.name = "Sawtooth Ripples"; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = filterUtils.mod(nx,1) - var fy = filterUtils.mod(ny,1) - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/sepia.js b/script/separate filters/sepia.js deleted file mode 100644 index c8db3f3..0000000 --- a/script/separate filters/sepia.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Creates a sepia effect on the image i.e. gives the image a yellow-brownish tone. - */ -function SepiaFilter(){ - this.name = "Sepia"; - this.defaultValues = { - amount : 10 - }; - this.valueRanges = { - amount : {min:0, max:30} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - amount *= 255/100; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var luma = inputData[pixel]*0.3 + inputData[pixel+1]*0.59 + inputData[pixel+2]*0.11; - var r,g,b; - r = g = b = luma; - r += 40; - g += 20; - b -= amount; - - inputData[pixel] = r; - inputData[pixel+1] = g; - inputData[pixel+2] = b; - } - } - } -} diff --git a/script/separate filters/sharpen.js b/script/separate filters/sharpen.js deleted file mode 100644 index 9669798..0000000 --- a/script/separate filters/sharpen.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Sharpens the image slightly. For increased effect, apply the filter multiple times. - */ -function SharpenFilter(){ - this.name = "Sharpen"; - this.defaultValues = { - }; - this.valueRanges = { - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var matrix = [ 0,-0.2, 0, - -0.2,1.8,-0.2, - 0, -0.2, 0]; - filterUtils.convolveFilter(inputData,matrix,width,height); - } -} diff --git a/script/separate filters/sineripple.js b/script/separate filters/sineripple.js deleted file mode 100644 index df179f1..0000000 --- a/script/separate filters/sineripple.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Creates ripples on the image horizontally/vertically in a sine pattern. - */ -function SineRippleFilter(){ - this.name = "Sine Ripples"; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = Math.sin(nx); - var fy = Math.sin(ny); - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/solarize.js b/script/separate filters/solarize.js deleted file mode 100644 index badc522..0000000 --- a/script/separate filters/solarize.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Produces a solarization effect on the image. - */ -function SolarizeFilter(){ - this.name = "Solarize"; - this.defaultValues = { - }; - this.valueRanges = { - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var table = []; - for(var i = 0; i < 256; i++){ - var val = (i/255 > 0.5) ? 2*(i/255-0.5) : 2*(0.5-i/255) - table[i] = parseInt(255 * val); - } - filterUtils.tableFilter(inputData, table, width, height); - } -} diff --git a/script/separate filters/sparkle.js b/script/separate filters/sparkle.js deleted file mode 100644 index 8e8c921..0000000 --- a/script/separate filters/sparkle.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Generates a sparkle/sunburst effect on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function SparkleFilter(){ - this.name = "Sparkle"; - this.defaultValues = { - rays : 50, - radius : 25, - amount : 50, - randomness : 25, - centerX : 0.5, - centerY : 0.5, - }; - this.valueRanges = { - rays : {min:1, max:100}, - radius : {min:1, max:200}, - amount : {min:0, max:100}, - randomness : {min:0, max:50}, - centerX : {min:0, max:1.0}, - centerY : {min:0, max:1.0}, - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var rays = (values.rays === undefined) ? this.defaultValues.rays : values.rays; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var randomness = (values.randomness === undefined) ? this.defaultValues.randomness : values.randomness; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var iCenterX = centerX * width; - var iCenterY = centerY * height; - var rayLengths = []; - for(var i = 0; i < rays; i++){ - rayLengths[i]= radius + randomness / 100 * radius * filterUtils.gaussianRandom(); - } - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - var angle = Math.atan2(dy,dx); - var d = (angle+Math.PI) / (Math.PI*2) * rays; - var i = parseInt(d); - var f = d - i; - if(radius != 0){ - var length = filterUtils.linearInterpolate(f, rayLengths[i % rays], rayLengths[(i+1) % rays]); - var g = length*length / (distance+0.0001); - g = Math.pow(g, (100-amount) / 50); - f -= 0.5; - f = 1 - f*f; - f *= g; - } - f = filterUtils.clampPixel(f,0,1); - var mixedRGB = filterUtils.mixColors(f,[inputData[pixel],inputData[pixel+1],inputData[pixel+2],inputData[pixel+3]],[255,255,255,255]); - for(var k = 0; k < 3; k++){ - inputData[pixel+k] = mixedRGB[k]; - } - } - } - } -} diff --git a/script/separate filters/squaresmear.js b/script/separate filters/squaresmear.js deleted file mode 100644 index f3e299c..0000000 --- a/script/separate filters/squaresmear.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Smears out the image with square shapes to create a painting style effect. - * The mix values sets the intensity of the effect. - * NOTE: This filter can be very slow, especially at higher densities/sizes. Use with caution. - */ -function SquareSmearFilter(){ - this.name = "Square Smear"; - this.defaultValues = { - size : 4, - density : 0.5, - mix : 0.5, - }; - this.valueRanges = { - size : {min:1, max:10}, - density : {min:0.0, max:1.0}, - mix : {min:0.0, max:1.0}, - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - for(var k = 0; k < inputData.length; k++){ - outputData[k] = inputData[k]; - } - if(values === undefined){ values = this.defaultValues; } - var size = (values.size === undefined) ? this.defaultValues.size : values.size; - if(size < 1){ size = 1;} - size = parseInt(size); - var density = (values.density === undefined) ? this.defaultValues.density : values.density; - var mix = (values.mix === undefined) ? this.defaultValues.mix : values.mix; - var radius = size+1; - var radius2 = radius*radius; - var numShapes = parseInt(2*density/30*width*height / 2); - for(var i = 0; i < numShapes; i++){ - var sx = (Math.random()*Math.pow(2,32) & 0x7fffffff) % width; - var sy = (Math.random()*Math.pow(2,32) & 0x7fffffff) % height; - var rgb2 = [inputData[(sy*width+sx)*4],inputData[(sy*width+sx)*4+1],inputData[(sy*width+sx)*4+2],inputData[(sy*width+sx)*4+3]]; - for(var x = sx - radius; x < sx + radius + 1; x++){ - - for(var y = sy - radius; y < sy + radius + 1; y++){ - if (x >= 0 && x < width && y >= 0 && y < height) { - var rgb1 = [outputData[(y*width+x)*4],outputData[(y*width+x)*4+1],outputData[(y*width+x)*4+2],outputData[(y*width+x)*4+3]]; - var mixedRGB = filterUtils.mixColors(mix,rgb1,rgb2) - for(var k = 0; k < 3; k++){ - outputData[(y*width+x)*4+k] = mixedRGB[k]; - } - } - } - } - } - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/threshold.js b/script/separate filters/threshold.js deleted file mode 100644 index 7572077..0000000 --- a/script/separate filters/threshold.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Divides the colors into black and white following the treshold value. Brightnesses above the threshold - * sets the color to white while values below the threshold sets the color to black. - */ -function ThresholdFilter(){ - this.name = "Black & White"; - this.defaultValues = { - threshold : 127 - }; - this.valueRanges = { - threshold : {min:0, max:255} - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var threshold = (values.threshold === undefined) ? this.defaultValues.threshold : values.threshold; - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var pixel = (y*width + x)*4; - var brightness = (inputData[pixel] + inputData[pixel+1] + inputData[pixel+2])/3 - var colorVal = 0; - if(brightness > threshold){ - colorVal = 255; - } - inputData[pixel] = inputData[pixel+1] = inputData[pixel+2] = colorVal; - } - } - } -} diff --git a/script/separate filters/triangleripple.js b/script/separate filters/triangleripple.js deleted file mode 100644 index c075dae..0000000 --- a/script/separate filters/triangleripple.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Creates ripples on the image horizontally/vertically in a sine pattern. - */ -function TriangleRippleFilter(){ - this.name = "Triangle Ripples"; - this.defaultValues = { - xAmplitude : 5, - yAmplitude : 5, - xWavelength : 16, - yWavelength : 16 - }; - this.valueRanges = { - xAmplitude : {min:0, max:30}, - yAmplitude : {min:0, max:30}, - xWavelength : {min:1, max:50}, - yWavelength : {min:1, max:50} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var xAmplitude = (values.xAmplitude === undefined) ? this.defaultValues.xAmplitude : values.xAmplitude; - var yAmplitude = (values.yAmplitude === undefined) ? this.defaultValues.yAmplitude : values.yAmplitude; - var xWavelength = (values.xWavelength === undefined) ? this.defaultValues.xWavelength : values.xWavelength; - var yWavelength = (values.yWavelength === undefined) ? this.defaultValues.yWavelength : values.yWavelength; - var transInverse = function(x,y,out){ - var nx = y/xWavelength; - var ny = x/yWavelength; - var fx = filterUtils.triangle(nx,1) - var fy = filterUtils.triangle(ny,1) - out[0] = x + xAmplitude * fx; - out[1] = y + yAmplitude * fy; - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/twirl.js b/script/separate filters/twirl.js deleted file mode 100644 index 836a0d0..0000000 --- a/script/separate filters/twirl.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Twists the image around a given center point. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function TwirlFilter(){ - this.name = "Twirl"; - this.defaultValues = { - radius : 100, - angle : 180, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - radius : {min: 1, max: 200}, - angle : {min: 0, max: 360}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var angle = (values.angle === undefined) ? this.defaultValues.angle : values.angle; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - angle = angle/180 * Math.PI; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance = dx*dx + dy*dy; - if(distance > radius2){ - out[0] = x; - out[1] = y; - } else { - distance = Math.sqrt(distance); - var a = Math.atan2(dy, dx) + angle * (radius-distance) / radius; - out[0] = iCenterX + distance*Math.cos(a); - out[1] = iCenterY + distance*Math.sin(a); - } - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} diff --git a/script/separate filters/vignette.js b/script/separate filters/vignette.js deleted file mode 100644 index 7ae5020..0000000 --- a/script/separate filters/vignette.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Creates a classical vignette effect on the image i.e. darkens the corners. - */ -function VignetteFilter(){ - this.name = "Vignette"; - this.defaultValues = { - amount : 0.3, - }; - this.valueRanges = { - amount : {min:0.0, max:1.0}, - }; - this.filter = function(input,values){ - var width = input.width, height = input.height; - var inputData = input.data; - var outputData = []; - if(values === undefined){ values = this.defaultValues; } - var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var canvas = document.createElement("canvas"); - canvas.width = width; - canvas.height = height; - var context = canvas.getContext("2d"); - var gradient; - var radius = Math.sqrt( Math.pow(width/2, 2) + Math.pow(height/2, 2) ); - context.putImageData(input,0,0); - context.globalCompositeOperation = 'source-over'; - - gradient = context.createRadialGradient(width/2, height/2, 0, width/2, height/2, radius); - gradient.addColorStop(0, 'rgba(0,0,0,0)'); - gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); - gradient.addColorStop(1, 'rgba(0,0,0,' + amount + ')'); - context.fillStyle = gradient; - context.fillRect(0, 0, width, height); - outputData = context.getImageData(0,0,width,height).data; - for(var k = 0; k < outputData.length; k++){ - inputData[k] = outputData[k]; - } - } -} diff --git a/script/separate filters/waterripple.js b/script/separate filters/waterripple.js deleted file mode 100644 index 3717986..0000000 --- a/script/separate filters/waterripple.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Produces a water ripple/waves on the image. CenterX and CenterY specify the - * position in terms of ratios of width and height. - */ -function WaterRippleFilter(){ - this.name = "Water Ripples"; - this.defaultValues = { - phase : 0, - radius : 50, - wavelength : 16, - amplitude : 10, - centerX : 0.5, - centerY : 0.5 - }; - this.valueRanges = { - phase : {min: 0, max: 100}, - radius : {min: 1, max: 200}, - wavelength : {min: 1, max: 100}, - amplitude : {min: 1, max: 100}, - centerX : {min: 0.0, max:1.0}, - centerY : {min: 0.0, max:1.0} - }; - if(!FilterUtils){ - if(console){ - console.error("Unable to find filterutils.js, please include this file! (Required by " + this.name + " filter)"); - } - return; - } - var filterUtils = new FilterUtils(); - - this.filter = function (input, values){ - var width = input.width, height = input.height; - var inputData = input.data; - if(values === undefined){ values = this.defaultValues; } - var wavelength = (values.wavelength === undefined) ? this.defaultValues.wavelength : values.wavelength; - var amplitude = (values.amplitude === undefined) ? this.defaultValues.amplitude : values.amplitude; - var phase = (values.phase === undefined) ? this.defaultValues.phase : values.phase; - var centerX = (values.centerX === undefined) ? this.defaultValues.centerX : values.centerX; - var centerY = (values.centerY === undefined) ? this.defaultValues.centerY : values.centerY; - var radius = (values.radius === undefined) ? this.defaultValues.radius : values.radius; - var radius2 = radius*radius; - var iCenterX = width * centerX; var iCenterY = height * centerY; - var transInverse = function(x,y,out){ - var dx = x-iCenterX; - var dy = y-iCenterY; - var distance2 = dx*dx + dy*dy; - if(distance2 > radius2){ - out[0] = x; - out[1] = y; - } else { - var distance = Math.sqrt(distance2); - var amount = amplitude * Math.sin(distance/wavelength * Math.PI * 2 - phase); - amount *= (radius-distance)/radius; - if(distance != 0){ - amount *= wavelength/distance; - } - out[0] = x + dx*amount; - out[1] = y + dy*amount; - } - } - filterUtils.transformFilter(inputData,transInverse,width,height); - } -} From 69a015053091287817efe5977db98e3aa229c0d7 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Tue, 15 Sep 2015 15:42:59 -0700 Subject: [PATCH 4/8] Error in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d70f05e..c8ad4ce 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "homepage": "http://joelb.me/jsmanipulate/", "repository": { "type": "git", - "url": "https://github.com/dholtzmann/JSManipulate.git", + "url": "https://github.com/dholtzmann/JSManipulate.git" }, "dependencies": { "canvas": "1.2.7" From fdee9ae0e20811229fce68ec5cc5a6deffe59292 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Fri, 18 Sep 2015 01:00:50 -0700 Subject: [PATCH 5/8] Changed dependency version number in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8ad4ce..d5f1bca 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/dholtzmann/JSManipulate.git" }, "dependencies": { - "canvas": "1.2.7" + "canvas": "~1.2.7" }, "engines": { "node": "*" } } From 06d8eccdab78e3832c588e8db1a598dfeeff66f7 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Fri, 18 Sep 2015 01:02:11 -0700 Subject: [PATCH 6/8] Changed dependency version number in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5f1bca..60af342 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/dholtzmann/JSManipulate.git" }, "dependencies": { - "canvas": "~1.2.7" + "canvas": "~1.2.9" }, "engines": { "node": "*" } } From 8898cd998ce8aa6916be5f01b4b47c905056f056 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Sun, 20 Sep 2015 22:28:31 -0700 Subject: [PATCH 7/8] Noticed a call to document.createElement() for canvas. Updated to also support node-canvas --- filters/vignette.js | 3 ++- jsmanipulate.js | 7 +++---- minified/jsmanipulate.min.js | 4 ++-- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/filters/vignette.js b/filters/vignette.js index 7ae5020..1540136 100644 --- a/filters/vignette.js +++ b/filters/vignette.js @@ -15,7 +15,8 @@ function VignetteFilter(){ var outputData = []; if(values === undefined){ values = this.defaultValues; } var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var canvas = document.createElement("canvas"); + if(typeof module !== "undefined" && typeof module.exports !== "undefined"){ var canvas = require("canvas"); } // nodejs + else{ var canvas = document.createElement("canvas"); } // browser canvas.width = width; canvas.height = height; var context = canvas.getContext("2d"); diff --git a/jsmanipulate.js b/jsmanipulate.js index 5fd1e47..87ebb9c 100644 --- a/jsmanipulate.js +++ b/jsmanipulate.js @@ -2182,7 +2182,8 @@ function VignetteFilter(){ var outputData = []; if(values === undefined){ values = this.defaultValues; } var amount = (values.amount === undefined) ? this.defaultValues.amount : values.amount; - var canvas = document.createElement("canvas"); + if(typeof module !== "undefined" && typeof module.exports !== "undefined"){ var canvas = require("canvas"); } // nodejs + else{ var canvas = document.createElement("canvas"); } // browser canvas.width = width; canvas.height = height; var context = canvas.getContext("2d"); @@ -2310,7 +2311,5 @@ var JSManipulate = { }; // node.js -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){ - module.exports = JSManipulate; -} +if (typeof module !== "undefined" && typeof module.exports !== "undefined"){ module.exports = JSManipulate; } diff --git a/minified/jsmanipulate.min.js b/minified/jsmanipulate.min.js index b67424a..1013c6b 100644 --- a/minified/jsmanipulate.min.js +++ b/minified/jsmanipulate.min.js @@ -11,5 +11,5 @@ MIT LICENSED (http://www.opensource.org/licenses/mit-license.php) Copyright (c) 2011, Joel Besada ========================================================================= */ -function FilterUtils(){this.HSVtoRGB=function(t,a,e){var i,r,n,s=Math.floor(6*t),l=6*t-s,h=e*(1-a),o=e*(1-l*a),u=e*(1-(1-l)*a);switch(s%6){case 0:i=e,r=u,n=h;break;case 1:i=o,r=e,n=h;break;case 2:i=h,r=e,n=u;break;case 3:i=h,r=o,n=e;break;case 4:i=u,r=h,n=e;break;case 5:i=e,r=h,n=o}return[255*i,255*r,255*n]},this.RGBtoHSV=function(t,a,e){t/=255,a/=255,e/=255;var i,r,n=Math.max(t,a,e),s=Math.min(t,a,e),l=n,h=n-s;if(r=0===n?0:h/n,n===s)i=0;else{switch(n){case t:i=(a-e)/h+(e>a?6:0);break;case a:i=(e-t)/h+2;break;case e:i=(t-a)/h+4}i/=6}return[i,r,l]},this.getPixel=function(t,a,e,i,r){var n=4*(e*i+a);return 0>a||a>=i||0>e||e>=r?[t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+1],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+2],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+3]]:[t[n],t[n+1],t[n+2],t[n+3]]};var t,a=!1;this.gaussianRandom=function(){if(a)return a=!1,t;var e,i,r;do e=2*Math.random()-1,i=2*Math.random()-1,r=e*e+i*i;while(r>=1||0===r);var n=Math.sqrt(-2*Math.log(r)/r);return t=i*n,a=!0,e*n},this.clampPixel=function(t,a,e){return a>t?a:t>e?e:t},this.triangle=function(t){var a=this.mod(t,1);return 2*(.5>a?a:1-a)},this.mod=function(t,a){var e=parseInt(t/a,10);return t-=e*a,0>t?t+a:t},this.mixColors=function(t,a,e){var i=this.linearInterpolate(t,a[0],e[0]),r=this.linearInterpolate(t,a[1],e[1]),n=this.linearInterpolate(t,a[2],e[2]),s=this.linearInterpolate(t,a[3],e[3]);return[i,r,n,s]},this.linearInterpolate=function(t,a,e){return a+t*(e-a)},this.bilinearInterpolate=function(t,a,e,i,r,n){var s,l,h=e[0],o=e[1],u=e[2],d=e[3],f=i[0],v=i[1],m=i[2],c=i[3],g=r[0],p=r[1],x=r[2],V=r[3],w=n[0],F=n[1],M=n[2],b=n[3],I=1-t,A=1-a;s=I*d+t*c,l=I*V+t*b;var y=A*s+a*l;s=I*h+t*f,l=I*g+t*w;var R=A*s+a*l;s=I*o+t*v,l=I*p+t*F;var D=A*s+a*l;s=I*u+t*m,l=I*x+t*M;var S=A*s+a*l;return[R,D,S,y]},this.tableFilter=function(t,a,e,i){for(var r=0;i>r;r++)for(var n=0;e>n;n++)for(var s=4*(r*e+n),l=0;3>l;l++)t[s+l]=a[t[s+l]]},this.convolveFilter=function(t,a,e,i){var r,n,s=[];r=n=Math.sqrt(a.length);for(var l=parseInt(r/2,10),h=parseInt(n/2,10),o=0;i>o;o++)for(var u=0;e>u;u++){for(var d=4*(o*e+u),f=0,v=0,m=0,c=-l;l>=c;c++){var g,p=o+c;g=p>=0&&i>p?p*e:o*e;for(var x=n*(c+l)+h,V=-h;h>=V;V++){var w=a[x+V];if(0!==w){var F=u+V;F>=0&&e>F||(F=u);var M=4*(g+F);f+=w*t[M],v+=w*t[M+1],m+=w*t[M+2]}}}s[d]=parseInt(f+.5,10),s[d+1]=parseInt(v+.5,10),s[d+2]=parseInt(m+.5,10),s[d+3]=t[d+3]}for(var b=0;bl;l++)for(var h=0;e>h;h++){var o=4*(l*e+h);a.apply(this,[h,l,r]);var u,d,f,v,m=Math.floor(r[0]),c=Math.floor(r[1]),g=r[0]-m,p=r[1]-c;if(m>=0&&e-1>m&&c>=0&&i-1>c){var x=4*(e*c+m);u=[t[x],t[x+1],t[x+2],t[x+3]],d=[t[x+4],t[x+5],t[x+6],t[x+7]],f=[t[x+4*e],t[x+4*e+1],t[x+4*e+2],t[x+4*e+3]],v=[t[x+4*(e+1)],t[x+4*(e+1)+1],t[x+4*(e+1)+2],t[x+4*(e+1)+3]]}else u=this.getPixel(t,m,c,e,i),d=this.getPixel(t,m+1,c,e,i),f=this.getPixel(t,m,c+1,e,i),v=this.getPixel(t,m+1,c+1,e,i);var V=this.bilinearInterpolate(g,p,u,d,f,v);n[o]=V[0],n[o+1]=V[1],n[o+2]=V[2],n[o+3]=V[3]}for(var w=0;wl&&(l=0),e=l>=2.5?.98711*l-.9633:l>=.5?3.97156-4.14554*Math.sqrt(1-.26891*l):2*l*(3.97156-4.14554*Math.sqrt(.865545));var h,o,u,d,f,v,m=e*e,c=m*e,g=1.57825+2.44413*e+1.4281*m+.422205*c,p=(2.44413*e+2.85619*m+1.26661*c)/g,x=-(1.4281*m+1.26661*c)/g,V=.422205*c/g,w=1-(p+x+V),F=0;for(F=0;3>F;F++)for(var M=0;n>M;M++){for(h=M*r+F,o=M*r+(i-1<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=M*r+(i-1<<2)+F,o=M*r+F,u=s[h],d=u,f=d,v=f;h>=o;h-=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}for(F=0;3>F;F++)for(var b=0;i>b;b++){for(h=(b<<2)+F,o=(n-1)*r+(b<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=(n-1)*r+(b<<2)+F,o=(b<<2)+F,u=s[h],d=u,f=d,v=f;h>=o;h-=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}}}function BrightnessFilter(){this.name="Brightness",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);u[2]+=s,u[2]<0?u[2]=0:u[2]>1&&(u[2]=1);for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function BumpFilter(){this.name="Bump",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[-1,-1,0,-1,1,1,0,1,1];t.convolveFilter(r,n,e,i)}}function CircleSmearFilter(){this.name="Circle Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=d*d,v=parseInt(2*o/30*i*r/2,10),m=0;v>m;m++)for(var c=(Math.random()*Math.pow(2,32)&2147483647)%i,g=(Math.random()*Math.pow(2,32)&2147483647)%r,p=[n[4*(g*i+c)],n[4*(g*i+c)+1],n[4*(g*i+c)+2],n[4*(g*i+c)+3]],x=c-d;c+d+1>x;x++)for(var V=g-d;g+d+1>V;V++){var w=(x-c)*(x-c)+(V-g)*(V-g);if(x>=0&&i>x&&V>=0&&r>V&&f>=w)for(var F=[s[4*(V*i+x)],s[4*(V*i+x)+1],s[4*(V*i+x)+2],s[4*(V*i+x)+3]],M=t.mixColors(u,F,p),b=0;3>b;b++)s[4*(V*i+x)+b]=M[b]}for(var I=0;Is&&(s=0);for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*((h/255-.5)*s+.5),10);t.tableFilter(n,l,i,r)}}function CrossSmearFilter(){this.name="Cross Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,mix:.5},this.valueRanges={distance:{min:0,max:30},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=0),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=parseInt(2*o*i*r/(h+1),10),f=0;d>f;f++){for(var v,m,c,g=(Math.random()*Math.pow(2,32)&2147483647)%i,p=(Math.random()*Math.pow(2,32)&2147483647)%r,x=Math.random()*Math.pow(2,32)%h+1,V=[n[4*(p*i+g)],n[4*(p*i+g)+1],n[4*(p*i+g)+2],n[4*(p*i+g)+3]],w=g-x;g+x+1>w;w++)if(w>=0&&i>w)for(v=[s[4*(p*i+w)],s[4*(p*i+w)+1],s[4*(p*i+w)+2],s[4*(p*i+w)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(p*i+w)+c]=m[c];for(var F=p-x;p+x+1>F;F++)if(F>=0&&r>F)for(v=[s[4*(F*i+g)],s[4*(F*i+g)+1],s[4*(F*i+g)+2],s[4*(F*i+g)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(F*i+g)+c]=m[c]}for(var M=0;Mo;o++){var u=2*Math.PI*o/256;l[o]=s*Math.sin(u),h[o]=s*Math.cos(u)}transInverse=function(t,a,e){var i=parseInt(255*Math.random(),10),r=Math.random();e[0]=t+r*l[i],e[1]=a+r*h[i]},t.transformFilter(n,transInverse,i,r)}}function DitherFilter(){this.name="Dither",this.isDirAnimatable=!1,this.defaultValues={levels:3,color:!0},this.valueRanges={levels:{min:2,max:30},color:{min:!1,max:!0}};new FilterUtils;this.filter=function(t,a){var e,i,r=t.width,n=t.height,s=t.data,l=[];for(i=0;i=h&&(h=1);var u=[0,0,0,0,0,7,3,5,1],d=16,f=0,v=[];for(e=0;h>e;e++)v[e]=parseInt(255*e/(h-1),10);var m=[];for(e=0;256>e;e++)m[e]=parseInt(h*e/256,10);for(var c=0;n>c;c++){var g,p=1==(1&c);p?(f=4*(c*r+r-1),g=-1):(f=c*r*4,g=1);for(var x=0;r>x;x++){var V=s[f],w=s[f+1],F=s[f+2];o||(V=w=F=parseInt((V+w+F)/3,10));var M=v[m[V]],b=v[m[w]],I=v[m[F]];l[f]=M,l[f+1]=b,l[f+2]=I,l[f+3]=s[f+3];var A=V-M,y=w-b,R=F-I;for(e=-1;1>=e;e++){var D=e+c;if(D>=0&&n>D)for(i=-1;1>=i;i++){var S=i+x;if(S>=0&&r>S){var P;if(P=p?u[3*(e+1)-i+1]:u[3*(e+1)+i+1],0!==P){var W=p?f-4*i:f+4*i;V=s[W],w=s[W+1],F=s[W+2];var U=P/d;V+=A*U,w+=y*U,F+=R*U,s[W]=V,s[W+1]=w,s[W+2]=F}}}}f+=4*g}}for(i=0;il;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=0;gh=0,bh=0;var d=0;gv=0,bv=0;for(var f=-1;1>=f;f++){var v,m=l+f;v=m>=0&&r>m?m*i*4:l*i*4;for(var c=3*(f+1)+1,g=-1;1>=g;g++){var p=h+g;p>=0&&i>p||(p=h),p*=4;var x=n[v+p],V=n[v+p+1],w=n[v+p+2],F=t[c+g],M=a[c+g];u+=parseInt(F*x,10),bh+=parseInt(F*V,10),gh+=parseInt(F*w,10),d+=parseInt(M*x,10),gv+=parseInt(M*V,10),bv+=parseInt(M*w,10)}}x=parseInt(Math.sqrt(u*u+d*d)/1.8,10),V=parseInt(Math.sqrt(gh*gh+gv*gv)/1.8,10),w=parseInt(Math.sqrt(bh*bh+bv*bv)/1.8,10),s[o]=x,s[o+1]=V,s[o+2]=w,s[o+3]=n[o+3]}for(var b=0;bA;A++,I+=d)for(var y=I,R=y+d,D=R+d,S=0;e>S;S++,y++,R++,D++){var P=4*(A*e+S);0!==A&&i-2>A&&0!==S&&e-2>S?(v=u[y-1]+u[R-1]+u[D-1]-u[y+1]-u[R+1]-u[D+1],m=u[D-1]+u[D]+u[D+1]-u[y-1]-u[y]-u[y+1],M=0===v&&0===m?b:(F=v*g+m*p+w)<0?0:parseInt(F/Math.sqrt(v*v+m*m+V),10)):M=b,r[P]=r[P+1]=r[P+2]=M}}}function ExposureFilter(){this.name="Exposure",this.isDirAnimatable=!0,this.defaultValues={exposure:1},this.valueRanges={exposure:{min:0,max:5}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.exposure?this.defaultValues.exposure:e.exposure,l=[],h=0;256>h;h++)l[h]=parseInt(255*(1-Math.exp(-(h/255)*s)),10);t.tableFilter(n,l,i,r)}}function GainFilter(){this.name="Gain/Bias",this.isDirAnimatable=!0,this.defaultValues={gain:.5,bias:.5},this.valueRanges={gain:{min:0,max:1},bias:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.gain?this.defaultValues.gain:e.gain,l=void 0===e.bias?this.defaultValues.bias:e.bias,h=[],o=0;256>o;o++){var u=o/255,d=(1/s-2)*(1-2*u);u=.5>u?u/(d+1):(d-u)/(d-1),u/=(1/l-2)*(1-u)+1,h[o]=parseInt(255*u,10)}t.tableFilter(n,h,i,r)}}function GammaFilter(){this.name="Gamma",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;if(0>n&&(n=0),!FilterUtils)return void(console&&console.error("Unable to find filterutils.js, please include this file! (Required by "+this.name+" filter)"));for(var s=new FilterUtils,l=[],h=0;256>h;h++)l[h]=255*Math.pow(h/255,1/n)+.5;s.tableFilter(r,l,e,i)}}function GrayscaleFilter(){this.name="Grayscale",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++){var s=4*(r*a+n),l=.3*i[s]+.59*i[s+1]+.11*i[s+2];i[s]=i[s+1]=i[s+2]=l}}}function HueFilter(){this.name="Hue",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);for(u[0]+=s;u[0]<0;)u[0]+=360;for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function InvertFilter(){this.name="Invert",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++)for(var s=4*(r*a+n),l=0;3>l;l++)i[s+l]=255-i[s+l]}}function KaleidoscopeFilter(){this.name="Kaleidoscope",this.isDirAnimatable=!1,this.defaultValues={angle:0,rotation:0,sides:3,centerX:.5,centerY:.5},this.valueRanges={angle:{min:0,max:360},rotation:{min:0,max:360},sides:{min:1,max:30},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.rotation?this.defaultValues.rotation:e.rotation,h=void 0===e.sides?this.defaultValues.sides:e.sides,o=void 0===e.centerX?this.defaultValues.centerX:e.centerX,u=void 0===e.centerY?this.defaultValues.centerY:e.centerY,d=i*o,f=r*u;s=s/180*Math.PI,l=l/180*Math.PI;var v=function(a,e,i){var r=a-d,n=e-f,o=Math.sqrt(r*r+n*n),u=Math.atan2(n,r)-s-l;u=t.triangle(u/Math.PI*h*.5),u+=s,i[0]=d+o*Math.cos(u),i[1]=f+o*Math.sin(u)};t.transformFilter(n,v,i,r)}}function LensDistortionFilter(){this.name="Lens Distortion",this.isDirAnimatable=!1,this.defaultValues={refraction:1.5,radius:50,centerX:.5,centerY:.5},this.valueRanges={refraction:{min:1,max:10},radius:{min:1,max:200},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.refraction?this.defaultValues.refraction:e.refraction,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o,d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i,l=r*r;if(l>=u-u*n/u)e[0]=t,e[1]=a;else{var h=1/s,o=Math.sqrt((1-n/u-l/u)*u),v=o*o,m=Math.acos(i/Math.sqrt(n+v)),c=Math.PI/2-m,g=Math.asin(Math.sin(c)*h);g=Math.PI/2-m-g,e[0]=t-Math.tan(g)*o;var p=Math.acos(r/Math.sqrt(l+v));c=Math.PI/2-p,g=Math.asin(Math.sin(c)*h),g=Math.PI/2-p-g,e[1]=a-Math.tan(g)*o}};t.transformFilter(n,v,i,r)}}function LineSmearFilter(){this.name="Line Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,angle:0,mix:.5},this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.angle?this.defaultValues.angle:e.angle,d=void 0===e.mix?this.defaultValues.mix:e.mix;u=u/180*Math.PI;for(var f=Math.sin(u),v=Math.cos(u),m=parseInt(2*o*r*n/2,10),c=0;m>c;c++){var g,p,x,V,w,F,M,b=(Math.random()*Math.pow(2,32)&2147483647)%r,I=(Math.random()*Math.pow(2,32)&2147483647)%n,A=(Math.random()*Math.pow(2,32)&2147483647)%h+1,y=[s[4*(I*r+b)],s[4*(I*r+b)+1],s[4*(I*r+b)+2],s[4*(I*r+b)+3]],R=parseInt(A*v,10),D=parseInt(A*f,10),S=b-R,P=I-D,W=b+R,U=I+D;F=S>W?-1:1,M=P>U?-1:1,R=W-S,D=U-P,R=Math.abs(R),D=Math.abs(D),g=S,p=P;var X,Y;if(r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i];if(Math.abs(R)>Math.abs(D)){for(x=2*D-R,V=2*D,w=2*(D-R);g!=W;)if(0>=x?x+=V:(x+=w,p+=M),g+=F,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}else for(x=2*R-D,V=2*R,w=2*(R-D);p!=U;)if(0>=x?x+=V:(x+=w,g+=F),p+=M,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,y),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}for(i=0;in;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=0,o=0,u=0,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.max(h,i[c]),o=Math.max(o,i[c+1]),u=Math.max(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=[],o=[],u=[],d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h.push(i[c]),o.push(i[c+1]),u.push(i[c+2])}}}var g=function(t,a){return t-a};h.sort(g),o.sort(g),u.sort(g),r[l]=h[4],r[l+1]=o[4],r[l+2]=u[4],r[l+3]=i[l+3]}for(var p=0;pn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=255,o=255,u=255,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.min(h,i[c]),o=Math.min(o,i[c+1]),u=Math.min(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gh;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);if(Math.random()<=s){var d;if(l)d=parseInt((2*Math.random()-1)*n,10),r[u]+=d,r[u+1]+=d,r[u+2]+=d;else for(var f=0;3>f;f++)d=parseInt((2*Math.random()-1)*n,10),r[u+f]+=d}}}}function OilFilter(){this.name="Oil Painting",this.isDirAnimatable=!1,this.defaultValues={range:3},this.valueRanges={range:{min:0,max:5}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.range?this.defaultValues.range:a.range;s=parseInt(s,10);for(var l=[],h=[],o=[],u=[],d=[],f=[],v=256,m=0;i>m;m++)for(var c=0;e>c;c++){for(var g=4*(m*e+c),p=0;v>p;p++)l[p]=h[p]=o[p]=u[p]=d[p]=f[p]=0;for(var x=-s;s>=x;x++){var V,w=m+x;if(w>=0&&i>w){V=w*e;for(var F=-s;s>=F;F++){var M=c+F;if(M>=0&&e>M){var b=r[4*(V+M)],I=r[4*(V+M)+1],A=r[4*(V+M)+2],y=b*v/256,R=I*v/256,D=A*v/256;u[y]+=b,d[R]+=I,f[D]+=A,l[y]++,h[R]++,o[D]++}}}}for(var S=0,P=0,W=0,U=1;v>U;U++)l[U]>l[S]&&(S=U),h[U]>h[P]&&(P=U),o[U]>o[W]&&(W=U);S=u[S]/l[S],P=d[P]/h[P],W=f[W]/o[W],n[g]=S,n[g+1]=P,n[g+2]=W,n[g+3]=r[g+3]}for(var X=0;Xs;s++)for(var l=0;e>l;l++){var h=4*(s*e+l);r[h+3]=255*n}}}function PinchFilter(){this.name="Pinch/Whirl",this.isDirAnimatable=!1,this.defaultValues={amount:.5,radius:100,angle:0,centerX:.5,centerY:.5},this.valueRanges={amount:{min:-1,max:1},radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=void 0===e.angle?this.defaultValues.angle:e.angle,h=void 0===e.centerX?this.defaultValues.centerX:e.centerX,o=void 0===e.centerY?this.defaultValues.centerY:e.centerY,u=void 0===e.radius?this.defaultValues.radius:e.radius,d=u*u;l=l/180*Math.PI;var f=i*h,v=r*o,m=function(t,a,e){var i=t-f,r=a-v,n=i*i+r*r;if(n>d||0===n)e[0]=t,e[1]=a;else{var h=Math.sqrt(n/d),o=Math.pow(Math.sin(.5*Math.PI*h),-s);i*=o,r*=o;var u=1-h,m=l*u*u,c=Math.sin(m),g=Math.cos(m);e[0]=f+g*i-c*r,e[1]=v+c*i+g*r}};t.transformFilter(n,m,i,r)}}function PixelationFilter(){this.name="Pixelation",this.isDirAnimatable=!1,this.defaultValues={size:5},this.valueRanges={size:{min:1,max:50}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.size?this.defaultValues.size:a.size;n=parseInt(n,10);for(var s,l,h,o=0;i>o;o+=n)for(var u=0;e>u;u+=n){var d=Math.min(n,e-u),f=Math.min(n,i-o),v=d*f,m=0,c=0,g=0;for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),m+=r[h],c+=r[h+1],g+=r[h+2];for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),r[h]=m/v,r[h+1]=c/v,r[h+2]=g/v}}}function PosterizeFilter(){this.name="Posterize",this.isDirAnimatable=!1,this.defaultValues={levels:6},this.valueRanges={levels:{min:2,max:30}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.levels?this.defaultValues.levels:parseInt(e.levels,10);if(!(1>=s)){for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*parseInt(h*s/256,10)/(s-1),10);t.tableFilter(n,l,i,r)}}}function RGBAdjustFilter(){this.name="RGBAdjust",this.isDirAnimatable=!0,this.defaultValues={red:1,green:1,blue:1},this.valueRanges={red:{min:0,max:2},green:{min:0,max:2},blue:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.red?this.defaultValues.red:a.red,s=void 0===a.green?this.defaultValues.green:a.green,l=void 0===a.blue?this.defaultValues.blue:a.blue;0>n&&(n=0),0>s&&(s=0),0>l&&(l=0);for(var h=0;i>h;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);r[u]*=n,r[u+1]*=s,r[u+2]*=l}}}function SaturationFilter(){this.name="Saturation",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);for(var n=void 0===a.amount?this.defaultValues.amount:a.amount,s=.3,l=.59,h=.11,o=(1-n)*s+n,u=(1-n)*s,d=(1-n)*s,f=(1-n)*l,v=(1-n)*l+n,m=(1-n)*l,c=(1-n)*h,g=(1-n)*h,p=(1-n)*h+n,x=0;i>x;x++)for(var V=0;e>V;V++){var w=4*(x*e+V),F=r[w],M=r[w+1],b=r[w+2];r[w]=o*F+f*M+c*b,r[w+1]=u*F+v*M+g*b,r[w+2]=d*F+m*M+p*b}}}function SawtoothRippleFilter(){this.name="Sawtooth Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.mod(r,1),d=t.mod(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function SepiaFilter(){this.name="Sepia",this.isDirAnimatable=!0,this.defaultValues={amount:10},this.valueRanges={amount:{min:0,max:30}};new FilterUtils;this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;n*=2.55;for(var s=0;i>s;s++)for(var l=0;e>l;l++){var h,o,u,d=4*(s*e+l),f=.3*r[d]+.59*r[d+1]+.11*r[d+2];h=o=u=f,h+=40,o+=20,u-=n,r[d]=h,r[d+1]=o,r[d+2]=u}}}function SharpenFilter(){this.name="Sharpen",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[0,-.2,0,-.2,1.8,-.2,0,-.2,0];t.convolveFilter(r,n,e,i)}}function SineRippleFilter(){this.name="Sine Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(t,a,e){var i=a/h,r=t/o,n=Math.sin(i),u=Math.sin(r);e[0]=t+s*n,e[1]=a+l*u};t.transformFilter(n,u,i,r)}}function SolarizeFilter(){this.name="Solarize",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){for(var e=a.width,i=a.height,r=a.data,n=[],s=0;256>s;s++){var l=s/255>.5?2*(s/255-.5):2*(.5-s/255);n[s]=parseInt(255*l,10)}t.tableFilter(r,n,e,i)}}function SparkleFilter(){this.name="Sparkle",this.isDirAnimatable=!1,this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:.5,centerY:.5},this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.rays?this.defaultValues.rays:e.rays;s=parseInt(s,10);for(var l=void 0===e.size?this.defaultValues.size:e.size,h=void 0===e.amount?this.defaultValues.amount:e.amount,o=void 0===e.randomness?this.defaultValues.randomness:e.randomness,u=void 0===e.centerX?this.defaultValues.centerX:e.centerX,d=void 0===e.centerY?this.defaultValues.centerY:e.centerY,f=u*i,v=d*r,m=[],c=0;s>c;c++)m[c]=l+o/100*l*t.gaussianRandom();for(var g=0;r>g;g++)for(var p=0;i>p;p++){var x=4*(g*i+p),V=p-f,w=g-v,F=V*V+w*w,M=Math.atan2(w,V),b=(M+Math.PI)/(2*Math.PI)*s,I=parseInt(b,10),A=b-I;if(0!==l){var y=t.linearInterpolate(A,m[I%s],m[(I+1)%s]),R=y*y/(F+1e-4);R=Math.pow(R,(100-h)/50),A-=.5,A=1-A*A,A*=R}A=t.clampPixel(A,0,1);for(var D=t.mixColors(A,[n[x],n[x+1],n[x+2],n[x+3]],[255,255,255,255]),S=0;3>S;S++)n[x+S]=D[S]}}}function SquareSmearFilter(){this.name="Square Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=parseInt(2*o/30*r*n/2,10),v=0;f>v;v++)for(var m=(Math.random()*Math.pow(2,32)&2147483647)%r,c=(Math.random()*Math.pow(2,32)&2147483647)%n,g=[s[4*(c*r+m)],s[4*(c*r+m)+1],s[4*(c*r+m)+2],s[4*(c*r+m)+3]],p=m-d;m+d+1>p;p++)for(var x=c-d;c+d+1>x;x++)if(p>=0&&r>p&&x>=0&&n>x){var V=[l[4*(x*r+p)],l[4*(x*r+p)+1],l[4*(x*r+p)+2],l[4*(x*r+p)+3]],w=t.mixColors(u,V,g);for(i=0;3>i;i++)l[4*(x*r+p)+i]=w[i]}for(i=0;is;s++)for(var l=0;e>l;l++){var h=4*(s*e+l),o=(r[h]+r[h+1]+r[h+2])/3,u=0;o>n&&(u=255),r[h]=r[h+1]=r[h+2]=u}}}function TriangleRippleFilter(){this.name="Triangle Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.triangle(r,1),d=t.triangle(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function TwirlFilter(){this.name="Twirl",this.isDirAnimatable=!1,this.defaultValues={radius:100,angle:180,centerX:.5,centerY:.5},this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o;s=s/180*Math.PI;var d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i+r*r;if(n>u)e[0]=t,e[1]=a;else{n=Math.sqrt(n);var l=Math.atan2(r,i)+s*(o-n)/o;e[0]=d+n*Math.cos(l),e[1]=f+n*Math.sin(l)}};t.transformFilter(n,v,i,r)}}function VignetteFilter(){this.name="Vignette",this.isDirAnimatable=!1,this.defaultValues={amount:.3},this.valueRanges={amount:{min:0,max:1}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.amount?this.defaultValues.amount:a.amount,l=document.createElement("canvas");l.width=e,l.height=i;var h,o=l.getContext("2d"),u=Math.sqrt(Math.pow(e/2,2)+Math.pow(i/2,2));o.putImageData(t,0,0),o.globalCompositeOperation="source-over",h=o.createRadialGradient(e/2,i/2,0,e/2,i/2,u),h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(.5,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,"+s+")"),o.fillStyle=h,o.fillRect(0,0,e,i),n=o.getImageData(0,0,e,i).data;for(var d=0;df)e[0]=t,e[1]=a;else{var o=Math.sqrt(n),u=l*Math.sin(o/s*Math.PI*2-h);u*=(d-o)/d,0!==o&&(u*=s/o),e[0]=t+i*u,e[1]=a+r*u}};t.transformFilter(n,c,i,r)}}var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter,maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter,triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter};"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=JSManipulate); +function FilterUtils(){this.HSVtoRGB=function(t,a,e){var i,r,n,s=Math.floor(6*t),l=6*t-s,h=e*(1-a),o=e*(1-l*a),u=e*(1-(1-l)*a);switch(s%6){case 0:i=e,r=u,n=h;break;case 1:i=o,r=e,n=h;break;case 2:i=h,r=e,n=u;break;case 3:i=h,r=o,n=e;break;case 4:i=u,r=h,n=e;break;case 5:i=e,r=h,n=o}return[255*i,255*r,255*n]},this.RGBtoHSV=function(t,a,e){t/=255,a/=255,e/=255;var i,r,n=Math.max(t,a,e),s=Math.min(t,a,e),l=n,h=n-s;if(r=0===n?0:h/n,n===s)i=0;else{switch(n){case t:i=(a-e)/h+(e>a?6:0);break;case a:i=(e-t)/h+2;break;case e:i=(t-a)/h+4}i/=6}return[i,r,l]},this.getPixel=function(t,a,e,i,r){var n=4*(e*i+a);return 0>a||a>=i||0>e||e>=r?[t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+1],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+2],t[4*(this.clampPixel(e,0,r-1)*i+this.clampPixel(a,0,i-1))+3]]:[t[n],t[n+1],t[n+2],t[n+3]]};var t,a=!1;this.gaussianRandom=function(){if(a)return a=!1,t;var e,i,r;do e=2*Math.random()-1,i=2*Math.random()-1,r=e*e+i*i;while(r>=1||0===r);var n=Math.sqrt(-2*Math.log(r)/r);return t=i*n,a=!0,e*n},this.clampPixel=function(t,a,e){return a>t?a:t>e?e:t},this.triangle=function(t){var a=this.mod(t,1);return 2*(.5>a?a:1-a)},this.mod=function(t,a){var e=parseInt(t/a,10);return t-=e*a,0>t?t+a:t},this.mixColors=function(t,a,e){var i=this.linearInterpolate(t,a[0],e[0]),r=this.linearInterpolate(t,a[1],e[1]),n=this.linearInterpolate(t,a[2],e[2]),s=this.linearInterpolate(t,a[3],e[3]);return[i,r,n,s]},this.linearInterpolate=function(t,a,e){return a+t*(e-a)},this.bilinearInterpolate=function(t,a,e,i,r,n){var s,l,h=e[0],o=e[1],u=e[2],d=e[3],f=i[0],v=i[1],m=i[2],c=i[3],g=r[0],p=r[1],x=r[2],V=r[3],w=n[0],F=n[1],M=n[2],b=n[3],I=1-t,y=1-a;s=I*d+t*c,l=I*V+t*b;var A=y*s+a*l;s=I*h+t*f,l=I*g+t*w;var R=y*s+a*l;s=I*o+t*v,l=I*p+t*F;var D=y*s+a*l;s=I*u+t*m,l=I*x+t*M;var S=y*s+a*l;return[R,D,S,A]},this.tableFilter=function(t,a,e,i){for(var r=0;i>r;r++)for(var n=0;e>n;n++)for(var s=4*(r*e+n),l=0;3>l;l++)t[s+l]=a[t[s+l]]},this.convolveFilter=function(t,a,e,i){var r,n,s=[];r=n=Math.sqrt(a.length);for(var l=parseInt(r/2,10),h=parseInt(n/2,10),o=0;i>o;o++)for(var u=0;e>u;u++){for(var d=4*(o*e+u),f=0,v=0,m=0,c=-l;l>=c;c++){var g,p=o+c;g=p>=0&&i>p?p*e:o*e;for(var x=n*(c+l)+h,V=-h;h>=V;V++){var w=a[x+V];if(0!==w){var F=u+V;F>=0&&e>F||(F=u);var M=4*(g+F);f+=w*t[M],v+=w*t[M+1],m+=w*t[M+2]}}}s[d]=parseInt(f+.5,10),s[d+1]=parseInt(v+.5,10),s[d+2]=parseInt(m+.5,10),s[d+3]=t[d+3]}for(var b=0;bl;l++)for(var h=0;e>h;h++){var o=4*(l*e+h);a.apply(this,[h,l,r]);var u,d,f,v,m=Math.floor(r[0]),c=Math.floor(r[1]),g=r[0]-m,p=r[1]-c;if(m>=0&&e-1>m&&c>=0&&i-1>c){var x=4*(e*c+m);u=[t[x],t[x+1],t[x+2],t[x+3]],d=[t[x+4],t[x+5],t[x+6],t[x+7]],f=[t[x+4*e],t[x+4*e+1],t[x+4*e+2],t[x+4*e+3]],v=[t[x+4*(e+1)],t[x+4*(e+1)+1],t[x+4*(e+1)+2],t[x+4*(e+1)+3]]}else u=this.getPixel(t,m,c,e,i),d=this.getPixel(t,m+1,c,e,i),f=this.getPixel(t,m,c+1,e,i),v=this.getPixel(t,m+1,c+1,e,i);var V=this.bilinearInterpolate(g,p,u,d,f,v);n[o]=V[0],n[o+1]=V[1],n[o+2]=V[2],n[o+3]=V[3]}for(var w=0;wl&&(l=0),e=l>=2.5?.98711*l-.9633:l>=.5?3.97156-4.14554*Math.sqrt(1-.26891*l):2*l*(3.97156-4.14554*Math.sqrt(.865545));var h,o,u,d,f,v,m=e*e,c=m*e,g=1.57825+2.44413*e+1.4281*m+.422205*c,p=(2.44413*e+2.85619*m+1.26661*c)/g,x=-(1.4281*m+1.26661*c)/g,V=.422205*c/g,w=1-(p+x+V),F=0;for(F=0;3>F;F++)for(var M=0;n>M;M++){for(h=M*r+F,o=M*r+(i-1<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=M*r+(i-1<<2)+F,o=M*r+F,u=s[h],d=u,f=d,v=f;h>=o;h-=4)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}for(F=0;3>F;F++)for(var b=0;i>b;b++){for(h=(b<<2)+F,o=(n-1)*r+(b<<2)+F,u=s[h],d=u,f=d,v=f;o>=h;h+=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u;for(h=(n-1)*r+(b<<2)+F,o=(b<<2)+F,u=s[h],d=u,f=d,v=f;h>=o;h-=r)u=w*s[h]+p*d+x*f+V*v,s[h]=u,v=f,f=d,d=u}}}function BrightnessFilter(){this.name="Brightness",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);u[2]+=s,u[2]<0?u[2]=0:u[2]>1&&(u[2]=1);for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function BumpFilter(){this.name="Bump",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[-1,-1,0,-1,1,1,0,1,1];t.convolveFilter(r,n,e,i)}}function CircleSmearFilter(){this.name="Circle Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=d*d,v=parseInt(2*o/30*i*r/2,10),m=0;v>m;m++)for(var c=(Math.random()*Math.pow(2,32)&2147483647)%i,g=(Math.random()*Math.pow(2,32)&2147483647)%r,p=[n[4*(g*i+c)],n[4*(g*i+c)+1],n[4*(g*i+c)+2],n[4*(g*i+c)+3]],x=c-d;c+d+1>x;x++)for(var V=g-d;g+d+1>V;V++){var w=(x-c)*(x-c)+(V-g)*(V-g);if(x>=0&&i>x&&V>=0&&r>V&&f>=w)for(var F=[s[4*(V*i+x)],s[4*(V*i+x)+1],s[4*(V*i+x)+2],s[4*(V*i+x)+3]],M=t.mixColors(u,F,p),b=0;3>b;b++)s[4*(V*i+x)+b]=M[b]}for(var I=0;Is&&(s=0);for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*((h/255-.5)*s+.5),10);t.tableFilter(n,l,i,r)}}function CrossSmearFilter(){this.name="Cross Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,mix:.5},this.valueRanges={distance:{min:0,max:30},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){for(var i=a.width,r=a.height,n=a.data,s=[],l=0;lh&&(h=0),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=parseInt(2*o*i*r/(h+1),10),f=0;d>f;f++){for(var v,m,c,g=(Math.random()*Math.pow(2,32)&2147483647)%i,p=(Math.random()*Math.pow(2,32)&2147483647)%r,x=Math.random()*Math.pow(2,32)%h+1,V=[n[4*(p*i+g)],n[4*(p*i+g)+1],n[4*(p*i+g)+2],n[4*(p*i+g)+3]],w=g-x;g+x+1>w;w++)if(w>=0&&i>w)for(v=[s[4*(p*i+w)],s[4*(p*i+w)+1],s[4*(p*i+w)+2],s[4*(p*i+w)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(p*i+w)+c]=m[c];for(var F=p-x;p+x+1>F;F++)if(F>=0&&r>F)for(v=[s[4*(F*i+g)],s[4*(F*i+g)+1],s[4*(F*i+g)+2],s[4*(F*i+g)+3]],m=t.mixColors(u,v,V),c=0;3>c;c++)s[4*(F*i+g)+c]=m[c]}for(var M=0;Mo;o++){var u=2*Math.PI*o/256;l[o]=s*Math.sin(u),h[o]=s*Math.cos(u)}transInverse=function(t,a,e){var i=parseInt(255*Math.random(),10),r=Math.random();e[0]=t+r*l[i],e[1]=a+r*h[i]},t.transformFilter(n,transInverse,i,r)}}function DitherFilter(){this.name="Dither",this.isDirAnimatable=!1,this.defaultValues={levels:3,color:!0},this.valueRanges={levels:{min:2,max:30},color:{min:!1,max:!0}};new FilterUtils;this.filter=function(t,a){var e,i,r=t.width,n=t.height,s=t.data,l=[];for(i=0;i=h&&(h=1);var u=[0,0,0,0,0,7,3,5,1],d=16,f=0,v=[];for(e=0;h>e;e++)v[e]=parseInt(255*e/(h-1),10);var m=[];for(e=0;256>e;e++)m[e]=parseInt(h*e/256,10);for(var c=0;n>c;c++){var g,p=1==(1&c);p?(f=4*(c*r+r-1),g=-1):(f=c*r*4,g=1);for(var x=0;r>x;x++){var V=s[f],w=s[f+1],F=s[f+2];o||(V=w=F=parseInt((V+w+F)/3,10));var M=v[m[V]],b=v[m[w]],I=v[m[F]];l[f]=M,l[f+1]=b,l[f+2]=I,l[f+3]=s[f+3];var y=V-M,A=w-b,R=F-I;for(e=-1;1>=e;e++){var D=e+c;if(D>=0&&n>D)for(i=-1;1>=i;i++){var S=i+x;if(S>=0&&r>S){var P;if(P=p?u[3*(e+1)-i+1]:u[3*(e+1)+i+1],0!==P){var W=p?f-4*i:f+4*i;V=s[W],w=s[W+1],F=s[W+2];var U=P/d;V+=y*U,w+=A*U,F+=R*U,s[W]=V,s[W+1]=w,s[W+2]=F}}}}f+=4*g}}for(i=0;il;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=0;gh=0,bh=0;var d=0;gv=0,bv=0;for(var f=-1;1>=f;f++){var v,m=l+f;v=m>=0&&r>m?m*i*4:l*i*4;for(var c=3*(f+1)+1,g=-1;1>=g;g++){var p=h+g;p>=0&&i>p||(p=h),p*=4;var x=n[v+p],V=n[v+p+1],w=n[v+p+2],F=t[c+g],M=a[c+g];u+=parseInt(F*x,10),bh+=parseInt(F*V,10),gh+=parseInt(F*w,10),d+=parseInt(M*x,10),gv+=parseInt(M*V,10),bv+=parseInt(M*w,10)}}x=parseInt(Math.sqrt(u*u+d*d)/1.8,10),V=parseInt(Math.sqrt(gh*gh+gv*gv)/1.8,10),w=parseInt(Math.sqrt(bh*bh+bv*bv)/1.8,10),s[o]=x,s[o+1]=V,s[o+2]=w,s[o+3]=n[o+3]}for(var b=0;by;y++,I+=d)for(var A=I,R=A+d,D=R+d,S=0;e>S;S++,A++,R++,D++){var P=4*(y*e+S);0!==y&&i-2>y&&0!==S&&e-2>S?(v=u[A-1]+u[R-1]+u[D-1]-u[A+1]-u[R+1]-u[D+1],m=u[D-1]+u[D]+u[D+1]-u[A-1]-u[A]-u[A+1],M=0===v&&0===m?b:(F=v*g+m*p+w)<0?0:parseInt(F/Math.sqrt(v*v+m*m+V),10)):M=b,r[P]=r[P+1]=r[P+2]=M}}}function ExposureFilter(){this.name="Exposure",this.isDirAnimatable=!0,this.defaultValues={exposure:1},this.valueRanges={exposure:{min:0,max:5}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.exposure?this.defaultValues.exposure:e.exposure,l=[],h=0;256>h;h++)l[h]=parseInt(255*(1-Math.exp(-(h/255)*s)),10);t.tableFilter(n,l,i,r)}}function GainFilter(){this.name="Gain/Bias",this.isDirAnimatable=!0,this.defaultValues={gain:.5,bias:.5},this.valueRanges={gain:{min:0,max:1},bias:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.gain?this.defaultValues.gain:e.gain,l=void 0===e.bias?this.defaultValues.bias:e.bias,h=[],o=0;256>o;o++){var u=o/255,d=(1/s-2)*(1-2*u);u=.5>u?u/(d+1):(d-u)/(d-1),u/=(1/l-2)*(1-u)+1,h[o]=parseInt(255*u,10)}t.tableFilter(n,h,i,r)}}function GammaFilter(){this.name="Gamma",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;if(0>n&&(n=0),!FilterUtils)return void(console&&console.error("Unable to find filterutils.js, please include this file! (Required by "+this.name+" filter)"));for(var s=new FilterUtils,l=[],h=0;256>h;h++)l[h]=255*Math.pow(h/255,1/n)+.5;s.tableFilter(r,l,e,i)}}function GrayscaleFilter(){this.name="Grayscale",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++){var s=4*(r*a+n),l=.3*i[s]+.59*i[s+1]+.11*i[s+2];i[s]=i[s+1]=i[s+2]=l}}}function HueFilter(){this.name="Hue",this.isDirAnimatable=!0,this.defaultValues={amount:0},this.valueRanges={amount:{min:-1,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);for(var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=0;r>l;l++)for(var h=0;i>h;h++){var o=4*(l*i+h),u=t.RGBtoHSV(n[o],n[o+1],n[o+2]);for(u[0]+=s;u[0]<0;)u[0]+=360;for(var d=t.HSVtoRGB(u[0],u[1],u[2]),f=0;3>f;f++)n[o+f]=d[f]}}}function InvertFilter(){this.name="Invert",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={},this.filter=function(t){for(var a=t.width,e=t.height,i=t.data,r=0;e>r;r++)for(var n=0;a>n;n++)for(var s=4*(r*a+n),l=0;3>l;l++)i[s+l]=255-i[s+l]}}function KaleidoscopeFilter(){this.name="Kaleidoscope",this.isDirAnimatable=!1,this.defaultValues={angle:0,rotation:0,sides:3,centerX:.5,centerY:.5},this.valueRanges={angle:{min:0,max:360},rotation:{min:0,max:360},sides:{min:1,max:30},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.rotation?this.defaultValues.rotation:e.rotation,h=void 0===e.sides?this.defaultValues.sides:e.sides,o=void 0===e.centerX?this.defaultValues.centerX:e.centerX,u=void 0===e.centerY?this.defaultValues.centerY:e.centerY,d=i*o,f=r*u;s=s/180*Math.PI,l=l/180*Math.PI;var v=function(a,e,i){var r=a-d,n=e-f,o=Math.sqrt(r*r+n*n),u=Math.atan2(n,r)-s-l;u=t.triangle(u/Math.PI*h*.5),u+=s,i[0]=d+o*Math.cos(u),i[1]=f+o*Math.sin(u)};t.transformFilter(n,v,i,r)}}function LensDistortionFilter(){this.name="Lens Distortion",this.isDirAnimatable=!1,this.defaultValues={refraction:1.5,radius:50,centerX:.5,centerY:.5},this.valueRanges={refraction:{min:1,max:10},radius:{min:1,max:200},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.refraction?this.defaultValues.refraction:e.refraction,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o,d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i,l=r*r;if(l>=u-u*n/u)e[0]=t,e[1]=a;else{var h=1/s,o=Math.sqrt((1-n/u-l/u)*u),v=o*o,m=Math.acos(i/Math.sqrt(n+v)),c=Math.PI/2-m,g=Math.asin(Math.sin(c)*h);g=Math.PI/2-m-g,e[0]=t-Math.tan(g)*o;var p=Math.acos(r/Math.sqrt(l+v));c=Math.PI/2-p,g=Math.asin(Math.sin(c)*h),g=Math.PI/2-p-g,e[1]=a-Math.tan(g)*o}};t.transformFilter(n,v,i,r)}}function LineSmearFilter(){this.name="Line Smear",this.isDirAnimatable=!1,this.defaultValues={distance:8,density:.5,angle:0,mix:.5},this.valueRanges={distance:{min:1,max:30},density:{min:0,max:1},angle:{min:0,max:360},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.angle?this.defaultValues.angle:e.angle,d=void 0===e.mix?this.defaultValues.mix:e.mix;u=u/180*Math.PI;for(var f=Math.sin(u),v=Math.cos(u),m=parseInt(2*o*r*n/2,10),c=0;m>c;c++){var g,p,x,V,w,F,M,b=(Math.random()*Math.pow(2,32)&2147483647)%r,I=(Math.random()*Math.pow(2,32)&2147483647)%n,y=(Math.random()*Math.pow(2,32)&2147483647)%h+1,A=[s[4*(I*r+b)],s[4*(I*r+b)+1],s[4*(I*r+b)+2],s[4*(I*r+b)+3]],R=parseInt(y*v,10),D=parseInt(y*f,10),S=b-R,P=I-D,W=b+R,U=I+D;F=S>W?-1:1,M=P>U?-1:1,R=W-S,D=U-P,R=Math.abs(R),D=Math.abs(D),g=S,p=P;var X,Y;if(r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,A),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i];if(Math.abs(R)>Math.abs(D)){for(x=2*D-R,V=2*D,w=2*(D-R);g!=W;)if(0>=x?x+=V:(x+=w,p+=M),g+=F,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,A),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}else for(x=2*R-D,V=2*R,w=2*(R-D);p!=U;)if(0>=x?x+=V:(x+=w,g+=F),p+=M,r>g&&g>=0&&n>p&&p>=0)for(X=[l[4*(p*r+g)],l[4*(p*r+g)+1],l[4*(p*r+g)+2],l[4*(p*r+g)+3]],Y=t.mixColors(d,X,A),i=0;3>i;i++)l[4*(p*r+g)+i]=Y[i]}for(i=0;in;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=0,o=0,u=0,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.max(h,i[c]),o=Math.max(o,i[c+1]),u=Math.max(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=[],o=[],u=[],d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h.push(i[c]),o.push(i[c+1]),u.push(i[c+2])}}}var g=function(t,a){return t-a};h.sort(g),o.sort(g),u.sort(g),r[l]=h[4],r[l+1]=o[4],r[l+2]=u[4],r[l+3]=i[l+3]}for(var p=0;pn;n++)for(var s=0;a>s;s++){for(var l=4*(n*a+s),h=255,o=255,u=255,d=-1;1>=d;d++){var f=n+d;if(f>=0&&e>f)for(var v=-1;1>=v;v++){var m=s+v;if(m>=0&&a>m){var c=4*(f*a+m);h=Math.min(h,i[c]),o=Math.min(o,i[c+1]),u=Math.min(u,i[c+2])}}}r[l]=h,r[l+1]=o,r[l+2]=u,r[l+3]=i[l+3]}for(var g=0;gh;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);if(Math.random()<=s){var d;if(l)d=parseInt((2*Math.random()-1)*n,10),r[u]+=d,r[u+1]+=d,r[u+2]+=d;else for(var f=0;3>f;f++)d=parseInt((2*Math.random()-1)*n,10),r[u+f]+=d}}}}function OilFilter(){this.name="Oil Painting",this.isDirAnimatable=!1,this.defaultValues={range:3},this.valueRanges={range:{min:0,max:5}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.range?this.defaultValues.range:a.range;s=parseInt(s,10);for(var l=[],h=[],o=[],u=[],d=[],f=[],v=256,m=0;i>m;m++)for(var c=0;e>c;c++){for(var g=4*(m*e+c),p=0;v>p;p++)l[p]=h[p]=o[p]=u[p]=d[p]=f[p]=0;for(var x=-s;s>=x;x++){var V,w=m+x;if(w>=0&&i>w){V=w*e;for(var F=-s;s>=F;F++){var M=c+F;if(M>=0&&e>M){var b=r[4*(V+M)],I=r[4*(V+M)+1],y=r[4*(V+M)+2],A=b*v/256,R=I*v/256,D=y*v/256;u[A]+=b,d[R]+=I,f[D]+=y,l[A]++,h[R]++,o[D]++}}}}for(var S=0,P=0,W=0,U=1;v>U;U++)l[U]>l[S]&&(S=U),h[U]>h[P]&&(P=U),o[U]>o[W]&&(W=U);S=u[S]/l[S],P=d[P]/h[P],W=f[W]/o[W],n[g]=S,n[g+1]=P,n[g+2]=W,n[g+3]=r[g+3]}for(var X=0;Xs;s++)for(var l=0;e>l;l++){var h=4*(s*e+l);r[h+3]=255*n}}}function PinchFilter(){this.name="Pinch/Whirl",this.isDirAnimatable=!1,this.defaultValues={amount:.5,radius:100,angle:0,centerX:.5,centerY:.5},this.valueRanges={amount:{min:-1,max:1},radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.amount?this.defaultValues.amount:e.amount,l=void 0===e.angle?this.defaultValues.angle:e.angle,h=void 0===e.centerX?this.defaultValues.centerX:e.centerX,o=void 0===e.centerY?this.defaultValues.centerY:e.centerY,u=void 0===e.radius?this.defaultValues.radius:e.radius,d=u*u;l=l/180*Math.PI;var f=i*h,v=r*o,m=function(t,a,e){var i=t-f,r=a-v,n=i*i+r*r;if(n>d||0===n)e[0]=t,e[1]=a;else{var h=Math.sqrt(n/d),o=Math.pow(Math.sin(.5*Math.PI*h),-s);i*=o,r*=o;var u=1-h,m=l*u*u,c=Math.sin(m),g=Math.cos(m);e[0]=f+g*i-c*r,e[1]=v+c*i+g*r}};t.transformFilter(n,m,i,r)}}function PixelationFilter(){this.name="Pixelation",this.isDirAnimatable=!1,this.defaultValues={size:5},this.valueRanges={size:{min:1,max:50}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.size?this.defaultValues.size:a.size;n=parseInt(n,10);for(var s,l,h,o=0;i>o;o+=n)for(var u=0;e>u;u+=n){var d=Math.min(n,e-u),f=Math.min(n,i-o),v=d*f,m=0,c=0,g=0;for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),m+=r[h],c+=r[h+1],g+=r[h+2];for(s=o;o+f>s;s++)for(l=u;u+d>l;l++)h=4*(s*e+l),r[h]=m/v,r[h+1]=c/v,r[h+2]=g/v}}}function PosterizeFilter(){this.name="Posterize",this.isDirAnimatable=!1,this.defaultValues={levels:6},this.valueRanges={levels:{min:2,max:30}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.levels?this.defaultValues.levels:parseInt(e.levels,10);if(!(1>=s)){for(var l=[],h=0;256>h;h++)l[h]=parseInt(255*parseInt(h*s/256,10)/(s-1),10);t.tableFilter(n,l,i,r)}}}function RGBAdjustFilter(){this.name="RGBAdjust",this.isDirAnimatable=!0,this.defaultValues={red:1,green:1,blue:1},this.valueRanges={red:{min:0,max:2},green:{min:0,max:2},blue:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.red?this.defaultValues.red:a.red,s=void 0===a.green?this.defaultValues.green:a.green,l=void 0===a.blue?this.defaultValues.blue:a.blue;0>n&&(n=0),0>s&&(s=0),0>l&&(l=0);for(var h=0;i>h;h++)for(var o=0;e>o;o++){var u=4*(h*e+o);r[u]*=n,r[u+1]*=s,r[u+2]*=l}}}function SaturationFilter(){this.name="Saturation",this.isDirAnimatable=!0,this.defaultValues={amount:1},this.valueRanges={amount:{min:0,max:2}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);for(var n=void 0===a.amount?this.defaultValues.amount:a.amount,s=.3,l=.59,h=.11,o=(1-n)*s+n,u=(1-n)*s,d=(1-n)*s,f=(1-n)*l,v=(1-n)*l+n,m=(1-n)*l,c=(1-n)*h,g=(1-n)*h,p=(1-n)*h+n,x=0;i>x;x++)for(var V=0;e>V;V++){var w=4*(x*e+V),F=r[w],M=r[w+1],b=r[w+2];r[w]=o*F+f*M+c*b,r[w+1]=u*F+v*M+g*b,r[w+2]=d*F+m*M+p*b}}}function SawtoothRippleFilter(){this.name="Sawtooth Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.mod(r,1),d=t.mod(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function SepiaFilter(){this.name="Sepia",this.isDirAnimatable=!0,this.defaultValues={amount:10},this.valueRanges={amount:{min:0,max:30}};new FilterUtils;this.filter=function(t,a){var e=t.width,i=t.height,r=t.data;void 0===a&&(a=this.defaultValues);var n=void 0===a.amount?this.defaultValues.amount:a.amount;n*=2.55;for(var s=0;i>s;s++)for(var l=0;e>l;l++){var h,o,u,d=4*(s*e+l),f=.3*r[d]+.59*r[d+1]+.11*r[d+2];h=o=u=f,h+=40,o+=20,u-=n,r[d]=h,r[d+1]=o,r[d+2]=u}}}function SharpenFilter(){this.name="Sharpen",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){var e=a.width,i=a.height,r=a.data,n=[0,-.2,0,-.2,1.8,-.2,0,-.2,0];t.convolveFilter(r,n,e,i)}}function SineRippleFilter(){this.name="Sine Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(t,a,e){var i=a/h,r=t/o,n=Math.sin(i),u=Math.sin(r);e[0]=t+s*n,e[1]=a+l*u};t.transformFilter(n,u,i,r)}}function SolarizeFilter(){this.name="Solarize",this.isDirAnimatable=!0,this.defaultValues={},this.valueRanges={};var t=new FilterUtils;this.filter=function(a){for(var e=a.width,i=a.height,r=a.data,n=[],s=0;256>s;s++){var l=s/255>.5?2*(s/255-.5):2*(.5-s/255);n[s]=parseInt(255*l,10)}t.tableFilter(r,n,e,i)}}function SparkleFilter(){this.name="Sparkle",this.isDirAnimatable=!1,this.defaultValues={rays:50,size:25,amount:50,randomness:25,centerX:.5,centerY:.5},this.valueRanges={rays:{min:1,max:100},size:{min:1,max:200},amount:{min:0,max:100},randomness:{min:0,max:50},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.rays?this.defaultValues.rays:e.rays;s=parseInt(s,10);for(var l=void 0===e.size?this.defaultValues.size:e.size,h=void 0===e.amount?this.defaultValues.amount:e.amount,o=void 0===e.randomness?this.defaultValues.randomness:e.randomness,u=void 0===e.centerX?this.defaultValues.centerX:e.centerX,d=void 0===e.centerY?this.defaultValues.centerY:e.centerY,f=u*i,v=d*r,m=[],c=0;s>c;c++)m[c]=l+o/100*l*t.gaussianRandom();for(var g=0;r>g;g++)for(var p=0;i>p;p++){var x=4*(g*i+p),V=p-f,w=g-v,F=V*V+w*w,M=Math.atan2(w,V),b=(M+Math.PI)/(2*Math.PI)*s,I=parseInt(b,10),y=b-I;if(0!==l){var A=t.linearInterpolate(y,m[I%s],m[(I+1)%s]),R=A*A/(F+1e-4);R=Math.pow(R,(100-h)/50),y-=.5,y=1-y*y,y*=R}y=t.clampPixel(y,0,1);for(var D=t.mixColors(y,[n[x],n[x+1],n[x+2],n[x+3]],[255,255,255,255]),S=0;3>S;S++)n[x+S]=D[S]}}}function SquareSmearFilter(){this.name="Square Smear",this.isDirAnimatable=!1,this.defaultValues={size:4,density:.5,mix:.5},this.valueRanges={size:{min:1,max:10},density:{min:0,max:1},mix:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i,r=a.width,n=a.height,s=a.data,l=[];for(i=0;ih&&(h=1),h=parseInt(h,10);for(var o=void 0===e.density?this.defaultValues.density:e.density,u=void 0===e.mix?this.defaultValues.mix:e.mix,d=h+1,f=parseInt(2*o/30*r*n/2,10),v=0;f>v;v++)for(var m=(Math.random()*Math.pow(2,32)&2147483647)%r,c=(Math.random()*Math.pow(2,32)&2147483647)%n,g=[s[4*(c*r+m)],s[4*(c*r+m)+1],s[4*(c*r+m)+2],s[4*(c*r+m)+3]],p=m-d;m+d+1>p;p++)for(var x=c-d;c+d+1>x;x++)if(p>=0&&r>p&&x>=0&&n>x){var V=[l[4*(x*r+p)],l[4*(x*r+p)+1],l[4*(x*r+p)+2],l[4*(x*r+p)+3]],w=t.mixColors(u,V,g);for(i=0;3>i;i++)l[4*(x*r+p)+i]=w[i]}for(i=0;is;s++)for(var l=0;e>l;l++){var h=4*(s*e+l),o=(r[h]+r[h+1]+r[h+2])/3,u=0;o>n&&(u=255),r[h]=r[h+1]=r[h+2]=u}}}function TriangleRippleFilter(){this.name="Triangle Ripples",this.isDirAnimatable=!1,this.defaultValues={xAmplitude:5,yAmplitude:5,xWavelength:16,yWavelength:16},this.valueRanges={xAmplitude:{min:0,max:30},yAmplitude:{min:0,max:30},xWavelength:{min:1,max:50},yWavelength:{min:1,max:50}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.xAmplitude?this.defaultValues.xAmplitude:e.xAmplitude,l=void 0===e.yAmplitude?this.defaultValues.yAmplitude:e.yAmplitude,h=void 0===e.xWavelength?this.defaultValues.xWavelength:e.xWavelength,o=void 0===e.yWavelength?this.defaultValues.yWavelength:e.yWavelength,u=function(a,e,i){var r=e/h,n=a/o,u=t.triangle(r,1),d=t.triangle(n,1);i[0]=a+s*u,i[1]=e+l*d};t.transformFilter(n,u,i,r)}}function TwirlFilter(){this.name="Twirl",this.isDirAnimatable=!1,this.defaultValues={radius:100,angle:180,centerX:.5,centerY:.5},this.valueRanges={radius:{min:1,max:200},angle:{min:0,max:360},centerX:{min:0,max:1},centerY:{min:0,max:1}};var t=new FilterUtils;this.filter=function(a,e){var i=a.width,r=a.height,n=a.data;void 0===e&&(e=this.defaultValues);var s=void 0===e.angle?this.defaultValues.angle:e.angle,l=void 0===e.centerX?this.defaultValues.centerX:e.centerX,h=void 0===e.centerY?this.defaultValues.centerY:e.centerY,o=void 0===e.radius?this.defaultValues.radius:e.radius,u=o*o;s=s/180*Math.PI;var d=i*l,f=r*h,v=function(t,a,e){var i=t-d,r=a-f,n=i*i+r*r;if(n>u)e[0]=t,e[1]=a;else{n=Math.sqrt(n);var l=Math.atan2(r,i)+s*(o-n)/o;e[0]=d+n*Math.cos(l),e[1]=f+n*Math.sin(l)}};t.transformFilter(n,v,i,r)}}function VignetteFilter(){this.name="Vignette",this.isDirAnimatable=!1,this.defaultValues={amount:.3},this.valueRanges={amount:{min:0,max:1}},this.filter=function(t,a){var e=t.width,i=t.height,r=t.data,n=[];void 0===a&&(a=this.defaultValues);var s=void 0===a.amount?this.defaultValues.amount:a.amount;if("undefined"!=typeof module&&"undefined"!=typeof module.exports)var l=require("canvas");else var l=document.createElement("canvas");l.width=e,l.height=i;var h,o=l.getContext("2d"),u=Math.sqrt(Math.pow(e/2,2)+Math.pow(i/2,2));o.putImageData(t,0,0),o.globalCompositeOperation="source-over",h=o.createRadialGradient(e/2,i/2,0,e/2,i/2,u),h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(.5,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,"+s+")"),o.fillStyle=h,o.fillRect(0,0,e,i),n=o.getImageData(0,0,e,i).data;for(var d=0;df)e[0]=t,e[1]=a;else{var o=Math.sqrt(n),u=l*Math.sin(o/s*Math.PI*2-h);u*=(d-o)/d,0!==o&&(u*=s/o),e[0]=t+i*u,e[1]=a+r*u}};t.transformFilter(n,c,i,r)}}var JSManipulate={blur:new BlurFilter,brightness:new BrightnessFilter,bump:new BumpFilter,circlesmear:new CircleSmearFilter,contrast:new ContrastFilter,crosssmear:new CrossSmearFilter,diffusion:new DiffusionFilter,dither:new DitherFilter,edge:new EdgeFilter,emboss:new EmbossFilter,exposure:new ExposureFilter,gain:new GainFilter,gamma:new GammaFilter,grayscale:new GrayscaleFilter,hue:new HueFilter,invert:new InvertFilter,kaleidoscope:new KaleidoscopeFilter,lensdistortion:new LensDistortionFilter,linesmear:new LineSmearFilter,maximum:new MaximumFilter,median:new MedianFilter,minimum:new MinimumFilter,noise:new NoiseFilter,oil:new OilFilter,opacity:new OpacityFilter,pinch:new PinchFilter,pixelate:new PixelationFilter,posterize:new PosterizeFilter,rgbadjust:new RGBAdjustFilter,saturation:new SaturationFilter,sawtoothripple:new SawtoothRippleFilter,sepia:new SepiaFilter,sharpen:new SharpenFilter,sineripple:new SineRippleFilter,solarize:new SolarizeFilter,sparkle:new SparkleFilter,squaresmear:new SquareSmearFilter,threshold:new ThresholdFilter,triangleripple:new TriangleRippleFilter,twirl:new TwirlFilter,vignette:new VignetteFilter,waterripple:new WaterRippleFilter};"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=JSManipulate); diff --git a/package.json b/package.json index 60af342..ecb38c3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Joel Besada", "name": "jsmanipulate", - "version": "1.0.0", + "version": "1.0.1", "description": "JSManipulate is an image filter and effects library written in Javascript for client-side manipulation of images on a web page.", "main": "jsmanipulate.js", "license": "MIT", From 9b2df98488323ce94c82f8c2cb06f14415cbad05 Mon Sep 17 00:00:00 2001 From: DHoltzmann Date: Sun, 20 Sep 2015 22:29:47 -0700 Subject: [PATCH 8/8] Updated version in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ecb38c3..db7a24a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Joel Besada", "name": "jsmanipulate", - "version": "1.0.1", + "version": "1.0.2", "description": "JSManipulate is an image filter and effects library written in Javascript for client-side manipulation of images on a web page.", "main": "jsmanipulate.js", "license": "MIT",