| 1 | export function blur(values, r) {
|
|---|
| 2 | if (!((r = +r) >= 0)) throw new RangeError("invalid r");
|
|---|
| 3 | let length = values.length;
|
|---|
| 4 | if (!((length = Math.floor(length)) >= 0)) throw new RangeError("invalid length");
|
|---|
| 5 | if (!length || !r) return values;
|
|---|
| 6 | const blur = blurf(r);
|
|---|
| 7 | const temp = values.slice();
|
|---|
| 8 | blur(values, temp, 0, length, 1);
|
|---|
| 9 | blur(temp, values, 0, length, 1);
|
|---|
| 10 | blur(values, temp, 0, length, 1);
|
|---|
| 11 | return values;
|
|---|
| 12 | }
|
|---|
| 13 |
|
|---|
| 14 | export const blur2 = Blur2(blurf);
|
|---|
| 15 |
|
|---|
| 16 | export const blurImage = Blur2(blurfImage);
|
|---|
| 17 |
|
|---|
| 18 | function Blur2(blur) {
|
|---|
| 19 | return function(data, rx, ry = rx) {
|
|---|
| 20 | if (!((rx = +rx) >= 0)) throw new RangeError("invalid rx");
|
|---|
| 21 | if (!((ry = +ry) >= 0)) throw new RangeError("invalid ry");
|
|---|
| 22 | let {data: values, width, height} = data;
|
|---|
| 23 | if (!((width = Math.floor(width)) >= 0)) throw new RangeError("invalid width");
|
|---|
| 24 | if (!((height = Math.floor(height !== undefined ? height : values.length / width)) >= 0)) throw new RangeError("invalid height");
|
|---|
| 25 | if (!width || !height || (!rx && !ry)) return data;
|
|---|
| 26 | const blurx = rx && blur(rx);
|
|---|
| 27 | const blury = ry && blur(ry);
|
|---|
| 28 | const temp = values.slice();
|
|---|
| 29 | if (blurx && blury) {
|
|---|
| 30 | blurh(blurx, temp, values, width, height);
|
|---|
| 31 | blurh(blurx, values, temp, width, height);
|
|---|
| 32 | blurh(blurx, temp, values, width, height);
|
|---|
| 33 | blurv(blury, values, temp, width, height);
|
|---|
| 34 | blurv(blury, temp, values, width, height);
|
|---|
| 35 | blurv(blury, values, temp, width, height);
|
|---|
| 36 | } else if (blurx) {
|
|---|
| 37 | blurh(blurx, values, temp, width, height);
|
|---|
| 38 | blurh(blurx, temp, values, width, height);
|
|---|
| 39 | blurh(blurx, values, temp, width, height);
|
|---|
| 40 | } else if (blury) {
|
|---|
| 41 | blurv(blury, values, temp, width, height);
|
|---|
| 42 | blurv(blury, temp, values, width, height);
|
|---|
| 43 | blurv(blury, values, temp, width, height);
|
|---|
| 44 | }
|
|---|
| 45 | return data;
|
|---|
| 46 | };
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | function blurh(blur, T, S, w, h) {
|
|---|
| 50 | for (let y = 0, n = w * h; y < n;) {
|
|---|
| 51 | blur(T, S, y, y += w, 1);
|
|---|
| 52 | }
|
|---|
| 53 | }
|
|---|
| 54 |
|
|---|
| 55 | function blurv(blur, T, S, w, h) {
|
|---|
| 56 | for (let x = 0, n = w * h; x < w; ++x) {
|
|---|
| 57 | blur(T, S, x, x + n, w);
|
|---|
| 58 | }
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | function blurfImage(radius) {
|
|---|
| 62 | const blur = blurf(radius);
|
|---|
| 63 | return (T, S, start, stop, step) => {
|
|---|
| 64 | start <<= 2, stop <<= 2, step <<= 2;
|
|---|
| 65 | blur(T, S, start + 0, stop + 0, step);
|
|---|
| 66 | blur(T, S, start + 1, stop + 1, step);
|
|---|
| 67 | blur(T, S, start + 2, stop + 2, step);
|
|---|
| 68 | blur(T, S, start + 3, stop + 3, step);
|
|---|
| 69 | };
|
|---|
| 70 | }
|
|---|
| 71 |
|
|---|
| 72 | // Given a target array T, a source array S, sets each value T[i] to the average
|
|---|
| 73 | // of {S[i - r], …, S[i], …, S[i + r]}, where r = ⌊radius⌋, start <= i < stop,
|
|---|
| 74 | // for each i, i + step, i + 2 * step, etc., and where S[j] is clamped between
|
|---|
| 75 | // S[start] (inclusive) and S[stop] (exclusive). If the given radius is not an
|
|---|
| 76 | // integer, S[i - r - 1] and S[i + r + 1] are added to the sum, each weighted
|
|---|
| 77 | // according to r - ⌊radius⌋.
|
|---|
| 78 | function blurf(radius) {
|
|---|
| 79 | const radius0 = Math.floor(radius);
|
|---|
| 80 | if (radius0 === radius) return bluri(radius);
|
|---|
| 81 | const t = radius - radius0;
|
|---|
| 82 | const w = 2 * radius + 1;
|
|---|
| 83 | return (T, S, start, stop, step) => { // stop must be aligned!
|
|---|
| 84 | if (!((stop -= step) >= start)) return; // inclusive stop
|
|---|
| 85 | let sum = radius0 * S[start];
|
|---|
| 86 | const s0 = step * radius0;
|
|---|
| 87 | const s1 = s0 + step;
|
|---|
| 88 | for (let i = start, j = start + s0; i < j; i += step) {
|
|---|
| 89 | sum += S[Math.min(stop, i)];
|
|---|
| 90 | }
|
|---|
| 91 | for (let i = start, j = stop; i <= j; i += step) {
|
|---|
| 92 | sum += S[Math.min(stop, i + s0)];
|
|---|
| 93 | T[i] = (sum + t * (S[Math.max(start, i - s1)] + S[Math.min(stop, i + s1)])) / w;
|
|---|
| 94 | sum -= S[Math.max(start, i - s0)];
|
|---|
| 95 | }
|
|---|
| 96 | };
|
|---|
| 97 | }
|
|---|
| 98 |
|
|---|
| 99 | // Like blurf, but optimized for integer radius.
|
|---|
| 100 | function bluri(radius) {
|
|---|
| 101 | const w = 2 * radius + 1;
|
|---|
| 102 | return (T, S, start, stop, step) => { // stop must be aligned!
|
|---|
| 103 | if (!((stop -= step) >= start)) return; // inclusive stop
|
|---|
| 104 | let sum = radius * S[start];
|
|---|
| 105 | const s = step * radius;
|
|---|
| 106 | for (let i = start, j = start + s; i < j; i += step) {
|
|---|
| 107 | sum += S[Math.min(stop, i)];
|
|---|
| 108 | }
|
|---|
| 109 | for (let i = start, j = stop; i <= j; i += step) {
|
|---|
| 110 | sum += S[Math.min(stop, i + s)];
|
|---|
| 111 | T[i] = sum / w;
|
|---|
| 112 | sum -= S[Math.max(start, i - s)];
|
|---|
| 113 | }
|
|---|
| 114 | };
|
|---|
| 115 | }
|
|---|