6

Les dessins

 

 

 

 

6.1. Le contexte graphique

                  La librairie Xlib propose des fonctions de dessin élémentaires. Quoique assez variées (contours de figures géométriques, remplissages de figures et dessin de textes), ces fonctions prennent toutes en argument un contexte graphique (type GC) qui permet de spécifier les paramètres de la requête. Un contexte graphique est en quelque sorte un paramètre multiple qui contient tous les paramètres utilisés par les requêtes de dessin.

                  Cependant, les paramètres du contexte graphique utilisés par une requête varient d'une requête à l'autre. Ainsi, un contexte graphique comporte un attribut  indiquant une police de caractères (font). Il contient aussi un attribut permettant de définir un style de tracé de ligne (line_style). Les fonctions qui dessinent des contours de polygones utilisent l'attribut line_style alors que celles qui impriment des textes utilisent l'attribut font.

 

                   Une requête graphique utilise fréquemment plusieurs paramètres du contexte graphique. Un principe général sous-tend cependant l’usage du contexte graphique pour toutes les primitives de dessin.

 

 

Principe général d'utilisation

 

                  Une requête graphique engendre un bitmap représentant les points du dessin (par exemple, un segment de droite avec XDrawLine). Appelons ce dessin initial la source. La requête graphique indique en outre toujours où doit être reporté ce dessin ­— la destination — et dans quel contexte graphique il doit être effectué. La requête graphique vient alors modifier la destination (en général une fenêtre) en fonction du dessin source et des valeurs des différents attributs du contexte graphique.

 

                  Pour toutes les requêtes, la destination est un argument de type Drawable, c’est-à-dire quelque chose sur lequel on peut dessiner :

                  • soit une fenêtre de type InputOutput

                  • soit un Pixmap

Les paramètres du contexte graphique sont d'abord consultés pour déterminer la couleur des points du dessin source, puis d’autres paramètres sont consultés pour déterminer les points effectivement modifiés sur la destination. Il existe par exemple des paramètres permettant de limiter le cadre du dessin et une fonction de transfert du dessin permettant de tenir compte également des points de la destination. Le Pixmap qui résulte de ce calcul est finalement placé sur les points de la destination.

 

                  Un contexte graphique est en général global au programme et n'est associé à aucune fenêtre particulière. Pour faire des dessins différents, on peut disposer de plusieurs contextes graphiques ou bien faire varier un même contexte graphique. Dans le premier cas, on minimise les échanges avec le serveur, dans le deuxième, on économise la mémoire.

 

La documentation sur la librairie recommande de limiter si possible le nombre de contextes graphiques. En effet, passé un certain nombre, les contextes graphiques ne sont plus gardés en mémoire et les performances du programme s’en trouvent particulièrement diminuées.

 

 

6.2. Les paramètres du contexte graphique

                  La structure de données permettant de définir les paramètres d'un contexte graphique (type GC) est de type XGCValues (cf. figure 6.1.).

 

 

typedef struct {

    int function;  /* opération logique entre  la source et la destination */

    unsigned long plane_mask;                                  /*  masque des plans */

    unsigned long foreground;                                   /* couleur du dessin */ unsigned long background;                                                                                           /* couleur du fond */ int line_width;                                                                                            /* largeur de ligne */

    intline_style;            /* LineSolid, LineOnOffDash, LineDoubleDash */ int cap_style;         /* CapNotLast, CapButt,  CapRound, CapProjecting */

    int join_style;           /* JoinMiter, JoinRound, JoinBevel */

    int fill_style;                     /* FillSolid, FillTiled */

                                                                                          /*  FillStippled, FillOpaqueStippled */

    int fill_rule;                                              /* EvenOddRule, WindingRule */

    int arc_mode;                                                            /* ArcChord, ArcPieSlice */

    Pixmap tile;             /* tuile pour les opérations de pavage */

    Pixmap stipple;                                                     /* pochoir (pixmap plan)  */ int ts_x_origin;              /* origine du premier pavé dans le dessin */

    int ts_y_origin;

    Font font;                   /*  fonte utilisée pour les textes */

    int subwindow_mode;                      /* ClipByChildren, IncludeInferiors */

    Bool graphics_exposures;     /* pour sélectionner des evenements  */

    int clip_x_origin;                    /* origine de la zone limitant le dessin */

    int clip_y_origin;

    Pixmap clip_mask;                                        /* masque de la zone atteinte */

    int dash_offset;                                         /*  attributs du mode pointillé */

    char dashes;

} XGCValues;

 

fig. 6.1. La structure de données XGCValues qui

permet de spécifier un contexte graphique

 

                  Les trois premiers champs de cette structure spécifient des attributs qui sont utilisés par toutes les requêtes graphiques.

 

 

La fonction de transfert du dessin

 

function                                La fonction de transfert du dessin indique une opération à effectuer bit à bit entre les pixels du Pixmap source et les pixels de la destination. On peut modifier le champ function d’un contexte graphique grâce à la requête XSetFunction (dpy, gc, function).

 

                  Il existe 16 fonctions de transfert correspondant aux 16 connecteurs binaires de la logique. Pour chacune d'entre elles, un symbole a été défini en C (cf. fig. 6.2. ). La fonction de transfert est effectuée bit à bit entre chaque pixel du dessin source et de la destination. Ainsi la fonction de transfert GXCopy permet d'ignorer la couleur des points de la destination. Avec GXCopy le dessin est directement reporté sur la destination (en général dans la couleur de foreground), sans tenir compte de son contenu antérieur. Inversement, GXnoop et GXinvert ne tiennent compte que des couleurs de la destination. L'effet de la fonction GXxor est illustré figure 6.3.

 

                 

 

              GXclear                                      /* 0 */

     GXand                                  /* src ET dst */

     GXandReverse                       /* src ET NON dst */

     GXcopy                                      /* src */

     GXandInverted                      /* NON src ET dst */

     GXnoop                                      /* dst */

     GXxor                           /* src OU (exclusif) dst */

     GXor                                  /* src OU dst */

     GXnor                          /* NON src ET NON dst */

     GXequiv                     /* NON src OU (exclusif) dst */

     GXinvert                                /* NON dst */

     GXorReverse                       /* src OU NON dst */

     GXcopyInverted                          /* NON src */

     GXorInverted                      /* NON src OU dst */

     GXnand                        /* NON src OU NON dst */

     GXset                                        /* 1 */

 

 

fig. 6.2. Les fonctions de transfert

 

                   

 

fig. 6.3. Effet de la fonction GXxor

 sur un pixel de la destination

                  On utilisera GXxor[1] ou GXInvert pour faire des affichages en inverse vidéo. GXxor permet de faire de l'inverse vidéo en couleur. Avec GXinvert, l'attribut foreground du contexte graphique est ignoré et seule la couleur de la destination est prise en compte (et inversée).

 

 

Le masque des plans de couleurs

plane_mask                          Le masque des plans indique les plans de couleurs effectivement atteints sur la destination. On pourra modifier cet attribut par la requête XSetPlaneMask (dpy, gc, plane_mask). Pour affecter tous les plans, on utilise la macro AllPlanes.

 

                   On ne reporte le dessin source sur la destination que pour les plans indiqués par le masque des plans. Les autres plans restent inchangés, c'est-à-dire que les bits des pixels concernés par le dessin gardent les valeurs qu'ils avaient précédemment sur la destination dans les plans non masqués (cf. figure 6.4.).

 

 

fig. 6.4. Effet du masque des plans sur un pixel

