webentwicklung-frage-antwort-db.com.de

PorterduffXfermode: Löscht einen Abschnitt einer Bitmap

Das Ziel besteht einfach darin, eine Bitmap zu zeichnen und darüber hinaus Formen zu zeichnen, die den darunterliegenden Bereich der Bitmap löschen.

Ich habe einen einfachen Proof-of-Concept-Code erstellt, um zu verstehen, wie genau ich das anstellen soll. In den verschiedenen Threads hier habe ich zahlreiche Hinweise zur Verwendung gefunden:

Android.graphics.PorterDuff.Mode.CLEAR

Der folgende Code erstellt einfach einen Bildschirm mit blauem Hintergrund und fügt eine benutzerdefinierte Ansicht hinzu. Diese Ansicht zeichnet auf ihrer Leinwand einen rosa Hintergrund, das Bitmap-Bild (mit einem leichten Rand, um den rosa Hintergrund anzuzeigen) und gelbe Kreise, die die einzelnen PorterDuffXfermode darstellen.

import Android.app.Activity;
import Android.content.Context;
import Android.graphics.Bitmap;
import Android.graphics.BitmapFactory;
import Android.graphics.Canvas;
import Android.graphics.Color;
import Android.graphics.Paint;
import Android.graphics.PorterDuffXfermode;
import Android.graphics.Paint.Style;
import Android.graphics.PorterDuff.Mode;
import Android.graphics.RectF;
import Android.os.Bundle;
import Android.view.View;
import Android.view.Window;
import Android.widget.RelativeLayout;

public class Test extends Activity {
    Drawing d = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        RelativeLayout.LayoutParams rlp = null;

        // Create the view for the xfermode test
        d = new Drawing(this);
        rlp = new RelativeLayout.LayoutParams(600, 900);
        rlp.addRule(RelativeLayout.CENTER_IN_PARENT);
        d.setLayoutParams(rlp);

        RelativeLayout rl = new RelativeLayout(this);
        rl.setBackgroundColor(Color.rgb(0, 0, 255));
        rl.addView(d);

        // Show the layout with the test view
        setContentView(rl);
    }

    public class Drawing extends View {
        Paint[] pDraw = null;
        Bitmap bm = null;

        public Drawing(Context ct) {
            super(ct);

            // Generate bitmap used for background
            bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg");

            // Generate array of paints
            pDraw = new Paint[16];

            for (int i = 0; i<pDraw.length; i++) {
                pDraw[i] = new Paint();
                pDraw[i].setARGB(255, 255, 255, 0);
                pDraw[i].setStrokeWidth(20);
                pDraw[i].setStyle(Style.FILL);
            }

            // Set all transfer modes
            pDraw[0].setXfermode(new PorterDuffXfermode(Mode.CLEAR));
            pDraw[1].setXfermode(new PorterDuffXfermode(Mode.DARKEN));
            pDraw[2].setXfermode(new PorterDuffXfermode(Mode.DST));
            pDraw[3].setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
            pDraw[4].setXfermode(new PorterDuffXfermode(Mode.DST_IN));
            pDraw[5].setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
            pDraw[6].setXfermode(new PorterDuffXfermode(Mode.DST_OVER));
            pDraw[7].setXfermode(new PorterDuffXfermode(Mode.LIGHTEN));
            pDraw[8].setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
            pDraw[9].setXfermode(new PorterDuffXfermode(Mode.SCREEN));
            pDraw[10].setXfermode(new PorterDuffXfermode(Mode.SRC));
            pDraw[11].setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
            pDraw[12].setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            pDraw[13].setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
            pDraw[14].setXfermode(new PorterDuffXfermode(Mode.SRC_OVER));
            pDraw[15].setXfermode(new PorterDuffXfermode(Mode.XOR));
        }

        @Override
        public void onDraw(Canvas canv) {
            // Background colour for canvas
            canv.drawColor(Color.argb(255, 255, 0, 255));

            // Draw the bitmap leaving small outside border to see background
            canv.drawBitmap(bm, null, new RectF(15, 15, getMeasuredWidth() - 15, getMeasuredHeight() - 15), null);

            float w, h;
            w = getMeasuredWidth();
            h = getMeasuredHeight();

            // Loop, draws 4 circles per row, in 4 rows on top of bitmap
            // Drawn in the order of pDraw (alphabetical)
            for(int i = 0; i<4; i++) {
                for(int ii = 0; ii<4; ii++) {
                    canv.drawCircle((w / 8) * (ii * 2 + 1), (h / 8) * (i * 2 + 1), w / 8 * 0.8f, pDraw[i*4 + ii]);
                }
            }
        }
    }

}

Dies ist das Ergebnis des Tests:

enter image description here

Der CLEAR-Modus ist oben links und wird schwarz angezeigt.

In einem anderen Beispiel, in dem ich versuchte, dies zu verwenden, hatte ich ein DialogFragment, in dem der CLEAR-Modus das gesamte DialogFragment löschte, sodass die darunter liegende Aktivität sichtbar wurde. Aus diesem Grund habe ich in diesem Test viele verschiedene Hintergrundfarben verwendet.

Könnte dies möglicherweise die Pixel der gesamten Aktivität löschen, wie dieses andere Beispiel mich glauben ließ? Ich hätte gedacht, dass nur die Pixel der Leinwand, die sich auf die Ansicht beziehen, gelöscht werden könnten, aber in meinem anderen Beispiel wurden die Pixel der benutzerdefinierten Ansicht, der zugrunde liegenden Bildansicht und des DialogFragment-Hintergrunds alle gelöscht.

Könnte mir bitte jemand helfen zu verstehen, was genau passiert und warum ich so schrecklich falsch gehe?

Vielen Dank. 

EDIT: Ich habe ein Beispiel reproduziert, das meinen Verdacht bestätigt. Beim Hinzufügen dieser genauen benutzerdefinierten Ansicht, aber in einem DialogFragment wird die zugrunde liegende Aktivität sichtbar.

enter image description here

Dies scheint mir ein deutlicher Hinweis darauf zu sein, dass der Mode.CLEAR irgendwie die Leinwand der darunter liegenden Ansichten löscht. Meine Vermutung wäre das Schwarze im ersten Beispiel das der Top-Level-Ansicht? 

Ich denke, ich mache irgendwo etwas sehr Falsches: S

28
B T

Das Problem ist die Hardwarebeschleunigung. Deaktivieren Sie es für die bestimmte Ansicht, die Sie mit CLEAR malen. Wenn Sie eine benutzerdefinierte Ansicht verwenden, führen Sie dies in den Konstruktoren aus:

if (Android.os.Build.VERSION.SDK_INT >= 11) 
{
     setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

Sie können setLayerType auch für eine Ansichtsreferenz aufrufen.

34
metaphyze

Ich sehe nichts Unerwartetes. Im speziellen Fall von Mode.CLEAR werden sowohl die Farbe als auch das Alpha des Ziels gelöscht, wodurch der schwarze Hintergrund angezeigt werden kann. Dieses Dienstprogramm ermöglicht das Experimentieren mit verschiedenen Modi, Farben und Alpha-Werten, und die Quelle kann einige Einblicke bieten. In dem (etwas veralteten) Bild unten zeigen die CLEAR-Bereiche das schwache Nadelstreifengrau, das vom PanelUI-Delegaten der Plattform bereitgestellt wird.

image

14
trashgod

Wie Romain Guy hier auf Folgendes hinweist: google stuff Sie müssen auf die Bitmap schreiben, nicht auf die Leinwand mit den Kreisen, die Sie löschen möchten, und dann die Hauptleinwand in Ihrer Ansicht festlegen. Also mache etwas wie: 

// Generate bitmap used for background
            bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg");

// Create the Paint and set the bitmap
Canvas temp = new Canvas(bm.getHeight(), bm.getWidth, Config.ARGB_8888);

// in onDraw()
temp.drawCircle((w / 8) * (ii * 2 + 1), (h / 8) * (i * 2 + 1), w / 8 * 0.8f, pDraw[i*4 + ii]);

// After loop
canvas.drawBitmap(....);

Hoffe das hilft.

0
AllDayAmazing