Tuto : Mask et Clipping d'une image via le layer
Philippe49
Membre
Jeudi 13 Août 2009, SDK iPhone 3.0, XCode 3.1
Voici un petit tuto pour masquer ou clipper les images. J'espère qu'il vous sera utile.
L'objet est de réaliser l'image ci-dessous :
• Une première solution est de réaliser le clipping dans la méthode pour dessiner un CALayer. Une première solution bis est de réaliser la même chose dans une UIView. On reportera dans drawRect: ce qui ici est fait dans drawInContext:
• Une seconde solution est d'utiliser la property mask d'un CALayer.
• Enfin, on ajoute un masque à une UIImageView.
N'est pas abordé dans cet article la solution qui consiste à reconstruire une image, ni celle qui consiste à sous-classer UIView. Merci d'avance des retours ...
Voici un petit tuto pour masquer ou clipper les images. J'espère qu'il vous sera utile.
L'objet est de réaliser l'image ci-dessous :
• Une première solution est de réaliser le clipping dans la méthode pour dessiner un CALayer. Une première solution bis est de réaliser la même chose dans une UIView. On reportera dans drawRect: ce qui ici est fait dans drawInContext:
• Une seconde solution est d'utiliser la property mask d'un CALayer.
• Enfin, on ajoute un masque à une UIImageView.
N'est pas abordé dans cet article la solution qui consiste à reconstruire une image, ni celle qui consiste à sous-classer UIView. Merci d'avance des retours ...
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
1) Créer un projet de type Window-Based Application, appelée ClippedImage.
2) Ouvrir IB, et ne rien faire d'autre que mettre le fond de la window en noir.
3) Importer le framework QuartzCore
4) Revenir à XCode, et créer une classe ClippedLayer dont l'interface ClippedLayer.h est
A ce stade, on peut compiler correctement ... pour voir le magnifique écran noir.
rq: Pour ne pas alourdir le tuto, je le fais dans applicationDidFinishLaunching:, ce qui dans une situation d'utilisation ne sera peut-être pas le meilleur endroit
Importer l'interface de ClippedLayer.h. Cela importe en même temps <QuartzCore/QuartzCore.h> qui se trouve en #import de ClippedLayer.h.
On traduit volontiers layer par calque au sens du dessinateur. Ainsi, comme une vue, un CALayer possède une frame. Le pendant de la property center d'une view est la property position d'un layer. Néammoins, si vous en avez besoin, la signification de la property position peut être changée grâce à la property anchorPoint.
Importer l'image qui servira de contenu au layer
1) Certaine options sont disponibles dans la doc de CALayer. On utilise par exemple la bordure rouge. Comme tout cela c'est du CoreGraphics, la couleur est une CGColor. Un moyen simple est d'utiliser les méthodes de UIColor, mais CGColorRef fournit également des possibilités exploitables.
2) On définit le contenu du layer comme étant l'image. La property contents est de type CGImeRef, type CoreGraphics qui est utilisé par QuartzCore. On passe par UIImage pour simplifier, et on en prend la CGImageRef associée par la property CGImage.
Enfin on ajoute le layer dans l'arborescence des layers, en utilisant window.layer qui est racine de cette arborescence.
A ce stade, cela ne peut marcher puisque le code de ClippedLayer n'est pas encore installé. Par contre, on peut obtenir le résultat ci-dessous, en faisant ces petites transformations :
Le message [layer display] a pour effet d'appeler la méthode - (void)drawInContext:(CGContextRef)ctx sur le ClippedLayer. C'est là que l'on fait le clipping.
En macro, on met des valeurs numériques.
Je définis ensuite la matrice de transformation qui retourne l'image. Cela est imposé par le système de coordonnées de l'écran de l'iPhone, qui est inversé par rapport au regard de l'utilisateur. CGContextConcatCTM() combine la Current Transform Matrix actuelle (ici l'identité) avec celle passée en argument.
On trace ensuite le polygone dans le contexte graphique
On applique le clipping
On dessine l'image
Build , Run & Enjoy
Seconde solution : utiliser la property mask
mask
An optional layer whose alpha channel is used as a mask to select between the layer's background and the result of compositing the layer's contents with its filtered background.
@property(retain) CALayer *mask
Discussion
Defaults to nil.
Special Considerations
When setting the mask to a new layer, the new layer's superlayer must first be set to nil, otherwise the behavior is undefined.
Availability
Available in iPhone OS 3.0b and later.
1) Créer un projet de type Window-Based Application, appelée ClippedImage.
2) Ouvrir IB, et ne rien faire d'autre que mettre le fond de la window en noir.
3) Importer le framework QuartzCore
4) Importer dans les ressources les images ci-dessous
Puis on crée le layer principal, sa géométrie, son contenu. (Les commentaires sur ce code sont faits plus haut)
On ajoute le layer à la subview, et le tour est joué
Ajouter un masque à une UIImageView
[EDIT] Une nuance : la doc sur la property mask d'un layer indique :
Special Considerations
When setting the mask to a new layer, the new layer's superlayer must first be set to nil, otherwise the behavior is undefined.
Avec un masque dont alpha varie en gradient :
J'ai également vu que c'était assez gourmand en ressources, notamment pour animer ce masque.
Que me conseilleriez-vous comme pistes ?
Merci par avance
Je souhaite réaliser un scale du masque.
Pour l'instant, j'ai quelque chose qui marche pas trop mal, en utilisant un layer et en lui mettant un masque, puis en créant une CAAnimation sur ce masque avec la clé "transform.scale".
Je tourne à 14-20 fps en faisant ce masque sur une image de la taille de l'écran.
Le seul gros souci c'est que je souhaite appliquer ensuite encore cette anim sur d'autres layers que je superpose, mais là ça lag pas mal.
Merci
+
Je me sers d'un group pour une autre animation, et elle ne pose aucun problème. J'ai juste un gros temps de calcul ^^
J'ai testé le cumul des CGPath sur une UIView, et ça lag à partir d'une cinquantaine sur le device. Je n'ai pas essayé le cumul dans une image pour en faire un mask, où on prend du temps pour faire l'image, mais où le display devrait être assez court.
Je vais regarder de ce côté là .
Après, je tenterai le cumul des masques, ça sonne bien ^^
Merci pour m'avoir aiguillé !