Wykrawanie otworu w nakładce prostokątnej z włączoną akceleracją sprzętową na widoku

Mam widok, który wykonuje podstawowe rysowanie. Następnie chcę narysować prostokąt z dziurkowanym otworem, aby widoczny był tylko obszar poprzedniego rysunku. I chciałbym to zrobić z akceleracją sprzętową włączoną dla mojego widoku dla najlepszej wydajności.

Obecnie mam dwie metody, które działają, ale działają tylko wtedy, gdy wyłączone jest akceleracja sprzętowa, a druga jest zbyt wolna.

Metoda 1: przyspieszenie SW (powolne)

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

To nie działa z akceleracją sprzętową włączoną dla widoku, ponieważ ' canvas.clipPath ' nie jest obsługiwany w tym trybie (wiem, że mogę wymusić renderowanie SW, ale chciałbym tego uniknąć).

Metoda 2: przyspieszenie HW (V. powolne)

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

Gdzie gumka jest tworzona jako

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

Jest to oczywiście wolne-przy każdym wywołaniu rysowania tworzony jest nowy Bitmap rozmiar widoku.

Metoda 3: przyspieszanie sprzętowe (szybkie, nie działa na niektórych urządzeniach)

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

To samo jako metoda kompatybilna z akceleracją sprzętową, ale nie wymaga dodatkowego płótna. Jest z tym jednak poważny problem - działa z wymuszonym renderowaniem SW, ale na HTC One X (Android 4.0.4-i prawdopodobnie niektórych innych urządzeniach) przynajmniej z włączonym renderowaniem HW pozostawia koło całkowicie czarne. Jest to prawdopodobnie związane z 22361.

Metoda 4: przyspieszenie sprzętowe (dopuszczalne, działa na wszystkich urządzeniach)

Zgodnie z sugestią Jana dotyczącą ulepszenia metody 2, I unikanie tworzenia bitmapy w każdym wywołaniu onDraw, zamiast tego w onSizeChanged:

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

A potem po prostu użyłem ich w 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);

Wydajność nie jest tak dobra jak Metoda 3, ale znacznie lepsza niż 2 i nieco lepsza niż 1.

Pytanie

Jak mogę osiągnąć ten sam efekt, ale zrobić to w sposób zgodny z akceleracją sprzętową (i to działa konsekwentnie na urządzeniach)? Metoda, która zwiększa wydajność renderowania SW będzie również akceptowalne.

NB: przesuwając okrąg wokół, unieważniam tylko region , a nie całe płótno , więc nie ma miejsca na poprawę wydajności.

Author: Joseph Earl, 2012-11-23

1 answers

Zamiast przydzielać nowe płótno do każdego malowania, powinieneś być w stanie przydzielić je raz, a następnie ponownie użyć płótna do każdego malowania.

Na init i na resize:

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

On repaint:

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
Author: John Dvorak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-23 13:26:21