Public
Edited
Nov 9, 2022
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
buffered = jumpFloodBuffer(input.pixels.slice(), ...shape, radius, feather)
Insert cell
blurred = stackblur(buffered.slice(), ...shape, blurRadius)
Insert cell
drawResult = {
const imageData = input.resultCtx.getImageData(0, 0, ...shape);
imageData.data.set(toImage(blurred, ...shape));
input.resultCtx.putImageData(imageData, 0, 0);
}
Insert cell
Insert cell
function jumpFloodBuffer(pixels, w, h, radius = 0, feather = 0) {
const t0 = performance.now();
const n = w * h;
const pixelsIn = pixels.slice();

// initialize a grid that keeps track of the closest data point for each pixel
const EMPTY = new Uint32Array([-1])[0];
const closest = new Uint32Array(w * h);

// init data points as closest to themselves
for (let i = 0; i < n; i++) {
closest[i] = pixels[i] > 128 ? i : EMPTY;
}

// starting flood step is the half of the grid size's closest power of two
const maxRadius = radius + feather * 0.5;
const maxStep =
2 ** Math.ceil(Math.log2(Math.min(maxRadius / 2, Math.max(w, h))));

//if (false) pass(1); // an optional 1-step pass that increases precision

// run passes at e.g. 512, 256, 128, ..., 1
for (let step = maxStep; step >= 1; step >>= 1) {
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const k = x + w * y;
let bestSeed = EMPTY;

for (let i = -step, bestDist = Infinity; i <= step; i += step) {
const y0 = y + i;
if (y0 < 0 || y0 >= h) continue;
for (let j = -step; j <= step; j += step) {
const x0 = x + j;
if (x0 < 0 || x0 >= w) continue;

const m = x0 + w * y0;
if (pixels[m] === 0) continue;

const seed = closest[m];
const dx = x - (seed % w);
const dy = y - ((seed / w) | 0);
const d = dx * dx + dy * dy;
if (d < bestDist) {
bestDist = d;
bestSeed = seed;
}
}
}
if (bestSeed === EMPTY) continue;
closest[k] = bestSeed;
pixels[k] = pixels[bestSeed];
}
}
}

// Turn our computation of the closest raster pixel index into a floating point distance
const f = Math.max(feather, 1e-8);
const a = radius - f * 0.5;
const b = radius + f * 0.5;
for (let y0 = 0; y0 < h; y0++) {
for (let x0 = 0; x0 < w; x0++) {
const idx = x0 + w * y0;
const closestIdx = closest[idx];
if (closestIdx === EMPTY) {
pixels[idx] = 0;
continue;
}
const dx = x0 - (closestIdx % w);
const dy = y0 - ((closestIdx / w) | 0);
const dist = Math.sqrt(dx * dx + dy * dy);
pixels[idx] = 255 * linearstep(b, a, dist);
}
}
const t1 = performance.now();
mutable jumpFloodMs = Math.round(10 * (t1 - t0)) * 0.1;

return pixels;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more