9

La couleur

 

 

 

9.1. Le traitement de la couleur

                  La couleur est en général constituée à l'écran par la composition de trois faisceaux de phosphore : un rouge, un vert et un bleu (red, green, blue). Ces couleurs sont dites fondamentales car elles permettent, par mélange, d'obtenir toutes les autres[1]. Si l'on produit l'intensité maximale pour chacun des faisceaux, on obtient du blanc. Si l'on combine des intensités moyennes et égales pour les trois faisceaux, on obtient divers niveaux de gris, et le noir correspond aux trois intensités nulles. On dit que la couleur est produite par synthèse additive car il faut augmenter les intensités pour augmenter la coloration.

 

          typedef struct {

      unsigned long pixel;         /* indice dans la colormap */

      unsigned short red, green, blue;    /* composantes */  char flags;      /* combinaison de DoRed DoGreen DoBlue */

      char pad;                    /* caractère de complétion */

          } XColor;

 

fig. 9.1. La structure XColor

 

                  Un pixel est un indice dans une table de couleurs (type Colormap). Une Colormap est un tableau d'entrées fournissant dans trois cellules, les valeurs respectives des intensités des faisceaux de rouge, vert et bleu. Sur les machines à différents niveaux de gris, il n’y a qu’une cellule (celle correspondant au rouge) qui se trouve utilisée. On trouvera un champ pixel et un niveau d’intensité pour chacune des trois couleurs fondamentales dans la structure XColor utilisée pour manipuler la couleur (cf. figure 9.1. page précédente).

 

                  On voit que la librairie a prévu de stocker les niveaux d’intensités dans cette structure sur des short. Un short étant codé sur 16 bits, on a virtuellement 65 536 valeurs possibles pour chaque faisceau, soit (65 536)3 couleurs virtuellement différentes !  Si les pixels ne sont stockés que sur 8 bits, on aura 2563 couleurs possibles, soit plus de 16 millions de couleurs. En réalité, ni l'œil, ni la machine ne peuvent distinguer tant de  couleurs et le nombre de couleurs possibles rendu par une machine dépend en fait du nombre de niveaux distinguables par la machine. Soit Max le nombre de niveaux possibles pour chaque composante, on aura finalement Max3 possibilités de couleurs[2].

 

                  Quoi qu'il en soit, le nombre de couleurs stockées dans une table de couleur est rarement égale au nombre de couleurs qui peuvent être rendues par une machine. Le nombre d’entrées dans une Colormap est le nombre de couleurs que l'on peut coder simultanément sur un écran. Ce nombre est en réalité fonction du nombre de plans de la mémoire écran et du modèle utilisé pour coder la couleur.

 

                  Dans le modèle illustré figure 9.2. un pixel est obtenu à partir des valeurs des bits dans les différents plans et ce pixel indexe directement une entrée dans une Colormap. Dans un tel modèle, un système comportant 4 plans ne peut indexer que 2valeurs de cellules différentes (puisque les pixels sont stockés sur 4 bits). On aura donc seulement 16 couleurs différentes simultanément à l’écran[3]. Un système comportant 8 plans permet de stocker 256 couleurs et un système comportant 24 plans peut a priori coder plus de 16 millions de couleurs (224 cellules). Sur une machine de performance moyenne on a de 4 à 8 plans, ce qui conduit à des palettes de 16 à 256 couleurs. En outre, chaque application peut définir une ou plusieurs palettes.  On a vu en effet dans le chapitre sur les fenêtres que l’on pouvait associer une Colormap à chaque fenêtre.

 

                  Certains modèles performants autorisent les clients à créer des tables de couleurs. Mais la plupart des machines ne permettent de connecter l'écran qu'à une seule table et les différentes Colormap définies par les applications ne sont en réalité pas toutes actives au même moment.

 

 

fig. 9.2. Le modèle à indexation directe

 

 

                  Généralement une seule table est connectée à l’écran[4]. On dira dans ce cas que la table est installée. La documentation parle aussi de hardware colormap par opposition aux tables virtuelles (virtual colormap). On peut définir autant de tables virtuelles que l’on veut, mais seules quelques tables seront réellement installées au niveau du hardware.

 

                  C’est le window manager qui est chargé d’installer la ou les tables de couleurs sur l’écran[5]. Si la machine ne permet pas d'installer plusieurs tables et que plusieurs clients ont créé leurs propres tables, le window manager installe à l'écran la table de la fenêtre qui a le focus des entrées. Dans la pratique, cela revient malheureusement à avoir les  couleurs souhaitées par l’une des applications et de “mauvaises” couleurs pour les autres, puisque les valeurs stockées dans les pixels de la table installée risquent de ne pas correspondre à celles utilisées par les autres clients. L’inconvénient pour l’utilisateur, outre que les couleurs de certaines applications soient fausses, est que l’écran change sans arrêt d'apparence quand la souris entre et sort des fenêtres.

 

                  C’est pourquoi l’utilisation de tables standards permettant aux différentes applications de partager des couleurs est vivement recommandée, même dans les modèles les plus performants. Les tables standards sont des tables dans lesquelles les liens entre les pixels et les valeurs stockées sont calculables. Ces tables proposent une gamme raisonnable de couleurs régulièrement réparties et peuvent donc satisfaire la plupart des clients. En outre, on peut tirer avantage du caractère prédictible du codage pour modifier les couleurs sans consulter le serveur au préalable.

 

                  De manière générale, partager une table de couleur revient simplement à demander l'allocation de cellules en lecture. Idéalement un seul client, le window manager, installe une table de couleurs standard sur la racine. Les autres clients se contentent alors d'utiliser les couleurs présentes[6]. L'inconvénient d'une allocation restreinte à la lecture est que l'on doit entièrement redessiner une figure pour en modifier les couleurs. A l'inverse, l'allocation privée en écriture présente l'avantage de la rapidité, car il suffit de changer les valeurs stockées dans les cellules pour modifier tout l'écran simultanément, sans avoir à redessiner. On retiendra cependant que le partage de tables standards est souvent préférable du fait des limitations de ressources du serveur.

                 

 

9.2. Indexation directe et indexation séparée

                  Le traitement de la couleur varie beaucoup selon les machines. Il est en effet fonction du nombre de plans et de la possibilité d’écrire ou non dans la table des couleurs. Il existe en outre deux grands modèles d'organisation vis-à-vis de l’indexation des pixels dans les tables de couleurs. Dans le premier modèle, dit à indexation directe, la mémoire des plans spécifie pour chaque point un pixel qui fournit directement les trois cellules d’intensité de rouge, vert et bleu correspondant à ce point de l'écran. Dans ce modèle, il y a autant d'entrées dans la table que de couleurs codables simultanément à l’écran. Pour 8 plans de mémoire, cela conduit, on l’a vu, à des tables de 256 couleurs (cf. fig. 9.2.).

 

                  Dans le second modèle, dit à indexation séparée, la couleur d'un point est déterminée par un pixel décomposé cette fois en trois morceaux. Un pixel spécifie, sur trois séries de plans, trois indices dans une table permettant d'indexer les trois niveaux d'intensités fondamentaux. Par exemple figure 9.3. page suivante, les 24 plans ont été partagés en trois séries de 8 plans. La première série fournit un index permettant de déterminer le niveau de rouge, la deuxième série fournit un index permettant de déterminer le niveau de vert, et la troisième série fournit un index permettant de déterminer le niveau de bleu.

 

                  L’indexation séparée est particulièrement économique puisque le nombre d'entrées dans la table diminue exponentiellement : cette fois, si Max est le nombre de niveaux distinguables par la machine pour chacune des trois couleurs, Max entrées suffisent à coder toutes les couleurs possibles.

 

                  Avec 8 plans seulement, on peut faire de l’indexation séparée en réservant par exemple 3 plans au codage du rouge, 3 plans au vert et 2 plans au bleu.  Ce système est alors appelé modèle 3/3/2 et il existe une table standard pour ce modèle. Dans ce modèle, on a 8 niveaux de rouge (codé de 0 à 7 sur 3 bits), 8 niveaux de vert et 4 niveaux de bleu. On peut alors avoir une table de seulement 8 entrées, permettant de définir 256 couleurs différentes (8x8x4).

 

 

