// These patterns are used by the pattern dither effect (function(){ var patterns = {} patterns[2] = [ 0, 2, 3, 1 ] patterns[3] = [ 0, 5, 3, 8, 1, 6, 4, 7, 2, ] patterns[4] = [ 0, 8, 2, 10, 6, 14, 4, 12, 3, 11, 1, 9, 5, 13, 7, 15 ] CanvasQuery.Wrapper.prototype.pattern2Dither = function() { this.patternDither(patterns[2]) } CanvasQuery.Wrapper.prototype.pattern3Dither = function() { this.patternDither(patterns[3]) } CanvasQuery.Wrapper.prototype.pattern4Dither = function() { this.patternDither(patterns[4]) } CanvasQuery.Wrapper.prototype.pattern2LiteDither = function() { this.patternLiteDither(patterns[2]) } CanvasQuery.Wrapper.prototype.pattern3LiteDither = function() { this.patternLiteDither(patterns[3]) } CanvasQuery.Wrapper.prototype.pattern4LiteDither = function() { this.patternLiteDither(patterns[4]) } })() // Threshold dither, black if less than n, white otherwise CanvasQuery.Wrapper.prototype.threshold = function(n) { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var width = this.canvas.width var height = this.canvas.height if (n < 1) n *= 255 for (var i = 0; i < height; i++) { for (var j = 0; j < width; j++) { var a = 4 * (i*width + j) var val = (bitmapData[a] + bitmapData[a+1] + bitmapData[a+2]) / 3 var lum = val > n ? 255 : 0 bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Random dither, black if less than a random number (chosen per pixel), white otherwise CanvasQuery.Wrapper.prototype.randomDither = function() { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var width = this.canvas.width var height = this.canvas.height for (var i = 0; i < height; i++) { for (var j = 0; j < width; j++) { var a = 4 * (i*width + j) var val = (bitmapData[a] + bitmapData[a+1] + bitmapData[a+2]) / (3*255) var lum = val > Math.random() ? 255 : 0 bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Pattern dither, uses a square matrix of dither steps (see patterns above) to dither CanvasQuery.Wrapper.prototype.patternDither = function(pattern) { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var width = this.canvas.width var height = this.canvas.height var n = Math.floor(Math.sqrt(pattern.length)) var len = pattern.length - 1 for (var i = 0; i < height; i++) { for (var j = 0; j < width; j++) { var p = ((i % n) * n) + (j % n) var a = 4 * (i*width + j) var val = (bitmapData[a] + bitmapData[a+1] + bitmapData[a+2]) / (3*255) var lum = val > pattern[p]/len ? 255 : 0 bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Light version of pattern dither, uses the wrong array size CanvasQuery.Wrapper.prototype.patternLiteDither = function(pattern) { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var width = this.canvas.width var height = this.canvas.height var n = Math.floor(Math.sqrt(pattern.length)) var len = pattern.length for (var i = 0; i < height; i++) { for (var j = 0; j < width; j++) { var p = ((i % n) * n) + (j % n) var a = 4 * (i*width + j) var val = (bitmapData[a] + bitmapData[a+1] + bitmapData[a+2]) / (3*255) var lum = val > pattern[p]/len ? 255 : 0 bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Floyd-Steinberg error diffusion, propagates error down and to the right CanvasQuery.Wrapper.prototype.floydSteinbergDither = function(n) { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var mask = this.grayscaleToMask() var width = this.canvas.width var height = this.canvas.height for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var p = (y*width + x) var a = 4 * p var val = mask[p] var lum = val > 127 ? 255 : 0 var error = val - lum if (x < width-1) mask[ (y*width) + x+1 ] += 7/16 * error if (y < height-1 && x > 0) mask[ ((y+1)*width) + x-1 ] += 5/16 * error if (y < height-1) mask[ ((y+1)*width) + x ] += 3/16 * error if (y < height-1 && x < width-1) mask[ ((y+1)*width) + x+1 ] += 1/16 * error bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Rightwise diffusion dither, propagates error to the right only CanvasQuery.Wrapper.prototype.rightDither = function(n) { var bitmap = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) var bitmapData = bitmap.data var mask = this.grayscaleToMask() var width = this.canvas.width var height = this.canvas.height for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var p = (y*width + x) var a = 4 * p var val = mask[p] var lum = val > 127 ? 255 : 0 var error = val - lum if (x < width-1) mask[ (y*width) + x+1 ] += 7/16 * error bitmapData[a] = bitmapData[a+1] = bitmapData[a+2] = lum } } this.context.putImageData(bitmap, 0, 0); return this; } // Print-style halftone effect CanvasQuery.Wrapper.prototype.halftone = function(radius, angle) { var mask = this.grayscaleToMask() this.fillStyle("#fff") this.fillRect(0, 0, this.canvas.width, this.canvas.height) var diameter = radius*2 var TWO_PI = Math.PI*2 var angle = angle / 180 * Math.PI var cos = Math.cos(angle) var sin = Math.sin(angle) var xstep = cos * radius var ystep = sin * radius var w = this.canvas.width var h = this.canvas.height this.fillStyle("#000") for (var i = -w; i < w; i++) { for (var j = -h; j < h; j++) { var x = i * ystep - j * xstep var y = i * xstep + j * ystep if (x > -diameter && y > -diameter && x < w+diameter && y < h+diameter) { circle(this,x,y) } } } function circle(cq,x,y) { var xx = x < 0 ? 0 : x > w ? w - 1 : x; var yy = y < 0 ? 0 : y > h ? h - 1 : y; var r = (1 - Math.pow( mask[ ~~(~~yy*w+(xx)) ] / 255, Math.E/4 )) * radius cq.beginPath() .arc(x,y,r,0,TWO_PI) .closePath() .fill(); } return this; }