PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/editor/filters/blur.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 17:00:49 UTC 4,539 bytes, 118 lines download raw bytes

1/**
2 * Pure blur renderers shared by the editor's live-preview popups.
3 *
4 * Each export matches the `renderer(snap, params, dst)` signature
5 * expected by `_applyLiveBlur` in galleryEditor.js — `snap` is the
6 * pre-blur snapshot canvas, `params` is the slider values object, and
7 * `dst` is the 2D context to draw the final result into. No module
8 * state.
9 */
10
11/**
12 * Gaussian blur with clamp-to-edge sampling.
13 *
14 * Canvas `filter: blur()` naively blends with TRANSPARENT pixels outside
15 * the image which fades the borders out. To match Photoshop's
16 * "Edge: Clamp" Gaussian we pad the source onto a larger buffer with
17 * the edge pixels stretched into the margin (4 strips + 4 corners),
18 * blur the padded buffer, then copy only the original-size centre back.
19 *
20 * @param {HTMLCanvasElement} snap
21 * @param {{ radius: number }} v
22 * @param {CanvasRenderingContext2D} dst
23 */
24export function gaussianBlur(snap, v, dst) {
25  if (!v.radius || v.radius <= 0) { dst.drawImage(snap, 0, 0); return; }
26  const r = v.radius;
27  const w = snap.width, h = snap.height;
28  // Margin needs to cover the kernel's effective reach — most
29  // engines saturate within ~2× the radius.
30  const m = Math.ceil(r * 2 + 4);
31  const pad = document.createElement('canvas');
32  pad.width = w + m * 2;
33  pad.height = h + m * 2;
34  const pctx = pad.getContext('2d');
35  pctx.drawImage(snap, m, m);
36  // Edge strips: drawImage with src height=1 (or width=1) into a
37  // dst region of size `m` stretches the edge pixels into the
38  // margin — same effect as clamp-to-edge sampling.
39  pctx.drawImage(snap, 0, 0, w, 1, m, 0, w, m);
40  pctx.drawImage(snap, 0, h - 1, w, 1, m, m + h, w, m);
41  pctx.drawImage(snap, 0, 0, 1, h, 0, m, m, h);
42  pctx.drawImage(snap, w - 1, 0, 1, h, m + w, m, m, h);
43  // Corners — stretch the corner pixel into an m×m block.
44  pctx.drawImage(snap, 0, 0, 1, 1, 0, 0, m, m);
45  pctx.drawImage(snap, w - 1, 0, 1, 1, m + w, 0, m, m);
46  pctx.drawImage(snap, 0, h - 1, 1, 1, 0, m + h, m, m);
47  pctx.drawImage(snap, w - 1, h - 1, 1, 1, m + w, m + h, m, m);
48  // Blur the padded buffer and crop the original-size centre back.
49  const out = document.createElement('canvas');
50  out.width = pad.width;
51  out.height = pad.height;
52  const octx = out.getContext('2d');
53  octx.filter = `blur(${r}px)`;
54  octx.drawImage(pad, 0, 0);
55  octx.filter = 'none';
56  dst.drawImage(out, m, m, w, h, 0, 0, w, h);
57}
58
59
60/**
61 * Zoom blur — radial smear from the canvas centre. 16 scaled copies at
62 * low alpha approximate a Gaussian zoom blur.
63 *
64 * @param {HTMLCanvasElement} snap
65 * @param {{ strength: number }} v
66 * @param {CanvasRenderingContext2D} dst
67 */
68export function zoomBlur(snap, v, dst) {
69  const w = snap.width, h = snap.height;
70  const steps = 16;
71  dst.drawImage(snap, 0, 0);
72  dst.globalAlpha = 0.18;
73  for (let s = 1; s <= steps; s++) {
74    const t = s / steps;
75    const scale = 1 + (v.strength / 200) * t;
76    const sw = w * scale, sh = h * scale;
77    dst.drawImage(snap, (w - sw) / 2, (h - sh) / 2, sw, sh);
78  }
79  dst.globalAlpha = 1;
80}
81
82
83/**
84 * Motion blur — directional smear along a user-chosen angle.
85 *
86 * Each shifted stamp is rendered at globalAlpha = 1/steps with
87 * globalCompositeOperation = 'lighter' (additive) into an offscreen
88 * accumulator, then blitted onto `dst`. Lighter adds premultiplied src
89 * to dst, so N stamps each contributing snap.RGB/N sum to snap.RGB and
90 * alpha sums to 1. Source-over blending would cause colour wash-out
91 * because each stamp would blend over the dst instead of summing into
92 * it. Using an accumulator keeps `dst` clean if anything throws mid-way.
93 *
94 * @param {HTMLCanvasElement} snap
95 * @param {{ length: number, angle: number }} v
96 * @param {CanvasRenderingContext2D} dst
97 */
98export function motionBlur(snap, v, dst) {
99  const w = snap.width, h = snap.height;
100  const rad = (v.angle * Math.PI) / 180;
101  const dx = Math.cos(rad);
102  const dy = Math.sin(rad);
103  // Step count = roughly one sample per pixel of length, capped
104  // so very long blurs don't tank performance.
105  const steps = Math.max(4, Math.min(80, Math.round(v.length)));
106  const acc = document.createElement('canvas');
107  acc.width = w; acc.height = h;
108  const actx = acc.getContext('2d');
109  actx.globalCompositeOperation = 'lighter';
110  actx.globalAlpha = 1 / steps;
111  for (let i = 0; i < steps; i++) {
112    const t = (i / Math.max(1, steps - 1)) - 0.5;
113    actx.drawImage(snap, dx * v.length * t, dy * v.length * t);
114  }
115  actx.globalCompositeOperation = 'source-over';
116  actx.globalAlpha = 1;
117  dst.drawImage(acc, 0, 0);
118}

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.