PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/editor/tools/flood-fill.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 17:00:42 UTC 2,822 bytes, 72 lines download raw bytes

1/**
2 * Iterative 4-connected flood fill on RGBA pixel data.
3 *
4 * Pure function — takes the source pixel array + seed + tolerance and
5 * returns a mask canvas with white where the fill landed. The legacy
6 * gallery editor's magic-wand tool delegates to this.
7 *
8 * @param {Uint8ClampedArray|Uint8Array} src   RGBA bytes (length = w*h*4).
9 * @param {number} w                           Pixel width.
10 * @param {number} h                           Pixel height.
11 * @param {number} seedX                       Floored seed X.
12 * @param {number} seedY                       Floored seed Y.
13 * @param {number} tolerance                   Tolerance 0..100. Internally
14 *                                             squared and scaled to RGB+A
15 *                                             space (max ≈ 195k at 100).
16 * @returns {HTMLCanvasElement|null}           A `w × h` mask canvas with
17 *                                             white-opaque pixels for
18 *                                             visited cells, or null if
19 *                                             the seed is out of bounds.
20 */
21export function floodFillMask(src, w, h, seedX, seedY, tolerance) {
22  if (seedX < 0 || seedY < 0 || seedX >= w || seedY >= h) return null;
23
24  const seedIdx = (seedY * w + seedX) * 4;
25  const sr = src[seedIdx], sg = src[seedIdx + 1];
26  const sb = src[seedIdx + 2], sa = src[seedIdx + 3];
27
28  // 0..100 → squared RGB+A distance threshold. Max single-channel diff
29  // is 255, so sqrt(4 * 255²) ≈ 510; squared cap ≈ 195k at tol = 100.
30  const tol = Math.pow(tolerance * 4.42, 2);
31
32  const visited = new Uint8Array(w * h);
33  const stack = [seedX, seedY];
34  visited[seedY * w + seedX] = 1;
35  while (stack.length) {
36    const y = stack.pop();
37    const x = stack.pop();
38    const nbrs = [
39      [x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1],
40    ];
41    for (const [nx, ny] of nbrs) {
42      if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
43      const idx = ny * w + nx;
44      if (visited[idx]) continue;
45      const o = idx * 4;
46      const dr = src[o] - sr, dg = src[o + 1] - sg;
47      const db = src[o + 2] - sb, da = src[o + 3] - sa;
48      // RGB + alpha-aware so a click on a transparent pixel selects
49      // the transparent region cleanly.
50      if (dr * dr + dg * dg + db * db + da * da <= tol) {
51        visited[idx] = 1;
52        stack.push(nx, ny);
53      }
54    }
55  }
56
57  const mask = document.createElement('canvas');
58  mask.width = w;
59  mask.height = h;
60  const mCtx = mask.getContext('2d');
61  const mData = mCtx.createImageData(w, h);
62  for (let i = 0; i < w * h; i++) {
63    if (visited[i]) {
64      mData.data[i * 4]     = 255;
65      mData.data[i * 4 + 1] = 255;
66      mData.data[i * 4 + 2] = 255;
67      mData.data[i * 4 + 3] = 255;
68    }
69  }
70  mCtx.putImageData(mData, 0, 0);
71  return mask;
72}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.