web-dev-qa-db-ja.com

ビューでハードウェアアクセラレーションを有効にして、長方形のオーバーレイに穴を開けます

基本的な描画を行うビューがあります。この後、前の描画の領域のみが表示されるように、穴を開けた長方形を描画します。そして、最高のパフォーマンスを得るために、ハードウェアアクセラレーションを有効にしてこれを実行したいと思います。

現在、2つの方法がありますが、ハードウェアアクセラレーションを無効にし、もう1つが遅すぎる場合にのみ機能します。

方法1:SWアクセラレーション(遅い)

final int saveCount = canvas.save();

// Clip out a circle.
circle.reset();
circle.addCircle(cx, cy, radius, Path.Direction.CW);
circle.close();
canvas.clipPath(circle, Region.Op.DIFFERENCE);

// Draw the rectangle color.
canvas.drawColor(backColor);

canvas.restoreToCount(saveCount);

'canvas.clipPath'はこのモードではサポートされていないため、これはビューに対してハードウェアアクセラレーションが有効になっている場合は機能しません(SWレンダリングを強制できることはわかっていますが、それを避けたいと思います)。

方法2:ハードウェアアクセラレーション(V.低速)

// Create a new canvas.
final Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
final Canvas c = new Canvas(b);

// Draw the rectangle colour.
c.drawColor(backColor);

// Erase a circle.
c.drawCircle(cx, cy, radius, eraser);

// Draw the bitmap on our views  canvas.
canvas.drawBitmap(b, 0, 0, null);

消しゴムが作成される場所

eraser = new Paint()
eraser.setColor(0xFFFFFFFF);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

これは明らかに遅いです-ビューのサイズの新しいBitmapが描画呼び出しごとに作成されます。

方法3:ハードウェアアクセラレーション(高速、一部のデバイスでは機能しません)

canvas.drawColor(backColor);
canvas.drawCircle(cx, cy, radius, eraser);

ハードウェアアクセラレーションと互換性のある方法と同じですが、追加のキャンバスは必要ありません。これには大きな問題があります-強制的にSWレンダリングで動作しますが、HTC One X(Android 4.0.4-およびおそらく他のいくつかのデバイス)では、少なくともHWレンダリングが有効になっていると、円が完全に黒くなります。これはおそらく 22361 に関連しています。

方法4:ハードウェアアクセラレーション(許容可能、すべてのデバイスで機能)

方法2を改善するためのJanの提案に従って、onDrawを呼び出すたびにビットマップを作成するのを避け、代わりにonSizeChangedで作成しました。

if (w != oldw || h != oldh) {
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    c = new Canvas(b);
}

そして、これらをonDrawで使用しました。

if (overlayBitmap == null) {
   b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
   c = new Canvas(b);
}
b.eraseColor(Color.TRANSPARENT);
c.drawColor(backColor);
c.drawCircle(cx, cy, radius, eraser);
canvas.drawBitmap(b, 0, 0, null);

パフォーマンスは方法3ほど良くはありませんが、2よりはるかに優れており、1よりわずかに優れています。

質問

同じ効果を実現するにはどうすればよいですか?ただし、ハードウェアアクセラレーションと互換性のある方法で実現できます(デバイスで一貫して機能します)。 SWレンダリングのパフォーマンスを向上させる方法も受け入れられます。

注意:円を移動するときは、キャンバス全体ではなく、領域を無効にしているだけなので、パフォーマンスを向上させる余地はありません。

35
Joseph Earl

再ペイントごとに新しいキャンバスを割り当てる代わりに、一度割り当ててから、再ペイントごとにキャンバスを再利用できるようにする必要があります。

初期化時とサイズ変更時:

Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

再塗装時:

b.eraseColor(Color.TRANSPARENT);
    // needed if backColor is not opaque; thanks @JosephEarl
c.drawColor(backColor);
c.drawCircle(cx, cy, radius, eraser);

canvas.drawBitmap(b, 0, 0, null);
18
John Dvorak