1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】Bucket Fill Algorithm【Canvas】

Last updated at Posted at 2020-11-10

#はじめに
古いプログラムを漁っていたら、Bucket Fill Algorithmのプログラムが見つかったので、自分用に残しておきます。
確か効率の良い凹塗りつぶしアルゴリズムであったと記憶しております。
元は自分が書いたものではありません。
元がES5で書かれていたので、ES6で書き直して、不具合を1件直して、コメントを追記しております。

下の画像を見ると若干塗り残しがあるように見えますが、
これは塗り残しではなく、外枠にアンチエイリアスがかかったためです。

ダウンロード (1).png

#使い方

const bucketFill = new BucketFill(canvas); // newする canvasを渡す
bucketFill.setSeed(pos.x, pos.y); // 塗りつぶし開始座標を渡す
bucketFill.setFillColor(255, 0, 0); // 塗りつぶす色を指定する
bucketFill.paint(); // 塗りつぶす

#ソース
BucketFill クラスのソースを掲載します。
割と単純なアルゴリズムですので、ソースのコメントを見れば理解できると思います。

class BucketFill {
    constructor(canvas) {
        this.seeds = [];
        this.setFillColor(0, 0, 0);
        this.canvas = canvas;
        this.imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
    }

    // 塗りつぶし色の変更
    setFillColor(r, g, b) {
        this.fillColor = (r << 16) + (g << 8) + b;
    }

    // シードの設定
    setSeed(x, y) {
        this.seeds = [];
        if (x < this.canvas.width && y < this.canvas.height) { // 引数チェック
            this.seeds.push({x, y});
            this.seedColor = this.getColor(x, y);
        }
    }

    // シードによる塗りつぶし
    seedFill(x, y) {
        const seedColor = this.seedColor;
        if(seedColor === this.fillColor) {// 既に塗られている
            return;
        }
        this.setColor(this.fillColor, x, y);

        // 右側へ走査する
        let rightX = x + 1;
        while (rightX < this.canvas.width) {
            const c = this.getColor(rightX, y);            
            if(c === seedColor) {// 現在の色がシードの色と一致する
                this.setColor(this.fillColor, rightX, y);
            } else {
                break;
            }
            rightX++;
        }

        // 左側へ走査
        let leftX = x - 1;
        while (leftX >= 0) {
            const c = this.getColor(leftX, y);            
            if(c === seedColor) {// 現在の色がシードの色と一致する
                this.setColor(this.fillColor, leftX, y);
            } else {
                break;
            }
            leftX--;
        }

        // 上側のシードを見つける
        if(y - 1 >= 0) {
            this.findSeed(leftX, rightX, y - 1);
        }
        // 下側のシードを見つける
        if (y + 1 < this.canvas.height) {
            this.findSeed(leftX, rightX, y + 1);
        }
    }

    // シードを見つける
    findSeed(leftX, rightX, y) {
        let seed = false;
        for(let x = leftX + 1; x < rightX; x += 1) {
            const c = this.getColor(x, y);
            if(this.seedColor === c) {// 現在の色がシードの色
                seed = true;
            } else if(seed) {// 左がシード色で現在の色がシードの色ではない
                this.seeds.push({ x: x - 1, y });
                seed = false;
            }
        }
        if(seed) {
            this.seeds.push({ x: rightX - 1, y });
        }
    }

    // 塗りつぶし
    paint() {
        while (this.seeds.length > 0) {// シードが無くなるまで繰り返す
            const seed = this.seeds.shift();
            this.seedFill(seed.x, seed.y);
        }
        this.canvas.getContext("2d").putImageData(this.imageData, 0, 0);
    }

    getColor(x, y) {
        const pixels = this.imageData.data,
            w = this.canvas.width,
            h = this.canvas.height,
            p = ((w * y) + x) * 4;
        let rgb = 0;
        rgb += (pixels[p] << 16);
        rgb += (pixels[p+1] << 8);
        rgb += (pixels[p+2]);
        return rgb;
    } 

    setColor(color, x, y) {
        const pixels = this.imageData.data,
            w = this.canvas.width,
            h = this.canvas.height,
            p = ((w * y) + x) * 4;
        pixels[p] = ((color & 0xFF0000) >> 16) 
        pixels[p + 1] = ((color & 0xFF00) >> 8) 
        pixels[p + 2] = color & 0xFF
    }
}
1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?