How can I perform flood fill with HTML Canvas?

前端 未结 3 1528
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-27 20:56

Has anyone implemented a flood fill algorithm in javascript for use with HTML Canvas?

My requirements are simple: flood with a single color starting from a single po

3条回答
  •  误落风尘
    2020-11-27 21:53

    Here's an implementation that I've been working on. It can get really slow if the replacement color is too close to the original color. It's quite a bit faster in Chrome than Firefox (I haven't tested it in any other browsers).

    I also haven't done exhaustive testing yet, so there may be edge cases where it doesn't work.

    function getPixel(pixelData, x, y) {
        if (x < 0 || y < 0 || x >= pixelData.width || y >= pixelData.height) {
            return NaN;
        }
        var pixels = pixelData.data;
        var i = (y * pixelData.width + x) * 4;
        return ((pixels[i + 0] & 0xFF) << 24) |
               ((pixels[i + 1] & 0xFF) << 16) |
               ((pixels[i + 2] & 0xFF) <<  8) |
               ((pixels[i + 3] & 0xFF) <<  0);
    }
    
    function setPixel(pixelData, x, y, color) {
        var i = (y * pixelData.width + x) * 4;
        var pixels = pixelData.data;
        pixels[i + 0] = (color >>> 24) & 0xFF;
        pixels[i + 1] = (color >>> 16) & 0xFF;
        pixels[i + 2] = (color >>>  8) & 0xFF;
        pixels[i + 3] = (color >>>  0) & 0xFF;
    }
    
    function diff(c1, c2) {
        if (isNaN(c1) || isNaN(c2)) {
            return Infinity;
        }
    
        var dr = ((c1 >>> 24) & 0xFF) - ((c2 >>> 24) & 0xFF);
        var dg = ((c1 >>> 16) & 0xFF) - ((c2 >>> 16) & 0xFF);
        var db = ((c1 >>>  8) & 0xFF) - ((c2 >>>  8) & 0xFF);
        var da = ((c1 >>>  0) & 0xFF) - ((c2 >>>  0) & 0xFF);
    
        return dr*dr + dg*dg + db*db + da*da;
    }
    
    function floodFill(canvas, x, y, replacementColor, delta) {
        var current, w, e, stack, color, cx, cy;
        var context = canvas.getContext("2d");
        var pixelData = context.getImageData(0, 0, canvas.width, canvas.height);
        var done = [];
        for (var i = 0; i < canvas.width; i++) {
            done[i] = [];
        }
    
        var targetColor = getPixel(pixelData, x, y);
        delta *= delta;
    
        stack = [ [x, y] ];
        done[x][y] = true;
        while ((current = stack.pop())) {
            cx = current[0];
            cy = current[1];
    
            if (diff(getPixel(pixelData, cx, cy), targetColor) <= delta) {
                setPixel(pixelData, cx, cy, replacementColor);
    
                w = e = cx;
                while (w > 0 && diff(getPixel(pixelData, w - 1, cy), targetColor) <= delta) {
                    --w;
                    if (done[w][cy]) break;
                    setPixel(pixelData, w, cy, replacementColor);
                }
                while (e < pixelData.width - 1 && diff(getPixel(pixelData, e + 1, cy), targetColor) <= delta) {
                    ++e;
                    if (done[e][cy]) break;
                    setPixel(pixelData, e, cy, replacementColor);
                }
    
                for (cx = w; cx <= e; cx++) {
                    if (cy > 0) {
                        color = getPixel(pixelData, cx, cy - 1);
                        if (diff(color, targetColor) <= delta) {
                            if (!done[cx][cy - 1]) {
                                stack.push([cx, cy - 1]);
                                done[cx][cy - 1] = true;
                            }
                        }
                    }
                    if (cy < canvas.height - 1) {
                        color = getPixel(pixelData, cx, cy + 1);
                        if (diff(color, targetColor) <= delta) {
                            if (!done[cx][cy + 1]) {
                                stack.push([cx, cy + 1]);
                                done[cx][cy + 1] = true;
                            }
                        }
                    }
                }
            }
        }
    
        context.putImageData(pixelData, 0, 0, 0, 0, canvas.width, canvas.height);
    }
    

提交回复
热议问题