Accueil > Articles > Conversion de couleurs entre GDI+ et GDI32 (et autres)

Conversion de couleurs entre GDI+ et GDI32 (et autres)

Cela peut paraitre surprenant, mais manipuler un objet aussi simple que la structure Color (Dans System.Drawing) peut réserver des mauvaises surprises, en particulier quand on cherche à l’utiliser avec les fonctions Windows ou des librairies externes C++ pour lesquelles on a besoin de la représentation sous forme d’un nombre entier. En fait, pour une même couleur stockée sur 32 bits, les différentes composantes (rouge, vert, bleu et alpha) ne sont pas forcément disposées dans le même ordre en mémoire.

 

Pour GDI+ par exemple (la librairie graphique du .NET Framework), les 32 bits d’une couleur seront ainsi découpés de cette façon :

#AARRGGBB

L’alpha est donc placé sur les bits de poids fort, suivi du rouge, du vert et du bleu.
Pour GDI32 (ou toutes les fonctions de la dll gdi32), le rouge se retrouve sur les bits de poids faible, à la place du bleu :

#AABBGGRR

D’ailleurs dans les fonctions Win32, l’alpha est rarement pris en compte. Suivant la composition des couleurs, on peut donc se retrouver avec des résultats curieux si l’on n’effectue pas de conversion.

 

Ce problème peut-être contourné de différentes façons. La plus évidente étant de construire un nouvel entier à partir des composantes, en utilisant un String.Format() ou bien un Color.FromArgb() et en inversant le bleu et le rouge par exemple. Mais ces solutions ne sont pas très propres, et elles peuvent poser des problèmes de performance.

La meilleure solution est en fait contenue dans une classe méconnue du .NET Framework : ColorTranslator. Cette petite classe statique se charge en effet de ces conversions de la façon la plus optimisée possible. Par exemple, la conversion d’une couleur GDI+ en une couleur Win32 est effectuée de cette façon :

public static int ToWin32(Color c)
{
    return ((c.R | (c.G << 8)) | (c.B << 0x10));
}

La conversion est donc réalisée à l’aide des opérateurs binaires. Ce n’est pas le seul type de conversion proposée par cette classe. Mis à part la conversion réalisée avec ToWin32, il est possible aussi d’obtenir l’équivalent web, ou HTML, avec la méthode ToHtml.
Cette classe étant située dans l’espace de nom System.Drawing, elle n’est donc pas accessible par défaut dans une application WPF. Plutôt que d’inclure la dll uniquement pour cette utilisation, la solution la plus pratique dans ce cas est d’ajouter manuellement au projet WPF le code des méthodes utiles (récupérées à l’aide de Reflector par exemple).

 

Pour tester cette classe et voir les différentes représentations d’une même couleur selon les systèmes, on peut utiliser cet exemple de code :

static void ShowColors(Color color)
{
    Console.WriteLine("GDI+ : {0} | 0x{0:X8}", color.ToArgb());
    Console.WriteLine("Win32 : {0} | 0x{0:X8}", ColorTranslator.ToWin32(color));
    Console.WriteLine("HTML : {0}", ColorTranslator.ToHtml(color));
    Console.WriteLine();
}

A noter que pour afficher un nombre entier dans sa représentation hexadécimale, il suffit d’utiliser la lettre « X » dans la chaîne de formatage (en majuscule ou en minuscule suivant le résultat souhaité). On peut aussi spécifier le nombre de chiffres souhaités, comme c’est le cas dans l’exemple précédent où j’ai forcé l’affichage des 8 chiffres hexadécimaux pour avoir l’ensemble des 32 bits. N’hésitez pas à jeter un œil du côté de MSDN pour un petit rappel sur le formatage.

L’exemple ci-dessous permet d’appeler la méthode précédente avec un échantillon de différentes couleurs : les couleurs primaires pour commencer, puis une couleur transparente, deux couleurs nommées (une web et une système), et une dernière possédant des valeurs non nulles pour chaque composante :

Console.WriteLine("=== Red ===");
ShowColors(Color.FromArgb(255, 0, 0));
Console.WriteLine("=== Green ===");
ShowColors(Color.FromArgb(0, 255, 0));
Console.WriteLine("=== Blue ===");
ShowColors(Color.FromArgb(0, 0, 255));
Console.WriteLine("=== Black with 50% opacity ===");
ShowColors(Color.FromArgb(128, 0, 0, 0));
Console.WriteLine("=== Web color : Orange ===");
ShowColors(Color.Orange);
Console.WriteLine("=== System color : ActiveCaption ===");
ShowColors(Color.FromKnownColor(KnownColor.ActiveCaption));
Console.WriteLine("=== A:0xAA, R:0xBB, G:0xCC, B:0xDD ===");
ShowColors(Color.FromArgb(0xAA, 0xBB, 0xCC, 0xDD));

Console.ReadKey();

 

Il existe aussi un troisième type de conversion réservé aux objets Ole (ToOle). Celle-ci fonctionne exactement comme la conversion Win32, sauf pour les couleurs système.

 

Voici le résultat obtenu :

=== Red ===
GDI+ : -65536 | 0xFFFF0000
Win32 : 255 | 0x000000FF
HTML : #FF0000

=== Green ===
GDI+ : -16711936 | 0xFF00FF00
Win32 : 65280 | 0x0000FF00
HTML : #00FF00

=== Blue ===
GDI+ : -16776961 | 0xFF0000FF
Win32 : 16711680 | 0x00FF0000
HTML : #0000FF

=== Black with 50% opacity ===
GDI+ : -2147483648 | 0x80000000
Win32 : 0 | 0x00000000
HTML : #000000

=== Web color : Orange ===
GDI+ : -23296 | 0xFFFFA500
Win32 : 42495 | 0x0000A5FF
HTML : Orange

=== System color : ActiveCaption ===
GDI+ : -13410648 | 0xFF335EA8
Win32 : 11034163 | 0x00A85E33
HTML : activecaption

=== A:0xAA, R:0xBB, G:0xCC, B:0xDD ===
GDI+ : -1430532899 | 0xAABBCCDD
Win32 : 14535867 | 0x00DDCCBB
HTML : #BBCCDD

 

A noter que la représentation HTML obtenue avec ToHtml ne se contente pas simplement d’écrire les composantes RGB dans une chaîne, elle peut aussi donner le nom Web de la couleur (système ou non), que l’on retrouve aussi dans l’énumération KnowColor de System.Drawing. Cependant, les limitations sont les même ici que si l’on fait appel à la méthode ToKnowColor() de la structure Color : cela ne marche que si la structure a été crée avec un item de l’énumération KnowColor et non avec la méthode FromArgb(), comme le précise MSDN. C’est plutôt normal en fait : une couleur « fixe » (dont on défini les composantes ARGB) ne doit pas être convertie en une couleur « variable » dépendante de la configuration de l’utilisateur (une couleur système par exemple).

 

  1. Pas encore de commentaire
  1. Pas encore de trackbacks