webentwicklung-frage-antwort-db.com.de

Wie passen die Teile der Android (2D) Canvas-Zeichenpipeline zusammen?

Ich möchte besser verstehen, wie die Komponenten der Zeichenpipeline von Android (2D) Canvas zusammenpassen.

Wie interagieren beispielsweise XferMode , Shader , MaskFilter und ColorFilter ? Die Referenzdokumente für diese Klassen sind recht spärlich und die Dokumente für Canvas und Paint bieten keine wirklich nützliche Erklärung.

Mir ist auch nicht ganz klar, wie Zeichenoperationen mit Eigenfarben (z. B. drawBitmap im Vergleich zu den "Vektor" -Primitiven wie drawRect) in all dies passen - ignorieren sie immer die Farbe der Paint und verwenden stattdessen ihre Eigenfarbe?

Ich war auch überrascht, dass man so etwas machen kann:

Paint eraser = new Paint();
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rectF, eraser);

Dies löscht ein Oval. Bevor ich dies bemerkte, bestand mein mentales Modell darin, dass das Zeichnen auf einer Leinwand (konzeptionell) auf einer separaten "Ebene" erfolgt und diese Ebene dann mit der Bitmap der Leinwand unter Verwendung des Übertragungsmodus der Farbe zusammengesetzt wird. Wenn es so einfach wäre, würde der obige Code die gesamte Bitmap (innerhalb des Beschneidungsbereichs) als CLEAR löschen. always setzt die Farbe (und das Alpha) auf 0, unabhängig vom Alpha der Quelle. Dies impliziert also, dass es eine zusätzliche Art von Maskierung gibt, die das Löschen auf ein Oval beschränkt.

Ich habe die API-Demos gefunden, aber jede Demo funktioniert "im luftleeren Raum" und zeigt nicht, wie das, worauf sie sich konzentriert (zB: XferModes), mit anderen Dingen interagiert (zB: ColorFilters).

Mit genügend Zeit und Mühe konnte ich empirisch herausfinden, wie diese Stücke die Quelle in Beziehung setzen oder entschlüsseln, aber ich hoffe, dass jemand anderes dies bereits ausgearbeitet hat, oder besser noch, dass es eine tatsächliche Dokumentation der Pipeline/des Zeichnungsmodells gibt Ich vermisste.

Diese Frage wurde durch den Code in diese Antwort auf eine andere SO Frage inspiriert.

Aktualisieren

Als ich mich nach einer Dokumentation umsah, kam mir der Gedanke, dass das Zeug, an dem ich hier interessiert bin, ein ziemlich dünnes Furnier über skia zu sein scheint. Vielleicht gibt es eine Skia-Dokumentation, die hilfreich wäre. Das Beste, was ich finden konnte, ist die Dokumentation für SkPaint , in der steht:

Es gibt 6 Arten von Effekten, die einer Farbe zugewiesen werden können:

  • SkPathEffect - Änderungen an der Geometrie (Pfad), bevor eine Alphamaske generiert wird (z. B. Bindestrich)
  • SkRasterizer - Zusammenstellen von benutzerdefinierten Maskenebenen (z. B. Schatten)
  • SkMaskFilter - Änderungen an der Alpha-Maske, bevor diese eingefärbt und gezeichnet wird (z. B. Unschärfe, Prägung)
  • SkShader - z. Farbverläufe (linear, radial, Sweep), Bitmap-Muster (Clamp, Repeat, Mirror)
  • SkColorFilter - Ändern Sie die Quellfarbe (n), bevor Sie den XFermode anwenden (z. B. Farbmatrix).
  • SkXfermode - z.B. Porter-Duff-Transfermodi, Mischmodi

Es wird nicht explizit angegeben, aber ich vermute, dass die Reihenfolge der Effekte hier die Reihenfolge ist, in der sie in der Pipeline angezeigt werden.

59

Wie Romain Guy sagte: "Diese Frage ist auf StackOverflow schwer zu beantworten". Es gab nicht wirklich eine vollständige Dokumentation, und eine vollständige Dokumentation wäre hier ziemlich umfangreich.

Am Ende las ich die Quelle durch und machte eine Reihe von Experimenten. Ich machte unterwegs Notizen und verwandelte sie in ein Dokument, das Sie hier sehen können:

sowie dieses Diagramm:

http://xenomachina.com/Android-canvas-pipeline.png

Es ist offensichtlich "inoffiziell", daher gelten die normalen Vorbehalte.

Basierend auf den obigen Ausführungen finden Sie hier Antworten auf einige der "Unterfragen":

Es ist mir auch nicht ganz klar, wie Operationen mit intrinsischen Farben (zB: drawBitmap, im Vergleich zu den "Vektor" -Primitiven wie drawRect) Einfügen all das - ignorieren sie immer die Farbe der Paint und verwenden stattdessen ihre Eigenfarbe?

