web-dev-qa-db-fra.com

Comment les morceaux du pipeline de dessin Canvas (2D) d'Android s'emboîtent-ils?

J'aimerais mieux comprendre comment les composants du pipeline de dessin Canvas d'Android (2D) s'emboîtent.

Par exemple, comment interagissent XferMode _, Shader , MaskFilter et ColorFilter ? Les documents de référence pour ces classes sont relativement clairsemés et ceux de Canvas et Paint n'ajoutent pas vraiment d'explication utile.

Je ne vois pas non plus clairement comment les opérations de dessin ayant des couleurs intrinsèques (par exemple: drawBitmap, par opposition aux primitives "vectorielles" telles que drawRect) entrent dans tout cela - ignorent-elles toujours la couleur de Paint et utilisent-elles leur couleur intrinsèque?

J'ai également été surpris par le fait que l'on peut faire quelque chose comme ceci:

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

Cela efface un ovale. Avant de remarquer cela, mon modèle mental consistait à dessiner sur un canevas (de manière conceptuelle) puis à dessiner un "calque" séparé, qui est ensuite composé avec le bitmap du canevas à l'aide du mode de transfert de Paint. Si c'était aussi simple que cela, alors le code ci-dessus effacerait le bitmap entier (dans la région de découpage) comme CLEAR always définit la couleur (et l'alpha) sur 0 indépendamment de l'alpha de la source. Cela implique donc qu’il existe un autre type de masquage pour limiter l’effacement à un ovale.

J'ai trouvé les API démos mais chaque démo fonctionne "en vase clos" et ne montre pas en quoi son objet (par exemple: XferModes) interagit avec d'autres éléments (par exemple: ColorFilters).

Avec suffisamment de temps et d’efforts, je pourrais comprendre de manière empirique comment ces éléments sont liés ou déchiffrés, mais j’espère que quelqu'un d’autre a déjà réglé le problème ou, mieux encore, qu’il existe une documentation sur le modèle pipeline/dessin qui J'ai raté.

Cette question a été inspirée en voyant le code dans cette réponse à une autre SO question }.

Mettre à jour

En cherchant de la documentation, je me suis rendu compte que, comme beaucoup de choses qui m'intéressent ici semblent être un mince placage sur skia , il y a peut-être une documentation en skia qui pourrait être utile. La meilleure chose que j'ai pu trouver est la documentation pour SkPaint qui dit:

Il existe 6 types d’effets pouvant être assignés à une peinture:

  • SkPathEffect - modifications apportées à la géométrie (chemin) avant qu'elle ne génère un masque alpha (par exemple, pointillé)
  • SkRasterizer - composition de calques de masque personnalisés (par exemple des ombres)
  • SkMaskFilter - modifications apportées au masque alpha avant qu'il ne soit coloré et dessiné (par exemple, flou, estampage)
  • SkShader - par exemple dégradés (linéaire, radial, balayage), motifs bitmap (clamp, repeat, mirror)
  • SkColorFilter - modifiez la ou les couleurs source avant d'appliquer le xfermode (par exemple, la matrice de couleurs)
  • SkXfermode - par exemple modes de transport porter-duff, modes de mélange

Ce n'est pas indiqué explicitement, mais j'imagine que l'ordre des effets est celui dans lequel ils apparaissent.

59
Laurence Gonsalves

Comme le disait Romain Guy, "Il est difficile de répondre à cette question sur StackOverflow". Il n'y avait pas vraiment de documentation complète, et une documentation complète serait assez volumineuse à inclure ici.

J'ai fini par lire la source et faire plusieurs expériences. J'ai pris des notes en cours de route et j'ai fini par les transformer en un document que vous pouvez voir ici:

ainsi que ce schéma:

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

C'est "non officiel", évidemment, donc les mises en garde normales s'appliquent.

Sur la base de ce qui précède, voici des réponses à certaines des "sous-questions":

Je ne vois pas non plus clairement comment Les opérations de dessin qui ont des couleurs Intrinsèques (par exemple: drawBitmap, par rapport aux primitives "Vector" telles que drawRect) s’inscrivent dans tout cela - est-ce qu'ils [toujours] ignorent toujours la couleur de Paint et utilisent plutôt leur couleur intrinsèque?

