トップAndroid Java > PorterDuff演算よるポリゴンのパターン塗りつぶし

PorterDuff演算よるポリゴンのパターン塗りつぶし

はじめに

Android Java の PorterDuff.Mode演算に関するネット記事はそこそこあるが、具体事例がみつからないため、 取りつきにくかったが、ネット記事に何度か目を通すうちに、少しずつ敷居が下がってきたので、 パターンによる塗りつぶしに PorterDuff.Mode演算を使ってみよう。

パターンによるポリゴン塗りつぶし

森林の場合、下図のように、葉っぱアイコンを描きこんだポリゴン塗りつぶしを行う。 描画は 256x256画素単位のため、予め、葉っぱアイコンを描きならべた256x256画素の画像データを用意しておく。 このパターン画像が SRC となる。

一方 DSTとなるポリゴンの色は何でもいいが、葉っぱアイコンのない地の色で塗りつぶしておこう。

この場合、PorterDuff.Mode.DST_IN演算を行えば、 単色塗りつぶしのポリゴンが葉っぱアイコンパターンのポリゴンに変わる。

Canvas#saveLayerがまだ理解できていないが、これを使えば、ワークも要らないのかも知れないが、 取りあえずは、これを使わず、自前のワークエリアを使う。

プログラム

最初のプログラム

最初のプログラムを下に示す。パターンによる塗りつぶしが起きてはいるが、所望の結果ではない。

3〜5行をコメントアウトすると単色でのポリゴン塗りつぶしになることを確認した。

よって、3〜5行に問題がある。

    void fillPolygon(Canvas canvas, TileToRender tile, Paint paint, Bitmap pattern) {
        // DST
        tile.cvWork.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        fillPolygon(tile.cvWork, tile, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        // SRC
        tile.cvWork.drawBitmap(pattern, 0, 0, paint);
        paint.setXfermode(null);

        canvas.drawBitmap(tile.bmpWork, 0, 0, null);
    }

上のプログラムではポリゴン塗りつぶし用の Paintインスタンスを そのまま PorterDuff演算の Paint インスタンス として使っていた。どうやら、共用はできないようである。

PorterDuff演算専用の Paint インスタンスを使う

次のように、PoreterDuff演算専用の Paintインスタンスを使えばよいことが分かった。 このインスタンスは TileToRender において、共用することができるが、 パフォーマンス上の差は微々たるものであろう。

    void fillPolygon(Canvas canvas, TileToRender tile, Paint paint, Bitmap pattern) {
        // DST
        tile.cvWork.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        fillPolygon(tile.cvWork, tile, paint);//polygonをcolorで塗りつぶし

        Paint p = new Paint();
        p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        // SRC
        tile.cvWork.drawBitmap(pattern, 0, 0, p);

        canvas.drawBitmap(tile.bmpWork, 0, 0, null);
    }

zoom 13か14以上についてのみ、パターンによる塗りつぶしとして、それ以下のズームは単色塗りつぶしとする。 そこで zoom 13 以上の実行時間を計測する。

[単色塗りつぶし]
ズーム   13  14  15  16  17  18  19  20  平均
郊 外   68  46  38  48  39  54  35  48  47ms
東 京   73  58  55  57  47  58  32  42  53ms

[パターン塗りつぶし]
ズーム   13  14  15  16  17  18  19  20  平均
郊 外  333  93  58  59  61  59  42  56  95ms
東 京  109 192  95  69  45  62  62  42  85ms

自前のプログラムよりも若干いい結果が得られている。

PorterDuff演算用Paintインスタンスを共用する

森林については DST_IN よりも SRC_ATOP の方が良さそうなので、変更した。

    void fillPolygon(Canvas canvas, TileToRender tile, Paint paint, Bitmap pattern) {
        tile.cvWork.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);    // DST
        fillPolygon(tile.cvWork, tile, paint);//polygonをcolorで塗りつぶし
        tile.cvWork.drawBitmap(pattern, 0, 0, tile.pPorterDuffSRC_ATOP);
        canvas.drawBitmap(tile.bmpWork, 0, 0, null);
    }

演算範囲を限定する

演算が必要な範囲は必ずしもタイル全体ではない。タイルとポリゴンの境界ボックスの交差範囲でよい。

タイルの左上を原点として、ポリゴンの境界ボックス相対座標を求める

リファレンス

[1] AndroidのCanvasを使いこなす! - PorterDuff
[2] その7 PorterDuff.Modeとは何なのか
[3] PorterDuff.Mode
[4] 何かの時にスッと使える力技 - PorterDuffXfermode 編
[5] android.support.v4.graphics.ColorUtils Java Examples
[6] 2015-06-12 Android の Canvas#saveLayer メソッドと xfermode について
[7] Android での実際の画像 PorterDuff モードの使用法[Kotlin]
[8] 矩形の重なりを求めるプログラム