Die "Quellfarben" stammen aus Shader. In drawBitmap wird Shader vorübergehend durch BitmapShader ersetzt, wenn ein nicht -ALPHA_8Bitmap verwendet wird. In anderen Fällen wird, wenn keine Shader angegeben ist, eine Shader, die nur eine durchgehende Farbe erzeugt, die Farbe der Paint verwendet.

Ich war auch überrascht, dass Man so etwas tun kann:

Paint eraser = new Paint();
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rectF, eraser);

Dadurch wird ein Oval gelöscht. Bevor ich Bemerkte, war dies mein Denkmodell, dass das Zeichnen Auf eine Leinwand (konzeptuell) auf eine Separate "Ebene" zeichnet und dann diese Ebene Zusammengesetzt wird mit der Canvas-Bitmap im Paint-Übertragungsmodus. Wenn So einfach wäre wie das, dann würde der obige Code die gesamte Bitmap (Innerhalb des Beschneidungsbereichs) als CLEAR Immerlöschen. _ setzt die Farbe (und das Alpha) ungeachtet des Alphas der Quelle auf 0. Dies impliziert, dass es eine zusätzliche Maskierung gibt, die Das Löschen auf ein Oval beschränkt.

XferMode gilt für die "Quellfarben" (aus Shader) und die "Zielfarben" (aus Canvass Bitmap). Das Ergebnis wird dann mithilfe der in Rasterization berechneten Maske mit dem Ziel abgemischt. Weitere Informationen finden Sie in der Transferphase im obigen Dokument.

47

Diese Frage ist in StackOverflow nur schwer zu beantworten. Beachten Sie jedoch, dass Formen (zB drawRect ()) KEINE Eigenfarbe haben. Die Farbinformationen immer stammen vom Paint-Objekt.

Dadurch wird ein Oval gelöscht. Bevor ich Bemerkte, war dies mein Denkmodell, dass das Zeichnen Auf eine Leinwand (konzeptuell) auf eine Separate "Ebene" zeichnet und dann diese Ebene Zusammengesetzt wird mit der Canvas-Bitmap im Paint-Übertragungsmodus. Wenn So einfach wäre wie das, dann würde der obige Code die gesamte Bitmap (Innerhalb des Beschneidungsbereichs) löschen, da CLEAR Immer die Farbe (und alpha) bis 0 , unabhängig vom Alpha der Quelle. Dies impliziert, dass es eine zusätzliche Maskierung gibt, die Das Löschen auf ein Oval beschränkt.

Dein Modell ist ein bisschen daneben. Das Oval wird nicht in eine separate Ebene gezeichnet (sofern Sie nicht Canvas.saveLayer () aufrufen), wird es direkt in die Hintergrundbitmap der Canvas gezeichnet. Der Übertragungsmodus des Paint wird auf alle vom Grundelement gezeichneten Pixel angewendet. In diesem Fall wirkt sich nur das Ergebnis der Rasterung eines Ovals auf die Bitmap aus. Es gibt keine spezielle Maskierung, das Oval selbst ist die Maske.

Wie dem auch sei, hier ist eine vereinfachte Ansicht der Pipeline:

  1. Primitiv (rekt, oval, Pfad usw.)
  2. PathEffect
  3. Rasterung
  4. MaskFilter
  5. Farbe/Shader/Farbfilter
  6. Xfermode

(Ich habe gerade Ihr Update gesehen und ja, das, was Sie gefunden haben, beschreibt die Phasen der Pipeline in Reihenfolge.)

Die Pipeline wird bei Verwendung von Layern (Canvas.saveLayer ()) nur etwas komplizierter, da sich die Pipeline verdoppelt. Sie gehen zunächst durch die Pipeline, um Ihr (e) Primitiv (e) in einer Offscreen-Bitmap (der Ebene) darzustellen, und die Offscreen-Bitmap wird dann auf den Canvas angewendet, indem Sie durch die Pipeline gehen.

11
Romain Guy

SkPathEffect - Änderungen an der Geometrie (Pfad), bevor eine Alpha-Maske erstellt wird (z. B. Striche) SkRasterizer - Bestimmen benutzerdefinierter Maskenebenen (z. B. Schatten) SkMaskFilter - Änderungen an der Alpha-Maske, bevor sie eingefärbt wird und gezeichnet (zB Unschärfe) SkShader - zB Farbverläufe (linear, radial, Sweep), Bitmap-Muster (Klemmen, Wiederholen, Spiegeln) SkColorFilter - Ändern Sie die Quellfarbe (n) vor dem Anwenden des xfermode (z. B. Farbmatrix) SkXfermode - z. Porter-Duff-Transfermoden, Mischmodi

http://imgur.com/0X5Yqod

0
Tường Vũ