2012-11-23 10 views
32

Ho una visione che fa un po 'di disegno di base. Dopodiché voglio disegnare un rettangolo con un foro punzonato in modo che sia visibile solo una regione del disegno precedente. E mi piacerebbe farlo con l'accelerazione hardware abilitata per la mia vista per le migliori prestazioni.fare un buco in una sovrapposizione rettangolo con l'accelerazione hardware abilitata su Visualizza

Attualmente ho due metodi che funzionano, ma funziona solo quando l'accelerazione hardware è disabilitata e l'altro è troppo lento.

Metodo 1: SW Accelerazione (lento)

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

Questo non funziona con accelerazione hardware abilitato per la vista perché 'canvas.clipPath' non è supportato in questa modalità (So che posso forzare Rendering SW, ma vorrei evitarlo).

Metodo 2: accelerazione HW (V. Lento)

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

Dove viene creato gomma come

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

Questo è ovviamente lento - un nuovo Bitmap la dimensione della vista è creato ogni chiamata di disegno.

Metodo 3: accelerazione HW (veloce, non funziona su alcuni dispositivi)

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

Stesso come il metodo compatibile con l'accelerazione hardware, ma nessuna tela di canapa supplementare richiesto. Tuttavia c'è un grosso problema: funziona con il rendering SW forzato, ma su HTC One X (Android 4.0.4 - e probabilmente con qualche altro dispositivo) almeno con il rendering HW abilitato lascia il cerchio completamente nero. Questo è probabilmente correlato a 22361.

Metodo 4: HW accelerazione (Accettabile, funziona su tutti i dispositivi)

Come da suggerimento di gennaio per migliorare il metodo 2, ho evitato di creare la bitmap in ogni chiamata a onDraw, invece di farlo in onSizeChanged:

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

E poi basta utilizzare questi in 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); 

La prestazione non è buono come il metodo 3, ma molto meglio di 2 e un po 'meglio di 1.

La domanda

Come posso ottenere lo stesso effetto, ma farlo in un modo compatibile con Accelerazione HW (E che funziona in modo coerente sui dispositivi)? Un metodo che aumenta le prestazioni di rendering SW sarebbe anche accettabile.

NB: quando si sposta il cerchio attorno, invalido solo una regione, non l'intera tela, quindi non c'è spazio per un miglioramento delle prestazioni.

+0

è semplicemente la memorizzazione nella cache del 'B' Bitmap e' C' Canvas possibile? Sarebbe d'aiuto? –

+0

Io non la penso così - se fai semplicemente la 'b' e' c' di dire in 'onSizeChanged' e non in 'onDraw', finirai per disegnare più volte sulla stessa tela senza cancellare (così faresti finire con più livelli). In realtà funzionerebbe se potessi cancellare 'b' in' onDraw', che potrebbe essere possibile - ci proverò, grazie! –

+0

@Jan: Grazie a questo ha funzionato - Ho creato il bitmap 'b' e Canvas' c' in 'onSizeChanged', quindi ho solo dovuto chiamare' b.eraseColor (Color.Transparent) 'prima di fare qualsiasi disegno per cancellare qualsiasi precedente disegno su 'c'. Pubblica il tuo commento come risposta e lo segnerò correttamente se non ricevo più risposte utili nel giorno successivo. Non è ancora così fluido come il metodo 3, ma molto meglio di 2. –

risposta

17

Invece di allocare una nuova tela su ogni ridisegno, dovresti essere in grado di allocarlo una volta e quindi riutilizzare la tela su ogni ridisegno.

su init e il ridimensionamento:

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

il ridisegno:

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

Superrb solution –

+0

È un po 'tardi, tuttavia ho una domanda al riguardo. Voglio fare esattamente la stessa cosa (foro nel rettangolo), tuttavia il mio problema è che la mia vista deve occupare tutto lo schermo, quindi la bitmap creata avrà la dimensione dello schermo che sarà di circa 4 MB - 10 MB a seconda sulla dimensione dello schermo. La creazione di tale bitmap di grandi dimensioni potrebbe causare un errore OutOfMemoryError. C'è qualche soluzione a riguardo? – MScott

+0

@MScott Non riesco ad immaginare che 10 MB rappresentino una sfida per gli smartphone di oggi da allocare. Non dimenticare che la GPU deve tenere comunque questa quantità di memoria solo per poterla alimentare allo schermo LCD. Due volte, molto probabilmente - una copia da mostrare all'utente, l'altra da aggiornare nel frattempo. Puoi giocare con la pittura di quattro rettangoli, uno per ciascun lato, ma non funzionerà molto bene con l'antialias se sono semi-trasparenti e può solo produrre fori rettangolari. –

Problemi correlati