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.