export function blur(values, r) { if (!((r = +r) >= 0)) throw new RangeError("invalid r"); let length = values.length; if (!((length = Math.floor(length)) >= 0)) throw new RangeError("invalid length"); if (!length || !r) return values; const blur = blurf(r); const temp = values.slice(); blur(values, temp, 0, length, 1); blur(temp, values, 0, length, 1); blur(values, temp, 0, length, 1); return values; } export const blur2 = Blur2(blurf); export const blurImage = Blur2(blurfImage); function Blur2(blur) { return function(data, rx, ry = rx) { if (!((rx = +rx) >= 0)) throw new RangeError("invalid rx"); if (!((ry = +ry) >= 0)) throw new RangeError("invalid ry"); let {data: values, width, height} = data; if (!((width = Math.floor(width)) >= 0)) throw new RangeError("invalid width"); if (!((height = Math.floor(height !== undefined ? height : values.length / width)) >= 0)) throw new RangeError("invalid height"); if (!width || !height || (!rx && !ry)) return data; const blurx = rx && blur(rx); const blury = ry && blur(ry); const temp = values.slice(); if (blurx && blury) { blurh(blurx, temp, values, width, height); blurh(blurx, values, temp, width, height); blurh(blurx, temp, values, width, height); blurv(blury, values, temp, width, height); blurv(blury, temp, values, width, height); blurv(blury, values, temp, width, height); } else if (blurx) { blurh(blurx, values, temp, width, height); blurh(blurx, temp, values, width, height); blurh(blurx, values, temp, width, height); } else if (blury) { blurv(blury, values, temp, width, height); blurv(blury, temp, values, width, height); blurv(blury, values, temp, width, height); } return data; }; } function blurh(blur, T, S, w, h) { for (let y = 0, n = w * h; y < n;) { blur(T, S, y, y += w, 1); } } function blurv(blur, T, S, w, h) { for (let x = 0, n = w * h; x < w; ++x) { blur(T, S, x, x + n, w); } } function blurfImage(radius) { const blur = blurf(radius); return (T, S, start, stop, step) => { start <<= 2, stop <<= 2, step <<= 2; blur(T, S, start + 0, stop + 0, step); blur(T, S, start + 1, stop + 1, step); blur(T, S, start + 2, stop + 2, step); blur(T, S, start + 3, stop + 3, step); }; } // Given a target array T, a source array S, sets each value T[i] to the average // of {S[i - r], …, S[i], …, S[i + r]}, where r = ⌊radius⌋, start <= i < stop, // for each i, i + step, i + 2 * step, etc., and where S[j] is clamped between // S[start] (inclusive) and S[stop] (exclusive). If the given radius is not an // integer, S[i - r - 1] and S[i + r + 1] are added to the sum, each weighted // according to r - ⌊radius⌋. function blurf(radius) { const radius0 = Math.floor(radius); if (radius0 === radius) return bluri(radius); const t = radius - radius0; const w = 2 * radius + 1; return (T, S, start, stop, step) => { // stop must be aligned! if (!((stop -= step) >= start)) return; // inclusive stop let sum = radius0 * S[start]; const s0 = step * radius0; const s1 = s0 + step; for (let i = start, j = start + s0; i < j; i += step) { sum += S[Math.min(stop, i)]; } for (let i = start, j = stop; i <= j; i += step) { sum += S[Math.min(stop, i + s0)]; T[i] = (sum + t * (S[Math.max(start, i - s1)] + S[Math.min(stop, i + s1)])) / w; sum -= S[Math.max(start, i - s0)]; } }; } // Like blurf, but optimized for integer radius. function bluri(radius) { const w = 2 * radius + 1; return (T, S, start, stop, step) => { // stop must be aligned! if (!((stop -= step) >= start)) return; // inclusive stop let sum = radius * S[start]; const s = step * radius; for (let i = start, j = start + s; i < j; i += step) { sum += S[Math.min(stop, i)]; } for (let i = start, j = stop; i <= j; i += step) { sum += S[Math.min(stop, i + s)]; T[i] = sum / w; sum -= S[Math.max(start, i - s)]; } }; }