Connaà®tre la valeur héxadécimal à  partir d'une couleur et vice-versa

TchouboudouTchouboudou Membre
23:23 modifié dans API AppKit #1
Bonjour...

Je me demandais en cours (parce que c'est pas très interessant l'EPS), comment connaà®tre la valeur héxadécimal d'une couleur. J'ai donc regarder la classe NSColor. Grâce à  mon fameux anglais, j'ai pas trouvé... C'est pourquoi je m'adresse à  vous, pour m'aider dans la recherche de la solution.

Merci d'avance,
Tchouboudou

By the way, je sais pas si j'ai posté dans le bon forum

Réponses

  • AliGatorAliGator Membre, Modérateur
    23:23 modifié #2
    Hello,

    Le plus simple est d'utiliser stringWithFormat en utilisant le format "%02X" pour écrire un octet sous forme décimale.
    Pour info, le "X" indiquant le format hexadécimal (x minuscule si tu prèfères que les lettres a-f soient en minuscules et non majuscules), le "2" indiquant que tu le veux sur 2 caractères minimum, et le "0" devant indiquant que si le résultat est sur moins de 2 caractères, tu remplis les blancs avec des zéros (et non pas des espaces comme c'est le cas par défaut)

    Comme les composantes d'une couleur sont stoquées sous forme de float entre 0.0 et 1.0 en Cocoa, il faut les multiplier par 255 avant de les convertir en hexa.

    Ce qui donne un truc comme :
    -(NSString)hexForColor:(NSColor*)color<br />{<br />&nbsp; return [NSString stringWithFormat: @&quot;%02X%02X%02X&quot;, [color redComponent]*255, [color greenComponent]*255, [color blueComponent]*255];<br />}
    
    (Attention à  ce que ta NSColor passée utilise le RGBColorSpace)
  • TchouboudouTchouboudou Membre
    23:23 modifié #3
    Et pour le contraire ?
  • AliGatorAliGator Membre, Modérateur
    septembre 2006 modifié #4
    Pour la conversion en sens inverse tu as la méthode "pur C" (avec sscanf, par exemple), ou tu as la classe NSScanner et sa méthode scanHexInt entre autres.
    Quitte à  les scanner 2 caractères par 2 caractères, pour récupérer 3 ints distincs, qu'il suffira ensuite de diviser par 255.0 pour avoir les valeurs des composantes red, green, blue.
    Je te laisse regarder ça si c'est ça que tu veux, histoire de t'entraà®ner à  chercher un peu aussi (et puis surtout parce que je suis loin d'être un as de NSScanner :))
  • BruBru Membre
    septembre 2006 modifié #5
    dans 1159368965:

    [...] ou tu as la classe NSScanner et sa méthode scanHexInt entre autres.
    Quitte à  les scanner 2 caractères par 2 caractères, pour récupérer 3 ints distincs, qu'il suffira ensuite de diviser par 255.0 pour avoir les valeurs des composantes red, green, blue.
    Je te laisse regarder ça si c'est ça que tu veux, histoire de t'entraà®ner à  chercher un peu aussi (et puis surtout parce que je suis loin d'être un as de NSScanner :))


    Très bien Ali !
    Et rien que pour toi, je te donne l'exemple de NSScanner selon ce que tu viens de dire !

    <br />{<br />    NSString *hexcol;<br />    NSScanner *scan;<br />    float rvb[0];<br />    int ix, composant;<br />    BOOL ok;<br />    NSColor *color;<br /><br />    hexcol=@&quot;C0B508&quot;;<br /><br />    for (ix=0; i&lt;3; ix++)<br />    {<br />        scan=[NSScanner scannerWithString:[hexcol substringWithRange:NSMakeRange(ix*2, 2)]];<br />        ok=[scan scanHexInt:&amp;composant]<br />        if (ok==NO) break;<br />        rvb[ix]=(float)composant/255.0;<br />    }<br /><br />    if (ok==YES) color=[NSColor colorWithDeviceRed:rvb[0] green:rvb[1] blue:rvb[2] alpha:1];<br />}<br />
    


    Tu verras, NSScanner deviendra vite ton ami...


    Edit : correction suite au post de Ali.
    .
  • TchouboudouTchouboudou Membre
    23:23 modifié #6
    J'ai ce code, mais il ne marche pas. Je sais pas si ce sont les couleurs qui se rajoutent ou si c'est le stringWithFormat qui merde :

    #import &quot;Controller.h&quot;<br /><br />@implementation Controller<br /><br />- (IBAction)colorText:(id)sender<br />{<br />	[textField setStringValue:@&quot;&quot;];<br />	[textField setStringValue:[self textHexadecimalWithColor:[sender color]]];<br />	NSLog(@&quot;%@&quot;, [self textHexadecimalWithColor:[sender color]]);<br />}<br /><br />- (IBAction)textColor:(id)sender<br />{<br />	return;<br />}<br /><br />- (NSString *)textHexadecimalWithColor:(NSColor *)aColor<br />{<br />	return [NSString stringWithFormat: @&quot;%02X%02X%02X&quot;, [aColor redComponent]*255, [aColor greenComponent]*255, [aColor blueComponent]*255];<br />}<br /><br />@end<br />
    
  • AliGatorAliGator Membre, Modérateur
    septembre 2006 modifié #7
    Bru : merci pour l'exemple... par contre j'ai l'impression que tu as oublié de multiplier ix par 2 quand tu prend le NSMakeRange, je me trompe ?

    Tchouboudou : tu as vérifié que ta NSColor utilisait le RGBColorSpace ? (si ce n'est pas le cas, tu auras une exception et donc à  priori plantage ensuite). Il aurait été intéressant de faire un NSLog de [sender color] aussi, par exemple...
    Je t'avais mis le code simpliste, mais un petit "NSColor* rgbColor = [aColor colorUsingColorSpaceName]" avant d'en extraire les red,green et blue components (de cette rgbColor et non plus de aColor du coup) serait quand même mieux pour s'assurer d'être dans le bon ColorSpace avant (plutôt que de le présupposer)

    Ceci dit tu ne nous aide pas beaucoup : quand tu dis "le code ne marche pas", tu peux préciser ce que tu entends par là  ? Il ne se compile pas, le compilo affiche des erreurs (si oui lesquelles !) ? Il plante au moment de l'execution (si oui avec quel message) ? Tu as testé les méthodes isolément ?
  • TchouboudouTchouboudou Membre
    23:23 modifié #8
    ça plante pas, ça met des nombres de plus de 6 chiffres...

    Voilà  ce que affiche [sender color] : NSCalibratedRGBColorSpace 0.8985 1 0.241167 1
  • AliGatorAliGator Membre, Modérateur
    septembre 2006 modifié #9
    Ah ouais j'suis bête moi : doit falloir caster ça en type octet avant (au moins int), sinon il risque de lire les float comme si c'était des entiers :P
    En effet le résultat d'un float (valeur de chaque composante, entre 0.0 et 1.0) multiplié par un entier est un float. Et moi je demande à  stringWithFormat de l'interpréter comme un entier (et de l'écrire sous forme hexa), donc en gros de lire la mémoire comme si les bits qu'il y a dans la variable représentaient un entier, alors qu'en fait ils représentent un float. C'est là  qu'il s'embrouille...

    Conclusion : il faut caster chaque composante qu'on a multiplié par 255 en un type entier, voire même plus précisément un [tt](unsigned char)[/tt] (puisque ça n'est codé que sur un octet, ne prenant que des valeurs entre 0x00 et 0xFF pour chaque composante).

    -(NSString*)hexValueForColor:(NSColor*)aColor<br />{<br />	NSColor* rgbCol = [aColor colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];<br />	return [NSString stringWithFormat: @&quot;%02X%02X%02X&quot;,<br />		(unsigned char)([rgbCol redComponent]*255),<br />		(unsigned char)([rgbCol greenComponent]*255),<br />		(unsigned char)([rgbCol blueComponent]*255)<br />		];<br />}
    


    [EDIT]Je viens à  l'instant de tester le code pour le valider, c'était bien ça. J'ai aussi rajouté la conversion en couleur RGB pour éviter tout souci.
  • BruBru Membre
    23:23 modifié #10
    dans 1159375783:

    Bru : merci pour l'exemple... par contre j'ai l'impression que tu as oublié de multiplier ix par 2 quand tu prend le NSMakeRange, je me trompe ?


    Non, tu ne te trompes pas.
    Merci à  ton oeil perspicace.

    .
  • AliGatorAliGator Membre, Modérateur
    septembre 2006 modifié #11
    dans 1159392227:
    Merci à  ton oeil perspicace.
    Je lui dirai :) (s'il veut bien m'écouter  :o )

    :o

    PS: Sinon pour ceux qui n'auraient pas vu mon edit (surtout Tchouboudou), j'ai rajouté plus haut la conversion de la couleur dans l'espace RGB avant d'en extraire les composantes, par sécurité et pour rendre la méthode générique.
  • TchouboudouTchouboudou Membre
    23:23 modifié #12
    Merci beaucoup les gens...
  • RocouRocou Membre
    23:23 modifié #13
    dans 1159372815:


    <br />{<br />&nbsp; &nbsp; NSString *hexcol;<br />&nbsp; &nbsp; NSScanner *scan;<br />&nbsp; &nbsp; float rvb[0];<br />&nbsp; &nbsp; int ix, composant;<br />&nbsp; &nbsp; BOOL ok;<br />&nbsp; &nbsp; NSColor *color;<br /><br />&nbsp; &nbsp; hexcol=@&quot;C0B508&quot;;<br /><br />&nbsp; &nbsp; for (ix=0; ix&lt;3; ix++)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; scan=[NSScanner scannerWithString:[hexcol substringWithRange:NSMakeRange(ix*2, 2)]];<br />&nbsp; &nbsp; &nbsp; &nbsp; ok=[scan scanHexInt:&amp;composant]<br />&nbsp; &nbsp; &nbsp; &nbsp; if (ok==NO) break;<br />&nbsp; &nbsp; &nbsp; &nbsp; rvb[ix]=(float)composant/255.0;<br />&nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; if (ok==YES) color=[NSColor colorWithDeviceRed:rvb[0] green:rvb[1] blue:rvb[2] alpha:1];<br />}<br />
    



    Bonjour,

    Je me permets de faire remonter ce fil car le code de bru est exactement ce que je cherchais.
    Le problème est qu'il ne se compile pas...
    cette ligne:
    ok=[scan scanHexInt:&composant]
    me donne comme warning:
    "passing argument 1 of 'scanHexInt:' makes pointer from integer without a cast"

    C'est sans doute tout con mais je n'arrive pas à  résoudre ce problème  :why?:
  • Philippe49Philippe49 Membre
    23:23 modifié #14
    il te dit que l'argument que tu as pris n'est pas un pointeur mais un entier : Tu n'as pas du mettre le symbole & devant composant , par exemple.

    Ce code est bien compliqué . Le C fournit direct

    char * hexcol="C0B508";
    long composant=strtol(hexcol,NULL,16);

    et si hexcol est une NSString prendre [hexcol UTF8String].
  • Philippe49Philippe49 Membre
    juin 2009 modifié #15
    ou
    char * hexcol="C0B508";
    unsigned int composant;
    sscanf(hexcol,"%x",&composant);
  • RocouRocou Membre
    juin 2009 modifié #16
    dans 1245074599:

    il te dit que l'argument que tu as pris n'est pas un pointeur mais un entier : Tu n'as pas du mettre le symbole & devant composant , par exemple.

    Comme tu peux le voir, ce n'est pas le cas, j'ai bien mis le symbole & devant composant. Je sais bien que tu as mis "par exemple" mais je ne vois pas du tout ce que cela peut-être d'autre  :)

    dans 1245074599:

    Ce code est bien compliqué . Le C fournit direct

    char * hexcol="C0B508";
    long composant=strtol(hexcol,NULL,16);

    et si hexcol est une NSString prendre [hexcol UTF8String].

    Je n'ai pas compris. Que fait-on de composant après? C'est un long qui contient la valeur décimale de hexcol mais peut-on créer une variable NSColor avec un long?

    EDIT: [[hexcol substringWithRange:NSMakeRange(ix*2, 2)] UTF8String]  ::)

    Bon, ça marchotte. je dessine bien avec la couleur choisie mais si je scrolle dans ma vue ou si j'agrandis ma fenêtre: boom! ça plante.  :-\\
    Mon NSColor ne doit pas avoir une valeur très catholique. (car si je mets un NSColor bidon, du style redColor, tout fonctionne)
  • Philippe49Philippe49 Membre
    23:23 modifié #17
    dans 1245081448:

    Comme tu peux le voir, ce n'est pas le cas, j'ai bien mis le symbole & devant composant. Je sais bien que tu as mis "par exemple" mais je ne vois pas du tout ce que cela peut-être d'autre  :)

    Comment as-tu déclaré composant ?
  • RocouRocou Membre
    23:23 modifié #18
    dans 1245082065:

    dans 1245081448:

    Comme tu peux le voir, ce n'est pas le cas, j'ai bien mis le symbole & devant composant. Je sais bien que tu as mis "par exemple" mais je ne vois pas du tout ce que cela peut-être d'autre  :)

    Comment as-tu déclaré composant ?

    int composant car scanHexInt: l'exige
  • Philippe49Philippe49 Membre
    juin 2009 modifié #19
    dans 1245081448:

    dans 1245074599:

    Ce code est bien compliqué . Le C fournit direct

    char * hexcol="C0B508";
    long composant=strtol(hexcol,NULL,16);

    et si hexcol est une NSString prendre [hexcol UTF8String].

    Je n'ai pas compris. Que fait-on de composant après? C'est un long qui contient la valeur décimale de hexcol mais peut-on créer une variable NSColor avec un long?

    Oui , je n'avais lu que la partie "lecture de la valeur composant" pas la couleur.
    Pour la couleur, je mettrais :
    <br />#define hexvalue(a) (((a)&gt;=&#39;0&#39; &amp;&amp; (a)&lt;=&#39;9&#39;) ? ((a)-&#39;0&#39;) : ((a)&gt;=&#39;a&#39; &amp;&amp; (a)&lt;=&#39;f&#39;) ? ((a)-&#39;a&#39;+10) :  ((a)&gt;=&#39;A&#39; &amp;&amp; (a)&lt;=&#39;F&#39;) ? ((a)-&#39;A&#39;+10) : 0)  <br />[NSColor colorWithDeviceRed:(hexvalue (hexcol[0])*16+ hexvalue (hexcol[1]))/255. green:(hexvalue(hexcol[2])*16+ hexvalue(hexcol[3]))/255. blue:(hexvalue(hexcol[4])*16+ hexvalue(hexcol[5]))/255. alpha:1];
    

  • Philippe49Philippe49 Membre
    juin 2009 modifié #20
    dans 1245082369:

    int composant car scanHexInt: l'exige


    Résumons
    composant est un int
    &composant est donc un  pointeur sur int
    et le prototype de scanHexInt est
    - (BOOL)scanHexInt:(unsigned *)intValue

    Essaie  avec composant déclaré en unsigned int ou en castant (unsigned *) &composant  
  • Philippe49Philippe49 Membre
    23:23 modifié #21
    [EDIT] pour la couleur, j'ai oublié de faire la transformtion caractère--> valeur numérique
    c'est fait.
  • RocouRocou Membre
    juin 2009 modifié #22
    dans 1245082531:

    Pour la couleur, je mettrais :
    <br />#define hexvalue(a) (((a)&gt;=&#39;0&#39; &amp;&amp; (a)&lt;=&#39;9&#39;) ? ((a)-&#39;0&#39;) : ((a)&gt;=&#39;a&#39; &amp;&amp; (a)&lt;=&#39;f&#39;) ? ((a)-&#39;a&#39;+10) :&nbsp; ((a)&gt;=&#39;A&#39; &amp;&amp; (a)&lt;=&#39;F&#39;) ? ((a)-&#39;A&#39;+10) : 0)&nbsp; <br />[NSColor colorWithDeviceRed:(hexvalue (hexcol[0])*16+ hexvalue (hexcol[1]))/255. green:(hexvalue(hexcol[2])*16+ hexvalue(hexcol[3]))/255. blue:(hexvalue(hexcol[4])*16+ hexvalue(hexcol[5]))/255. alpha:1];
    



    Pour le coup, je trouve cela peu lisible (et ton code ne se compile pas). J'ai préféré opter pour ton idée à  base de strtol et j'ai écrit une méthode très simple, au code certainement peu apprécié des adorateurs de la factorisation à  outrance  :)

    -(NSColor*)ColorValueForHex:(NSString*)hexcol<br />{<br /><br />	CGFloat c1,c2,c3;<br />	c1=strtol([[hexcol substringWithRange:NSMakeRange(0, 2)] UTF8String],NULL,16);<br />	c2=strtol([[hexcol substringWithRange:NSMakeRange(2, 2)] UTF8String],NULL,16);<br />	c3=strtol([[hexcol substringWithRange:NSMakeRange(4, 2)] UTF8String],NULL,16);<br /><br />	return [NSColor colorWithDeviceRed:c1/255.0 green:c2/255.0 blue:c3/255.0 alpha:1];<br />	<br />}<br />
    


    J'ai abandonné l'utilisation du NSScanner que je ne maà®trise pas du tout.

    (Sinon, je vais ouvrir un autre fil car je rencontre un problème avec NSColor similaire à  celui que j'avais connu avec NSDate)
  • Philippe49Philippe49 Membre
    juin 2009 modifié #23
    Ok, tu peux la mettre en catégorie

    @interface NSColor(RocouAdditions)
    +(NSColor*)ColorValueForHex:(NSString*)hexcol;
    @end

    @implementation NSColor(RocouAdditions)
    +(NSColor*)ColorValueForHex:(NSString*)hexcol
    {

    CGFloat c1,c2,c3;
    c1=strtol([[hexcol substringWithRange:NSMakeRange(0, 2)] UTF8String],NULL,16);
    c2=strtol([[hexcol substringWithRange:NSMakeRange(2, 2)] UTF8String],NULL,16);
    c3=strtol([[hexcol substringWithRange:NSMakeRange(4, 2)] UTF8String],NULL,16);

    return [NSColor colorWithDeviceRed:c1/255.0 green:c2/255.0 blue:c3/255.0 alpha:1];

    }
    @end
  • RocouRocou Membre
    23:23 modifié #24
    dans 1245135980:

    Ok, tu peux la mettre en catégorie

    @interface NSColor(RocouAdditions)
    +(NSColor*)ColorValueForHex:(NSString*)hexcol;
    @end

    @implementation NSColor(RocouAdditions)
    -(NSColor*)ColorValueForHex:(NSString*)hexcol
    {

    CGFloat c1,c2,c3;
    c1=strtol([[hexcol substringWithRange:NSMakeRange(0, 2)] UTF8String],NULL,16);
    c2=strtol([[hexcol substringWithRange:NSMakeRange(2, 2)] UTF8String],NULL,16);
    c3=strtol([[hexcol substringWithRange:NSMakeRange(4, 2)] UTF8String],NULL,16);

    return [NSColor colorWithDeviceRed:c1/255.0 green:c2/255.0 blue:c3/255.0 alpha:1];

    }
    @end

    Ha oui, j'oublie toujours; merci beaucoup!
  • AliGatorAliGator Membre, Modérateur
    juin 2009 modifié #25
    Je suggère "colorForHexValue:" (et avec un "c" minuscule en première lettre" histoire de respecter au plus près les conventions de nommage préconisées et utilisées par Apple... et bien mettre un "+" (car méthode de classe)... aussi dans @implementation ;)

    Sinon j'ai pas essayé, mais strtol ne sait pas prendre une chaà®ne hexa à  6 caractères ? (et donc retourner un long entre 0 et 256^3) ? Si c'est le cas j'aurais plutôt demandé à  strtol de convertir tout d'un coup, et découpé (avec des masques) le résultat après, genre :
    long v = strtol([hexcol UTF8String],NULL,16);<br />char r = (v&gt;&gt;16) &amp; 0xFF;<br />char g = (v&gt;&gt;8) &amp; 0xFF;<br />char b = c &amp; 0xFF;<br />return [NSColor colorWithDeviceRed:(r/255.f) green:(g/255.f) blue:(b/255.f) alpha:1.f];
    
    L'avantage de cette méthode est qu'elle sait aussi gérer les cas où hexcol commence par "0x", genre une valeur hexa "0x123456".

    Après pour faire propre et avoir une catégorie assez générique, on peut rajouter la gestion de l'alpha, de manière automatique tant qu'à  faire :
    long v = strtol([hexcol UTF8String],NULL,16);<br /><br />char a = 255; // valeur de l&#39;alpha si pas présent dans hexcol<br />// j&#39;avais pensé vérifier que (v&gt;0xFFFFFF) mais si on a &quot;0x0000FF88&quot; ça marchera pas...<br />BOOL isRGBA = ( /* &quot;0x12345678&quot; */ ([hexcol length]==10) || /* &quot;12345678&quot; */ (([hexcol length]==8)&amp;&amp;(![hexcol hasPrefix:@&quot;0x&quot;])) );<br />if (isRGBA) {<br />  a = v &amp; 0xFF; // get lowest byte<br />  v = v &gt;&gt; 8; // return to the &quot;RGB&quot; case by dismissing the alpha part<br />}<br /><br />char r = (v&gt;&gt;16) &amp; 0xFF;<br />char g = (v&gt;&gt;8) &amp; 0xFF;<br />char b = c &amp; 0xFF;<br />return [NSColor colorWithDeviceRed:(r/255.f) green:(g/255.f) blue:(b/255.f) alpha:(a/255.f)];
    
    Voilà ... ce qui est d'ailleurs encore améliorable en faisant un meilleur test pour isRGBA (genre en passant un 2e paramètre non NULL à  strtol pour vérifier le nombre de caractères effectivement lus, pour affiner les cas où l'on passerait des valeurs de hexcol bizarres genre @  0x123456 zk  ...
  • Philippe49Philippe49 Membre
    23:23 modifié #26
    dans 1245134226:

    dans 1245082531:

    Pour la couleur, je mettrais :
    <br />#define hexvalue(a) (((a)&gt;=&#39;0&#39; &amp;&amp; (a)&lt;=&#39;9&#39;) ? ((a)-&#39;0&#39;) : ((a)&gt;=&#39;a&#39; &amp;&amp; (a)&lt;=&#39;f&#39;) ? ((a)-&#39;a&#39;+10) :&nbsp; ((a)&gt;=&#39;A&#39; &amp;&amp; (a)&lt;=&#39;F&#39;) ? ((a)-&#39;A&#39;+10) : 0)&nbsp; <br />[NSColor colorWithDeviceRed:(hexvalue (hexcol[0])*16+ hexvalue (hexcol[1]))/255. green:(hexvalue(hexcol[2])*16+ hexvalue(hexcol[3]))/255. blue:(hexvalue(hexcol[4])*16+ hexvalue(hexcol[5]))/255. alpha:1];
    



    Pour le coup, je trouve cela peu lisible (et ton code ne se compile pas)


    Si cela compile, (il y avait simplement un ; en fin du #define à  supprimer)
Connectez-vous ou Inscrivez-vous pour répondre.