PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/editor/mask-utils.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 17:00:40 UTC 3,281 bytes, 91 lines download raw bytes

1/**
2 * Mask-canvas helpers used by the inpaint pipeline.
3 *
4 * Pure utility functions — they take a canvas (or layer-shape) as
5 * input and return a fresh canvas, with no module-level state.
6 */
7
8/**
9 * Dilate (positive `px`) or erode (negative `px`) a binary alpha mask.
10 *
11 * Strategy: blur the source by `|px|`, then re-threshold the result.
12 * - Dilation keeps anything with non-trivial blurred alpha (low cutoff).
13 * - Erosion keeps only pixels that retained near-full alpha after blur.
14 *
15 * @param {HTMLCanvasElement} src   Source mask canvas.
16 * @param {number}            px    Pixels to dilate (>0) or erode (<0). 0 = copy.
17 * @returns {HTMLCanvasElement}     Fresh canvas with the same dimensions.
18 */
19export function dilateMask(src, px) {
20  const w = src.width, h = src.height;
21  const tmp = document.createElement('canvas');
22  tmp.width = w; tmp.height = h;
23  const ctx = tmp.getContext('2d');
24  if (px === 0) {
25    ctx.drawImage(src, 0, 0);
26    return tmp;
27  }
28  const dilate = px > 0;
29  const radius = Math.abs(px);
30  ctx.filter = `blur(${radius}px)`;
31  ctx.drawImage(src, 0, 0);
32  ctx.filter = 'none';
33  const img = ctx.getImageData(0, 0, w, h);
34  const threshold = dilate ? 8 : 247;
35  for (let i = 0; i < img.data.length; i += 4) {
36    const a = img.data[i + 3];
37    const keep = dilate ? a > threshold : a >= threshold;
38    if (keep) {
39      img.data[i] = img.data[i + 1] = img.data[i + 2] = 255;
40      img.data[i + 3] = 255;
41    } else {
42      img.data[i + 3] = 0;
43    }
44  }
45  ctx.putImageData(img, 0, 0);
46  return tmp;
47}
48
49
50/**
51 * Re-derive an inpaint-result layer's alpha from its cached AI image +
52 * the hard mask, applying a feather + optional dilate/erode of the
53 * boundary. Mutates `layer.canvas` in place via `layer.ctx`.
54 *
55 * The layer must carry an `inpaintSource = { ai, mask }` cache from the
56 * original inpaint call so we can re-shape the alpha cheaply (no
57 * second model call required).
58 *
59 * @param {{canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D,
60 *          inpaintSource?: {ai: CanvasImageSource, mask: HTMLCanvasElement}}} layer
61 * @param {number} featherPx     Gaussian blur radius applied to the mask alpha.
62 * @param {number} [edgeShiftPx] Dilate (+) or erode (-) the mask before blurring.
63 */
64export function applyInpaintFeather(layer, featherPx, edgeShiftPx = 0) {
65  if (!layer || !layer.inpaintSource) return;
66  const { ai, mask } = layer.inpaintSource;
67  const w = layer.canvas.width;
68  const h = layer.canvas.height;
69  // 1) Optional dilate/erode, then optional blur, into a fresh mask.
70  let shaped = mask;
71  if (edgeShiftPx !== 0) shaped = dilateMask(mask, edgeShiftPx);
72  const softMask = document.createElement('canvas');
73  softMask.width = w; softMask.height = h;
74  const smCtx = softMask.getContext('2d');
75  if (featherPx > 0) {
76    smCtx.filter = `blur(${featherPx}px)`;
77    smCtx.drawImage(shaped, 0, 0, w, h);
78    smCtx.filter = 'none';
79  } else {
80    smCtx.drawImage(shaped, 0, 0, w, h);
81  }
82  // 2) Draw the AI image fresh, then multiply alpha by the soft mask.
83  const ctx = layer.ctx;
84  ctx.save();
85  ctx.globalCompositeOperation = 'source-over';
86  ctx.clearRect(0, 0, w, h);
87  ctx.drawImage(ai, 0, 0);
88  ctx.globalCompositeOperation = 'destination-in';
89  ctx.drawImage(softMask, 0, 0);
90  ctx.restore();
91}

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.