Les "couleurs source" proviennent de Shader. Dans drawBitmap, Shader est temporairement remplacé par un BitmapShader si un code non -ALPHA_8Bitmap est utilisé. Dans d'autres cas, si aucun Shader n'est spécifié, un Shader ne générant qu'une couleur unie, la couleur de Paint est utilisée.

J'ai également été surpris par le fait que Puisse faire quelque chose comme ceci:

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

Cela efface un ovale. Avant de remarquer Ceci, mon modèle mental était que dessiner Sur une toile (conceptuellement) dessine vers un "calque" séparé Puis que ce calque Est composé avec le bitmap de la toile en utilisant le mode de transfert de Paint. Si Était aussi simple que cela, le code Ci-dessus effacerait tout le bitmap (Dans la région de découpage) en tant que CLEAR toujours définit la couleur (et alpha) sur 0 quel que soit l'alpha de la source. Donc, Cela implique qu’il existe une Masque supplémentaire allant à Contraindre l’effacement à un ovale.

XferMode s'applique aux "couleurs source" (à partir de Shader) et aux "couleurs de destination" (à partir de Canvas 's Bitmap). Le résultat est ensuite fusionné avec la destination à l'aide du masque calculé dans Rasterization. Voir la phase de transfert dans le document ci-dessus pour plus de détails.

47

Il est difficile de répondre à cette question sur StackOverflow. Avant de commencer cependant, notez que les formes (drawRect () par exemple) n'ont PAS de couleur intrinsèque. Les informations de couleur toujours proviennent de l'objet Paint.

Cela efface un ovale. Avant de remarquer Ceci, mon modèle mental était que dessiner Sur une toile (conceptuellement) dessine vers un "calque" séparé Puis que ce calque Est composé avec le bitmap de la toile en utilisant le mode de transfert de Paint. Si Était aussi simple que cela, le code Ci-dessus effacerait le bitmap entier. alpha) à 0 quel que soit l'alpha de la source. Donc, Cela implique qu’il existe une Masque supplémentaire allant à Contraindre l’effacement à un ovale.

Votre modèle est un peu éteint. L'ovale n'est pas dessiné dans un calque séparé (sauf si vous appelez Canvas.saveLayer ()), il est dessiné directement sur le bitmap de support de Canvas. Le mode de transfert de la peinture est appliqué à chaque pixel dessiné par la primitive. Dans ce cas, seul le résultat de la pixellisation d'un ovale affecte le bitmap. Il n'y a pas de masquage spécial, l'ovale lui-même c'est le masque.

Quoi qu'il en soit, voici une vue simplifiée du pipeline:

  1. Primitive (rect, ovale, chemin, etc.)
  2. PathEffect
  3. Rastérisation
  4. MasqueFiltre
  5. Couleur/Shader/ColorFilter
  6. Xfermode

(Je viens de voir votre mise à jour et oui, ce que vous avez trouvé décrit les étapes du pipeline dans l'ordre.)

Le pipeline devient un peu plus compliqué lorsque vous utilisez des couches (Canvas.saveLayer ()), car le pipeline double. Vous commencez par parcourir le pipeline pour rendre vos primitives dans un bitmap hors écran (le calque), qui est ensuite appliqué au canevas en parcourant le pipeline.

11
Romain Guy

SkPathEffect - modifications apportées à la géométrie (chemin) avant qu'elle ne génère un masque alpha (par exemple, un tiret) SkRasterizer - composition de calques de masque personnalisés (par exemple, ombres) SkMaskFilter - modifications apportées au masque alpha avant sa coloration et dessiné (par exemple flou) SkShader - par exemple dégradés (linéaire, radial, balayage), motifs bitmap (clamp, repeat, mirror) SkColorFilter - modifiez la ou les couleurs source avant d'appliquer le xfermode (par exemple, la matrice de couleurs) SkXfermode - par exemple. modes de transport porter-duff, modes de mélange

http://imgur.com/0X5Yqod

0
Tường Vũ