Pertanyaan Punch lubang di overlay persegi panjang dengan akselerasi HW diaktifkan pada View


Saya memiliki pandangan yang melakukan beberapa gambar dasar. Setelah ini saya ingin menggambar persegi panjang dengan lubang yang dilubangi sehingga hanya wilayah gambar sebelumnya yang terlihat. Dan saya ingin melakukan ini dengan akselerasi perangkat keras yang diaktifkan untuk tampilan saya untuk kinerja terbaik.

Saat ini saya memiliki dua metode yang berfungsi, tetapi hanya berfungsi ketika akselerasi perangkat keras dinonaktifkan dan yang lainnya terlalu lambat.

Metode 1: Akselerasi SW (Lambat)

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);

Ini tidak bekerja dengan akselerasi perangkat keras yang diaktifkan untuk tampilan karena 'canvas.clipPath' tidak didukung dalam mode ini (saya tahu saya dapat memaksa SW rendering, tapi saya ingin menghindari itu).

Metode 2: Akselerasi HW (V. Lambat)

// 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);

Di mana penghapus dibuat sebagai

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

Ini jelas lambat - baru Bitmap ukuran tampilan dibuat setiap panggilan gambar.

Metode 3: Akselerasi HW (Cepat, tidak berfungsi pada beberapa perangkat)

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

Sama seperti metode akselerasi HW acceleration, tetapi tidak diperlukan tambahan kanvas. Ada masalah besar dengan ini meskipun - ia bekerja dengan SW render paksa, tetapi pada HTC One X (Android 4.0.4 - dan mungkin beberapa perangkat lain) setidaknya dengan rendering HW memungkinkannya meninggalkan lingkaran sepenuhnya hitam. Ini mungkin terkait dengan 22361.

Metode 4: Akselerasi HW (Dapat Diterima, berfungsi di semua perangkat)

Sesuai saran Jan untuk memperbaiki metode 2, saya menghindari membuat bitmap di setiap panggilan onDraw, bukannya melakukannya di onSizeChanged:

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

Dan kemudian hanya menggunakan ini 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);

Performanya tidak sebaik metode 3, tetapi jauh lebih baik daripada 2 dan sedikit lebih baik daripada 1.

Pertanyaan

Bagaimana saya dapat mencapai efek yang sama tetapi melakukannya dengan cara yang kompatibel dengan akselerasi HW (DAN yang bekerja secara konsisten pada perangkat)? Suatu metode yang meningkatkan kinerja rendering SW juga akan diterima.

NB: Saat memindahkan lingkaran di sekitar saya hanya membatalkan wilayah - bukan seluruh kanvas - jadi tidak ada ruang untuk peningkatan kinerja di sana.


32
2017-11-23 11:49


asal


Jawaban:


Alih-alih mengalokasikan kanvas baru pada setiap pengecatan, Anda harus dapat mengalokasikannya sekali dan kemudian menggunakan kembali kanvas pada setiap pengecatan.

pada init dan resize:

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

pada pengecatan:

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);

17
2017-11-23 13:26