Jusqu'à présent nous avons spécifié les couleurs au travers des champs foreground et background d'un GC. Pour cela, nous avons utilisé les macros BlackPixel() et WhitePixel() car nous nous étions limités au noir et blanc. Il est cependant possible de mettre dans ces champs une valeur correspondant à une vértitable couleur.
Comment utiliser la couleur ? Comment X gère-t-il les couleurs ?
Parceque X a été conçu pour supporter une large variété d'architectures hardware, les mécanismes de gestion de la couleur proposés par la Xlib sont relativement complexes.
La plupart des écrans couleurs aujourd'hui utilisent le modèle RGB pour le codage des couleurs.
Un écran couleur utilise plusieurs bits par pixels (multiple bit planes screen) pour coder la couleur de ce pixel (pixel value).
Une colormap est utilisée pour transformer la valeur numérique d'un pixel (pixel value) vers la couleur effectivement visible à l'écran. Une colormap est en réalité une table située dans la mémoire du serveur; la valeur numérique d'un pixel est un index dans cette table (pixel value). A une pixel value de 16 correspond une couleur spécifiée par le seizième élément de la colormap. Un élément de la colormap est appelé un colorcell.
En général, chaque colorcell contient 3 valeurs codées sur 16 bits, correspondant aux intensités de chacune des composante RGB.
Si on utilise 8 bits (sur les 16) pour coder chaque composante, un colorcell peut coder 256 puissance 3 couleurs (16 millions).

Le nombre de couleurs pouvant être affichées simultanément à l'écran dépend du nombre de bitplanes utilisés pour coder la pixel value. Un système à 8 bitplanes pourra indexer 2 puissance 8 colorcells (256 couleurs). Un système avec 24 bitplanes pourra afficher 16 millions de couleurs.
Un client désirant utiliser une couleur donnée ne spécifie pas explicitement la pixel value et la couleur de la colorcell correspondant à cette pixel value. A la place, il demande l'accès à un colorcell dans la colormap (gérée par le serveur X), et une pixel value lui est retournée.
On appelle ce mécanisme l'allocation d'une couleur.
Lorsqu'un client désire utiliser la couleur bleu, c'est comme s'il posait au serveur la question :
"Quelle colorcell dois-je utiliser pour dessiner en bleu ?",
et le serveur lui répond :
"Tu peux utiliser la colorcell correspondant à cette pixel value!"
C'est ça l'allocation des couleurs!
X propose une base de données de couleurs standards, qui associe des noms de couleurs à des valeurs RGB. Cette base de données standard encourage le partage des couleurs par les applications clients. Elle se trouve dans /usr/lib/X11/rgb.txt (/usr/openwin/lib/rgb.txt sous OpenWindows) et possède près de 300 entrées.
Le partage des couleurs ne peut intervenir que si deux applications allouent une même couleur read-only, c'est-à-dire une couleur possèdant les mêmes valeurs des composantes R, G et B, et ne pouvant être modifiée (c'est pourquoi on l'appelle read-only). A l'aide de cette base de données, il y a plus de chances pour que les clients partagent les couleurs que s'ils les allouent eux-même en les prenant parmi les 2 puissance 48 combinaisons RGB possibles.
Il est à noter que le serveur X n'utilise pas directement le fichier /usr/lib/X11/lib/rgb.txt, mais une version compilée de celui-ci.
Il est également possible, même si cela est déconseillé, de spécifier une couleur en hexadécimal.
Quatre formats possibles :
#RGB (4 bits pour chaque composante) #RRGGBB (8 bits " " " " ) #RRRGGGBBB (12 bits " " " " ) # RRRRGGGGBBBB (16 bits " " " " )
![]()
Chaque lettre correspond à un chiffre hexadécimal. #3a7 et #3000a0007000 sont équivalents.
Les machines les plus courantes possèdent entre 4 et 8 bitplanes pour l'affichage et utilisent les techniques de colormaps indexées présentées précedemment (colormap, colorcell et pixel values).
La correspondance pixel value <-> colorcell autorise un grand choix de couleurs possibles, même si on ne peut en visualiser qu'un nombre limité simultanément.
En général, on ne dispose que d'une seule colormap harware. Toutes les fenêtres affichées à l'écran utilisent les couleurs de cette colormap. Chaque application peut cependant modifier dynamiquement la valeur des colorcells, et ainsi configurer à souhait la colormap hardware.
X propose le concept de colormap virtuelle ou colormap privée, qui permet de gérer plusieurs colormaps, chacune proposant une palette différente; mais une seule peut être active à un moment donné. Ces colormaps sont swappées (échangées avec la colormap hardware) par le Window Manager lorsque le focus passe d'une fenêtre à l'autre. Conséquence de cette limitation : les fenêtres présentes à l'écran voient leurs couleurs s'altérer lorsque le focus est dans une fenêtre possèdant une colormap différente.
Les écrans Monochromes possèdent un seul bitplan. Chaque pixel value peut valoir 0 (noir) ou 1 (blanc).
Les écrans en niveaux de gris fonctionnent également avec des pixel values et une colormap mais les colorcells ne contiennent qu'une seule valeur correspondant à l'intensité du niveau de gris (au lieu de 3 intensités RGB pour un écran couleur).
En général il s'agit d'écrans possèdant au moins 24 bitplanes, et permettant d'afficher 16 million de couleurs simultanément. Toutes les couleurs disponibles sont affichables simultanément.
Dans ce mode, il n'est pas possible de gérer les colormaps de la même manière qu'avec les écrans courants, dont la colormap n'excède pas 256 entrées. On n'imagine pas en effet une colormap de 16 million de lignes!
A la place, les 24 bits de la pixel value sont scindés en 3 valeurs 8 bits, chacune correspondant à l'indice d'un colorcell dans la colormap, chacune correspondant à chaque couleur primaire RGB. Avec ce système il suffit de 3 colormaps primaires, chacune possèdant 256 entrées, pour spécifier 16 million de couleurs sur un écran 24 bits.

