{"version":3,"file":"magic-wand.min.js","sources":["../src/MagicWand.js"],"sourcesContent":[" \r\nvar MagicWand = (function () {\r\n var lib = {};\r\n\r\n /** Create a binary mask on the image by color threshold\r\n * Algorithm: Scanline flood fill (http://en.wikipedia.org/wiki/Flood_fill)\r\n * @param {Object} image: {Uint8Array} data, {int} width, {int} height, {int} bytes\r\n * @param {int} x of start pixel\r\n * @param {int} y of start pixel\r\n * @param {int} color threshold\r\n * @param {Uint8Array} mask of visited points (optional) \r\n * @param {boolean} [includeBorders=false] indicate whether to include borders pixels\r\n * @return {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n */\r\n lib.floodFill = function(image, px, py, colorThreshold, mask, includeBorders) {\r\n return includeBorders\r\n ? floodFillWithBorders(image, px, py, colorThreshold, mask)\r\n : floodFillWithoutBorders(image, px, py, colorThreshold, mask);\r\n };\r\n\r\n function floodFillWithoutBorders(image, px, py, colorThreshold, mask) {\r\n\r\n var c, x, newY, el, xr, xl, dy, dyl, dyr, checkY,\r\n data = image.data,\r\n w = image.width,\r\n h = image.height,\r\n bytes = image.bytes, // number of bytes in the color\r\n maxX = -1, minX = w + 1, maxY = -1, minY = h + 1,\r\n i = py * w + px, // start point index in the mask data\r\n result = new Uint8Array(w * h), // result mask\r\n visited = new Uint8Array(mask ? mask : w * h); // mask of visited points\r\n\r\n if (visited[i] === 1) return null;\r\n\r\n i = i * bytes; // start point index in the image data\r\n var sampleColor = [data[i], data[i + 1], data[i + 2], data[i + 3]]; // start point color (sample)\r\n\r\n var stack = [{ y: py, left: px - 1, right: px + 1, dir: 1 }]; // first scanning line\r\n do {\r\n el = stack.shift(); // get line for scanning\r\n\r\n checkY = false;\r\n for (x = el.left + 1; x < el.right; x++) {\r\n dy = el.y * w;\r\n i = (dy + x) * bytes; // point index in the image data\r\n\r\n if (visited[dy + x] === 1) continue; // check whether the point has been visited\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n\r\n checkY = true; // if the color of the new point(x,y) is similar to the sample color need to check minmax for Y \r\n\r\n result[dy + x] = 1; // mark a new point in mask\r\n visited[dy + x] = 1; // mark a new point as visited\r\n\r\n xl = x - 1;\r\n // walk to left side starting with the left neighbor\r\n while (xl > -1) {\r\n dyl = dy + xl;\r\n i = dyl * bytes; // point index in the image data\r\n if (visited[dyl] === 1) break; // check whether the point has been visited\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n\r\n result[dyl] = 1;\r\n visited[dyl] = 1;\r\n\r\n xl--;\r\n }\r\n xr = x + 1;\r\n // walk to right side starting with the right neighbor\r\n while (xr < w) {\r\n dyr = dy + xr;\r\n i = dyr * bytes; // index point in the image data\r\n if (visited[dyr] === 1) break; // check whether the point has been visited\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n\r\n result[dyr] = 1;\r\n visited[dyr] = 1;\r\n\r\n xr++;\r\n }\r\n\r\n // check minmax for X\r\n if (xl < minX) minX = xl + 1;\r\n if (xr > maxX) maxX = xr - 1;\r\n\r\n newY = el.y - el.dir;\r\n if (newY >= 0 && newY < h) { // add two scanning lines in the opposite direction (y - dir) if necessary\r\n if (xl < el.left) stack.push({ y: newY, left: xl, right: el.left, dir: -el.dir }); // from \"new left\" to \"current left\"\r\n if (el.right < xr) stack.push({ y: newY, left: el.right, right: xr, dir: -el.dir }); // from \"current right\" to \"new right\"\r\n }\r\n newY = el.y + el.dir;\r\n if (newY >= 0 && newY < h) { // add the scanning line in the direction (y + dir) if necessary\r\n if (xl < xr) stack.push({ y: newY, left: xl, right: xr, dir: el.dir }); // from \"new left\" to \"new right\"\r\n }\r\n }\r\n // check minmax for Y if necessary\r\n if (checkY) {\r\n if (el.y < minY) minY = el.y;\r\n if (el.y > maxY) maxY = el.y;\r\n }\r\n } while (stack.length > 0);\r\n\r\n return {\r\n data: result,\r\n width: image.width,\r\n height: image.height,\r\n bounds: {\r\n minX: minX,\r\n minY: minY,\r\n maxX: maxX,\r\n maxY: maxY\r\n }\r\n };\r\n };\r\n\r\n function floodFillWithBorders(image, px, py, colorThreshold, mask) {\r\n\r\n var c, x, newY, el, xr, xl, dy, dyl, dyr, checkY,\r\n data = image.data,\r\n w = image.width,\r\n h = image.height,\r\n bytes = image.bytes, // number of bytes in the color\r\n maxX = -1, minX = w + 1, maxY = -1, minY = h + 1,\r\n i = py * w + px, // start point index in the mask data\r\n result = new Uint8Array(w * h), // result mask\r\n visited = new Uint8Array(mask ? mask : w * h); // mask of visited points\r\n\r\n if (visited[i] === 1) return null;\r\n\r\n i = i * bytes; // start point index in the image data\r\n var sampleColor = [data[i], data[i + 1], data[i + 2], data[i + 3]]; // start point color (sample)\r\n\r\n var stack = [{ y: py, left: px - 1, right: px + 1, dir: 1 }]; // first scanning line\r\n do {\r\n el = stack.shift(); // get line for scanning\r\n\r\n checkY = false;\r\n for (x = el.left + 1; x < el.right; x++) {\r\n dy = el.y * w;\r\n i = (dy + x) * bytes; // point index in the image data\r\n\r\n if (visited[dy + x] === 1) continue; // check whether the point has been visited\r\n\r\n checkY = true; // if the color of the new point(x,y) is similar to the sample color need to check minmax for Y \r\n\r\n result[dy + x] = 1; // mark a new point in mask\r\n visited[dy + x] = 1; // mark a new point as visited\r\n\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) continue;\r\n\r\n xl = x - 1;\r\n // walk to left side starting with the left neighbor\r\n while (xl > -1) {\r\n dyl = dy + xl;\r\n i = dyl * bytes; // point index in the image data\r\n if (visited[dyl] === 1) break; // check whether the point has been visited\r\n\r\n result[dyl] = 1;\r\n visited[dyl] = 1;\r\n xl--;\r\n\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n }\r\n xr = x + 1;\r\n // walk to right side starting with the right neighbor\r\n while (xr < w) {\r\n dyr = dy + xr;\r\n i = dyr * bytes; // index point in the image data\r\n if (visited[dyr] === 1) break; // check whether the point has been visited\r\n\r\n result[dyr] = 1;\r\n visited[dyr] = 1;\r\n xr++;\r\n\r\n // compare the color of the sample\r\n c = data[i] - sampleColor[0]; // check by red\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 1] - sampleColor[1]; // check by green\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n c = data[i + 2] - sampleColor[2]; // check by blue\r\n if (c > colorThreshold || c < -colorThreshold) break;\r\n }\r\n\r\n // check minmax for X\r\n if (xl < minX) minX = xl + 1;\r\n if (xr > maxX) maxX = xr - 1;\r\n\r\n newY = el.y - el.dir;\r\n if (newY >= 0 && newY < h) { // add two scanning lines in the opposite direction (y - dir) if necessary\r\n if (xl < el.left) stack.push({ y: newY, left: xl, right: el.left, dir: -el.dir }); // from \"new left\" to \"current left\"\r\n if (el.right < xr) stack.push({ y: newY, left: el.right, right: xr, dir: -el.dir }); // from \"current right\" to \"new right\"\r\n }\r\n newY = el.y + el.dir;\r\n if (newY >= 0 && newY < h) { // add the scanning line in the direction (y + dir) if necessary\r\n if (xl < xr) stack.push({ y: newY, left: xl, right: xr, dir: el.dir }); // from \"new left\" to \"new right\"\r\n }\r\n }\r\n // check minmax for Y if necessary\r\n if (checkY) {\r\n if (el.y < minY) minY = el.y;\r\n if (el.y > maxY) maxY = el.y;\r\n }\r\n } while (stack.length > 0);\r\n\r\n return {\r\n data: result,\r\n width: image.width,\r\n height: image.height,\r\n bounds: {\r\n minX: minX,\r\n minY: minY,\r\n maxX: maxX,\r\n maxY: maxY\r\n }\r\n };\r\n };\r\n\r\n /** Apply the gauss-blur filter to binary mask\r\n * Algorithms: http://blog.ivank.net/fastest-gaussian-blur.html\r\n * http://www.librow.com/articles/article-9\r\n * http://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @param {int} blur radius\r\n * @return {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n */\r\n lib.gaussBlur = function(mask, radius) {\r\n\r\n var i, k, k1, x, y, val, start, end,\r\n n = radius * 2 + 1, // size of the pattern for radius-neighbors (from -r to +r with the center point)\r\n s2 = radius * radius,\r\n wg = new Float32Array(n), // weights\r\n total = 0, // sum of weights(used for normalization)\r\n w = mask.width,\r\n h = mask.height,\r\n data = mask.data,\r\n minX = mask.bounds.minX,\r\n maxX = mask.bounds.maxX,\r\n minY = mask.bounds.minY,\r\n maxY = mask.bounds.maxY;\r\n\r\n // calc gauss weights\r\n for (i = 0; i < radius; i++) {\r\n var dsq = (radius - i) * (radius - i);\r\n var ww = Math.exp(-dsq / (2.0 * s2)) / (2 * Math.PI * s2);\r\n wg[radius + i] = wg[radius - i] = ww;\r\n total += 2 * ww;\r\n }\r\n // normalization weights\r\n for (i = 0; i < n; i++) {\r\n wg[i] /= total;\r\n }\r\n\r\n var result = new Uint8Array(w * h), // result mask\r\n endX = radius + w,\r\n endY = radius + h;\r\n\r\n //walk through all source points for blur\r\n for (y = minY; y < maxY + 1; y++)\r\n for (x = minX; x < maxX + 1; x++) {\r\n val = 0;\r\n k = y * w + x; // index of the point\r\n start = radius - x > 0 ? radius - x : 0;\r\n end = endX - x < n ? endX - x : n; // Math.min((((w - 1) - x) + radius) + 1, n);\r\n k1 = k - radius;\r\n // walk through x-neighbors\r\n for (i = start; i < end; i++) {\r\n val += data[k1 + i] * wg[i];\r\n }\r\n start = radius - y > 0 ? radius - y : 0;\r\n end = endY - y < n ? endY - y : n; // Math.min((((h - 1) - y) + radius) + 1, n);\r\n k1 = k - radius * w;\r\n // walk through y-neighbors\r\n for (i = start; i < end; i++) {\r\n val += data[k1 + i * w] * wg[i];\r\n }\r\n result[k] = val > 0.5 ? 1 : 0;\r\n }\r\n\r\n return {\r\n data: result,\r\n width: w,\r\n height: h,\r\n bounds: {\r\n minX: minX,\r\n minY: minY,\r\n maxX: maxX,\r\n maxY: maxY\r\n }\r\n };\r\n };\r\n\r\n /** Create a border index array of boundary points of the mask with radius-neighbors\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @param {int} blur radius\r\n * @param {Uint8Array} visited: mask of visited points (optional) \r\n * @return {Array} border index array of boundary points with radius-neighbors (only points need for blur)\r\n */\r\n function createBorderForBlur(mask, radius, visited) {\r\n\r\n var x, i, j, y, k, k1, k2,\r\n w = mask.width,\r\n h = mask.height,\r\n data = mask.data,\r\n visitedData = new Uint8Array(data),\r\n minX = mask.bounds.minX,\r\n maxX = mask.bounds.maxX,\r\n minY = mask.bounds.minY,\r\n maxY = mask.bounds.maxY,\r\n len = w * h,\r\n temp = new Uint8Array(len), // auxiliary array to check uniqueness\r\n border = [], // only border points\r\n x0 = Math.max(minX, 1),\r\n x1 = Math.min(maxX, w - 2),\r\n y0 = Math.max(minY, 1),\r\n y1 = Math.min(maxY, h - 2);\r\n\r\n if (visited && visited.length > 0) {\r\n // copy visited points (only \"black\")\r\n for (k = 0; k < len; k++) {\r\n if (visited[k] === 1) visitedData[k] = 1;\r\n }\r\n }\r\n\r\n // walk through inner values except points on the boundary of the image\r\n for (y = y0; y < y1 + 1; y++)\r\n for (x = x0; x < x1 + 1; x++) {\r\n k = y * w + x;\r\n if (data[k] === 0) continue; // \"white\" point isn't the border\r\n k1 = k + w; // y + 1\r\n k2 = k - w; // y - 1\r\n // check if any neighbor with a \"white\" color\r\n if (visitedData[k + 1] === 0 || visitedData[k - 1] === 0 ||\r\n visitedData[k1] === 0 || visitedData[k1 + 1] === 0 || visitedData[k1 - 1] === 0 ||\r\n visitedData[k2] === 0 || visitedData[k2 + 1] === 0 || visitedData[k2 - 1] === 0) {\r\n //if (visitedData[k + 1] + visitedData[k - 1] + \r\n // visitedData[k1] + visitedData[k1 + 1] + visitedData[k1 - 1] +\r\n // visitedData[k2] + visitedData[k2 + 1] + visitedData[k2 - 1] == 8) continue;\r\n border.push(k);\r\n }\r\n }\r\n\r\n // walk through points on the boundary of the image if necessary\r\n // if the \"black\" point is adjacent to the boundary of the image, it is a border point\r\n if (minX == 0)\r\n for (y = minY; y < maxY + 1; y++)\r\n if (data[y * w] === 1)\r\n border.push(y * w);\r\n\r\n if (maxX == w - 1)\r\n for (y = minY; y < maxY + 1; y++)\r\n if (data[y * w + maxX] === 1)\r\n border.push(y * w + maxX);\r\n\r\n if (minY == 0)\r\n for (x = minX; x < maxX + 1; x++)\r\n if (data[x] === 1)\r\n border.push(x);\r\n\r\n if (maxY == h - 1)\r\n for (x = minX; x < maxX + 1; x++)\r\n if (data[maxY * w + x] === 1)\r\n border.push(maxY * w + x);\r\n\r\n var result = [], // border points with radius-neighbors\r\n start, end,\r\n endX = radius + w,\r\n endY = radius + h,\r\n n = radius * 2 + 1; // size of the pattern for radius-neighbors (from -r to +r with the center point)\r\n\r\n len = border.length;\r\n // walk through radius-neighbors of border points and add them to the result array\r\n for (j = 0; j < len; j++) {\r\n k = border[j]; // index of the border point\r\n temp[k] = 1; // mark border point\r\n result.push(k); // save the border point\r\n x = k % w; // calc x by index\r\n y = (k - x) / w; // calc y by index\r\n start = radius - x > 0 ? radius - x : 0;\r\n end = endX - x < n ? endX - x : n; // Math.min((((w - 1) - x) + radius) + 1, n);\r\n k1 = k - radius;\r\n // walk through x-neighbors\r\n for (i = start; i < end; i++) {\r\n k2 = k1 + i;\r\n if (temp[k2] === 0) { // check the uniqueness\r\n temp[k2] = 1;\r\n result.push(k2);\r\n }\r\n }\r\n start = radius - y > 0 ? radius - y : 0;\r\n end = endY - y < n ? endY - y : n; // Math.min((((h - 1) - y) + radius) + 1, n);\r\n k1 = k - radius * w;\r\n // walk through y-neighbors\r\n for (i = start; i < end; i++) {\r\n k2 = k1 + i * w;\r\n if (temp[k2] === 0) { // check the uniqueness\r\n temp[k2] = 1;\r\n result.push(k2);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n /** Apply the gauss-blur filter ONLY to border points with radius-neighbors\r\n * Algorithms: http://blog.ivank.net/fastest-gaussian-blur.html\r\n * http://www.librow.com/articles/article-9\r\n * http://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @param {int} blur radius\r\n * @param {Uint8Array} visited: mask of visited points (optional) \r\n * @return {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n */\r\n lib.gaussBlurOnlyBorder = function(mask, radius, visited) {\r\n\r\n var border = createBorderForBlur(mask, radius, visited), // get border points with radius-neighbors\r\n ww, dsq, i, j, k, k1, x, y, val, start, end,\r\n n = radius * 2 + 1, // size of the pattern for radius-neighbors (from -r to +r with center point)\r\n s2 = 2 * radius * radius,\r\n wg = new Float32Array(n), // weights\r\n total = 0, // sum of weights(used for normalization)\r\n w = mask.width,\r\n h = mask.height,\r\n data = mask.data,\r\n minX = mask.bounds.minX,\r\n maxX = mask.bounds.maxX,\r\n minY = mask.bounds.minY,\r\n maxY = mask.bounds.maxY,\r\n len = border.length;\r\n\r\n // calc gauss weights\r\n for (i = 0; i < radius; i++) {\r\n dsq = (radius - i) * (radius - i);\r\n ww = Math.exp(-dsq / s2) / Math.PI;\r\n wg[radius + i] = wg[radius - i] = ww;\r\n total += 2 * ww;\r\n }\r\n // normalization weights\r\n for (i = 0; i < n; i++) {\r\n wg[i] /= total;\r\n }\r\n\r\n var result = new Uint8Array(data), // copy the source mask\r\n endX = radius + w,\r\n endY = radius + h;\r\n\r\n //walk through all border points for blur\r\n for (i = 0; i < len; i++) {\r\n k = border[i]; // index of the border point\r\n val = 0;\r\n x = k % w; // calc x by index\r\n y = (k - x) / w; // calc y by index\r\n start = radius - x > 0 ? radius - x : 0;\r\n end = endX - x < n ? endX - x : n; // Math.min((((w - 1) - x) + radius) + 1, n);\r\n k1 = k - radius;\r\n // walk through x-neighbors\r\n for (j = start; j < end; j++) {\r\n val += data[k1 + j] * wg[j];\r\n }\r\n if (val > 0.5) {\r\n result[k] = 1;\r\n // check minmax\r\n if (x < minX) minX = x;\r\n if (x > maxX) maxX = x;\r\n if (y < minY) minY = y;\r\n if (y > maxY) maxY = y;\r\n continue;\r\n }\r\n start = radius - y > 0 ? radius - y : 0;\r\n end = endY - y < n ? endY - y : n; // Math.min((((h - 1) - y) + radius) + 1, n);\r\n k1 = k - radius * w;\r\n // walk through y-neighbors\r\n for (j = start; j < end; j++) {\r\n val += data[k1 + j * w] * wg[j];\r\n }\r\n if (val > 0.5) {\r\n result[k] = 1;\r\n // check minmax\r\n if (x < minX) minX = x;\r\n if (x > maxX) maxX = x;\r\n if (y < minY) minY = y;\r\n if (y > maxY) maxY = y;\r\n } else {\r\n result[k] = 0;\r\n }\r\n }\r\n\r\n return {\r\n data: result,\r\n width: w,\r\n height: h,\r\n bounds: {\r\n minX: minX,\r\n minY: minY,\r\n maxX: maxX,\r\n maxY: maxY\r\n }\r\n };\r\n };\r\n\r\n /** Create a border mask (only boundary points)\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @return {Object} border mask: {Uint8Array} data, {int} width, {int} height, {Object} offset\r\n */\r\n lib.createBorderMask = function(mask) {\r\n\r\n var x, y, k, k1, k2,\r\n w = mask.width,\r\n h = mask.height,\r\n data = mask.data,\r\n minX = mask.bounds.minX,\r\n maxX = mask.bounds.maxX,\r\n minY = mask.bounds.minY,\r\n maxY = mask.bounds.maxY,\r\n rw = maxX - minX + 1, // bounds size\r\n rh = maxY - minY + 1,\r\n result = new Uint8Array(rw * rh), // reduced mask (bounds size)\r\n x0 = Math.max(minX, 1),\r\n x1 = Math.min(maxX, w - 2),\r\n y0 = Math.max(minY, 1),\r\n y1 = Math.min(maxY, h - 2);\r\n\r\n // walk through inner values except points on the boundary of the image\r\n for (y = y0; y < y1 + 1; y++)\r\n for (x = x0; x < x1 + 1; x++) {\r\n k = y * w + x;\r\n if (data[k] === 0) continue; // \"white\" point isn't the border\r\n k1 = k + w; // y + 1\r\n k2 = k - w; // y - 1\r\n // check if any neighbor with a \"white\" color\r\n if (data[k + 1] === 0 || data[k - 1] === 0 ||\r\n data[k1] === 0 || data[k1 + 1] === 0 || data[k1 - 1] === 0 ||\r\n data[k2] === 0 || data[k2 + 1] === 0 || data[k2 - 1] === 0) {\r\n //if (data[k + 1] + data[k - 1] + \r\n // data[k1] + data[k1 + 1] + data[k1 - 1] +\r\n // data[k2] + data[k2 + 1] + data[k2 - 1] == 8) continue;\r\n result[(y - minY) * rw + (x - minX)] = 1;\r\n }\r\n }\r\n\r\n // walk through points on the boundary of the image if necessary\r\n // if the \"black\" point is adjacent to the boundary of the image, it is a border point\r\n if (minX == 0)\r\n for (y = minY; y < maxY + 1; y++)\r\n if (data[y * w] === 1)\r\n result[(y - minY) * rw] = 1;\r\n\r\n if (maxX == w - 1)\r\n for (y = minY; y < maxY + 1; y++)\r\n if (data[y * w + maxX] === 1)\r\n result[(y - minY) * rw + (maxX - minX)] = 1;\r\n\r\n if (minY == 0)\r\n for (x = minX; x < maxX + 1; x++)\r\n if (data[x] === 1)\r\n result[x - minX] = 1;\r\n\r\n if (maxY == h - 1)\r\n for (x = minX; x < maxX + 1; x++)\r\n if (data[maxY * w + x] === 1)\r\n result[(maxY - minY) * rw + (x - minX)] = 1;\r\n\r\n return {\r\n data: result,\r\n width: rw,\r\n height: rh,\r\n offset: { x: minX, y: minY }\r\n };\r\n };\r\n \r\n /** Create a border index array of boundary points of the mask\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height\r\n * @return {Array} border index array boundary points of the mask\r\n */\r\n lib.getBorderIndices = function(mask) {\r\n\r\n var x, y, k, k1, k2,\r\n w = mask.width,\r\n h = mask.height,\r\n data = mask.data,\r\n border = [], // only border points\r\n x1 = w - 1,\r\n y1 = h - 1;\r\n\r\n // walk through inner values except points on the boundary of the image\r\n for (y = 1; y < y1; y++)\r\n for (x = 1; x < x1; x++) {\r\n k = y * w + x;\r\n if (data[k] === 0) continue; // \"white\" point isn't the border\r\n k1 = k + w; // y + 1\r\n k2 = k - w; // y - 1\r\n // check if any neighbor with a \"white\" color\r\n if (data[k + 1] === 0 || data[k - 1] === 0 ||\r\n data[k1] === 0 || data[k1 + 1] === 0 || data[k1 - 1] === 0 ||\r\n data[k2] === 0 || data[k2 + 1] === 0 || data[k2 - 1] === 0) {\r\n //if (data[k + 1] + data[k - 1] + \r\n // data[k1] + data[k1 + 1] + data[k1 - 1] +\r\n // data[k2] + data[k2 + 1] + data[k2 - 1] == 8) continue;\r\n border.push(k);\r\n }\r\n }\r\n\r\n // walk through points on the boundary of the image if necessary\r\n // if the \"black\" point is adjacent to the boundary of the image, it is a border point\r\n for (y = 0; y < h; y++)\r\n if (data[y * w] === 1)\r\n border.push(y * w);\r\n\r\n for (x = 0; x < w; x++)\r\n if (data[x] === 1)\r\n border.push(x);\r\n\r\n k = w - 1;\r\n for (y = 0; y < h; y++)\r\n if (data[y * w + k] === 1)\r\n border.push(y * w + k);\r\n\r\n k = (h - 1) * w;\r\n for (x = 0; x < w; x++)\r\n if (data[k + x] === 1)\r\n border.push(k + x);\r\n\r\n return border;\r\n };\r\n \r\n /** Create a compressed mask with a \"white\" border (1px border with zero values) for the contour tracing\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @return {Object} border mask: {Uint8Array} data, {int} width, {int} height, {Object} offset\r\n */\r\n function prepareMask(mask) {\r\n var x, y,\r\n w = mask.width,\r\n data = mask.data,\r\n minX = mask.bounds.minX,\r\n maxX = mask.bounds.maxX,\r\n minY = mask.bounds.minY,\r\n maxY = mask.bounds.maxY,\r\n rw = maxX - minX + 3, // bounds size +1 px on each side (a \"white\" border)\r\n rh = maxY - minY + 3,\r\n result = new Uint8Array(rw * rh); // reduced mask (bounds size)\r\n\r\n // walk through inner values and copy only \"black\" points to the result mask\r\n for (y = minY; y < maxY + 1; y++)\r\n for (x = minX; x < maxX + 1; x++) {\r\n if (data[y * w + x] === 1)\r\n result[(y - minY + 1) * rw + (x - minX + 1)] = 1;\r\n }\r\n\r\n return {\r\n data: result,\r\n width: rw,\r\n height: rh,\r\n offset: { x: minX - 1, y: minY - 1 }\r\n };\r\n };\r\n \r\n /** Create a contour array for the binary mask\r\n * Algorithm: http://www.sciencedirect.com/science/article/pii/S1077314203001401\r\n * @param {Object} mask: {Uint8Array} data, {int} width, {int} height, {Object} bounds\r\n * @return {Array} contours: {Array} points, {bool} inner, {int} label\r\n */\r\n lib.traceContours = function(mask) {\r\n var m = prepareMask(mask),\r\n contours = [],\r\n label = 0,\r\n w = m.width,\r\n w2 = w * 2,\r\n h = m.height,\r\n src = m.data,\r\n dx = m.offset.x,\r\n dy = m.offset.y,\r\n dest = new Uint8Array(src), // label matrix\r\n i, j, x, y, k, k1, c, inner, dir, first, second, current, previous, next, d;\r\n\r\n // all [dx,dy] pairs (array index is the direction)\r\n // 5 6 7\r\n // 4 X 0\r\n // 3 2 1\r\n var directions = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]];\r\n\r\n for (y = 1; y < h - 1; y++)\r\n for (x = 1; x < w - 1; x++) {\r\n k = y * w + x;\r\n if (src[k] === 1) {\r\n for (i = -w; i < w2; i += w2) { // k - w: outer tracing (y - 1), k + w: inner tracing (y + 1)\r\n if (src[k + i] === 0 && dest[k + i] === 0) { // need contour tracing\r\n inner = i === w; // is inner contour tracing ?\r\n label++; // label for the next contour\r\n\r\n c = [];\r\n dir = inner ? 2 : 6; // start direction\r\n current = previous = first = { x: x, y: y };\r\n second = null;\r\n while (true) {\r\n dest[current.y * w + current.x] = label; // mark label for the current point \r\n // bypass all the neighbors around the current point in a clockwise\r\n for (j = 0; j < 8; j++) {\r\n dir = (dir + 1) % 8;\r\n\r\n // get the next point by new direction\r\n d = directions[dir]; // index as direction\r\n next = { x: current.x + d[0], y: current.y + d[1] };\r\n\r\n k1 = next.y * w + next.x;\r\n if (src[k1] === 1) // black boundary pixel\r\n {\r\n dest[k1] = label; // mark a label\r\n break;\r\n }\r\n dest[k1] = -1; // mark a white boundary pixel\r\n next = null;\r\n }\r\n if (next === null) break; // no neighbours (one-point contour)\r\n current = next;\r\n if (second) {\r\n if (previous.x === first.x && previous.y === first.y && current.x === second.x && current.y === second.y) {\r\n break; // creating the contour completed when returned to original position\r\n }\r\n } else {\r\n second = next;\r\n }\r\n c.push({ x: previous.x + dx, y: previous.y + dy });\r\n previous = current;\r\n dir = (dir + 4) % 8; // next dir (symmetrically to the current direction)\r\n }\r\n\r\n if (next != null) {\r\n c.push({ x: first.x + dx, y: first.y + dy }); // close the contour\r\n contours.push({ inner: inner, label: label, points: c }); // add contour to the list\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return contours;\r\n };\r\n \r\n /** Simplify contours\r\n * Algorithms: http://psimpl.sourceforge.net/douglas-peucker.html \r\n * http://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D0%BF%D1%80%D0%BE%D1%89%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE%D0%BB%D0%B8%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%86%D0%B5%D0%BF%D0%B8\r\n * @param {Array} contours: {Array} points, {bool} inner, {int} label\r\n * @param {float} simplify tolerant\r\n * @param {int} simplify count: min number of points when the contour is simplified\r\n * @return {Array} contours: {Array} points, {bool} inner, {int} label, {int} initialCount\r\n */\r\n lib.simplifyContours = function(contours, simplifyTolerant, simplifyCount) {\r\n var lenContours = contours.length,\r\n result = [],\r\n i, j, k, c, points, len, resPoints, lst, stack, ids,\r\n maxd, maxi, dist, r1, r2, r12, dx, dy, pi, pf, pl;\r\n\r\n // walk through all contours \r\n for (j = 0; j < lenContours; j++) {\r\n c = contours[j];\r\n points = c.points;\r\n len = c.points.length;\r\n\r\n if (len < simplifyCount) { // contour isn't simplified\r\n resPoints = [];\r\n for (k = 0; k < len; k++) {\r\n resPoints.push({ x: points[k].x, y: points[k].y });\r\n }\r\n result.push({ inner: c.inner, label: c.label, points: resPoints, initialCount: len });\r\n continue;\r\n }\r\n\r\n lst = [0, len - 1]; // always add first and last points\r\n stack = [{ first: 0, last: len - 1 }]; // first processed edge\r\n\r\n do {\r\n ids = stack.shift();\r\n if (ids.last <= ids.first + 1) // no intermediate points\r\n {\r\n continue;\r\n }\r\n\r\n maxd = -1.0; // max distance from point to current edge\r\n maxi = ids.first; // index of maximally distant point\r\n\r\n for (i = ids.first + 1; i < ids.last; i++) // bypass intermediate points in edge\r\n {\r\n // calc the distance from current point to edge\r\n pi = points[i];\r\n pf = points[ids.first];\r\n pl = points[ids.last];\r\n dx = pi.x - pf.x;\r\n dy = pi.y - pf.y;\r\n r1 = Math.sqrt(dx * dx + dy * dy);\r\n dx = pi.x - pl.x;\r\n dy = pi.y - pl.y;\r\n r2 = Math.sqrt(dx * dx + dy * dy);\r\n dx = pf.x - pl.x;\r\n dy = pf.y - pl.y;\r\n r12 = Math.sqrt(dx * dx + dy * dy);\r\n if (r1 >= Math.sqrt(r2 * r2 + r12 * r12)) dist = r2;\r\n else if (r2 >= Math.sqrt(r1 * r1 + r12 * r12)) dist = r1;\r\n else dist = Math.abs((dy * pi.x - dx * pi.y + pf.x * pl.y - pl.x * pf.y) / r12);\r\n\r\n if (dist > maxd) {\r\n maxi = i; // save the index of maximally distant point\r\n maxd = dist;\r\n }\r\n }\r\n\r\n if (maxd > simplifyTolerant) // if the max \"deviation\" is larger than allowed then...\r\n {\r\n lst.push(maxi); // add index to the simplified list\r\n stack.push({ first: ids.first, last: maxi }); // add the left part for processing\r\n stack.push({ first: maxi, last: ids.last }); // add the right part for processing\r\n }\r\n\r\n } while (stack.length > 0);\r\n\r\n resPoints = [];\r\n len = lst.length;\r\n lst.sort(function(a, b) { return a - b; }); // restore index order\r\n for (k = 0; k < len; k++) {\r\n resPoints.push({ x: points[lst[k]].x, y: points[lst[k]].y }); // add result points to the correct order\r\n }\r\n result.push({ inner: c.inner, label: c.label, points: resPoints, initialCount: c.points.length });\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return lib;\r\n})();\r\n\r\nif (typeof module !== \"undefined\" && module !== null) module.exports = MagicWand;\r\nif (typeof window !== \"undefined\" && window !== null) window.MagicWand = MagicWand;\r\n"],"names":["lib","MagicWand","floodFill","image","px","py","colorThreshold","mask","includeBorders","c","x","newY","el","xr","xl","dy","dyl","dyr","checkY","data","w","width","h","height","bytes","maxX","minX","maxY","minY","i","result","Uint8Array","visited","sampleColor","stack","y","left","right","dir","shift","push","length","bounds","floodFillWithBorders","floodFillWithoutBorders","gaussBlur","radius","k","k1","val","end","n","s2","wg","Float32Array","total","dsq","ww","Math","exp","PI","endX","endY","gaussBlurOnlyBorder","j","border","k2","visitedData","len","temp","x0","max","x1","min","y0","y1","createBorderForBlur","createBorderMask","rw","rh","offset","getBorderIndices","traceContours","inner","first","second","current","previous","next","d","m","prepareMask","contours","label","w2","src","dx","dest","directions","points","simplifyContours","simplifyTolerant","simplifyCount","resPoints","lst","ids","maxd","maxi","dist","r1","r2","r12","pi","pf","pl","lenContours","initialCount","last","sqrt","abs","sort","a","b","module","exports","window"],"mappings":"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"}