Advertisement
Guest User

Untitled

a guest
May 30th, 2015
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.14 KB | None | 0 0
  1. /**
  2. * Translucify
  3. * jim.yang@thisplace.com
  4. * 20/4/2015
  5. */
  6.  
  7. (function () {
  8. var DEFAULT_TOLERANCE_VALUE = 0.05;
  9.  
  10. // Tolerance threshold for flood fill.
  11. var _toleranceValue = DEFAULT_TOLERANCE_VALUE;
  12.  
  13. /**
  14. * @param {HTMLImageElement} image
  15. * @returns {boolean}
  16. */
  17. function isImageLoaded(image) {
  18. if (image.nodeType === 1 && image.tagName.toLowerCase() === 'img' && image.src !== '') {
  19. return image.complete || image.readyState === 4 || image.naturalWidth + image.naturalHeight > 0;
  20. } else {
  21. return false;
  22. }
  23. }
  24.  
  25. /**
  26. * Generates a set of <canvas>, <img> which is untainted by Cross-Origin image data.
  27. * @param {HTMLImageElement} image
  28. * @returns {{canvas: HTMLCanvasElement, imageCORS: HTMLImageElement}}
  29. */
  30. function getCanvasAndCORSImage(image) {
  31. /*
  32. Get CORS image without triggering security exceptions
  33. which occur when accessing pixel data even on an image
  34. with response header 'Access-Control-Allow-Origin: *'
  35. */
  36. var imageCORS = new Image();
  37. imageCORS.crossOrigin = "Anonymous";
  38.  
  39. var canvas = document.createElement("canvas");
  40.  
  41. var w = image.naturalWidth;
  42. var h = image.naturalHeight;
  43.  
  44. canvas.width = w;
  45. canvas.height = h;
  46.  
  47. return {
  48. canvas : canvas,
  49. imageCORS: imageCORS
  50. };
  51. }
  52.  
  53. /**
  54. * @param {HTMLImageElement} image
  55. */
  56. function modifyImagePixels(image) {
  57. var created = getCanvasAndCORSImage(image);
  58. created.imageCORS.onload = function(){
  59. applyFloodFill(image, created.imageCORS, created.canvas);
  60. };
  61. created.imageCORS.src = image.src;
  62.  
  63. // Apply filter immediately if imageCORS is loaded from cache
  64. // and doesn't fire the load event.
  65. if (isImageLoaded(created.imageCORS)) {
  66. applyFloodFill(image, created.imageCORS, created.canvas);
  67. }
  68. }
  69.  
  70. /**
  71. * @param {number} x
  72. * @param {number} y
  73. * @param {CanvasRenderingContext2D} context
  74. * @param {number} tolerance
  75. */
  76. function floodFill(x, y, context, tolerance) {
  77. var pixelStack = [[x, y]];
  78. var width = context.canvas.width;
  79. var height = context.canvas.height;
  80. var pixelPos = (y * width + x) * 4;
  81. var imageData = context.getImageData(0, 0, width, height);
  82.  
  83. var rMax = imageData.data[pixelPos] * (1 + tolerance);
  84. var gMax = imageData.data[pixelPos + 1] * (1 + tolerance);
  85. var bMax = imageData.data[pixelPos + 2] * (1 + tolerance);
  86.  
  87. var rMin = imageData.data[pixelPos] * (1 - tolerance);
  88. var gMin = imageData.data[pixelPos + 1] * (1 - tolerance);
  89. var bMin = imageData.data[pixelPos + 2] * (1 - tolerance);
  90.  
  91. /**
  92. * Returns true if within tolerance bounds of flood-fill origin.
  93. * @param pixelIndex
  94. * @returns {boolean}
  95. */
  96. function matchTolerance(pixelIndex) {
  97. var r = imageData.data[pixelIndex];
  98. var g = imageData.data[pixelIndex + 1];
  99. var b = imageData.data[pixelIndex + 2];
  100.  
  101. return (r >= rMin && r <= rMax) && (g >= gMin && g <= gMax) && (b >= bMin && b <= bMax);
  102. }
  103.  
  104. while (pixelStack.length) {
  105. var newPos = pixelStack.pop();
  106. x = newPos[0];
  107. y = newPos[1];
  108. pixelPos = (y * width + x) * 4;
  109. while (y-- >= 0 && matchTolerance(pixelPos)) {
  110. pixelPos -= width * 4;
  111. }
  112. pixelPos += width * 4;
  113. ++y;
  114. var reachLeft = false;
  115. var reachRight = false;
  116. while (y++ < height - 1 && matchTolerance(pixelPos)) {
  117. imageData.data[pixelPos] = 0;
  118. imageData.data[pixelPos + 1] = 0;
  119. imageData.data[pixelPos + 2] = 0;
  120. imageData.data[pixelPos + 3] = 0;
  121.  
  122. if (x > 0) {
  123. if (matchTolerance(pixelPos - 4)) {
  124. if (!reachLeft) {
  125. pixelStack.push([x - 1, y]);
  126. reachLeft = true;
  127. }
  128. }
  129. else if (reachLeft) {
  130. reachLeft = false;
  131. }
  132. }
  133. if (x < width - 1) {
  134. if (matchTolerance(pixelPos + 4)) {
  135. if (!reachRight) {
  136. pixelStack.push([x + 1, y]);
  137. reachRight = true;
  138. }
  139. }
  140. else if (matchTolerance(pixelPos + 4 - (width * 4))) {
  141. if (!reachLeft) {
  142. pixelStack.push([x + 1, y - 1]);
  143. reachLeft = true;
  144. }
  145. }
  146. else if (reachRight) {
  147. reachRight = false;
  148. }
  149. }
  150. pixelPos += width * 4;
  151. }
  152. }
  153. context.putImageData(imageData, 0, 0);
  154. }
  155.  
  156. /**
  157. * Flood-fill from (0,0).
  158. * @param {HTMLImageElement} originalImage
  159. * @param {HTMLImageElement} imageCORS
  160. * @param {HTMLCanvasElement} canvas
  161. */
  162. function applyFloodFill(originalImage, imageCORS, canvas) {
  163. var ctx = canvas.getContext("2d");
  164. ctx.drawImage(imageCORS, 0, 0);
  165. floodFill(0, 0, ctx, _toleranceValue);
  166. if (originalImage.parentNode) {
  167. originalImage.parentNode.insertBefore(canvas, originalImage);
  168. originalImage.parentNode.removeChild(originalImage);
  169. }
  170. }
  171.  
  172. function translucifyOneFloodFill(image) {
  173. if (isImageLoaded(image)) {
  174. modifyImagePixels(image);
  175. } else {
  176. image.onload = function(){
  177. modifyImagePixels(image);
  178. };
  179. }
  180. }
  181.  
  182. /**
  183. * Translucifies one HTMLImageElement or a set of them via NodeList.
  184. * Specifies which modifier to use.
  185. * @param {HTMLImageElement | NodeList | jQuery} queryResult
  186. * @param {number} [tolerance]
  187. */
  188. function translucifyAll(queryResult, tolerance) {
  189. if (typeof tolerance !== 'undefined') {
  190. _toleranceValue = tolerance;
  191. } else {
  192. _toleranceValue = DEFAULT_TOLERANCE_VALUE;
  193. }
  194.  
  195. if (queryResult instanceof HTMLImageElement) {
  196. // <img> passed in directly
  197. translucifyOneFloodFill(queryResult);
  198.  
  199. } else if (queryResult instanceof NodeList) {
  200. // document.querySelectorAll support
  201. Array.prototype.slice.call(queryResult).forEach(translucifyOneFloodFill);
  202.  
  203. } else {
  204. // jQuery object support
  205. if (queryResult && queryResult.toArray) {
  206. queryResult.toArray().forEach(translucifyOneFloodFill);
  207. }
  208. }
  209. }
  210.  
  211.  
  212. if (typeof module !== 'undefined' && module.exports) {
  213. module.exports = translucifyAll;
  214.  
  215. } else if (typeof define === 'function' && define.amd) {
  216. define(function () {
  217. return translucifyAll;
  218. });
  219.  
  220. } else {
  221. window.translucify = translucifyAll;
  222. }
  223. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement