├── .gitignore ├── History.md ├── Makefile ├── Readme.md ├── component.json ├── index.html ├── index.js ├── maru.jpg ├── maru_edges.jpg └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | build 3 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.1.0 / 2012-09-25 3 | ================== 4 | 5 | * refactor into a progressive api 6 | 7 | 0.0.2 / 2012-09-25 8 | ================== 9 | 10 | * add convolve.canvas(canvas, matrix) 80% use-case method 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: components index.js 3 | @component build --dev 4 | 5 | components: 6 | @component install --dev 7 | 8 | clean: 9 | rm -fr build components template.js 10 | 11 | .PHONY: clean 12 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # convolve 3 | 4 | Canvas [convolution](http://en.wikipedia.org/wiki/Convolution) filter. 5 | 6 | ![](maru_edges.jpg) 7 | 8 | ## API 9 | 10 | ### convolve(matrix) 11 | 12 | Return a new convolution `Filter` with the given `matrix`. 13 | 14 | ### Filter#factor(n) 15 | 16 | Change the factor to `n`, defaults to `1`. 17 | 18 | ### Filter#bias(n) 19 | 20 | Change the bias to `n`, defaults to `0`. 21 | 22 | ### Filter#width(n) 23 | 24 | Canvas width. 25 | 26 | ### Filter#height(n) 27 | 28 | Canvas height. 29 | 30 | ### Filter#apply(input, result) 31 | 32 | Apply the convolution filter to the `input` ImageData, populating 33 | the `result` ImageData. This is a lower-level method, you most 34 | likely want to apply to the entire canvas, in which case use below: 35 | 36 | ### Filter#canvas(canvas) 37 | 38 | Apply the convolution filter to the entire `canvas` 39 | and immediately draw the results. 40 | 41 | ## Example 42 | 43 | ```js 44 | var convolve = require('convolve'); 45 | var canvas = document.querySelector('canvas'); 46 | var ctx = canvas.getContext('2d'); 47 | 48 | var img = new Image; 49 | img.onload = draw; 50 | img.src = 'maru.jpg'; 51 | 52 | var sharpen = [ 53 | [-1, -1, -1], 54 | [-1, 9, -1], 55 | [-1, -1, -1] 56 | ]; 57 | 58 | var blur = [ 59 | [0, .2, 0], 60 | [.2, .2, .2], 61 | [0, .2, 0], 62 | ]; 63 | 64 | // factor 1 / 7 65 | var motionBlur = [ 66 | [1, 0, 0, 0, 0, 0, 0], 67 | [0, 1, 0, 0, 0, 0, 0], 68 | [0, 0, 1, 0, 0, 0, 0], 69 | [0, 0, 0, 1, 0, 0, 0], 70 | [0, 0, 0, 0, 1, 0, 0], 71 | [0, 0, 0, 0, 0, 1, 0], 72 | [0, 0, 0, 0, 0, 0, 1] 73 | ]; 74 | 75 | var edges = [ 76 | [0, -1, 0], 77 | [-1, 4, -1], 78 | [0, -1, 0] 79 | ]; 80 | 81 | function draw() { 82 | canvas.width = img.width; 83 | canvas.height = img.height; 84 | ctx.drawImage(img, 0, 0); 85 | convolve(motionBlur) 86 | .factor(1 / 7) 87 | .canvas(canvas); 88 | } 89 | ``` 90 | 91 | # License 92 | 93 | MIT 94 | 95 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convolve", 3 | "repo": "component/convolve", 4 | "description": "Canvas convolution filters", 5 | "version": "0.1.0", 6 | "keywords": ["canvas", "sharpen", "blur", "edges"], 7 | "dependencies": {}, 8 | "development": {}, 9 | "scripts": [ 10 | "index.js" 11 | ] 12 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 61 | 62 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Expose `Filter`. 4 | */ 5 | 6 | module.exports = Filter; 7 | 8 | /** 9 | * Initialize a new `Filter` with the given `matrix`. 10 | * 11 | * @param {Array} matrix 12 | * @api public 13 | */ 14 | 15 | function Filter(matrix) { 16 | if (!(this instanceof Filter)) return new Filter(matrix); 17 | if (!matrix) throw new TypeError('convolution matrix required'); 18 | this.matrix = matrix; 19 | this.factor(1); 20 | this.bias(0); 21 | } 22 | 23 | /** 24 | * Set width. 25 | * 26 | * @param {Number} n 27 | * @return {Filter} 28 | * @api public 29 | */ 30 | 31 | Filter.prototype.width = function(n){ 32 | this.w = n; 33 | return this; 34 | }; 35 | 36 | /** 37 | * Set height. 38 | * 39 | * @param {Number} n 40 | * @return {Filter} 41 | * @api public 42 | */ 43 | 44 | Filter.prototype.height = function(n){ 45 | this.h = n; 46 | return this; 47 | }; 48 | 49 | /** 50 | * Set factor, this may be used to 51 | * ensure that all matrix elements 52 | * produce a sum of 1. 53 | * 54 | * @param {Number} n 55 | * @return {Filter} 56 | * @api public 57 | */ 58 | 59 | Filter.prototype.factor = function(n){ 60 | this._factor = n; 61 | return this; 62 | }; 63 | 64 | /** 65 | * Set bias, this may be used to 66 | * brighten or darken the result. 67 | * 68 | * @param {Number} n 69 | * @return {Filter} 70 | * @api public 71 | */ 72 | 73 | Filter.prototype.bias = function(n){ 74 | this._bias = n; 75 | return this; 76 | }; 77 | 78 | /** 79 | * Apply the filter to `input` and populate `result`. 80 | * 81 | * @param {ImageData} input 82 | * @param {ImageData} result 83 | * @api public 84 | */ 85 | 86 | Filter.prototype.apply = function(input, result){ 87 | var data = input.data; 88 | var out = result.data; 89 | var width = this.w; 90 | var height = this.h; 91 | var matrix = this.matrix; 92 | var w = matrix[0].length; 93 | var h = matrix.length; 94 | var half = Math.floor(h / 2); 95 | var factor = this._factor; 96 | var bias = this._bias; 97 | 98 | for (var y = 0; y < height - 1; y++) { 99 | for (var x = 0; x < width - 1; x++) { 100 | var px = (y * width + x) * 4; 101 | var r = 0, g = 0, b = 0; 102 | 103 | for (var cy = 0; cy < w; ++cy) { 104 | for (var cx = 0; cx < h; ++cx) { 105 | var cpx = ((y + (cy - half)) * width + (x + (cx - half))) * 4; 106 | r += data[cpx + 0] * matrix[cy][cx]; 107 | g += data[cpx + 1] * matrix[cy][cx]; 108 | b += data[cpx + 2] * matrix[cy][cx]; 109 | } 110 | } 111 | 112 | out[px + 0] = factor * r + bias; 113 | out[px + 1] = factor * g + bias; 114 | out[px + 2] = factor * b + bias; 115 | out[px + 3] = data[px + 3]; 116 | } 117 | } 118 | }; 119 | 120 | /** 121 | * Apply filter to the entire `canvas`. 122 | * 123 | * @param {Canvas} canvas 124 | * @api public 125 | */ 126 | 127 | Filter.prototype.canvas = function(canvas){ 128 | var w = canvas.width; 129 | var h = canvas.height; 130 | var ctx = canvas.getContext('2d'); 131 | var data = ctx.getImageData(0, 0, w, h); 132 | var result = ctx.createImageData(w, h); 133 | this.width(w); 134 | this.height(h); 135 | this.apply(data, result); 136 | ctx.putImageData(result, 0, 0); 137 | }; 138 | -------------------------------------------------------------------------------- /maru.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/component/convolve/f15db0f14cc57a213805276fc14b8638640b1b1b/maru.jpg -------------------------------------------------------------------------------- /maru_edges.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/component/convolve/f15db0f14cc57a213805276fc14b8638640b1b1b/maru_edges.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convolve", 3 | "description": "Canvas convolution filters", 4 | "version": "0.1.0", 5 | "keywords": ["canvas", "sharpen", "blur", "edges"], 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/component/convolve.git" 9 | }, 10 | "dependencies": {}, 11 | "development": {}, 12 | "main": "index.js" 13 | } 14 | --------------------------------------------------------------------------------