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 インスタンス として使っていた。どうやら、共用はできないようである。
次のように、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
自前のプログラムよりも若干いい結果が得られている。
森林については 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); }
演算が必要な範囲は必ずしもタイル全体ではない。タイルとポリゴンの境界ボックスの交差範囲でよい。
タイルの左上を原点として、ポリゴンの境界ボックス相対座標を求める