9.3. Le type visuel

                  La structure Visual permet de caractériser le type de traitement de la couleur. La classe d'un Visual indique si le modèle est à indexation directe ou à indexation séparée et si l'on peut ou non écrire dans la table des couleurs. Il y a quatre classes de Visual couleur (DirectColor, TrueColor, PseudoColor et StaticColor) et deux classes de Visual gris (GrayScale et StaticGray). Seuls les classes DirectColor et TrueColor permettent de faire de l'indexation séparée. Les classes DirectColor, PseudoColor et GrayScale permettent d'écrire dans les tables de couleurs alors que les classes TrueColor, StaticColor et StaticGray supportent une table qui n'est accessible qu'en lecture.

 

 

 

 

fig. 9.3. Le modèle à indexation séparée

 

                  Le modèle le plus général correspond aux Visual de classe DirectColor. On peut alors lire et écrire dans la table de couleurs et y faire de l'indexation séparée. Les machines supportant ce modèle supportent en réalité tous les autres. De manière générale, les modèles les plus performants peuvent supporter les modèles les moins performants et on a les inclusions de modèles illustrées figure 9.4.

 

 

 

fig. 9.4. Les inclusions de classes de Visual

 

 

                  On distingue ensuite les Visual de classe TrueColor qui procèdent aussi par indexation séparée (donc beaucoup de couleurs) mais où les cellules ne sont accessibles qu’en lecture.

 

                  La classe PseudoColor correspond à un modèle où l'on peut écrire mais où l’on procède par indexation directe. Les possibilités des Visual de classe PseudoColor sont donc virtuellement les mêmes que celles de la classe DirectColor mais l’on dispose de beaucoup moins de couleurs. La classe StaticColor est à indexation directe et ne permet pas l’écriture. Viennent ensuite des modèles pour lesquels la question de l’indexation ne se pose plus car ils ne possèdent qu’un seul niveau : le niveau de gris[7]. Ces modèles font donc nécessairement de l'indexation directe. La classe GrayScale est la plus performante car on peut lire et écrire dans une table de couleurs. La classe StaticGray ne permet que la lecture. Ainsi par exemple, les machines noir et blanc ont des visuels de classe StaticGray et sont de profondeur 1.

 

 

 

 

Déterminer le type visuel

 

                  Le type de traitement de la couleur doit être spécifié à la création de chaque Colormap par une structure de type Visual. On doit également indiquer une struc­ture Visual à la création de la fenêtre qui utilisera cette Colormap. Ces deux Visual doivent être les mêmes où des erreurs se produiront à l'exécution. Le pointeur que l'on obtient par la macro

 

DefaultVisual (dpy, screen)

 

est l'adresse du Visual associé à la racine de l'écran[8]. Il contient la Colormap par défaut que l'on peut obtenir par la macro DefaultColormap (dpy, screen). Cependant, cette  table n'est pas nécessairement celle que l’on souhaite utiliser par la suite. Par exemple, certains écrans qui comportent 24 plans peuvent être scindés en deux visuels de 12 plans de classe PseudoColor. On peut alors se servir de 12 des plans comme mémoire secondaire et cela permet d’implanter des techniques spéciales pour dessiner en 3D ou faire des animations. On prendra donc bien garde à analyser d'abord ses besoins en matière de traitement de la couleur avant de créer les fenêtres.

 

                  Le type Visual est un type opaque au programmeur et l’on ne doit pas accé­der directement à ses champs. Pour connaître les informations relatives aux différents Visual supportés par un écran, on aura recours aux fonctions XMatchVisualInfo et/ou XGetVisualInfo.

 

Status XMatchVisualInfo (dpy, screen, depth, class, &vinfo_return)

Display*          dpy ;

int                      screen ;

int                      depth ;

int                      class ;

XVisualInfo   vinfo_return ;

 

                  XMatchVisualInfo retourne les informations sur un visuel qui satisfait la profondeur depth  (nombre de plans) et la classe indiquée pour un écran screen donné. On fera en général un appel à XMatchVisualInfo dans l’espoir de trouver le type visuel souhaité par l’application. S’il n’est pas disponible, on se rabattra alors sur un type secondaire obtenu avec XGetVisualInfo.

 

XVisualInfo * XGetVisualInfo (dpy, vinfo_mask, &vinfo_template,

                                                                                &nitems_return)

long vinfo_mask ;

XVisualInfo   vinfo_template;

int                      nitems_return ;

 

                  La fonction XGetVisualInfo utilise une structure et un masque permettant d’indiquer les champs pertinents de la structure. La structure de type XVisualInfo et les masques (représentés figure 9.5.) sont déclarés dans <X11/Xutil.h>. La fonction XGetVisualInfo retourne une liste de structures qui satisfont les attributs indiqués par l'argument vinfo_template. Si aucun type visuel n'est convenable, elle retourne le pointeur NULL. Dans ce cas on pourra reconsulter la liste des visuels possibles en minimisant ses prétentions. Pour la libérer ensuite, on fera appel à XFree.

 

    typedef struct {

          Visual *visual;

          VisualID visualid;

          int screen;

          int depth;

          int class;

          unsigned long red_mask;

          unsigned long green_mask;

          unsigned long blue_mask;

          int colormap_size;

          int bits_per_rgb;

    } XVisualInfo;

 

    #define VisualNoMask                   0x0

    #define VisualIDMask                   0x1

    #define VisualScreenMask               0x2

    #define VisualDepthMask                0x4

    #define VisualClassMask                0x8

    #define VisualRedMaskMask              0x10

    #define VisualGreenMaskMask            0x20

    #define VisualBlueMaskMask             0x40

    #define VisualColormapSizeMask         0x80

    #define VisualBitsPerRGBMask           0x100

    #define VisualAllMask                  0x1FF

 

fig. 9.5. Récupérer des informations sur le type visuel

                  Une fois trouvé un visuel de caractéristiques optimales, on pourra en passer l'adresse[9] à XCreateColormap et à XCreateWindow. Si le type visuel ou les couleurs requises n'existent pas sur cet écran, on pourra toujours se rabattre sur une version en noir et blanc ou quitter le programme en avertissant l'utilisateur du problème.

 

9.4. Le noir et blanc

                  Cette section est destinée à attirer l'attention des programmeurs qui ne possèdent pas de machine couleurs sur les questions qui, bien que relevant de la couleur, les concernent aussi. En premier lieu, il est important de noter qu'on ne connaît pas a priori la valeur du pixel noir et celle du pixel blanc. C'est pourquoi on a utilisé dans les précédents chapitres les macros

(unsigned long) WhitePixel (dpy, screen)

(unsigned long) BlackPixel (dpy, screen)

 

                  On sait simplement que sur une machine noir et blanc l'une de ces valeurs est 0 et l'autre 1. C'est pourquoi on peut peindre en inverse vidéo sur ces machines en mettant la fonction de transfert à GXxor ou GXinvert.

                  Par contre, si l'on dessine en noir et blanc sur une machine couleurs, les valeurs du noir et du blanc peuvent être quelconques et rien ne garantit que le ou exclusif WhitePixel ^ BlackPixel ne fournisse pas une troisième couleur. Les programmes écrits pour du noir et blanc sans tenir compte de ce problème ne fonctionnent plus correctement quand ils sont portés sur une machine couleurs.

 

                  Les solutions à ce problème sont :

                  • soit de mettre l'attribut foreground du contexte graphique au ou exclusif BlackPixel ^  WhitePixel et utiliser la fonction de transfert GXxor.

                  • soit de mettre la fonction de transfert à GXinvert et utiliser un masque des plans égal au ou exclusif  WhitePixel ^ BlackPixel.

 

                  Ces deux méthodes se généralisent très bien à un affichage en inverse vidéo couleurs. En effet, supposons que l'on ait deux pixels c1 et c2 :

 

c1                                        =     00000000000000001111111111111111

