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.