Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Programmation graphique système - 2e partie : allouer une palette de couleurs
(Article écrit par l'équipe de GuruMed et extrait de GuruMed.net - juillet 2002)
|
|
Nous avons vu dans le premier article
de cette série comment ouvrir une fenêtre. Vous avez peut-être essayé d'utiliser quelques fonctions de la
graphics.library pour écrire dedans... et remarqué que SetAPen() ne permet que de choisir un "pinceau" (pen)
parmi la palette actuelle du Workbench. C'est très limitatif, puisqu'on ne maîtrise absolument pas cette
palette ! Au mieux, on peut tabler sur du gris dans le pinceau 0, du noir en 1 et du blanc en 2...
Heureusement, AmigaOS 3.0 a apporté des fonctions supplémentaires pour modifier proprement la palette de
tout écran public tel que le Workbench.
Allouer un pinceau de couleur
La fonction ObtainBestPen() de la graphics.library permet d'obtenir un pinceau, c'est-à-dire un emplacement
dans la palette, de la couleur que l'on désire.
D'après les AutoDocs, ObtainBestPen() s'utilise de cette façon :
color = ObtainBestPen(cm,r,g,b,tags....);
|
Avec :
- color : numéro de pinceau (entre 0 et 255) ou -1 si échec.
- cm : pointeur sur une structure ColorMap.
- r : quantité de rouge (fraction 32 bits justifiée à gauche).
- g : quantité de vert (fraction 32 bits justifiée à gauche).
- b : quantité de bleu (fraction 32 bits justifiée à gauche).
- tags : balises spécifiant les réglages de la sélection du pinceau.
Le principe d'ObtainBestPen() est le suivant : si un pinceau de la palette actuelle contient déjà la
couleur demandée (couleur plus ou moins approximative selon les réglages passés dans les balises),
alors ce pinceau sera fourni en valeur de retour. Sinon, s'il reste de la place dans la palette, un
nouveau pinceau de la couleur demandée sera créé. S'il ne reste pas de place, le pinceau de la couleur
la plus approchante sera choisi.
Procédons par ordre. Il nous faut d'abord une structure ColorMap. Par chance, toute structure ViewPort
contient justement un pointeur vers une ColorMap. Par miracle, tout écran Intuition contient une
structure ViewPort ! Et comme on a vraiment beaucoup de veine, chaque fenêtre est également associée à
un écran Intuition :-). Nous avons donc tout ce qu'il faut sous la main.
Dans intuition/intuition.h, struct Window, on peut lire :
struct Screen *WScreen; /* this Window's Screen */
|
Dans intuition/screens.h, struct Screen :
struct ViewPort ViewPort; /* describing the Screen's display */
|
Et enfin, dans graphics/view.h, struct ViewPort :
struct ColorMap *ColorMap; /* table of colors for this viewport */
|
Ceci nous permet d'écrire les lignes suivantes :
struct ColorMap *cm;
cm = win->WScreen->ViewPort.ColorMap;
|
Ensuite, les valeurs de rouge, vert et bleu. Elles sont encodées chacune sur 32 bits, ce qui nous laisse
une certaine marge puisque actuellement aucune carte graphique (grand public) ne gère plus de 8 bits par
composante à ma connaissance.
Si vous voulez obtenir une couleur quelconque R,G,B (avec R, G et B compris chacun entre 0 et 255 inclus),
voici comment calculer les valeurs de r, g et b à passer à ObtainBestPen() :
ULONG r,g,b;
r = ( R<<24 ) | ( R<<16 ) | ( R<<8 ) | R;
g = ( G<<24 ) | ( G<<16 ) | ( G<<8 ) | G;
b = ( B<<24 ) | ( B<<16 ) | ( B<<8 ) | B;
|
En dernier viennent les balises :
- OBP_Precision détermine la précision souhaitée de la correspondance entre la couleur demandée
et la couleur obtenue. Les valeurs possibles sont PRECISION_GUI, PRECISION_ICON, PRECISION_IMAGE et PRECISION_EXACT.
PRECISION_GUI sert pour les dessins dont la couleur exacte n'a que peu d'importance, tandis que PRECISION_EXACT
permet d'obtenir la couleur la plus proche que possible de la couleur demandée. La valeur par défaut est
PRECISION_IMAGE.
- OBP_FailIfBad, s'il est positionné sur "TRUE", demande à ce qu'ObtainBestPen() échoue s'il est
impossible d'allouer une couleur dans la précision souhaitée, au lieu de retourner la couleur la plus proche.
Vous n'en aurez probablement pas besoin.
Ces balises sont donc optionnelles, les valeurs par défaut convenant à la plupart des usages.
Tout est maintenant prêt pour tracer une jolie ligne rouge dans notre fenêtre. Reprenons le
source du premier
article en y intégrant le code nécessaire :
#include <intuition/intuition.h>
#include <proto/exec.h>
#include <proto/graphics.h> // pour ObtainBestPen(), ReleasePen(),
// SetAPen(), Move() et Draw()
#include <proto/intuition.h>
#include <stdio.h>
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
int main(void)
{
struct Window *win=NULL;
LONG redPen;
struct ColorMap *cm;
ULONG r,g,b;
IntuitionBase = (struct IntuitionBase *) OpenLibrary( "intuition.library", 36 );
if( !IntuitionBase ) {
puts( "Impossible d'ouvrir intuition.library v36." );
return 20;
}
GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library", 39 );
if( !GfxBase ) {
puts( "Impossible d'ouvrir graphics.library v39." );
CloseLibrary( (struct Library *) IntuitionBase );
return 20;
}
win = OpenWindowTags( NULL,
WA_Title, (ULONG)"Pouet",
WA_InnerWidth, 320,
WA_InnerHeight, 240,
WA_DragBar, TRUE,
WA_CloseGadget, TRUE,
WA_DepthGadget, TRUE,
WA_IDCMP, IDCMP_CLOSEWINDOW,
TAG_END );
if( !win ) {
puts( "Impossible d'ouvrir la fenêtre." );
CloseLibrary( (struct Library *) IntuitionBase );
CloseLibrary( (struct Library *) GfxBase );
return 10;
}
cm = win->WScreen->ViewPort.ColorMap;
r = 255<<24 | 255<<16 | 255<<8 | 255;
g = 0<<24 | 0<<16 | 0<<8 | 0;
b = 0<<24 | 0<<16 | 0<<8 | 0;
redPen = ObtainBestPen( cm, r, g, b, TAG_END );
if( redPen == -1 ) {
puts( "Impossible d'allouer un pinceau !" );
CloseWindow( win );
CloseLibrary( (struct Library *) IntuitionBase );
CloseLibrary( (struct Library *) GfxBase );
return 10;
}
SetAPen( win->RPort, redPen );
Move( win->RPort, 0, 239 );
Draw( win->RPort, 319, 0 );
WaitPort( win->UserPort );
ReleasePen( cm, redPen );
CloseWindow( win );
CloseLibrary( (struct Library *) IntuitionBase );
CloseLibrary( (struct Library *) GfxBase );
return 0;
}
|
Je ne vais pas rentrer dans les détails en ce qui concerne l'ouverture et la fermeture de la graphics.library,
ainsi que la libération des ressources en cas d'échec, puisque le principe est exactement le même que dans
le source précédent.
Ceci nous amène directement à la ligne :
redPen = ObtainBestPen( cm, r, g, b, TAG_END );
|
Rien de spécial puisque tout a déjà été expliqué plus haut. La seule chose à noter est qu'aucune balise
n'a été spécifiée : seule TAG_END a donc été écrite à l'emplacement de la liste des balises.
Ensuite, on teste si l'allocation a réussi. Normalement, il devrait être impossible qu'elle échoue sur
l'écran du Workbench, mais on ne sait jamais.
SetAPen( win->RPort, redPen );
|
Allouer un pinceau, c'est bien, mais il faut encore le sélectionner pour tracer avec ! C'est à ça que
sert SetAPen(). On lui donne le RastPort de la fenêtre en premier argument, et le pinceau obtenu avec
ObtainBestPen() en second. Par la suite, toutes les opérations de tracé (basiques) utiliseront cette couleur.
Move( win->RPort, 0, 239 );
|
Move() déplace le "curseur" de tracé vers la position spécifiée en 2e et 3e arguments. Les opérations
de tracé suivantes commenceront donc en (0,239) (sachant que (0,0) est l'angle supérieur gauche de la fenêtre).
Draw( win->RPort, 319, 0 );
|
Draw() trace une ligne allant de la position du curseur jusqu'à celle passée en arguments. Ici, on trace
donc une ligne rouge allant de (0,239) à (319,0).
ReleasePen( cm, redPen );
|
Une fois qu'on a fini d'utiliser le pinceau, il faut le libérer. C'est ce à quoi sert ReleasePen().
Voilà. Si vous essayez ce programme, vous vous apercevrez que le segment est tracé en partie sur la bordure
de la fenêtre, et non pas bien comme il faut dans la zone à l'intérieur de la fenêtre. La raison est tout
simplement que les coordonnées que l'on donne à Move(), Draw(), etc. sont relatives au bord supérieur gauche
de la fenêtre, bordures incluses !
Il existe deux solutions pour remédier à cela :
1. Rajouter la taille des bordures de la fenêtre à toutes les coordonnées
La structure Window renferme quatre champs contenant la taille des bordures :
BYTE BorderLeft, BorderTop, BorderRight, BorderBottom;
|
Il suffit donc de rajouter "BorderLeft" à toutes les coordonnées horizontales et "BorderTop" à toutes
les coordonnées verticales :
Move( win->RPort, win->BorderLeft+0, win->BorderTop+239 );
Draw( win->RPort, win->BorderLeft+319, win->BorderTop+0 );
|
2. Utiliser la balise WA_GimmeZeroZero à l'ouverture de la fenêtre
Utiliser cette option signifie que l'on souhaite que les coordonnées soient relatives à l'intérieur de
la fenêtre. De plus, les opérations de tracé seront coupées sur
les bordures de la fenêtre ; il est donc impossible d'écrire sur la bordure d'une fenêtre GimmeZeroZero.
C'est en quelque sorte la solution de facilité, mais il semblerait que les performances soient moindres
lorsqu'on utilise des fenêtres de ce type. En tout cas, c'est ce qu'on m'a dit :-) (NDHenes : oui, utiliser
GimmeZeroZero, c'est mal, les députés prévoient une loi l'interdisant). De plus, cela consomme plus de
mémoire (comme expliqué dans le fichier d'inclusion intuition/intuition.h juste avant les champs BorderLeft,
BorderTop, etc.).
Vous connaissez maintenant l'essentiel pour dessiner en couleur dans une fenêtre du Workbench. La prochaine
fois, nous verrons le tracé de graphiques au format chunky.
|