c2                                        =     00000000111111111111111100000000

                  Si le masque des plans du contexte graphique vaut c1 ^ c2, et que l'on utilise la fonction GXInvert, on passera bien de c1 à c2 en redessinant  :

c1                                        =     00000000000000001111111111111111

 

Inversé                              =     11111111111111110000000000000000

+ planemask =     00000000111111110000000011111111

devient             c2             =     00000000111111111111111100000000

 

                  Le lecteur pourra vérifier que le deuxième cas et la première méthode sont également valides.

                  Un développeur peut aussi avoir besoin sur machines noir et blanc d’utiliser quelques fonctions de traitement de la couleur. Par exemple, pour créer un curseur avec XCreatePixmapCursor on a besoin d'initialiser des structures XColor au noir et au blanc de la machine.

                  Pour connaître les valeurs d'intensités associées à un pixel donné on utilise généralement la fonction

 

XQueryColor (dpy, cmap, &color_in_out)

Colormap        cmap ;

XColor             color_in_out ;

 

                  On pourra appeler cette fonction avec la table de couleur par défaut (en utilisant la macro DefaultColormap (dpy, screen) ) et passer l'adresse d'une variable de type XColor dans laquelle le champ pixel aura été spécifié (pour le noir ou le blanc grâce aux macros WhitePixel et Blackpixel). La procédure remplit alors les valeurs des champs d'intensités RGB associées, d’où le nom de color_in_out de ce paramètre[10]. Comme on va le voir, cet usage des structures XColor comme paramètre d’entrée/sortie est fréquente dans toutes les procédures traitant de la couleur.

 

