Advertisement
Guest User

Untitled

a guest
Jun 19th, 2019
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.20 KB | None | 0 0
  1. var r1, r2; // red values
  2. var g1, g2; // green values
  3. var b1, b2; // blue values
  4. var actualColorDelta = Math.sqrt((r1 - r2)*(r1 - r2) + (g1 - g2)*(g1 - g2) + (b1 - b2)*(b1 - b2))
  5.  
  6. function floodFill(canvas, x, y, fillColor, borderColorDelta) {
  7. ...
  8. }
  9.  
  10. var ImageProcessing;
  11.  
  12. ImageProcessing = {
  13.  
  14. /* Convert HTML color (e.g. "#rrggbb" or "#rrggbbaa") to object with properties r, g, b, a.
  15. * If no alpha value is given, 255 (0xff) will be assumed.
  16. */
  17. toRGB: function (color) {
  18. var r, g, b, a, html;
  19. html = color;
  20.  
  21. // Parse out the RGBA values from the HTML Code
  22. if (html.substring(0, 1) === "#")
  23. {
  24. html = html.substring(1);
  25. }
  26.  
  27. if (html.length === 3 || html.length === 4)
  28. {
  29. r = html.substring(0, 1);
  30. r = r + r;
  31.  
  32. g = html.substring(1, 2);
  33. g = g + g;
  34.  
  35. b = html.substring(2, 3);
  36. b = b + b;
  37.  
  38. if (html.length === 4) {
  39. a = html.substring(3, 4);
  40. a = a + a;
  41. }
  42. else {
  43. a = "ff";
  44. }
  45. }
  46. else if (html.length === 6 || html.length === 8)
  47. {
  48. r = html.substring(0, 2);
  49. g = html.substring(2, 4);
  50. b = html.substring(4, 6);
  51. a = html.length === 6 ? "ff" : html.substring(6, 8);
  52. }
  53.  
  54. // Convert from Hex (Hexidecimal) to Decimal
  55. r = parseInt(r, 16);
  56. g = parseInt(g, 16);
  57. b = parseInt(b, 16);
  58. a = parseInt(a, 16);
  59. return {r: r, g: g, b: b, a: a};
  60. },
  61.  
  62. /* Get the color at the given x,y location from the pixels array, assuming the array has a width and height as given.
  63. * This interprets the 1-D array as a 2-D array.
  64. *
  65. * If useColor is defined, its values will be set. This saves on object creation.
  66. */
  67. getColor: function (pixels, x, y, width, height, useColor) {
  68. var redIndex = y * width * 4 + x * 4;
  69. if (useColor === undefined) {
  70. useColor = { r: pixels[redIndex], g: pixels[redIndex + 1], b: pixels[redIndex + 2], a: pixels[redIndex + 3] };
  71. }
  72. else {
  73. useColor.r = pixels[redIndex];
  74. useColor.g = pixels[redIndex + 1]
  75. useColor.b = pixels[redIndex + 2];
  76. useColor.a = pixels[redIndex + 3];
  77. }
  78. return useColor;
  79. },
  80.  
  81. setColor: function (pixels, x, y, width, height, color) {
  82. var redIndex = y * width * 4 + x * 4;
  83. pixels[redIndex] = color.r;
  84. pixels[redIndex + 1] = color.g,
  85. pixels[redIndex + 2] = color.b;
  86. pixels[redIndex + 3] = color.a;
  87. },
  88.  
  89. /*
  90. * fill: Flood a canvas with the given fill color.
  91. *
  92. * Returns a rectangle { x, y, width, height } that defines the maximum extent of the pixels that were changed.
  93. *
  94. * canvas .................... Canvas to modify.
  95. * fillColor ................. RGBA Color to fill with.
  96. * This may be a string ("#rrggbbaa") or an object of the form { r: red, g: green, b: blue, a: alpha }.
  97. * x, y ...................... Coordinates of seed point to start flooding.
  98. * bounds .................... Restrict flooding to this rectangular region of canvas.
  99. * This object has these attributes: { x, y, width, height }.
  100. * If undefined or null, use the whole of the canvas.
  101. * stopFunction .............. Function that decides if a pixel is a boundary that should cause
  102. * flooding to stop. If omitted, any pixel that differs from seedColor
  103. * will cause flooding to stop. seedColor is the color under the seed point (x,y).
  104. * Parameters: stopFunction(fillColor, seedColor, pixelColor).
  105. * Returns true if flooding shoud stop.
  106. * The colors are objects of the form { r: red, g: green, b: blue, a: alpha }
  107. */
  108. fill: function (canvas, fillColor, x, y, bounds, stopFunction) {
  109. // Supply default values if necessary.
  110. var ctx, minChangedX, minChangedY, maxChangedX, maxChangedY, wasTested, shouldTest, imageData, pixels, currentX, currentY, currentColor, currentIndex, seedColor, tryX, tryY, tryIndex, boundsWidth, boundsHeight, pixelStart, fillRed, fillGreen, fillBlue, fillAlpha;
  111. if (Object.isString(fillColor)) {
  112. fillColor = ImageProcessing.toRGB(fillColor);
  113. }
  114. x = Math.round(x);
  115. y = Math.round(y);
  116. if (bounds === null || bounds === undefined) {
  117. bounds = { x: 0, y: 0, width: canvas.width, height: canvas.height };
  118. }
  119. else {
  120. bounds = { x: Math.round(bounds.x), y: Math.round(bounds.y), width: Math.round(bounds.y), height: Math.round(bounds.height) };
  121. }
  122. if (stopFunction === null || stopFunction === undefined) {
  123. stopFunction = new function (fillColor, seedColor, pixelColor) {
  124. return pixelColor.r != seedColor.r || pixelColor.g != seedColor.g || pixelColor.b != seedColor.b || pixelColor.a != seedColor.a;
  125. }
  126. }
  127. minChangedX = maxChangedX = x - bounds.x;
  128. minChangedY = maxChangedY = y - bounds.y;
  129. boundsWidth = bounds.width;
  130. boundsHeight = bounds.height;
  131.  
  132. // Initialize wasTested to false. As we check each pixel to decide if it should be painted with the new color,
  133. // we will mark it with a true value at wasTested[row = y][column = x];
  134. wasTested = new Array(boundsHeight * boundsWidth);
  135. /*
  136. $R(0, bounds.height - 1).each(function (row) {
  137. var subArray = new Array(bounds.width);
  138. wasTested[row] = subArray;
  139. });
  140. */
  141.  
  142. // Start with a single point that we know we should test: (x, y).
  143. // Convert (x,y) to image data coordinates by subtracting the bounds' origin.
  144. currentX = x - bounds.x;
  145. currentY = y - bounds.y;
  146. currentIndex = currentY * boundsWidth + currentX;
  147. shouldTest = [ currentIndex ];
  148.  
  149. ctx = canvas.getContext("2d");
  150. //imageData = ctx.getImageData(bounds.x, bounds.y, bounds.width, bounds.height);
  151. imageData = ImageProcessing.getImageData(ctx, bounds.x, bounds.y, bounds.width, bounds.height);
  152. pixels = imageData.data;
  153. seedColor = ImageProcessing.getColor(pixels, currentX, currentY, boundsWidth, boundsHeight);
  154. currentColor = { r: 0, g: 0, b: 0, a: 1 };
  155. fillRed = fillColor.r;
  156. fillGreen = fillColor.g;
  157. fillBlue = fillColor.b;
  158. fillAlpha = fillColor.a;
  159. while (shouldTest.length > 0) {
  160. currentIndex = shouldTest.pop();
  161. currentX = currentIndex % boundsWidth;
  162. currentY = (currentIndex - currentX) / boundsWidth;
  163. if (! wasTested[currentIndex]) {
  164. wasTested[currentIndex] = true;
  165. //currentColor = ImageProcessing.getColor(pixels, currentX, currentY, boundsWidth, boundsHeight, currentColor);
  166. // Inline getColor for performance.
  167. pixelStart = currentIndex * 4;
  168. currentColor.r = pixels[pixelStart];
  169. currentColor.g = pixels[pixelStart + 1]
  170. currentColor.b = pixels[pixelStart + 2];
  171. currentColor.a = pixels[pixelStart + 3];
  172.  
  173. if (! stopFunction(fillColor, seedColor, currentColor)) {
  174. // Color the pixel with the fill color.
  175. //ImageProcessing.setColor(pixels, currentX, currentY, boundsWidth, boundsHeight, fillColor);
  176. // Inline setColor for performance
  177. pixels[pixelStart] = fillRed;
  178. pixels[pixelStart + 1] = fillGreen;
  179. pixels[pixelStart + 2] = fillBlue;
  180. pixels[pixelStart + 3] = fillAlpha;
  181.  
  182. if (minChangedX < currentX) { minChangedX = currentX; }
  183. else if (maxChangedX > currentX) { maxChangedX = currentX; }
  184. if (minChangedY < currentY) { minChangedY = currentY; }
  185. else if (maxChangedY > currentY) { maxChangedY = currentY; }
  186.  
  187. // Add the adjacent four pixels to the list to be tested, unless they have already been tested.
  188. tryX = currentX - 1;
  189. tryY = currentY;
  190. tryIndex = tryY * boundsWidth + tryX;
  191. if (tryX >= 0 && ! wasTested[tryIndex]) {
  192. shouldTest.push(tryIndex);
  193. }
  194. tryX = currentX;
  195. tryY = currentY + 1;
  196. tryIndex = tryY * boundsWidth + tryX;
  197. if (tryY < boundsHeight && ! wasTested[tryIndex]) {
  198. shouldTest.push(tryIndex);
  199. }
  200. tryX = currentX + 1;
  201. tryY = currentY;
  202. tryIndex = tryY * boundsWidth + tryX;
  203. if (tryX < boundsWidth && ! wasTested[tryIndex]) {
  204. shouldTest.push(tryIndex);
  205. }
  206. tryX = currentX;
  207. tryY = currentY - 1;
  208. tryIndex = tryY * boundsWidth + tryX;
  209. if (tryY >= 0 && ! wasTested[tryIndex]) {
  210. shouldTest.push(tryIndex);
  211. }
  212. }
  213. }
  214. }
  215. //ctx.putImageData(imageData, bounds.x, bounds.y);
  216. ImageProcessing.putImageData(ctx, imageData, bounds.x, bounds.y);
  217.  
  218. return { x: minChangedX + bounds.x, y: minChangedY + bounds.y, width: maxChangedX - minChangedX + 1, height: maxChangedY - minChangedY + 1 };
  219. },
  220.  
  221. getImageData: function (ctx, x, y, w, h) {
  222. return ctx.getImageData(x, y, w, h);
  223. },
  224.  
  225. putImageData: function (ctx, data, x, y) {
  226. ctx.putImageData(data, x, y);
  227. }
  228.  
  229. };
  230.  
  231. stopFill : function (fillColor, seedColor, pixelColor) {
  232. // Ignore alpha difference for now.
  233. return Math.abs(pixelColor.r - seedColor.r) > this.colorTolerance || Math.abs(pixelColor.g - seedColor.g) > this.colorTolerance || Math.abs(pixelColor.b - seedColor.b) > this.colorTolerance;
  234. },
  235.  
  236. function getPixel(pixelData, x, y) {
  237. if (x < 0 || y < 0 || x >= pixelData.width || y >= pixelData.height) {
  238. return NaN;
  239. }
  240. var pixels = pixelData.data;
  241. var i = (y * pixelData.width + x) * 4;
  242. return ((pixels[i + 0] & 0xFF) << 24) |
  243. ((pixels[i + 1] & 0xFF) << 16) |
  244. ((pixels[i + 2] & 0xFF) << 8) |
  245. ((pixels[i + 3] & 0xFF) << 0);
  246. }
  247.  
  248. function setPixel(pixelData, x, y, color) {
  249. var i = (y * pixelData.width + x) * 4;
  250. var pixels = pixelData.data;
  251. pixels[i + 0] = (color >>> 24) & 0xFF;
  252. pixels[i + 1] = (color >>> 16) & 0xFF;
  253. pixels[i + 2] = (color >>> 8) & 0xFF;
  254. pixels[i + 3] = (color >>> 0) & 0xFF;
  255. }
  256.  
  257. function diff(c1, c2) {
  258. if (isNaN(c1) || isNaN(c2)) {
  259. return Infinity;
  260. }
  261.  
  262. var dr = ((c1 >>> 24) & 0xFF) - ((c2 >>> 24) & 0xFF);
  263. var dg = ((c1 >>> 16) & 0xFF) - ((c2 >>> 16) & 0xFF);
  264. var db = ((c1 >>> 8) & 0xFF) - ((c2 >>> 8) & 0xFF);
  265. var da = ((c1 >>> 0) & 0xFF) - ((c2 >>> 0) & 0xFF);
  266.  
  267. return dr*dr + dg*dg + db*db + da*da;
  268. }
  269.  
  270. function floodFill(canvas, x, y, replacementColor, delta) {
  271. var current, w, e, stack, color, cx, cy;
  272. var context = canvas.getContext("2d");
  273. var pixelData = context.getImageData(0, 0, canvas.width, canvas.height);
  274. var done = [];
  275. for (var i = 0; i < canvas.width; i++) {
  276. done[i] = [];
  277. }
  278.  
  279. var targetColor = getPixel(pixelData, x, y);
  280. delta *= delta;
  281.  
  282. stack = [ [x, y] ];
  283. done[x][y] = true;
  284. while ((current = stack.pop())) {
  285. cx = current[0];
  286. cy = current[1];
  287.  
  288. if (diff(getPixel(pixelData, cx, cy), targetColor) <= delta) {
  289. setPixel(pixelData, cx, cy, replacementColor);
  290.  
  291. w = e = cx;
  292. while (w > 0 && diff(getPixel(pixelData, w - 1, cy), targetColor) <= delta) {
  293. --w;
  294. if (done[w][cy]) break;
  295. setPixel(pixelData, w, cy, replacementColor);
  296. }
  297. while (e < pixelData.width - 1 && diff(getPixel(pixelData, e + 1, cy), targetColor) <= delta) {
  298. ++e;
  299. if (done[e][cy]) break;
  300. setPixel(pixelData, e, cy, replacementColor);
  301. }
  302.  
  303. for (cx = w; cx <= e; cx++) {
  304. if (cy > 0) {
  305. color = getPixel(pixelData, cx, cy - 1);
  306. if (diff(color, targetColor) <= delta) {
  307. if (!done[cx][cy - 1]) {
  308. stack.push([cx, cy - 1]);
  309. done[cx][cy - 1] = true;
  310. }
  311. }
  312. }
  313. if (cy < canvas.height - 1) {
  314. color = getPixel(pixelData, cx, cy + 1);
  315. if (diff(color, targetColor) <= delta) {
  316. if (!done[cx][cy + 1]) {
  317. stack.push([cx, cy + 1]);
  318. done[cx][cy + 1] = true;
  319. }
  320. }
  321. }
  322. }
  323. }
  324. }
  325.  
  326. context.putImageData(pixelData, 0, 0, 0, 0, canvas.width, canvas.height);
  327. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement