2017-09-14 1 views
0

Ich habe versucht, dies die ganze Nacht herauszufinden, aber Antworten auf Google beziehen sich auf sehr spezifische Probleme in Bezug auf Android Canvas und ich habe keine 101 Erklärungen zu diesem Thema gefunden. Sogar die Android-Dokumentation verwendet Bitmaps, anstatt Formen zu zeichnen.Worauf beziehen sich die Quelle und das Ziel von PorterDuff beim Zeichnen auf Leinwand?

Spezifisches Problem:

Ich brauche einen ovalen und einen Weg auf die Leinwand zu zeichnen. Und nach documentation Farbe Quelle mit einer Farbe, Ziel aus einer anderen Farbe und überlappenden Bereich, entweder Quelle oder Ziel in, eine dritte Farbe. Ich versuche das alles in einem Offscreen-Canvas zu machen. Bei einigen der obigen Schritte treten jedoch Probleme auf und werden schlimmer, wenn man versucht, sie in irgendeiner Weise zu kombinieren.

  • -Code -

    Bitmap bmp = Bitmap.CreateBitmap (720, 720, Bitmap.Config.Argb8888); 
        Canvas c = new Canvas (bmp); 
    
        Paint paint = new Paint(); 
        paint.SetARGB (255, 255, 0, 0); 
        c.DrawOval (200, 200, 520, 520, paint); //assumed destination 
    
        paint.SetARGB (255, 0, 0, 255); 
        paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.*)); //replace mode here 
        paint.SetStyle (Paint.Style.Fill); 
    
        Path path = new Path(); 
        path.MoveTo (c.Width/2f, c.Height/2f); 
    
        foreach (var m in measurements) { 
         //calculations 
    
         float x = xCalculatedValue 
         float y = yCalculatedValue 
    
         path.LineTo (x, y); 
        } 
    
        path.LineTo (c.Width/2f, c.Height/2f); 
    
        c.DrawPath (path, paint); //assumed source 
    
  • Quelle aus -

Dieser stattdessen zieht, was XOR soll ziehen.

  • Reiseziel aus -

Dies funktioniert wie erwartet.

  • Quelle in -

Zeichnet welcher Quelle oben auf sollte.

  • Destination in -

Zeichnet was Ziel sollte.

Weitere allgemeine Frage:

Was in diesem Zusammenhang beziehen sich Quell- und Ziel? Intuitiv würde ich davon ausgehen, dass das Ziel der aktuelle Zustand der Bitmap und die Quelle die von canvas.Draw * und Paint PortedDuff.Mode hinzugefügte Matrix ist. Aber das scheint nicht der Fall zu sein.

EDIT: Dies ist im Grunde der Effekt, nach dem ich bin, wo der "Stern" ist ein dynamischer Pfad. Je nach Überlappung drei verschiedene Farben.

Crude drawing

EDIT 2: Yorker Shen hat einen tollen Job die eigentliche Frage zu beantworten. Aber für alle, die einen ähnlichen Effekt erzielen möchten, ist hier der endgültige Code.

Bitmap DrawGraphBitmapOffscreen() 
{ 
    Bitmap bmp = Bitmap.CreateBitmap (720, 720, Bitmap.Config.Argb8888); 
    Canvas c = new Canvas (bmp); 

    // Replace with calculated path 
    Path path = new Path(); 
    path.MoveTo (c.Width/2f, c.Height/2f); 
    path.LineTo (263, 288); 
    path.LineTo (236, 202); 
    path.LineTo (312, 249); 
    path.LineTo (331, 162); 
    path.LineTo (374, 240); 
    path.LineTo (434, 174); 
    path.LineTo (431, 263); 
    path.LineTo (517, 236); 
    path.LineTo (470, 312); 
    path.LineTo (557, 331); 
    path.LineTo (479, 374); 
    path.LineTo (545, 434); 
    path.LineTo (456, 431); 
    path.LineTo (483, 517); 
    path.LineTo (407, 470); 
    path.LineTo (388, 557); 
    path.LineTo (345, 479); 
    path.LineTo (285, 545); 
    path.LineTo (288, 456); 
    path.LineTo (202, 483); 
    path.LineTo (249, 407); 
    path.LineTo (162, 388); 
    path.LineTo (240, 345); 
    path.LineTo (174, 285); 
    path.LineTo (263, 288); 
    path.Close(); 

    Paint paint = new Paint(); 

    paint.SetARGB (255, 255, 0, 0); 
    paint.SetStyle (Paint.Style.Fill); 

    c.DrawPath (path, paint); 

    paint.SetARGB (255, 0, 0, 255); 
    paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.SrcIn)); 

    c.DrawOval (200, 200, 520, 520, paint); 

    paint.SetARGB (255, 255, 255, 255); 
    paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.DstOver)); 

    c.DrawOval (200, 200, 520, 520, paint); 

    return bmp; 
} 
+0

Was ist der gewünschte Effekt, zeichnen Sie ein Oval und einen Pfad auf der Leinwand? Warum benutzt du 'PorterDuffXfermode'? Wenn Sie nur ein Oval und einen Pfad zeichnen müssen, müssen Sie PorterDuffX nicht verwenden. –

+0

Ich habe ein Bild hinzugefügt, um zu erklären, was ich versuche zu tun. Ich bin mir nicht sicher, ob Alpha-Compositing die beste Lösung ist, aber es ist die einzige, die mir in den Sinn kam. Und mehr als eine tatsächliche Lösung für das Problem, würde ich gerne verstehen, was Quelle und Ziel im Zusammenhang mit dem Zeichnen von Dingen nacheinander auf einer Leinwand beziehen. – JaanTohver

+0

Sie möchten also ein Oval und ein Polygon zeichnen und dann 'PorterDuffXfermode' verwenden, um den Effekt in dem Bild zu erzielen, das Sie geposted haben? –

Antwort

0

Was PorterDuff Quelle und das Ziel beziehen sich auf, wenn sie auf Leinwand zu zeichnen?

Mein früherer Kommentar ist falsch, Entschuldigung für irgendwelche Unannehmlichkeiten.

Nach eingehendem Studium schreibe ich ein paar Demo, um dies zu erklären. Um zu verstehen, worauf sich Quelle und Ziel beziehen.

Zuerst schauen Sie sich den folgenden Code:

protected override void OnDraw(Canvas canvas) 
{ 
    base.OnDraw(canvas); 

    Paint paint = new Paint(); 

    //Set the background color 
    canvas.DrawARGB(255, 139, 197, 186); 

    int canvasWidth = canvas.Width; 
    int r = canvasWidth/3; 

    //Draw a yellow circle 
    paint.Color = Color.Yellow; 
    canvas.DrawCircle(r, r, r, paint); 

    //Draw a blue rectangle 
    paint.Color = Color.Blue; 
    canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint); 
} 

ich überschreiben die OnDraw Methode, einem grünen Hintergrund, zeichnen dann einen gelben Kreis und ein blaues Rechteck, Wirkung:

enter image description here

Oben ist der normale Vorgang, wenn wir eine Canvas zeichnen, ich habe keine PorterDuffXfermode verwendet, lassen Sie uns den Prozess analysieren:

  • Zuerst rufen wir canvas.DrawARGB(255, 139, 197, 186) Methode, um die ganzen Canvas mit einer einzigen Farbe zeichnen, alle Pixel in dieser Leinwand haben den gleichen Wert ARGB: (255, 139, 197, 186). Da der Alpha-Wert in ARGB 255 statt 0 ist, ist also jeder Pixel undurchsichtig.

  • Zweitens, wenn wir canvas.DrawCircle(r, r, r, paint) Methode ausführen, wird Android einen gelben Kreis an der Position zeichnen, die Sie definiert haben. Alle Pixel, deren ARGB-Wert in diesem Kreis (255,139,197,186) ist, werden durch gelbe Pixel ersetzt. Die gelben Pixel sind Quelle und die Pixel, deren ARGB-Wert (255,139,197,186) Ziel ist. Ich werde später erklären.

  • Drittens, nach der canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint) Methode Android wird ein blaues Rechteck zeichnen, alle Pixel in diesem Rechteck ist blau, und diese blauen Pixel ersetzen andere Pixel an der gleichen Position. So kann das blaue Rechteck auf Canvas gezeichnet werden.

Zweitens verwende ich einen Modus von Xfermode, PorterDuff.Mode.Clear:

protected override void OnDraw(Canvas canvas) 
    { 
     base.OnDraw(canvas); 

     Paint paint = new Paint(); 

     //Set the background color 
     canvas.DrawARGB(255, 139, 197, 186); 

     int canvasWidth = canvas.Width; 
     int r = canvasWidth/3; 

     //Draw a yellow circle 
     paint.Color = Color.Yellow; 
     canvas.DrawCircle(r, r, r, paint); 

     //Use Clear as PorterDuffXfermode to draw a blue rectangle 
     paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear)); 

     paint.Color = Color.Blue; 
     canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint); 

     paint.SetXfermode(null); 
     this.SetLayerType(LayerType.Software, null); 

     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
     //I found that PorterDuff.Mode.Clear doesn't work with hardware acceleration, so you have add this code 
    } 