Un visual décrit les caractèristiques d'une colormap destinée à être utilisée sur un type d'écran donné. Pratiquement, il s'agit d'un pointeur sur une structure de type Visual qui contient des informations concernant un moyen d'utiliser un écran donné (ouf!)
Il est nécessaire de spécifier un visual lors de la création d'une colormap ou de la création d'une fenêtre, et le même visual doit être utilisé lors de ces deux création si la colormap va être associée à la fenêtre. La plupart des fenêtres héritent du visual; bien souvent, lors de la création d'une fenêtre on utilise la macro DefaultVisual() qui retourne le visual de la Root Window, et donc on utilise la colormap par défaut. Une fenêtre crée avec XCreateSimpleWindow() utilise la colormap par défaut.
La structure Visual est opaque. On ne peut accèder aux informations qu'elle renferme directement.
Deux fonctions sont disponibles : XMatchVisualInfo() et XGetVisualInfo(). Elles renvoient une structure de type XVisualInfo qui est publique (contrairement à la structure Visual qui est opaque).
Le champ class de la structure XVisualInfo correspond à une des six classes possibles: DirectColor, GrayScale, PseudoColor, StaticColor, StaticGray et TrueColor.
On peut connaître les visuals supportés par un écran donné à l'aide de la commande xdpyinfo.
| Colormap Type | Read/Write | Read-Only |
| Monochrome/Gray | GrayScale | StaticGray |
| Single Index for RGB | PseudoColor | StaticColor |
| Decomposed Index for RGB | DirectColor | TrueColor |
En pratique, on utilisera le plus souvent le DefaultVisual() qui est celui qui correspond le plus souvent aux besoins usuels et qui exploite au mieux le hardware dont on dispose.
On va essayer de s'y retrouver:
Attention: les colormaps modifiables (Read/Write) ont deux types de colorcells:
Avantages des colormaps immuables:
Désavantages des colormaps immuables:
Avantages des colormaps modifiables:
Désavantages des colormaps modifiables:
REMARQUE: la colormap par défaut contient des colorcells en Read-Only et en Read-Write. Si une application ne trouve pas dans cette colormap toutes les couleurs dont elle a besoin, elle pourra modifier, si elles sont disponibles, les colorcells en Read/Write. Il faut néanmoins remarquer que ces colorcells ne seront plus disponibles pour les autres applications tant qu'elles ne seront pas libérées. Dans ce cas, la seule solution pour l'application en manque de couleurs consiste à allouer une nouvelle colormap (une colormap privée). C'est ce que font certaines applications comme XV.
Lorsqu'on peut s'en contenter, pour les raisons précedemment citées, il est fortement conseillé d'allouer des couleurs en Read-Only.
La pixel value retournée par les fonctions d'allocation de couleur servira à positionner les champs foreground ou background d'un GC ou bien les attributs background_pixel ou border_pixel d'une window.
XParseColor() parse le nom d'une couleur ou la spécification hexadécimale d'une couleur et renvoie les valeurs RGB correspondantes. On l'utilise en général en tandem avec XAllocColor().
L'utilisation de ces deux fonctions au lieu de XAllocNamedColor() permet de repérer de manière plus fine les erreurs de syntaxe dans la spécification de la couleur.
Les fonctions XQueryColor() et XQueryColors() permettent de connaître les valeurs RGB de chaque colorcell.
La seule manière de savoir combien de colorcells sont disponibles pour l'allocation consiste à allouer N couleurs à l'aide de la fonction XAllocColorCells(), avec une grande valeur de N, puis à recommencer en diminuant la valeur de N jusqu'à ce que l'allocation ait réussi (il est recommandé de ruser en procèdant par dichotomie).
Une requête d'allocation de couleur peut échouer parcequ'il n'y a plus de colorcell de libre dans la colormap, ou, dans le cas d'allocation de couleur en read-only, s'il n'y a pas de colorcell dans la colormap dont la couleur est suffisamment proche de la couleur RGB demandée. Ainsi, les applications doivent allouer les couleurs en testant la valeur de retour de chaque requête. SI la valeur de retour de XAlloc...() est False alors il faut recommencer l'allocation avec des paramètres différents. Si vraiment il y a trop d'echecs, alors il faudra soit travailler avec un nombre de couleurs limité, soit allouer une colomap privée. Netscape travaille indifféremment avec la colormap standard ou avec une colomap privée (netscape -install...). Les résultats sont bien différents lorsque la colormap standard est déjà squattée par de nombreuses applications (window manager avec de nombreuses icônes, etc...)
XAllocColor() et XAllocNamedColor() ont comme argument un objet de type XColor. Cette structure contient les valeurs RGB désirées et la pixel value renvoyée.
typedef struct { unsigned long pixel; /* pixel value */ unsigned short red, green, blue; /* rgb values */ char flags; /* DoRed, DoGreen, DoBlue */ char pad; } XColor;
![]()
Lors de l'utilisation de XAllocColor() ou de XAllocNamedColor(), le champ pixel contient la pixel value qu'il faudra utiliser lors du positionnement du GC ou des attributs d'une window. Avec XStoreColor() (étudiée plus tard) et XQueryColor(), ce champ indique quelle colorcell est utilisée (read/write colorcell déjà allouée).
Les champs red, green et blue sont codés sur 16 bits.
Ci-dessous le fichier get_color.ro.c se trouvant dans le répertoire /basicwin/color/ des exemples du volume 1 du livre "XLib programming manual". Vous trouverez ces exemples dans ~buffa/cours/minfo/xlib/exemples_oreilly/*.
Vous pourrez étudier également le programme basic.ro qui utilise ce fichier.
/* * Copyright 1989 O'Reilly and Associates, Inc. * See ../Copyright for complete rights and liability information. */ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <stdio.h> extern Display *display; extern int screen_num; extern Screen *screen_ptr; extern unsigned long foreground_pixel, background_pixel, border_pixel; extern char *progname; #define MAX_COLORS 3 static char *visual_class[] = { "StaticGray", "GrayScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor" }; get_colors() { int default_depth; Visual *default_visual; static char *name[] = {"Red", "Yellow", "Green"}; XColor exact_def; Colormap default_cmap; int ncolors = 0; int colors[MAX_COLORS]; int i = 5; XVisualInfo visual_info; /* Try to allocate colors for PseudoColor, TrueColor, * DirectColor, and StaticColor. Use black and white * for StaticGray and GrayScale */ default_depth = DefaultDepth(display, screen_num); default_visual = DefaultVisual(display, screen_num); default_cmap = DefaultColormap(display, screen_num); if (default_depth == 1) { /* must be StaticGray, use black and white */ border_pixel = BlackPixel(display, screen_num); background_pixel = WhitePixel(display, screen_num); foreground_pixel = BlackPixel(display, screen_num); return(0); } while (!XMatchVisualInfo(display, screen_num, default_depth, /* visual class */i--, &visual_info)); printf("%s: found a %s class visual at default_depth.\n", progname, visual_class[++i]); if (i < 2) { /* No color visual available at default_depth. * Some applications might call XMatchVisualInfo * here to try for a GrayScale visual * if they can use gray to advantage, before * giving up and using black and white. */ border_pixel = BlackPixel(display, screen_num); background_pixel = WhitePixel(display, screen_num); foreground_pixel = BlackPixel(display, screen_num); return(0); } /* otherwise, got a color visual at default_depth */ /* The visual we found is not necessarily the * default visual, and therefore it is not necessarily * the one we used to create our window. However, * we now know for sure that color is supported, so the llowing code will work (or fail in a controlled way). * Let's check just out of curiosity: */ if (visual_info.visual != default_visual) printf("%s: PseudoColor visual at default depth is not default visual!\nContinuing anyway...\n", progname); for (i = 0; i < MAX_COLORS; i++) { printf("allocating %s\n", name[i]); if (!XParseColor (display, default_cmap, name[i], &exact_def)) { fprintf(stderr, "%s: color name %s not in database", progname, name[i]); exit(0); } printf("The RGB values from the database are %d, %d, %d\n", exact_def.red, exact_def.green, exact_def.blue); if (!XAllocColor(display, default_cmap, &exact_def)) { fprintf(stderr, "%s: can't allocate color: all colorcells allocated and no matching cell found.\n", progname); exit(0); } printf("The RGB values actually allocated are %d, %d, %d\n", exact_def.red, exact_def.green, exact_def.blue); colors[i] = exact_def.pixel; ncolors++; } printf("%s: allocated %d read-only color cells\n", progname, ncolors); border_pixel = colors[0]; background_pixel = colors[1]; foreground_pixel = colors[2]; return(1); }
![]()
REMARQUE: le visual trouvé en premier n'est peut-être pas le default visual. Ce n'est pas très grave dans cet exemple car si un visual couleur est disponible, il est probable que le visual par défaut est aussi un visual couleur; dans ce cas on peut sans craintes allouer des couleurs.
Avec XMatchVisualInfo() il est difficile de trouver un algorithme capable de trouver à coup sûr le default visual. Il est préférable dans ce cas d'utiliser XGetVisualInfo() qui retourne une liste de structures spécifiant les visuals disponibles.
Avec des visuals PseudoColor ou TrueColor, un client peut allouer des colorcells en Read/Write.
Plusieurs cas se présentent :
REMARQUE: l'allocation de couleurs privées n'est pas possible avec des visuals TrueColor ou StaticColor.
Pour allouer simplement quelques colorcells, il suffit de positionner le paramètre ncolors à la valeur désirée et de mettre le paramètre nplanes à 0. Toutes les pixel values seront retournées dans le tableau pixels.
On positionnera les valeurs RGB des colorcells allouées à l'aide des fonctions XStoreColor(), XStoreColors() ou XStoreNamedColor().
Ci-dessous le fichier get_color.rw.c se trouvant dans le répertoire /basicwin/color/ des exemples du volume 1 du livre "XLib programming manual". Vous trouverez ces exemples dans ~buffa/cours/minfo/xlib/exemples_oreilly/*.
Vous pourrez étudier également le programme basicwin.rw qui utilise ce fichier.
/* * Copyright 1989 O'Reilly and Associates, Inc. * See ../Copyright for complete rights and liability information. */ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <stdio.h> extern Display *display; extern int screen_num; extern unsigned long foreground_pixel, background_pixel, border_pixel; #define MAX_COLORS 3 get_colors() { int default_depth; Visual *default_visual; static char *name[] = {"Red", "Yellow", "Green"}; XColor exact_defs[MAX_COLORS]; Colormap default_cmap; int ncolors = MAX_COLORS; int plane_masks[1]; int colors[MAX_COLORS]; int i; XVisualInfo visual_info; int class; class = PseudoColor; default_depth = DefaultDepth(display, screen_num); default_visual = DefaultVisual(display, screen_num); default_cmap = DefaultColormap(display, screen_num); if (default_depth == 1) { /* must be StaticGray, use black and white */ border_pixel = BlackPixel(display, screen_num); background_pixel = WhitePixel(display, screen_num); foreground_pixel = BlackPixel(display, screen_num); return(0); } if (!XMatchVisualInfo(display, screen_num, default_depth, PseudoColor, &visual_info)) { if (!XMatchVisualInfo(display, screen_num, default_depth, DirectColor, &visual_info)) { /* No PseudoColor visual available at default_depth. * Some applications might try for a GrayScale visual * here if they can use gray to advantage, before * giving up and using black and white. */ border_pixel = BlackPixel(display, screen_num); background_pixel = WhitePixel(display, screen_num); foreground_pixel = BlackPixel(display, screen_num); return(0); } } /* got PseudoColor visual at default_depth */ /* The visual we found is not necessarily the * default visual, and therefore it is not necessarily * the one we used to create our window. However, * we now know for sure that color is supported, so the * following code will work (or fail in a controlled way). */ /* allocate as many cells as we can */ ncolors = MAX_COLORS; while (1) { if (XAllocColorCells (display, default_cmap, False, plane_masks, 0, colors, ncolors)) break; ncolors--; if (ncolors = 0) fprintf(stderr, "basic: couldn't allocate read/write colors\n"); exit(0); } printf("basic: allocated %d read/write color cells\n", ncolors); for (i = 0; i < ncolors; i++) { if (!XParseColor (display, default_cmap, name[i], &exact_defs[i])) { fprintf(stderr, "basic: color name %s not in database", name[i]); exit(0); } /* set pixel value in struct to the allocated one */ exact_defs[i].pixel = colors[i]; } /* this sets the color of read/write cell */ XStoreColors (display, default_cmap, exact_defs, ncolors); border_pixel = colors[0]; background_pixel = colors[1]; foreground_pixel = colors[2]; }
![]()
Le programme principal qui appelle cette fonction get_colors() contient un appel à XQueryColor() pour connaître les valeurs RGB des colorcells allouées par get_colors() (fichiers séparés).
Le main fait également appel à XStoreColor() pour modifier dynamiquement la couleur du texte qui est affiché dans la fenêtre sans avoir à le re-dessiner. Essayez donc le programme basic.rw et cliquez sur un bouton souris pour modifier la couleur du texte.
void main(argc, argv) int argc; char **argv; { XColor color; unsigned short red, green, blue; . . . /* open display, etc... */ color.pixel = foreground_pixel; XQueryColor(display, DefaultColormap(display, screen_num), &color); printf("red is %d, green is %d, blue is %d\n", color.red, color.green, color.blue); while (1) { XNextEvent(display, &report); switch (report.type) { . . . case ButtonPress: color.red += 5000; color.green -= 5000; color.blue += 3000; printf("red is %d, green is %d, blue is %d\n", color.red, color.green, color.blue); XStoreColor(display, DefaultColormap(display, screen_num), &color); break; . . . } }
![]()
A COMPLETER
Nous avons parlé de colormaps hardware et de colormaps privées ou colormaps virtuelles. Nous allons étudier ici comment manipuler ces objets.
Une colormap hardware est un objet physique dont le contenu (valeurs RGB) est lu par le hardware vidéo de l'écran pour afficher les couleurs. La plupart des stations de travail possèdent une seule colormap hardware. Certaines machines haut de gamme en possèdent plusieurs (station de travail Indy de Sillicon Graphics par exemple), ce qui permet à plusieurs fenêtres d'avoir chacune sa propre colormap hardware.
En général, on peut modifier les valeurs RGB des colorcells de la colormap hardware, ou bien on peut swapper l'ensemble des valeurs de la colormap hardware avec celles d'une colormap située dans la mémoire de l'ordinateur (colormap virtuelle ou privée). Les visuals DirectColor, GrayScale et PseudoColor ne sont disponibles que sur des écrans ayant ces possibilités.
Jusqu'à présent, nous n'avons alloué que des couleurs dans la colormap par défaut (créée lors du lancement du serveur). Sur des écrans limités à 256 couleurs, il arrive souvent que des clients gourmands en couleurs, ou qui nécessitent des couleurs précises pour fonctionner correctement (comme XV, Netscape, Xemacs), allouent la totalité des colorcells disponibles en Read/Write.
Dans ce cas, la solution pour une application en manque de couleurs consiste à utiliser une colormap privée. Le Window Manager se chargera de faire l'échange entre la colormap hardware et cette colormap privée lorsque le focus sera dans une fenêtre de l'application.
Lorsqu'une application crée une colormap privée, elle doit positionner l'attribut colormap de sa top-level window avec l'identificateur de cette colormap, afin que le Window Manager sache quelle colormap installer.
Rappel : le WM ne peut dialoguer qu'avec les top-level windows.