summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJules <jules@asdf.us>2015-05-23 13:31:55 -0400
committerJules <jules@asdf.us>2015-05-23 13:31:55 -0400
commit3d57fba4ae63fe996a3d10bf5e3e32c516933526 (patch)
tree9a8a81635c7f875d8da3c5d6698f8bf0bb1044a4
displacement projHEADmaster
-rw-r--r--.gitignore3
-rw-r--r--cam.html207
-rw-r--r--canvasquery.js1599
-rw-r--r--dat.gui.min.js82
-rw-r--r--index.html197
5 files changed, 2088 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..91269fc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.webm
+*.png
+*.jpg
diff --git a/cam.html b/cam.html
new file mode 100644
index 0000000..614cd51
--- /dev/null
+++ b/cam.html
@@ -0,0 +1,207 @@
+<!doctype html>
+<html>
+<head>
+<style>body{background:white}</style>
+</head>
+<body>
+<div id="state"></div><br>
+<button id="clear">clear</button>
+<div id="buttons"></div>
+<div id="canvas"></div>
+<video id="vid" style="display: none"></video>
+<button id="camera" style="display:none">camera</button>
+</body>
+<script src="/js/jquery.js"></script>
+<script src="http://asdf.us/dither/js/camera.js"></script>
+<script src="dat.gui.min.js"></script>
+<script src="canvasquery.js"></script>
+<script>
+
+/*
+var videos = "diesel1.mov.webm diesel2.mov.webm diesel3.mov.webm diesel4.mov.webm diesel5.mov.webm MVI_00245.mov.webm MVI_0026_17(23).mov.webm MVI_1671_2.mov.webm MVI_50545.mov.webm MVI_5054_16(21).mov.webm MVI_0026_1319.mov.webm MVI_0026_38.mov.webm MVI_1671_3.mov.webm MVI_5054_10(15).mov.webm smoke.webm MVI_0026_1420.mov.webm MVI_0026_915.mov.webm MVI_1671_4.mov.webm MVI_5054_12(17).mov.webm MVI_0026_1521.mov.webm MVI_1671.mov.webm MVI_1671_5.mov.webm MVI_5054_13(18).mov.webm MVI_0026_16.mov.webm MVI_1671_1.mov.webm MVI_5045.mov.webm MVI_5054_15(20).mov.webm".split(" ")
+videos.forEach(function(v){
+ var button = document.createElement("button")
+ button.innerHTML = v
+ button.onclick = function(){
+ video.src = v
+ }
+ document.getElementById("buttons").appendChild(button)
+})
+*/
+
+var cc = cq(1,1)
+min = Math.min
+$("#camera").trigger("click")
+var checker = setInterval(function(){
+ if (window.cam) {
+ clearInterval(checker)
+ video = cam
+ loaded()
+ loadGUI()
+ }
+}, 200)
+
+function loaded () {
+ vw = video.videoWidth
+ vh = video.videoHeight
+ videoSrc = cq(vw, vh)
+ video.setAttribute("loop", "loop")
+ video.play()
+ loadImage( "nn-d-draft-1-crop-1024.jpg")
+
+}
+
+
+// video.src = "174708-1c2pRjKN-aI.mp4";
+// video.src = "ad.mp4";
+// video.src = "gopro.mp4";
+
+var imageData, imageDataData, imageCopy, imageCopyData, destCopy, logo
+
+function loadImage(src){
+ document.getElementById("canvas").innerHTML = ""
+ var image = new Image ();
+ image.addEventListener("load", function(){
+ w = image.naturalWidth
+ h = image.naturalHeight
+ dest = cq(w, h).drawImage( image, 0, 0, w, h, 0,0,w,vh ).appendTo("#canvas")
+ destCopy = cq(w, h)
+ imageData = dest.getImageData( 0, 0, w, h )
+ imageCopy = dest.getImageData( 0, 0, w, h )
+ imageDataData = imageData.data
+ imageCopyData = imageCopy.data
+
+ for (var i = 0, len = imageCopyData; i < len; i++) {
+ imageCopyData[i] = 0
+ }
+ gray = new Array (imageCopyData/4|0)
+ for (var i = 0, len = imageCopyData/4; i < len; i++) {
+ gray[i] = 0
+ }
+
+ var logoImage = new Image ();
+ logoImage.addEventListener("load", function(){
+ logo = cq().drawImage( logoImage, 0,0, logoImage.naturalWidth, logoImage.naturalHeight )
+ })
+ logoImage.src = "DIESEL_LOGO_BLK.png"
+
+ }, false);
+ image.src = src;
+}
+
+var videoSrc, dest, mask, fill, imageData, gray;
+var ready = false;
+
+function go () {
+}
+
+(function animate(){
+ requestAnimationFrame(animate);
+ if (! videoSrc || ! imageData || ! logo) return;
+ render();
+})();
+
+function render(){
+ videoSrc.drawImage(video, 0, 0, vw, vh)
+ var videoData = videoSrc.getImageData(0, 0, vw, vh)
+ var videoDataData = videoData.data
+ var x, y, xx, yy, val, realVal
+ var j = 0
+
+ var rAvg = params.rAvg, gAvg = params.gAvg, bAvg = params.bAvg, valAvg = params.valAvg
+ var xfac = params.xFactor, yfac = params.yFactor, valfac = params.valFactor
+
+ for (var i = 0, len = videoDataData.length/4 |0; i < len; i++) {
+
+// x = i % vw
+// y = ~~(i/vw)
+//
+// xx = x % vw
+// yy = y % vh
+
+ xx = i % vw
+ yy = ~~(i/vw)
+
+ x = xx
+ y = yy
+
+ jj = (y*w + x) * 4
+
+ realVal = videoDataData[ ((yy*vw + xx)*4) ]
+ val = gray[i] = avga( valAvg, gray[i], realVal )
+
+ val *= valfac
+
+ x = clamp( ~~(x + val * xfac), 0, w-1 ) | 0
+ y = clamp( ~~(y + val * yfac), 0, h-1 ) | 0
+
+ j = clamp( (y*w + x), 0, len-1) * 4
+
+ j = ~~ j
+ jj = ~~ jj
+
+ imageCopyData[jj] = avga(rAvg, imageDataData[j], imageCopyData[j])
+ imageCopyData[jj+1] = avga(gAvg, imageDataData[j+1], imageCopyData[j+1])
+ imageCopyData[jj+2] = avga(bAvg, imageDataData[j+2], imageCopyData[j+2])
+
+ imageCopyData[jj+3] = 255
+
+ }
+
+ destCopy.putImageData(imageCopy, 0, 0)
+
+if (params.logoAlpha) {
+ dest.context.globalAlpha = params.logoAlpha
+ dest.context.globalCompositeOperation = 'destination-in'
+ dest.drawImage(logo.canvas, params.logoX, params.logoY, w*params.logoScale,h*params.logoScale)
+}
+
+ dest.context.globalAlpha = params.imageAlpha
+ dest.context.globalCompositeOperation = 'source-over'
+ dest.drawImage(destCopy.canvas, 0, 0, w,h)
+
+ dest.context.globalAlpha = 1.0
+// dest.context.globalAlpha = 0.2
+// dest.context.globalCompositeOperation = 'destination-in'
+// dest.drawImage(logo.canvas, params.logoX, params.logoY, w*params.logoScale,h*params.logoScale)
+}
+var params = {}
+params.logoX = 166
+params.logoY = 133
+params.logoScale = 0.7
+params.logoAlpha = 0.0
+params.imageAlpha = 1.0
+params.rAvg = 5
+params.gAvg = 5
+params.bAvg = 5
+params.valAvg = 2
+params.xFactor = 1
+params.yFactor = -1
+params.valFactor = 1
+
+function loadGUI() {
+ var gui = new dat.GUI();
+ gui.add(params, 'logoX', 0, w);
+ gui.add(params, 'logoY', 0, h);
+ gui.add(params, 'logoScale', 0.2, 1);
+ gui.add(params, 'logoAlpha', 0.0, 1);
+ gui.add(params, 'imageAlpha', 0.0, 1);
+ gui.add(params, 'rAvg', 2, 20);
+ gui.add(params, 'gAvg', 2, 20);
+ gui.add(params, 'bAvg', 2, 20);
+ gui.add(params, 'valAvg', 2, 20);
+ gui.add(params, 'xFactor', -5, 5);
+ gui.add(params, 'yFactor', -5, 5);
+ gui.add(params, 'valFactor', -20, 20);
+}
+
+document.getElementById("clear").onclick = function(){
+ dest.clearRect(0,0,w,h)
+}
+
+function avga(n,a,b) { return ((a*(~~n-1) + b)/~~n)|0 }
+function avgb(n,b,a) { return ((a*(~~n-1) + b)/~~n)|0 }
+function clamp(n,a,b) { return n < a ? a : n > b ? b : n }
+</script>
+</html>
+
diff --git a/canvasquery.js b/canvasquery.js
new file mode 100644
index 0000000..70257ea
--- /dev/null
+++ b/canvasquery.js
@@ -0,0 +1,1599 @@
+/*
+ Canvas Query 0.8.1
+ http://canvasquery.org
+ (c) 2012-2013 http://rezoner.net
+ Canvas Query may be freely distributed under the MIT license.
+*/
+
+(function(window, undefined) {
+
+ var MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
+
+
+ window.requestAnimationFrame = (function() {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+ })();
+
+
+ var $ = function(selector) {
+ if(arguments.length === 0) {
+ var canvas = $.createCanvas(window.innerWidth, window.innerHeight);
+ window.addEventListener("resize", function() {
+ // canvas.width = window.innerWidth;
+ // canvas.height = window.innerHeight;
+ });
+ } else if(typeof selector === "string") {
+ var canvas = document.querySelector(selector);
+ } else if(typeof selector === "number") {
+ var canvas = $.createCanvas(arguments[0], arguments[1]);
+ } else if(selector instanceof Image || selector instanceof HTMLImageElement) {
+ var canvas = $.createCanvas(selector);
+ } else if(selector instanceof $.Wrapper) {
+ return selector;
+ } else {
+ var canvas = selector;
+ }
+
+ return new $.Wrapper(canvas);
+ }
+
+ $.extend = function() {
+ for(var i = 1; i < arguments.length; i++) {
+ for(var j in arguments[i]) {
+ arguments[0][j] = arguments[i][j];
+ }
+ }
+
+ return arguments[0];
+ }
+
+ $.augment = function() {
+ for(var i = 1; i < arguments.length; i++) {
+ _.extend(arguments[0], arguments[i]);
+ arguments[i](arguments[0]);
+ }
+ }
+
+ $.extend($, {
+
+ keycodes: {
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 45: "insert",
+ 46: "delete",
+ 8: "backspace",
+ 9: "tab",
+ 13: "enter",
+ 16: "shift",
+ 17: "ctrl",
+ 18: "alt",
+ 19: "pause",
+ 20: "capslock",
+ 27: "escape",
+ 32: "space",
+ 33: "pageup",
+ 34: "pagedown",
+ 35: "end",
+ 112: "f1",
+ 113: "f2",
+ 114: "f3",
+ 115: "f4",
+ 116: "f5",
+ 117: "f6",
+ 118: "f7",
+ 119: "f8",
+ 120: "f9",
+ 121: "f10",
+ 122: "f11",
+ 123: "f12",
+ 144: "numlock",
+ 145: "scrolllock",
+ 186: "semicolon",
+ 187: "equal",
+ 188: "comma",
+ 189: "dash",
+ 190: "period",
+ 191: "slash",
+ 192: "graveaccent",
+ 219: "openbracket",
+ 220: "backslash",
+ 221: "closebraket",
+ 222: "singlequote"
+ },
+
+ cleanArray: function(array, property) {
+
+ var lastArgument = arguments[arguments.length - 1];
+ var isLastArgumentFunction = typeof lastArgument === "function";
+
+ for(var i = 0, len = array.length; i < len; i++) {
+ if(array[i] === null || (property && array[i][property])) {
+ if(isLastArgumentFunction) {
+ lastArgument(array[i]);
+ }
+ array.splice(i--, 1);
+ len--;
+ }
+ }
+ },
+
+ specialBlendFunctions: ["color", "value", "hue", "saturation"],
+
+ blendFunctions: {
+ normal: function(a, b) {
+ return b;
+ },
+
+ overlay: function(a, b) {
+ a /= 255;
+ b /= 255;
+ var result = 0;
+
+ if(a < 0.5) result = 2 * a * b;
+ else result = 1 - 2 * (1 - a) * (1 - b);
+
+ return Math.min(255, Math.max(0, result * 255 | 0));
+ },
+
+ hardLight: function(a, b) {
+ return $.blendFunctions.overlay(b, a);
+ },
+
+ softLight: function(a, b) {
+ a /= 255;
+ b /= 255;
+
+ var v = (1 - 2 * b) * (a * a) + 2 * b * a;
+ return $.limitValue(v * 255, 0, 255);
+ },
+
+ dodge: function(a, b) {
+ return Math.min(256 * a / (255 - b + 1), 255);
+ },
+
+ burn: function(a, b) {
+ return 255 - Math.min(256 * (255 - a) / (b + 1), 255);
+ },
+
+ multiply: function(a, b) {
+ return b * a / 255;
+ },
+
+ divide: function(a, b) {
+ return Math.min(256 * a / (b + 1), 255);
+ },
+
+ screen: function(a, b) {
+ return 255 - (255 - b) * (255 - a) / 255;
+ },
+
+ grainExtract: function(a, b) {
+ return $.limitValue(a - b + 128, 0, 255);
+ },
+
+ grainMerge: function(a, b) {
+ return $.limitValue(a + b - 128, 0, 255);
+ },
+
+ difference: function(a, b) {
+ return Math.abs(a - b);
+ },
+
+ addition: function(a, b) {
+ return Math.min(a + b, 255);
+ },
+
+ substract: function(a, b) {
+ return Math.max(a - b, 0);
+ },
+
+ darkenOnly: function(a, b) {
+ return Math.min(a, b);
+ },
+
+ lightenOnly: function(a, b) {
+ return Math.max(a, b);
+ },
+
+ color: function(a, b) {
+ var aHSL = $.rgbToHsl(a);
+ var bHSL = $.rgbToHsl(b);
+
+ return $.hslToRgb(bHSL[0], bHSL[1], aHSL[2]);
+ },
+
+ hue: function(a, b) {
+ var aHSV = $.rgbToHsv(a);
+ var bHSV = $.rgbToHsv(b);
+
+ if(!bHSV[1]) return $.hsvToRgb(aHSV[0], aHSV[1], aHSV[2]);
+ else return $.hsvToRgb(bHSV[0], aHSV[1], aHSV[2]);
+ },
+
+ value: function(a, b) {
+ var aHSV = $.rgbToHsv(a);
+ var bHSV = $.rgbToHsv(b);
+
+ return $.hsvToRgb(aHSV[0], aHSV[1], bHSV[2]);
+ },
+
+ saturation: function(a, b) {
+ var aHSV = $.rgbToHsv(a);
+ var bHSV = $.rgbToHsv(b);
+
+ return $.hsvToRgb(aHSV[0], bHSV[1], aHSV[2]);
+ }
+ },
+
+ blend: function(below, above, mode, mix) {
+ if(typeof mix === "undefined") mix = 1;
+
+ var below = $(below);
+ var above = $(above);
+
+ var belowCtx = below.context;
+ var aboveCtx = above.context;
+
+ var belowData = belowCtx.getImageData(0, 0, below.canvas.width, below.canvas.height);
+ var aboveData = aboveCtx.getImageData(0, 0, above.canvas.width, above.canvas.height);
+
+ var belowPixels = belowData.data;
+ var abovePixels = aboveData.data;
+
+ var imageData = this.createImageData(below.canvas.width, below.canvas.height);
+ var pixels = imageData.data;
+
+ var blendingFunction = $.blendFunctions[mode];
+
+ if($.specialBlendFunctions.indexOf(mode) !== -1) {
+ for(var i = 0, len = belowPixels.length; i < len; i += 4) {
+ var rgb = blendingFunction([belowPixels[i + 0], belowPixels[i + 1], belowPixels[i + 2]], [abovePixels[i + 0], abovePixels[i + 1], abovePixels[i + 2]]);
+
+ pixels[i + 0] = belowPixels[i + 0] + (rgb[0] - belowPixels[i + 0]) * mix;
+ pixels[i + 1] = belowPixels[i + 1] + (rgb[1] - belowPixels[i + 1]) * mix;
+ pixels[i + 2] = belowPixels[i + 2] + (rgb[2] - belowPixels[i + 2]) * mix;
+
+ pixels[i + 3] = belowPixels[i + 3];
+ }
+ } else {
+
+ for(var i = 0, len = belowPixels.length; i < len; i += 4) {
+ var r = blendingFunction(belowPixels[i + 0], abovePixels[i + 0]);
+ var g = blendingFunction(belowPixels[i + 1], abovePixels[i + 1]);
+ var b = blendingFunction(belowPixels[i + 2], abovePixels[i + 2]);
+
+ pixels[i + 0] = belowPixels[i + 0] + (r - belowPixels[i + 0]) * mix;
+ pixels[i + 1] = belowPixels[i + 1] + (g - belowPixels[i + 1]) * mix;
+ pixels[i + 2] = belowPixels[i + 2] + (b - belowPixels[i + 2]) * mix;
+
+ pixels[i + 3] = belowPixels[i + 3];
+ }
+ }
+
+ below.context.putImageData(imageData, 0, 0);
+
+ return below;
+ },
+
+ wrapValue: function(value, min, max) {
+ var d = Math.abs(max - min);
+ return min + (value - min) % d;
+ },
+
+ limitValue: function(value, min, max) {
+ return value < min ? min : value > max ? max : value;
+ },
+
+ mix: function(a, b, ammount) {
+ return a + (b - a) * ammount;
+ },
+
+ hexToRgb: function(hex) {
+ return ['0x' + hex[1] + hex[2] | 0, '0x' + hex[3] + hex[4] | 0, '0x' + hex[5] + hex[6] | 0];
+ },
+
+ rgbToHex: function(r, g, b) {
+ return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1, 7);
+ },
+
+ /* author: http://mjijackson.com/ */
+
+ rgbToHsl: function(r, g, b) {
+
+ if(r instanceof Array) {
+ b = r[2];
+ g = r[1];
+ r = r[0];
+ }
+
+ r /= 255, g /= 255, b /= 255;
+ var max = Math.max(r, g, b),
+ min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+
+ if(max == min) {
+ h = s = 0; // achromatic
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ 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, l];
+ },
+
+ /* author: http://mjijackson.com/ */
+
+ hslToRgb: function(h, s, l) {
+ var r, g, b;
+
+ if(s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ function hue2rgb(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;
+ }
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+
+ return [r * 255 | 0, g * 255 | 0, b * 255 | 0];
+ },
+
+ rgbToHsv: function(r, g, b) {
+ if(r instanceof Array) {
+ b = r[2];
+ g = r[1];
+ r = r[0];
+ }
+
+ 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; // achromatic
+ } 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];
+ },
+
+ 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];
+ },
+
+ color: function() {
+ var result = new $.Color();
+ result.parse(arguments);
+ return result;
+ },
+
+ createCanvas: function(width, height) {
+ var result = document.createElement("canvas");
+
+ if(arguments[0] instanceof Image || arguments[0] instanceof HTMLImageElement) {
+ var image = arguments[0];
+ result.width = image.width;
+ result.height = image.height;
+ result.getContext("2d").drawImage(image, 0, 0);
+ } else {
+ result.width = width;
+ result.height = height;
+ }
+
+ return result;
+ },
+
+ createImageData: function(width, height) {
+ return document.createElement("Canvas").getContext("2d").createImageData(width, height);
+ },
+
+
+ /* https://gist.github.com/3781251 */
+
+ mousePosition: function(event) {
+ var totalOffsetX = 0,
+ totalOffsetY = 0,
+ coordX = 0,
+ coordY = 0,
+ currentElement = event.target || event.srcElement,
+ mouseX = 0,
+ mouseY = 0;
+
+ // Traversing the parents to get the total offset
+ do {
+ totalOffsetX += currentElement.offsetLeft;
+ totalOffsetY += currentElement.offsetTop;
+ }
+ while ((currentElement = currentElement.offsetParent));
+ // Set the event to first touch if using touch-input
+ if(event.changedTouches && event.changedTouches[0] !== undefined) {
+ event = event.changedTouches[0];
+ }
+ // Use pageX to get the mouse coordinates
+ if(event.pageX || event.pageY) {
+ mouseX = event.pageX;
+ mouseY = event.pageY;
+ }
+ // IE8 and below doesn't support event.pageX
+ else if(event.clientX || event.clientY) {
+ mouseX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ mouseY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+ }
+ // Subtract the offset from the mouse coordinates
+ coordX = mouseX - totalOffsetX;
+ coordY = mouseY - totalOffsetY;
+
+ return {
+ x: coordX,
+ y: coordY
+ };
+ }
+ });
+
+ $.Wrapper = function(canvas) {
+ this.context = canvas.getContext("2d");
+ this.canvas = canvas;
+ }
+
+ $.Wrapper.prototype = {
+ appendTo: function(selector) {
+ if(typeof selector === "object") {
+ var element = selector;
+ } else {
+ var element = document.querySelector(selector);
+ }
+
+ element.appendChild(this.canvas);
+
+ return this;
+ },
+
+ blendOn: function(what, mode, mix) {
+ $.blend(what, this, mode, mix);
+
+ return this;
+ },
+
+ blend: function(what, mode, mix) {
+ if(typeof what === "string") {
+ var color = what;
+ what = $($.createCanvas(this.canvas.width, this.canvas.height));
+ what.fillStyle(color).fillRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ $.blend(this, what, mode, mix);
+
+ return this;
+ },
+
+ circle: function(x, y, r) {
+ this.context.arc(x, y, r, 0, Math.PI * 2);
+ return this;
+ },
+
+ crop: function(x, y, w, h) {
+
+ var canvas = $.createCanvas(w, h);
+ var context = canvas.getContext("2d");
+
+ context.drawImage(this.canvas, x, y, w, h, 0, 0, w, h);
+ this.canvas.width = w;
+ this.canvas.height = h;
+ this.clear();
+ this.context.drawImage(canvas, 0, 0);
+
+ return this;
+ },
+
+ set: function(properties) {
+ $.extend(this.context, properties);
+ },
+
+ resize: function(width, height) {
+ var w = width,
+ h = height;
+ if(height === null) {
+ if(this.canvas.width > width) {
+ h = this.canvas.height * (width / this.canvas.width) | 0;
+ w = width;
+ } else {
+ w = this.canvas.width;
+ h = this.canvas.height;
+ }
+ } else if(width === null) {
+ if(this.canvas.width > width) {
+ w = this.canvas.width * (height / this.canvas.height) | 0;
+ h = height;
+ } else {
+ w = this.canvas.width;
+ h = this.canvas.height;
+ }
+ }
+
+ var $resized = $(w, h).drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, w, h);
+ this.canvas = $resized.canvas;
+ this.context = $resized.context;
+
+ return this;
+ },
+
+ trim: function(color) {
+ var transparent;
+
+ if(color) {
+ color = $.color(color).toArray();
+ transparent = !color[3];
+ } else transparent = true;
+
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var bound = [this.canvas.width, this.canvas.height, 0, 0];
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ if(transparent) {
+ if(!sourcePixels[i + 3]) continue;
+ } else if(sourcePixels[i + 0] === color[0] && sourcePixels[i + 1] === color[1] && sourcePixels[i + 2] === color[2]) continue;
+ var x = (i / 4 | 0) % this.canvas.width | 0;
+ var y = (i / 4 | 0) / this.canvas.width | 0;
+
+ if(x < bound[0]) bound[0] = x;
+ if(x > bound[2]) bound[2] = x;
+
+ if(y < bound[1]) bound[1] = y;
+ if(y > bound[3]) bound[3] = y;
+ }
+
+ if(bound[2] === 0 || bound[3] === 0) {
+
+ } else this.crop(bound[0], bound[1], bound[2] - bound[0] + 1, bound[3] - bound[1] + 1);
+
+ return this;
+ },
+
+ resizePixel: function(pixelSize) {
+
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+ var canvas = document.createElement("canvas");
+ var context = canvas.context = canvas.getContext("2d");
+
+ canvas.width = this.canvas.width * pixelSize | 0;
+ canvas.height = this.canvas.height * pixelSize | 0;
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ if(!sourcePixels[i + 3]) continue;
+ context.fillStyle = $.rgbToHex(sourcePixels[i + 0], sourcePixels[i + 1], sourcePixels[i + 2]);
+
+ var x = (i / 4) % this.canvas.width;
+ var y = (i / 4) / this.canvas.width | 0;
+
+ context.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
+ }
+
+ this.canvas.width = canvas.width;
+ this.canvas.height = canvas.height;
+ this.clear().drawImage(canvas, 0, 0);
+
+ return this;
+
+ /* this very clever method is working only under Chrome */
+
+ var x = 0,
+ y = 0;
+
+ var canvas = document.createElement("canvas");
+ var context = canvas.context = canvas.getContext("2d");
+
+ canvas.width = this.canvas.width * pixelSize | 0;
+ canvas.height = this.canvas.height * pixelSize | 0;
+
+ while(x < this.canvas.width) {
+ y = 0;
+ while(y < this.canvas.height) {
+ context.drawImage(this.canvas, x, y, 1, 1, x * pixelSize, y * pixelSize, pixelSize, pixelSize);
+ y++;
+ }
+ x++;
+ }
+
+ this.canvas = canvas;
+ this.context = context;
+
+ return this;
+ },
+
+
+ matchPalette: function(palette) {
+ var imgData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+
+ var rgbPalette = [];
+ for(var i = 0; i < palette.length; i++) {
+ rgbPalette.push($.color(palette[i]));
+ }
+
+
+ for(var i = 0; i < imgData.data.length; i += 4) {
+ var difList = [];
+ for(var j = 0; j < rgbPalette.length; j++) {
+ var rgbVal = rgbPalette[j];
+ var rDif = Math.abs(imgData.data[i] - rgbVal[0]),
+ gDif = Math.abs(imgData.data[i + 1] - rgbVal[1]),
+ bDif = Math.abs(imgData.data[i + 2] - rgbVal[2]);
+ difList.push(rDif + gDif + bDif);
+ }
+
+ var closestMatch = 0;
+ for(var j = 0; j < palette.length; j++) {
+ if(difList[j] < difList[closestMatch]) {
+ closestMatch = j;
+ }
+ }
+
+ var paletteRgb = cq.hexToRgb(palette[closestMatch]);
+ imgData.data[i] = paletteRgb[0];
+ imgData.data[i + 1] = paletteRgb[1];
+ imgData.data[i + 2] = paletteRgb[2];
+ }
+
+ this.context.putImageData(imgData, 0, 0);
+
+ return this;
+ },
+
+ getPalette: function() {
+ var palette = [];
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ if(sourcePixels[i + 3]) {
+ var hex = $.rgbToHex(sourcePixels[i + 0], sourcePixels[i + 1], sourcePixels[i + 2]);
+ if(palette.indexOf(hex) === -1) palette.push(hex);
+ }
+ }
+
+ return palette;
+ },
+
+ pixelize: function(size) {
+ if(!size) return this;
+ size = size || 4;
+
+ var mozImageSmoothingEnabled = this.context.mozImageSmoothingEnabled;
+ var webkitImageSmoothingEnabled = this.context.webkitImageSmoothingEnabled;
+
+ this.context.mozImageSmoothingEnabled = false;
+ this.context.webkitImageSmoothingEnabled = false;
+
+ var scale = (this.canvas.width / size) / this.canvas.width;
+
+ var temp = cq(this.canvas.width, this.canvas.height);
+
+ temp.drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, this.canvas.width * scale | 0, this.canvas.height * scale | 0);
+ this.clear().drawImage(temp.canvas, 0, 0, this.canvas.width * scale | 0, this.canvas.height * scale | 0, 0, 0, this.canvas.width, this.canvas.height);
+
+ this.context.mozImageSmoothingEnabled = mozImageSmoothingEnabled;
+ this.context.webkitImageSmoothingEnabled = webkitImageSmoothingEnabled;
+
+ return this;
+ },
+
+ colorToMask: function(color, inverted) {
+ color = $.color(color).toArray();
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var mask = [];
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ if(sourcePixels[i + 0] == color[0] && sourcePixels[i + 1] == color[1] && sourcePixels[i + 2] == color[2]) mask.push(inverted || false);
+ else mask.push(!inverted);
+ }
+
+ return mask;
+ },
+
+ grayscaleToMask: function(color) {
+ color = $.color(color).toArray();
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var mask = [];
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ mask.push((sourcePixels[i + 0] + sourcePixels[i + 1] + sourcePixels[i + 2]) / 3 | 0);
+ }
+
+ return mask;
+ },
+
+ grayscaleToAlpha: function() {
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var mask = [];
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ sourcePixels[i + 3] = (sourcePixels[i + 0] + sourcePixels[i + 1] + sourcePixels[i + 2]) / 3 | 0;
+
+ sourcePixels[i + 0] = sourcePixels[i + 1] = sourcePixels[i + 2] = 255;
+ }
+
+ this.context.putImageData(sourceData, 0, 0);
+
+ return this;
+ },
+
+ applyMask: function(mask) {
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var mode = typeof mask[0] === "boolean" ? "bool" : "byte";
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ var value = mask[i / 4];
+
+ if(mode === "bool") sourcePixels[i + 3] = 255 * value | 0;
+ else {
+ sourcePixels[i + 3] = value | 0;
+ }
+ }
+
+
+ this.context.putImageData(sourceData, 0, 0);
+ return this;
+ },
+
+ fillMask: function(mask) {
+
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var sourcePixels = sourceData.data;
+
+ var maskType = typeof mask[0] === "boolean" ? "bool" : "byte";
+ var colorMode = arguments.length === 2 ? "normal" : "gradient";
+
+ var color = $.color(arguments[1]);
+ if(colorMode === "gradient") colorB = $.color(arguments[2]);
+
+ for(var i = 0, len = sourcePixels.length; i < len; i += 4) {
+ var value = mask[i / 4];
+
+ if(maskType === "byte") value /= 255;
+
+ if(colorMode === "normal") {
+ if(value) {
+ sourcePixels[i + 0] = color[0] | 0;
+ sourcePixels[i + 1] = color[1] | 0;
+ sourcePixels[i + 2] = color[2] | 0;
+ sourcePixels[i + 3] = value * 255 | 0;
+ }
+ } else {
+ sourcePixels[i + 0] = color[0] + (colorB[0] - color[0]) * value | 0;
+ sourcePixels[i + 1] = color[1] + (colorB[1] - color[1]) * value | 0;
+ sourcePixels[i + 2] = color[2] + (colorB[2] - color[2]) * value | 0;
+ sourcePixels[i + 3] = 255;
+ }
+ }
+
+ this.context.putImageData(sourceData, 0, 0);
+ return this;
+ },
+
+ clear: function(color) {
+ if(color) {
+ this.context.fillStyle = color;
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+ } else {
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ return this;
+ },
+
+ clone: function() {
+ var result = $.createCanvas(this.canvas.width, this.canvas.height);
+ result.getContext("2d").drawImage(this.canvas, 0, 0);
+ return $(result);
+ },
+
+ fillStyle: function(fillStyle) {
+ this.context.fillStyle = fillStyle;
+ return this;
+ },
+
+ strokeStyle: function(strokeStyle) {
+ this.context.strokeStyle = strokeStyle;
+ return this;
+ },
+
+ gradientText: function(text, x, y, maxWidth, gradient) {
+
+ var words = text.split(" ");
+
+ var h = this.font().match(/\d+/g)[0] * 2;
+
+ var ox = 0;
+ var oy = 0;
+
+ if(maxWidth) {
+ var line = 0;
+ var lines = [""];
+
+ for(var i = 0; i < words.length; i++) {
+ var word = words[i] + " ";
+ var wordWidth = this.context.measureText(word).width;
+
+ if(ox + wordWidth > maxWidth) {
+ lines[++line] = "";
+ ox = 0;
+ }
+
+ lines[line] += word;
+
+ ox += wordWidth;
+ }
+ } else var lines = [text];
+
+ for(var i = 0; i < lines.length; i++) {
+ var oy = y + i * h * 0.6 | 0;
+ var lingrad = this.context.createLinearGradient(0, oy, 0, oy + h * 0.6 | 0);
+
+ for(var j = 0; j < gradient.length; j += 2) {
+ lingrad.addColorStop(gradient[j], gradient[j + 1]);
+ }
+
+ var text = lines[i];
+
+ this.fillStyle(lingrad).fillText(text, x, oy);
+ }
+
+ return this;
+ },
+
+ setHsl: function() {
+
+ if(arguments.length === 1) {
+ var args = arguments[0];
+ } else {
+ var args = arguments;
+ }
+
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b, a, h, s, l, hsl = [],
+ newPixel = [];
+
+ for(var i = 0, len = pixels.length; i < len; i += 4) {
+ hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
+
+ h = args[0] === null ? hsl[0] : $.limitValue(args[0], 0, 1);
+ s = args[1] === null ? hsl[1] : $.limitValue(args[1], 0, 1);
+ l = args[2] === null ? hsl[2] : $.limitValue(args[2], 0, 1);
+
+ newPixel = $.hslToRgb(h, s, l);
+
+ pixels[i + 0] = newPixel[0];
+ pixels[i + 1] = newPixel[1];
+ pixels[i + 2] = newPixel[2];
+ }
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ shiftHsl: function() {
+
+ if(arguments.length === 1) {
+ var args = arguments[0];
+ } else {
+ var args = arguments;
+ }
+
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b, a, h, s, l, hsl = [],
+ newPixel = [];
+
+ for(var i = 0, len = pixels.length; i < len; i += 4) {
+ hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
+
+ h = args[0] === null ? hsl[0] : $.wrapValue(hsl[0] + args[0], 0, 1);
+ s = args[1] === null ? hsl[1] : $.limitValue(hsl[1] + args[1], 0, 1);
+ l = args[2] === null ? hsl[2] : $.limitValue(hsl[2] + args[2], 0, 1);
+
+ newPixel = $.hslToRgb(h, s, l);
+
+ pixels[i + 0] = newPixel[0];
+ pixels[i + 1] = newPixel[1];
+ pixels[i + 2] = newPixel[2];
+ }
+
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ replaceHue: function(src, dst) {
+
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b, a, h, s, l, hsl = [],
+ newPixel = [];
+
+ for(var i = 0, len = pixels.length; i < len; i += 4) {
+ hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
+
+ if(Math.abs(hsl[0] - src) < 0.05) h = $.wrapValue(dst, 0, 1);
+ else h = hsl[0];
+
+ newPixel = $.hslToRgb(h, hsl[1], hsl[2]);
+
+ pixels[i + 0] = newPixel[0];
+ pixels[i + 1] = newPixel[1];
+ pixels[i + 2] = newPixel[2];
+ }
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ invert: function(src, dst) {
+
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b, a, h, s, l, hsl = [],
+ newPixel = [];
+
+ for(var i = 0, len = pixels.length; i < len; i += 4) {
+ pixels[i + 0] = 255 - pixels[i + 0];
+ pixels[i + 1] = 255 - pixels[i + 1];
+ pixels[i + 2] = 255 - pixels[i + 2];
+ }
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ roundRect: function(x, y, width, height, radius) {
+
+ this.beginPath();
+ this.moveTo(x + radius, y);
+ this.lineTo(x + width - radius, y);
+ this.quadraticCurveTo(x + width, y, x + width, y + radius);
+ this.lineTo(x + width, y + height - radius);
+ this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+ this.lineTo(x + radius, y + height);
+ this.quadraticCurveTo(x, y + height, x, y + height - radius);
+ this.lineTo(x, y + radius);
+ this.quadraticCurveTo(x, y, x + radius, y);
+ this.closePath();
+
+ return this;
+ },
+
+ wrappedText: function(text, x, y, maxWidth, newlineCallback) {
+
+ var words = text.split(" ");
+
+ var h = this.font().match(/\d+/g)[0] * 2;
+
+ var ox = 0;
+ var oy = 0;
+
+ if(maxWidth) {
+ var line = 0;
+ var lines = [""];
+
+ for(var i = 0; i < words.length; i++) {
+ var word = words[i] + " ";
+ var wordWidth = this.context.measureText(word).width;
+
+ if(ox + wordWidth > maxWidth) {
+ lines[++line] = "";
+ ox = 0;
+ }
+
+ lines[line] += word;
+
+ ox += wordWidth;
+ }
+ } else {
+ var lines = [text];
+ }
+
+ for(var i = 0; i < lines.length; i++) {
+ var oy = y + i * h * 0.6 | 0;
+
+ var text = lines[i];
+
+ if(newlineCallback) newlineCallback.call(this, x, y + oy);
+
+ this.fillText(text, x, oy);
+ }
+
+ return this;
+ },
+
+ textBoundaries: function(text, maxWidth) {
+ var words = text.split(" ");
+
+ var h = this.font().match(/\d+/g)[0] * 2;
+
+ var ox = 0;
+ var oy = 0;
+
+ if(maxWidth) {
+ var line = 0;
+ var lines = [""];
+
+ for(var i = 0; i < words.length; i++) {
+ var word = words[i] + " ";
+ var wordWidth = this.context.measureText(word).width;
+
+ if(ox + wordWidth > maxWidth) {
+ lines[++line] = "";
+ ox = 0;
+ }
+
+ lines[line] += word;
+
+ ox += wordWidth;
+ }
+ } else {
+ var lines = [text];
+ maxWidth = this.measureText(text).width;
+ }
+
+ return {
+ height: lines.length * h * 0.6 | 0,
+ width: maxWidth
+ }
+ },
+
+ paperBag: function(x, y, width, height, blowX, blowY) {
+ var lx, ly;
+ this.beginPath();
+ this.moveTo(x, y);
+ this.quadraticCurveTo(x + width / 2 | 0, y + height * blowY | 0, x + width, y);
+ this.quadraticCurveTo(x + width - width * blowX | 0, y + height / 2 | 0, x + width, y + height);
+ this.quadraticCurveTo(x + width / 2 | 0, y + height - height * blowY | 0, x, y + height);
+ this.quadraticCurveTo(x + width * blowX | 0, y + height / 2 | 0, x, y);
+ },
+
+ borderImage: function(image, x, y, w, h, t, r, b, l, fill) {
+
+ /* top */
+ this.drawImage(image, l, 0, image.width - l - r, t, x + l, y, w - l - r, t);
+
+ /* bottom */
+ this.drawImage(image, l, image.height - b, image.width - l - r, b, x + l, y + h - b, w - l - r, b);
+
+ /* left */
+ this.drawImage(image, 0, t, l, image.height - b - t, x, y + t, l, h - b - t);
+
+ /* right */
+ this.drawImage(image, image.width - r, t, r, image.height - b - t, x + w - r, y + t, r, h - b - t);
+
+ /* top-left */
+ this.drawImage(image, 0, 0, l, t, x, y, l, t);
+
+ /* top-right */
+ this.drawImage(image, image.width - r, 0, r, t, x + w - r, y, r, t);
+
+ /* bottom-right */
+ this.drawImage(image, image.width - r, image.height - b, r, b, x + w - r, y + h - b, r, b);
+
+ /* bottom-left */
+ this.drawImage(image, 0, image.height - b, l, b, x, y + h - b, l, b);
+
+ if(fill) {
+ if(typeof fill === "string") {
+ this.fillStyle(fill).fillRect(x + l, y + t, w - l - r, h - t - b);
+ } else {
+ this.drawImage(image, l, t, image.width - r - l, image.height - b - t, x + l, y + t, w - l - r, h - t - b);
+ }
+ }
+ },
+
+ /* www.html5rocks.com/en/tutorials/canvas/imagefilters/ */
+
+ convolve: function(matrix, mix, divide) {
+
+ if(typeof divide === "undefined") divide = 1;
+ if(typeof mix === "undefined") mix = 1;
+
+ var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var matrixSize = Math.sqrt(matrix.length) + 0.5 | 0;
+ var halfMatrixSize = matrixSize / 2 | 0;
+ var src = sourceData.data;
+ var sw = sourceData.width;
+ var sh = sourceData.height;
+ var w = sw;
+ var h = sh;
+ var output = $.createImageData(this.canvas.width, this.canvas.height);
+ var dst = output.data;
+
+ for(var y = 1; y < h - 1; y++) {
+ for(var x = 1; x < w - 1; x++) {
+
+ var dstOff = (y * w + x) * 4;
+ var r = 0,
+ g = 0,
+ b = 0,
+ a = 0;
+ for(var cy = 0; cy < matrixSize; cy++) {
+ for(var cx = 0; cx < matrixSize; cx++) {
+ var scy = y + cy - halfMatrixSize;
+ var scx = x + cx - halfMatrixSize;
+ if(scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
+ var srcOff = (scy * sw + scx) * 4;
+ var wt = matrix[cy * matrixSize + cx] / divide;
+ r += src[srcOff + 0] * wt;
+ g += src[srcOff + 1] * wt;
+ b += src[srcOff + 2] * wt;
+ a += src[srcOff + 3] * wt;
+ }
+ }
+ }
+ dst[dstOff + 0] = $.mix(src[dstOff + 0], r, mix);
+ dst[dstOff + 1] = $.mix(src[dstOff + 1], g, mix);
+ dst[dstOff + 2] = $.mix(src[dstOff + 2], b, mix);
+ dst[dstOff + 3] = src[dstOff + 3];
+ }
+ }
+
+ this.context.putImageData(output, 0, 0);
+
+ return this;
+ },
+
+ blur: function(mix) {
+ return this.convolve([1, 1, 1, 1, 1, 1, 1, 1, 1], mix, 9);
+ },
+
+ gaussianBlur: function(mix) {
+ return this.convolve([0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067, 0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292, 0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117, 0.00038771, 0.01330373, 0.11098164, 0.22508352, 0.11098164, 0.01330373, 0.00038771, 0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117, 0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292, 0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067], mix, 1);
+ },
+
+ sharpen: function(mix) {
+ return this.convolve([0, -1, 0, -1, 5, -1, 0, -1, 0], mix);
+ },
+
+ threshold: function(threshold) {
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b;
+
+ for(var i = 0; i < pixels.length; i += 4) {
+ var r = pixels[i];
+ var g = pixels[i + 1];
+ var b = pixels[i + 2];
+ var v = (0.2126 * r + 0.7152 * g + 0.0722 * b >= threshold) ? 255 : 0;
+ pixels[i] = pixels[i + 1] = pixels[i + 2] = v
+ }
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ sepia: function() {
+ var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ var pixels = data.data;
+ var r, g, b;
+
+ for(var i = 0; i < pixels.length; i += 4) {
+ pixels[i + 0] = $.limitValue((pixels[i + 0] * .393) + (pixels[i + 1] * .769) + (pixels[i + 2] * .189), 0, 255);
+ pixels[i + 1] = $.limitValue((pixels[i + 0] * .349) + (pixels[i + 1] * .686) + (pixels[i + 2] * .168), 0, 255);
+ pixels[i + 2] = $.limitValue((pixels[i + 0] * .272) + (pixels[i + 1] * .534) + (pixels[i + 2] * .131), 0, 255);
+ }
+
+ this.context.putImageData(data, 0, 0);
+
+ return this;
+ },
+
+ measureText: function() {
+ return this.context.measureText.apply(this.context, arguments);
+ },
+
+ createRadialGradient: function() {
+ return this.context.createRadialGradient.apply(this.context, arguments);
+ },
+
+ createLinearGradient: function() {
+ return this.context.createLinearGradient.apply(this.context, arguments);
+ },
+
+ getImageData: function() {
+ return this.context.getImageData.apply(this.context, arguments);
+ },
+
+ /* framework */
+
+ framework: function(args, context) {
+ if(context) {
+ this.tempContext = context === true ? args : context;
+ }
+
+ for(var name in args) {
+ if(this[name]) this[name](args[name], undefined, undefined);
+ }
+
+ this.tempContext = null;
+
+ return this;
+ },
+
+ onStep: function(callback, interval) {
+ var self = this.tempContext || this;
+ var lastTick = Date.now();
+
+ this.timer = setInterval(function() {
+ var delta = Date.now() - lastTick;
+ lastTick = Date.now();
+ callback.call(self, delta, lastTick);
+ }, interval);
+
+ return this;
+ },
+
+ onRender: function(callback) {
+ var self = this.tempContext || this;
+
+ var lastTick = Date.now();
+
+ function step() {
+ var delta = Date.now() - lastTick;
+ lastTick = Date.now();
+ requestAnimationFrame(step)
+ callback.call(self, delta, lastTick);
+ };
+
+ requestAnimationFrame(step);
+
+ return this;
+ },
+
+ onMouseMove: function(callback) {
+ var self = this.tempContext || this;
+
+ if(!MOBILE) this.canvas.addEventListener("mousemove", function(e) {
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y);
+ });
+
+ else this.canvas.addEventListener("touchmove", function(e) {
+ e.preventDefault();
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y);
+ });
+
+ return this;
+ },
+
+ onMouseDown: function(callback) {
+ var self = this.tempContext || this;
+
+ if(!MOBILE) {
+ this.canvas.addEventListener("mousedown", function(e) {
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y, e.button);
+ });
+ } else {
+ this.canvas.addEventListener("touchstart", function(e) {
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y, e.button);
+ });
+ }
+
+ return this;
+ },
+
+ onMouseUp: function(callback) {
+ var self = this.tempContext || this;
+
+ if(!MOBILE) {
+ this.canvas.addEventListener("mouseup", function(e) {
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y, e.button);
+ });
+ } else {
+ this.canvas.addEventListener("touchend", function(e) {
+ var pos = $.mousePosition(e);
+ callback.call(self, pos.x, pos.y, e.button);
+ });
+ }
+
+ return this;
+ },
+
+
+ onSwipe: function(callback, threshold, timeout) {
+ var self = this.tempContext || this;
+
+ var swipeThr = threshold || 35;
+ var swipeTim = timeout || 350;
+
+ var swipeSP = 0;
+ var swipeST = 0;
+ var swipeEP = 0;
+ var swipeET = 0;
+
+ function swipeStart(e) {
+ e.preventDefault();
+ swipeSP = $.mousePosition(e);
+ swipeST = Date.now();
+ }
+
+ function swipeUpdate(e) {
+ e.preventDefault();
+ swipeEP = $.mousePosition(e);
+ swipeET = Date.now();
+ }
+
+ function swipeEnd(e) {
+ e.preventDefault();
+
+ var xDif = (swipeSP.x - swipeEP.x);
+ var yDif = (swipeSP.y - swipeEP.y);
+ var x = (xDif * xDif);
+ var y = (yDif * yDif);
+ var swipeDist = Math.sqrt(x + y);
+ var swipeTime = (swipeET - swipeST);
+ var swipeDir = undefined;
+
+ if(swipeDist > swipeThr && swipeTime < swipeTim) {
+ if(Math.abs(xDif) > Math.abs(yDif)) {
+ if(xDif > 0) {
+ swipeDir = "left";
+ } else {
+ swipeDir = "right";
+ }
+ } else {
+ if(yDif > 0) {
+ swipeDir = "up";
+ } else {
+ swipeDir = "down";
+ }
+ }
+ callback.call(self, swipeDir);
+ }
+ }
+
+ this.canvas.addEventListener("touchstart", function(e) {
+ swipeStart(e);
+ });
+ this.canvas.addEventListener("touchmove", function(e) {
+ swipeUpdate(e);
+ });
+ this.canvas.addEventListener("touchend", function(e) {
+ swipeEnd(e);
+ });
+ this.canvas.addEventListener("mousedown", function(e) {
+ swipeStart(e);
+ });
+ this.canvas.addEventListener("mousemove", function(e) {
+ swipeUpdate(e);
+ });
+ this.canvas.addEventListener("mouseup", function(e) {
+ swipeEnd(e);
+ });
+
+ return this;
+ },
+
+ onKeyDown: function(callback) {
+ document.addEventListener("keydown", function(e) {
+ if(e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
+ else var keyName = $.keycodes[e.which];
+ callback.call(self, keyName);
+ });
+ return this;
+ },
+
+ onKeyUp: function(callback) {
+ document.addEventListener("keyup", function(e) {
+ if(e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
+ else var keyName = $.keycodes[e.which];
+ callback.call(self, keyName);
+ });
+ return this;
+ },
+
+ onResize: function(callback) {
+ var self = this;
+
+ window.addEventListener("resize", function() {
+ callback.call(self, window.innerWidth, window.innerHeight);
+ });
+
+ callback.call(self, window.innerWidth, window.innerHeight);
+
+ return this;
+ },
+
+ onDropImage: function(callback) {
+ var self = this;
+ document.addEventListener('drop', function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ var file = e.dataTransfer.files[0];
+
+ if(!(/image/i).test(file.type)) return false;
+ var reader = new FileReader();
+
+ reader.onload = function(e) {
+ var image = new Image;
+
+ image.onload = function() {
+ callback.call(self, this);
+ };
+
+ image.src = e.target.result;
+ };
+
+ reader.readAsDataURL(file);
+
+ });
+
+ document.addEventListener("dragover", function(e) {
+ e.preventDefault();
+ });
+
+ return this;
+ }
+
+ };
+
+ /* extend wrapper with drawing context methods */
+
+ var methods = ["arc", "arcTo", "beginPath", "bezierCurveTo", "clearRect", "clip", "closePath", "createImageData", "createLinearGradient", "createRadialGradient", "createPattern", "drawFocusRing", "drawImage", "fill", "fillRect", "fillText", "getImageData", "isPointInPath", "lineTo", "measureText", "moveTo", "putImageData", "quadraticCurveTo", "rect", "restore", "rotate", "save", "scale", "setTransform", "stroke", "strokeRect", "strokeText", "transform", "translate"];
+ for(var i = 0; i < methods.length; i++) {
+ var name = methods[i];
+ if(!$.Wrapper.prototype[name]) $.Wrapper.prototype[name] = Function("this.context." + name + ".apply(this.context, arguments); return this;");
+ };
+
+ /* create setters and getters */
+
+ var properties = ["canvas", "fillStyle", "font", "globalAlpha", "globalCompositeOperation", "lineCap", "lineJoin", "lineWidth", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "strokeStyle", "textAlign", "textBaseline"];
+ for(var i = 0; i < properties.length; i++) {
+ var name = properties[i];
+ if(!$.Wrapper.prototype[name]) $.Wrapper.prototype[name] = Function("if(arguments.length) { this.context." + name + " = arguments[0]; return this; } else { return this.context." + name + "; }");
+ };
+
+ /* color */
+
+ $.Color = function() {
+ if(arguments.length) this.parse(arguments);
+ }
+
+ $.Color.prototype = {
+ parse: function(args) {
+ if(typeof args[0] === "string") {
+ var rgb = $.hexToRgb(args[0]);
+ this[0] = rgb[0];
+ this[1] = rgb[1];
+ this[2] = rgb[2];
+ this[3] = 255;
+ } else {
+ this[0] = args[0];
+ this[1] = args[1];
+ this[2] = args[2];
+ this[3] = typeof args[3] === "undefined" ? 255 : args[3];
+ }
+ },
+
+ toArray: function() {
+ return [this[0], this[1], this[2], this[3]];
+ },
+
+ toRgb: function() {
+ return "rgb(" + this[0] + ", " + this[1] + ", " + this[2] + ")";
+ },
+
+ toRgba: function() {
+ return "rgb(" + this[0] + ", " + this[1] + ", " + this[2] + ", " + this[3] + ")";
+ },
+
+ toHex: function() {
+ return $.rgbToHex(this[0], this[1], this[2]);
+ },
+
+ toHsl: function() {
+ return $.rgbToHsl(this[0], this[1], this[2]);
+ },
+
+ toHsv: function() {
+ return $.rgbToHsv(this[0], this[1], this[2]);
+ }
+
+ };
+
+ window["cq"] = window["CanvasQuery"] = $;
+
+ if(typeof define === "function" && define.amd) {
+ define([], function() {
+ return $;
+ });
+ }
+
+})(window); \ No newline at end of file
diff --git a/dat.gui.min.js b/dat.gui.min.js
new file mode 100644
index 0000000..87b9039
--- /dev/null
+++ b/dat.gui.min.js
@@ -0,0 +1,82 @@
+var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){a=a||document;var b=a.createElement("link");b.type="text/css";b.rel="stylesheet";b.href=e;a.getElementsByTagName("head")[0].appendChild(b)},inject:function(e,a){a=a||document;var b=document.createElement("style");b.type="text/css";b.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(b)}}}();
+dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(b){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(b[f]=a[f])},this);return b},defaults:function(b){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(b[f])&&(b[f]=a[f])},this);return b},compose:function(){var b=a.call(arguments);return function(){for(var d=a.call(arguments),f=b.length-1;0<=f;f--)d=[b[f].apply(this,d)];return d[0]}},
+each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var c=0,p=a.length;c<p&&!(c in a&&d.call(f,a[c],c)===this.BREAK);c++);else for(c in a)if(d.call(f,a[c],c)===this.BREAK)break},defer:function(a){setTimeout(a,0)},toArray:function(b){return b.toArray?b.toArray():a.call(b)},isUndefined:function(a){return void 0===a},isNull:function(a){return null===a},isNaN:function(a){return a!==a},isArray:Array.isArray||function(a){return a.constructor===Array},isObject:function(a){return a===
+Object(a)},isNumber:function(a){return a===a+0},isString:function(a){return a===a+""},isBoolean:function(a){return!1===a||!0===a},isFunction:function(a){return"[object Function]"===Object.prototype.toString.call(a)}}}();
+dat.controllers.Controller=function(e){var a=function(a,d){this.initialValue=a[d];this.domElement=document.createElement("div");this.object=a;this.property=d;this.__onFinishChange=this.__onChange=void 0};e.extend(a.prototype,{onChange:function(a){this.__onChange=a;return this},onFinishChange:function(a){this.__onFinishChange=a;return this},setValue:function(a){this.object[this.property]=a;this.__onChange&&this.__onChange.call(this,a);this.updateDisplay();return this},getValue:function(){return this.object[this.property]},
+updateDisplay:function(){return this},isModified:function(){return this.initialValue!==this.getValue()}});return a}(dat.utils.common);
+dat.dom.dom=function(e){function a(c){if("0"===c||e.isUndefined(c))return 0;c=c.match(d);return e.isNull(c)?0:parseFloat(c[1])}var b={};e.each({HTMLEvents:["change"],MouseEvents:["click","mousemove","mousedown","mouseup","mouseover"],KeyboardEvents:["keydown"]},function(c,a){e.each(c,function(c){b[c]=a})});var d=/(\d+(\.\d+)?)px/,f={makeSelectable:function(c,a){void 0!==c&&void 0!==c.style&&(c.onselectstart=a?function(){return!1}:function(){},c.style.MozUserSelect=a?"auto":"none",c.style.KhtmlUserSelect=
+a?"auto":"none",c.unselectable=a?"on":"off")},makeFullscreen:function(c,a,d){e.isUndefined(a)&&(a=!0);e.isUndefined(d)&&(d=!0);c.style.position="absolute";a&&(c.style.left=0,c.style.right=0);d&&(c.style.top=0,c.style.bottom=0)},fakeEvent:function(c,a,d,f){d=d||{};var q=b[a];if(!q)throw Error("Event type "+a+" not supported.");var n=document.createEvent(q);switch(q){case "MouseEvents":n.initMouseEvent(a,d.bubbles||!1,d.cancelable||!0,window,d.clickCount||1,0,0,d.x||d.clientX||0,d.y||d.clientY||0,!1,
+!1,!1,!1,0,null);break;case "KeyboardEvents":q=n.initKeyboardEvent||n.initKeyEvent;e.defaults(d,{cancelable:!0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,keyCode:void 0,charCode:void 0});q(a,d.bubbles||!1,d.cancelable,window,d.ctrlKey,d.altKey,d.shiftKey,d.metaKey,d.keyCode,d.charCode);break;default:n.initEvent(a,d.bubbles||!1,d.cancelable||!0)}e.defaults(n,f);c.dispatchEvent(n)},bind:function(c,a,d,b){c.addEventListener?c.addEventListener(a,d,b||!1):c.attachEvent&&c.attachEvent("on"+a,d);return f},
+unbind:function(c,a,d,b){c.removeEventListener?c.removeEventListener(a,d,b||!1):c.detachEvent&&c.detachEvent("on"+a,d);return f},addClass:function(a,d){if(void 0===a.className)a.className=d;else if(a.className!==d){var b=a.className.split(/ +/);-1==b.indexOf(d)&&(b.push(d),a.className=b.join(" ").replace(/^\s+/,"").replace(/\s+$/,""))}return f},removeClass:function(a,d){if(d){if(void 0!==a.className)if(a.className===d)a.removeAttribute("class");else{var b=a.className.split(/ +/),e=b.indexOf(d);-1!=
+e&&(b.splice(e,1),a.className=b.join(" "))}}else a.className=void 0;return f},hasClass:function(a,d){return RegExp("(?:^|\\s+)"+d+"(?:\\s+|$)").test(a.className)||!1},getWidth:function(c){c=getComputedStyle(c);return a(c["border-left-width"])+a(c["border-right-width"])+a(c["padding-left"])+a(c["padding-right"])+a(c.width)},getHeight:function(c){c=getComputedStyle(c);return a(c["border-top-width"])+a(c["border-bottom-width"])+a(c["padding-top"])+a(c["padding-bottom"])+a(c.height)},getOffset:function(a){var d=
+{left:0,top:0};if(a.offsetParent){do d.left+=a.offsetLeft,d.top+=a.offsetTop;while(a=a.offsetParent)}return d},isActive:function(a){return a===document.activeElement&&(a.type||a.href)}};return f}(dat.utils.common);
+dat.controllers.OptionController=function(e,a,b){var d=function(f,c,e){d.superclass.call(this,f,c);var k=this;this.__select=document.createElement("select");if(b.isArray(e)){var l={};b.each(e,function(a){l[a]=a});e=l}b.each(e,function(a,c){var d=document.createElement("option");d.innerHTML=c;d.setAttribute("value",a);k.__select.appendChild(d)});this.updateDisplay();a.bind(this.__select,"change",function(){k.setValue(this.options[this.selectedIndex].value)});this.domElement.appendChild(this.__select)};
+d.superclass=e;b.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue());return a},updateDisplay:function(){this.__select.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.controllers.NumberController=function(e,a){var b=function(d,f,c){b.superclass.call(this,d,f);c=c||{};this.__min=c.min;this.__max=c.max;this.__step=c.step;a.isUndefined(this.__step)?this.__impliedStep=0==this.initialValue?1:Math.pow(10,Math.floor(Math.log(this.initialValue)/Math.LN10))/10:this.__impliedStep=this.__step;d=this.__impliedStep;d=d.toString();d=-1<d.indexOf(".")?d.length-d.indexOf(".")-1:0;this.__precision=d};b.superclass=e;a.extend(b.prototype,e.prototype,{setValue:function(a){void 0!==
+this.__min&&a<this.__min?a=this.__min:void 0!==this.__max&&a>this.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return b.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return b}(dat.controllers.Controller,dat.utils.common);
+dat.controllers.NumberControllerBox=function(e,a,b){var d=function(f,c,e){function k(){var a=parseFloat(n.__input.value);b.isNaN(a)||n.setValue(a)}function l(a){var c=r-a.clientY;n.setValue(n.getValue()+c*n.__impliedStep);r=a.clientY}function q(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",q)}this.__truncationSuspended=!1;d.superclass.call(this,f,c,e);var n=this,r;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",k);a.bind(this.__input,
+"blur",function(){k();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(c){a.bind(window,"mousemove",l);a.bind(window,"mouseup",q);r=c.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;b.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input,c;if(this.__truncationSuspended)c=
+this.getValue();else{c=this.getValue();var b=Math.pow(10,this.__precision);c=Math.round(c*b)/b}a.value=c;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
+dat.controllers.NumberControllerSlider=function(e,a,b,d,f){function c(a,c,d,b,f){return b+(a-c)/(d-c)*(f-b)}var p=function(d,b,f,e,r){function y(d){d.preventDefault();var b=a.getOffset(h.__background),f=a.getWidth(h.__background);h.setValue(c(d.clientX,b.left,b.left+f,h.__min,h.__max));return!1}function g(){a.unbind(window,"mousemove",y);a.unbind(window,"mouseup",g);h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())}p.superclass.call(this,d,b,{min:f,max:e,step:r});var h=this;this.__background=
+document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(c){a.bind(window,"mousemove",y);a.bind(window,"mouseup",g);y(c)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=e;p.useDefaultStyles=function(){b.inject(f)};d.extend(p.prototype,e.prototype,{updateDisplay:function(){var a=
+(this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}");
+dat.controllers.FunctionController=function(e,a,b){var d=function(b,c,e){d.superclass.call(this,b,c);var k=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===e?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();k.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;b.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this,
+this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.controllers.BooleanController=function(e,a,b){var d=function(b,c){d.superclass.call(this,b,c);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;b.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
+this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.color.toString=function(e){return function(a){if(1==a.a||e.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
+dat.color.interpret=function(e,a){var b,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
+return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!=
+a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(c){return a.isNumber(c.r)&&a.isNumber(c.g)&&a.isNumber(c.b)&&a.isNumber(c.a)?{space:"RGB",r:c.r,g:c.g,b:c.b,a:c.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(c){return a.isNumber(c.r)&&
+a.isNumber(c.g)&&a.isNumber(c.b)?{space:"RGB",r:c.r,g:c.g,b:c.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(c){return a.isNumber(c.h)&&a.isNumber(c.s)&&a.isNumber(c.v)&&a.isNumber(c.a)?{space:"HSV",h:c.h,s:c.s,v:c.v,a:c.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(d){return a.isNumber(d.h)&&a.isNumber(d.s)&&a.isNumber(d.v)?{space:"HSV",h:d.h,s:d.s,v:d.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d=!1;
+var c=1<arguments.length?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(c))return a.each(e.conversions,function(e,f){b=e.read(c);if(!1===d&&!1!==b)return d=b,b.conversionName=f,b.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common);
+dat.GUI=dat.gui.GUI=function(e,a,b,d,f,c,p,k,l,q,n,r,y,g,h){function t(a,c,b,e){if(void 0===c[b])throw Error("Object "+c+' has no property "'+b+'"');e.color?c=new n(c,b):(c=[c,b].concat(e.factoryArgs),c=d.apply(a,c));e.before instanceof f&&(e.before=e.before.__li);v(a,c);g.addClass(c.domElement,"c");b=document.createElement("span");g.addClass(b,"property-name");b.innerHTML=c.property;var q=document.createElement("div");q.appendChild(b);q.appendChild(c.domElement);e=u(a,q,e.before);g.addClass(e,m.CLASS_CONTROLLER_ROW);
+g.addClass(e,typeof c.getValue());s(a,e,c);a.__controllers.push(c);return c}function u(a,d,c){var b=document.createElement("li");d&&b.appendChild(d);c?a.__ul.insertBefore(b,params.before):a.__ul.appendChild(b);a.onResize();return b}function s(a,d,b){b.__li=d;b.__gui=a;h.extend(b,{options:function(d){if(1<arguments.length)return b.remove(),t(a,b.object,b.property,{before:b.__li.nextElementSibling,factoryArgs:[h.toArray(arguments)]});if(h.isArray(d)||h.isObject(d))return b.remove(),t(a,b.object,b.property,
+{before:b.__li.nextElementSibling,factoryArgs:[d]})},name:function(a){b.__li.firstElementChild.firstElementChild.innerHTML=a;return b},listen:function(){b.__gui.listen(b);return b},remove:function(){b.__gui.remove(b);return b}});if(b instanceof l){var e=new k(b.object,b.property,{min:b.__min,max:b.__max,step:b.__step});h.each(["updateDisplay","onChange","onFinishChange"],function(a){var d=b[a],J=e[a];b[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);d.apply(b,a);return J.apply(e,a)}});
+g.addClass(d,"has-slider");b.domElement.insertBefore(e.domElement,b.domElement.firstElementChild)}else if(b instanceof k){var f=function(d){return h.isNumber(b.__min)&&h.isNumber(b.__max)?(b.remove(),t(a,b.object,b.property,{before:b.__li.nextElementSibling,factoryArgs:[b.__min,b.__max,b.__step]})):d};b.min=h.compose(f,b.min);b.max=h.compose(f,b.max)}else b instanceof c?(g.bind(d,"click",function(){g.fakeEvent(b.__checkbox,"click")}),g.bind(b.__checkbox,"click",function(a){a.stopPropagation()})):
+b instanceof p?(g.bind(d,"click",function(){g.fakeEvent(b.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(b.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(b.__button,"hover")})):b instanceof n&&(g.addClass(d,"color"),b.updateDisplay=h.compose(function(a){d.style.borderLeftColor=b.__color.toString();return a},b.updateDisplay),b.updateDisplay());b.setValue=h.compose(function(d){a.getRoot().__preset_select&&b.isModified()&&D(a.getRoot(),!0);return d},b.setValue)}function v(a,
+d){var b=a.getRoot(),c=b.__rememberedObjects.indexOf(d.object);if(-1!=c){var e=b.__rememberedObjectIndecesToControllers[c];void 0===e&&(e={},b.__rememberedObjectIndecesToControllers[c]=e);e[d.property]=d;if(b.load&&b.load.remembered){b=b.load.remembered;if(b[a.preset])b=b[a.preset];else if(b[z])b=b[z];else return;b[c]&&void 0!==b[c][d.property]&&(c=b[c][d.property],d.initialValue=c,d.setValue(c))}}}function K(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement,"has-save");a.__ul.insertBefore(b,
+a.__ul.firstChild);g.addClass(b,"save-row");var d=document.createElement("span");d.innerHTML="&nbsp;";g.addClass(d,"button gears");var c=document.createElement("span");c.innerHTML="Save";g.addClass(c,"button");g.addClass(c,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var q=a.__preset_select=document.createElement("select");a.load&&
+a.load.remembered?h.each(a.load.remembered,function(b,d){E(a,d,d==a.preset)}):E(a,z,!1);g.bind(q,"change",function(){for(var b=0;b<a.__preset_select.length;b++)a.__preset_select[b].innerHTML=a.__preset_select[b].value;a.preset=this.value});b.appendChild(q);b.appendChild(d);b.appendChild(c);b.appendChild(e);b.appendChild(f);if(w){var b=document.getElementById("dg-save-locally"),n=document.getElementById("dg-local-explain");b.style.display="block";b=document.getElementById("dg-local-storage");"true"===
+localStorage.getItem(document.location.href+".isLocal")&&b.setAttribute("checked","checked");var k=function(){n.style.display=a.useLocalStorage?"block":"none"};k();g.bind(b,"change",function(){a.useLocalStorage=!a.useLocalStorage;k()})}var r=document.getElementById("dg-new-constructor");g.bind(r,"keydown",function(a){!a.metaKey||67!==a.which&&67!=a.keyCode||A.hide()});g.bind(d,"click",function(){r.innerHTML=JSON.stringify(a.getSaveObject(),void 0,2);A.show();r.focus();r.select()});g.bind(c,"click",
+function(){a.save()});g.bind(e,"click",function(){var b=prompt("Enter a new preset name.");b&&a.saveAs(b)});g.bind(f,"click",function(){a.revert()})}function L(a){function b(f){f.preventDefault();e=f.clientX;g.addClass(a.__closeButton,m.CLASS_DRAG);g.bind(window,"mousemove",d);g.bind(window,"mouseup",c);return!1}function d(b){b.preventDefault();a.width+=e-b.clientX;a.onResize();e=b.clientX;return!1}function c(){g.removeClass(a.__closeButton,m.CLASS_DRAG);g.unbind(window,"mousemove",d);g.unbind(window,
+"mouseup",c)}a.__resize_handle=document.createElement("div");h.extend(a.__resize_handle.style,{width:"6px",marginLeft:"-3px",height:"200px",cursor:"ew-resize",position:"absolute"});var e;g.bind(a.__resize_handle,"mousedown",b);g.bind(a.__closeButton,"mousedown",b);a.domElement.insertBefore(a.__resize_handle,a.domElement.firstElementChild)}function F(a,b){a.domElement.style.width=b+"px";a.__save_row&&a.autoPlace&&(a.__save_row.style.width=b+"px");a.__closeButton&&(a.__closeButton.style.width=b+"px")}
+function B(a,b){var d={};h.each(a.__rememberedObjects,function(c,e){var f={};h.each(a.__rememberedObjectIndecesToControllers[e],function(a,d){f[d]=b?a.initialValue:a.getValue()});d[e]=f});return d}function E(a,b,d){var c=document.createElement("option");c.innerHTML=b;c.value=b;a.__preset_select.appendChild(c);d&&(a.__preset_select.selectedIndex=a.__preset_select.length-1)}function D(a,b){var d=a.__preset_select[a.__preset_select.selectedIndex];d.innerHTML=b?d.value+"*":d.value}function G(a){0!=a.length&&
+r(function(){G(a)});h.each(a,function(a){a.updateDisplay()})}e.inject(b);var z="Default",w;try{w="localStorage"in window&&null!==window.localStorage}catch(M){w=!1}var A,H=!0,x,C=!1,I=[],m=function(a){function b(){localStorage.setItem(document.location.href+".gui",JSON.stringify(c.getSaveObject()))}function d(){var a=c.getRoot();a.width+=1;h.defer(function(){a.width-=1})}var c=this;this.domElement=document.createElement("div");this.__ul=document.createElement("ul");this.domElement.appendChild(this.__ul);
+g.addClass(this.domElement,"dg");this.__folders={};this.__controllers=[];this.__rememberedObjects=[];this.__rememberedObjectIndecesToControllers=[];this.__listening=[];a=a||{};a=h.defaults(a,{autoPlace:!0,width:m.DEFAULT_WIDTH});a=h.defaults(a,{resizable:a.autoPlace,hideable:a.autoPlace});h.isUndefined(a.load)?a.load={preset:z}:a.preset&&(a.load.preset=a.preset);h.isUndefined(a.parent)&&a.hideable&&I.push(this);a.resizable=h.isUndefined(a.parent)&&a.resizable;a.autoPlace&&h.isUndefined(a.scrollable)&&
+(a.scrollable=!0);var e=w&&"true"===localStorage.getItem(document.location.href+".isLocal");Object.defineProperties(this,{parent:{get:function(){return a.parent}},scrollable:{get:function(){return a.scrollable}},autoPlace:{get:function(){return a.autoPlace}},preset:{get:function(){return c.parent?c.getRoot().preset:a.load.preset},set:function(b){c.parent?c.getRoot().preset=b:a.load.preset=b;for(b=0;b<this.__preset_select.length;b++)this.__preset_select[b].value==this.preset&&(this.__preset_select.selectedIndex=
+b);c.revert()}},width:{get:function(){return a.width},set:function(b){a.width=b;F(c,b)}},name:{get:function(){return a.name},set:function(b){a.name=b;q&&(q.innerHTML=a.name)}},closed:{get:function(){return a.closed},set:function(b){a.closed=b;a.closed?g.addClass(c.__ul,m.CLASS_CLOSED):g.removeClass(c.__ul,m.CLASS_CLOSED);this.onResize();c.__closeButton&&(c.__closeButton.innerHTML=b?m.TEXT_OPEN:m.TEXT_CLOSED)}},load:{get:function(){return a.load}},useLocalStorage:{get:function(){return e},set:function(a){w&&
+((e=a)?g.bind(window,"unload",b):g.unbind(window,"unload",b),localStorage.setItem(document.location.href+".isLocal",a))}}});if(h.isUndefined(a.parent)){a.closed=!1;g.addClass(this.domElement,m.CLASS_MAIN);g.makeSelectable(this.domElement,!1);if(w&&e){c.useLocalStorage=!0;var f=localStorage.getItem(document.location.href+".gui");f&&(a.load=JSON.parse(f))}this.__closeButton=document.createElement("div");this.__closeButton.innerHTML=m.TEXT_CLOSED;g.addClass(this.__closeButton,m.CLASS_CLOSE_BUTTON);this.domElement.appendChild(this.__closeButton);
+g.bind(this.__closeButton,"click",function(){c.closed=!c.closed})}else{void 0===a.closed&&(a.closed=!0);var q=document.createTextNode(a.name);g.addClass(q,"controller-name");f=u(c,q);g.addClass(this.__ul,m.CLASS_CLOSED);g.addClass(f,"title");g.bind(f,"click",function(a){a.preventDefault();c.closed=!c.closed;return!1});a.closed||(this.closed=!1)}a.autoPlace&&(h.isUndefined(a.parent)&&(H&&(x=document.createElement("div"),g.addClass(x,"dg"),g.addClass(x,m.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(x),
+H=!1),x.appendChild(this.domElement),g.addClass(this.domElement,m.CLASS_AUTO_PLACE)),this.parent||F(c,a.width));g.bind(window,"resize",function(){c.onResize()});g.bind(this.__ul,"webkitTransitionEnd",function(){c.onResize()});g.bind(this.__ul,"transitionend",function(){c.onResize()});g.bind(this.__ul,"oTransitionEnd",function(){c.onResize()});this.onResize();a.resizable&&L(this);c.getRoot();a.parent||d()};m.toggleHide=function(){C=!C;h.each(I,function(a){a.domElement.style.zIndex=C?-999:999;a.domElement.style.opacity=
+C?0:1})};m.CLASS_AUTO_PLACE="a";m.CLASS_AUTO_PLACE_CONTAINER="ac";m.CLASS_MAIN="main";m.CLASS_CONTROLLER_ROW="cr";m.CLASS_TOO_TALL="taller-than-window";m.CLASS_CLOSED="closed";m.CLASS_CLOSE_BUTTON="close-button";m.CLASS_DRAG="drag";m.DEFAULT_WIDTH=245;m.TEXT_CLOSED="Close Controls";m.TEXT_OPEN="Open Controls";g.bind(window,"keydown",function(a){"text"===document.activeElement.type||72!==a.which&&72!=a.keyCode||m.toggleHide()},!1);h.extend(m.prototype,{add:function(a,b){return t(this,a,b,{factoryArgs:Array.prototype.slice.call(arguments,
+2)})},addColor:function(a,b){return t(this,a,b,{color:!0})},remove:function(a){this.__ul.removeChild(a.__li);this.__controllers.slice(this.__controllers.indexOf(a),1);var b=this;h.defer(function(){b.onResize()})},destroy:function(){this.autoPlace&&x.removeChild(this.domElement)},addFolder:function(a){if(void 0!==this.__folders[a])throw Error('You already have a folder in this GUI by the name "'+a+'"');var b={name:a,parent:this};b.autoPlace=this.autoPlace;this.load&&this.load.folders&&this.load.folders[a]&&
+(b.closed=this.load.folders[a].closed,b.load=this.load.folders[a]);b=new m(b);this.__folders[a]=b;a=u(this,b.domElement);g.addClass(a,"folder");return b},open:function(){this.closed=!1},close:function(){this.closed=!0},onResize:function(){var a=this.getRoot();if(a.scrollable){var b=g.getOffset(a.__ul).top,d=0;h.each(a.__ul.childNodes,function(b){a.autoPlace&&b===a.__save_row||(d+=g.getHeight(b))});window.innerHeight-b-20<d?(g.addClass(a.domElement,m.CLASS_TOO_TALL),a.__ul.style.height=window.innerHeight-
+b-20+"px"):(g.removeClass(a.domElement,m.CLASS_TOO_TALL),a.__ul.style.height="auto")}a.__resize_handle&&h.defer(function(){a.__resize_handle.style.height=a.__ul.offsetHeight+"px"});a.__closeButton&&(a.__closeButton.style.width=a.width+"px")},remember:function(){h.isUndefined(A)&&(A=new y,A.domElement.innerHTML=a);if(this.parent)throw Error("You can only call remember on a top level GUI.");var b=this;h.each(Array.prototype.slice.call(arguments),function(a){0==b.__rememberedObjects.length&&K(b);-1==
+b.__rememberedObjects.indexOf(a)&&b.__rememberedObjects.push(a)});this.autoPlace&&F(this,this.width)},getRoot:function(){for(var a=this;a.parent;)a=a.parent;return a},getSaveObject:function(){var a=this.load;a.closed=this.closed;0<this.__rememberedObjects.length&&(a.preset=this.preset,a.remembered||(a.remembered={}),a.remembered[this.preset]=B(this));a.folders={};h.each(this.__folders,function(b,d){a.folders[d]=b.getSaveObject()});return a},save:function(){this.load.remembered||(this.load.remembered=
+{});this.load.remembered[this.preset]=B(this);D(this,!1)},saveAs:function(a){this.load.remembered||(this.load.remembered={},this.load.remembered[z]=B(this,!0));this.load.remembered[a]=B(this);this.preset=a;E(this,a,!0)},revert:function(a){h.each(this.__controllers,function(b){this.getRoot().load.remembered?v(a||this.getRoot(),b):b.setValue(b.initialValue)},this);h.each(this.__folders,function(a){a.revert(a)});a||D(this.getRoot(),!1)},listen:function(a){var b=0==this.__listening.length;this.__listening.push(a);
+b&&G(this.__listening)}});return m}(dat.utils.css,'<div id="dg-save" class="dg dialogue">\n\n Here\'s the new load parameter for your <code>GUI</code>\'s constructor:\n\n <textarea id="dg-new-constructor"></textarea>\n\n <div id="dg-save-locally">\n\n <input id="dg-local-storage" type="checkbox"/> Automatically save\n values to <code>localStorage</code> on exit.\n\n <div id="dg-local-explain">The values saved to <code>localStorage</code> will\n override those passed to <code>dat.GUI</code>\'s constructor. This makes it\n easier to work incrementally, but <code>localStorage</code> is fragile,\n and your friends may not see the same values you do.\n \n </div>\n \n </div>\n\n</div>',
+".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row, <li> */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n",
+dat.controllers.factory=function(e,a,b,d,f,c,p){return function(k,l,q,n){var r=k[l];if(p.isArray(q)||p.isObject(q))return new e(k,l,q);if(p.isNumber(r))return p.isNumber(q)&&p.isNumber(n)?new b(k,l,q,n):new a(k,l,{min:q,max:n});if(p.isString(r))return new d(k,l);if(p.isFunction(r))return new f(k,l,"");if(p.isBoolean(r))return new c(k,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,b){var d=
+function(b,c){function e(){k.setValue(k.__input.value)}d.superclass.call(this,b,c);var k=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;b.extend(d.prototype,
+e.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,
+dat.controllers.ColorController=function(e,a,b,d,f){function c(a,b,d,c){a.style.background="";f.each(l,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+d+" 0%, "+c+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";
+a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var k=function(e,n){function r(b){t(b);a.bind(window,"mousemove",t);a.bind(window,
+"mouseup",l)}function l(){a.unbind(window,"mousemove",t);a.unbind(window,"mouseup",l)}function g(){var a=d(this.value);!1!==a?(s.__color.__state=a,s.setValue(s.__color.toOriginal())):this.value=s.__color.toString()}function h(){a.unbind(window,"mousemove",u);a.unbind(window,"mouseup",h)}function t(b){b.preventDefault();var d=a.getWidth(s.__saturation_field),c=a.getOffset(s.__saturation_field),e=(b.clientX-c.left+document.body.scrollLeft)/d;b=1-(b.clientY-c.top+document.body.scrollTop)/d;1<b?b=1:0>
+b&&(b=0);1<e?e=1:0>e&&(e=0);s.__color.v=b;s.__color.s=e;s.setValue(s.__color.toOriginal());return!1}function u(b){b.preventDefault();var d=a.getHeight(s.__hue_field),c=a.getOffset(s.__hue_field);b=1-(b.clientY-c.top+document.body.scrollTop)/d;1<b?b=1:0>b&&(b=0);s.__color.h=360*b;s.setValue(s.__color.toOriginal());return!1}k.superclass.call(this,e,n);this.__color=new b(this.getValue());this.__temp=new b(0);var s=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1);
+this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input");
+this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(s.__selector,"drag")})});var v=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});f.extend(this.__field_knob.style,
+{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(0.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(v.style,{width:"100%",height:"100%",
+background:"none"});c(v,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",r);a.bind(this.__field_knob,"mousedown",r);a.bind(this.__hue_field,"mousedown",function(b){u(b);a.bind(window,
+"mousemove",u);a.bind(window,"mouseup",h)});this.__saturation_field.appendChild(v);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};k.superclass=e;f.extend(k.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue());if(!1!==a){var e=!1;
+f.each(b.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=!0,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var k=0.5>this.__color.v||0.5<this.__color.s?255:0,l=255-k;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+"rgb("+
+k+","+k+","+k+")"});this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px";this.__temp.s=1;this.__temp.v=1;c(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+k+","+k+","+k+")",textShadow:this.__input_textShadow+"rgba("+l+","+l+","+l+",.7)"})}});var l=["-moz-","-o-","-webkit-","-ms-",""];return k}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,b,d){function f(a,
+b,d){Object.defineProperty(a,b,{get:function(){if("RGB"===this.__state.space)return this.__state[b];p(this,b,d);return this.__state[b]},set:function(a){"RGB"!==this.__state.space&&(p(this,b,d),this.__state.space="RGB");this.__state[b]=a}})}function c(a,b){Object.defineProperty(a,b,{get:function(){if("HSV"===this.__state.space)return this.__state[b];k(this);return this.__state[b]},set:function(a){"HSV"!==this.__state.space&&(k(this),this.__state.space="HSV");this.__state[b]=a}})}function p(b,c,e){if("HEX"===
+b.__state.space)b.__state[c]=a.component_from_hex(b.__state.hex,e);else if("HSV"===b.__state.space)d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function k(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});d.isNaN(c.h)?d.isUndefined(b.__state.h)&&(b.__state.h=0):b.__state.h=c.h}var l=function(){this.__state=e.apply(this,arguments);if(!1===this.__state)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||
+1};l.COMPONENTS="r g b h s v hex a".split(" ");d.extend(l.prototype,{toString:function(){return b(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(l.prototype,"r",2);f(l.prototype,"g",1);f(l.prototype,"b",0);c(l.prototype,"h");c(l.prototype,"s");c(l.prototype,"v");Object.defineProperty(l.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(l.prototype,"hex",{get:function(){"HEX"!==!this.__state.space&&(this.__state.hex=
+a.rgb_to_hex(this.r,this.g,this.b));return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return l}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,b,d){var e=a/60-Math.floor(a/60),c=d*(1-b),p=d*(1-e*b);b=d*(1-(1-e)*b);a=[[d,b,c],[p,d,c],[c,d,b],[c,p,d],[b,c,d],[d,c,p]][Math.floor(a/60)%6];return{r:255*a[0],g:255*a[1],b:255*a[2]}},rgb_to_hsv:function(a,b,d){var e=Math.min(a,b,d),c=Math.max(a,b,d),e=c-e;if(0==c)return{h:NaN,s:0,v:0};
+a=(a==c?(b-d)/e:b==c?2+(d-a)/e:4+(a-b)/e)/6;0>a&&(a+=1);return{h:360*a,s:e/c,v:c/255}},rgb_to_hex:function(a,b,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,b);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,b){return a>>8*b&255},hex_with_component:function(a,b,d){return d<<(e=8*b)|a&~(255<<e)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||
+window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e,a){window.setTimeout(e,1E3/60)}}(),dat.dom.CenteredDiv=function(e,a){var b=function(){this.backgroundElement=document.createElement("div");a.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear"});e.makeFullscreen(this.backgroundElement);this.backgroundElement.style.position="fixed";this.domElement=
+document.createElement("div");a.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear"});document.body.appendChild(this.backgroundElement);document.body.appendChild(this.domElement);var b=this;e.bind(this.backgroundElement,"click",function(){b.hide()})};b.prototype.show=function(){var b=this;this.backgroundElement.style.display="block";this.domElement.style.display="block";this.domElement.style.opacity=
+0;this.domElement.style.webkitTransform="scale(1.1)";this.layout();a.defer(function(){b.backgroundElement.style.opacity=1;b.domElement.style.opacity=1;b.domElement.style.webkitTransform="scale(1)"})};b.prototype.hide=function(){var a=this,b=function(){a.domElement.style.display="none";a.backgroundElement.style.display="none";e.unbind(a.domElement,"webkitTransitionEnd",b);e.unbind(a.domElement,"transitionend",b);e.unbind(a.domElement,"oTransitionEnd",b)};e.bind(this.domElement,"webkitTransitionEnd",
+b);e.bind(this.domElement,"transitionend",b);e.bind(this.domElement,"oTransitionEnd",b);this.backgroundElement.style.opacity=0;this.domElement.style.opacity=0;this.domElement.style.webkitTransform="scale(1.1)"};b.prototype.layout=function(){this.domElement.style.left=window.innerWidth/2-e.getWidth(this.domElement)/2+"px";this.domElement.style.top=window.innerHeight/2-e.getHeight(this.domElement)/2+"px"};return b}(dat.dom.dom,dat.utils.common),dat.dom.dom,dat.utils.common); \ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2061c31
--- /dev/null
+++ b/index.html
@@ -0,0 +1,197 @@
+<!doctype html>
+<html>
+<head>
+<style>body{background:white}</style>
+</head>
+<body>
+<button id="clear">clear</button>
+<input type="text" id="url" placeholder="enter a url" style="width: 200px;">
+<a href="javascript:(function(a)%7Ba%3Ddocument.createElement(%22script%22)%3Ba.src%3D%22http://asdf.us/gif-recorder/js/record.min.js%22%3Bdocument.body.appendChild(a)%7D)()">screen record bookmarklet</a>
+<div id="buttons"></div>
+<div id="canvas"></div>
+<video id="vid" style="display: none"></video>
+</body>
+<script src="dat.gui.min.js"></script>
+<script src="canvasquery.js"></script>
+<script>
+
+/*
+var videos = "diesel1.mov.webm diesel2.mov.webm diesel3.mov.webm diesel4.mov.webm diesel5.mov.webm MVI_00245.mov.webm MVI_0026_17(23).mov.webm MVI_1671_2.mov.webm MVI_50545.mov.webm MVI_5054_16(21).mov.webm MVI_0026_1319.mov.webm MVI_0026_38.mov.webm MVI_1671_3.mov.webm MVI_5054_10(15).mov.webm smoke.webm MVI_0026_1420.mov.webm MVI_0026_915.mov.webm MVI_1671_4.mov.webm MVI_5054_12(17).mov.webm MVI_0026_1521.mov.webm MVI_1671.mov.webm MVI_1671_5.mov.webm MVI_5054_13(18).mov.webm MVI_0026_16.mov.webm MVI_1671_1.mov.webm MVI_5045.mov.webm MVI_5054_15(20).mov.webm".split(" ")
+videos.forEach(function(v){
+ var button = document.createElement("button")
+ button.innerHTML = v
+ button.onclick = function(){
+ video.src = v
+ }
+ document.getElementById("buttons").appendChild(button)
+})
+*/
+
+var video = document.getElementById("vid");
+video.addEventListener('loadedmetadata', loaded, false);
+function loaded () {
+ vw = video.videoWidth
+ vh = video.videoHeight
+ videoSrc = cq(vw, vh)
+ video.setAttribute("loop", "loop")
+ video.play()
+ loadImage( "http://www.carbonpictures.com/bucky/data/3160/Screen%20Shot%202015-05-06%20at%203.22.37%20PM.png" )
+
+}
+video.src = "smoke.webm";
+
+
+// video.src = "174708-1c2pRjKN-aI.mp4";
+// video.src = "ad.mp4";
+// video.src = "gopro.mp4";
+
+var imageData, imageDataData, imageCopy, imageCopyData, destCopy, logo
+
+url.addEventListener("keydown", function(e){
+ if (e.keyCode != 13) return
+ loadImage(url.value)
+})
+
+function loadImage(src){
+ src = "/cgi-bin/proxy?" + src
+ document.getElementById("canvas").innerHTML = ""
+ var image = new Image ();
+ image.addEventListener("load", function(){
+ w = image.naturalWidth
+ h = image.naturalHeight
+ dest = cq(w, h).drawImage( image, 0, 0, w, h, 0,0,w,vh ).appendTo("#canvas")
+ destCopy = cq(w, h)
+ imageData = dest.getImageData( 0, 0, w, h )
+ imageCopy = dest.getImageData( 0, 0, w, h )
+ imageDataData = imageData.data
+ imageCopyData = imageCopy.data
+
+ for (var i = 0, len = imageCopyData; i < len; i++) {
+ imageCopyData[i] = 0
+ }
+ gray = new Array (imageCopyData/4|0)
+ for (var i = 0, len = imageCopyData/4; i < len; i++) {
+ gray[i] = 0
+ }
+
+
+ }, false);
+ image.src = src;
+}
+
+var videoSrc, dest, mask, fill, imageData, gray;
+var ready = false;
+
+function go () {
+}
+
+(function animate(){
+ requestAnimationFrame(animate)
+ if (! videoSrc || ! imageData) return
+ render()
+})();
+
+function render(){
+ videoSrc.drawImage(video, 0, 0, vw, vh)
+ var videoData = videoSrc.getImageData(0, 0, vw, vh)
+ var videoDataData = videoData.data
+ var x, y, xx, yy, val, realVal
+ var j = 0
+
+ var rAvg = params.rAvg, gAvg = params.gAvg, bAvg = params.bAvg, valAvg = params.valAvg
+ var xfac = params.xFactor, yfac = params.yFactor, valfac = params.valFactor
+
+ for (var i = 0, len = videoDataData.length/4 |0; i < len; i++) {
+
+// x = i % vw
+// y = ~~(i/vw)
+//
+// xx = x % vw
+// yy = y % vh
+
+ xx = i % vw
+ yy = ~~(i/vw)
+
+ x = xx
+ y = yy
+
+ jj = (y*w + x) * 4
+
+ realVal = videoDataData[ ((yy*vw + xx)*4) ]
+ val = gray[i] = avga( valAvg, gray[i], realVal )
+
+ val *= valfac
+
+ x = clamp( ~~(x + val * xfac), 0, w-1 ) | 0
+ y = clamp( ~~(y + val * yfac), 0, h-1 ) | 0
+
+ j = clamp( (y*w + x), 0, len-1) * 4
+
+ j = ~~ j
+ jj = ~~ jj
+
+ imageCopyData[jj] = avga(rAvg, imageDataData[j], imageCopyData[j])
+ imageCopyData[jj+1] = avga(gAvg, imageDataData[j+1], imageCopyData[j+1])
+ imageCopyData[jj+2] = avga(bAvg, imageDataData[j+2], imageCopyData[j+2])
+
+ imageCopyData[jj+3] = 255
+
+ }
+
+ destCopy.putImageData(imageCopy, 0, 0)
+
+//if (params.logoAlpha) {
+// dest.context.globalAlpha = params.logoAlpha
+// dest.context.globalCompositeOperation = 'destination-in'
+// dest.drawImage(logo.canvas, params.logoX, params.logoY, w*params.logoScale,h*params.logoScale)
+//}
+
+ dest.context.globalAlpha = params.imageAlpha
+ dest.context.globalCompositeOperation = 'source-over'
+ dest.drawImage(destCopy.canvas, 0, 0, w,h)
+
+ dest.context.globalAlpha = 1.0
+// dest.context.globalAlpha = 0.2
+// dest.context.globalCompositeOperation = 'destination-in'
+// dest.drawImage(logo.canvas, params.logoX, params.logoY, w*params.logoScale,h*params.logoScale)
+}
+var params = {}
+// params.logoX = 166
+// params.logoY = 133
+// params.logoScale = 0.7
+// params.logoAlpha = 0.0
+params.imageAlpha = 1.0
+params.rAvg = 5
+params.gAvg = 5
+params.bAvg = 5
+params.valAvg = 2
+params.xFactor = 1
+params.yFactor = 1
+params.valFactor = 1
+
+window.onload = function() {
+ var gui = new dat.GUI();
+// gui.add(params, 'logoX', 0, w);
+// gui.add(params, 'logoY', 0, h);
+// gui.add(params, 'logoScale', 0.2, 1);
+// gui.add(params, 'logoAlpha', 0.0, 1);
+ gui.add(params, 'imageAlpha', 0.0, 1);
+ gui.add(params, 'rAvg', 2, 20);
+ gui.add(params, 'gAvg', 2, 20);
+ gui.add(params, 'bAvg', 2, 20);
+ gui.add(params, 'valAvg', 2, 20);
+ gui.add(params, 'xFactor', -5, 5);
+ gui.add(params, 'yFactor', -5, 5);
+ gui.add(params, 'valFactor', -20, 20);
+}
+
+document.getElementById("clear").onclick = function(){
+ dest.clearRect(0,0,w,h)
+}
+
+function avga(n,a,b) { return ((a*(~~n-1) + b)/~~n)|0 }
+function avgb(n,b,a) { return ((a*(~~n-1) + b)/~~n)|0 }
+function clamp(n,a,b) { return n < a ? a : n > b ? b : n }
+</script>
+</html>
+