Wirkung:

enter image description here

wir seinen Prozess analysieren:

  • Zuerst rufen wir canvas.DrawARGB(255, 139, 197, 186) Methode, um die gesamte Canvas als eine einzige Farbe zu zeichnen, ist jede Pixel undurchsichtig.

  • Zweitens rufen wir canvas.DrawCircle(r, r, r, paint) Methode, um einen gelben Kreis in Canvas zu zeichnen.

  • Drittens, führen Sie paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear)) aus, stellen Sie das Modell PorterDuff auf Clear ein.

  • Forth, rufen Sie canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint), um ein blaues Rechteck zu zeichnen, und schließlich zeigt es ein weißes Rechteck.

Warum es ein weißes Rechteck angezeigt werden? Normalerweise, wenn wir canvas.DrawXXX() Methode aufrufen, übergeben wir einen Paint Parameter, wenn Android Draw-Methode ausführen, wird es überprüfen, ob die Farbe einen Xfermode-Modus hat. Wenn nicht, dann deckt die Grafik direkt die Pixel ab, die in Canvas an der gleichen Position sind. Andernfalls werden die Pixel in Canvas gemäß dem Xfermode-Modus aktualisiert.

In meinem Beispiel, wenn canvas.DrawCirlce() Methode ausführen, Paint nicht hat ein Xfermode Modell, so dass der gelbe Kreis direkt die Pixel in Canvas decken. Aber wenn wir canvas.DrawRect() anrufen, um ein Rechteck zu zeichnen, hat Paint einen Xfermode Wert PorterDuff.Mode.Clear. Dann Android wird ein Rechteck im Speicher zeichnen, die Pixel in diesem Rechteck hat einen Namen: Source. Das Rechteck im Speicher hat ein entsprechendes Rechteck in Canvas, das entsprechende Rechteck heißt: Ziel.

Der Wert des ARGB des Quellpixels und dem Wert des ARGB des Zielpixels entsprechend den durch Xfermode definierten Regeln berechnet, es wird die endgültige ARGB Wert zu berechnen. Aktualisieren Sie dann den Wert ARGB des Zielpixels mit dem endgültigen Wert ARGB.

In meinem Beispiel ist die XfermodePorterDuff.Mode.Clear es Zielpixel ARGB wird (0,0,0,0) erfordern, das bedeutet, es transparent ist. So verwenden wir canvas.DrawRect() Methode zeichnen ein transparentes Rechteck in Canvas, da Activity selbst eine weiße Hintergrundfarbe hat, so wird es ein weißes Rechteck zeigen.

EDIT:

die Funktion, die Sie im Bild Post zu implementieren, schreibe ich eine Demo:

protected override void OnDraw(Canvas canvas) 
{ 
    base.OnDraw(canvas); 

    Paint paint = new Paint(); 
    paint.SetARGB(255, 255, 0, 0); 
    RectF oval2 = new RectF(60, 100, 300, 200); 
    canvas.DrawOval(oval2, paint); 

    paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.*)); 

    Path path = new Path(); 
    paint.SetStyle(Paint.Style.Fill); 
    paint.SetARGB(255, 0, 0, 255); 

    path.MoveTo(180, 50); 
    path.LineTo(95, 240); 
    path.LineTo(255, 240); 
    path.Close(); 

    this.SetLayerType(LayerType.Software, null); 
    canvas.DrawPath(path, paint); 
    paint.SetXfermode(null); 
} 

Bei Verwendung unterschiedlicher Xfermode, ihre Wirkung:

Xor, SrcOut, Screen, Lighten, Darken, Add.

Wie Sie sehen können, könnten Sie verschiedene Farben und andere Xfermode verwenden, um Ihren Effekt zu erzielen.

+0

@JaanTohver, hast du dein Problem gelöst? –

+0

Brilliant. Ich danke dir sehr. "Dann wird Android ein Rechteck im Speicher zeichnen, die Pixel in diesem Rechteck haben einen Namen: Source. Das Rechteck im Speicher hat ein entsprechendes Rechteck in Canvas, das entsprechende Rechteck heißt: Ziel." Genau danach war ich. Ich dachte, Ziel war die gesamte Leinwand. – JaanTohver

Verwandte Themen