Application de peinture sur l'iPhone

2»

Réponses

  • DrakenDraken Membre
    04:23 modifié #32
    Le principe du coloriage est de dessiner un cercle de couleur sur l'écran à  chaque contact avec le doigt, comme si je déplaçais un "pinceau". A chaque tracé j'ai besoin de connaà®tre la couleur et la taille de ce pinceau. C'est pourquoi j'ajoute les propriétés couleurPinceau et taillePinceau à  mon code.

    //&nbsp; UneArdoiseViewController.h<br /><br />#import &lt;UIKit/UIKit.h&gt;<br /><br />@interface UneArdoiseViewController : UIViewController {<br />	UIImageView *m_imageDessinView;<br />	UIColor *couleurPinceau;<br />	NSInteger taillePinceau;<br />}<br /><br />@property (nonatomic, retain) UIColor *couleurPinceau;<br />@property (nonatomic, assign) NSInteger taillePinceau;<br /><br />@end
    


    Cela me permet d'accéder à  ces propriétés dans toutes les méthodes de UneArdoiseViewController.

    Ces propriétés doivent êtres définies avant l'utilisation, sous peine de plantages et autres désagréments. Il suffit d'écrire dedans.

    // DEFINITION DU PINCEAU<br />self.couleurPinceau = [UIColor blueColor];<br />self.taillePinceau = 30;
    


    J'ai modifié la méthode d'initialisation de l'application, afin d'y insérer le paramétrage du pinceau.

    // Implement viewDidLoad to do additional setup <br />// after loading the view, typically from a nib.<br />- (void)viewDidLoad {<br />&nbsp; &nbsp; [super viewDidLoad];<br />		<br />&nbsp;  // CHARGEMENT DE L&#039;IMAGE<br />&nbsp;  UIImage *image = [UIImage imageNamed:@&quot;image_guide.png&quot;];<br />&nbsp;  <br />&nbsp;  // CREATION CONTROLE GRAPHIQUE UIImageView<br />&nbsp;  m_imageDessinView = [[UIImageView alloc] initWithImage:image];<br />&nbsp;  <br />&nbsp;  // AJOUT DU CONTROLE SUR LA VUE COURANTE<br />&nbsp;  [self.view addSubview:m_imageDessinView];<br />&nbsp; <br />&nbsp;  // RELEASE DU CONTROLE GRAPHIQUE<br />&nbsp;  [m_imageDessinView release];<br /><br />&nbsp;  // DEFINITION DU PINCEAU<br />&nbsp;  self.couleurPinceau = [UIColor blueColor];<br />&nbsp;  self.taillePinceau = 30;<br />	<br />}<br />
    


  • DrakenDraken Membre
    04:23 modifié #33
    dans 1303396984:

    Pourquoi n'utilises-tu pas UIBezierPath ?
    De plus, il faut sans doute tracer des lignes entre deux points successifs pour obtenir un dessin continu.

    (Ah ben oui, Draken, tu t'exposes à  la critique  >:D)


    ça vas venir, t'inquiète. J'avance pas à  pas afin d'être plus compréhensible par les débutants. Il y a un CGContextStrokeLineSegments dans la version finale ! Et la critique est toujours bienvenu.

    Pas facile à  faire en surveillant les monstres en même temps. J'ai une horde de neveux et nièces sous ma garde pendant les vacances. Tu comprendras ta douleur quand ta petite aura 7 ou 8 ans de plus.

  • DrakenDraken Membre
    avril 2011 modifié #34
    Pour que l'application réagisse au contact du doigt, il faut gérer l'événement touchesBegan, envoyé par Cocoa Touch. Le code suivant récupère le message et détermine le point de contact:

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {<br />	UITouch *touch = [touches anyObject];<br />	CGPoint position = [touch locationInView:m_imageDessinView];<br /><br />	// Traitement du touch<br />&nbsp; &nbsp; &nbsp; &nbsp; // .....<br />}
    


    Il ne reste plus qu'à  dessiner un cercle à  la position de contact, en 8 étapes :

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {<br /><br />&nbsp; &nbsp; &nbsp;  // ETAPE 1 - RECUPERATION POINT DE TOUCH<br />	UITouch *touch = [touches anyObject];<br />	CGPoint point = [touch locationInView:m_imageDessinView];<br /><br />	// ETAPE 2 - PREPARATION DU CONTEXT GRAPHIQUE<br />	CGRect frameImage = m_imageDessinView.frame;<br />	UIGraphicsBeginImageContext(frameImage.size);<br />	<br />	// ETAPE 3 - DESSIN IMAGE DANS LE FOND DU CONTEXT GRAPHIQUE<br />	[m_imageDessinView.image drawAtPoint:CGPointMake(0, 0)];<br />	<br />	// ETAPE 4 - COULEUR DE DESSIN<br />	CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), self.couleurPinceau.CGColor);<br />	<br />	// ETAPE 5 - CALCUL DU RECTANGLE DE DESSIN<br />&nbsp; &nbsp; &nbsp; &nbsp; CGRect rectangleCercle = CGRectMake(point.x-self.taillePinceau/2, point.y-self.taillePinceau/2,<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  self.taillePinceau, self.taillePinceau);<br /><br />&nbsp; &nbsp; &nbsp;  // ETAPE 6 - DESSIN DU CERCLE <br />&nbsp; &nbsp; &nbsp;  CGContextFillEllipseInRect(UIGraphicsGetCurrentContext(), rectangleDessin);<br />	<br />	// ETAPE 7 - RECOPIE DU CONTEXT GRAPHIQUE DANS LE CONTROLE<br />	m_imageCoucheDessinView.image = UIGraphicsGetImageFromCurrentImageContext();<br />	<br />	// ETAPE 8 - FIN DES OPERATIONS GRAPHIQUES<br />	UIGraphicsEndImageContext();<br /><br />}
    


    Etape 3 : A sa création, "l'ardoise" d'un context graphique est vide. Il faut y recopier l'image avant de faire les opérations graphiques. La méthode drawAtPoint permet de dessiner une image UIImage à  n'importe quelle position d'un context graphique.

    // ETAPE 3 - DESSIN IMAGE DANS LE FOND DU CONTEXT GRAPHIQUE<br />[m_imageDessinView.image drawAtPoint:CGPointMake(0, 0)];
    


    Etape 4 : La couleur de tracé est fixée d'après le contenu de la propriété couleurPinceau.

    // ETAPE 4 - COULEUR DE DESSIN<br />CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), self.couleurPinceau.CGColor);
    


    Cette couleur peut être changée dans n'importe quelle méthode de UneArdoiseViewController. Par exemple, pourquoi ne pas tracer desz cercles verts ?

    self.pinceau = [UIColor greenColor];
    


    Etape 7 : Une fois les opérations graphiques terminées, il faut mettre à  jour le contenu de l'image. Comme je l'ai dit plus haut, on ne peut pas modifier une image. Heureusement, on peut l'effacer et la remplacer par une nouvelle. Cela se fait en une seule ligne :

    // ETAPE 7 - RECOPIE DU CONTEXT GRAPHIQUE DANS LE CONTROLE<br />m_imageDessinView.image = UIGraphicsGetImageFromCurrentImageContext();
    


    Cette ligne de code demande à  Cocoa Touch d'effacer l'image du contrôle graphique m_imageDessinView, pour la remplacer par le contenu du context graphique.

  • DrakenDraken Membre
    04:23 modifié #35
    Chaque fois que le doigt touche l'écran, un cercle bleu apparait au point de contact ! C'est un coloriage ... oui, mais plutôt limité. Le "barbouillage" ne se fait pas quand le doigt se déplace sur l'écran. C'est parce le message touchesBegan est émis uniquement lorsque le doigt commence à  toucher l'écran.

    Pour suivre les mouvements sur l'écran, il faut s'occuper du message touchesMoved, émis par Cocoa Touch à  chaque déplacement du doigt.

    On peut penser qu'il suffit de dessiner un point à  chaque événement touchesBegan et touchMoved, pour suivre les mouvements du doigt. C'est une mauvaise approche, comme le montre l'image suivante :

  • Ma123Ma123 Membre
    avril 2011 modifié #36
    salut.

    tous va bien sauf que le frontiere de l'image a été effacé par la couleur choisie.

  • DrakenDraken Membre
    04:23 modifié #37
    C'est normal que les frontières de l'image soit effacées, parce que le dessin sur une image efface les pixels s'y trouvant. Si tu dessines un pixel bleu sur un pixel noir appartenant à  la "frontière" il est replacé par le bleu, et pouf .. disparition des lignes noires.

    Pour éviter ça, il faut utiliser deux contrôles graphiques superposés, l'un contenant l'image guide et l'autre de zone de dessin. Mais il faut ruser pour gérer les problèmes de superposition graphique. Je traiterais du sujet à  la fin du tuto.

  • Ma123Ma123 Membre
    avril 2011 modifié #38
    le problème de frontiere est résolu.
    j'ai utilise deux image l'une png et l'autre jpg et de cette façon le problème est résolu
  • DrakenDraken Membre
    04:23 modifié #39
    Reprise des émissions après une longue nuit..

    iOS scrute l'état de l'écran tactile plusieurs dizaines de fois par seconde. C'est suffisant pour assurer une bonne réactivité, sauf dans le cas d'un suivi du déplacement au pixel prés. Si le doigt bouge très rapidement, l'événement touchesMoved fournit une série de points éloignés les uns des autres, disposés le long de la trajectoire. Concrètement on ne peut pas obtenir la liste de toutes les positions parcourues par le doigt, juste une approximation.

    Heureusement on peut reconstituer la courbe de déplacement en traçant des lignes entre chaque point. C'est un truc que les profs de mathématiques montrent souvent en traçant des courbes au tableau noir. Voici la manière de tracer une ligne entre deux points :

    // PARAMETRES DE LA LIGNE<br />CGPoint point1 = CGPointMake(10, 30);<br />CGPoint point2 = CGPointMake(100, 100);<br />UIColor *couleurLigne = [UIColor greenColor]; <br />NSInteger epaisseurLigne = 20;<br /><br />// LECTURE CONTEXTE GRAPHIQUE COURANT<br />CGContextRef context = UIGraphicsGetCurrentContext();<br /><br />// DEFINITION DE L&#039;EPAISSEUR DE LA LIGNE<br />CGContextSetLineWidth(context, epaisseurLigne);<br /><br />// DEFINITION COULEUR DE LA LIGNE<br />CGContextSetStrokeColorWithColor(context, couleurLigne.CGColor);<br />	<br />// FORME DES EXTREMITES DE LA LIGNE<br />CGContextSetLineCap(context, kCGLineCapRound);<br />	<br />// TRACE DE LA LIGNE<br />CGContextBeginPath(context);<br />CGContextMoveToPoint(context, pointDebut.x, pointDebut.y);<br />CGContextAddLineToPoint(context, pointFin.x, pointFin.y);<br />CGContextStrokePath(context);<br />
    


    L'instruction CGContextSetLineCap définit la forme des extrémités des lignes. Avec le paramètre kCGLineCapRound on a des extrémités sphériques, ce qui convient parfaitement à  l'application.

    J'ai écrit une méthode traçant une ligne entre deux points, tenant compte des propriétés couleurPinceau et taillePinceau.

    - (void) dessinLigneAvecPoint:(CGPoint)pointDebut pointFin:(CGPoint)pointFin<br />{<br />	// PREPARATION DU CONTEXT GRAPHIQUE<br />	CGRect frameImage = m_imageDessinView.frame;<br />	UIGraphicsBeginImageContext(frameImage.size);	<br />	CGContextRef context = UIGraphicsGetCurrentContext();<br />	<br />	// DESSIN IMAGE DANS LE FOND DU CONTEXT GRAPHIQUE<br />	[m_imageDessinView.image drawAtPoint:CGPointMake(0, 0)];<br />	<br />	// PARAMETRAGE COULEUR DE DESSIN<br />	CGContextSetStrokeColorWithColor(context, couleurDessin.CGColor);<br />	<br />	// EPAISSEUR TRACE<br />	CGContextSetLineWidth(context, taillePinceau);<br />	<br />	// FORME DES EXTREMITES<br />	CGContextSetLineCap(context, kCGLineCapRound);<br />	<br />	// TRACE LIGNE<br />	CGContextBeginPath(context);<br />	CGContextMoveToPoint(context, pointDebut.x, pointDebut.y);<br />	CGContextAddLineToPoint(context, pointFin.x, pointFin.y);<br />	CGContextStrokePath(context);<br />	<br />	// RECOPIE DU CONTEXT GRAPHIQUE DANS LE CONTROLE<br />	m_imageDessinView.image = UIGraphicsGetImageFromCurrentImageContext();<br />	<br />	// FIN DES OPERATIONS GRAPHIQUES<br />	UIGraphicsEndImageContext();<br />}<br />
    


    L'utilisation est simple :

    self.taillePinceau = 30;<br />self.couleurPinceau = [UIColor greenColor];<br /><br />CGPoint point1 = CGPointMake(20, 20);<br />CGPoint point2 = CGPointMake(100, 100);<br /><br />[self dessinLigneAvecPoint:point1 pointFin:point2];<br />
    


    Il ne reste plus qu'à  modifier la gestion des événements touchesBegan et touchesMoved.

    <br />- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {<br />	UITouch *touch = [touches anyObject];<br />	CGPoint position = [touch locationInView:m_imageDessinView];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; // DESSIN CERCLE DE COULEUR	<br />	[self dessinLigneAvecPoint:position pointFin:position];<br />&nbsp; &nbsp; &nbsp; &nbsp; // MEMORISATION POSITION DE CONTACT<br />	m_pointCourant = position;<br />}
    


    C'est plus simple que le code précédent. J'emploie une petite astuce pour tracer le cercle de couleur: dessiner une ligne partant et finissant du même point. Cela trace une ligne d'un seul pixel, entourée d'un cercle de l'épaisseur du pinceau !

    La méthode mémorise la position du point de contact dans une variable d'instance, définie dans le fichier UneArdoiseViewController.h. L'application en a besoin pour tracer la ligne au prochain événement touchesMoved.

    //&nbsp; UneArdoiseViewController.h<br />//&nbsp; UneArdoise<br /><br />#import &lt;UIKit/UIKit.h&gt;<br /><br />@interface UneArdoiseViewController : UIViewController {<br />	UIColor *couleurPinceau;<br />	NSInteger taillePinceau;<br />	UIImageView *m_imageDessinView;<br />	CGPoint m_pointCourant; // &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; ICI LA NOUVELLE VARIABLE<br />}<br /><br />@property (nonatomic, retain) UIColor *couleurPinceau;<br />@property (nonatomic, assign) NSInteger taillePinceau;<br /><br />@end<br />
    


    Le traitement du message touchesMoved est presque identique à  touchesBegan, à  ceci prés que la méthode dessine la ligne entre la position précédente de contact et la position actuelle.

    - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{<br />	UITouch *touch = [touches anyObject];<br />	CGPoint position = [touch locationInView:m_imageDessinView];<br />	<br />&nbsp; &nbsp; &nbsp; &nbsp; // DESSIN LIGNE ENTRE LE POINT PRECEDENT ET LE POINT ACTUEL<br />	[self dessinLigneAvecPoint:m_pointCourant pointFin:position];<br />&nbsp; &nbsp; &nbsp; &nbsp; // MEMORISATION POINT COURANT<br />	m_pointCourant = position;<br />}<br />
    


    Avec les modifications, l'application dessine une trajectoire "sans trous", même si le doigt bouge très vite. Le coloriage est correct, comme le montre l'image suivante :

  • Ma123Ma123 Membre
    avril 2011 modifié #40
    Bonjour,
    Merci beaucoup pour le tutorial,
    j'ai quelque question
    -Comment diviser l'image en plusieurs zone.
    -colorer chaque zone par un couleur sans dépasser la frontiere de la zone.
    -a quoi sert le CGContextStrokeLineSegments

  • DrakenDraken Membre
    avril 2011 modifié #41
    CGContextStrokeLineSegments permet de tracer des lignes, de la même manière que CGContextMoveToPoint et CGContextAddLineToPoint. Mais son utilisation est plus complexe.

    Tu peux préciser ce que tu appelles plusieurs zones ?

    Pour le coloriage par une couleur sans dépasser la frontière de la zone, je n'ai pas d'idée simple. Je continue à  y réfléchir.

  • Ma123Ma123 Membre
    avril 2011 modifié #42
    @draken
    ok merci beaucoup pour tous les réponses.

  • DrakenDraken Membre
    avril 2011 modifié #43
    Si tu veux utiliser CGContextStrokeLineSegments dans ton application, efface le code de tracé de lignes de ton application

    // TRACE DE LA LIGNE<br />CGContextBeginPath(context);<br />CGContextMoveToPoint(context, pointDebut.x, pointDebut.y);<br />CGContextAddLineToPoint(context, pointFin.x, pointFin.y);<br />CGContextStrokePath(context);<br />
    

    Et remplace-le par celui-ci :

    // TRACE LIGNE<br />CGPoint listePoint[2] = {pointDebut, pointFin};<br />CGContextStrokeLineSegments(context, listePoint, 2);
    


  • Ma123Ma123 Membre
    mai 2011 modifié #44
    Bonjour
    j'ai veux modifier l'alpha des extrémités si c'est possible .

    Et j'aime faire de chose comme ca (image 0062)



    Merci
  • DrakenDraken Membre
    04:23 modifié #45
    Pour reproduire le même effet que l'image 0062, il faut superposer deux images et modifier l'alpha de dessin dans le context graphique. Pas compliqué. Je t'explique ça ce soir.


  • Ma123Ma123 Membre
    mai 2011 modifié #46
    j'ai modifie l'alpha de dessin dans le context graphique:CGContextSetAlpha(context, 0.5);
    mais pas le resultat

  • DrakenDraken Membre
    04:23 modifié #47
    A première vue, je pense que ce coloriage a été réalisé avec un pattern. C'est une petite image que le système peut utiliser à  la place d'une couleur pour dessiner des formes géométriques. Je ne connais pas trop le sujet, tu trouveras des informations sur la manière de les utiliser dans la doc Apple.

    http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html

  • Ma123Ma123 Membre
    mai 2011 modifié #48
    Bonjour,
    comment modifié l'alpha de dessin avec le context graphique.
     
    Cordialement.



  • DrakenDraken Membre
    04:23 modifié #49
    Chez moi ça marche. J'ai repris la méthode de tracé de ligne pour ajouter un facteur de transparence.

    <br />- (void) dessinLigneAvecPoint:(CGPoint)pointDebut pointFin:(CGPoint)pointFin<br />{<br />	// PREPARATION DU CONTEXT GRAPHIQUE<br />	CGRect frameImage = m_imageCoucheDessinView.frame;<br />	UIGraphicsBeginImageContext(frameImage.size);	<br />	CGContextRef context = UIGraphicsGetCurrentContext();<br />	<br />	// DESSIN IMAGE DANS LE FOND DU CONTEXT GRAPHIQUE<br />	[m_imageCoucheDessinView.image drawAtPoint:CGPointMake(0, 0)];<br />	<br />	// PARAMETRAGE COULEUR DE DESSIN<br />	CGContextSetStrokeColorWithColor(context, couleurPinceau.CGColor);<br />	<br />	// EPAISSEUR TRACE<br />	CGContextSetLineWidth(context, taillePinceau);<br />	<br />	// FORME DES EXTREMITES<br />	CGContextSetLineCap(context, kCGLineCapRound);<br />	<br />	CGContextSetAlpha(context, 0.5f); &lt;&lt;&lt;&lt;&lt;&lt;&lt; ICI LE CHANGEMENT ALPHA	<br />	<br />	// TRACE POINT<br />	CGContextBeginPath(context);<br />	CGContextMoveToPoint(context, pointDebut.x, pointDebut.y);<br />	CGContextAddLineToPoint(context, pointFin.x, pointFin.y);<br />	CGContextStrokePath(context);<br />	<br />	// RECOPIE DU CONTEXT GRAPHIQUE DANS LE CONTROLE<br />	m_imageCoucheDessinView.image = UIGraphicsGetImageFromCurrentImageContext();<br />	<br />	// FIN DES OPERATIONS GRAPHIQUES<br />	UIGraphicsEndImageContext();<br />}<br />
    




  • 04:23 modifié #50
    C'est vraiment à  vomir les méthodes écrites en français  :'(
  • DrakenDraken Membre
    04:23 modifié #51
    Raciste !  >:)

  • AliGatorAliGator Membre, Modérateur
    04:23 modifié #52
    dans 1305967824:

    C'est vraiment à  vomir les méthodes écrites en français  :'(
    +100
  • DrakenDraken Membre
    04:23 modifié #53
    dans 1306066520:

    dans 1305967824:

    C'est vraiment à  vomir les méthodes écrites en français  :'(
    +100

    Sectaire !  >:)
  • AliGatorAliGator Membre, Modérateur
    04:23 modifié #54
    Non, ingénieur qualité.
  • DrakenDraken Membre
    04:23 modifié #55
    Quand je bossais chez Matra Missile pour un stage, j'ai entendu parler d'un ingénieur qui avais conçu un système de climatisation pour un entrepôt. Un système de rêve, bien au delà  des exigences du cahier de charges. Une fois installé, cela a fonctionné un peu avant de s'effondrer. La climatisation était sur le toit de l'entrepôt et le gars avait oublié de tenir compte de la charge maximale de la toiture.  :D

    A ton avis, il était "qualité" cet ingénieur ou pas ? D'ailleurs, y a-t-il une norme Afnor pour les ingénieurs qualités ?
  • AliGatorAliGator Membre, Modérateur
    04:23 modifié #56
    Ca me rappelle les fameux projets où certains écrivaient en français et d'autres en anglais, et surtout certains exprimaient les mesures en pouces et d'autres en cm. Résultat, une catastrophe au final (pas au point de faire exploser une fusée comme l'exemple mythique qui a eu le mm pb, mais pas loin)
  • Ma123Ma123 Membre
    04:23 modifié #57
    thank you @ldesroziers
Connectez-vous ou Inscrivez-vous pour répondre.