9.5. La définition des couleurs

                  En physique, on définit les couleurs par les sources lumineuses qui les produisent, la couleur des corps variant selon la nature de la lumière qui leur est envoyée. Les lumières peuvent être simples ou complexes selon qu'elles se décomposent ou non par réfraction sur un prisme. Les lumières simples ont une seule longueur d'onde et sont dites monochromatiques. La plupart des lumières colorées sont loin d'être monochromatiques. Chaque source lumineuse est caractérisée par sa décomposition sur le spectre qui peut être continue ou par raies. Le spectre distingue différentes teintes variant continûment selon l'angle de réfraction (bleu, vert, jaune, orangé, rouge, violet et indigo qui est en réalité un violet-bleu).

 

                  Le mélange de trois sources lumineuses de longueurs d'onde l1, l2, l3,  et de puissance respectives P1, P2, P3 fournit une teinte correspondant finalement à une lumière de longueur d'onde l émise à une certaine puissance. La manière d'obtenir ce résultat n'est pas unique et des mélanges de lumières très différentes peuvent en réalité produire la même teinte. (Par contre il n'y a en général qu'une seule combinaison possible de trois lumières données permettant d'atteindre une teinte fixée par avance.)

 

 

 

Dépendance et indépendance par rapport à la machine

 

                  Dans la synthèse additive, les couleurs sont constituées par le mélange de trois sources lumineuses de rouge, vert et bleu, notées FR, FG, et FB. Les couleurs sont implantées au niveau du serveur en niveaux de rouge, vert et bleu, dont les tonalités et degrés d'intensité sont dépendants de la machine. Dans la Release 5, il existe deux types de spécifications qui dépendent de la machine : les spécifications en niveaux RGB, codés sur 4, 8, 12 ou 16 bits selon les machines (format interne) et les spécifications en niveaux d'intensité normalisés, représentés par des flottants variant entre 0 et 1 (format interne normalisé). Dans les versions antérieures à la Release 5, seul le premier type de format existe. Bien que normalisé, le second type de spécification fournit un rendu de couleur qui dépend encore de la machine car chaque machine possède fondamentalement un rouge, un vert et un bleu différent. De même le blanc, en théorie fournit par la combinaison des trois faisceaux d'intensité maximale, est en réalité différent sur chaque machine[11].

 

                  Cependant, des noms de couleurs et des spécifications indépendantes de la machine ont été définies. Dans les traités de colorimétrie, on distingue 3 composantes colorimétriques X, Y, Z. Ces composantes sont des fonctions linéaires des trois composantes de flux lumineux FR, FG, et FB. On définit également des coefficients x, y, et z proportionnels à X, Y, Z, mais dont la somme vaut 1. Les deux premiers composants x et y permettent d'identifier la couleur, compte tenu de la luminance L. La luminance n'est en réalité perçue que si elle est très faible ou très forte. Ces espaces de codage indépendants de la machine sont présents dans la Release 5.

 

                   On a également observé chez l'homme trois aspects fondamentaux de la perception des couleurs : le ton, qui est caractéristique de la teinte, la brillance ou luminosité, et la puissance de coloration à l'intérieur d'un même ton ou saturation. Les degrés chromatiques s'opposent aux nuances de tons. Une nuance est obtenue en mélangeant d'autres tonalités à un ton donné (on fait ainsi varier la teinte) alors que la variation de degré chromatique ne fait que modifier la puissance de coloration à l'intérieur d'un même ton. Ainsi un rouge carmin peut-il être plus ou moins soutenu et plus ou moins lumineux, il n'en reste pas moins perçu comme la même teinte sous des aspects différents. Ces spécifications fondées sur des observations psychophysiologiques sont également présentes dans la Release 5. 

 

                   La traduction des spécifications indépendantes de la machine en spécifications dépendant de la machine (niveaux RGB) se fait en tenant compte du caractère propre de la machine. Cette étape de conversion comporte une correction de gamme (gamma correction). La fiabilité des conversions dépend des implantations. En particulier, il se peut qu'une couleur définie dans un espace de couleur indépendant n'existe pas dans la gamme de couleurs supportée par la machine. Dans ce cas, des procédures dites de compression de gamme (gamut compression procedure) pourront être utilisées pour réduire la gamme de la teinte à convertir de sorte que la conversion soit possible. Ces compressions peuvent se faire au détriment de la luminance ou de la coloration mais essayent de préserver la teinte. De même, des procédures d'ajustement du blanc sont utilisées quand le blanc de l'écran diffère du blanc souhaité par le client.

 

                  Ces procédures de conversion sont stockées dans un contexte de conversion de couleur  (type XcmsCCC) qui se trouve associé implicitement à chaque Colormap. Dans la Release 5, des macros et des fonctions permettront de connaître et de modifier les paramètres d'un contexte de conversion de couleurs.

 

 

9.6. Les formats de spécification de couleurs

                  Il existe bien entendu des spécifications par nom. Une base de noms de couleurs a été distribué en standard dès la Release 3. Ces noms donnent plus d'indépendance vis‑à‑vis des machines et sont fiables dans la mesure où une correction de gamme peut être apportée pour fournir les valeurs RGB approchant le mieux les couleurs spécifiées par ces noms[12]. 

 

                  La base de noms distribuée en standard n'utilise que des caractères ISO (ce sont des termes anglais) et le fichier de définitions se trouve généralement installé dans /usr/lib/X11/rgb.txt. L'intérêt d'utiliser des noms de couleurs réside dans le fait que cela augmente les chances de partager des couleurs avec d'autres clients. On essaiera donc, dans la mesure du possible, d'utiliser les noms des couleurs pour procéder à des allocations (en lecture unique) dans les tables.

 

                  Dans la Release 4, on peut spécifier une couleur par un XColor, par un nom, ou par une chaîne indiquant directement les niveaux RGB dans le format interne non normalisé. La syntaxe suivante permet de spécifier ce format par une chaîne de caractères, notée specif :

specif := #RGB|#RRGGBB|#RRRGGGBBB|#RRRRGGGGBBBB

R,G,B := 1 caractère hexadécimal

 

                  Dans cette syntaxe, quand moins de 16 bits sont spécifiés, ils représentent les bits de poids forts. Ainsi par exemple "3a7" et "3000a0007000" sont équivalents. Dans la suite, nous ferons référence à ce format de spécification sous la forme de “l'ancienne syntaxe”.

 

 

Les espaces de couleurs de la Release 5

 

                  Dans la Release 5, les valeurs psychométriques ont conduit à représenter les couleurs dans des espaces à trois dimensions. Les spécifications de couleurs indépendantes des machines sont toutes dérivées de l'espace de codage[13] intitulé CIE XYZ. Les spécifications supportées par la librairie sont les suivantes : CIE xyY, CIE L*u*v*, CIE L*a*b* et TekHVC.  Dans tous ces nouveaux espaces différents du format interne, les composantes sont représentées par des flottants (cf. figure 9.6.).

 

          typedef struct {

                            union {

                                             XcmsRGB RGB;

                                             XcmsRGBi RGBi;

                                             XcmsCIEXYZ CIEXYZ;

                                             XcmsCIEuvY CIEuvY;

                                             XcmsCIExyY CIExyY;

                                             XcmsCIELab CIELab;

                                             XcmsCIELuv CIELuv;

                                             XcmsTekHVC TekHVC;

                                             XcmsPad Pad;

                            } spec;

                            XcmsColorFormat format;

                            unsigned int pixel;

          } XcmsColor;

 

          typedef struct {

                            unsigned short red;              /* 0x0000 à 0xffff */

                            unsigned short green;          /* 0x0000 à 0xffff */

                            unsigned short blue;            /* 0x0000 à 0xffff */

          } XcmsRGB;                                           /* RGB Device */

          typedef struct {

                            XcmsFloat red;                      /* 0.0 à 1.0 */

                            XcmsFloat green;                 /* 0.0 à 1.0 */

                            XcmsFloat  blue; /* 0.0 à 1.0 */

          } XcmsRGBi;                                          /* RGB Intensity */

          typedef struct {

                            XcmsFloat X;                         /* 0.0 à 1.0 */

                            XcmsFloat Y;                         /* 0.0 à 1.0 */

                            XcmsFloat  Z;                        /* 0.0 à 1.0 */

          } XcmsCIEXYZ;                                   /* CIE XYZ */

 

                                                               /* etc. */

 

fig. 9.6. La structure XcmsColor

 

                  Dans l'espace TekHVC une couleur est spécifiée par un angle qui en exprime le ton (Hue angle), une valeur qui en fournit la luminosité (Value) et un degré de saturation chromatique (Chroma). Les espaces L*u*v* et L*a*b* donnent la luminance L et les deux autres composantes permettent de retrouver l'angle et le degré chromatique.

 

                  Les fonctions permettant d'utiliser ce type de spécifications sont préfixées par Xcms. Pour les utiliser, il faut inclure le fichier <X11/Xcms.h>[14]. Dans ces fonctions, les structures XcmsColor permettent de spécifier les couleurs dans un espace de couleur indiqué par le champ format. Les structures XcmsColor jouent le même rôle que les XColor dans les fonctions des versions antérieures[15]. Un XcmsColor est une structure contenant un champ spec, un champ format et un champ pixel.  Le champ spec est une union de plusieurs types de spécifications et c'est le champ format qui permet d'en déterminer le membre d'accès (cf. figure 9.6. page précédente).

                   Les formats possibles correspondant aux différents espaces de couleurs sont XcmsCIEXYZFormat, XcmsCIEuvYFormat, XcmsCIExyYFormat,  XcmsCIELabFormat, XcmsCIELuvFormat, XcmsCIETekHVCFormat, XcmsCIERGBFormat et XcmsCIERGBiFormat.

 

                  Pour résumer, les fonctions spécifiant des couleurs utilisent finalement des spécifications données sous la forme :

                  • soit d'une structure de type XColor

                  • soit d'une structure de type XcmsColor (Release 5)

                  • soit d'une chaîne de caractères pouvant elle-même spécifier : soit un nom de couleur standard, soit une spécification en niveaux RGB du format interne vérifiant l'ancienne syntaxe indiquée en tête de section (Release 4), soit une spécification dans le format d'un espace de couleurs vérifiant la syntaxe suivante (Release 5) :

 

"<espace_de_couleur>:<valeur>/.../<valeur>"

 

                  Les champs valeurs seront bien entendu fonction du format attendu. Ainsi, on pourra spécifier :

 

"CIEXYZ:0.3277/0.22456/0.2569"

"rgb:ff/1e/00"

"RGBi:1.0/0.0/0.5"

"CIELuv:50.0/0.0/0.0"

                  Le format rgb correspond au format interne du serveur. Le format RGBi est le format interne normalisé. Le format rgb suit la syntaxe suivante :

rgb:<red>/<green>/<blue>

<red>,<green>,<blue> := h|hh|hhh|hhhh

 

 

9.7. Conversions de formats et de couleurs

 

Recherche de couleurs

 

                  La fonction XParseColor permet de récupérer les valeurs RGB correspondant à la définition d'une couleur spécifiée par une chaîne. Cette fonction rapporte ces valeurs dans un argument de type XColor (passé par référence) — argument que l’on pourra ensuite demander à utiliser (en lecture) avec XAllocColor. Utilisée avec une spécification du format interne dans l'ancienne syntaxe (Release 4), elle permet de déterminer si la couleur peut être obtenue sur l'écran. Dans la Release 5, elle sera utilisée de manière équivalente à XParseNamedColor.

 

                  Plus intéressante, la fonction XLookupColor prend un nom de couleur en entrée et permet, d'une part, de récupérer la définition exacte de la couleur et, d'autre part, de récupérer la définition d'une couleur approchée sur l'écran. Si aucune couleur disponible sur ce type d'écran n'est suffisamment proche, la fonction retournera False. (XLookupColor admet également une spécification de couleur par chaîne de caractères indiquant des niveaux RGB dans l'ancienne syntaxe.)

Status XParseColor (dpy, cmap, specif, &exact_def_return)

Display*                           dpy ;

Colormap                         cmap ;

char*                                  specif ;   /* nom ou ancienne syntaxe */

XColor                              exact_def_return ;

 

Status XLookupColor ( dpy, cmap, color_name, &exact_def_return,

                                                                &screen_def_return)

char *                                color_name ; /* nom de couleur */

XColor                              exact_def_return, screen_def_return ;

 

                  La fonction équivalente pour les formats de la Release 5 s'appelle XcmsLookupColor.

 

Status XcmsLookupColor (dpy, cmap, color_string, &color_exact_return,

                                                                &color_screen_return, result_format )

char *                                color_string ;        /* specification R5 */

XcmsColor                      color_exact_return, color_screen_return ;

XcmsColorFormat        result_format ;

 

Les fonctions en Xcms dans lesquelles des conversions de gamme peuvent se produire retournent une variable entière qui peut valoir XcmsFailure, XcmsSuccess ou XcmsSuccessWithCompression.

 

      Ces fonctions ne font que traduire un nom de couleur en ses valeurs de définition sur le type d'écran associé à la Colormap, mais elles ne consultent pas la table de couleur passée en argument. Elles pourront être utilisées pour vérifier que des couleurs recherchées sont suffisamment contrastées et seront ensuite suivies d'appels à XAllocColor ou XStoreColor.

 

 

Contextes de conversion de couleurs

 

                  A partir de la Release 5[16], il existe des fonctions de conversions d'un format à l'autre connues du serveur. Lors de ces conversions, le serveur utilise un ensemble de données regroupées dans un contexte de conversion de couleurs (XcmsCCC) associé de manière transparente à chaque Colormap. Ce contexte de conversion de couleurs contient la procédure de compression de gamme (gamut compression procedure), la procédure d'ajustement du blanc (white point adjustment procedure), et peut contenir la définition du blanc du client (client white point) et des procédures de compression et d'ajustement fournies par le client.

 

                  On peut récupérer un contexte de conversion par défaut ou le contexte de conversion associé à une Colormap grâce aux fonctions

 

XcmsCCC XcmsDefaultCCC (dpy, screen_number)

XcmsCCC XcmsCCCofColormap (dpy, cmap)

 

                  Pour accéder aux différentes valeurs d'un contexte de conversion, on utilisera les macros fonctions :

 

DisplayOfCCC (ccc)

VisualOfCCC (ccc)

ScreenNumberOfCCC (ccc)

ScreenWhitePointOfCCC (ccc)

ClientWhitePointOfCCC (ccc)

XcmsCCC      ccc ;

 

                  Les deux dernières macros retournent une structure XcmsColor. Par défaut, le blanc du client est considéré comme étant identique à celui de l'écran.

 

Récupérer le noir, le blanc, et les couleurs fondamentales

 

                  Une fois récupéré le contexte de conversion associé à une table de couleur, on pourra utiliser les requêtes de conversion suivantes, qui permettent d'obtenir les définitions des couleurs fondamentales, du noir et du blanc :

Status XcmsQueryBlack (ccc, target_format, &color_return)

Status XcmsQueryWhite (ccc, target_format, &color_return)

Status XcmsQueryRed (ccc, target_format, &color_return)

Status XcmsQueryGreen (ccc, target_format, &color_return)

Status XcmsQueryBlue (ccc, target_format, &color_return)

XcmsCCC                       ccc ;

XcmsColorFormat        target_format ;

XcmsColor                      color_return ;

 

                  En outre, des procédures propres aux espaces CIELab, CIELuv et TekHVC permettent de récupérer les valeurs maximisant (ou minimisant) certaines composantes. Par exemple, dans l'espace CIELab, on pourra utiliser la fonction

 

Status XcmsCIELabQueryMaxC (ccc, hue_angle, L_star, &color_return)

 

qui retourne la couleur de saturation maximale (chroma) d'une teinte (hue_angle) pour une intensité lumineuse donnée (L_star). On pourra également utiliser dans cet espace les requêtes XcmsLabQueryMaxL, XcmsCIELabQueryMaxLC et XcmsCIELabQueryMinL.

 

                  Dans l'espace CIELuv, on pourra utiliser XcmsCIELuvQueryMaxC,  XcmsCIELuvQueryMaxL, XcmsCIELuvQueryMaxLC et XcmsCIELuvQueryMinL.

 

                  Dans l'espace TekHVC, on dispose de XcmsTekHVCQueryMaxC, XcmsTekHVCQueryMaxV, XcmsTekHVCQueryMaxVSamples,  XcmsTekHVCQueryMaxVC et XcmsTekHVCQueryMinV.

 

Modifier ou créer un contexte de conversion

                  On peut modifier le contexte de conversion d'une table de couleur ou en créer un nouveau grâce aux fonctions :

 

XcmsCCC XcmsSetCCCOfColormap (dpy, cmap, ccc)

Status XcmsSetWhitePoint (ccc, color)

XcmsCCC                                         ccc ;

Colormap                                           cmap ;

XcmsColor                                        color ;

XcmsCompressionProc XcmsSetCompressionProc (ccc, compression_proc,

                                                                                                                     client_data)

XcmsWhiteAdjustProc XcmsSetWhiteAdjustProc (ccc, white_ajust_proc,

                                                                                                                     client_data)

XcmsCCC XcmsCreateCCC (dpy, screen_number, visual, client_white_point,

                                             compression_proc, compression_client_data,

                                             white_adjust_proc, white_adjust_client_data)

XcmsCCC                                         ccc ;

XcmsCompressionProc               compression_proc ;

XcmsWhiteAdjustProc                white_adjust_proc ;

XPointer                                            client_data, compression_client_data,

                                                               white_adjust_client_data ;

                  La procédure de compression de gamme est la procédure à appliquer quand une couleur à convertir se situe en dehors de la gamme des couleurs de l'écran. La procédure d'ajustement du blanc est la procédure à appliquer quand la définition du client diffère de la définition du blanc de l'écran. Si le point blanc du client n'est pas défini (NULL), il sera considéré comme identique à celui de l'écran. Ces procédures doivent bien entendu vérifier des contraintes de prototypage et l'on consultera la documentation sur ce point si l'on souhaite les redéfinir.

                  Enfin, on pourra également ajouter de nouveaux espaces de couleurs et leur associer des ensembles de fonctions permettant de les convertir dans l'espace CIE XYZ.   Pour ces extensions, on consultera la documentation.

9.8. Allocation, lecture et écriture des couleurs dans une Colormap

                  Même si l'on ne souhaite utiliser des pixels qu'en lecture, on doit en déclarer la liste au serveur. Cette déclaration s'appelle l'allocation de couleur. La raison de cette déclaration est que les cellules allouées en lecture sont partagées entre tous les clients. Le serveur doit maintenir la liste des clients qui utilisent un pixel donné pour libérer les cellules quand aucun client n'a plus l'intention d'utiliser ce pixel. On doit donc demander l'allocation de pixels en lecture, tout autant qu'en lecture/écriture.

                  L'allocation s'effectue de manière générale avec des fonctions commençant par XAllocColor. L'allocation en lecture se fera avec les fonctions XAllocColor, XAllocNamedColor, XcmsAllocColor ou XcmsAllocNamedColor, et l'allocation en lecture/écriture avec les fonctions XAllocColorCells  ou XAllocColorPlanes (selon le modèle utilisé). Une allocation en lecture/écriture est toujours privée, et les cellules allouées ne seront pas partagées.

 

                  Dans tous les cas, on doit libérer les cellules que l'on ne souhaite plus utiliser. La libération s'effectue avec la fonction XFreeColors. Cette fonction ne libère réellement les cellules que si aucun client ne les utilise plus. En effet, le serveur compte pour chaque pixel les demandes d'allocation et libération des différents clients. Si un client s'est alloué plusieurs fois le même pixel, il devra procéder à autant de demandes de libération pour que le serveur puisse conclure à une libération effective.

                  Le contenu des cellules allouées en lecture/écriture est initialement arbitraire et devra être initialisé. Pour écrire dans des cellules, on utilisera les fonctions XStoreColor, XStoreColors, XStoreNamedColor, XcmsStoreColor ou XcmsStoreColors.

 

                  Les cellules allouées en lecture unique ont un contenu qui a été attribué par le serveur. Pour lire le contenu des cellules, on dispose des fonctions en XQueryColor comme XQueryColor, XQueryColors, XcmsQueryColor ou XcmsQueryColors. Une telle lecture n'est pas toujours nécessaire, car on connaît souvent par avance le contenu des cellules. On peut utiliser ces fonctions pour n'importe quels pixels alloués, en lecture ou en écriture. Voici maintenant une brève description de toutes ces fonctions d'allocation, lecture et écriture.

 

 

 

Allocation de cellules en lecture (uniquement)

                  Une fois récupérée la définition d'une couleur avec l'une des fonctions en XLookupColor ou avec XParseColor, on peut demander l'allocation d'un pixel avec XAllocColor. La fonction XAllocNamedColor permettra de demander directement l'allocation d'un pixel à partir d'un nom de couleur :

 

Status XAllocColor ;(dpy, cmap, &color_in_out)

 

Status XAllocNamedColor (dpy, cmap, color_name, &screen_def_return,

                                                                                 &exact_def_return)

char*                 color_name ;

XColor             color_in_out,

                            screen_def_return, exact_def_return ;

 

                  XAllocColor alloue un pixel en lecture, et XAllocNamedColor permet d'allouer un pixel pour la couleur la plus proche d'une couleur donnée par son nom et effectivement disponible sur ce type d’écran. Contrairement à la fonction XLookupColor, XAllocNamedColor consulte la table de couleur cmap et alloue le pixel mentionné. Cette fonction retourne également la définition exacte de la couleur spécifiée par le nom color_name dans la structure exact_def_return dont l'adresse est passée en argument.

 

                  Les fonctions XcmsAllocColor et XcmsAllocNamedColor de la Release 5 font la même chose que les deux fonctions précédentes, mais utilisent une spécification de format quelconque. Elles effectuent en fait la conversion dans le format de la machine et appellent les fonctions précédentes pour retraduire le résultat dans le format spécifié :

Status XcmsAllocColor ;(dpy, cmap, &color_in_out, result_format)

Status XcmsAllocNamedColor (dpy, cmap, color_string, result_format,

                                                                                 &screen_def_return, &exact_def_return)

char*                                  color_string ;

XcmsColorFormat        result_format;

XcmsColor                      color_in_out,

                                             screen_def_return, exact_def_return ;

 

                  Ces fonctions retournent une variable entière Status qui peut valoir XcmsFailure, XcmsSuccess ou XcmsSuccessWithCompression.

 

 

Allocation de cellules en lecture/écriture

                  Contrairement aux fonctions précédentes, XAllocColorCells permet l'allocation de cellules en lecture/écriture (read/write). Cette fonction est prévue pour allouer des cellules dans le modèle à indexation directe PseudoColor :

Status XAllocColorCells (dpy, cmap, contig, &plane_masks_return,

                                                                 nplanes, pixels_return, npixels)

Bool               contig ;

unsigned long                 plane_masks_return;

unsigned int                    nplanes  ;

unsigned long                 pixels_return [];

unsigned int   npixels ;

 

                  Pour obtenir des pixels quelconques, on en indiquera simplement le nombre dans npixels et on appellera la fonction avec nplanes = 0. Mais XAllocColorCells permet également de spécifier des contraintes sur les pixels alloués. On peut demander à ce que ces pixels soient contiguës, grâce au booléen contig, ou encore demander une allocation régulière par rapport à un ou plusieurs plans. Plus précisément, on peut demander l'allocation d’un certain nombre de pixels et d’un certain nombre de plans, tels que les pixels retournés et leurs paires masquables par les plans retournés soient également alloués.  Ainsi, si l'on demande n pixels et p plans, on aura en fait obtenu l'allocation de nx2p pixels distincts. Le tableau de pixels retourné ne contiendra que n éléments, mais les pixels obtenus en faisant jouer le masque des plans indiqués par plane_masks_return seront également alloués par le serveur.

 

                  Par exemple, si on a fait la demande de deux pixels masquables par un plan, on aura en retour un tableau de 2 pixels et un masque de plan. Les deux pixels retournés différeront au moins sur un bit, mais auront tous deux le bit correspondant au masque du plan retourné à zéro :

pixels[0] = ------0---0----

pixels[1] = ------0---1----

plane_mask[0]  = ------1--------

 

                  Le serveur aura en réalité alloué quatre pixels : les deux pixels retournés dans le tableau pixels_return, mais également la paire duale obtenue en positionnant le bit du plan retourné à 1. Ainsi, on dispose en réalité des 4 pixels suivants (obtenus par application du masque de plan) :

pixels[0] = ------0---0----

pixels[1] = ------0---1----

pixel_0_bis    = ------1---0----

pixel_1_bis    = ------1---1----

 

                  L'intérêt est que l'on peut alors stocker des couleurs par paire de sorte que le masque du plan retourné suffise à faire basculer d'une couleur vers l'autre :

pixel_0_bis    =  pixels[0]  |  planemask[0]

                 

                  On peut parfaitement contrôler les couleurs présentes dans les paires concernées puisque l'on se situe ici au niveau de l'allocation dans un modèle où l'on peut écrire dans la table. Une fois récupérées les définitions des couleurs souhaitées, on effectuera cette opération de stockage grâce aux fonctions en XStoreColor;. On pourra utiliser ce type d'allocation pour tracer un dessin dans une couleur variant selon le fond sur lequel il est dessiné. Il suffit en effet de coupler les couleurs présentes à l'écran par les couleurs que l’on souhaite voir apparaître en dessinant[17].  Si la machine a de faibles performances, on peut utiliser cette technique pour modifier les couleurs de tout un dessin sans le redessiner entièrement. Il suffit de redessiner un rectangle plein englobant le dessin dans un contexte graphique tel que le ou les plans affectés (par l'attribut plane_mask) modifient les couleurs de l'écran comme dans l'exemple de la note précédente.

                   

                  La fonction XAllocColorPlanes permet elle aussi d'allouer des cellules vierges en lecture/écriture mais elle est prévue pour fonctionner dans le modèle à indexation séparée DirectColor. Elle permettra d'appliquer des techniques de dessin analogues.

 

Status XAllocColorPlanes (dpy, cmap, contig, pixels_return, ncolors, nreds,

                                                                                ngreens, nblues, &red_mask_return,

                                                                                &green_mask_return, &blue_mask_return)

Bool                                   contig ;

unsigned long                 pixels_return[] ;

unsigned long                 red_mask_return, green_mask_return,

                                             blue_mask_return ;

 

Libération de cellules allouées

                  Pour libérer des cellules de couleurs allouées par XAllocColor, XAllocNamedColor,  XAllocColorCells ou XAllocColorPlanes, on utilisera la fonction XFreeColors :

 

XFreeColors (dpy, cmap, pixels, npixels, planes)

unsigned long                 pixels [] ;

int                                        npixels ;

unsigned long                 planes ;

 

                  Dans le cas d'une allocation en écriture, les valeurs de npixels et planes seront celles fournies lors de l'appel à XAllocColorCells ou XAllocColorPlanes. En particulier, les pixels mentionnés dans le tableau et les plans indiqués par le masque n'auront pas de bit à 1 en commun. Les pixels libérés seront tous les pixels obtenus en formant leurs paires duales relativement aux différents plans.

 

Lecture de cellules

                  Pour lire des couleurs dans une table de couleurs, on dispose des fonctions suivantes :

 

XQueryColor ;(dpy, cmap, &def_in_out)

XQueryColors ;(dpy, cmap, defs_in_out, ncolors)

XColor             def_in_out,

                            defs_in_out[];

 

Status XcmsQueryColor (dpy, cmap, &color_in_out, result_format)

Status XcmsQueryColors (dpy, cmap, colors_in_out, ncolors, result_format)

XcmsColor    color_in_out,  colors_in_out[] ;

XcmsFormat result_format ;

 

;                  Ces fonctions spécifient les pixels dans les structures passées en argument et y reçoivent en retour les définitions associées (d'où le nom de color_in_out des paramètres). XQueryColors est une variante de XQueryColor permettant de récupérer les intensités RGB associées à plusieurs pixels simultanément.

 

 

Ecriture de cellules

 

                  Des pixels ayant été alloués par XAllocColorCells ou XAllocColorPlanes, on utilise les fonctions en XStoreColor pour y placer des valeurs de niveaux RGB :

 

XStoreColor (dpy, cmap, &xcolor)

Colormap                         cmap ;

XColor                              xcolor ;

XStoreColors (dpy, cmap, colors, ncolors)

XColor                              colors [] ;

 

                  Le pixel indiqué dans la structure XColor est affecté dans la table de couleur cmap par les valeurs spécifiées dans les autres champs de la structure. On peut spécifier également quelles cellules doivent être affectées en positionnant le drapeau flags de la structure à une combinaison des masques DoRed, DoGreen et DoBlue.

 

                  Les fonctions analogues pour les espaces de couleurs de la Release 5 sont les suivantes :

 

Status XcmsStoreColor (dpy, cmap, &color)

Colormap                         cmap ;

XcmsColor                      color ;

 

Status XcmsStoreColors (dpy, cmap, colors, ncolors,

                                                                                compression_flags_return)

XcmsColor                      colors [] ;

int                                        ncolors ;

Bool                  compression_flags_return [] ;           /* ou NULL */

 

                  S'il n'est pas NULL, le tableau de booléens retourné par cette requête permet de savoir si la couleur correspondante a été comprimée ou non.

 

XStoreNamedColor (dpy, cmap, color_string, pixel, flags)

const char*                      color_string ;

unsigned long                 pixel  ;

int                                        flags ;     /* combinaison de DoRed, DoGreen, DoBlue */

                  Cette fonction permet bien entendu de placer au pixel donné, les valeurs RGB correspondant à la spécification de couleur fournie dans color_string selon les masques indiqués dans flags.

 

 

9.9. Manipulation des tables de couleurs

                  On a vu qu'il existait une table de couleurs par défaut contenant des couleurs disponibles à l'écran. Cette table est partagée par tous les clients et s'obtient avec la macro

 

DefaultColormap (dpy, screen)

 

                  Si le modèle autorise l'écriture et que la table de couleurs par défaut n'a pas été entièrement remplie par d'autres clients, on pourra écrire dans cette table mais il n'est pas recommandé de le faire. Les allocations doivent normalement être effectuées dans une autre table. En fait, si l'on souhaite utiliser des couleurs correspondant au type Visual par défaut, il est recommandé d'utiliser plutôt une table standard.

 

                  De manière générale, on peut créer une table de couleur avec la fonction XCreateColormap.

 

Colormap XCreateColormap (dpy, win, visual, alloc)

Visual*            visual ;

int                      alloc ;                       /* AllocNone ou AllocAll */

 

                  Les valeurs initiales de cette table sont indéfinies pour les Visual de classe GrayScale, PseudoColor et DirectColor. Pour les classes StaticGray, StaticColor et TrueColor, les entrées sont définies mais sont spécifiques au visuel et non pas définies par X.

 

On ne doit pas passer la constante AllocAll dans les modèles n'autorisant pas l'écriture car il en résulterait une erreur.

 

                  Une table privée entièrement allouée par le passage de la constante AllocAll devra être libérée globalement par

XFreeColormap (dpy, cmap)

Mais, attention, aucune de ses entrées ne doit être libérée par XFreeColors dont l'usage est réservé à libérer les cellules allouées par XAllocColorCells ou XAllocColorPlanes.

                  Si des allocations échouent dans une table partagée que l'on utilisait jusqu'à présent, on pourra utiliser la fonction XCopyColormapAndFree pour créer une table de couleurs de mêmes caractéristiques et recopier le contenu des cellules allouées dans cette table avec les mêmes droits, tout en libérant les cellules de l'ancienne table :

 

Colormap XCopyColormapAndFree (dpy, cmap)

                  Les tables de couleurs seront associées aux fenêtres grâce à la fonction XSetWindowColormap, ou directement placées dans l'attribut colormap de la fenêtre à l'aide de XCreateWindow ou XChangeWindowAttributes. La valeur de cet attribut n'est cependant pas directement prise en compte par le serveur ; c'est le window manager qui est chargé (à partir de la Release 4) de contrôler l'installation des tables au niveau du hardware avec XInstallColormap et XUninstallColormap.

                  Les applications pourront être informées des modifications survenues sur les tables installées en sélectionnant les événements de type ColormapNotify (ou en appelant la fonction XListInstalledColormaps). La structure XColormapEvent associée aux événements de type ColormapNotify contient un champ colormap qui est à None si l'événement résulte d'une destruction de la table par XFreeColormap ; le champ new de cet événement est à True si un attribut de la table a changé et à False si la table a été installée ou désinstallée. Le champ state vaut ColormapInstalled ou ColormapUninstalled selon l'état de la table.

 

9.10. Les tables de couleurs standards

                  On a vu qu'il était important de partager les tables de couleurs. En effet, la plupart des machines ne permettent d'installer simultanément qu'une seule table de couleurs et seule l'application active à un instant donné apparaît alors à l'écran dans ses propres couleurs. A l'inverse, si différents clients partagent une même table, ils se trouvent affichés avec leurs propres couleurs chaque fois que l'un d'entre eux est actif, ce qui augmente leurs chances d'être affiché avec les bonnes couleurs. En outre, si le serveur sait par avance qu'ils utilisent la même table, cette table n'a pas besoin d'être réinstallée quand la main passe de l'un à l'autre. C'est pourquoi un mécanisme de partage de tables standards a été défini. Il est recommandé aux applications d'utiliser les tables standards chaque fois qu'elles le peuvent. Habituellement, le window manager créera des tables standards mais n'importe quel client peut également le faire.

 

          typedef struct {

                           Colormap colormap;             /* créée par XCreateColormap */

                           unsigned long red_max;                           /* niveau maximum */

                           unsigned long red_mult;           /* position du bit séparateur */  

                           unsigned long green_max;

                           unsigned long green_mult;

                           unsigned long blue_max;

                           unsigned long blue_mult;

                           unsigned long base_pixel;                  /* premier pixel alloué */

                           VisualID visualid;                                              /* visuel associé */

                           XID killid;                                                                                                    

          } XStandardColormap;

 

fig. 9.7. La structure XStandardColormap

 

                  Dans une table standard, les couleurs sont réparties régulièrement.  La structure XStandardColormap caractérise cette répartition et contient les membres indiqués figure 9.7. Pour chaque couleur fondamentale, le niveau peut varier de 0 à color_max. Par exemple, dans le modèle 3/3/2, on aura red_max=7, green_max=7 et blue_max=3. Le champ color_mult indique en fait où est placé le premier bit significatif (de poids le plus faible) d'une couleur fondamentale. Par exemple, dans le modèle 3/3/2 qui utiliserait la répartition red/green/blue, on aura blue_mult=20=1, green_mult=22=4 et red_mult=25=32 (cf. figure 9.8).

 

 

fig. 9.8. Les bits séparateurs de couleurs

                  Le champ base_pixel indique l'emplacement du premier pixel alloué (ce pixel a été retourné par un appel à XAllocColorPlanes). Pour calculer le pixel contenant les cellules de valeurs r, g, et b (sous réserve que ces valeurs soient bien comprises dans les intervalles spécifiés par les niveaux maximum), on pourra appliquer la formule :

 

pixel = (r*red_mult + g*green_mult + b*blue_mult + base_pixel) & 0xFFFFFFFF

                 

                  Les tables standards sont stockées sous forme de propriétés associées à la fenêtre racine. Les fonctions manipulant les propriétés sont décrites dans le chapitre suivant, mais on n'a pas besoin de les connaître pour manipuler les propriétés relatives aux tables de couleurs standards.

 

                  Pour créer une table de couleurs standard, le window manager utilise d'abord la fonction XAllocStandardColormap qui retourne un pointeur sur une structure de type XStandardColormap. Il crée ensuite une table avec XCreateColormap et installe dans les cellules de cette table les couleurs souhaitées selon une répartition régulière. Les paramètres de cette répartition et l'identificateur de la Colormap sont alors placés dans la structure XStandardColormap, qui est ensuite installée comme propriété de la fenêtre racine grâce à la fonction[18]

 

XSetRGBColormaps (dpy, win, &std_cmap, count, property)

XStandardColormap                     std_cmap ;

Atom                                                   property ;

                  L'Atom property utilisé ici est un nom de propriété prédéfinie. Ces noms (ou atomes) décrivent des propriétés dont les valeurs sont des XStandardColormap (de type RGB_COLOR_MAP et de format 32), sauf la propriété nommée RGB_DEFAULT_MAP dont la valeur est un tableau de XStandardColormap. En particulier, les propriétés suivantes ont été prévues : RGB_BEST_MAP, RGB_RED_MAP, RGB_GREEN_MAP, RGB_BLUE_MAP et RGB_GRAY_MAP.

 

                  Par exemple, la propriété RGB_BEST_MAP fournit une structure XStandardColormap qui définit la “meilleure” table standard disponible sur l'écran. Pour 8 plans en PseudoColor, RGB_BEST_MAP est habituellement une allocation du modèle 3/3/2. Pour 24 plans en DirectColor, c'est une allocation 8/8/8.

 

                  RGB_DEFAULT_MAP contient un tableau[19] de sous‑ensembles de la table de couleurs par défaut. En effet, certaines applications n'utilisent qu'un petit nombre de couleurs et les allouent dans la table de couleurs par défaut. C'est la situation idéale pour qu'une table soit partagée. Une allocation typique de RGB_DEFAULT_MAP sur une machine à 8 plans est de fournir 6 rouges, 6 verts et 6 bleus. Ce qui donne 216 couleurs distribuées uniformément (6 intensités de 36 tons différents) et laisse encore 40 éléments disponibles pour écrire dans la table.

                  Pour récupérer la ou les XStandardColormap associées à une propriété, les applications utiliseront la fonction XGetRGBColormaps :

Status XGetRGBColormaps (dpy, win, std_cmap_return, &count_return,

                                                                                property)

XStandardColormap**   std_colormap_return ;

Atom                                     property ;

 

                  On appellera cette fonction sur la fenêtre racine avec l'une des propriétés de type XStandardColormaps énumérées précédemment.

 

 

 

       Les fonctions importantes

 

          DefaultVisual (dpy, screen)

          Status XMatchVisualInfo (dpy, screen, depth, class, &vinfo_return)

          XVisualInfo * XGetVisualInfo (dpy, vinfo_mask, &vinfo_template,

                                                                                 &nitems_return)

 

          Status XParseColor (dpy, cmap, specif, &exact_def_return)

          Status XLookupColor ( dpy, cmap, color_name, &exact_def_return,

                                                                &screen_def_return)

          Status XcmsLookupColor (dpy, cmap, color_string, &color_exact_return,

                                                                &color_screen_return, result_format )

          Status XcmsQueryBlack (ccc, target_format, &color_return)

          Status XcmsQueryWhite (ccc, target_format, &color_return)

          Status XcmsQueryRed (ccc, target_format, &color_return)

          Status XcmsQueryGreen (ccc, target_format, &color_return)

          Status XcmsQueryBlue (ccc, target_format, &color_return)

 

         

          Status XAllocColor (dpy, cmap, &color_in_out)

          Status XAllocNamedColor (dpy, cmap, color_name, &screen_def_return,                                                                &exact_def_return)

          Status XcmsAllocColor (dpy, cmap, &color_in_out, result_format)

          Status XcmsAllocNamedColor (dpy, cmap, color_string, result_format,

                                                                                 &screen_def_return, &exact_def_return)

 

          Status XAllocColorCells (dpy, cmap, contig, &plane_masks_return,

                                                                 nplanes, pixels_return, npixels)

          Status XAllocColorPlanes (dpy, cmap, contig, pixels_return, ncolors,

                                                                                 nreds, ngreens, nblues, &red_mask_return,

                                                                                 &green_mask_return, &blue_mask_return)

   XFreeColors (dpy, cmap, pixels, npixels, planes)

 

   XQueryColor (dpy, cmap, &def_in_out)

   XQueryColors (dpy, cmap, defs_in_out, ncolors)

          Status XcmsQueryColor (dpy, cmap, &color_in_out, result_format)

          Status XcmsQueryColors (dpy, cmap, colors_in_out, ncolors,

                                                                                 result_format)

 

   XStoreColor (dpy, cmap, &xcolor)

   XStoreColors (dpy, cmap, colors, ncolors)

          Status XcmsStoreColor (dpy, cmap, &color)

          Status XcmsStoreColors (dpy, cmap, colors, ncolors,

                                                                                compression_flags_return)

   XStoreNamedColor (dpy, cmap, color_string, pixel, flags)

 

          Colormap DefaultColormap (dpy, screen)

          Colormap XCreateColormap (dpy, win, visual, alloc)

          XFreeColormap (dpy, cmap)

          Colormap XCopyColormapAndFree (dpy, cmap)

          Status XGetRGBColormaps (dpy, win, std_cmap_return, &count_return,

                                                                                property)

 


Exercice sur la couleur

Le corrigé de l'exercice 19 se trouve pages 347 et suivantes.

 

Exercice 19 : (Allocation et écriture de cellules)

Le but de cet exercice est de créer et d'afficher une palette de couleurs. On passe en argument au programme le nombre de niveaux souhaités dans chaque couleur fondamentale. Soit n, m et p les trois entrées, il s'agit alors de créer n*m*p couleurs et de les afficher dans des bandes régulièrement réparties sur une fenêtre (plutôt plus large que haute). Par exemple, si le programme a pris en argument 3, 4 et 2, on formera une palette comportant trois niveaux de rouge (0, max/2 et max), quatre niveaux de vert (0, max/3, 2*max/3 et max) et 2 niveaux de bleu (0 et max). Quand on cliquera avec la souris dans une des bandes de couleurs, le programme affichera les niveaux d'intensités RGB correspondants.

 

 



[1] Et aucune d'entre elles ne peut être obtenue par le mélange des deux autres.

[2]  Dans les XColor, on renormalisera ces niveaux de  0 à N* 65535/(Max - 1) avec N variant de 0 à Max-1.

[3] Qui pourront cependant être prises, parmi Max3 possibilités, où Max est le nombre de niveaux d’intensités distinguables.

[4] Sur les machines les plus performantes, on peut avoir plusieurs tables de couleurs installées sur différentes fenêtres simultanément actives à l’écran, mais ce n’est pas le cas courant. La fonction XListInstalledColormaps permet de récupérer des informations sur le nombre de Colormap installées sur l'écran.

[5] Avant la Release 4, les window manager ne se chargeaient pas toujours d'installer les Colormap des fenêtres. A partir de cette version cependant, la convention utilisée par X est que les clients ne doivent pas installer eux-mêmes de tables de couleur. Les fonctions utilisées par les window manager pour installer les tables de couleurs sont XInstallColormap et XUninstallColormap.

[6] Il est en effet toujours possible de demander une allocation en lecture simple, même dans une table de couleurs autorisant l'écriture. Les tables standards offrent également parfois des cellules vierges que certains clients pourront s'allouer.

[7]   Le gris est alors codé dans la cellule correspondant au rouge.

[8] Le Visual par défaut est utilisé par les fenêtres créées par XCreateSimpleWindow.

[9]  C'est le premier champ de la structure XVisualInfo.

[10] Le champ pixel étant initialisé, il s’agit bien d’un paramètre d’entrée, et les champs red, green, blue étant remplis en sortie de procédure, il s’agit également d’un paramètre de sortie.

[11] Une machine donnée n'est en fait capable de ne rendre qu'une certaine gamme de couleur, correspondant aux différentes combinaisons de tonalité et intensité des trois couleurs primaires qu'elle peut produire.

[12] Une base de noms de couleurs ne fournit pas en elle-même de correction de gamme puisqu'elle se présente dans la Release 3 sous la forme d'un fichier traduisant des noms en spécifications RGB — lesquelles, on l'a vu, ont un rendu qui dépend de la machine. La fiabilité des rendus de couleurs dépendra donc toujours des implantations. En outre, les spécifications de noms par chaîne de caractères posent un autre problème, celui de la standardisation dans les différentes langues.

[13]  Lorsqu'il traduit des niveaux RGB dans un espace de couleurs indépendant de la machine, le serveur passe en fait par l'intermédiaire de la traduction dans l'espace CIE XYZ.

[14]  Les autres fonctions traitant la couleur se trouvent définies dans <X11/Xlib.h>.

[15]  Les fonctions traitant la couleur dans la Release 4 utilisent des structures XColor (cf. figure 9.1.) et manipulent donc toujours la couleur dans le format du serveur.

[16] Les programmeurs ne bénéficiant que d'une version antérieure peuvent passer directement à la section 9.8.

[17] Par exemple, si on veut dessiner une droite jaune sur un fond comportant un rectangle bleu et un cercle rouge, on pourra obtenir un tracé orange quand la droite passe sur du rouge, et vert quand elle passe sur du bleu (avec le même ordre de dessin). Il suffit pour cela de coupler les couleurs (rouge,orange) et (bleu,vert) relativement à un certain plan que l’on appellera par exemple plan_de_jaune. Le tracé d’une droite en jaune se fait alors tout naturellement comme le tracé dans un contexte graphique où l’on a modifié le champ plane_mask par plan_de_jaune. Peindre en jaune revient alors à peindre relativement au plan_de_jaune. L’ordre de dessin initial peut ainsi rester le même quelques soient les surfaces de couleurs traversées. C’est l'attribut plane_mask du contexte graphique qui garantit les changements de couleurs, selon les couleurs rencontrées sur l’écran.

[18] Pour installer la propriété, il faut en outre monopoliser le serveur pendant la section critique par un appel à XGrabServer et le libérer ensuite avec XUngrabServer.

[19] Normalement, seule RGB_DEFAULT_MAP contient éventuellement plusieurs définitions.