(avec la fonction GXcopy)

 

                  L’intérêt du masque des plans sera illustré dans le chapitre sur la couleur. Sur les écrans noir et blanc, le masque des plans est en général positionné à AllPlanes et on ne s'en préoccupera pas, à moins que l'on ne souhaite réaliser un programme portable sur une machine couleur (cf. section 9.4. Le noir et blanc).

Les couleurs du dessin source

 

foreground

background

L'attribut foreground est très important : il permet d'indiquer la couleur du dessin source. L'attribut background indique la couleur du fond utilisé par certaines requêtes (comme XDrawImageString, XFillRectangles, etc.).  

                  On peut modifier les attributs foreground et background d’un contexte graphique grâce aux requêtes

 

XSetForeground(dpy,gc,pixel)

XSetBackground(dpy,gc,pixel)

 

 

La forme des lignes

 

                  Quatre attributs précisent la forme des lignes dans les dessins de segments ou de polygones.

 

fig. 6.5. L'épaisseur de ligne (line_width)

 

line_width                          indique l'épaisseur des lignes (cf. figure 6.5.).

 

line_style                          indique un style de ligne. Cet attribut peut prendre les valeurs LineSolid, LineOffOnDash ou LineDoubleDash (cf. figure 6.6.).

 

 

fig. 6.6. Le style de ligne (line_style)

