UNPKG

@bigfishtv/cockpit

Version:

1,776 lines (1,591 loc) 109 kB
// Generated by CoffeeScript 1.6.1 (function() { var $, Analyze, Blender, Calculate, Caman, CamanParser, Canvas, Convert, Event, Fiber, Filter, IO, Image, Layer, Log, Logger, PixelInfo, Plugin, Renderer, Root, Store, Util, fs, slice, vignetteFilters, __hasProp = {}.hasOwnProperty, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __slice = [].slice, _this = this; slice = Array.prototype.slice; $ = function(sel, root) { if (root == null) { root = document; } if (typeof sel === "object" || (typeof exports !== "undefined" && exports !== null)) { return sel; } return root.querySelector(sel); }; Util = (function() { function Util() {} Util.uniqid = (function() { var id; id = 0; return { get: function() { return id++; } }; })(); Util.extend = function(obj) { var copy, dest, prop, src, _i, _len; dest = obj; src = slice.call(arguments, 1); for (_i = 0, _len = src.length; _i < _len; _i++) { copy = src[_i]; for (prop in copy) { if (!__hasProp.call(copy, prop)) continue; dest[prop] = copy[prop]; } } return dest; }; Util.clampRGB = function(val) { if (val < 0) { return 0; } if (val > 255) { return 255; } return val; }; Util.copyAttributes = function(from, to, opts) { var attr, _i, _len, _ref, _ref1, _results; if (opts == null) { opts = {}; } _ref = from.attributes; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { attr = _ref[_i]; if ((opts.except != null) && (_ref1 = attr.nodeName, __indexOf.call(opts.except, _ref1) >= 0)) { continue; } _results.push(to.setAttribute(attr.nodeName, attr.nodeValue)); } return _results; }; Util.dataArray = function(length) { if (length == null) { length = 0; } if (Caman.NodeJS || (window.Uint8Array != null)) { return new Uint8Array(length); } return new Array(length); }; return Util; })(); if (typeof exports !== "undefined" && exports !== null) { Root = exports; Canvas = require('canvas'); Image = Canvas.Image; Fiber = require('fibers'); fs = require('fs'); } else { Root = window; } Root.Caman = Caman = (function() { Caman.version = { release: "4.1.1", date: "4/8/2013" }; Caman.DEBUG = false; Caman.NodeJS = typeof exports !== "undefined" && exports !== null; Caman.autoload = !Caman.NodeJS; Caman.allowRevert = true; Caman.crossOrigin = "anonymous"; Caman.toString = function() { return "Version " + Caman.version.release + ", Released " + Caman.version.date; }; Caman.remoteProxy = ""; Caman.proxyParam = "camanProxyUrl"; Caman.getAttrId = function(canvas) { if (Caman.NodeJS) { return true; } if (typeof canvas === "string") { canvas = $(canvas); } if (!((canvas != null) && (canvas.getAttribute != null))) { return null; } return canvas.getAttribute('data-caman-id'); }; function Caman() { var args, callback, id, _this = this; if (arguments.length === 0) { throw "Invalid arguments"; } if (this instanceof Caman) { this.finishInit = this.finishInit.bind(this); this.imageLoaded = this.imageLoaded.bind(this); args = arguments[0]; if (!Caman.NodeJS) { id = parseInt(Caman.getAttrId(args[0]), 10); callback = typeof args[1] === "function" ? args[1] : typeof args[2] === "function" ? args[2] : function() {}; if (!isNaN(id) && Store.has(id)) { return Store.execute(id, callback); } } this.id = Util.uniqid.get(); this.initializedPixelData = this.originalPixelData = null; this.cropCoordinates = { x: 0, y: 0 }; this.cropped = false; this.resized = false; this.pixelStack = []; this.layerStack = []; this.canvasQueue = []; this.currentLayer = null; this.scaled = false; this.analyze = new Analyze(this); this.renderer = new Renderer(this); this.domIsLoaded(function() { _this.parseArguments(args); return _this.setup(); }); return this; } else { return new Caman(arguments); } } Caman.prototype.domIsLoaded = function(cb) { var listener, _this = this; if (Caman.NodeJS) { return setTimeout(function() { return cb.call(_this); }, 0); } else { if (document.readyState === "complete") { Log.debug("DOM initialized"); return setTimeout(function() { return cb.call(_this); }, 0); } else { listener = function() { if (document.readyState === "complete") { Log.debug("DOM initialized"); return cb.call(_this); } }; return document.addEventListener("readystatechange", listener, false); } } }; Caman.prototype.parseArguments = function(args) { var key, val, _ref, _results; if (args.length === 0) { throw "Invalid arguments given"; } this.initObj = null; this.initType = null; this.imageUrl = null; this.callback = function() {}; this.setInitObject(args[0]); if (args.length === 1) { return; } switch (typeof args[1]) { case "string": this.imageUrl = args[1]; break; case "function": this.callback = args[1]; } if (args.length === 2) { return; } this.callback = args[2]; if (args.length === 4) { _ref = args[4]; _results = []; for (key in _ref) { if (!__hasProp.call(_ref, key)) continue; val = _ref[key]; _results.push(this.options[key] = val); } return _results; } }; Caman.prototype.setInitObject = function(obj) { if (Caman.NodeJS) { this.initObj = obj; this.initType = 'node'; return; } if (typeof obj === "object") { this.initObj = obj; } else { this.initObj = $(obj); } if (this.initObj == null) { throw "Could not find image or canvas for initialization."; } return this.initType = this.initObj.nodeName.toLowerCase(); }; Caman.prototype.setup = function() { switch (this.initType) { case "node": return this.initNode(); case "img": return this.initImage(); case "canvas": return this.initCanvas(); } }; Caman.prototype.initNode = function() { var _this = this; Log.debug("Initializing for NodeJS"); this.image = new Image(); this.image.onload = function() { Log.debug("Image loaded. Width = " + (_this.imageWidth()) + ", Height = " + (_this.imageHeight())); _this.canvas = new Canvas(_this.imageWidth(), _this.imageHeight()); return _this.finishInit(); }; this.image.onerror = function(err) { throw err; }; return this.image.src = this.initObj; }; Caman.prototype.initImage = function() { this.image = this.initObj; this.canvas = document.createElement('canvas'); this.context = this.canvas.getContext('2d'); Util.copyAttributes(this.image, this.canvas, { except: ['src'] }); this.image.parentNode.replaceChild(this.canvas, this.image); this.imageAdjustments(); return this.waitForImageLoaded(); }; Caman.prototype.initCanvas = function() { this.canvas = this.initObj; this.context = this.canvas.getContext('2d'); if (this.imageUrl != null) { this.image = document.createElement('img'); this.image.src = this.imageUrl; this.imageAdjustments(); return this.waitForImageLoaded(); } else { return this.finishInit(); } }; Caman.prototype.imageAdjustments = function() { if (this.needsHiDPISwap()) { Log.debug(this.image.src, "->", this.hiDPIReplacement()); this.swapped = true; this.image.src = this.hiDPIReplacement(); } if (IO.isRemote(this.image)) { this.image.src = IO.proxyUrl(this.image.src); return Log.debug("Remote image detected, using URL = " + this.image.src); } }; Caman.prototype.waitForImageLoaded = function() { if (this.isImageLoaded()) { return this.imageLoaded(); } else { return this.image.onload = this.imageLoaded; } }; Caman.prototype.isImageLoaded = function() { if (!this.image.complete) { return false; } if ((this.image.naturalWidth != null) && this.image.naturalWidth === 0) { return false; } return true; }; Caman.prototype.imageWidth = function() { return this.image.width || this.image.naturalWidth; }; Caman.prototype.imageHeight = function() { return this.image.height || this.image.naturalHeight; }; Caman.prototype.imageLoaded = function() { Log.debug("Image loaded. Width = " + (this.imageWidth()) + ", Height = " + (this.imageHeight())); if (this.swapped) { this.canvas.width = this.imageWidth() / this.hiDPIRatio(); this.canvas.height = this.imageHeight() / this.hiDPIRatio(); } else { this.canvas.width = this.imageWidth(); this.canvas.height = this.imageHeight(); } return this.finishInit(); }; Caman.prototype.finishInit = function() { var i, pixel, _i, _len, _ref; if (this.context == null) { this.context = this.canvas.getContext('2d'); } this.originalWidth = this.preScaledWidth = this.width = this.canvas.width; this.originalHeight = this.preScaledHeight = this.height = this.canvas.height; this.hiDPIAdjustments(); if (!this.hasId()) { this.assignId(); } if (this.image != null) { this.context.drawImage(this.image, 0, 0, this.imageWidth(), this.imageHeight(), 0, 0, this.preScaledWidth, this.preScaledHeight); } this.reloadCanvasData(); if (Caman.allowRevert) { this.initializedPixelData = Util.dataArray(this.pixelData.length); this.originalPixelData = Util.dataArray(this.pixelData.length); _ref = this.pixelData; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { pixel = _ref[i]; this.initializedPixelData[i] = pixel; this.originalPixelData[i] = pixel; } } this.dimensions = { width: this.canvas.width, height: this.canvas.height }; Store.put(this.id, this); this.callback.call(this, this); return this.callback = function() {}; }; Caman.prototype.reloadCanvasData = function() { this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); return this.pixelData = this.imageData.data; }; Caman.prototype.resetOriginalPixelData = function() { var pixel, _i, _len, _ref, _results; if (!Caman.allowRevert) { throw "Revert disabled"; } this.originalPixelData = Util.dataArray(this.pixelData.length); _ref = this.pixelData; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { pixel = _ref[_i]; _results.push(this.originalPixelData.push(pixel)); } return _results; }; Caman.prototype.hasId = function() { return Caman.getAttrId(this.canvas) != null; }; Caman.prototype.assignId = function() { if (Caman.NodeJS || this.canvas.getAttribute('data-caman-id')) { return; } return this.canvas.setAttribute('data-caman-id', this.id); }; Caman.prototype.hiDPIDisabled = function() { return this.canvas.getAttribute('data-caman-hidpi-disabled') !== null; }; Caman.prototype.hiDPIAdjustments = function() { var ratio; if (Caman.NodeJS || this.hiDPIDisabled()) { return; } ratio = this.hiDPIRatio(); if (ratio !== 1) { Log.debug("HiDPI ratio = " + ratio); this.scaled = true; this.preScaledWidth = this.canvas.width; this.preScaledHeight = this.canvas.height; this.canvas.width = this.preScaledWidth * ratio; this.canvas.height = this.preScaledHeight * ratio; this.canvas.style.width = "" + this.preScaledWidth + "px"; this.canvas.style.height = "" + this.preScaledHeight + "px"; this.context.scale(ratio, ratio); this.width = this.originalWidth = this.canvas.width; return this.height = this.originalHeight = this.canvas.height; } }; Caman.prototype.hiDPIRatio = function() { var backingStoreRatio, devicePixelRatio; devicePixelRatio = window.devicePixelRatio || 1; backingStoreRatio = this.context.webkitBackingStorePixelRatio || this.context.mozBackingStorePixelRatio || this.context.msBackingStorePixelRatio || this.context.oBackingStorePixelRatio || this.context.backingStorePixelRatio || 1; return devicePixelRatio / backingStoreRatio; }; Caman.prototype.hiDPICapable = function() { return (window.devicePixelRatio != null) && window.devicePixelRatio !== 1; }; Caman.prototype.needsHiDPISwap = function() { if (this.hiDPIDisabled() || !this.hiDPICapable()) { return false; } return this.hiDPIReplacement() !== null; }; Caman.prototype.hiDPIReplacement = function() { if (this.image == null) { return null; } return this.image.getAttribute('data-caman-hidpi'); }; Caman.prototype.replaceCanvas = function(newCanvas) { var oldCanvas; oldCanvas = this.canvas; this.canvas = newCanvas; this.context = this.canvas.getContext('2d'); oldCanvas.parentNode.replaceChild(this.canvas, oldCanvas); this.width = this.canvas.width; this.height = this.canvas.height; this.reloadCanvasData(); return this.dimensions = { width: this.canvas.width, height: this.canvas.height }; }; Caman.prototype.render = function(callback) { var _this = this; if (callback == null) { callback = function() {}; } Event.trigger(this, "renderStart"); return this.renderer.execute(function() { _this.context.putImageData(_this.imageData, 0, 0); return callback.call(_this); }); }; Caman.prototype.revert = function() { var i, pixel, _i, _len, _ref; if (!Caman.allowRevert) { throw "Revert disabled"; } _ref = this.originalVisiblePixels(); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { pixel = _ref[i]; this.pixelData[i] = pixel; } return this.context.putImageData(this.imageData, 0, 0); }; Caman.prototype.reset = function() { var canvas, ctx, i, imageData, pixel, pixelData, _i, _len, _ref; canvas = document.createElement('canvas'); Util.copyAttributes(this.canvas, canvas); canvas.width = this.originalWidth; canvas.height = this.originalHeight; ctx = canvas.getContext('2d'); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); pixelData = imageData.data; _ref = this.initializedPixelData; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { pixel = _ref[i]; pixelData[i] = pixel; } ctx.putImageData(imageData, 0, 0); this.cropCoordinates = { x: 0, y: 0 }; this.resized = false; return this.replaceCanvas(canvas); }; Caman.prototype.originalVisiblePixels = function() { var canvas, coord, ctx, endX, endY, i, imageData, pixel, pixelData, pixels, scaledCanvas, startX, startY, width, _i, _j, _len, _ref, _ref1, _ref2, _ref3; if (!Caman.allowRevert) { throw "Revert disabled"; } pixels = []; startX = this.cropCoordinates.x; endX = startX + this.width; startY = this.cropCoordinates.y; endY = startY + this.height; if (this.resized) { canvas = document.createElement('canvas'); canvas.width = this.originalWidth; canvas.height = this.originalHeight; ctx = canvas.getContext('2d'); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); pixelData = imageData.data; _ref = this.originalPixelData; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { pixel = _ref[i]; pixelData[i] = pixel; } ctx.putImageData(imageData, 0, 0); scaledCanvas = document.createElement('canvas'); scaledCanvas.width = this.width; scaledCanvas.height = this.height; ctx = scaledCanvas.getContext('2d'); ctx.drawImage(canvas, 0, 0, this.originalWidth, this.originalHeight, 0, 0, this.width, this.height); pixelData = ctx.getImageData(0, 0, this.width, this.height).data; width = this.width; } else { pixelData = this.originalPixelData; width = this.originalWidth; } for (i = _j = 0, _ref1 = pixelData.length; _j < _ref1; i = _j += 4) { coord = PixelInfo.locationToCoordinates(i, width); if (((startX <= (_ref2 = coord.x) && _ref2 < endX)) && ((startY <= (_ref3 = coord.y) && _ref3 < endY))) { pixels.push(pixelData[i], pixelData[i + 1], pixelData[i + 2], pixelData[i + 3]); } } return pixels; }; Caman.prototype.process = function(name, processFn) { this.renderer.add({ type: Filter.Type.Single, name: name, processFn: processFn }); return this; }; Caman.prototype.processKernel = function(name, adjust, divisor, bias) { var i, _i, _ref; if (!divisor) { divisor = 0; for (i = _i = 0, _ref = adjust.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { divisor += adjust[i]; } } this.renderer.add({ type: Filter.Type.Kernel, name: name, adjust: adjust, divisor: divisor, bias: bias || 0 }); return this; }; Caman.prototype.processPlugin = function(plugin, args) { this.renderer.add({ type: Filter.Type.Plugin, plugin: plugin, args: args }); return this; }; Caman.prototype.newLayer = function(callback) { var layer; layer = new Layer(this); this.canvasQueue.push(layer); this.renderer.add({ type: Filter.Type.LayerDequeue }); callback.call(layer); this.renderer.add({ type: Filter.Type.LayerFinished }); return this; }; Caman.prototype.executeLayer = function(layer) { return this.pushContext(layer); }; Caman.prototype.pushContext = function(layer) { this.layerStack.push(this.currentLayer); this.pixelStack.push(this.pixelData); this.currentLayer = layer; return this.pixelData = layer.pixelData; }; Caman.prototype.popContext = function() { this.pixelData = this.pixelStack.pop(); return this.currentLayer = this.layerStack.pop(); }; Caman.prototype.applyCurrentLayer = function() { return this.currentLayer.applyToParent(); }; return Caman; })(); Analyze = (function() { function Analyze(c) { this.c = c; } Analyze.prototype.calculateLevels = function() { var i, levels, numPixels, _i, _j, _k, _ref; levels = { r: {}, g: {}, b: {} }; for (i = _i = 0; _i <= 255; i = ++_i) { levels.r[i] = 0; levels.g[i] = 0; levels.b[i] = 0; } for (i = _j = 0, _ref = this.c.pixelData.length; _j < _ref; i = _j += 4) { levels.r[this.c.pixelData[i]]++; levels.g[this.c.pixelData[i + 1]]++; levels.b[this.c.pixelData[i + 2]]++; } numPixels = this.c.pixelData.length / 4; for (i = _k = 0; _k <= 255; i = ++_k) { levels.r[i] /= numPixels; levels.g[i] /= numPixels; levels.b[i] /= numPixels; } return levels; }; return Analyze; })(); Caman.DOMUpdated = function() { var img, imgs, parser, _i, _len, _results; imgs = document.querySelectorAll("img[data-caman]"); if (!(imgs.length > 0)) { return; } _results = []; for (_i = 0, _len = imgs.length; _i < _len; _i++) { img = imgs[_i]; _results.push(parser = new CamanParser(img, function() { this.parse(); return this.execute(); })); } return _results; }; if (Caman.autoload) { (function() { if (document.readyState === "complete") { return Caman.DOMUpdated(); } else { return document.addEventListener("DOMContentLoaded", Caman.DOMUpdated, false); } })(); } CamanParser = (function() { var INST_REGEX; INST_REGEX = "(\\w+)\\((.*?)\\)"; function CamanParser(ele, ready) { this.dataStr = ele.getAttribute('data-caman'); this.caman = Caman(ele, ready.bind(this)); } CamanParser.prototype.parse = function() { var args, filter, func, inst, instFunc, m, r, unparsedInstructions, _i, _len, _ref, _results; this.ele = this.caman.canvas; r = new RegExp(INST_REGEX, 'g'); unparsedInstructions = this.dataStr.match(r); if (!(unparsedInstructions.length > 0)) { return; } r = new RegExp(INST_REGEX); _results = []; for (_i = 0, _len = unparsedInstructions.length; _i < _len; _i++) { inst = unparsedInstructions[_i]; _ref = inst.match(r), m = _ref[0], filter = _ref[1], args = _ref[2]; instFunc = new Function("return function() { this." + filter + "(" + args + "); };"); try { func = instFunc(); _results.push(func.call(this.caman)); } catch (e) { _results.push(Log.debug(e)); } } return _results; }; CamanParser.prototype.execute = function() { var ele; ele = this.ele; return this.caman.render(function() { return ele.parentNode.replaceChild(this.toImage(), ele); }); }; return CamanParser; })(); Caman.Blender = Blender = (function() { function Blender() {} Blender.blenders = {}; Blender.register = function(name, func) { return this.blenders[name] = func; }; Blender.execute = function(name, rgbaLayer, rgbaParent) { return this.blenders[name](rgbaLayer, rgbaParent); }; return Blender; })(); Caman.Calculate = Calculate = (function() { function Calculate() {} Calculate.distance = function(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); }; Calculate.randomRange = function(min, max, getFloat) { var rand; if (getFloat == null) { getFloat = false; } rand = min + (Math.random() * (max - min)); if (getFloat) { return rand.toFixed(getFloat); } else { return Math.round(rand); } }; Calculate.luminance = function(rgba) { return (0.299 * rgba.r) + (0.587 * rgba.g) + (0.114 * rgba.b); }; Calculate.bezier = function(start, ctrl1, ctrl2, end, lowBound, highBound) { var Ax, Ay, Bx, By, Cx, Cy, bezier, curveX, curveY, i, j, leftCoord, rightCoord, t, x0, x1, x2, x3, y0, y1, y2, y3, _i, _j, _k, _ref, _ref1; x0 = start[0]; y0 = start[1]; x1 = ctrl1[0]; y1 = ctrl1[1]; x2 = ctrl2[0]; y2 = ctrl2[1]; x3 = end[0]; y3 = end[1]; bezier = {}; Cx = parseInt(3 * (x1 - x0), 10); Bx = 3 * (x2 - x1) - Cx; Ax = x3 - x0 - Cx - Bx; Cy = 3 * (y1 - y0); By = 3 * (y2 - y1) - Cy; Ay = y3 - y0 - Cy - By; for (i = _i = 0; _i < 1000; i = ++_i) { t = i / 1000; curveX = Math.round((Ax * Math.pow(t, 3)) + (Bx * Math.pow(t, 2)) + (Cx * t) + x0); curveY = Math.round((Ay * Math.pow(t, 3)) + (By * Math.pow(t, 2)) + (Cy * t) + y0); if (lowBound && curveY < lowBound) { curveY = lowBound; } else if (highBound && curveY > highBound) { curveY = highBound; } bezier[curveX] = curveY; } if (bezier.length < end[0] + 1) { for (i = _j = 0, _ref = end[0]; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) { if (bezier[i] == null) { leftCoord = [i - 1, bezier[i - 1]]; for (j = _k = i, _ref1 = end[0]; i <= _ref1 ? _k <= _ref1 : _k >= _ref1; j = i <= _ref1 ? ++_k : --_k) { if (bezier[j] != null) { rightCoord = [j, bezier[j]]; break; } } bezier[i] = leftCoord[1] + ((rightCoord[1] - leftCoord[1]) / (rightCoord[0] - leftCoord[0])) * (i - leftCoord[0]); } } } if (bezier[end[0]] == null) { bezier[end[0]] = bezier[end[0] - 1]; } return bezier; }; return Calculate; })(); Convert = (function() { function Convert() {} Convert.hexToRGB = function(hex) { var b, g, r; if (hex.charAt(0) === "#") { hex = hex.substr(1); } r = parseInt(hex.substr(0, 2), 16); g = parseInt(hex.substr(2, 2), 16); b = parseInt(hex.substr(4, 2), 16); return { r: r, g: g, b: b }; }; Convert.rgbToHSL = function(r, g, b) { var d, h, l, max, min, s; if (typeof r === "object") { g = r.g; b = r.b; r = r.r; } r /= 255; g /= 255; b /= 255; max = Math.max(r, g, b); min = Math.min(r, g, b); l = (max + min) / 2; if (max === min) { h = s = 0; } else { d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); h = (function() { switch (max) { case r: return (g - b) / d + (g < b ? 6 : 0); case g: return (b - r) / d + 2; case b: return (r - g) / d + 4; } })(); h /= 6; } return { h: h, s: s, l: l }; }; Convert.hslToRGB = function(h, s, l) { var b, g, p, q, r; if (typeof h === "object") { s = h.s; l = h.l; h = h.h; } if (s === 0) { r = g = b = l; } else { q = l < 0.5 ? l * (1 + s) : l + s - l * s; p = 2 * l - q; r = this.hueToRGB(p, q, h + 1 / 3); g = this.hueToRGB(p, q, h); b = this.hueToRGB(p, q, h - 1 / 3); } return { r: r * 255, g: g * 255, b: b * 255 }; }; Convert.hueToRGB = function(p, q, t) { if (t < 0) { t += 1; } if (t > 1) { t -= 1; } if (t < 1 / 6) { return p + (q - p) * 6 * t; } if (t < 1 / 2) { return q; } if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } return p; }; Convert.rgbToHSV = function(r, g, b) { var d, h, max, min, s, v; r /= 255; g /= 255; b /= 255; max = Math.max(r, g, b); min = Math.min(r, g, b); v = max; d = max - min; s = max === 0 ? 0 : d / max; if (max === min) { h = 0; } else { h = (function() { switch (max) { case r: return (g - b) / d + (g < b ? 6 : 0); case g: return (b - r) / d + 2; case b: return (r - g) / d + 4; } })(); h /= 6; } return { h: h, s: s, v: v }; }; Convert.hsvToRGB = function(h, s, v) { var b, f, g, i, p, q, r, t; i = Math.floor(h * 6); f = h * 6 - i; p = v * (1 - s); q = v * (1 - f * s); 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; } return { r: r * 255, g: g * 255, b: b * 255 }; }; Convert.rgbToXYZ = function(r, g, b) { var x, y, z; r /= 255; g /= 255; b /= 255; if (r > 0.04045) { r = Math.pow((r + 0.055) / 1.055, 2.4); } else { r /= 12.92; } if (g > 0.04045) { g = Math.pow((g + 0.055) / 1.055, 2.4); } else { g /= 12.92; } if (b > 0.04045) { b = Math.pow((b + 0.055) / 1.055, 2.4); } else { b /= 12.92; } x = r * 0.4124 + g * 0.3576 + b * 0.1805; y = r * 0.2126 + g * 0.7152 + b * 0.0722; z = r * 0.0193 + g * 0.1192 + b * 0.9505; return { x: x * 100, y: y * 100, z: z * 100 }; }; Convert.xyzToRGB = function(x, y, z) { var b, g, r; x /= 100; y /= 100; z /= 100; r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z); g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z); b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z); if (r > 0.0031308) { r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055; } else { r *= 12.92; } if (g > 0.0031308) { g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055; } else { g *= 12.92; } if (b > 0.0031308) { b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055; } else { b *= 12.92; } return { r: r * 255, g: g * 255, b: b * 255 }; }; Convert.xyzToLab = function(x, y, z) { var a, b, l, whiteX, whiteY, whiteZ; if (typeof x === "object") { y = x.y; z = x.z; x = x.x; } whiteX = 95.047; whiteY = 100.0; whiteZ = 108.883; x /= whiteX; y /= whiteY; z /= whiteZ; if (x > 0.008856451679) { x = Math.pow(x, 0.3333333333); } else { x = (7.787037037 * x) + 0.1379310345; } if (y > 0.008856451679) { y = Math.pow(y, 0.3333333333); } else { y = (7.787037037 * y) + 0.1379310345; } if (z > 0.008856451679) { z = Math.pow(z, 0.3333333333); } else { z = (7.787037037 * z) + 0.1379310345; } l = 116 * y - 16; a = 500 * (x - y); b = 200 * (y - z); return { l: l, a: a, b: b }; }; Convert.labToXYZ = function(l, a, b) { var x, y, z; if (typeof l === "object") { a = l.a; b = l.b; l = l.l; } y = (l + 16) / 116; x = y + (a / 500); z = y - (b / 200); if (x > 0.2068965517) { x = x * x * x; } else { x = 0.1284185493 * (x - 0.1379310345); } if (y > 0.2068965517) { y = y * y * y; } else { y = 0.1284185493 * (y - 0.1379310345); } if (z > 0.2068965517) { z = z * z * z; } else { z = 0.1284185493 * (z - 0.1379310345); } return { x: x * 95.047, y: y * 100.0, z: z * 108.883 }; }; Convert.rgbToLab = function(r, g, b) { var xyz; if (typeof r === "object") { g = r.g; b = r.b; r = r.r; } xyz = this.rgbToXYZ(r, g, b); return this.xyzToLab(xyz); }; Convert.labToRGB = function(l, a, b) {}; return Convert; })(); Event = (function() { function Event() {} Event.events = {}; Event.types = ["processStart", "processComplete", "renderStart", "renderFinished", "blockStarted", "blockFinished"]; Event.trigger = function(target, type, data) { var event, _i, _len, _ref, _results; if (this.events[type] && this.events[type].length) { _ref = this.events[type]; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { event = _ref[_i]; if (event.target === null || target.id === event.target.id) { _results.push(event.fn.call(target, data)); } else { _results.push(void 0); } } return _results; } }; Event.listen = function(target, type, fn) { var _fn, _type; if (typeof target === "string") { _type = target; _fn = type; target = null; type = _type; fn = _fn; } if (__indexOf.call(this.types, type) < 0) { return false; } if (!this.events[type]) { this.events[type] = []; } this.events[type].push({ target: target, fn: fn }); return true; }; return Event; })(); Caman.Event = Event; Caman.Filter = Filter = (function() { function Filter() {} Filter.Type = { Single: 1, Kernel: 2, LayerDequeue: 3, LayerFinished: 4, LoadOverlay: 5, Plugin: 6 }; Filter.register = function(name, filterFunc) { return Caman.prototype[name] = filterFunc; }; return Filter; })(); Caman.IO = IO = (function() { function IO() {} IO.domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/; IO.isRemote = function(img) { if (img == null) { return false; } if (this.corsEnabled(img)) { return false; } return this.isURLRemote(img.src); }; IO.corsEnabled = function(img) { var _ref; return (img.crossOrigin != null) && ((_ref = img.crossOrigin.toLowerCase()) === 'anonymous' || _ref === 'use-credentials'); }; IO.isURLRemote = function(url) { var matches; matches = url.match(this.domainRegex); if (matches) { return matches[1] !== document.domain; } else { return false; } }; IO.remoteCheck = function(src) { if (this.isURLRemote(src)) { if (!Caman.remoteProxy.length) { Log.info("Attempting to load a remote image without a configured proxy. URL: " + src); } else { if (Caman.isURLRemote(Caman.remoteProxy)) { Log.info("Cannot use a remote proxy for loading images."); return; } return "" + Caman.remoteProxy + "?camanProxyUrl=" + (encodeURIComponent(src)); } } }; IO.proxyUrl = function(src) { return "" + Caman.remoteProxy + "?" + Caman.proxyParam + "=" + (encodeURIComponent(src)); }; IO.useProxy = function(lang) { var langToExt; langToExt = { ruby: 'rb', python: 'py', perl: 'pl', javascript: 'js' }; lang = lang.toLowerCase(); if (langToExt[lang] != null) { lang = langToExt[lang]; } return "proxies/caman_proxy." + lang; }; return IO; })(); Caman.prototype.save = function() { if (typeof exports !== "undefined" && exports !== null) { return this.nodeSave.apply(this, arguments); } else { return this.browserSave.apply(this, arguments); } }; Caman.prototype.browserSave = function(type) { var image; if (type == null) { type = "png"; } type = type.toLowerCase(); image = this.toBase64(type).replace("image/" + type, "image/octet-stream"); return document.location.href = image; }; Caman.prototype.nodeSave = function(file, overwrite) { var stats; if (overwrite == null) { overwrite = true; } try { stats = fs.statSync(file); if (stats.isFile() && !overwrite) { return false; } } catch (e) { Log.debug("Creating output file " + file); } return fs.writeFile(file, this.canvas.toBuffer(), function() { return Log.debug("Finished writing to " + file); }); }; Caman.prototype.toImage = function(type) { var img; img = document.createElement('img'); img.src = this.toBase64(type); img.width = this.dimensions.width; img.height = this.dimensions.height; if (window.devicePixelRatio) { img.width /= window.devicePixelRatio; img.height /= window.devicePixelRatio; } return img; }; Caman.prototype.toBase64 = function(type) { if (type == null) { type = "png"; } type = type.toLowerCase(); return this.canvas.toDataURL("image/" + type); }; Layer = (function() { function Layer(c) { this.c = c; this.filter = this.c; this.options = { blendingMode: 'normal', opacity: 1.0 }; this.layerID = Util.uniqid.get(); this.canvas = typeof exports !== "undefined" && exports !== null ? new Canvas() : document.createElement('canvas'); this.canvas.width = this.c.dimensions.width; this.canvas.height = this.c.dimensions.height; this.context = this.canvas.getContext('2d'); this.context.createImageData(this.canvas.width, this.canvas.height); this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); this.pixelData = this.imageData.data; } Layer.prototype.newLayer = function(cb) { return this.c.newLayer.call(this.c, cb); }; Layer.prototype.setBlendingMode = function(mode) { this.options.blendingMode = mode; return this; }; Layer.prototype.opacity = function(opacity) { this.options.opacity = opacity / 100; return this; }; Layer.prototype.copyParent = function() { var i, parentData, _i, _ref; parentData = this.c.pixelData; for (i = _i = 0, _ref = this.c.pixelData.length; _i < _ref; i = _i += 4) { this.pixelData[i] = parentData[i]; this.pixelData[i + 1] = parentData[i + 1]; this.pixelData[i + 2] = parentData[i + 2]; this.pixelData[i + 3] = parentData[i + 3]; } return this; }; Layer.prototype.fillColor = function() { return this.c.fillColor.apply(this.c, arguments); }; Layer.prototype.overlayImage = function(image) { if (typeof image === "object") { image = image.src; } else if (typeof image === "string" && image[0] === "#") { image = $(image).src; } if (!image) { return this; } this.c.renderer.renderQueue.push({ type: Filter.Type.LoadOverlay, src: image, layer: this }); return this; }; Layer.prototype.applyToParent = function() { var i, layerData, parentData, result, rgbaLayer, rgbaParent, _i, _ref, _results; parentData = this.c.pixelStack[this.c.pixelStack.length - 1]; layerData = this.c.pixelData; _results = []; for (i = _i = 0, _ref = layerData.length; _i < _ref; i = _i += 4) { rgbaParent = { r: parentData[i], g: parentData[i + 1], b: parentData[i + 2], a: parentData[i + 3] }; rgbaLayer = { r: layerData[i], g: layerData[i + 1], b: layerData[i + 2], a: layerData[i + 3] }; result = Blender.execute(this.options.blendingMode, rgbaLayer, rgbaParent); result.r = Util.clampRGB(result.r); result.g = Util.clampRGB(result.g); result.b = Util.clampRGB(result.b); if (result.a == null) { result.a = rgbaLayer.a; } parentData[i] = rgbaParent.r - ((rgbaParent.r - result.r) * (this.options.opacity * (result.a / 255))); parentData[i + 1] = rgbaParent.g - ((rgbaParent.g - result.g) * (this.options.opacity * (result.a / 255))); _results.push(parentData[i + 2] = rgbaParent.b - ((rgbaParent.b - result.b) * (this.options.opacity * (result.a / 255)))); } return _results; }; return Layer; })(); Logger = (function() { function Logger() { var name, _i, _len, _ref; _ref = ['log', 'info', 'warn', 'error']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; this[name] = (function(name) { return function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (!Caman.DEBUG) { return; } try { return console[name].apply(console, args); } catch (e) { return console[name](args); } }; })(name); } this.debug = this.log; } return Logger; })(); Log = new Logger(); PixelInfo = (function() { PixelInfo.coordinatesToLocation = function(x, y, width) { return (y * width + x) * 4; }; PixelInfo.locationToCoordinates = function(loc, width) { var x, y; y = Math.floor(loc / (width * 4)); x = (loc % (width * 4)) / 4; return { x: x, y: y }; }; function PixelInfo(c) { this.c = c; this.loc = 0; } PixelInfo.prototype.locationXY = function() { var x, y; y = this.c.dimensions.height - Math.floor(this.loc / (this.c.dimensions.width * 4)); x = (this.loc % (this.c.dimensions.width * 4)) / 4; return { x: x, y: y }; }; PixelInfo.prototype.getPixelRelative = function(horiz, vert) { var newLoc; newLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); if (newLoc > this.c.pixelData.length || newLoc < 0) { return { r: 0, g: 0, b: 0, a: 0 }; } return { r: this.c.pixelData[newLoc], g: this.c.pixelData[newLoc + 1], b: this.c.pixelData[newLoc + 2], a: this.c.pixelData[newLoc + 3] }; }; PixelInfo.prototype.putPixelRelative = function(horiz, vert, rgba) { var nowLoc; nowLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); if (newLoc > this.c.pixelData.length || newLoc < 0) { return; } this.c.pixelData[newLoc] = rgba.r; this.c.pixelData[newLoc + 1] = rgba.g; this.c.pixelData[newLoc + 2] = rgba.b; this.c.pixelData[newLoc + 3] = rgba.a; return true; }; PixelInfo.prototype.getPixel = function(x, y) { var loc; loc = this.coordinatesToLocation(x, y, this.width); return { r: this.c.pixelData[loc], g: this.c.pixelData[loc + 1], b: this.c.pixelData[loc + 2], a: this.c.pixelData[loc + 3] }; }; PixelInfo.prototype.putPixel = function(x, y, rgba) { var loc; loc = this.coordinatesToLocation(x, y, this.width); this.c.pixelData[loc] = rgba.r; this.c.pixelData[loc + 1] = rgba.g; this.c.pixelData[loc + 2] = rgba.b; return this.c.pixelData[loc + 3] = rgba.a; }; return PixelInfo; })(); Plugin = (function() { function Plugin() {} Plugin.plugins = {}; Plugin.register = function(name, plugin) { return this.plugins[name] = plugin; }; Plugin.execute = function(context, name, args) { return this.plugins[name].apply(context, args); }; return Plugin; })(); Caman.Plugin = Plugin; Caman.Renderer = Renderer = (function() { Renderer.Blocks = Caman.NodeJS ? require('os').cpus().length : 4; function Renderer(c) { var _this = this; this.c = c; this.processNext = function() { return Renderer.prototype.processNext.apply(_this, arguments); }; this.renderQueue = []; this.modPixelData = null; } Renderer.prototype.add = function(job) { if (job == null) { return; } return this.renderQueue.push(job); }; Renderer.prototype.processNext = function() { var layer; if (this.renderQueue.length === 0) { Event.trigger(this, "renderFinished"); if (this.finishedFn != null) { this.finishedFn.call(this.c); } return this; } this.currentJob = this.renderQueue.shift(); switch (this.currentJob.type) { case Filter.Type.LayerDequeue: layer = this.c.canvasQueue.shift(); this.c.executeLayer(layer); return this.processNext(); case Filter.Type.LayerFinished: this.c.applyCurrentLayer(); this.c.popContext(); return this.processNext(); case Filter.Type.LoadOverlay: return this.loadOverlay(this.currentJob.layer, this.currentJob.src); case Filter.Type.Plugin: return this.executePlugin(); default: return this.executeFilter(); } }; Renderer.prototype.execute = function(callback) { this.finishedFn = callback; this.modPixelData = Util.dataArray(this.c.pixelData.length); return this.processNext(); }; Renderer.prototype.eachBlock = function(fn) { var blockN, blockPixelLength, bnum, end, f, i, lastBlockN, n, start, _i, _ref, _results, _this = this; this.blocksDone = 0; n = this.c.pixelData.length; blockPixelLength = Math.floor((n / 4) / Renderer.Blocks); blockN = blockPixelLength * 4; lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4; _results = []; for (i = _i = 0, _ref = Renderer.Blocks; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { start = i * blockN; end = start + (i === Renderer.Blocks - 1 ? lastBlockN : blockN); if (Caman.NodeJS) { f = Fiber(function() { return fn.call(_this, i, start, end); }); bnum = f.run(); _results.push(this.blockFinished(bnum)); } else { _results.push(setTimeout((function(i, start, end) { return function() { return fn.call(_this, i, start, end); }; })(i, start, end), 0)); } } return _results; }; Renderer.prototype.executeFilter = function() { Event.trigger(this.c, "processStart", this.currentJob); if (this.currentJob.type === Filter.Type.Single) { return this.eachBlock(this.renderBlock); } else { return this.eachBlock(this.renderKernel); } }; Renderer.prototype.executePlugin = function() { Log.debug("Executing plugin " + this.currentJob.plugin); Plugin.execute(this.c, this.currentJob.plugin, this.currentJob.args); Log.debug("Plugin " + this.currentJob.plugin + " finished!"); return this.processNext(); }; Renderer.prototype.renderBlock = function(bnum, start, end) { var data, i, pixelInfo, res, _i; Log.debug("Block #" + bnum + " - Filter: " + this.currentJob.name + ", Start: " + start + ", End: " + end); Event.trigger(this.c, "blockStarted", { blockNum: bnum, totalBlocks: Renderer.Blocks, startPixel: start, endPixel: end }); data = { r: 0, g: 0, b: 0, a: 0 }; pixelInfo = new PixelInfo(this.c); for (i = _i = start; _i < end; i = _i += 4) { pixelInfo.loc = i; data.r = this.c.pixelData[i]; data.g = this.c.pixelData[i + 1]; data.b = this.c.pixelData[i + 2]; data.a = this.c.pixelData[i + 3]; res = this.currentJob.processFn.call(pixelInfo, data); if (res.a == null) { res.a = data.a; } this.c.pixelData[i] = Util.clampRGB(res.r); this.c.pixelData[i + 1] = Util.clampRGB(res.g); this.c.pixelData[i + 2] = Util.clampRGB(res.b); this.c.pixelData[i + 3] = Util.clampRGB(res.a); } if (Caman.NodeJS) { return Fiber["yield"](bnum); } else { return this.blockFinished(bnum); } }; Renderer.prototype.renderKernel = function(bnum, start, end) { var adjust, adjustSize, bias, builder, builderIndex, divisor, i, j, k, kernel, n, name, pixel, pixelInfo, res, _i, _j, _k; name = this.currentJob.name; bias = this.currentJob.bias; divisor = this.currentJob.divisor; n = this.c.pixelData.length; adjust = this.currentJob.adjust; adjustSize = Math.sqrt(adjust.length); kernel = []; Log.debug("Rendering kernel - Filter: " + this.currentJob.name); start = Math.max(start, this.c.dimensions.width * 4 * ((adjustSize - 1) / 2)); end = Math.min(end, n - (this.c.dimensions.width * 4 * ((adjustSize - 1) / 2))); builder = (adjustSize - 1) / 2; pixelInfo = new PixelInfo(this.c); for (i = _i = start; _i < end; i = _i += 4) { pixelInfo.loc = i; builderIndex = 0; for (j = _j = -builder; -builder <= builder ? _j <= builder : _j >= builder; j = -builder <= builder ? ++_j : --_j) { for (k = _k = builder; builder <= -builder ? _k <= -builder : _k >= -builder; k = builder <= -builder ? ++_k : --_k) { pixel = pixelInfo.getPixe