Skip to content
Browse files

Added more filters and ported some more util functions over

  • Loading branch information...
1 parent b35b14e commit 3d9e6639f9a218597c702e0a311dbbf2d11a20af @meltingice committed
Showing with 278 additions and 79 deletions.
  1. +0 −12 Makefile
  2. +0 −52 Makefile.js
  3. +7 −7 README.md
  4. +138 −1 dist/caman.js
  5. +5 −3 dist/caman.min.js
  6. +5 −1 src/calculate.coffee
  7. +121 −1 src/filters.coffee
  8. +2 −2 src/renderjob.coffee
View
12 Makefile
@@ -1,12 +0,0 @@
-all:
- node Makefile.js
-
-pack: all
- packer -b -s -i dist/caman.js -o dist/caman.pack.js
- packer -b -s -i dist/caman.full.js -o dist/caman.full.pack.js
-
-docs: all
- docco src/*.js
-
-clean:
- rm -f dist/*
View
52 Makefile.js
@@ -1,52 +0,0 @@
-var fs = require('fs'),
-smoosh = require('smoosh'),
-
-SRC_DIR = 'src',
-PLUGIN_DIR = 'src/plugins/plugins';
-
-/*
- * Prepare our plugins
- */
-
-// Make sure plugins submodule is initialized first
-try {
- fs.readdirSync(PLUGIN_DIR);
- finish();
-} catch (e) {
- console.log("####################################");
- console.log("It looks like the CamanJS-Plugins submodule hasn't");
- console.log("been initialized yet. Let me fix that for you.");
- console.log("####################################");
-
- exec('git submodule init', function () {
- exec('git submodule update --recursive', function () {
- finish();
- });
- });
-}
-
-function finish() {
- // then generate plugins.js file
- try {
- // Remove plugins.js if it exists
- fs.statSync(SRC_DIR + "/plugins/plugins.js");
- fs.unlinkSync(SRC_DIR + "/plugins/plugins.js");
- } catch (e) { /* Do nothing */ }
-
- var plugins = "";
- fs.readdirSync(PLUGIN_DIR).forEach(function (plugin) {
- plugins += fs.readFileSync(PLUGIN_DIR + '/' + plugin, 'UTF-8') + "\n";
- });
-
- // output plugins.js
- fs.writeFileSync(SRC_DIR + "/plugins/plugins.js", plugins);
-
- /*
- * Time to smoosh!
- */
- smoosh.config('./config.json');
- smoosh.run().build().analyze();
-
- // Remove the temporary plugins.js file
- fs.unlinkSync(SRC_DIR + "/plugins/plugins.js");
-}
View
14 README.md
@@ -6,7 +6,9 @@ The main focus of CamanJS is manipulating images using the HTML5 canvas and Java
CamanJS is very easy to extend with new filters and plugins, and it comes with a wide array of image editing functionality, which is only growing as the community makes more plugins.
-For more information, I highly recommend taking a look at the <a href="http://camanjs.com">official website</a> where there is more comprehensive documentation and interactive demos. You can also <a href="https://github.com/meltingice/CamanJS/wiki">read the wiki</a> for some basic information about the project and how to use it.
+For more information, I highly recommend taking a look at the [official website](http://camanjs.com) where there is more comprehensive documentation and interactive demos. You can also [read the wiki](https://github.com/meltingice/CamanJS/wiki) for some basic information about the project and how to use it.
+
+CamanJS is written in [Coffeescript](http://coffeescript.org) as of version 3.0.
## Example Usage
@@ -43,23 +45,21 @@ git submodule update
The library is split up into several source files and has a separate submodule for plugins. The reason behind this organization is to make it as simple as possible to support the NodeJS port of Caman. This also helps to avoid library bloat.
-**Makefile**
-
-The NodeJS powered Makefile for the project will automatically check to make sure you've initialized the submodules, and if you haven't, will do so for you. Running the Makefile requires smoosh (available in npm).
+**Building CamanJS**
To build, simply run:
```
-make
+cake build
```
The resulting files will be placed in the dist/ folder.
## CDN JS Hosting
-CamanJS is hosted on CDN JS if you're looking for a CDN hosting solution. It is the full and minified version of the library, which means all plugins are included. Simply load CamanJS directly from <a href="http://ajax.cdnjs.com/ajax/libs/camanjs/2.2/caman.full.min.js">this URL</a> for usage on your site.
+CamanJS is hosted on CDN JS if you're looking for a CDN hosting solution. It is the full and minified version of the library, which means all plugins are included. Simply load CamanJS directly from [this URL](http://ajax.cdnjs.com/ajax/libs/camanjs/2.2/caman.full.min.js) for usage on your site.
## NodeJS Compatibility
-There is now a version of CamanJS that is made to work with NodeJS. It has all of the functionality of the normal browser version, including plugins. Take a look at the <a href="https://github.com/meltingice/CamanJS/tree/node">node branch</a> for more information.
+There is now a version of CamanJS that is made to work with NodeJS. It has all of the functionality of the normal browser version, including plugins. Take a look at the [node branch](https://github.com/meltingice/CamanJS/tree/node) for more information.
**tl;dr**
View
139 dist/caman.js
@@ -207,6 +207,17 @@
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};
+ Calculate.randomRange = function(min, max, float) {
+ var rand;
+ if (float == null) float = false;
+ rand = min + (Math.random() * (max - min));
+ if (float) {
+ return rand.toFixed(float);
+ } else {
+ return Math.round(rand);
+ }
+ };
+
return Calculate;
})();
@@ -583,6 +594,130 @@
});
});
+ Filter.register("vibrance", function(adjust) {
+ adjust *= -1;
+ return this.process("vibrance", function(rgba) {
+ var amt, avg, max;
+ max = Math.max(rgba.r, rgba.g, rgba.b);
+ avg = (rgba.r + rgba.g + rgba.b) / 3;
+ amt = ((Math.abs(max - avg) * 2 / 255) * adjust) / 100;
+ if (rgba.r !== max) rgba.r += (max - rgba.r) * adjust;
+ if (rgba.g !== max) rgba.g += (max - rgba.g) * adjust;
+ if (rgba.b !== max) rgba.b += (max - rgba.b) * adjust;
+ return rgba;
+ });
+ });
+
+ Filter.register("greyscale", function(adjust) {
+ return this.process("greyscale", function(rgba) {
+ var avg;
+ avg = 0.3 * rgba.r + 0.59 * rgba.g + 0.11 * rgba.b;
+ rgba.r = avg;
+ rgba.g = avg;
+ rgba.b = avg;
+ return rgba;
+ });
+ });
+
+ Filter.register("contrast", function(adjust) {
+ adjust = Math.pow((adjust + 100) / 100, 2);
+ return this.process("contrast", function(rgba) {
+ rgba.r /= 255;
+ rgba.r -= 0.5;
+ rgba.r *= adjust;
+ rgba.r += 0.5;
+ rgba.r *= 255;
+ rgba.g /= 255;
+ rgba.g -= 0.5;
+ rgba.g *= adjust;
+ rgba.g += 0.5;
+ rgba.g *= 255;
+ rgba.b /= 255;
+ rgba.b -= 0.5;
+ rgba.b *= adjust;
+ rgba.b += 0.5;
+ rgba.b *= 255;
+ return rgba;
+ });
+ });
+
+ Filter.register("hue", function(adjust) {
+ return this.process("hue", function(rgba) {
+ var h, hsv, rgb;
+ hsv = Convert.rgbToHSV(rgba.r, rgba.g, rgba.b);
+ h = hsv.h * 100;
+ h += Math.abs(adjust);
+ h = h % 100;
+ h /= 100;
+ hsv.h = h;
+ rgb = Convert.hsvToRGB(hsv.h, hsv.s, hsv.v);
+ rgb.a = rgba.a;
+ return rgb;
+ });
+ });
+
+ Filter.register("colorize", function() {
+ var level, rgb;
+ if (arguments.length === 2) {
+ rgb = Convert.hexToRGB(arguments[0]);
+ level = arguments[1];
+ } else if (arguments.length === 4) {
+ rgb = {
+ r: arguments[0],
+ g: arguments[1],
+ b: arguments[2]
+ };
+ level = arguments[3];
+ }
+ return this.process("colorize", function(rgba) {
+ rgba.r -= (rgba.r - rgb.r) * (level / 100);
+ rgba.g -= (rgba.g - rgb.g) * (level / 100);
+ rgba.b -= (rgba.b - rgb.b) * (level / 100);
+ return rgba;
+ });
+ });
+
+ Filter.register("invert", function() {
+ return this.process("invert", function(rgba) {
+ rgba.r = 255 - rgba.r;
+ rgba.g = 255 - rgba.g;
+ rgba.b = 255 - rgba.b;
+ return rgba;
+ });
+ });
+
+ Filter.register("sepia", function(adjust) {
+ if (adjust == null) adjust = 100;
+ adjust /= 100;
+ return this.process("sepia", function(rgba) {
+ rgba.r = Math.min(255, (rgba.r * (1 - (0.607 * adjust))) + (rgba.g * (0.769 * adjust)) + (rgba.b * (0.189 * adjust)));
+ rgba.g = Math.min(255, (rgba.r * (0.349 * adjust)) + (rgba.g * (1 - (0.314 * adjust))) + (rgba.b * (0.168 * adjust)));
+ rgba.b = Math.min(255, (rgba.r * (0.272 * adjust)) + (rgba.g * (0.534 * adjust)) + (rgba.b * (1 - (0.869 * adjust))));
+ return rgba;
+ });
+ });
+
+ Filter.register("gamma", function(adjust) {
+ return this.process("gamma", function(rgba) {
+ rgba.r = Math.pow(rgba.r / 255, adjust) * 255;
+ rgba.g = Math.pow(rgba.g / 255, adjust) * 255;
+ rgba.b = Math.pow(rgba.b / 255, adjust) * 255;
+ return rgba;
+ });
+ });
+
+ Filter.register("noise", function(adjust) {
+ adjust = Math.abs(adjust) * 2.55;
+ return this.process("noise", function(rgba) {
+ var rand;
+ rand = Calculate.randomRange(adjust * -1, adjust);
+ rgba.r += rand;
+ rgba.g += rand;
+ rgba.b += rand;
+ return rgba;
+ });
+ });
+
Logger = (function() {
function Logger() {
@@ -694,7 +829,9 @@
};
RenderJob.prototype.blockFinished = function(bnum) {
- if (bnum >= 0) Log.debug("Block #" + bnum + " finished! Filter: " + name);
+ if (bnum >= 0) {
+ Log.debug("Block #" + bnum + " finished! Filter: " + this.job.name);
+ }
this.blocksDone++;
if (this.blocksDone === RenderJob.Blocks || bnum === -1) {
if (bnum >= 0) Log.debug("Filter " + this.job.name + " finished!");
View
8 dist/caman.min.js
@@ -8,7 +8,7 @@ CamanInstance.prototype.loadImage=function(id,callback){var element,image,_ref;v
if($(id)!=null){image=$(id);if(image.complete){return this.imageLoaded(id,image,callback);}else{return image.onload=function(){return _this.imageLoaded(id,image,callback);};}}};CamanInstance.prototype.imageLoaded=function(id,image,callback){this.image=image;this.canvas=document.createElement('canvas');this.canvas.id=image.id;image.parentNode.replaceChild(this.canvas,this.image);this.canvasID=id;this.options={canvas:id,image:this.image.src};return this.finishInit(callback);};CamanInstance.prototype.loadCanvas=function(url,id,callback){var element,_ref;var _this=this;if(callback==null)callback=function(){};if(typeof id==="object"&&((_ref=id.nodeName)!=null?_ref.toLowerCase():void 0)==="canvas"){element=id;if(id.id){id=element.id;}else{id="caman-"+(uniqid.get());element.id=id;}}
if($(id)!=null){return this.canvasLoaded(url,id,callback);}else{return document.addEventListener("DOMContentLoaded",function(){return _this.canvasLoaded(url,id,callback);},false);}};CamanInstance.prototype.canvasLoaded=function(url,id,callback){var _this=this;this.canvas=$(id);if(url!=null){this.image=document.createElement('img');this.image.onload=function(){return _this.finishInit(callback);};this.canvasID=id;this.options={canvas:id,image:url};return this.image.src=url;}else{return this.finishInit(callback);}};CamanInstance.prototype.finishInit=function(callback){this.context=this.canvas.getContext("2d");if(this.image!=null){this.canvas.width=this.image.width;this.canvas.height=this.image.height;this.context.drawImage(this.image,0,0,this.image.width,this.image.height);}
this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;this.dimensions={width:this.canvas.width,height:this.canvas.height};Store.put(this.canvasID,this);callback.call(this,this);return this;};return CamanInstance;})();Calculate=(function(){function Calculate(){}
-Calculate.distance=function(x1,y1,x2,y2){return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));};return Calculate;})();Convert=(function(){function Convert(){}
+Calculate.distance=function(x1,y1,x2,y2){return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));};Calculate.randomRange=function(min,max,float){var rand;if(float==null)float=false;rand=min+(Math.random()*(max-min));if(float){return rand.toFixed(float);}else{return Math.round(rand);}};return Calculate;})();Convert=(function(){function Convert(){}
Convert.hexToRGB=function(hex){var b,g,r;if(hex.charAt(0)==="#")hex=hex.substr(1);r=parseInt(hex.substr(0,2),16);g=parseInt(hex.substr(2,2),16);b=parseInt(hex.substr(4,2),16);return{r:r,g:g,b:b};};Convert.rgbToHSL=function(r,g,b){var d,h,l,max,min,s;r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);l=(max+min)/2;if(max===min){h=s=0;}else{d=max-min;s=l>0.5?d/(2-max-min):d/(max+min);h=(function(){switch(max){case r:return(g-b)/d+(g<b?6:0);case g:return(b-r)/d+2;case b:return(r-g)/d+4;}})();h/=6;}
return{h:h,s:s,l:l};};Convert.hslToRGB=function(h,s,l){var b,g,p,q,r;if(s===0){r=g=b=l;}else{q=l<0.5?l*(1+s):l+s-l*s;p=2*l-q;r=this.hueToRGB(p,q,h+1/3);g=this.hueToRGB(p,q,h);b=this.hueToRGB(p,q,h-1/3);}
return{r:r*255,g:g*255,b:b*255};};Convert.hueToRGB=function(p,q,t){if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p;};Convert.rgbToHSV=function(r,g,b){var d,h,max,min,s,v;r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);v=max;d=max-min;s=max===0?0:d/max;if(max===min){h=0;}else{h=(function(){switch(max){case r:return(g-b)/d+(g<b?6:0);case g:return(b-r)/d+2;case b:return(r-g)/d+4;}})();h/=6;}
@@ -28,11 +28,13 @@ if(z>0.2068965517){z=z*z*z;}else{z=0.1284185493*(z-0.1379310345);}
return{x:x*95.047,y:y*100.0,z:z*108.883};};return Convert;})();Filter=(function(){function Filter(){}
Filter.Type={Single:1,Kernel:2,LayerDequeued:3,LayerFinished:4,LoadOverlay:5,Plugin:6};Filter.register=function(name,filterFunc){return CamanInstance.prototype[name]=filterFunc;};Filter.prototype.render=function(callback){var _this=this;if(callback==null)callback=function(){};return this.processNext(function(){_this.context.putImageData(_this.imageData,0,0);return callback.call(_this);});};Filter.prototype.process=function(name,processFn){return this.renderQueue.push({type:Filter.Type.Single,name:name,processFn:processFn});};Filter.prototype.processNext=function(finishedFn){var next;var _this=this;if(typeof finishedFn==="function")this.finishedFn=finishedFn;if(this.renderQueue.length===0){if(this.finishedFn!=null)this.finishedFn.call(this);return;}
next=this.renderQueue.shift();return RenderJob.execute(this,next,function(){return _this.processNext();});};return Filter;})();extend(CamanInstance.prototype,Filter.prototype);Filter.register("fillColor",function(){var color;if(arguments.length===1){color=Convert.hexToRGB(arguments[0]);}else{color={r:arguments[0],g:arguments[1],b:arguments[2]};}
-return this.process("fillColor",function(rgba){rgba.r=color.r;rgba.g=color.g;rgba.b=color.b;return rgba;});});Filter.register("brightness",function(adjust){adjust=Math.floor(255*(adjust/100));return this.process("brightness",function(rgba){rgba.r+=adjust;rgba.g+=adjust;rgba.b+=adjust;return rgba;});});Filter.register("saturation",function(adjust){adjust*=-0.01;return this.process("saturation",function(rgba){var max;max=Math.max(rgba.r,rgba.g,rgba.b);if(rgba.r!==max)rgba.r+=(max-rgba.r)*adjust;if(rgba.g!==max)rgba.g+=(max-rgba.g)*adjust;if(rgba.b!==max)rgba.b+=(max-rgba.b)*adjust;return rgba;});});Logger=(function(){function Logger(){var name,_i,_len,_ref;_ref=['log','info','warn','error'];for(_i=0,_len=_ref.length;_i<_len;_i++){name=_ref[_i];this[name]=(function(name){return function(){if(window.console!=null){return window.console[name].apply(console,arguments);}};})(name);}
+return this.process("fillColor",function(rgba){rgba.r=color.r;rgba.g=color.g;rgba.b=color.b;return rgba;});});Filter.register("brightness",function(adjust){adjust=Math.floor(255*(adjust/100));return this.process("brightness",function(rgba){rgba.r+=adjust;rgba.g+=adjust;rgba.b+=adjust;return rgba;});});Filter.register("saturation",function(adjust){adjust*=-0.01;return this.process("saturation",function(rgba){var max;max=Math.max(rgba.r,rgba.g,rgba.b);if(rgba.r!==max)rgba.r+=(max-rgba.r)*adjust;if(rgba.g!==max)rgba.g+=(max-rgba.g)*adjust;if(rgba.b!==max)rgba.b+=(max-rgba.b)*adjust;return rgba;});});Filter.register("vibrance",function(adjust){adjust*=-1;return this.process("vibrance",function(rgba){var amt,avg,max;max=Math.max(rgba.r,rgba.g,rgba.b);avg=(rgba.r+rgba.g+rgba.b)/3;amt=((Math.abs(max-avg)*2/255)*adjust)/100;if(rgba.r!==max)rgba.r+=(max-rgba.r)*adjust;if(rgba.g!==max)rgba.g+=(max-rgba.g)*adjust;if(rgba.b!==max)rgba.b+=(max-rgba.b)*adjust;return rgba;});});Filter.register("greyscale",function(adjust){return this.process("greyscale",function(rgba){var avg;avg=0.3*rgba.r+0.59*rgba.g+0.11*rgba.b;rgba.r=avg;rgba.g=avg;rgba.b=avg;return rgba;});});Filter.register("contrast",function(adjust){adjust=Math.pow((adjust+100)/100,2);return this.process("contrast",function(rgba){rgba.r/=255;rgba.r-=0.5;rgba.r*=adjust;rgba.r+=0.5;rgba.r*=255;rgba.g/=255;rgba.g-=0.5;rgba.g*=adjust;rgba.g+=0.5;rgba.g*=255;rgba.b/=255;rgba.b-=0.5;rgba.b*=adjust;rgba.b+=0.5;rgba.b*=255;return rgba;});});Filter.register("hue",function(adjust){return this.process("hue",function(rgba){var h,hsv,rgb;hsv=Convert.rgbToHSV(rgba.r,rgba.g,rgba.b);h=hsv.h*100;h+=Math.abs(adjust);h=h%100;h/=100;hsv.h=h;rgb=Convert.hsvToRGB(hsv.h,hsv.s,hsv.v);rgb.a=rgba.a;return rgb;});});Filter.register("colorize",function(){var level,rgb;if(arguments.length===2){rgb=Convert.hexToRGB(arguments[0]);level=arguments[1];}else if(arguments.length===4){rgb={r:arguments[0],g:arguments[1],b:arguments[2]};level=arguments[3];}
+return this.process("colorize",function(rgba){rgba.r-=(rgba.r-rgb.r)*(level/100);rgba.g-=(rgba.g-rgb.g)*(level/100);rgba.b-=(rgba.b-rgb.b)*(level/100);return rgba;});});Filter.register("invert",function(){return this.process("invert",function(rgba){rgba.r=255-rgba.r;rgba.g=255-rgba.g;rgba.b=255-rgba.b;return rgba;});});Filter.register("sepia",function(adjust){if(adjust==null)adjust=100;adjust/=100;return this.process("sepia",function(rgba){rgba.r=Math.min(255,(rgba.r*(1-(0.607*adjust)))+(rgba.g*(0.769*adjust))+(rgba.b*(0.189*adjust)));rgba.g=Math.min(255,(rgba.r*(0.349*adjust))+(rgba.g*(1-(0.314*adjust)))+(rgba.b*(0.168*adjust)));rgba.b=Math.min(255,(rgba.r*(0.272*adjust))+(rgba.g*(0.534*adjust))+(rgba.b*(1-(0.869*adjust))));return rgba;});});Filter.register("gamma",function(adjust){return this.process("gamma",function(rgba){rgba.r=Math.pow(rgba.r/255,adjust)*255;rgba.g=Math.pow(rgba.g/255,adjust)*255;rgba.b=Math.pow(rgba.b/255,adjust)*255;return rgba;});});Filter.register("noise",function(adjust){adjust=Math.abs(adjust)*2.55;return this.process("noise",function(rgba){var rand;rand=Calculate.randomRange(adjust*-1,adjust);rgba.r+=rand;rgba.g+=rand;rgba.b+=rand;return rgba;});});Logger=(function(){function Logger(){var name,_i,_len,_ref;_ref=['log','info','warn','error'];for(_i=0,_len=_ref.length;_i<_len;_i++){name=_ref[_i];this[name]=(function(name){return function(){if(window.console!=null){return window.console[name].apply(console,arguments);}};})(name);}
this.debug=this.log;}
return Logger;})();Log=new Logger();PixelInfo=(function(){function PixelInfo(c){this.c=c;this.loc=0;}
PixelInfo.prototype.locationXY=function(){var x,y;y=this.dimensions.height-Math.floor(this.loc/(this.dimensions.width*4));x=(this.loc%(this.dimensions.width*4))/4;return{x:x,y:y};};return PixelInfo;})();RenderJob=(function(){RenderJob.Blocks=4;RenderJob.execute=function(instance,job,callback){var rj;rj=new RenderJob(instance,job,callback);switch(job.type){case Filter.Type.Single:return rj.executeFilter();}};function RenderJob(c,job,renderDone){this.c=c;this.job=job;this.renderDone=renderDone;}
RenderJob.prototype.executeFilter=function(){var blockN,blockPixelLength,end,j,lastBlockN,n,start,_ref,_results;var _this=this;this.blocksDone=0;n=this.c.pixelData.length;blockPixelLength=Math.floor((n/4)/RenderJob.Blocks);blockN=blockPixelLength*4;lastBlockN=blockN+((n/4)%RenderJob.Blocks)*4;if(this.job.type===Filter.Type.Single){_results=[];for(j=0,_ref=RenderJob.Blocks;0<=_ref?j<_ref:j>_ref;0<=_ref?j++:j--){start=j*blockN;end=start+(j===RenderJob.Blocks-1?lastBlockN:blockN);_results.push(setTimeout((function(j,start,end){return function(){return _this.renderBlock(j,start,end);};})(j,start,end),0));}
return _results;}};RenderJob.prototype.renderBlock=function(bnum,start,end){var data,i,pixelInfo,res;Log.debug("BLOCK #"+bnum+" - Filter: "+this.job.name+", Start: "+start+", End: "+end);data={r:0,g:0,b:0,a:0};pixelInfo=new PixelInfo(this.c);for(i=start;i<end;i+=4){pixelInfo.loc=i;data.r=this.c.pixelData[i];data.g=this.c.pixelData[i+1];data.b=this.c.pixelData[i+2];res=this.job.processFn.call(pixelInfo,data);this.c.pixelData[i]=clampRGB(res.r);this.c.pixelData[i+1]=clampRGB(res.g);this.c.pixelData[i+2]=clampRGB(res.b);}
-return this.blockFinished(bnum);};RenderJob.prototype.blockFinished=function(bnum){if(bnum>=0)Log.debug("Block #"+bnum+" finished! Filter: "+name);this.blocksDone++;if(this.blocksDone===RenderJob.Blocks||bnum===-1){if(bnum>=0)Log.debug("Filter "+this.job.name+" finished!");if(bnum<0)Log.debug("Kernel filter "+this.job.name+" finished!");return this.renderDone();}};return RenderJob;})();Store=(function(){function Store(){}
+return this.blockFinished(bnum);};RenderJob.prototype.blockFinished=function(bnum){if(bnum>=0){Log.debug("Block #"+bnum+" finished! Filter: "+this.job.name);}
+this.blocksDone++;if(this.blocksDone===RenderJob.Blocks||bnum===-1){if(bnum>=0)Log.debug("Filter "+this.job.name+" finished!");if(bnum<0)Log.debug("Kernel filter "+this.job.name+" finished!");return this.renderDone();}};return RenderJob;})();Store=(function(){function Store(){}
Store.items={};Store.has=function(search){return this.items[search]!=null;};Store.get=function(search){return this.items[search];};Store.put=function(name,obj){return this.items[name]=obj;};Store.execute=function(search,callback){return callback.call(this.get(search),this.get(search));};return Store;})();}).call(this);
View
6 src/calculate.coffee
@@ -1,3 +1,7 @@
class Calculate
@distance: (x1, y1, x2, y2) ->
- Math.sqrt Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
+ Math.sqrt Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
+
+ @randomRange: (min, max, float = false) ->
+ rand = min + (Math.random() * (max - min))
+ return if float then rand.toFixed(float) else Math.round(rand)
View
122 src/filters.coffee
@@ -35,4 +35,124 @@ Filter.register "saturation", (adjust) ->
rgba.g += (max - rgba.g) * adjust if rgba.g isnt max
rgba.b += (max - rgba.b) * adjust if rgba.b isnt max
rgba
-
+
+Filter.register "vibrance", (adjust) ->
+ adjust *= -1
+
+ @process "vibrance", (rgba) ->
+ max = Math.max rgba.r, rgba.g, rgba.b
+ avg = (rgba.r + rgba.g + rgba.b) / 3
+ amt = ((Math.abs(max - avg) * 2 / 255) * adjust) / 100
+
+ rgba.r += (max - rgba.r) * adjust if rgba.r isnt max
+ rgba.g += (max - rgba.g) * adjust if rgba.g isnt max
+ rgba.b += (max - rgba.b) * adjust if rgba.b isnt max
+ rgba
+
+Filter.register "greyscale", (adjust) ->
+ @process "greyscale", (rgba) ->
+ # Calculate the average value of the 3 color channels
+ # using the special factors
+ avg = 0.3 * rgba.r + 0.59 * rgba.g + 0.11 * rgba.b
+
+ rgba.r = avg
+ rgba.g = avg
+ rgba.b = avg
+ rgba
+
+Filter.register "contrast", (adjust) ->
+ adjust = Math.pow((adjust + 100) / 100, 2)
+
+ @process "contrast", (rgba) ->
+ # Red channel
+ rgba.r /= 255;
+ rgba.r -= 0.5;
+ rgba.r *= adjust;
+ rgba.r += 0.5;
+ rgba.r *= 255;
+
+ # Green channel
+ rgba.g /= 255;
+ rgba.g -= 0.5;
+ rgba.g *= adjust;
+ rgba.g += 0.5;
+ rgba.g *= 255;
+
+ # Blue channel
+ rgba.b /= 255;
+ rgba.b -= 0.5;
+ rgba.b *= adjust;
+ rgba.b += 0.5;
+ rgba.b *= 255;
+
+ rgba
+
+Filter.register "hue", (adjust) ->
+ @process "hue", (rgba) ->
+ hsv = Convert.rgbToHSV rgba.r, rgba.g, rgba.b
+
+ h = hsv.h * 100
+ h += Math.abs adjust
+ h = h % 100
+ h /= 100
+ hsv.h = h
+
+ rgb = Convert.hsvToRGB hsv.h, hsv.s, hsv.v
+ rgb.a = rgba.a
+ rgb
+
+Filter.register "colorize", ->
+ if arguments.length is 2
+ rgb = Convert.hexToRGB(arguments[0])
+ level = arguments[1]
+ else if arguments.length is 4
+ rgb =
+ r: arguments[0]
+ g: arguments[1]
+ b: arguments[2]
+
+ level = arguments[3]
+
+ @process "colorize", (rgba) ->
+ rgba.r -= (rgba.r - rgb.r) * (level / 100)
+ rgba.g -= (rgba.g - rgb.g) * (level / 100)
+ rgba.b -= (rgba.b - rgb.b) * (level / 100)
+ rgba
+
+Filter.register "invert", ->
+ @process "invert", (rgba) ->
+ rgba.r = 255 - rgba.r
+ rgba.g = 255 - rgba.g
+ rgba.b = 255 - rgba.b
+ rgba
+
+Filter.register "sepia", (adjust = 100) ->
+ adjust /= 100
+
+ @process "sepia", (rgba) ->
+ # All three color channels have special conversion factors that
+ # define what sepia is. Here we adjust each channel individually,
+ # with the twist that you can partially apply the sepia filter.
+ rgba.r = Math.min(255, (rgba.r * (1 - (0.607 * adjust))) + (rgba.g * (0.769 * adjust)) + (rgba.b * (0.189 * adjust)));
+ rgba.g = Math.min(255, (rgba.r * (0.349 * adjust)) + (rgba.g * (1 - (0.314 * adjust))) + (rgba.b * (0.168 * adjust)));
+ rgba.b = Math.min(255, (rgba.r * (0.272 * adjust)) + (rgba.g * (0.534 * adjust)) + (rgba.b * (1- (0.869 * adjust))));
+
+ rgba
+
+Filter.register "gamma", (adjust) ->
+ @process "gamma", (rgba) ->
+ rgba.r = Math.pow(rgba.r / 255, adjust) * 255
+ rgba.g = Math.pow(rgba.g / 255, adjust) * 255
+ rgba.b = Math.pow(rgba.b / 255, adjust) * 255
+ rgba
+
+Filter.register "noise", (adjust) ->
+ adjust = Math.abs(adjust) * 2.55
+
+ @process "noise", (rgba) ->
+ rand = Calculate.randomRange adjust * -1, adjust
+
+ rgba.r += rand
+ rgba.g += rand
+ rgba.b += rand
+ rgba
View
4 src/renderjob.coffee
@@ -1,6 +1,6 @@
class RenderJob
@Blocks = 4
-
+
@execute: (instance, job, callback) ->
rj = new RenderJob instance, job, callback
@@ -50,7 +50,7 @@ class RenderJob
@blockFinished(bnum)
blockFinished: (bnum) ->
- Log.debug "Block ##{bnum} finished! Filter: #{name}" if bnum >= 0
+ Log.debug "Block ##{bnum} finished! Filter: #{@job.name}" if bnum >= 0
@blocksDone++
if @blocksDone is RenderJob.Blocks or bnum is -1

0 comments on commit 3d9e663

Please sign in to comment.
Something went wrong with that request. Please try again.