cap_style                             caractérise la manière dont se terminent les lignes. Les bouts de lignes peuvent être arrêtés nets avec CapButt. CapNotLast  permet de ne pas inclure le dernier pixel. CapRound  et CapProjecting indiquent des bouts de lignes respectivement arrondis et rectangulaires, pour des lignes possédant déjà un certain relief (une demi-épaisseur de ligne d'avancée).

join_style                          caractérise la manière dont se joignent les segments dans le tracé de polygones (cf. figure 6.7.). Il s’agit d’approximation du fait de la nature discrète des points d’écran.

 

fig. 6.7. Les styles de raccords de lignes (join_style)

                  La fonction XSetLineAttributes permet de modifier simultanément les quatre attributs du dessin des lignes :

 

XSetLineAttributes(dpy, gc, line_width, line_style, cap_style, join_style)

 

                  Pour les lignes pointillées, deux autres attributs du contexte graphique seront utilisés :  dash_offset, et dashes (de type char). On pourra ainsi préciser l'origine de la reproduction du tiret et sa longueur en pixel.  Cependant, on évitera d'utiliser les modes pointillés dans la Release 4 car ils sont très lents.

                  Les attributs dash_offset et dashes du contexte graphique permettent de définir un style de pointillé standard mais la fonction XSetDashes permet d'affecter au contexte graphique un style de pointillé plus complexe :

 

XSetDashes;(dpy, gc, dash_offset, dash_list, nb_dash)

Display *        dpy ;

GC                     gc ;

int                      dash_offset ;

char  dash_list [];

int                      nb_dash ;

 

                  Le motif du pointillé est alors défini par un tableau de caractères (argument dash_list) indiquant, par une liste d'entier, le nombre de pixels devant successivement être alternés dans les couleurs foreground et background. La figure 6.8. en donne quelques exemples.

 

 

fig. 6.8. Exemples de lignes pointillées

 

                   L’argument nb_dash indique le nombre d'entiers figurant dans le tableau, et dash_offset précise l'origine en pixel de la reproduction du motif. Les pointillés peuvent ainsi être plus complexes que ceux que l'on peut définir à partir des seuls attributs du contexte graphique.

 

 

Les attributs de remplissage

 

                  Les attributs fill_style, fill_rule, arc_mode, tile, stipple, ts_x_origin et ts_y_origin concernent les requêtes de remplissage comme XFillRectangles ou XFillArcs. Ces requêtes dessinent en général dans la couleur foreground mais elles peuvent aussi utiliser un motif pour paver régulièrement le dessin.

 

tile

stipple

Des motifs peuvent être indiqués dans ces attributs. Le premier est un Pixmap quelconque appelé tile  (tuile) pour évoquer la disposition des tuiles sur un toit. Le second, stipple (pochoir), est appelé ainsi car c'est un Pixmap plan qui permet de laisser des points de la destination inchangés. L'utilisation de ces attributs est gouvernée par l'attribut fill_style.

 

fill_style                          Cet attribut détermine le style de remplissage. Il peut prendre les valeurs FillSolid, FillTiled, FillStippled et FillOpaqueStipple. Ces différents styles sont illustrés figure 6.9.

 

ts_x_origin

ts_y_origin

;Ces attributs indiquent l'origine à partir de laquelle est reporté le motif quand le style de remplissage utilise la tuile ou le pochoir (cf. figure 6.10).

 

 

                  Les fonctions suivantes permettent de modifier ces attributs :

 

XSetFillStyle (dpy, gc,fill_style)

XSetTile ;(dpy, gc, tile)

XSetStipple (dpy, gc, stipple)

XSetTSOrigin (dpy, gc, ts_x_origin, ts_y_origin)

 

fig. 6.9. Le style de remplissage (fill_style)

 

 

fig. 6.10. L'origine du pavage d’un motif

 

 

                  Avec FillSolid, la figure est remplie avec la couleur de foreground. Le style FillTiled permet d'effectuer un pavage de la tuile et le style FillStippled effectue un remplissage du pochoir dans la couleur foreground. FillOpaqueStippled permet de disposer d'un deuxième motif dans les couleurs du foreground et du background.

Il est recommandé d'utiliser des tailles de motif spécifiques pour obtenir une vitesse de réaffichage optimale. Ces tailles dépendent du serveur. Les fonctions suivantes permettent de connaître les dimensions préférées du serveur :

 

Status XQueryBestTile (dpy, drawable, width, height, &ret_width,

                                                                &ret_height)

Status XQueryBestStipple (dpy, drawable, width, height, &ret_width,

                                                                &ret_height)

Status XQueryBestSize (dpy, class, drawable, width, height, &ret_width,

                                                                &ret_height)

 

                  Dans ces trois fonctions l’argument drawable sert à indiquer la destination. Les arguments width et height indiquent les dimensions souhaitées par le client et ret_width et ret_height permettent de récupérer les tailles préférées du serveur. Les deux premières fonctions permettent de récupérer des dimensions pour une tuile et pour un pochoir. La fonction XQueryBestSize est plus générale car l’entier class permet d’indiquer l’usage qui sera fait du motif : CursorShape pour un curseur, TileShape pour une tuile et StippleShape pour un pochoir.

 

fill_rule          L'attribut fill_rule (règle de remplissage) concerne le remplissage des polygones fermés. Il peut prendre deux valeurs :  EvenOddRule et WindingRule (cf. figure 6.11. page suivante).

 

                    La fonction XSetFillRule (dpy, gc, fill_rule) permet de modifier cet attribut[2]. Avec EvenOddRule, littéralement règle paire/impaire,  la requête ne remplira les surfaces internes que si elles se recouvrent un nombre impair de fois alors qu’avec la règle WindingRule elle remplira l’intérieur du polygone quel que soit le nombre de superpositions.

 

fig. 6.11. Les valeurs de l'attribut fill_rule

pour remplir l’intérieur des polygones

 

arc_mode                              L'attribut arc_mode définit le mode de remplissage des arcs. Cet attribut peut prendre deux valeurs, ArcChord (à la corde) et ArcPieSlice  (en part de gâteau, cf. figure 6.12.) La fonction XSetArcMode permet de changer cet attribut.

 

fig. 6.12. Modes de remplissage des arcs

 

 

La fonte

 

font                                        L'attribut font indique la police de caractères utilisée par les requêtes XDrawString, XDrawImageString et XDrawText.

 

                  La fonction XSetFont (dpy, gc, font)  permet de changer la fonte d’un contexte graphique. Nous reviendrons en détail sur les fontes dans les deux chapitres suivants.

 

 

Les attributs qui contrôlent la zone de report du dessin

 

subwindow_mode              L'attribut subwindow_mode indique si l'opération effectuée dans une fenêtre parent doit également porter sur les fenêtres filles (IncludeInferiors) ou les ignorer (ClipByChildren). On peut modifier cet attribut avec la fonction XSetSubwindowMode (dpy, gc, mode).

 

L’attribut subwindow_mode est utilisé aussi bien par les requêtes de dessin que par les fonctions de copie. Par exemple, si l’on souhaite copier tout l’écran (i.e. la fenêtre racine et le contenu de toutes ses filles), il faut positionner l’attribut subwindow_mode à IncludeInferiors pour que la requête copie également le contenu des filles de la racine.

 

                  Les fonctions de copies (XCopyArea et XCopyPlane) permettent de transférer le contenu d'une zone rectangulaire d’un objet source sur un autre objet. Ces procédures peuvent échouer plus ou moins car la zone source peut ne pas être entièrement disponible, du fait des dimensions indiquées ou parce qu'elle peut être en partie cachée. On peut être informé en cas d’échec (dû à une indisponibilité de la source) des zones non atteintes par la requête de copie, à condition d'avoir positionné l’attribut graphics_exposures du contexte graphique à True dans la requête. 

 

graphics_exposures Cet attribut permet de sélectionner les événements de type GraphicsExpose et NoExpose résultant d'un appel à une fonction de copie comme XCopyArea ou XCopyPlane. On pourra modifier cet attribut avec la fonction XSetGraphicsExposures (dpy, gc, bool).

 

                  Si l'attribut graphics_exposures est positionné à True lors de l'appel à la requête de copie, le serveur émettra :

                  • un ou plusieurs événements de type GraphicsExpose si la source n’était que partiellement disponible pour indiquer les zones qui n'ont pas été atteintes et qui doivent être réexposées à la requête. Ces régions sont analogues à celles envoyées par un événement de type Expose, car elles nécessitent également un réaffichage.

                  • un seul événement de type NoExpose si la source était entièrement disponible (succès total) ; cependant, le serveur émet également un événement de type NoExpose si la destination n'était pas du tout disponible sur la région concernée[3].

 

                  Viennent ensuite trois attributs permettant de restreindre la zone des points de la destination affectés par une requête graphique. Cette zone s'appelle en anglais la zone de clipping.

 

clip_x_origin

clip_y_origin

clip_mask

;Les attributs clip_x_origin et clip_y_origin indiquent l'origine de la zone affectée par la requête et l'attribut clip_mask délimite la zone proprement dite (cf. figure 6.13.). On appellera cette zone par abus de langage la zone de clipping..

 

 

fig. 6.13. Effet du masque de cadrage sur le tracé d'un segment

                  Les fonctions qui permettent de modifier les attributs de la zone de clipping du contexte graphique sont les suivantes :

 

XSetClipMask (dpy, gc, clip_mask)

XSetClipOrigin (dpy, gc, clip_x_origin, clip_y_origin)

 

      XSetClipMask ;permet de définir la zone atteinte par le Pixmap passé en argument. On peut passer la constante None si l’on ne souhaite plus utiliser de zone de clipping. Un appel à XSetClipMask ;(dpy, gc, None) permet donc d'annuler la zone de clipping.

 

                  La zone de clipping est modifiée pour le réaffichage de dessins. En effet, cette optimisation est nécessaire pour éviter de réafficher une zone où le dessin est encore présent. On fait appel à des requêtes dessinant le dessin dans toute la fenêtre, mais on restreint la zone atteinte par ces requêtes à la zone qui a perdu les dessins (celle correspondant à l'événement Expose qui a été reçu) dans le contexte graphique.

                  Pour faciliter la programmation, la librairie offre plusieurs fonctions permettant de modifier la zone de clipping. La fonction XSetClipRectangles permet de limiter la zone de clipping du contexte graphique à une réunion de rectangles (passée en argument sous forme d’un tableau). On peut ainsi limiter la zone de clipping aux rectangles rapportés par les événements de type Expose ou GraphicsExpose. Nous reviendrons sur cette fonction dans la section 6.5.

 

                  La fonction XSetRegion permet également d'affecter la zone de clipping à une zone délimitée par une région (cf. section 6.6.).

 

 

6.3. Création et modification du contexte graphique

                  Pour créer un contexte graphique on utilise la fonction

 

GC XCreateGC (display, drawable, valuemask, &xgc_values)

unsigned long                 valuemask ;

XGCValues                     xgc_values ;

 

                  Cette fonction utilise le même principe que XCreateWindow. Le contexte graphique ainsi créé bénéficie des valeurs d'attributs de la structure xgc_values qui sont spécifiés par la combinaison de masques valuemask. Les masques correspondants aux différents champs de la structure XGCValues (cf. figure 6.1.) sont énumérés figure 6.14. L'argument de type Drawable sert ici à indiquer les caractéristiques de l'écran contenant la fenêtre ou le Pixmap sur lequel seront réalisés les dessins. On pourra donc souvent passer en argument la fenêtre racine.

 

           #define GCFunction              (1L<<0)

           #define GCPlaneMask             (1L<<1)

           #define GCForeground            (1L<<2)

           #define GCBackground            (1L<<3)

           #define GCLineWidth             (1L<<4)

           #define GCLineStyle             (1L<<5)

           #define GCCapStyle              (1L<<6)

           #define GCJoinStyle             (1L<<7)

           #define GCFillStyle             (1L<<8)

           #define GCFillRule              (1L<<9)      #define GCTile     (1L<<10)                        #define GCStipple           (1L<<11)                        #define GCTileStipXOrigin           (1L<<12)

           #define GCTileStipYOrigin       (1L<<13)     #define GCFont     (1L<<14)                        #define GCSubwindowMode           (1L<<15)                        #define GCGraphicsExposures                        (1L<<16)

           #define GCClipXOrigin           (1L<<17)

           #define GCClipYOrigin           (1L<<18)

           #define GCClipMask              (1L<<19)

           #define GCDashOffset            (1L<<20)

           #define GCDashList              (1L<<21)

           #define GCArcMode               (1L<<22)

 

fig. 6.14. Masques pour la définition

 des champs du contexte graphique

                  De manière analogue, on peut modifier un contexte graphique existant grâce à la fonction

XChangeGC (dpy, gc, valuemask, &xgc_values);

 

                  Cependant cette procédure est un peu lourde puisqu'il faut d'abord initialiser une structure de type XGCValues.  Si l'on souhaite modifier un contexte graphique sur quelques attributs seulement, on utilisera volontiers une procédure ad hoc. Comme nous l'avons vu dans la section précédente, il existe quasiment une fonction par attribut :

 

 

XSetState (dpy, gc, fg, bg, function, plane_mask)

XSetForeground (dpy, gc, fg)

XSetBackground (dpy, gc, bg)

XSetFunction (dpy, gc, function)

XSetPlaneMask (dpy, gc, planemask)

XSetLineAttributes (dpy, gc, lin_width, line_style, cap_style, join_style)

XSetDashes (dpy,gc,dash_offset, dash_list, n)

XSetFillStyle (dpy, gc, fill_style)

XSetFillRule (dpy, gc, fill_rule)

XSetTile ( dpy, gc, tile)

XSetStipple (dpy, gc, stipple)

XSetTSOrigin (dpy, gc, ts_x, ts_y)

XSetFont (dpy, gc, font)

XSetClipOrigin (dpy, gc, x, y)

XSetClipMask (dpy, gc, pixmap)

XSetClipRectangles (dpy, gc, cx, xy, rectangles, nrect, ordre)

XSetArcMode (dpy, gc, arc_mode)

XSetSubwindowMode (dpy, gc, subwindow_mode)

 

 

                  On peut également copier les attributs d'un contexte graphique dans un autre avec la fonction XCopyGC qui spécifie dans valuemask les valeurs devant être copiées.

 

XCopyGC (dpy, gc_source, valuemask, gc_dest)

 

 

 

Le contexte graphique par défaut

 

                  La macro DefaultGC (dpy,screen) permet d'éviter la création d'un contexte graphique en retournant un contexte graphique par défaut. Les valeurs des attributs du contexte graphique par défaut sont énumérées figure 6.15. page suivante. On prendra garde cependant à bien contrôler que les valeurs des attributs importants, comme par exemple celle de l'attribut foreground, soient convenables. Si le programme est en noir et blanc, on a tout de même une chance sur deux d’écrire en blanc sur blanc ou en noir sur noir dans les fenêtres ! La figure 6.15. indique les valeurs par défaut de ce contexte graphique.

 

 

int                                  function                                    GCCopy

unsigned long           plane_mask                              Tous à 1

unsigned long           foreground                              0

unsigned long           background                              1

int                                  line_width                              0

int                                  line_style                              LineSolid

int                                  cap_style                                 CapButt

int                                  join_style                              JoinMiter

int                                  fill_style                              FillSolid

int                                  fill_rule                                 EvenOddRule

int                                  arc_mode                                    ArcPieSlice

Pixmap                        tile                                                de la couleur de foreground

Pixmap                        stipple                                       rempli de 1

int                                  ts_x_origin                           0

int                                  ts_y_origin                           0

Font                              font                                                dépend de l'implantation

int                                  subwindow_mode                  ClipByChildren

Bool                              graphics_exposures      True

int                                  clip_x_origin                     0

int                                  clip_y_origin                     0

Pixmap                        clip_mask                                 None

int                                  dash_offset                           0

char                               dashes                                          4

 

fig. 6.15. Les valeurs des attributs du contexte graphique par défaut

 

 6.4. Les fonctions de dessin

                  On dispose de fonctions graphiques élémentaires pour dessiner sur un objet. Toutes ces requêtes portent sur un objet de type Drawable, c'est-à-dire une fenêtre de classe InputOutput ou un Pixmap. Elles consultent toutes le contexte graphique sur les attributs foreground, function, subwindow_mode, plane_mask, clip_x_origin, clip_y_origin et clip_mask. Il existe également des requêtes de dessin permettant de dessiner des morceaux de textes, mais ces fonctions seront détaillées dans les chapitres suivants.

 

 

 

 Pour éviter des déceptions, on prendra garde avec toutes les fonctions de dessin qui suivent :

                  • A bien contrôler les valeurs des paramètres du contexte graphique intervenant dans le calcul des couleurs du dessin final. Ainsi, pour dessiner dans une fenêtre de fond blanc, on pourra utiliser un contexte graphique de foreground noir et de fonction de transfert Gxcopy, ou bien utiliser la fonction de transfert GXinvert. L'avantage de la fonction GXinvert sur écran noir et blanc est qu'elle permet de dessiner sur fond blanc comme sur fond noir. L'inconvénient est que deux dessins successifs effacent le dessin.

                  • A s'assurer que la fenêtre dans laquelle on dessine est bien visible à l'écran au moment où se produit la requête de dessin (voir plus loin section 6.5.). Sinon, le dessin n'apparaitra pas, mais aucun message d'erreur ne sera émis.

 

 

Dessiner des points

 

                  On peut dessiner un ou plusieurs points dans la couleur de foreground grâce aux requêtes XDrawPoint et XDrawPoints :

XDrawPoint (dpy, draw, gc, x, y)

XDrawPoints (dpy, draw, gc, points, npoints, mode)

Drawable        draw ;

GC                     gc ;

XPoint              points [] ;

int                      npoints ;

int                      mode ;

 

typedef struct {

          short x, y ;

} XPoint ;

 

 

Dessiner des lignes

 

                  Les requêtes dessinant des lignes, des segments ou des contours de polygones consulteront les attributs foreground, line_width, line_style, cap_style, join_style, dash_offset et dash_list. Pour certains styles de pointillés (cf. la description de l'attribut line_style), les attributs background, dashes et dash_offset seront également consultés.

 

                  Pour dessiner une ou plusieurs lignes passant par les points indiqués en argument, on utilisera les requêtes XDrawLine et XDrawLines :

 

XDrawLine (dpy, draw, gc, x1, y1, x2, y2)

int                      x1, y1, x2, y2 ;

XDrawLines (dpy, draw, gc, points, npoints, mode)

Drawable        draw ;

GC                     gc ;

XPoint              points [] ;

int                      npoints  ;

int                      mode ;    /* CoordModeOrigin ou CoordModePrevious */

 

                  Le mode permet de spécifier dans la requête si les points sont donnés relativement à l'origine, ou à partir de l'origine pour le premier point et relativement au point précédent pour les autres. XDrawLines trace toutes les lignes entre deux points successifs dans le tableau et permet donc de tracer des polygones[4]. Par contre XDrawSegments est utilisée pour tracer un groupe de segment disjoints :

XDrawSegments (dpy, draw, gc, segments, nsegments)

Drawable        draw ;

GC                     gc ;

XSegment       segments [] ;

int                      nsegments ;

 

typedef struct {

          short x1, y1, x2, y2 ;

} XSegment ;

 

Tracer des rectangles

 

                  Pour tracer un ou plusieurs rectangles, on utilisera les fonctions

XDrawRectangle (dpy, draw, gc, x, y, width, height)

Drawable        draw ;

GC                     gc ;

int                      x, y ;

unsigned int   width, height ;

XDrawRectangles (dpy, draw, gc, rectangles, nrects)

Drawable        draw ;

GC                     gc ;

XRectangle    rectangles [] ;

int                      nrects ;

 

typedef struct {

          short x, y ;

          unsigned short width, height ;

} XRectangle ;

 

 

Dessiner des arcs

 

                  XDrawArc et XDrawArcs utilisent des angles exprimés en 64e de degré ; on utilise donc 64*90 pour désigner p/2 et 64*180 pour désigner un angle plat.

 

XDrawArc (dpy, draw, gc, x, y, width, height, angle1, angle2)

Drawable        draw ;

GC                     gc ;

int                      x, y, angle1, angle2 ;

unsigned int   width, height ;

 

XDrawArcs (dpy, draw, gc, x, y, arcs, narcs)

Drawable        draw ;

GC                     gc ;

XArc                 arcs [] ;

int                      narcs ;

                 

typedef struct {

          short        x,y;

          unsigned short width, height;

          short angle1, angle2;

} XArc ;

 

                  Les angles peuvent être positifs (sens inverse des aiguilles d’une montre) ou négatifs (sens des aiguilles d’une montre). Ainsi par exemple, la requête

 

XDrawArc (dpy,win,gc,x,y,width,height, 90x64, -(45x64))

 

dessine l'arc correspondant au secteur angulaire représenté figure 6.16. page suivante.

 

fig. 6.16. Le dessin des arcs

 

 

Remplir des figures

                  Des fonctions analogues aux précédentes permettent de remplir des figures :

 

XFillRectangle (dpy, draw, gc, x, y, width, height)

int                      x, y ;

unsigned int   width, height ;

 

XFillRectangles (dpy, draw, gc, rectangles, nrects)

XRectangle    rectangles [] ;

int                      nrects ;

 

XFillArc (dpy, draw, gc, x, y, width, height, angle1, angle2)

int                      x, y, angle1, angle2;

unsigned int width, height ;

XFillArcs (dpy, draw, gc, x, y, arcs, narcs)

XArc                 arcs [] ;

int                      narcs ;

 

XFillPolygon (dpy, draw, gc, points, npoints, shape, mode)

XPoint              points [] ;

int                      npoints, shape, /* Complex, Convex ou Nonconvex */

                            mode ; /* CoordModeOrigin ou CoordModPrevious */

 

                  Toutes ces fonctions utilisent les attributs fill_style,  background, tile, stipple, ts_x_origin et ts_y_origin pour spécifier le mode de remplissage. Les fonctions sur les arcs utilisent en outre l'attribut arc_mode et la fonction XFillPolygon utilise l'attribut fill_rule.

 

   1. Les requêtes qui dessinent plusieurs objets simultanément ont une limite. Le nombre maximal d'objets dépend du serveur et des requêtes[5].

                       2. Les requêtes XFillRectangle et XDrawRectangle ne spécifient pas le rectangle de la même façon. Employées avec les mêmes arguments, elles ne dessinent pas le contour et l'intérieur d'un même objet. En fait XFillRectangle respecte bien les dimensions indiquées alors que XDrawRectangle dessine un rectangle dont les dimensions ont été augmentées d'un pixel.

 

 

Copier des dessins

 

                  Il existe également des fonctions permettant de copier le contenu d'une zone rectangulaire d’un objet source vers un autre objet :

XCopyArea (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)

XCopyPlane (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y,

                                 plane)

Drawable                          src, dest ;

int                                        src_x, src_y, dest_x, dest_y :

unsigned int                    width, height ;

unsigned long                 plane ;

                  La zone rectangulaire de la source src est délimitée par les quatre arguments src_x, src_y, width, et height, et le point d'origine du report est spécifié sur la destination dest par les coordonnées dest_x et dest_y (cf. figure 6.17. page suivante).

 

 

fig. 6.17. Copier de la source vers la destination

 

 

                  Ces procédures sont susceptibles d’échouer si la source n'est pas entièrement disponible. En cas d’échec, on peut recevoir sous forme d'événements les zones de la destination qui n'ont pu être atteintes par la requête du fait d'une indisponibilité de la source. Pour recevoir ces événements, il faut avoir positionné l’attribut graphics_exposures du contexte graphique à True dans la requête de copie. Le serveur émet alors après la copie : soit un ou plusieurs événements de type GraphicsExpose, pour indiquer les zones de la destination qui devront être à nouveau soumises à la requête (du fait d'une indisponibilité de la source), soit un événement de type NoExpose si la copie s'est bien passée[6].

 

                  La fonction XClearArea permet d'effacer les dessins d'une zone en réaffichant le fond de la fenêtre[7]. Elle ne prend pas de contexte graphique en argument mais le booléen exposures_bool permet d'indiquer la sélection des événements GraphicsExpose et NoExpose qui résultent de la requête.

 

XClearArea (dpy, win, x, y, width, height, exposures_bool)

Bool exposures_bool;

 

XClearWindow (dpy,win)

                  La fonction XClearWindow permet de réafficher entièrement le fond de la fenêtre.

 

 

6.5. Quand faut-il dessiner ou redessiner ?

                  Un événement de type Expose est envoyé à l’application quand une fenêtre (ou une partie) précédemment invisible devient visible (sous réserve, bien entendu, que la fenêtre concernée ait bien sélectionné ce type d'événement). C'est l'indication pour l'application qu'elle a perdu le contenu de la fenêtre, et elle doit normalement le redessiner.

                  La librairie Xlib n’est pas une librairie d’objets graphiques. Elle permet de dessiner dans les fenêtres quand celles-ci se trouvent à l'écran, mais les dessins qui sont produits ne sont mémorisés nulle part. Les primitives graphiques modifient directement l’écran. Tout se passe donc comme sur un tableau noir : on dessine dans une fenêtre, mais si une autre fenêtre vient à recouvrir la première, le dessin précédent se trouve effacé par l'affichage de la nouvelle fenêtre. Quand l’ancienne fenêtre est remise en avant, le serveur lui envoie un événement de type Expose mais c'est à l’application d’en redessiner le contenu. Le serveur est chargé d’informer les applications de la mise en avant des fenêtres (via les événements de type Expose) mais le contenu des fenêtres est en général à la charge des applications (seul le fond est repeint par le serveur).

 

                  En réalité, quand le serveur le permet, on peut mémoriser le contenu des fenêtres (cf. l’attribut backing_store des fenêtres). Cependant, les applications devant être prêtes à tourner sur n’importe quelle machine, les programmes doivent être préparés à réafficher le contenu des fenêtres sur réception des événements de type Expose.

 

                  Selon les programmes, ces informations pourront être stockées dans différents objets caractérisant le dessin. Dans d'autres cas, on pourra procéder à la sauvegarde systématique de tous les dessins effectués dans la fenêtre (ces dessins étant alors tout simplement effectués deux fois, une fois dans la fenêtre et une seconde fois dans un Pixmap ou une Image permettant de posséder un "double" de la fenêtre).

 

Quoi qu'il en soit, l'application effectuera le réaffichage des dessins — ainsi que les premiers dessins eux-mêmes — sur réception des événements de type Expose. En effet, si la requête de dessin est effectuée avant que la fenêtre soit effectivement visible à l’écran, le dessin n’est tout simplement pas reporté sur le fond de la fenêtre.

 

 

 

Optimiser le réaffichage

 

                  Les événements de type Expose sont sélectionnés avec le masque ExposureMask et ne concernent que les fenêtres InputOutput car ce sont les seules fenêtres dans lesquelles on peut dessiner. La structure associée à un événement de type Expose contient la zone rectangulaire mise en avant. On peut donc optimiser l'affichage en affectant la zone de clipping du contexte graphique à ce rectangle juste avant de procéder au réaffichage des dessins.

 

                   Le champ count d'un événement de type Expose indique en outre son rang dans la série des événements provenant de la même mise en avant. En effet, un même mouvement de fenêtre peut provoquer la mise en avant de plusieurs zones disjointes de la fenêtre. Il en résulte l’envoi de plusieurs événements de type Expose, comme l’illustre l'exemple de la figure 6.18. ci-contre. Ces événements sont numérotés de manière décroissante, le dernier événement envoyé ayant un champ count à zéro.

 

                  Pour optimiser les échanges avec le serveur, on peut affecter la zone de clipping du contexte graphique à toute la zone exposée lors de la mise en avant. Pour cela,  au fur et à mesure que l'on reçoit des événements de type Expose et tant que le champ count de l’événement n’est pas nul, on accumule les petits rectangles exposés dans un tableau de XRectangle. Une fois le champ count à zéro, on réaffiche le contenu de la fenêtre après avoir modifié la zone de clipping du contexte graphique par l’union des rectangles accumulés. On remet ensuite à jour la zone de clipping du contexte graphique[8] (à moins qu'on ne procède toujours de cette manière pour dessiner).

La librairie a prévu spécialement une fonction permettant de réaliser l'optimisation proposée dans le paragraphe précédent. Il s’agit de la fonction XSetClipRectangles. Cette fonction modifie la zone de clipping (attributs clip_mask, clip_x_origin et clip_y_origin du contexte graphique) à partir d'un tableau de XRectangles :

 

fig. 6.18. Trois zones pouvant être exposées simultanément

par la mise en avant de la fenêtre située en arrière-plan

 

 

XSetClipRectangles ;(dpy, gc, clip_x_origin, clip_y_origin, rectangles,

                                                               nb_r, ordering)

GC                     gc ;

int                      clip_x_origin, clip_y_origin ;

XRectangle    rectangles [] ;

int                      nb_r,  ordering ;

 

 

                   Avec XSetClipRectangles, le champ clip_mask du contexte graphique gc est affecté par la réunion des rectangles du tableau rectangles. L’argument ordering indique l’ordre dans lesquels sont fournis les nb_r rectangles du tableau de rectangles.  L’ordre dans lequel les rectangles exposés sont envoyés par le serveur dépend des implantations[9] mais peut être connu.  (Le serveur d’un Sun4 effectue un découpage par bandes et les envoie selon l’ordre YXBanded illustré figure 6.19. page suivante). Les valeurs possibles sont Unsorted, YSorted, YXSorted, or YXBanded. En l'absence d'information, on utilisera la constante Unsorted.

 

 

fig. 6.19. Quatre zones exposées envoyées par le serveur dans l’ordre YXBanded

 

 

                  Les événements de type GraphicsExpose pourront être traités de manière analogue, car ils sont envoyés pour indiquer les zones qui doivent être réexposées à une requête de copie (une fois que l'utilisateur aura été prévenu et aura modifié la disposition des fenêtres pour rendre la copie possible).

 

 

6.6. Les régions

                   Les régions présentent l’intérêt de définir des zones non connexes et permettent d’implanter des objets graphiques. Les fonctions sur les régions sont optimisées et on utilisera volontiers les régions pour définir des zones sensibles non rectangulaires ou non connexes, comme des réunions de fenêtres disjointes.

 

                  Il existe parmi les fonctions sur les régions, des opérateurs ensemblistes intéressants (réunion, intersection, etc.), et de nombreux opérateurs booléens permettant de déterminer si un point ou un rectangle se trouve dans une région.

 

 

                  Les fonctions disponibles sur les Region sont les suivantes :

 

Region XCreateRegion ()

Region XPolygonRegion (points, npoints, fill_rule)

XPoint              points [] ;

int                      npoints, fill_rule ;

XDestroyRegion (region)

Region             region ;

 

XSetRegion (dpy, gc, region)

Region             region ;

GC                     gc ;

 

XOffsetRegion (region,dx,dy)

XShrinkRegion (region, dx, dy)

Region             region ;

int                      dx, dy ;

 

XClipBox (region, &rect_return)

Region             region ;

XRectangle    rect_return ;

 

XIntersectRegion (region1,region2, region_dest)

XUnionRegion (region1,region2, region_dest)

XXorRegion (region1,region2, region_dest)

XSubstractRegion (region1,region2, region_dest)

XUnionRectWithRegion (&rect, region_src, region_dest)

XRectangle    rect ;

Region             region1, region2,

                            region_dest, region_src ;

 

Bool XEmptyRegion (region)

Bool XEqualRegion (region1, region2)

Bool XPointInRegion (region, x, y)

int XRectInRegion (region, x, y, width, height)

Region             region, region1, region2 ;

int                      x, y, width, height ;

 

 

                  On notera en particulier XSetRegion qui positionne l’attribut clip_mask d’un contexte graphique à la zone définie par une région.

 

 

Les régions peuvent être déplacées par rapport à une origine avec la requête XOffsetRegion. La zone de clipping définie ensuite par un appel à XSetRegion se trouve alors doublement décalée, d’une part à cause du décalage de la région, et d'autre part, à cause des attributs clip_x_origin et clip_y_origin du contexte graphique. Ces deux décalages s’ajoutent pour définir la position finale de la zone.

 

 

6.7. Les images

                  On ne peut clore le chapitre sur les dessins sans indiquer l'existence d'autres structures que les Pixmap pour stocker des dessins : les images (type XImage). Les images permettent de transférer des informations graphiques entre un client et le serveur.  L'intérêt des images est que ce sont des structures locales au client (par opposition aux Pixmap qui nécessitent pour leurs manipulations des communications avec le serveur). Le traitement d'images étant exécuté localement, il sera plus rapide. Les fonctions de base sur les images sont XGetImage et XPutImage. XGetImage crée une image à partir du contenu d'un Drawable et XPutImage permet de transférer le contenu d'une image dans une fenêtre ou un Pixmap.

 

                  Malheureusement, il n’y pas de format unique sur les images[10] et c’est pourquoi les fonctions sur les images sont en général très lentes. Mais ce n'est pas toujours le cas car certains constructeurs ont implanté des fonctions rapides correspondant aux formats les plus fréquemment utilisés sur leurs machines[11].

 

                  La structure XImage est la structure utilisée pour représenter l'image dans la mémoire du client. Cette structure (cf. figure 6.20) contient toutes les données nécessaires à la compréhension du format des données. Certaines caractéristiques peuvent être modifiées par l'utilisateur (height, width, xoffset). D'autres paramètres (byte_order, bitmap_unit, etc.) sont caractéristiques à la fois de l'image et du serveur. Si ces paramètres diffèrent entre ceux de l'image et du serveur, XPutImage effectuera les conversions appropriées. Le champ de fonction f a été prévu pour permettre d'optimiser les procédures de manipulation d'images quand les caractéristiques sont connues.

 

typedef struct _XImage {

   int   width, height;                                                      /* taille de l’image */

          int   xoffset;   /* nombre de pixels de décalage dans la direction x */

   int   format;                                        /* XYBitmap, XYPixmap, ZPixmap */

   char *data;                 /* pointeur sur les données image */

   int   byte_order;                        /* ordre des octets:LSBFirst,MSBFirst */

   int   bitmap_unit;                          /* quant. de bits parcourus 8, 16, 32 */

   int   bitmap_bit_order;                                     /* LSBFirst, MSBFirst */

   int   bitmap_pad;                            /* 8, 16, 32 ou bien XY ou ZPixmap */

   int   depth;                                                                  /* profondeur de l’image */

   int   bytes_per_line;                                           /* nb d’octets par ligne */

   int   bits_per_pixel;            /* bits par pixel (ZPixmap) */

   unsigned long red_mask;           /* bits en arrangement selon  l’axe z*/

   unsigned long green_mask;

   unsigned long blue_mask;

   char *obdata;

   struct funcs {          /* procédures de manipulation d’image */

         struct _XImage *(*create_image)();

         int (*destroy_image)();

         unsigned long (*get_pixel)();

         int (*put_pixel)();

         struct _XImage *(*sub_image)();

         int (*add_pixel)();

         } f;

} XImage;

 

fig. 6.20. La structure XImage

 

 

                  On notera l'existence des fonctions suivantes pour créer et manipuler les images :

 

XImage* XCreateImage (dpy, visual, depth, format, offset, data, width, height,

                                                               bitmap_pad, bytes_per_line)

;Visual*            visual ;

unsigned int   depth ;

int                      format, offset ;

char*                 data ;

unsigned int   width, height ;

int                      bitmap_pad, bytes_per_line ;

XImage* XGetImage (dpy, draw, x, y, width, height, plane_mask, format)

XPutImage ;(dpy, draw, gc, image, sx, sy, dx, dy, width, height)

XImage*         image ;

Ximage* XSubImage (image, x, y, width, height)

XImage* XGetSubImage (dpy, draw, x, y, width, height, plane_mask, format,

                                                               dest_image, dest_x, dest_y)

unsigned long XGetPixel (image, x, y)

unsigned long XPutPixel (image, x, y, pixel)

XAddPixel (image, value)

XDestroyImage (image)

 

                  On utilisera bien entendu les images pour faire du traitement d’images, mais aussi pour tester des pixels.  On notera que la fonction  XGetPixel permet de récupérer la valeur des pixels point par point ­— ce qui n’est pas possible avec les fonctions opérant sur des Pixmap. Ainsi, dans un logiciel de dessin on pourra utiliser une structure XImage pour représenter le voisinage d'un point désigné par l’utilisateur (par exemple, pour sélectionner un objet) uniquement dans le but de tester les valeurs des pixels dans ce voisinage[12].

 

 

6.8. Les curseurs

.h;                  On appelle curseur la forme qui représente la position de la souris à l'écran. Pour créer un curseur, on utilise le plus souvent la fonction XCreateFontCursor qui permet de charger un curseur à partir d'un nom de forme  (correspondant en réalité à un caractère d'une fonte spéciale appelée fonte cursor).

 

Cursor XCreateFontCursor (dpy, forme)

Display*          dpy ;

unsigned int   forme ;

 

                  Les noms de formes utilisables se trouvent définis dans le fichier <X11/cursorfont.h> dont un extrait est présenté figure 6.21. .h;Il faudra bien entendu inclure ce fichier dans le programme avant d'utiliser ces noms. Pour choisir un curseur, on pourra visualiser la fonte cursor à l'écran à l’aide de la commande shell xfd (pour X font displayer).

                  Un curseur est en réalité défini par la donnée d'un motif rectangulaire bicolore appelé Pixmap source, d'un Pixmap plan de même dimension servant de masque, pour indiquer quels points doivent être pris en considération dans la source, et d'un point de référence, appelé en anglais hot spot, servant à indiquer la position de la souris (cf. figure 6.22 page suivante).

 

                  L'intérêt du masque est de permettre un découpage plus fin du dessin. On peut ainsi avoir des curseurs non rectangulaires. Le masque sera en général un peu plus grand que le motif central. De cette manière, le motif est augmenté d'un liseré qui lui permet de se détacher sur n'importe quel fond.

 

                      #define XC_num_glyphs                                                     154

                      #define XC_X_cursor                                                                0

                      #define XC_arrow                                                                       2

                      #define XC_based_arrow_down                                            4

                      #define XC_based_arrow_up                                                  6

                      #define XC_boat                                                                          8

                      #define XC_bogosity                                                               10

                      #define XC_bottom_left_corner                                          12

                      #define XC_bottom_right_corner                                       14

                      #define XC_bottom_side                                                        16

                      #define XC_bottom_tee                                                          18

                      #define XC_box_spiral                                                           20

                      #define XC_center_ptr                                                            22

                      #define XC_circle                                                                     24

                      #define XC_clock                                                                     26

 

fig. 6.21. Un extrait du fichier cursorfont.h

                  On peut soi-même définir un curseur à partir de deux Pixmap, l'un servant de dessin et l'autre de masque (bitmap), avec à la fonction

 

Cursor XCreatePixmapCursor (dpy, source, mask, &fg, &bg, x_hot, y_hot)

Pixmap            source, mask ;

XColor             fg, bg ;

unsigned int   x_hot, y_hot ;  

 

                  Les couleurs d'un curseur pourront être modifiées grâce à la requête

 

XRecolorCursor (dpy, cursor, &fg, &bg)

 

Les arguments fg et bg utilisés par XRecolorCursor et XCreatePixmapCursor ne sont pas des pixels, mais des pointeurs sur des structures XColor qui devront être correctement initialisées (cf. chapitre 9 sur la couleur). Cette initialisation peut se faire par exemple avec la fonction XQueryColor[13] (pour traduire les pixels en niveaux d'intensité) ou avec une fonction commençant par XAllocColor.

 

 

fig. 6.22. La définition des curseurs

 

 

                  La souris se déplaçant fréquemment à l'écran, le serveur gère lui-même le réaffichage des dessins situés sous le curseur. La taille du curseur influe sur la vitesse du réaffichage et la taille optimale varie selon les machines. On pourra obtenir cette information à l'aide de la fonction XQueryBestSize ou XQueryBestCursor.

 

Status XQueryBestCursor (dpy, draw, width, height, &width_return,

                                                                                 &height_return)

unsigned int   width, height, width_return, height_return ;

 

                  Signalons aussi XCreateGlyphCursor qui est similaire à XCreatePixmapCursor à la différence près que les Pixmap source et masque sont spécifiés par les index de caractères dans des fontes. XCreateGlyphCursor permet ainsi d'utiliser un caractère comme curseur.

 

                  Pour associer un curseur à une fenêtre, on utilisera la fonction XDefineCursor. La fonction XUndefinedCursor permet de revenir au curseur précédent[14] (celui de la fenêtre parent).

 

XDefineCursor (dpy, win, cursor)

XUndefineCursor (dpy, win)

Cursor              cursor ;

 

                  Pour libérer les ressources utilisées par un curseur, on appellera la fonction

XFreeCursor (dpy,cursor).

 


 

 

       Les fonctions importantes

 

   GC XCreateGC (display, drawable, valuemask, &xgc_values)

          GC DefaultGC (dpy,screen)

 

          XSetState (dpy, gc, fg, bg, function, plane_mask)

          XSetLineAttributes (dpy, gc, line_width, line_style, cap_style,

                                                                                 join_style)

          XSetForeground (dpy, gc, fg)

          XSetBackground (dpy, gc, bg)

          XSetFunction (dpy, gc, function)

          XSetPlaneMask (dpy, gc, planemask)

          XSetLineAttributes (dpy, gc, lin_width, line_style, cap_style,

                                                                                 join_style)

          XSetDashes (dpy,gc,dash_offset, dash_list, n)

          XSetFillStyle (dpy, gc, fill_style)

          XSetFillRule (dpy, gc, fill_rule)

          XSetTile ( dpy, gc, tile)

          XSetStipple (dpy, gc, stipple)

          XSetTSOrigin (dpy, gc, ts_x, ts_y)

          XSetFont  (dpy, gc, font)

          XSetClipOrigin (dpy, gc, x, y)

          XSetClipMask (dpy, gc, pixmap)

          XSetArcMode (dpy, gc, arc_mode)

          XSetSubwindowMode (dpy, gc, subwindow_mode)

 

    XCopyGC (dpy, gc_source, valuemask, gc_dest)

          XChangeGC (dpy, gc, valuemask, &xgc_values)

 

          XSetClipRectangles (dpy, gc, cx, xy, rectangles, nrect, ordre)

 

          XDrawPoint (dpy, draw, gc, x, y)

          XDrawPoints (dpy, draw, gc, points, npoints, mode)

          XDrawLine (dpy, draw, gc, x1, y1, x2, y2)

          XDrawLines (dpy, draw, gc, points, npoints, mode)

          XDrawSegments (dpy, draw, gc, segments, nsegments)

          XDrawRectangle (dpy, draw, gc, x, y, width, height)

 

          XDrawRectangles  (dpy, draw, gc, rectangles, nrects)

          XDrawArc (dpy, draw, gc, x, y, width, height, angle1, angle2)

          XDrawArcs  (dpy, draw, gc, x, y, arcs, narcs)

   XDrawArc (dpy,win,gc,x,y,width,height, 90x64, -(45x64))

 

          XFillRectangle (dpy, draw, gc, x, y, width, height)

          XFillRectangles (dpy, draw, gc, rectangles, nrects)

          XFillArc (dpy, draw, gc, x, y, width, height, angle1, angle2)

          XFillArcs  (dpy, draw, gc, x, y, arcs, narcs)

          XFillPolygon (dpy, draw, gc, points, npoints, shape, mode)

 

 

   XCopyArea (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)

          XCopyPlane (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y,

                                           plane)

   XClearWindow (dpy,win)

   XClearArea (dpy, win, x, y, width, height, exposures_bool)

 

          Cursor XCreateFontCursor (dpy, forme)

          XDefineCursor (dpy, win, cursor)

 

          Toute primitive graphique utilise un contexte graphique permettant de spécifier les paramètres gouvernant le report du dessin sur la destination. Il est important de contrôler les attributs foreground, function et plane_mask du contexte graphique, car ils interviennent dans le calcul des couleurs et du dessin.

 

          Les dessins sont effectués dans les fenêtres sur réception des événements de type Expose. Il est donc primordial de sélectionner ce type d'événement si l'on souhaite dessiner dans des fenêtres.

 

 

Exercices sur les contextes graphiques et les dessins

Les corrigés de ces exercices sont regroupés pages 297 et suivantes.

 

Exercice 10 :  (événements, contexte graphique)

Faire une copie de tout l'écran dans une structure Pixmap. En afficher une partie dans une fenêtre et en faire défiler le contenu sur déplacement de la souris avec le bouton 2 enfoncé.

 

Exercice 11 :  (contexte graphique)

Petite farce :  copier toute la racine de l'écran d'un collègue dans une fenêtre (de même taille que la racine) et l'afficher ensuite sur son écran. Prévoir tout de même un cas de sortie. On peut aussi adapter l'exercice précédent de sorte que toute la racine de l'écran se déplace quand on tire la souris en maintenant le bouton 2 enfoncé.

 

Exercice 12 :  (Etude des événements de type Expose)

Créer une fenêtre comportant un certain nombre de sous-fenêtres. On sélectionnera sur toutes les fenêtres les événements de type Expose et on imprimera sur réception d'un événement, les composantes de la zone exposée et le champ count de l'événement. (Pour créer des événements de type Expose, créer des recouvrements suivis de mises en avant-plan à l'aide du window manager.)

 

Exercice 13 : (Etude des événements de type GraphicsExpose)

Créer deux fenêtres dont l'une suffisamment grande. Quand on clique dans une fenêtre, le contenu de la racine située en (0,0) de largeur 200 et hauteur 200 est copié en (150, 200) dans la grande fenêtre avec la fonction XCopyArea. Tester ainsi les événements de type GraphicsExpose envoyés selon la configuration des deux fenêtres (disponibilité de la source, de la destination, des deux).

 

Exercice 14 : (Un rectangle qui suit la souris)

Dans une fenêtre, quand on clique avec les boutons 1 ou 2 et que l'on maintient le bouton enfoncé, on trace le rectangle dont le premier point correspond au point où le bouton a été enfoncé et dont le sommet opposé suit la souris. Quand on relâche, le rectangle reste affiché jusqu'au prochain enfoncement de bouton. On quitte sur enfoncement du bouton 3.

 

 

 



[1] Attention, GXxor prend deux x et est différent de GXor. Pour l'utilisation de GXxor et GXinvert, voir la section 9.4.  dans le chapitre sur la couleur.

[2] Egalement utilisé comme argument des fonctions XFillPolygon et XPolygonRegion.

[3] On a envie dans ce cas de parler d'échec total puisque le transfert du dessin n'a pas eu lieu ; cependant, du point de vue du serveur, la copie s'est de toute façon nécessairement bien passée, car le résultat est le même que si la source était entièrement disponible. Le fait que le dessin n'ait pas atteint la destination n'est en réalité jamais pris en compte par le serveur.

[4] Cependant, elle ne consulte pas les attributs de jointure de lignes.

[5] Il s'agit des requêtes XDrawArcs, XDrawLines, XDrawPoints, XDrawRectangles, XDrawSegments, XDrawText, XDrawText16, XFillArcs et XFillRectangles. D'après [NYE 90], pour savoir quel est le nombre maximum de requêtes graphiques que le serveur peut appréhender simultanément, on peut utiliser la requête XMaxRequestSize et appliquer la recette suivante : retrancher d'abord trois pour avoir le nombre de points dessinables avec XDrawPoints, diviser ensuite par deux pour obtenir le nombre d'objets dessinables avec XDrawRectangles, et diviser par  trois pour les arcs. Pour les requêtes XDrawText et XDrawText16, ce nombre dépendra de la taille des chaînes de caractères utilisées.

[6]  La zone source était entièrement disponible ou la destination n'est pas du tout disponible (indépendamment de l'état de la source), ce qui revient au même du point de vue du serveur. En fait un événement de type NoExpose ne fait qu'indiquer qu'il n'y a pas d'événement de type GraphicsExpose.

[7] XClearArea appelée avec (dpy, win, 0, 0, 0, 0, False) a le même effet que XClearWindow (dpy, win) qui réaffiche simplement le fond dans toute la partie visible de la fenêtre.

[8] On utilisera pour cela la fonction XSetClipMask en lui passant la constante None.

[9] On pourra observer l’ordre dans lequel les événements sont envoyés par le serveur en imprimant les composantes et le champ count de chaque événement Expose reçu (cf. exercice n° 3).

[10] On notera, pour les mêmes raisons, l’absence de fonctions permettant de sauvegarder des images dans des fichiers, ou de les lire sur disque. Il faudra passer ici par l’intermédiaire des Pixmap et de bitmap pour les images bicolores, et écrire ses propres fonctions (ou utiliser les différents convertisseurs du marché) pour faire des sauvegardes sur disque.

[11] Si votre machine n'est pas suffisamment rapide, il vaut mieux réécrire les fonctions d'accès et utiliser directement les données si le format est connu, car les fonctions standards, prévues pour un format quelconque, ne sont pas très performantes.

[12] On crée une petite image représentant le voisinage du point. On peut alors repeindre dans le voisinage tous les objets présents à l'écran en testant après chaque requête de dessin les pixels de ce voisinage. S’il en est d’affectés, c’est que le voisinage a rencontré l’objet.

[13] Même pour créer des curseurs noir et blanc, on doit utiliser XQueryColor pour initialiser les XColor à des structures codant le noir et le blanc (cf. section 9.4.).

[14] Et non pas, comme on pourrait le croire à partir du nom, d'éliminer le curseur. Pour définir un curseur transparent, il faut créer un curseur (avec XCreatePixmapCursor) à partir d'un masque entièrement nul. L'idée n'est pas absurde car c'est la méthode utilisée pour définir un curseur comme intersection de deux droites (la grande croix utilisée dans certains logiciels de dessin). On définit d'abord un curseur transparent et on affiche en permanence deux droites pour marquer par leur intersection l'endroit où se trouve la souris.