diff --git a/index.html b/index.html index 3b20ef4..b4fd70a 100644 --- a/index.html +++ b/index.html @@ -31,23 +31,26 @@

triangulate images

- - + +
- - + +
- - - + + +
- - - + + + +
+
+
diff --git a/scripts/lib/delaunay.js b/scripts/lib/delaunay.js deleted file mode 100644 index f7ba795..0000000 --- a/scripts/lib/delaunay.js +++ /dev/null @@ -1,190 +0,0 @@ -/*global define*/ -define( - function() - { - // https://github.com/ironwallaby/delaunay/blob/master/delaunay.js - function Triangle(a, b, c) { - this.a = a - this.b = b - this.c = c - - var A = b.x - a.x, - B = b.y - a.y, - C = c.x - a.x, - D = c.y - a.y, - E = A * (a.x + b.x) + B * (a.y + b.y), - F = C * (a.x + c.x) + D * (a.y + c.y), - G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)), - minx, miny, dx, dy - - /* If the points of the triangle are collinear, then just find the - * extremes and use the midpoint as the center of the circumcircle. */ - if(Math.abs(G) < 0.000001) { - minx = Math.min(a.x, b.x, c.x) - miny = Math.min(a.y, b.y, c.y) - dx = (Math.max(a.x, b.x, c.x) - minx) * 0.5 - dy = (Math.max(a.y, b.y, c.y) - miny) * 0.5 - - this.x = minx + dx - this.y = miny + dy - this.r = dx * dx + dy * dy - } - - else { - this.x = (D*E - B*F) / G - this.y = (A*F - C*E) / G - dx = this.x - a.x - dy = this.y - a.y - this.r = dx * dx + dy * dy - } - } - - Triangle.prototype.draw = function(ctx) { - ctx.beginPath() - ctx.moveTo(this.a.x, this.a.y) - ctx.lineTo(this.b.x, this.b.y) - ctx.lineTo(this.c.x, this.c.y) - ctx.closePath() - ctx.stroke() - } - - function byX(a, b) { - return b.x - a.x - } - - function dedup(edges) { - var j = edges.length, - a, b, i, m, n - - outer: while(j) { - b = edges[--j] - a = edges[--j] - i = j - while(i) { - n = edges[--i] - m = edges[--i] - if((a === m && b === n) || (a === n && b === m)) { - edges.splice(j, 2) - edges.splice(i, 2) - j -= 2 - continue outer - } - } - } - } - - function triangulate(vertices) { - /* Bail if there aren't enough vertices to form any triangles. */ - if(vertices.length < 3) - return [] - - /* Ensure the vertex array is in order of descending X coordinate - * (which is needed to ensure a subquadratic runtime), and then find - * the bounding box around the points. */ - vertices.sort(byX) - - var i = vertices.length - 1, - xmin = vertices[i].x, - xmax = vertices[0].x, - ymin = vertices[i].y, - ymax = ymin - - while(i--) { - if(vertices[i].y < ymin) ymin = vertices[i].y - if(vertices[i].y > ymax) ymax = vertices[i].y - } - - /* Find a supertriangle, which is a triangle that surrounds all the - * vertices. This is used like something of a sentinel value to remove - * cases in the main algorithm, and is removed before we return any - * results. - * - * Once found, put it in the "open" list. (The "open" list is for - * triangles who may still need to be considered; the "closed" list is - * for triangles which do not.) */ - var dx = xmax - xmin, - dy = ymax - ymin, - dmax = (dx > dy) ? dx : dy, - xmid = (xmax + xmin) * 0.5, - ymid = (ymax + ymin) * 0.5, - open = [ - new Triangle( - {x: xmid - 20 * dmax, y: ymid - dmax, __sentinel: true}, - {x: xmid , y: ymid + 20 * dmax, __sentinel: true}, - {x: xmid + 20 * dmax, y: ymid - dmax, __sentinel: true} - ) - ], - closed = [], - edges = [], - j, a, b - - /* Incrementally add each vertex to the mesh. */ - i = vertices.length - while(i--) { - /* For each open triangle, check to see if the current point is - * inside it's circumcircle. If it is, remove the triangle and add - * it's edges to an edge list. */ - edges.length = 0 - j = open.length - while(j--) { - /* If this point is to the right of this triangle's circumcircle, - * then this triangle should never get checked again. Remove it - * from the open list, add it to the closed list, and skip. */ - dx = vertices[i].x - open[j].x - if(dx > 0 && dx * dx > open[j].r) { - closed.push(open[j]) - open.splice(j, 1) - continue - } - - /* If not, skip this triangle. */ - dy = vertices[i].y - open[j].y - if(dx * dx + dy * dy > open[j].r) - continue - - /* Remove the triangle and add it's edges to the edge list. */ - edges.push( - open[j].a, open[j].b, - open[j].b, open[j].c, - open[j].c, open[j].a - ) - open.splice(j, 1) - } - - /* Remove any doubled edges. */ - dedup(edges) - - /* Add a new triangle for each edge. */ - j = edges.length - while(j) { - b = edges[--j] - a = edges[--j] - open.push(new Triangle(a, b, vertices[i])) - } - } - - /* Copy any remaining open triangles to the closed list, and then - * remove any triangles that share a vertex with the supertriangle. */ - Array.prototype.push.apply(closed, open) - - i = closed.length - while(i--) - if(closed[i].a.__sentinel || - closed[i].b.__sentinel || - closed[i].c.__sentinel) - closed.splice(i, 1) - - /* Yay, we're done! */ - return closed - } - - if (typeof module !== 'undefined') { - module.exports = { - Triangle: Triangle, - triangulate: triangulate - } - } - - return triangulate; - } -); \ No newline at end of file diff --git a/scripts/lib/superfast-blur.0.5.js b/scripts/lib/superfast-blur.0.5.js deleted file mode 100644 index b018eea..0000000 --- a/scripts/lib/superfast-blur.0.5.js +++ /dev/null @@ -1,338 +0,0 @@ -/*global define*/ -/* - -Superfast Blur - a fast Box Blur For Canvas - -Version: 0.5 -Author: Mario Klingemann -Contact: mario@quasimondo.com -Website: http://www.quasimondo.com/BoxBlurForCanvas -Twitter: @quasimondo - -In case you find this class useful - especially in commercial projects - -I am not totally unhappy for a small donation to my PayPal account -mario@quasimondo.de - -Or support me on flattr: -https://flattr.com/thing/140066/Superfast-Blur-a-pretty-fast-Box-Blur-Effect-for-CanvasJavascript - -Copyright (c) 2011 Mario Klingemann - -Note by Georg Fischer (snorpey@gmail.com / @snorpey): -While much of the original algorithm is still the same, -I modified some parts of the script to fit my needs: -- removed the iterations argument -- modified the functions to accept an imageData object - instead of element ids to remove dependency on the - document object. -- added AMD / requirejs wrapper - - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -*/ -define( - function() - { - var mul_table = [ - 1,57,41,21,203,34,97,73,227,91,149,62,105,45,39,137,241,107,3,173,39,71,65,238,219,101, - 187,87,81,151,141,133,249,117,221,209,197,187,177,169,5,153,73,139,133,127,243,233,223, - 107,103,99,191,23,177,171,165,159,77,149,9,139,135,131,253,245,119,231,224,109,211,103, - 25,195,189,23,45,175,171,83,81,79,155,151,147,9,141,137,67,131,129,251,123,30,235,115, - 113,221,217,53,13,51,50,49,193,189,185,91,179,175,43,169,83,163,5,79,155,19,75,147,145, - 143,35,69,17,67,33,65,255,251,247,243,239,59,29,229,113,111,219,27,213,105,207,51,201, - 199,49,193,191,47,93,183,181,179,11,87,43,85,167,165,163,161,159,157,155,77,19,75,37, - 73,145,143,141,35,138,137,135,67,33,131,129,255,63,250,247,61,121,239,237,117,29,229, - 227,225,111,55,109,216,213,211,209,207,205,203,201,199,197,195,193,48,190,47,93,185, - 183,181,179,178,176,175,173,171,85,21,167,165,41,163,161,5,79,157,78,154,153,19,75, - 149,74,147,73,144,143,71,141,140,139,137,17,135,134,133,66,131,65,129,1 - ]; - - var shg_table = [ - 0,9,10,10,14,12,14,14,16,15,16,15,16,15,15,17,18,17,12,18,16,17,17,19,19,18,19,18,18, - 19,19,19,20,19,20,20,20,20,20,20,15,20,19,20,20,20,21,21,21,20,20,20,21,18,21,21,21, - 21,20,21,17,21,21,21,22,22,21,22,22,21,22,21,19,22,22,19,20,22,22,21,21,21,22,22,22, - 18,22,22,21,22,22,23,22,20,23,22,22,23,23,21,19,21,21,21,23,23,23,22,23,23,21,23,22, - 23,18,22,23,20,22,23,23,23,21,22,20,22,21,22,24,24,24,24,24,22,21,24,23,23,24,21,24, - 23,24,22,24,24,22,24,24,22,23,24,24,24,20,23,22,23,24,24,24,24,24,24,24,23,21,23,22, - 23,24,24,24,22,24,24,24,23,22,24,24,25,23,25,25,23,24,25,25,24,22,25,25,25,24,23,24, - 25,25,25,25,25,25,25,25,25,25,25,25,23,25,23,24,25,25,25,25,25,25,25,25,25,24,22,25, - 25,23,25,25,20,24,25,24,25,25,22,24,25,24,25,24,25,25,24,25,25,25,25,22,25,25,25,24, - 25,24,25,18 - ]; - - function boxBlurCanvas( image_data, radius, blur_alpha_channel ) - { - var result = image_data; - - if ( ! ( isNaN( radius ) || radius < 1 ) ) - { - if ( blur_alpha_channel ) - { - result = boxBlurCanvasRGBA( image_data, radius ); - } - - else - { - result = boxBlurCanvasRGB( image_data, radius ); - } - } - - return result; - } - - function boxBlurCanvasRGBA( image_data, radius ) - { - radius |= 0; - - var pixels = image_data.data; - var width = image_data.width; - var height = image_data.height; - var rsum, gsum, bsum, asum, x, y, i, p, p1, p2, yp, yi, yw, idx, pa; - var wm = width - 1; - var hm = height - 1; - var wh = width * height; - var rad1 = radius + 1; - - var mul_sum = mul_table[radius]; - var shg_sum = shg_table[radius]; - - var r = [ ]; - var g = [ ]; - var b = [ ]; - var a = [ ]; - - var vmin = [ ]; - var vmax = [ ]; - - yw = yi = 0; - - for ( y = 0; y < height; y++ ) - { - rsum = pixels[yw] * rad1; - gsum = pixels[yw + 1] * rad1; - bsum = pixels[yw + 2] * rad1; - asum = pixels[yw + 3] * rad1; - - for ( i = 1; i <= radius; i++ ) - { - p = yw + ( ( ( i > wm ? wm : i ) ) << 2 ); - rsum += pixels[p++]; - gsum += pixels[p++]; - bsum += pixels[p++]; - asum += pixels[p]; - } - - for ( x = 0; x < width; x++ ) - { - r[yi] = rsum; - g[yi] = gsum; - b[yi] = bsum; - a[yi] = asum; - - if ( y === 0 ) - { - vmin[x] = ( ( p = x + rad1) < wm ? p : wm ) << 2; - vmax[x] = ( ( p = x - radius) > 0 ? p << 2 : 0 ); - } - - p1 = yw + vmin[x]; - p2 = yw + vmax[x]; - - rsum += pixels[p1++] - pixels[p2++]; - gsum += pixels[p1++] - pixels[p2++]; - bsum += pixels[p1++] - pixels[p2++]; - asum += pixels[p1] - pixels[p2]; - - yi++; - } - - yw += ( width << 2 ); - } - - for ( x = 0; x < width; x++ ) - { - yp = x; - rsum = r[yp] * rad1; - gsum = g[yp] * rad1; - bsum = b[yp] * rad1; - asum = a[yp] * rad1; - - for ( i = 1; i <= radius; i++ ) - { - yp += ( i > hm ? 0 : width ); - rsum += r[yp]; - gsum += g[yp]; - bsum += b[yp]; - asum += a[yp]; - } - - yi = x << 2; - - for ( y = 0; y < height; y++ ) - { - pixels[yi + 3] = pa = ( asum * mul_sum ) >>> shg_sum; - - if ( pa > 0 ) - { - pa = 255 / pa; - pixels[yi] = ( ( rsum * mul_sum ) >>> shg_sum ) * pa; - pixels[yi+1] = ( ( gsum * mul_sum ) >>> shg_sum ) * pa; - pixels[yi+2] = ( ( bsum * mul_sum ) >>> shg_sum ) * pa; - } - - else - { - pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; - } - - if ( x === 0 ) - { - vmin[y] = ( ( p = y + rad1) < hm ? p : hm ) * width; - vmax[y] = ( ( p = y - radius) > 0 ? p * width : 0 ); - } - - p1 = x + vmin[y]; - p2 = x + vmax[y]; - - rsum += r[p1] - r[p2]; - gsum += g[p1] - g[p2]; - bsum += b[p1] - b[p2]; - asum += a[p1] - a[p2]; - - yi += width << 2; - } - } - - return image_data; - } - - function boxBlurCanvasRGB( image_data, radius ) - { - radius |= 0; - - var pixels = image_data.data; - var width = image_data.width; - var height = image_data.height; - var rsum, gsum, bsum, asum, x, y, i, p, p1, p2, yp, yi, yw, idx; - var wm = width - 1; - var hm = height - 1; - var wh = width * height; - var rad1 = radius + 1; - - var r = [ ]; - var g = [ ]; - var b = [ ]; - - var mul_sum = mul_table[radius]; - var shg_sum = shg_table[radius]; - - var vmin = [ ]; - var vmax = [ ]; - - yw = yi = 0; - - for ( y = 0; y < height; y++ ) - { - rsum = pixels[yw] * rad1; - gsum = pixels[yw + 1] * rad1; - bsum = pixels[yw + 2] * rad1; - - for ( i = 1; i <= radius; i++ ) - { - p = yw + ( ( ( i > wm ? wm : i ) ) << 2 ); - rsum += pixels[p++]; - gsum += pixels[p++]; - bsum += pixels[p++]; - } - - for ( x = 0; x < width; x++ ) - { - r[yi] = rsum; - g[yi] = gsum; - b[yi] = bsum; - - if ( y === 0 ) - { - vmin[x] = ( ( p = x + rad1) < wm ? p : wm ) << 2; - vmax[x] = ( ( p = x - radius) > 0 ? p << 2 : 0 ); - } - - p1 = yw + vmin[x]; - p2 = yw + vmax[x]; - - rsum += pixels[p1++] - pixels[p2++]; - gsum += pixels[p1++] - pixels[p2++]; - bsum += pixels[p1++] - pixels[p2++]; - - yi++; - } - - yw += ( width << 2 ); - } - - for ( x = 0; x < width; x++ ) - { - yp = x; - rsum = r[yp] * rad1; - gsum = g[yp] * rad1; - bsum = b[yp] * rad1; - - for ( i = 1; i <= radius; i++ ) - { - yp += ( i > hm ? 0 : width ); - rsum += r[yp]; - gsum += g[yp]; - bsum += b[yp]; - } - - yi = x << 2; - - for ( y = 0; y < height; y++ ) - { - pixels[yi] = (rsum * mul_sum) >>> shg_sum; - pixels[yi+1] = (gsum * mul_sum) >>> shg_sum; - pixels[yi+2] = (bsum * mul_sum) >>> shg_sum; - - if ( x === 0 ) - { - vmin[y] = ( ( p = y + rad1) < hm ? p : hm ) * width; - vmax[y] = ( ( p = y - radius) > 0 ? p * width : 0 ); - } - - p1 = x + vmin[y]; - p2 = x + vmax[y]; - - rsum += r[p1] - r[p2]; - gsum += g[p1] - g[p2]; - bsum += b[p1] - b[p2]; - - yi += width << 2; - } - } - - return image_data; - } - - return boxBlurCanvas; - } -); \ No newline at end of file diff --git a/scripts/lib/triangulate-image.js b/scripts/lib/triangulate-image.js new file mode 100644 index 0000000..97a4d5b --- /dev/null +++ b/scripts/lib/triangulate-image.js @@ -0,0 +1,951 @@ +(function(f) { + if (typeof exports === 'object' && typeof module !== 'undefined') { + module.exports = f(); + } else { + if (typeof define === 'function' && define.amd) { + define([], f); + } else { + var g; + if (typeof window !== 'undefined') { + g = window; + } else { + if (typeof global !== 'undefined') { + g = global; + } else { + if (typeof self !== 'undefined') { + g = self; + } else { + g = this; + } + } + } + g.triangulate = f(); + } + } +})(function() { + var define, module, exports; + return function e(t, n, r) { + function s(o, u) { + if (!n[o]) { + if (!t[o]) { + var a = typeof require == 'function' && require; + if (!u && a) { + return a(o, !0); + } + if (i) { + return i(o, !0); + } + var f = new Error('Cannot find module \'' + o + '\''); + throw f.code = 'MODULE_NOT_FOUND', f; + } + var l = n[o] = { + exports: {} + }; + t[o][0].call(l.exports, function(e) { + var n = t[o][1][e]; + return s(n ? n : e); + }, l, l.exports, e, t, n, r); + } + return n[o].exports; + } + var i = typeof require == 'function' && require; + for (var o = 0; o < r.length; o++) { + s(r[o]); + } + return s; + }({ + 1: [ function(_dereq_, module, exports) { + 'use strict'; + module.exports = { + accuracy: .7, + blur: 40, + fill: !0, + stroke: !0, + strokeWidth: .5, + lineJoin: 'miter', + vertexCount: 700 + }; + }, {} ], + 2: [ function(_dereq_, module, exports) { + 'use strict'; + function checkParams(params) { + return 'object' != typeof params && (params = {}), 'number' != typeof params.accuracy || isNaN(params.accuracy) ? params.accuracy = defaultParams.accuracy : params.accuracy = clamp(params.accuracy, 0, 1), + ('number' != typeof params.blur || isNaN(params.blur)) && (params.blur = defaultParams.blur), + 'string' != typeof params.fill && 'boolean' != typeof params.fill && (params.fill = defaultParams.fill), + 'string' != typeof params.stroke && 'boolean' != typeof params.stroke && (params.stroke = defaultParams.stroke), + ('number' != typeof params.strokeWidth || isNaN(params.strokeWidth)) && (params.strokeWidth = defaultParams.strokeWidth), + ('string' != typeof params.lineJoin || -1 === allowedLineJoins.indexOf(params.lineJoin)) && (params.lineJoin = defaultParams.lineJoin), + ('number' != typeof params.vertexCount || isNaN(params.vertexCount)) && (params.vertexCount = defaultParams.vertexCount), + params; + } + var clamp = _dereq_('./util/clamp.js'), defaultParams = _dereq_('./defaultParams.js'), fromImageToImageData = _dereq_('./input/fromImageToImageData.js'), polygonsToImageData = _dereq_('./output/polygonsToImageData.js'), polygonsToDataURL = _dereq_('./output/polygonsToDataURL.js'), polygonsToSVG = _dereq_('./output/polygonsToSVG.js'), fromBufferToImageData, polygonsToBuffer, polygonsToPNGStream, polygonsToJPGStream, polygonsToSVGStream, allowedLineJoins = [ 'miter', 'round', 'bevel' ]; + module.exports = function(params, callback) { + function getInput() { + return input; + } + function getOutput() { + return output; + } + function canStart() { + return inputFn && outputFn && params; + } + function getParams() { + return params; + } + function go(fn) { + return canStart() ? start() : fn(); + } + function start() { + var imageData = inputFn(), polygons = callback(imageData, params); + return outputFn(polygons, imageData); + } + params = checkParams(params); + var inputFn, outputFn, input = { + getParams: getParams + }, output = { + getParams: getParams + }; + return input.fromImageData = function(imageData) { + return inputFn = function() { + return imageData; + }, go(getOutput); + }, output.toDataURL = function(dataUrlParams) { + return outputFn = function(polygons, size) { + return polygonsToDataURL(polygons, size, dataUrlParams); + }, go(getInput); + }, output.toImageData = function(imageDataParams) { + return outputFn = function(polygons, size) { + return polygonsToImageData(polygons, size, imageDataParams); + }, go(getInput); + }, output.toSVG = function() { + return outputFn = polygonsToSVG, go(getInput); + }, output.toData = function() { + return outputFn = function(polygons) { + return polygons; + }, go(getInput); + }, input.fromImage = function(imageEl) { + return inputFn = function() { + return fromImageToImageData(imageEl); + }, go(getOutput); + }, getInput(); + }; + }, { + './defaultParams.js': 1, + './input/fromImageToImageData.js': 6, + './output/polygonsToDataURL.js': 7, + './output/polygonsToImageData.js': 8, + './output/polygonsToSVG.js': 9, + './util/clamp.js': 14 + } ], + 3: [ function(_dereq_, module, exports) { + 'use strict'; + var Canvas = _dereq_('canvas-browserify'); + module.exports = function(imageData) { + if ('undefined' == typeof Uint8ClampedArray) { + var canvas = Canvas(imageData.width, imageData.height), ctx = canvas.getContext('2d'); + return ctx.putImageData(imageData, 0, 0), ctx.getImageData(0, 0, imageData.width, imageData.height); + } + return { + width: imageData.width, + height: imageData.height, + data: new Uint8ClampedArray(imageData.data) + }; + }; + }, { + 'canvas-browserify': 18 + } ], + 4: [ function(_dereq_, module, exports) { + 'use strict'; + function detectEdges(imageData, accuracy, edgeSize, divisor) { + var matrix = getEdgeMatrix(edgeSize).slice(), multiplier = parseInt(10 * (accuracy || .5), 10) || 1; + divisor = divisor || 1; + var k, len, divscalar = divisor ? 1 / divisor : 0; + if (1 !== divscalar) { + for (k = 0, len = matrix.length; k < matrix.length; k++) { + matrix[k] *= divscalar; + } + } + var data = imageData.data; + len = data.length >> 2; + var copy = new Uint8Array(len); + for (i = 0; len > i; i++) { + copy[i] = data[i << 2]; + } + var x, y, r, g, b, v, col, row, sx, sy, i, istep, jstep, kstep, width = 0 | imageData.width, height = 0 | imageData.height, size = Math.sqrt(matrix.length), range = .5 * size | 0; + for (y = 0; height > y; y += multiplier) { + for (istep = y * width, x = 0; width > x; x += multiplier) { + for (r = g = b = 0, row = -range; range >= row; row++) { + if (sy = y + row, jstep = sy * width, kstep = (row + range) * size, sy >= 0 && height > sy) { + for (col = -range; range >= col; col++) { + sx = x + col, sx >= 0 && width > sx && (v = matrix[col + range + kstep]) && (r += copy[sx + jstep] * v); + } + } + } + 0 > r ? r = 0 : r > 255 && (r = 255), data[x + istep << 2] = 255 & r; + } + } + return imageData; + } + function getEdgeMatrix(size) { + var i, matrix = [], side = 2 * size + 1, len = side * side, center = .5 * len | 0; + for (i = 0; len > i; i++) { + matrix[i] = i === center ? -len + 1 : 1; + } + return matrix; + } + module.exports = detectEdges; + }, {} ], + 5: [ function(_dereq_, module, exports) { + 'use strict'; + function greyscale(imageData) { + for (var len = imageData.data.length, data = imageData.data, brightness = void 0, i = 0; len > i; i += 4) { + brightness = .34 * data[i] + .5 * data[i + 1] + .16 * data[i + 2], data[i] = brightness, + data[i + 1] = brightness, data[i + 2] = brightness; + } + return imageData.data = data, imageData; + } + module.exports = greyscale; + }, {} ], + 6: [ function(_dereq_, module, exports) { + 'use strict'; + var Canvas = _dereq_('canvas-browserify'), Image = Canvas.Image; + module.exports = function(image) { + if (image instanceof HTMLImageElement) { + if (0 === image.naturalWidth || 0 === image.naturalHeight || image.complete === !1) { + throw new Error('This this image hasn\'t finished loading: ' + image.src); + } + var canvas = new Canvas(image.naturalWidth, image.naturalHeight), ctx = canvas.getContext('2d'); + return ctx.drawImage(image, 0, 0, canvas.width, canvas.height), ctx.getImageData(0, 0, canvas.width, canvas.height); + } + throw new Error('This object does not seem to be an image.'); + }; + }, { + 'canvas-browserify': 18 + } ], + 7: [ function(_dereq_, module, exports) { + 'use strict'; + var Canvas = _dereq_('canvas-browserify'), drawPolygonsOnContext = _dereq_('../util/drawPolygonsOnContext.js'); + module.exports = function(polygons, size, options) { + var dpr = options && options.dpr ? options.dpr : 1, backgroundColor = options && options.backgroundColor ? options.backgroundColor : !1, canvas = new Canvas(size.width * dpr, size.height * dpr), ctx = canvas.getContext('2d'); + return backgroundColor && (ctx.fillStyle = backgroundColor, ctx.fillRect(0, 0, size.width * dpr, size.height * dpr), + ctx.fillStyle = 'transparent'), drawPolygonsOnContext(ctx, polygons, size, dpr), + canvas.toDataURL(); + }; + }, { + '../util/drawPolygonsOnContext.js': 15, + 'canvas-browserify': 18 + } ], + 8: [ function(_dereq_, module, exports) { + 'use strict'; + var Canvas = _dereq_('canvas-browserify'), drawPolygonsOnContext = _dereq_('../util/drawPolygonsOnContext.js'); + module.exports = function(polygons, size, options) { + var dpr = options && options.dpr ? options.dpr : 1, backgroundColor = options && options.backgroundColor ? options.backgroundColor : !1, canvas = new Canvas(size.width * dpr, size.height * dpr), ctx = canvas.getContext('2d'); + return backgroundColor && (ctx.fillStyle = backgroundColor, ctx.fillRect(0, 0, size.width * dpr, size.height * dpr), + ctx.fillStyle = 'transparent'), drawPolygonsOnContext(ctx, polygons, size, dpr), + ctx.getImageData(0, 0, size.width * dpr, size.height * dpr); + }; + }, { + '../util/drawPolygonsOnContext.js': 15, + 'canvas-browserify': 18 + } ], + 9: [ function(_dereq_, module, exports) { + 'use strict'; + module.exports = function(polygons, size) { + var svg = '\n\n '; + return polygons.forEach(function(polygon, index) { + var a = polygon.a, b = polygon.b, c = polygon.c; + svg += ' y; y += multiplier) { + for (x = 0; width > x; x += multiplier) { + for (sum = total = 0, row = -1; 1 >= row; row++) { + if (sy = y + row, step = sy * width, sy >= 0 && height > sy) { + for (col = -1; 1 >= col; col++) { + sx = x + col, sx >= 0 && width > sx && (sum += data[sx + step << 2], total++); + } + } + } + total && (sum /= total), sum > edgeDetectValue && points.push({ + x: x, + y: y + }); + } + } + return points; + } + module.exports = getEdgePoints; + }, {} ], + 12: [ function(_dereq_, module, exports) { + 'use strict'; + function sortByArea(a, b) { + return a.x * a.y - b.y * b.x; + } + function getVerticesFromPoints(points, maxPointCount, accuracy, width, height) { + var result = [], sidePointCount = 2 * Math.ceil(width / (100 - accuracy)) + 2 * Math.ceil(height / (100 - accuracy)) + 2, pointCount = Math.max(points.length, maxPointCount), randomPointCount = clamp(pointCount - sidePointCount, 0, maxPointCount - sidePointCount), increment = pointCount / randomPointCount, i = 0, x = 0, y = 0, len = 0; + for (points.sort(sortByArea), i = 0, len = pointCount; len > i; i += increment) { + result.push({ + x: points[~~i].x, + y: points[~~i].y + }); + } + for (x = 0; width > x; x += 100 - accuracy) { + result.push({ + x: ~~x, + y: 0 + }), result.push({ + x: ~~x, + y: height + }); + } + for (y = 0; height > y; y += 100 - accuracy) { + result.push({ + x: 0, + y: ~~y + }), result.push({ + x: width, + y: ~~y + }); + } + return result.push({ + x: 0, + y: height + }), result.push({ + x: width, + y: height + }), points = null, result; + } + var clamp = _dereq_('../util/clamp.js'); + module.exports = getVerticesFromPoints; + }, { + '../util/clamp.js': 14 + } ], + 13: [ function(_dereq_, module, exports) { + 'use strict'; + var stackBlur = _dereq_('stackblur-canvas'), delaunay = _dereq_('delaunay-fast'), isImageData = _dereq_('../util/isImageData.js'), copyImageData = _dereq_('../imagedata/copyImageData.js'), greyscale = _dereq_('../imagedata/greyscale'), detectEdges = _dereq_('../imagedata/detectEdges'), getEdgePoints = _dereq_('./getEdgePoints.js'), getVerticesFromPoints = _dereq_('./getVerticesFromPoints.js'), addColorToPolygons = _dereq_('./addColorToPolygons.js'); + module.exports = function(imageData, params) { + if (isImageData(imageData)) { + var imageSize = { + width: imageData.width, + height: imageData.height + }, tmpImageData = copyImageData(imageData), colorImageData = copyImageData(imageData), blurredImageData = stackBlur.imageDataRGBA(tmpImageData, 0, 0, imageSize.width, imageSize.height, params.blur), greyscaleImageData = greyscale(blurredImageData), edgesImageData = detectEdges(greyscaleImageData), edgePoints = getEdgePoints(edgesImageData, 50, params.accuracy), edgeVertices = getVerticesFromPoints(edgePoints, params.vertexCount, params.accuracy, imageSize.width, imageSize.height), polygons = delaunay.triangulate(edgeVertices); + return addColorToPolygons(polygons, colorImageData, params); + } + throw new Error('Can\'t work with the imageData provided. It seems to be corrupt'); + }; + }, { + '../imagedata/copyImageData.js': 3, + '../imagedata/detectEdges': 4, + '../imagedata/greyscale': 5, + '../util/isImageData.js': 16, + './addColorToPolygons.js': 10, + './getEdgePoints.js': 11, + './getVerticesFromPoints.js': 12, + 'delaunay-fast': 19, + 'stackblur-canvas': 20 + } ], + 14: [ function(_dereq_, module, exports) { + 'use strict'; + function clamp(value, min, max) { + return min > value ? min : value > max ? max : value; + } + module.exports = clamp; + }, {} ], + 15: [ function(_dereq_, module, exports) { + 'use strict'; + module.exports = function(ctx, polygons, size, dpr) { + return dpr = dpr || 1, polygons.forEach(function(polygon, index) { + ctx.beginPath(), ctx.moveTo(polygon.a.x * dpr, polygon.a.y * dpr), ctx.lineTo(polygon.b.x * dpr, polygon.b.y * dpr), + ctx.lineTo(polygon.c.x * dpr, polygon.c.y * dpr), ctx.lineTo(polygon.a.x * dpr, polygon.a.y * dpr), + polygon.fill && (ctx.fillStyle = polygon.fill, ctx.fill()), polygon.strokeColor && (ctx.strokeStyle = polygon.strokeColor, + ctx.lineWidth = polygon.strokeWidth * dpr, ctx.lineJoin = polygon.lineJoin, ctx.stroke()), + ctx.closePath(); + }), ctx; + }; + }, {} ], + 16: [ function(_dereq_, module, exports) { + 'use strict'; + module.exports = function(imageData) { + return imageData && 'number' == typeof imageData.width && 'number' == typeof imageData.height && imageData.data && 'number' == typeof imageData.data.length; + }; + }, {} ], + 17: [ function(_dereq_, module, exports) { + 'use strict'; + function triangulate(params) { + return getInterfaceObj(params, imageDataToPolygons); + } + Object.defineProperty(exports, '__esModule', { + value: !0 + }), exports['default'] = triangulate; + var getInterfaceObj = _dereq_('./getInterfaceObj'), imageDataToPolygons = _dereq_('./polygons/imageDataToPolygons.js'); + module.exports = exports['default']; + }, { + './getInterfaceObj': 2, + './polygons/imageDataToPolygons.js': 13 + } ], + 18: [ function(_dereq_, module, exports) { + var Canvas = module.exports = function Canvas(w, h) { + var canvas = document.createElement('canvas'); + canvas.width = w || 300; + canvas.height = h || 150; + return canvas; + }; + Canvas.Image = function() { + var img = document.createElement('img'); + return img; + }; + }, {} ], + 19: [ function(_dereq_, module, exports) { + function Triangle(a, b, c) { + this.a = a; + this.b = b; + this.c = c; + var A = b.x - a.x, B = b.y - a.y, C = c.x - a.x, D = c.y - a.y, E = A * (a.x + b.x) + B * (a.y + b.y), F = C * (a.x + c.x) + D * (a.y + c.y), G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)), minx, miny, dx, dy; + if (Math.abs(G) < 1e-6) { + minx = Math.min(a.x, b.x, c.x); + miny = Math.min(a.y, b.y, c.y); + dx = (Math.max(a.x, b.x, c.x) - minx) * .5; + dy = (Math.max(a.y, b.y, c.y) - miny) * .5; + this.x = minx + dx; + this.y = miny + dy; + this.r = dx * dx + dy * dy; + } else { + this.x = (D * E - B * F) / G; + this.y = (A * F - C * E) / G; + dx = this.x - a.x; + dy = this.y - a.y; + this.r = dx * dx + dy * dy; + } + } + Triangle.prototype.draw = function(ctx) { + ctx.beginPath(); + ctx.moveTo(this.a.x, this.a.y); + ctx.lineTo(this.b.x, this.b.y); + ctx.lineTo(this.c.x, this.c.y); + ctx.closePath(); + ctx.stroke(); + }; + function byX(a, b) { + return b.x - a.x; + } + function dedup(edges) { + var j = edges.length, a, b, i, m, n; + outer: while (j) { + b = edges[--j]; + a = edges[--j]; + i = j; + while (i) { + n = edges[--i]; + m = edges[--i]; + if (a === m && b === n || a === n && b === m) { + edges.splice(j, 2); + edges.splice(i, 2); + j -= 2; + continue outer; + } + } + } + } + function triangulate(vertices) { + if (vertices.length < 3) { + return []; + } + vertices.sort(byX); + var i = vertices.length - 1, xmin = vertices[i].x, xmax = vertices[0].x, ymin = vertices[i].y, ymax = ymin; + while (i--) { + if (vertices[i].y < ymin) { + ymin = vertices[i].y; + } + if (vertices[i].y > ymax) { + ymax = vertices[i].y; + } + } + var dx = xmax - xmin, dy = ymax - ymin, dmax = dx > dy ? dx : dy, xmid = (xmax + xmin) * .5, ymid = (ymax + ymin) * .5, open = [ new Triangle({ + x: xmid - 20 * dmax, + y: ymid - dmax, + __sentinel: true + }, { + x: xmid, + y: ymid + 20 * dmax, + __sentinel: true + }, { + x: xmid + 20 * dmax, + y: ymid - dmax, + __sentinel: true + }) ], closed = [], edges = [], j, a, b; + i = vertices.length; + while (i--) { + edges.length = 0; + j = open.length; + while (j--) { + dx = vertices[i].x - open[j].x; + if (dx > 0 && dx * dx > open[j].r) { + closed.push(open[j]); + open.splice(j, 1); + continue; + } + dy = vertices[i].y - open[j].y; + if (dx * dx + dy * dy > open[j].r) { + continue; + } + edges.push(open[j].a, open[j].b, open[j].b, open[j].c, open[j].c, open[j].a); + open.splice(j, 1); + } + dedup(edges); + j = edges.length; + while (j) { + b = edges[--j]; + a = edges[--j]; + open.push(new Triangle(a, b, vertices[i])); + } + } + Array.prototype.push.apply(closed, open); + i = closed.length; + while (i--) { + if (closed[i].a.__sentinel || closed[i].b.__sentinel || closed[i].c.__sentinel) { + closed.splice(i, 1); + } + } + return closed; + } + if (typeof module !== 'undefined') { + module.exports = { + Triangle: Triangle, + triangulate: triangulate + }; + } + }, {} ], + 20: [ function(_dereq_, module, exports) { + var mul_table = [ 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 ]; + var shg_table = [ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]; + function processImage(img, canvas, radius, blurAlphaChannel) { + if (typeof img == 'string') { + var img = document.getElementById(img); + } else { + if (!img instanceof HTMLImageElement) { + return; + } + } + var w = img.naturalWidth; + var h = img.naturalHeight; + if (typeof canvas == 'string') { + var canvas = document.getElementById(canvas); + } else { + if (!canvas instanceof HTMLCanvasElement) { + return; + } + } + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + canvas.width = w; + canvas.height = h; + var context = canvas.getContext('2d'); + context.clearRect(0, 0, w, h); + context.drawImage(img, 0, 0); + if (isNaN(radius) || radius < 1) { + return; + } + if (blurAlphaChannel) { + processCanvasRGBA(canvas, 0, 0, w, h, radius); + } else { + processCanvasRGB(canvas, 0, 0, w, h, radius); + } + } + function getImageDataFromCanvas(canvas, top_x, top_y, width, height) { + if (typeof canvas == 'string') { + var canvas = document.getElementById(canvas); + } else { + if (!canvas instanceof HTMLCanvasElement) { + return; + } + } + var context = canvas.getContext('2d'); + var imageData; + try { + try { + imageData = context.getImageData(top_x, top_y, width, height); + } catch (e) { + try { + netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead'); + imageData = context.getImageData(top_x, top_y, width, height); + } catch (e) { + alert('Cannot access local image'); + throw new Error('unable to access local image data: ' + e); + return; + } + } + } catch (e) { + alert('Cannot access image'); + throw new Error('unable to access image data: ' + e); + } + return imageData; + } + function processCanvasRGBA(canvas, top_x, top_y, width, height, radius) { + if (isNaN(radius) || radius < 1) { + return; + } + radius |= 0; + var imageData = getImageDataFromCanvas(canvas, top_x, top_y, width, height); + imageData = processImageDataRGBA(imageData, top_x, top_y, width, height, radius); + canvas.getContext('2d').putImageData(imageData, top_x, top_y); + } + function processImageDataRGBA(imageData, top_x, top_y, width, height, radius) { + var pixels = imageData.data; + var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, r_out_sum, g_out_sum, b_out_sum, a_out_sum, r_in_sum, g_in_sum, b_in_sum, a_in_sum, pr, pg, pb, pa, rbs; + var div = radius + radius + 1; + var w4 = width << 2; + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; + var stackStart = new BlurStack(); + var stack = stackStart; + for (i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + if (i == radiusPlus1) { + var stackEnd = stack; + } + } + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + yw = yi = 0; + var mul_sum = mul_table[radius]; + var shg_sum = shg_table[radius]; + for (y = 0; y < height; y++) { + r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); + a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]); + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + stack = stackStart; + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + for (i = 1; i < radiusPlus1; i++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = pg = pixels[p + 1]) * rbs; + b_sum += (stack.b = pb = pixels[p + 2]) * rbs; + a_sum += (stack.a = pa = pixels[p + 3]) * rbs; + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + stack = stack.next; + } + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) { + pixels[yi + 3] = pa = a_sum * mul_sum >> shg_sum; + if (pa != 0) { + pa = 255 / pa; + pixels[yi] = (r_sum * mul_sum >> shg_sum) * pa; + pixels[yi + 1] = (g_sum * mul_sum >> shg_sum) * pa; + pixels[yi + 2] = (b_sum * mul_sum >> shg_sum) * pa; + } else { + pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; + } + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; + r_in_sum += stackIn.r = pixels[p]; + g_in_sum += stackIn.g = pixels[p + 1]; + b_in_sum += stackIn.b = pixels[p + 2]; + a_in_sum += stackIn.a = pixels[p + 3]; + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + stackIn = stackIn.next; + r_out_sum += pr = stackOut.r; + g_out_sum += pg = stackOut.g; + b_out_sum += pb = stackOut.b; + a_out_sum += pa = stackOut.a; + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + stackOut = stackOut.next; + yi += 4; + } + yw += width; + } + for (x = 0; x < width; x++) { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + yi = x << 2; + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); + a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]); + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + stack = stackStart; + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + yp = width; + for (i = 1; i <= radius; i++) { + yi = yp + x << 2; + r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = pg = pixels[yi + 1]) * rbs; + b_sum += (stack.b = pb = pixels[yi + 2]) * rbs; + a_sum += (stack.a = pa = pixels[yi + 3]) * rbs; + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + stack = stack.next; + if (i < heightMinus1) { + yp += width; + } + } + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) { + p = yi << 2; + pixels[p + 3] = pa = a_sum * mul_sum >> shg_sum; + if (pa > 0) { + pa = 255 / pa; + pixels[p] = (r_sum * mul_sum >> shg_sum) * pa; + pixels[p + 1] = (g_sum * mul_sum >> shg_sum) * pa; + pixels[p + 2] = (b_sum * mul_sum >> shg_sum) * pa; + } else { + pixels[p] = pixels[p + 1] = pixels[p + 2] = 0; + } + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; + r_sum += r_in_sum += stackIn.r = pixels[p]; + g_sum += g_in_sum += stackIn.g = pixels[p + 1]; + b_sum += b_in_sum += stackIn.b = pixels[p + 2]; + a_sum += a_in_sum += stackIn.a = pixels[p + 3]; + stackIn = stackIn.next; + r_out_sum += pr = stackOut.r; + g_out_sum += pg = stackOut.g; + b_out_sum += pb = stackOut.b; + a_out_sum += pa = stackOut.a; + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + stackOut = stackOut.next; + yi += width; + } + } + return imageData; + } + function processCanvasRGB(canvas, top_x, top_y, width, height, radius) { + if (isNaN(radius) || radius < 1) { + return; + } + radius |= 0; + var imageData = getImageDataFromCanvas(canvas, top_x, top_y, width, height); + imageData = processImageDataRGB(imageData, top_x, top_y, width, height, radius); + canvas.getContext('2d').putImageData(imageData, top_x, top_y); + } + function processImageDataRGB(imageData, top_x, top_y, width, height, radius) { + var pixels = imageData.data; + var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, r_out_sum, g_out_sum, b_out_sum, r_in_sum, g_in_sum, b_in_sum, pr, pg, pb, rbs; + var div = radius + radius + 1; + var w4 = width << 2; + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; + var stackStart = new BlurStack(); + var stack = stackStart; + for (i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + if (i == radiusPlus1) { + var stackEnd = stack; + } + } + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + yw = yi = 0; + var mul_sum = mul_table[radius]; + var shg_sum = shg_table[radius]; + for (y = 0; y < height; y++) { + r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + stack = stackStart; + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack = stack.next; + } + for (i = 1; i < radiusPlus1; i++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = pg = pixels[p + 1]) * rbs; + b_sum += (stack.b = pb = pixels[p + 2]) * rbs; + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + stack = stack.next; + } + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) { + pixels[yi] = r_sum * mul_sum >> shg_sum; + pixels[yi + 1] = g_sum * mul_sum >> shg_sum; + pixels[yi + 2] = b_sum * mul_sum >> shg_sum; + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; + r_in_sum += stackIn.r = pixels[p]; + g_in_sum += stackIn.g = pixels[p + 1]; + b_in_sum += stackIn.b = pixels[p + 2]; + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + stackIn = stackIn.next; + r_out_sum += pr = stackOut.r; + g_out_sum += pg = stackOut.g; + b_out_sum += pb = stackOut.b; + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + stackOut = stackOut.next; + yi += 4; + } + yw += width; + } + for (x = 0; x < width; x++) { + g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; + yi = x << 2; + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + stack = stackStart; + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack = stack.next; + } + yp = width; + for (i = 1; i <= radius; i++) { + yi = yp + x << 2; + r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = pg = pixels[yi + 1]) * rbs; + b_sum += (stack.b = pb = pixels[yi + 2]) * rbs; + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + stack = stack.next; + if (i < heightMinus1) { + yp += width; + } + } + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) { + p = yi << 2; + pixels[p] = r_sum * mul_sum >> shg_sum; + pixels[p + 1] = g_sum * mul_sum >> shg_sum; + pixels[p + 2] = b_sum * mul_sum >> shg_sum; + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; + r_sum += r_in_sum += stackIn.r = pixels[p]; + g_sum += g_in_sum += stackIn.g = pixels[p + 1]; + b_sum += b_in_sum += stackIn.b = pixels[p + 2]; + stackIn = stackIn.next; + r_out_sum += pr = stackOut.r; + g_out_sum += pg = stackOut.g; + b_out_sum += pb = stackOut.b; + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + stackOut = stackOut.next; + yi += width; + } + } + return imageData; + } + function BlurStack() { + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.next = null; + } + module.exports = { + image: processImage, + canvasRGBA: processCanvasRGBA, + canvasRGB: processCanvasRGB, + imageDataRGBA: processImageDataRGBA, + imageDataRGB: processImageDataRGB + }; + }, {} ] + }, {}, [ 17 ])(17); +}); diff --git a/scripts/src/controls.js b/scripts/src/controls.js index b49c629..c5807ce 100644 --- a/scripts/src/controls.js +++ b/scripts/src/controls.js @@ -27,8 +27,8 @@ define( control.addEventListener( 'change', controlUpdated, false ); - updateValue( getInputKey( control.id ), control.value ); - updateInput( getCorrespondingInput( control.id ), control.value ); + updateValue( getInputKey( control.id ), getInputValue( control ) ); + updateInput( getCorrespondingInput( control.id ), getInputValue( control ) ); } is_initialized = true; @@ -50,8 +50,8 @@ define( element = element.target; } - updateValue( getInputKey( element.id ), element.value ); - updateInput( getCorrespondingInput( element.id ), element.value ); + updateValue( getInputKey( element.id ), getInputValue( element ) ); + updateInput( getCorrespondingInput( element.id ), getInputValue( element ) ); }, 100 ); @@ -76,19 +76,24 @@ define( function updateValue( key, value ) { - values[key] = value; - - if ( is_initialized ) - { - signals['control-updated'].dispatch( values ); + if ( typeof value !== 'undefined' ) { + values[key] = value; + + if ( is_initialized ) + { + signals['control-updated'].dispatch( values ); + } } } function updateInput( input, value ) { - if ( input.value !== value ) + if ( input && getInputValue( input ) !== value && typeof value !== 'undefined' ) { - input.value = value; + if ( input.type === 'checkbox' ) { + } else { + input.value = value; + } } } @@ -101,14 +106,25 @@ define( for ( var i = 0, len = controls.length; i < len; i++ ) { element_id = controls[i].id; - - if ( - element_id !== id && - element_id.indexOf( key ) !== -1 - ) + + if ( element_id.indexOf( key ) !== -1 ) { - result = controls[i]; - break; + if ( controls[i].type === 'checkbox' ) + { + if ( element_id === id ) + { + result = controls[i]; + break; + } + } + + else { + if ( element_id !== id ) + { + result = controls[i]; + break; + } + } } } @@ -117,7 +133,16 @@ define( function getInputKey( id ) { - return id.replace( '-slider', '' ).replace( '-number', '' ); + return id.replace( '-slider', '' ).replace( '-number', '' ).replace( '-input', '' ); + } + + function getInputValue( input ) + { + if ( input.type === 'checkbox' ) { + return input.checked; + } elseĀ { + return input.value; + } } return { init: init }; diff --git a/scripts/src/export-button.js b/scripts/src/export-button.js index 5890d13..d22aa97 100644 --- a/scripts/src/export-button.js +++ b/scripts/src/export-button.js @@ -31,8 +31,7 @@ define( png_link.href = data.png; png_link.classList.add( 'is-active' ); - var svg_string = getSVG( data.svg.triangles, data.svg.size ); - var blob = new Blob( [ svg_string ], { type: 'image/svg+xml' } ); + var blob = new Blob( [ data.svg ], { type: 'image/svg+xml' } ); var svg_url = window.URL.createObjectURL( blob ); svg_link.href = svg_url; @@ -49,41 +48,6 @@ define( svg_link.classList.remove( 'is-active' ); } - // http://stackoverflow.com/questions/6918597/convert-canvas-or-control-points-to-svg - // https://developer.mozilla.org/en-US/docs/SVG/Element/polygon - function getSVG( triangles, size ) - { - var triangle_keys = [ 'a', 'b', 'c' ]; - var svg = ''; - - svg += ''; - svg += ' 1 ) ? window.devicePixelRatio : 1; function init( shared ) @@ -76,62 +56,18 @@ define( function processImage( img ) { is_processing = true; - - clearCanvas( tmp_canvas, tmp_ctx ); clearCanvas( canvas, ctx ); - - resizeCanvas( tmp_canvas, img, pxratio ); resizeCanvas( canvas, img, pxratio ); - - tmp_ctx.drawImage( img, 0, 0 ); - - // get the image data - image_data = tmp_ctx.getImageData( 0, 0, tmp_canvas.width, tmp_canvas.height ); - - // since the image data is blurred and greyscaled later on, - // we need another copy of the image data with preserved colors - color_data = tmp_ctx.getImageData( 0, 0, tmp_canvas.width, tmp_canvas.height ); - - // blur the imagedata using superfast blur by @quasimondo - // not very accurate, but fast - blurred_image_data = blur( image_data, values.blur, false ); - - greyscale_data = greyscale( image_data ); - edge_image_data = detectEdges( greyscale_data, values.accuracy, 5 ); - - // gets some of the edge points to construct triangles - edge_points = getEdgePoints( edge_image_data, 50, values.accuracy ); - edge_vertices = getRandomVertices( edge_points, values['point-rate'], values['point-count'], values.accuracy, tmp_canvas.width, tmp_canvas.height ); - - // makes triangles out of points - polygons = triangulate( edge_vertices ); - - // get the color for every triangle - triangles = getColorfulTriangles( polygons, color_data ); - - drawTriangles( ctx, triangles ); + + triangulated_image_data = triangulate( values ).fromImage( img ).toImageData( { dpr: pxratio } ); + updateCanvas( ctx, triangulated_image_data ); is_processing = false; } - function drawTriangles( ctx, triangles ) + function updateCanvas ( ctx, image_data ) { - len = triangles.length; - - for ( i = 0; i < len; i++ ) - { - triangle = triangles[i]; - - ctx.beginPath(); - ctx.moveTo( triangle.a.x * pxratio, triangle.a.y * pxratio ); - ctx.lineTo( triangle.b.x * pxratio, triangle.b.y * pxratio ); - ctx.lineTo( triangle.c.x * pxratio, triangle.c.y * pxratio ); - ctx.lineTo( triangle.a.x * pxratio, triangle.a.y * pxratio ); - - ctx.fillStyle = triangle.color; - ctx.fill(); - ctx.closePath(); - } + ctx.putImageData( image_data, 0, 0 ); } function resizeCanvas( canvas, img, ratio ) @@ -154,36 +90,13 @@ define( { var export_data = { png: canvas.toDataURL( 'image/png' ), - svg: { - triangles: triangles, - size : { width: canvas.width / pxratio, height: canvas.height / pxratio } - } + svg: triangulate( values ).fromImage( image ).toSVG() }; callback( export_data ); } } - function getColorfulTriangles( triangles, color_data ) - { - len = triangles.length; - - for ( i = 0; i < len; i++ ) - { - triangle = triangles[i]; - - // triangle color = color at center of triangle - triangle_center_x = ( triangle.a.x + triangle.b.x + triangle.c.x ) * 0.33333; - triangle_center_y = ( triangle.a.y + triangle.b.y + triangle.c.y ) * 0.33333; - - pixel = ( ( triangle_center_x | 0 ) + ( triangle_center_y | 0 ) * color_data.width ) << 2; - - triangle.color = 'rgb(' + color_data.data[pixel] + ', ' + color_data.data[pixel + 1] + ', ' + color_data.data[pixel + 2] + ')'; - } - - return triangles; - } - function getAdjustedValues( new_values ) { var result = { }; @@ -200,14 +113,14 @@ define( result[key] = scaleRange( new_values[key], 0, 100, 1, 0.1 ); break; - case 'point-rate' : - result[key] = scaleRange( new_values[key], 0, 100, 0.001, 0.1 ); - break; - - case 'point-count' : - result[key] = parseInt( scaleRange( new_values[key], 0, 100, 100, 5000 ), 10 ); + case 'vertex-count' : + result.vertexCount = parseInt( scaleRange( new_values[key], 0, 100, 100, 5000 ), 10 ); break; } + + result.fill = !! new_values.fill; + + result.strokeWidth = scaleRange( new_values['stroke-width'], 0, 100, 0, 100 ); } return result; diff --git a/scripts/util/detect-edges.js b/scripts/util/detect-edges.js deleted file mode 100644 index b3ce21c..0000000 --- a/scripts/util/detect-edges.js +++ /dev/null @@ -1,117 +0,0 @@ -// most parts taken from http://jsdo.it/akm2/xoYx -// (starting line 366++) -/*global define*/ -define( - function() - { - /** - * @see http://jsdo.it/akm2/iMsL - */ - function detectEdges( image_data, accuracy, edge_size, divisor ) - { - var matrix = getEdgeMatrix( edge_size ).slice(); - var multiplier = parseInt( ( accuracy || 0.5 ) * 10, 10 ) || 1; - - divisor = divisor || 1; - - var divscalar = divisor ? 1 / divisor : 0; - var k, len; - - if ( divscalar !== 1 ) - { - for ( k = 0, len = matrix.length; k < matrix.length; k++ ) - { - matrix[k] *= divscalar; - } - } - - var data = image_data.data; - - len = data.length >> 2; - - var copy = new Uint8Array( len ); - - for (i = 0; i < len; i++) - { - copy[i] = data[i << 2]; - } - - var width = image_data.width | 0; - var height = image_data.height | 0; - var size = Math.sqrt( matrix.length ); - var range = size * 0.5 | 0; - - var x, y; - var r, g, b, v; - var col, row, sx, sy; - var i, istep, jstep, kstep; - - for ( y = 0; y < height; y += multiplier ) - { - istep = y * width; - - for ( x = 0; x < width; x += multiplier ) - { - r = g = b = 0; - - for ( row = -range; row <= range; row++ ) - { - sy = y + row; - jstep = sy * width; - kstep = (row + range) * size; - - if ( sy >= 0 && sy < height ) - { - for ( col = -range; col <= range; col++ ) - { - sx = x + col; - - if ( - sx >= 0 && sx < width && - ( v = matrix[( col + range ) + kstep] ) - ) - { - r += copy[sx + jstep] * v; - } - } - } - } - - if ( r < 0 ) - { - r = 0; - } - - else - { - if ( r > 255 ) - { - r = 255; - } - } - - data[( x + istep ) << 2] = r & 0xFF; - } - } - - return image_data; - } - - function getEdgeMatrix( size ) - { - var matrix = [ ]; - var side = size * 2 + 1; - var i, len = side * side; - var center = len * 0.5 | 0; - - for ( i = 0; i < len; i++ ) - { - matrix[i] = i === center ? -len + 1 : 1; - } - - return matrix; - } - - return detectEdges; - } -); \ No newline at end of file diff --git a/scripts/util/get-edge-points.js b/scripts/util/get-edge-points.js deleted file mode 100644 index b84fb6e..0000000 --- a/scripts/util/get-edge-points.js +++ /dev/null @@ -1,60 +0,0 @@ -// most parts taken from http://jsdo.it/akm2/xoYx -// (starting line 293++) -/*global define*/ -define( - function() - { - function getEdgePoints( image_data, sensitivity, accuracy ) - { - var multiplier = parseInt( ( accuracy || 0.1 ) * 10, 10 ) || 1; - var edge_detect_value = sensitivity; - var width = image_data.width; - var height = image_data.height; - var data = image_data.data; - var points = [ ]; - var x, y, row, col, sx, sy, step, sum, total; - - for ( y = 0; y < height; y += multiplier ) - { - for ( x = 0; x < width; x += multiplier ) - { - sum = total = 0; - - for ( row = -1; row <= 1; row++ ) - { - sy = y + row; - step = sy * width; - - if ( sy >= 0 && sy < height ) - { - for ( col = -1; col <= 1; col++ ) - { - sx = x + col; - - if ( sx >= 0 && sx < width ) - { - sum += data[( sx + step ) << 2]; - total++; - } - } - } - } - - if ( total ) - { - sum /= total; - } - - if ( sum > edge_detect_value ) - { - points.push( { x: x, y: y } ); - } - } - } - - return points; - } - - return getEdgePoints; - } -); \ No newline at end of file diff --git a/scripts/util/get-random-vertices.js b/scripts/util/get-random-vertices.js deleted file mode 100644 index 4eadbd9..0000000 --- a/scripts/util/get-random-vertices.js +++ /dev/null @@ -1,59 +0,0 @@ -// most parts taken from akm2's script: -// http://jsdo.it/akm2/xoYx (line 230++) -/*global define*/ -define( - function() - { - function getRandomVertices( points, rate, max_num, accuracy, width, height ) - { - var j; - var result = [ ]; - var i = 0; - var i_len = points.length; - var t_len = i_len; - var limit = Math.round( i_len * rate ); - - points = points.slice(); - - if ( limit > max_num ) - { - limit = max_num; - } - - while ( i < limit && i < i_len ) - { - j = t_len * Math.random() | 0; - result.push( { x: points[j].x, y: points[j].y } ); - - // this seems to be extremely time - // intensive. - // points.splice( j, 1 ); - - t_len--; - i++; - } - - var x, y; - - // gf: add more points along the edges so we always use the full canvas, - for ( x = 0; x < width; x += (100 - accuracy) ) - { - result.push( { x: ~~x, y: 0 } ); - result.push( { x: ~~x, y: height } ); - } - - for ( y = 0; y < height; y += (100 - accuracy) ) - { - result.push( { x: 0, y: ~~y } ); - result.push( { x: width, y: ~~y } ); - } - - result.push( { x: 0, y: height } ); - result.push( { x: width, y: height } ); - - return result; - } - - return getRandomVertices; - } -); diff --git a/scripts/util/greyscale.js b/scripts/util/greyscale.js deleted file mode 100644 index 44be4fd..0000000 --- a/scripts/util/greyscale.js +++ /dev/null @@ -1,25 +0,0 @@ -/*global define*/ -define( - function() - { - function greyscale( image_data ) - { - var len = image_data.data.length; - var data = image_data.data; - - for ( var i = 0; i < len; i += 4 ) - { - var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]; - - data[i] = brightness; - data[i + 1] = brightness; - data[i + 2] = brightness; - } - - image_data.data = data; - return image_data; - } - - return greyscale; - } -); \ No newline at end of file diff --git a/styles/main.css b/styles/main.css index 8f1f6e1..3dd6e24 100644 --- a/styles/main.css +++ b/styles/main.css @@ -87,7 +87,7 @@ a:hover { #random-button { position: relative; - top: 6px; + top: -4px; } .headline { @@ -152,16 +152,16 @@ a:hover { .control-wrapper { display: inline-block; - width: 160px; + width: 120px; margin-right: 20px; } .control-label { - font-size: 11px; + max-width: 90px; + float: left; display: block; + font-size: 11px; color: #666; - text-align: center; - max-width: 120px; } .control-slider { @@ -197,7 +197,7 @@ a:hover { .download-link.is-active { display: inline-block; - margin-left: 15px; + margin-left: 5px; } .download-link span {