2010-04-12 7 views
17

Ich bin derzeit auf der Suche nach einer Möglichkeit, eine schwarz-weiß Bitmap zu verwenden, um den Alpha-Kanal einer anderen Bitmap oder Drawable auf Android zu maskieren. Ich bin neugierig, was der beste Weg ist, dies zu tun. Ich habe sicherlich ein paar Ideen, wie das geht, aber sie sind nicht optimal.Maskierung eines Drawable/Bitmap auf Android

Ich muss in der Lage sein, eine neue Maske auf das Bild anzubringen (die Schwarz-Weiß-Bitmap wird sich alle paar Sekunden ändern).

Jede Rückmeldung, wie dies zu erreichen wäre, würde sehr geschätzt werden.

+0

Ich versuche, die schwarzen Teile der Maske Bitmap verwenden, um den Alpha-Kanal des entsprechenden Pixels auf der anderen Bitmap/Drawable auf 0 setzen. –

Antwort

3

Ich habe es funktioniert, es ist also so etwas wie dies

// we first same the layer, rectF is the area of interest we plan on drawing 
    // this will create an offscreen bitmap 
    canvas.saveLayer(rectF, null, Canvas.MATRIX_SAVE_FLAG 
      | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG 
      | Canvas.FULL_COLOR_LAYER_SAVE_FLAG 
      | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 

    // draw our unmasked stuff 
    super.draw(canvas); 
    // We same a layer again but this time we pass a paint object to mask 
    // the above layer 
    maskPaint = new Paint() 
    maskPaint.setColor(0xFFFFFFFF); 
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); 

    canvas.saveLayer(rectF, maskPaint, 
      Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG 
        | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG 
        | Canvas.FULL_COLOR_LAYER_SAVE_FLAG 
        | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 
    // we draw the mask which is black and white. In my case 
    // I have a path, and I use a blurPaint which blurs the mask slightly 
    // You can do anti aliasing or whatever you which. Just black & white 
    canvas.drawPath(path, blurPaint); 
    // We restore twice, this merges the results upward 
    // as each saveLayer() allocates a new drawing bitmap 
    canvas.restore(); 
    canvas.restore(); 
+0

Wenn die onDraw Methode überschrieben wird, sollte 'super.draw (canvas)' 'super.onDraw (canvas)' sein. Andernfalls enden Sie in Endlosschleife mit StackOverflow-Fehler. – Speedy

+0

Ich habe das selbe wie oben in 'onDraw' meiner benutzerdefinierten Ansicht gemacht, aber es scheint, dass 'saveLayer' teuer ist und daher stelle ich stattdessen eine Hardware-Ebene auf meine Ansicht (' View.setLayerType (LAYER_TYPE_HARDWARE, paint) '). Weitere Informationen: http://stackoverflow.com/a/33483016/4747587 – Henry

2

Ich bin nicht ganz klar, auf was Sie gehen, aber ich glaube, dass eine Kombination von BitmapDrawable und funktionieren kann. BitmapDrawable ermöglicht es Ihnen, Ihre Bitmaps als Drawables zu verwenden, und dann können Sie LayerDrawable verwenden, um die Maske über einem anderen Drawable zu überlagern.

+0

Ich werde es überprüfen, danke. –

1

Mithilfe des Xfermodes-Beispiels in der API-Demo konnte ich einen PorterDuffXfermode verwenden, der auf ein Paint-Objekt angewendet wurde, um zwei Bitmaps auf einer Zeichenfläche zu verbinden. Das funktioniert genau so, wie ich es brauche.

+12

Können Sie weitere Details hinzufügen? – Casebash

5

Meine Lösung ist in der Nähe von @ over_optimistic Lösung, weniger einen saveLayer() Anruf. Ich benutze eine Drawable-Maske anstelle eines Pfades, in meinem Fall war es eine Disc.

erklärte ich diese Variablen als Felder (es ist gute Praxis Speicher außerhalb der OnDraw-Methode zuzuteilen):

private Paint maskingPaint = new Paint(); 
private Drawable mask = <insert your drawable here> 

Dann irgendwo außerhalb von OnDraw(), Einrichtung der Objekte:

// Xfermode won't work if hardware accelerated 
setLayerType(View.LAYER_TYPE_SOFTWARE, null); 

// Using destination shape as a mask 
// For a good explanation of PorterDuff transfer modes : http://ssp.impulsetrain.com/porterduff.html 
maskingPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
maskingPaint.setAntiAlias(true); 

// Position the mask 
mask.setBounds(<insert your mask bounds here>); 

Dann wendet die Methode onDraw() schließlich die Maske an:

@Override 
protected synchronized void onDraw(Canvas canvas) 
{ 
    // Draw the mask first, making it the PorterDuff destination 
    mask.draw(canvas); 

    // Save the layer with the masking paint, that will be applied on restore() 
    // Using CLIP_TO_LAYER_SAVE_FLAG to avoid any overflow of the masked image outside the mask bounds. 
    Rect bounds = mask.getBounds(); 
    canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, maskingPaint, 
      Canvas.CLIP_TO_LAYER_SAVE_FLAG); 

    // Draw the shape offscreen, making it the PorterDuff source 
    super.onDraw(canvas); 

    // Apply the source to the destination, using SRC_IN transfer mode 
    canvas.restore(); 
} 

Für ein besseres Verständnis o Für die Übertragungsmodi habe ich auf http://ssp.impulsetrain.com/porterduff.html verwiesen. Diese Seite ist ziemlich interessant zu lesen. Danach können Sie mit der gleichen Art von Code viel mehr erreichen als nur eine einfache Maske!

+0

Perfekte Lösung. Andere sind ein bisschen zu lang. – Jona

+0

IMO besser als die akzeptierte Antwort, ziehe ich lieber über Pfad –