Graphisme

Transcription

Graphisme
LE GRAPHISME
Ce (début de) cours a été réalisé de manière totalement indépendante du CEPTA, centre de formation
où d'ailleurs il n'y a pas de cours consacré graphisme proprement dit.
Jean-Claude Armici
__________________________________________________________________________________________
Le graphisme (JC Armici)
-1www.unvrai.com
1.
Introduction
Toutes les applications qui interagissent directement avec l'utilisateur font appel d'une
manière ou d'une autre à des techniques de programmation graphique. Qui développent
des applications destinées à être utilisées par d'autres personnes se doit donc de
connaître les bases de la programmation graphique.
Bien que les techniques qui seront expliquées dans ce cours font appel à des concepts
avancés, le néophyte en programmation graphique ne doit pas les ignorer. Delphi est un
environnement de programmation permettant de développer des applications touchant à
différents domaines. Par exemple, on trouve de plus en plus de jeux écrits en Delphi, soit
sous forme de shareware, soit sous une forme professionnelle.
Afin de familiariser le programmeur avec les concepts de base de la programmation
graphique, nous allons d'abord examiner les concepts de base tels que pixel, ligne,
polygone, bitmaps, ainsi que l'utilisation de l'objet canvas.
2.
Le GDI
Le système GDI (Graphical Device Interface) constitue la partie de Windows responsable
de tout le système graphique. Ce système est accessible au travers de toute une variété
de fonctions et de procédures. Que ce soit un bouton, une ligne, une barre de défilement,
une application Windows elle-même, ou encore du texte, tout est géré par des fonctions
et des procédures du GDI.
Remarque:
dans ce cours intervient souvent la notion de "device context" qui est
normalement traduit en français par "contexte de dispositif". Cette
traduction n'étant pas du plus bel effet, nous garderons l'appellation
anglaise.
Un des points remarquables de Windows est d'être "device indépendant", c'est-à-dire
d'être capable de s'adapter à une gamme très étendue de combinaisons matérielles
(carte graphiques, imprimantes, etc). Avec cet objectif d'indépendance vis-à-vis du
matériel en vue, le rôle principal du GDI est de fournir des mécanismes au travers
desquels une application peut créer un contenu graphique sans que le programmeur ne
soit concerné par le type et donc les caractéristiques du matériel utilisé.
Malheureusement une conséquence directe de cette indépendance est le manque relatif
de performances au niveau de la vitesse d'exécution. Ce ralentissement est surtout
visible lorsqu'il s'agit de programmer des jeux, pour lesquels une certaine fluidité est
généralement requise. C'est une des raisons qui justifie l'existence de systèmes tels que
DirectX.
Ce cours restera néanmoins axé sur le système GDI qui, dans la plupart des tâches
confiées à un programmeur "traditionnel", est tout à fait satisfaisant.
__________________________________________________________________________________________
Le graphisme (JC Armici)
-2www.unvrai.com
3.
Eléments graphiques
Avant de se lancer dans la programmation graphique il convient de définir un certain
nombre de termes et de concepts (la programmation graphique a aussi son jargon).
3.1
Pixel
Lorsque le GDI affiche une image à l'écran, il affiche, en fait, une série de points colorés.
Un tel point s'appelle un pixel: il s'agit du plus petit élément graphique pouvant être
affiché à l'écran. L'affichage d'une série de points constituant une image est tellement
rapide qu'il donne l'impression d'une opération instantanée.
Pixel: le plus petit élément constituant une image graphique ou une surface d'affichage
L'écran d'un ordinateur peut être vu comme une grille constituée de pixels. La taille de
cette grille et la dimension des pixels détermine la résolution du mode graphique utilisé.
Le nombre de couleurs avec lesquelles il est possible de colorer un pixel détermine la
profondeur de couleur (voir plus loin).
3.2
Ligne
La ligne est un élément graphique plus riche qu'un point. On la retrouve fréquemment
dans une interface graphique. Par exemple, on peut utiliser des lignes de différentes
couleurs pour simuler un effet de profondeur ou de relief.
Ligne: un ensemble de points arrangés de façon contiguë
La représentation des lignes sur une grille orthogonale pose le problème de
l'interpolation, dans le cas où les lignes ne sont ni horizontales ni verticales.
__________________________________________________________________________________________
Le graphisme (JC Armici)
-3www.unvrai.com
Sur la figure suivante on voit bien l'effet d'"escaliers" pour les lignes inclinées.
3.3
Polygones
Un polygone est formé d'un certain nombre de lignes, mises bout à bout. Il est ainsi
possible de dessiner des figures géométriques, régulières ou irrégulières.
Polygone: un ensemble de lignes, placées bout à bout de manière à former une figure
continue et fermée
3.4
Bitmaps
Le graphisme sur ordinateur serait un peu terne avec uniquement des lignes et des
figures géométriques. Notre cerveau a une extraordinaire capacité de reconnaissance de
formes. C'est probablement pour cette raison que les nouvelles interfaces graphiques
utilisent abondamment icônes et images de toutes sortes.
Bitmap: un ensemble de pixels arrangés dans une configuration rectangulaire. Il
constitue généralement une forme, une image ou une texture reconnaissable.
Par essence, les bitmaps utilisent une grande quantité de mémoire; raison pour laquelle
beaucoup de formats disposent d'algorithmes de compression.
__________________________________________________________________________________________
Le graphisme (JC Armici)
-4www.unvrai.com
3.5
Classification des éléments graphiques
Lorsque l'on parle d'affichage graphique, on distingue deux catégories:
•
•
Les graphiques en mode points (Raster Graphics)
Les graphiques vectoriels (vector graphics)
Graphiques en mode points
Il s'agit d'images formées de pixels statiquement arrangés de manière à former une
image ou un dessin. Les icônes ou les bitmaps en sont deux exemples.
Graphiques en mode points: graphiques composés de pixels formant une image ou un
dessin.
En général les graphiques en mode points sont très dépendants de la résolution et
supportent mal les redimensionnements. Les algorithmes de redimensionnement sont
d'ailleurs très complexes et les résultats sont de qualité variable.
Sujet de réflexion:
Imaginez une image en mode points sur laquelle est représentée une ligne
verticale d'un pixel de large. Si on veut rendre cette image deux fois plus petite,
comment faut-il traiter la ligne ? Sera-t-elle encore visible une fois l'image
réduite?
Le format le plus connu dans cette catégorie est le fameux BMP de Windows.
Graphiques vectoriels
Les graphiques vectoriels sont des images décrites comme une série de points connectés
entre eux dans une grille virtuelle en 2 ou 3 dimensions. Ce type de graphique inclut les
lignes, polygones, cercles, courbes, etc.
Graphiques vectoriels: graphiques composés par une série de points reliés dans un
système de coordonnées virtuel à 2 ou 3 dimensions.
Contrairement au graphiques en mode points, les graphiques vectoriels sont
indépendants de la résolution et peuvent être redimensionnés sans perte de qualité. Un
exemple de format graphique vectoriel est le format Metafile de Windows. C'est d'ailleurs
pour ces raisons que les cliparts sont le plus souvent fournis sous forme vectorielle. Le
contenu d'une image vectorielle n'est pas un ensemble de pixels, mais plutôt un
ensemble de directives (instructions) permettant de retracer ou redessiner l'image.
__________________________________________________________________________________________
Le graphisme (JC Armici)
-5www.unvrai.com
4.
Résolution et nombre de couleurs
Dans le monde du graphisme, on parle souvent du mode vidéo: résolution et nombre de
couleurs par pixels.
Mode vidéo: état dans lequel se trouve le matériel d'affichage, en termes de résolution
et nombre de couleurs affichables.
Les deux paramètres, résolution et nombre de couleurs, influent sur la qualité perçue
d'une image graphique. Plus ces deux paramètres sont élevés, plus l'image affichée est
réaliste et détaillée. Mais d'un autre côté, plus la quantité de mémoire utilisée est
grande, donc plus les opérations graphiques prennent du temps.
Souvent on opte pour le choix d'une résolution moyenne et un grand nombre de
couleurs, plutôt que pour une grande résolution et un nombre moyen de couleurs. C'est
le cas de la plupart des jeux, du moins jusqu'à l'apparition d'accélérateurs et contrôleurs
graphiques ultra-performants.
4.1
Résolution
La résolution d'un mode graphique donné spécifie combien de pixels peuvent être
affichés sur l'écran.
Résolution: nombre de points pouvant être affichés.
Par exemple une image de 50 x 50 pixels apparaîtra plus grande dans une résolution de
800 x 600 pixels que dans une résolution de 1024 x 768 pixels.
La résolution supportée par un écran dépend des caractéristiques de l'écran (en termes
de fréquences de balayage), mais aussi du contrôleur (carte) graphique installé dans
l'ordinateur.
Les résolutions de 320 x 200 d'il y a une quinzaine d'années ont laissé place à des
résolutions de 800 x 600 (minimum vital), 1024 x 768 (probablement la plus standard),
voire 1280 x 1024 (relativement courante) ou 1600 x 1200. Malheureusement le
développement d'applications grand public oblige à prendre en compte également les
basses résolutions, ce qui diminue la place disponible pour les éléments d'interface. On
peut bien entendu effectuer des adaptations dynamiques et automatiques en y mettant le
code, donc le temps, donc l'argent nécessaire.
4.2
Nombre de couleurs
On parle également de profondeur de couleurs ou encore de nombre de bits par pixel.
Plus il y a de bits disponibles pour représenter la couleur d'un pixel, plus le nombre de
couleurs à disposition est grand.
Nombre de couleurs: mesure du nombre de bits par pixels requis pour décrire la
couleur d'un pixel.
Le nombre de couleurs affecte bien plus que la résolution les techniques utilisées pour
afficher des éléments graphiques. Entre autres, le nombre de couleurs détermine si le
mode graphique sera palettisé ou non palettisé (voir plus loin). La tableau suivant
indique le nombre de couleurs disponibles en fonction du nombre de bits par pixels:
__________________________________________________________________________________________
Le graphisme (JC Armici)
-6www.unvrai.com
Bits par pixel
Nombre de couleurs disponibles
4
16 couleurs
8
256 couleurs
16
65'535 couleurs (high color)
24
16'777'216 couleurs (true color)
32
16'777'216 couleurs plus 256 niveaux de
transparence (true color)
Les deux premiers modes sont de moins en moins utilisés, au profit des modes 16 ou 24
bits par pixel.
4.3
Mémoire vidéo
Généralement les cartes graphiques supportent un grand nombre de modes vidéo, en
termes de combinaison de résolution et de nombre de couleurs. La quantité de mémoire
vidéo disponible constitue aussi un facteur limitatif. Par exemple un mode 800 x 600 en 8
bits par pixel requiert 480'000 octets. Un mode 1600 x 1200 en 32 bits par pixel
demande 7'680'000 octets.
4.4
Composantes de couleur
La couleur d'un pixel est exprimée de manière interne en trois composantes: rouge, vert
et bleu. L'intensité de chaque couleur de base peut varier de 0 à 255. Il s'agit de la
représentation RGB des couleurs. On dispose de 16'777'216 couleurs, ce qui est bien plus
que le nombre de nuances que l'oeil humain peut distinguer.
4.5
Modes palettisés
Les modes avec 4 et 8 bits par pixel sont les seuls modes palettisés. Nous nous
attarderons uniquement sur le mode à 8 bits par pixel. Chaque pixel peut avoir des
valeurs comprises entre 0 et 255. Chacune de ces valeurs doit être vue comme un index
dans un tableau de 256 couleurs, matérialisant la palette.
Chaque entrée (ou élément) du tableau contient 3 valeurs codées sur 8 bits (donc 3
octets) pour les 3 composantes R, G et B. Donc on dispose de 256 couleurs distinctes,
affichables simultanément, prises parmi 16'777'216 couleurs possibles.
0
0
122
0
0
0
0
0
0
R
0
15
G
0
0
1
15
...
120 145 112
121 127 65
122 233 45
B
0
32
2
48
54
__________________________________________________________________________________________
Le graphisme (JC Armici)
-7www.unvrai.com
Un premier inconvénient de ce mode d'affichage est que si plusieurs bitmaps sont
affichés, ils doivent utiliser la même palette. De plus, un bitmap créé avec une palette
donnée, mais affiché sur un dispositif ayant une palette différente aura une apparence
imprévisible. Souvent l'environnement graphique (par exemple Windows) doit se réserver
quelques entrées dans la palette pour ses propres éléments graphiques, ce qui réduit
d'autant le nombre de couleurs à disposition.
Il est possible de faire correspondre à un pixel d'un bitmap la couleur la plus proche de la
palette courante, mais cette procédure est généralement lente; du moins trop lente pour
un domaine comme les jeux. Toutefois, ce que l'on perd en limitation de palette, on le
gagne par un traitement très rapide des images, car des opérations portant sur des
registres à 32 bits permettent de traiter 4 pixels à la fois.
On peut également changer de palette en fonction des scènes ou des images à afficher.
Un avantage de ce mode graphique est la possibilité d'animer la palette, cycliquement ou
ponctuellement, ce qui permet de modifier l'aspect de l'image en changeant uniquement
les points d'entrée dans la palette.
L'utilisation des palettes n'est pas une opération facile. De plus, rien n'est directement
prévu dans Delphi concernant la gestion des palettes. Il faut donc faire appel aux
fonctions de l'API de Windows.
Enfin, signalons qu'en graphisme certains résultats peuvent dépendre du matériel (carte
vidéo, écran, etc ) et du logiciel (essentiellement les drivers graphiques).
4.6
Modes non palettisés
Les modes ayant 16, 24 ou 32 bits par pixel stockent directement en mémoire la valeur
de la couleur d'un pixel. Généralement, dans le mode avec 16 bits par pixel, 5 bits sont
attribués à chaque composante. Dans le mode avec 24 bits par pixel, 8 bits (1 octet) sont
attribués à chaque composante. Le mode avec 32 bits par pixel a aussi 8 bits par
composante, plus 8 bits décrivant la transparence.
Les modes non palettisés ont l'avantage de pouvoir afficher n'importe quel nombre
d'images bitmap simultanément, sans souci de gestion de palette. Mais, comme nous
l'avons déjà vu, ils demandent une plus grande quantité de mémoire.
5.
Techniques d'affichage graphique
Il existe des centaines de fonctions et procédures liées à l'affichage graphique. Dans un
environnement comme Delphi, il est possible de travailler à plus ou moins bas niveau. On
peut utiliser les primitives mises à disposition par le GDI ou bien faire appel aux routines
de Delphi.
5.1
Device context
Un device context représente une surface sur laquelle un graphique peut être affiché.
Cette surface peut se trouver sur un écran, une imprimante, un traceur de courbes, etc.
__________________________________________________________________________________________
Le graphisme (JC Armici)
-8www.unvrai.com
6.
Utilisation du graphisme avec Delphi
6.1
Introduction
Dans la suite de ce cours nous allons aborder les différents aspects de la programmation
graphique en Delphi au travers de nombreux exemples. Nous étudierons d'abord les
opérations simples faisant appel à des objets courants. Nous nous attaquerons ensuite à
des cas un peu plus complexes, tels que le traitement d'images. Delphi faisant partie de
la grande famille des outils RAD, rappelons que les comportements par défaut n'ont pas
besoin d'être définis et donnent des résultats satisfaisants. Ainsi, si l'on dessine un
rectangle à l'aide de la procédure appropriée, il aura les caractéristiques suivantes:
•
•
•
•
Couleur du trait: noir
Epaisseur du trait: 1 pixel
Style du trait: continu
Couleur de l'intérieur du rectangle: blanc (non transparent)
On peut se lancer dans la programmation graphique en Delphi et ignorer ce qu'elle cache
au niveau de Windows. Ce serait vraiment dommage, car il est toujours intéressant et
parfois très utile d'avoir une connaissance ne serait-ce que basique de l'architecture avec
laquelle on s'apprête à travaille.
Revenons sur le Device context. Bien que le GDI soit indépendant du matériel, les
routines auxquelles on fait appel doivent connaître les caractéristiques du périphérique
auquel elles s'attaquent. Ainsi l'instruction suivante:
Rectangle (0, 0, 200, 400);
dessine un rectangle de 200 x 400 unités. Mais on ignore quelles sont ces unités et à
quel emplacement de l'écran va s'afficher le rectangle. Le GDI peut le savoir grâce au
Device context, qui lui, connaît les particularités du périphérique. En réalité voici
comment on devrait dessiner un rectangle en faisant appel aux fonctions de l'API de
Windows:
procedure TForm1.Button1Click(Sender: TObject);
var DC : HDC;
begin
DC := GetDC (handle);
Rectangle (DC, 20, 20, 120, 130);
ReleaseDC (handle, DC);
end;
Ex1.exe
et voici le résultat:
__________________________________________________________________________________________
Le graphisme (JC Armici)
-9www.unvrai.com
Dans cet exemple, la fonction GetDC fournit un Device context pour la fenêtre qui en fait
la demande au travers de son Handle (ou descripteur). Une fois cette information
obtenue, le GDI sait qu'il va travailler sur l'écran et en connaît les caractéristiques. On
peut donc dessiner le rectangle à l'aide de la fonction Rectangle du GDI, à laquelle on
fournit précisément le Device context DC. Ce n'est pas la même fonction que celle de
Delphi, qui possède uniquement 4 paramètres. Il est bien entendu impératif de libérer la
ressource après son utilisation.
Dans Delphi, l'objet Canvas encapsule toutes les caractéristiques d'un Device context de
Windows et se charge, entre autres, d'obtenir et de libérer le Device context. Le second
bouton de l'exemple ci-dessus dessine un rectangle à l'aide des instructions suivantes:
procedure TForm1.Button2Click(Sender: TObject);
begin
canvas.Rectangle (140, 20, 240, 130);
end;
Le canvas utilisé ici est celui de la fenêtre principale du programme. Ceci est sousentendu, et on aurait pu écrire:
Ex1.exe
procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.canvas.Rectangle (140, 20, 240, 130);
end;
Dans cet exemple le gain en complexité n'est pas spectaculaire. Mais on peut effectuer
une seconde comparaison pour le dessin d'un rectangle rouge avec un intérieur jaune.
Voici les instructions en version GDI:
procedure TForm1.Button1Click(Sender: TObject);
var DC : HDC;
NewPen, OldPen : HPen;
NewBrush, OldBrush : HBrush;
begin
DC := GetDC (handle);
NewPen := CreatePen (PS_SOLID, 1, RGB (255,0,0));
OldPen := SelectObject (DC, NewPen);
NewBrush := CreateSolidBrush (RGB (255,255,0));
OldBrush := SelectObject (DC, NewBrush);
Rectangle (DC,20,20,120,130);
SelectObject (DC, OldPen);
SelectObject (DC, OldBrush);
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 10 www.unvrai.com
DeleteObject (NewPen);
DeleteObject (NewBrush);
ReleaseDC (handle, DC);
end;
Et en version canvas de Delphi:
Ex2.exe
procedure TForm1.Button2Click(Sender: TObject);
begin
canvas.Brush.color := clYellow;
canvas.pen.color := clRed;
canvas.rectangle (140,20,240,130);
end;
Plusieurs composants de Delphi disposent d'un canvas. Si nous voulions dessiner ce
même rectangle à l'intérieur d'un composant Image1 de type Timage, il suffirait de
préfixer la spécification du canvas par l'objet sur le canvas duquel on veut dessiner:
procedure TForm1.Button2Click(Sender: TObject);
begin
Image1.canvas.Brush.color := clYellow;
Image1.canvas.pen.color := clRed;
Image1.canvas.rectangle (140,20,240,130);
end;
Dans ce qui suit, nous examinerons les principale propriétés et méthodes associées au
Canvas.
Exercice 1:
Ecrire un programme permettant d'afficher des rectangles de diverses caractéristiques: emplacement,
dimensions, couleur, remplissage, épaisseur...
6.2
Pen et Brush
L'objet Pen de type Tpen représente le crayon (ou la plume) et permet de dessiner.
L'objet Brush de type Tbrush représente le pinceau et sert à peindre, c'est-à-dire à
remplir des surfaces d'une certaine couleur, avec un certain motif. Un pinceau possède
également une forme.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 11 www.unvrai.com
Par défaut, Pen possède les caractéristiques suivantes:
• Couleur: noir
• Epaisseur: 1 pixel
• Style: trait continu
• Mode: pmCopy, c'est-à-dire la couleur spécifiée par la propriété Color
Par défaut, Brush possède les caractéristiques suivantes:
• Couleur: blanc
• Style: bsSolid, c'est-à-dire remplissage uniforme
Indications concernant les couleurs
Lorsque l'on s'intéresse au graphisme en informatique il faut savoir comment les couleurs
sont représentées.
Le programme proposé ici permet d'explorer les mélanges de couleurs fondamentales
dans les deux types de synthèses. Il est donné uniquement dans un but d'illustration. La
manière dont il est écrit n'est pas forcément très explicite à ce stade du cours.
Ex2b.exe
La représentation RGB (Red Green Blue ou Rouge Vert Bleu) utilise une synthèse additive
des couleurs et intervient, par exemple, dans l'affichage sur les écrans. Dans ce cas c'est
la lumière qui est mélangée: on part du noir (aucune couleur) et on ajoute des couleurs.
La représentation CMY (Cyan Magenta Yellow) utilise une synthèse soustractive des
couleurs et intervient, par exemple, dans l'impression. Ce sont des encres ou des
pigments qui sont mélangés.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 12 www.unvrai.com
Ex2b.exe
En synthèse additive:
noir + rouge = rouge
noir + vert = vert
noir + bleu = bleu
noir + rouge + vert = jaune
noir + rouge + bleu = magenta
noir + bleu + vert = cyan
noir + rouge + vert + bleu = blanc
En synthèse soustractive:
blanc - rouge = cyan
blanc - vert = magenta
blanc - bleu = jaune
blanc - rouge - vert = bleu
blanc - rouge - bleu = vert
blanc - bleu - vert = rouge
blanc - rouge - vert - bleu = noir
Avec ces précisions à l'esprit, une tulipe sera plutôt "non bleue" que "jaune".
6.3 Pixels
Pour chaque objet ou composant disposant d'un canvas, il est possible de spécifier et/ou
de lire la couleur d'un pixel donné. Par exemple, pour placer un point rouge aux
coordonnées (80,100), il suffit d'écrire:
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 13 www.unvrai.com
Canvas.pixels[80,100] := clRed;
On voit que le canvas, en tant que surface de dessin, est considéré comme un tableau à
deux dimensions. L'origine du canvas étant le point (0,0) qui se trouve en haut et à
gauche du canvas, les coordonnées croissant vers la droite et vers le bas.
Dans notre exemple, clRed est une constante entière indiquant la couleur rouge. On
aurait également pu écrire:
Canvas.pixels[80,100] := 45766;
pour autant que l'on connaisse la couleur associée à la valeur 45766, ou encore:
Canvas.pixels[80,100] := RGB (255, 0, 0);
la fonction RGB indiquant une couleur au moyen de ses composantes rouge, vert et bleu;
chaque composante peut avoir une valeur comprise entre 0 et 255.
Pour connaître la couleur d'un pixel, il faut faire appel au contenu du tableau pixels pour
la coordonnée désirée, par exemple:
Couleur := canvas.pixels[34,56];
La variable Couleur contiendra la valeur entière représentant la couleur du point de
coordonnées (34,56).
De même, pour tester si un point est "allumé" sur un fond blanc on pourra écrire:
If canvas.pixels[30,50] <> clWhite then…
Ex3.exe
Dans le programme suivant le bouton "Afficher pixel" permet d'afficher des points de la
couleur et à des emplacements choisis par l'utilisateur. Le bouton lire pixel effectue
l'opération inverse; il affiche la couleur du point dont les coordonnées sont spécifiées.
Comme nous l'avons vu, il n'est pas évident de choisir une couleur en fonction de sa
seule valeur numérique. Il est donc judicieux de proposer le choix visuel d'une couleur à
l'aide du nouveau bouton visible sur l'exemple suivant:
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 14 www.unvrai.com
Ex4.exe
Exercice 2:
Ecrire un programme permettant d'afficher une image de type BMP dans un composant Timage et de
connaître la valeur des composantes R, G et B des points sur lesquels se déplace la souris. Les
fonctions GetRvalue, GetGvalue et GetBvalue permettent d'extraire les composante d'une couleur.
L'affichage se fera sous la forme: R=230 G=12 B=255.
Exercice 3:
Modifier le programme de l'exercice précédent afin qu'un clic gauche de souris sur un point de l'image
provoque le remplissage uniforme d'un composant (p.ex. Panel, Label, etc) avec la couleur du point
sur lequel on a cliqué.
6.4 Lignes
Travailler avec des points afin de tracer des lignes est un peu laborieux. Le canvas de
Delphi dispose d'une méthode LineTo permettant de tracer un segment de droite.
L'instruction:
Canvas.LineTo (60,80);
Trace une ligne entre la position précédente et le point (60,80). Si aucun tracé n'a encore
été effectué, la position précédente est le point (0,0). Sinon il s'agit du point d'arrivée de
la dernière opération graphique effectuée sur le canvas.
Le canvas connaît donc la notion de position courante. Cette position courante peut être
consultée à l'aide de la propriété PenPos (de type TPoint):
Var p : TPoint;
Indic : string;
. . .
p := canvas.PenPos;
indic := 'La position courante est: x=' + inttostr(p.x) + '
y=' + inttostr(p.y);
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 15 www.unvrai.com
Revenons à nos lignes. Pour tracer une ligne du point (20,20) au point (65,30), il
conviendra d'écrire:
Canvas.MoveTo (20,20);
Canvas.LineTo (65,30);
La première instruction déplace le point courant à la position (20,20) et la seconde trace
une ligne jusqu'à la position (65,30).
Ex5.exe
L'exemple qui suit permet de tracer des segments de droites en suivant les clics de la
souris.
Dans ce programme le tracé est effectué sur le canvas de la fenêtre principale, alors que
pour les deux exemples précédents nous avons utilisé le canvas d'un composant de type
TImage. Le principe est identique et l'avantage d'utiliser un composant TImage est de
restreindre les opérations de dessin dans un rectangle.
Il est même possible de déclarer une variable de type TCanvas et de lui affecter, au choix
de l'utilisateur, le canvas de l'un ou l'autre de deux composants de type TImage.
Ex6.exe
public
{ Déclarations publiques }
canv : TCanvas;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 16 www.unvrai.com
Le clic sur le bouton radio "droite" affecte à la variable canv le canvas du TImage de
droite, puis efface le contenu de ce même canvas:
canv := image2.canvas;
canv.fillrect (rect(0,0,image2.width,image2.height));
Le bouton "Lignes au hasard" trace 100 lignes d'emplacement et de couleur aléatoires, et
d'épaisseur 2 pixels.
procedure TForm1.HasardClick(Sender: TObject);
var i : integer;
begin
canv.pen.width := 2;
for i := 1 to 100 do begin
canv.pen.color := RGB(random(256), random(256), random(256));
canv.lineto (random(image1.width), random(image1.height));
application.processmessages; // pour voir une ligne après l'autre
end;
end;
Exercice 4:
Ecrire un programme permettant de tracer une ligne entre deux points sur lesquels on a cliqué avec la
souris. Dans ce programme, des options doivent permettre de choisir le style de la ligne (traitillée,
pointillée, etc), son épaisseur, sa couleur. Que constatez-vous en variant les divers paramètres ?
6.5 Rectangles, ellipses, arcs, cordes
Ex7.exe
Sur un canvas il est possible de tracer des figures telles que rectangles (avec ou sans les
coins arrondis), ellipses, arcs ou cordes. Voici un programme permettant d'afficher ces
figures.
La procédure suivante trace un rectangle (avec ou sans coins arrondis en fonction de
l'état de la case à cocher:
procedure TForm1.Button1Click(Sender: TObject);
begin
if rond.checked then
image1.canvas.roundrect (random(100), random(100), 120 + random(100), 120 +
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 17 www.unvrai.com
random(50), 20, 20)
else
image1.canvas.rectangle (random(100), random(100), 120 + random(100), 120 +
random(50));
end;
La procédure suivante trace une ellipse:
procedure TForm1.Button2Click(Sender: TObject);
begin
image1.canvas.ellipse (random(100),random(100),120+random(100),120+random(50));
end;
La procédure suivante trace un arc d'ellipse:
procedure TForm1.Button3Click(Sender: TObject);
begin
image1.canvas.arc (random(100), random(100), 120 + random(100), 120 + random(50),
random(image1.width), image1.height, random(image1.width), 0);
end;
La procédure suivante trace un arc et une corde d'ellipse:
procedure TForm1.Button5Click(Sender: TObject);
begin
image1.canvas.chord (random(100),random(100),120 + random(100), 120 + random(50),
random(image1.width),image1.height,random(image1.width),0);
end;
6.6 Rectangles pleins et remplissage de surfaces
Ex8.exe
Au travers du programme qui suit nous allons voir comment tracer des rectangles
remplis de différentes manières.
En cliquant avec le bouton gauche ou droit de la souris sur le ColorGrid, on choisit la
couleur du crayon (Pen) pour le bord du rectangle ou la couleur du pinceau (Brush) pour
l'intérieur du rectangle.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 18 www.unvrai.com
Un TrackBar permet de choisir l'épaisseur du bord du rectangle (entre 1 et 6).
On peut également choisir le style de remplissage du rectangle, parmi les 8 possibilités
offertes par Windows.
Dans le programme suivant, l'utilisateur peut dessiner en utilisant la souris (avec le
bouton gauche enfoncé). Lorsqu'il clique a un emplacement avec le bouton droit de la
souris, la surface est remplie de la couleur choisie à l'aide du ColorGrid.
Ex9.exe
La couleur du tracé est choisie avec le bouton gauche de la souris sur le ColorGrid, alors
que la couleur de remplissage est choisie avec le bouton droit de la souris.
La procédure suivante permet à l'utilisateur d'effectuer un tracé à l'aide du bouton
gauche enfoncé.
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
image1.canvas.pen.color := colorgrid1.ForegroundColor;
if shift = [ssleft] then
image1.canvas.lineTo (x,y);
end;
La procédure suivante effectue le remplissage en partant du point cliqué et en utilisant la
couleur du pinceau (Brush) jusqu'à rencontrer la couleur colorgrid1.ForegroundColor:
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
image1.canvas.Brush.color := colorgrid1.BackgroundColor;
if shift = [ssright] then
image1.canvas.FloodFill (x, y, colorgrid1.ForegroundColor, fsBorder);
end;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 19 www.unvrai.com
Exercice 5:
Ecrire un programme qui ressemble à l'exemple Ex9 ci-dessus et Ex111 ci-dessous, mais dans lequel
l'utilisateur trace des segments de droites formant une ligne polygonale. La surface sera ensuite
remplie d'une couleur à choix. Lorsque l'utilisateur clique sur la souris le segment de droite en cours
suit le mouvement du curseur et s'affiche définitivement lorsque le bouton de la souris est relâché.
6.7 Courbes de Bézier
Une courbe de Bézier utilise 4 points. La courbe va du 1er point au 4ème point. Les points
intermédiaires servent de points de contrôle. La méthode PolyBezier du Canvas permet
de tracer automatiquement une courbe de Bézier, en lui fournissant un tableau de 4, 7,
10, 13, … points.
Ex10.exe
Le programme suivant permet à l'utilisateur d'indiquer 10 points en cliquant avec le
bouton gauche de la souris. Le bouton "Bézier" trace la courbe de Bézier:
Il convient de signaler que la courbe est de la couleur du crayon (Pen), mais qu'il n'y a
aucun remplissage.
De plus, le tracer d'une courbe de Bézier ne change pas la valeur de PenPos.
Exercice 6:
Ecrire un programme qui s'inspire de l'exemple Ex10 ci-dessus, mais avec lequel il est possible de
joindre les segments de courbes de Bézier sans former un angle.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 20 www.unvrai.com
6.8 Polygones
En prenant comme base de départ 10 points choisis par l'utilisateur comme dans le
programme précédent, il est aussi possible de tracer une ligne polygonale, à l'aide de la
méthode Polygon du Canvas. La ligne prend les attributs du crayon (Pen) et l'intérieur est
rempli en prenant les attributs du pinceau (Brush). La méthode Polygon relie
automatiquement le premier et le dernier point.
Ex11.exe
Par rapport à l'exercice précédent le choix des deux couleurs a été ajouté. Il s'effectue à
l'aide d'un ColorGrid.
De plus, le tracer d'une telle ligne polygonale ne change pas la valeur de PenPos.
Le Canvas dispose également de la méthode PolyLine qui ressemble beaucoup à Polygon,
avec toutefois les différences suivantes:
•
•
La ligne n'est pas automatiquement fermée
Il n'y a aucun remplissage
6.9 Affichage de textes
Le Canvas dispose de deux méthodes permettant d'afficher du texte à un emplacement
donné: TextOut et TextRect. La seconde offre la possibilité de spécifier un rectangle à
l'extérieur duquel le texte sera masqué.
Lorsqu'il s'agit d'afficher du texte il est parfois nécessaire de connaître la hauteur et la
largeur de ce texte en pixels. La méthode TextExtent permet de retrouver ces deux
informations. Voici sa déclaration:
type
TSize = record
cx: Longint;
cy: Longint;
end;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 21 www.unvrai.com
function TextExtent(const Text: string): TSize;
Ex12.exe
Signalons qu'il existe aussi la méthode TextHeight pour connaître uniquement la hauteur
du texte et TextWidth pour connaître uniquement la largeur. L'exemple qui suit illustre
l'utilisation de ces nouvelles méthodes du Canvas.
Afin de mieux visualiser le rectangle de masquage, le programme en trace le contour en
rouge.
Les dimensions de la chaîne de caractères sont affichées en cliquant sur le bouton
TextOut. Voici le code associé au clic de ce bouton:
procedure TForm1.Button1Click(Sender: TObject);
var taille : TSize;
begin
image1.canvas.font.size := 14;
image1.canvas.textout (20, 20, 'Bonjour');
taille := image1.canvas.TextExtent ('Bonjour');
edit1.Text := inttostr (taille.cx);
edit2.Text := inttostr (taille.cy);
end;
On peut également observer le choix de la taille de la fonte. La valeur de la propriété
Font est un objet de type TFont. Il suffit d'affecter des valeurs aux propriétés de l'objet
TFont pour spécifier la police, la couleur, la taille et le style de la fonte.
6.10 Images
Jusqu'ici nous avons utilisé le canvas d'un composant TImage pour dessiner. Il est
également possible d'attribuer une image de type bitmap à ce composant. Le programme
suivant permet à l'utilisateur de charger et d'enregistrer une image, ainsi que de modifier
trois propriétés d'une image: Autosize, Center et Stretch.
Ce programme fait également appel à deux dialogues standards pour lire et enregistrer
les images: OpenPictureDialog et SavePictureDialog. A la différence de leurs homologues
OpenDialog et SaveDialog, ils proposent une prévisualisation de l'image à charger ou à
sauvegarder.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 22 www.unvrai.com
Ex13.exe
Ex13.exe
Voici la boîte de dialogue qui apparaît en cliquant sur le bouton "Ouvrir":
La propriété Center permet de centrer l'image dans le composant TImage.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 23 www.unvrai.com
Ex13.exe
Ex13.exe
La propriété Stretch effectue un redimensionnement de l'image de manière à ce qu'elle
occupe toute la surface du composant TImage:
L'option Autosize redimensionne le composant TImage de manière à ce que sa taille
corresponde à celle de l'image qui est chargée.
Exercice 7:
Ecrire un programme permettant d'afficher sous forme réduite toutes les images de type BMP qu'il
trouve dans un répertoire donné. Amélioration possible: parcours récursif des sous-répertoires.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 24 www.unvrai.com
6.11 Programme de dessin sans ambition
Ex13b.exe
Nous allons construire un petit programme de dessin (du style Paint). Dans un premier
temps notre programme aura un minimum de fonctionnalités. Dans un second temps
nous lui ajouterons petit à petit des possibilités, aussi bien au niveau des outils
disponibles qu'au niveau des possibilités. La première version ressemble à ceci:
Nous allons décrire en détail le programme au niveau de son interface et de son
implémentation.
Eléments intervenant dans le programme:
•
•
•
•
•
•
•
•
Une fenêtre principale, volontairement non dimensionnable.
Un menu contenant aussi bien des options générales (Charger une image,
Quitter…) que des options qui, par exemple, se retrouvent au niveau de boutons
de l'interface graphique.
Une surface de dessin im (de type TImage), placée dans un Panel. Toutes les
opérations de dessin s'effectuent sur le canvas de l'objet im.
.Une barre d'état (StatusBar), indiquant dans le 1er volet l'outil en cours
d'utilisation, dans le 2ème volet les coordonnées du cirseur (souris), dans le 3ème
volet la taille de la figure en cours de dessin (uniquement pour les figures inscrites
dans un rectangle.
Un composant ColorGrid pour le choix des couleurs du crayon et du pinceau
(remplissage)
Un dispositif permettant de choisir l'épaisseur des lignes (1, 2, 4 ou 8 pixels).
Plusieurs possibilités étaient envisageables; nous avons choisi celle consistant à
placer 4 Labels de hauteur variable.
Différents composants de type TImage contenant des icônes empruntées à
MSPAINT.EXE
Deux éléments ne sont pas visibles sur la copie d'écran ci-dessus. Il s'agit des
dialogues de chargement et de sauvegarde de l'image.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 25 www.unvrai.com
Voici la définition des constantes correspondant aux 3 outils disponibles:
const fLIBRE
fRECTANGLE
fELLIPSE
= 0;
= 1;
= 2;
// main levée
// rectangle
// ellipse
fsLIBRE
= 'Desin libre';
fsRECTANGLE = 'Rectangle';
fsELLIPSE
= 'Ellipse';
// libellés correspondants
De plus, nous avons besoin des variables (champs) suivantes:
public
{ Déclarations publiques }
fig : integer;
// choix de l'outil
x0, y0 : integer; // position intermédiaire
xd, yd : integer; // position de départ
end;
La variable fig contient l'outil courant. Les variables xd, yd, x0 et y0 sont utilisées lors du
dessin de figure telles que rectangles ou ellipses.
Lors du lancement du programme (OnActivate) on effectue les opérations suivantes:
procedure TForm1.FormActivate(Sender: TObject);
begin
im.canvas.moveto (0,0);
fig := fLIBRE;
// tracé à main levée
statusbar1.panels[0].text := fsLIBRE;
end;
Deux options du menu Fichier permettent de charger et d'enregistrer l'image:
procedure TForm1.Ouvrir1Click(Sender: TObject);
begin
if OpenPictureDialog1.execute then
im.Picture.LoadFromFile (OpenPictureDialog1.FileName);
end;
procedure TForm1.Enregistrersous1Click(Sender: TObject);
begin
if SavePictureDialog1.execute then
im.Picture.SaveToFile (SavePictureDialog1.Filename);
end;
Lorsque l'utilisateur clique (OnClick) avec le bouton droit ou gauche pour choisir une
couleur, voici les instructions qui sont exécutées:
procedure TForm1.ColorGrid1Click(Sender: TObject);
begin
im.canvas.Pen.color := colorgrid1.ForegroundColor;
im.canvas.brush.color := colorgrid1.BackgroundColor;
end;
Lorsque l'utilisateur clique sur la surface de dessin (OnMouseDown), le code suivant est
exécuté:
procedure TForm1.imMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
im.canvas.moveto (x,y);
xd := x;
// on garde la position de départ
yd := y;
end;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 26 www.unvrai.com
Après que l'utilisateur a appuyé sur le bouton de la souris, il peut déplacer celle-ci; le
résultat obtenu dépend de l'outil sélectionné. Voici le code derrière l'événement
OnMouseMove:
procedure TForm1.imMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
statusbar1.panels[1].text := inttostr (x) + ',' + inttostr(y);
if ssleft in shift then
case fig of
fLIBRE
: im.canvas.LineTo (x,y);
fRECTANGLE : begin
im.canvas.Pen.Mode := pmxor;
im.canvas.Rectangle (xd, yd, x0, y0);
im.canvas.Rectangle (xd, yd, x, y);
statusbar1.panels[2].text := inttostr (abs(x-xd)) + 'x' +
inttostr(abs(y-yd));
end;
fELLIPSE
: begin
im.canvas.Pen.Mode := pmxor;
im.canvas.ellipse (xd, yd, x0, y0);
im.canvas.ellipse (xd, yd, x, y);
statusbar1.panels[2].text := inttostr (abs(x-xd)) + 'x' +
inttostr(abs(y-yd));
end;
end;
x0 := x;
y0 := y;
end;
Dans un premier temps on affiche les coordonnées de la souris dans le 2ème volet de la
barre d'état. Ensuite les instructions dépendent de l'outil sélectionné. Pour le dessin libre,
on effectue simplement un tracé de ligne entre le point précédent et le point actuel. Pour
le rectangle ou l'ellipse, on dessine la figure à la position précédente (de (xd,yd) à
(x0,y0)) en mode xor, puis à la nouvelle position (de (xd,yd) à (x,y)). L'utilisation du
mode xor permet de ne pas détruire le fond de l'image pendant le dessin de la figure. On
en profite également pour indiquer, dans le 3ème volet de la barre d'état, les dimensions
actuelles de la figure qui est en train d'être dessinée. Enfin on actualise la valeur des
coordonnées de la position précédente. C'est dans cette procédure que viendra s'ajouter
le code qui concernera l'utilisation d'outils futurs (gomme, spray…).
Lorsque l'utilisateur lâche le bouton de la souris (OnMouseUp) les opérations suivantes
sont effectuées:
procedure TForm1.imMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
case fig of
fRECTANGLE : begin
im.canvas.Pen.Mode := pmcopy;
im.canvas.Rectangle (xd, yd, x, y);
end;
fELLIPSE
: begin
im.canvas.Pen.Mode := pmcopy;
im.canvas.Ellipse (xd, yd, x, y);
end;
end;
statusbar1.panels[2].text := '';
end;
En fait, pour le rectangle et pour l'ellipse, il faut passer en mode normal (pmcopy) et de
dessiner la figure à sa position finale. Il ne faut pas oublier d'effacer le contenu du 3ème
volet de la barre d'état.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 27 www.unvrai.com
Lorsque l'utilisateur change d'outil, soit à l'aide du menu Outils soit en cliquant sur l'icône
de l'outil, le programme doit mémoriser le nouvel outil choisi et afficher sa description
dans le 1er volet de la barre d'état. Voici uniquement l'exemple concernant l'outil "Dessin
libre":
procedure TForm1.Dessinlibrfe1Click(Sender: TObject);
begin
fig := fLIBRE;
statusbar1.panels[0].text := fsLIBRE;
end;
Pour le choix de l'épaisseur du crayon nous avons utilisé une particularité de Delphi, à
savoir le paramètre Sender que l'on retrouve dans quasiment toutes les procédures
événementielles. Le code qui gère le choix de l'épaisseur mérite quelques explications.
Tout d'abord il faut savoir que les 4 labels représentant les épaisseurs de ligne ont
comme noms e1, e2, e4 et e8. Lorsque l'utilisateur clique sur e8 (épaisseur de 8 pixels),
voici la procédure événementielle qui est exécutée:
procedure TForm1.e8Click(Sender: TObject);
begin
im.canvas.pen.width := strtoint((sender as Tlabel).name[2]);
end;
Il suffit de choisir cette même procédure pour les 3 autres labels. En fait:
(sender as Tlabel).name représente le nom du label (par exemple 'e8')
(sender as Tlabel).name[2] représente le 2ème caractère du nom, donc le nombre de
pixels d'épaisseur
On transforme ensuite ce caractère en chiffre et on l'affecte à l'épaisseur du crayon pour
le canvas de l'objet im (surface de dessin).
Enfin, une option "Effacer tout" du menu Edition permet d'effacer le contenu de la surface
de dessin. Voici son code:
procedure TForm1.Effacertout1Click(Sender: TObject);
var back : integer;
begin
back := im.canvas.brush.color;
// sauvegarde de la couleur en cours
im.canvas.brush.color := clWhite;
im.canvas.fillrect (rect (0, 0, im.width, im.height));
im.Canvas.brush.color := back;
// restitution de la couleur en cours
end;
6.12 Amélioration du programme de dessin
Il manque beaucoup de fonctionnalités à la version précédente de notre programme de
dessin. Entre autres:
•
•
•
•
•
Il n'y a aucune possibilité de faire des zooms
La taille de la fenêtre est fixe
Si l'image chargée est de grande dimension, on n'en voit qu'une partie
On ne peut pas choisir d'autres couleurs que les 16 proposées
Les outils sont minimalistes, en nombre et en possibilités
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 28 www.unvrai.com
Ex14.exe
Voici l'aspect du nouveau programme de dessin:
Voyons quelles améliorations ont été apportées au programme.
Fenêtre dimensionnable et image avec défilement
La fenêtre a un style de bordure dimensionnable. Le composant im (de type TImage)
dont on utilise le canvas en tant que surface de dessin est maintenant contenu dans un
composant ScrollBox avec les propriétés suivantes:
•
•
•
•
Autosize: false
Autoscroll: true
Anchors: les quatre sont true
Barres de défilement:
o smooth: true
o tracking: true
o style: ssHotTrack
Le composant im a les propriétés suivantes:
•
•
Autosize: true
Stretch: false
Avec ces propriétés ainsi définies, le dimensionnement de la fenêtre provoque le
dimensionnement de l'image. De plus, les composants liés aux icônes des outils, au choix
d'épaisseur de ligne et des couleurs sont ancrés en bas et à gauche (au lieu de en haut et
à gauche comme précédemment). Enfin les deux Trackbars réglant les paramètres du
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 29 www.unvrai.com
spray sont ancrés en bas et à droite. De cette manière, lors du changement de dimension
de la fenêtre, ces composants restent attachés à leur coin respectif.
Signalons également qu'une taille minimale à été imposée à la fenêtre à l'aide de ses
propriétés Constraints.MinHeight et Constraints.MinWidth.
Les outils
L'organisation des outils a été améliorée. Voici les définitions de constantes en rapport
avec les outils:
const MAX = 7;
const fLIBRE
fRECTANGLE
fELLIPSE
fSPRAY
fSPRAY2
fRECTANGLER
fPIPETTE
fZOOM
=
=
=
=
=
=
=
=
0;
1;
2;
3;
4;
5;
6;
7;
//
//
//
//
//
//
//
//
main levée
rectangle
ellipse
spray
spray 2ème type
rectangle à coins arrondis
pipette
loupe
Les libellés à afficher dans la barre d'état sont maintenant placés dans un tableau:
lib : array[0..MAX] of string [30];
et initialisés lors du lancement du programme:
procedure TForm1.FormActivate(Sender: TObject);
begin
lib[0] := 'Desin libre';
lib[1] := 'Rectangle';
lib[2] := 'Ellipse';
lib[3] := 'Spray';
lib[4] := 'Spray continu';
lib[5] := 'Rectangle à coins arrondis';
lib[6] := 'Pipette';
lib[7] := 'Loupe';
im.canvas.moveto (0,0);
fig := fLIBRE;
// tracé à main levée
statusbar1.panels[0].text := lib[0];
tspr := 5;
// taille du spray
fspr := spROND;
// forme du spray
dspr := 10;
// densité du spray
colorgrid1.ForegroundIndex := 0;
colorgrid1.BackgroundIndex := 15;
fond.color := clwhite;
// pannel indiquant la couleur du pinceau
texte.color := clblack;
// pannel indiquant la couleur du crayon
end;
Les composants de type TImage contenant l'icône des outils s'appellent maintenant btn0
pour le dessin libre, btn1 pour le rectangle, etc. Lors du clic sur une de ces icônes le code
suivant est exécuté (pour tous les outils):
procedure TForm1.btn0Click(Sender: TObject);
var no : integer; // no. de l'outil
begin
no := strtoint((sender as TImage).name[4]);
fig := no;
statusbar1.panels[0].text := lib[no];
cache.Visible := not (fig in [fSPRAY,fSPRAY2]);
end;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 30 www.unvrai.com
Nous avons déjà rencontré précédemment l'utilisation du paramètre Sender. Le
composant Cache auquel il est fait référence est un Panel qui masque les deux TrackBars
concernant la taille et la densité du spray lorsque le spray n'est pas sélectionné.
Enfin, le tracé de figures telles que rectangles et ellipses a été modifié pour qu'elles
soient vides (intérieur transparent) lorsqu'elles sont dessinées à l'aide du bouton gauche
de la souris et remplies (avec la couleur du pinceau) lorsqu'elles sont dessinées à l'aide
du bouton droit de la souris.
Rectangle à coins arrondis
Ce nouvel outil ressemble à celui dessinant un rectangle. La méthode RoundRect est
appelée en lieu et place de la méthode Rectangle.
Les deux types de spray
Nous avons choisi d'implémenter deux sortes de sprays:
•
•
Un spray qui spraye uniquement lorsqu'on le bouge
Un spray qui spraye en continu, même lorsque l'on ne déplace pas la souris
Ex14.exe
Chaque spray peut avoir une forme carrée ou ronde choisie à l'aide du menu suivant:
Les deux options concernant la forme du spray provoque l'exécution du code suivant:
procedure TForm1.Circulaire1Click(Sender: TObject);
begin
fspr := spROND;
// forme du spray
carr1.checked := false;
// enlève la coche
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 31 www.unvrai.com
circulaire1.checked := true;
end;
// place la coche
procedure TForm1.Carr1Click(Sender: TObject);
begin
fspr := spCARRE;
// forme du spray
carr1.checked := true;
// place la coche
circulaire1.checked := false; // enlève la coche
end;
Le spray simple fonctionne de manière assez simple. La procédure événementielle
exécutée lorsque l'on déplace la souris contient le code suivant:
fSPRAY
: begin
for i := 1 to 10*dspr do begin
if fspr = spCARRE then
im.canvas.pixels[x+tspr-random(2*tspr),y+tspr- random(2*tspr)] :=
im.canvas.pen.color
else begin
xx := tspr - random(2*tspr);
yy := tspr - random(2*tspr);
if sqrt (xx*xx + yy*yy) < tspr then
im.canvas.pixels[x+xx,y+yy] := im.canvas.pen.color
end;
end;
end;
end;
On remarquera qu'une boucle place un certain nombre de points au hasard dans un carré
ou un cercle. Le nombre de points dépend du contenu de la variable dspr (qui détermine
la densité du spray et est mise à jour à l'aide du 1er TrackBar situé en bas à droite de la
fenêtre, lorsque l'un des sprays est sélectionné). La taille du spray dépend de la variable
tspr (mise à jour à l'aide du 2ème TrackBar situé en bas à droite de la fenêtre).
Pour le spray en mode continu, il fallait trouver une méthode qui permette de placer des
points même lorsque la souris n'est pas déplacée. Nous avons opté pour l'utilisation d'un
Timer qui est mis en marche lorsque le bouton gauche de la souris est appuyé:
if fig = fSPRAY2 then
timer1.enabled := true;
et qui est arrêté lorsque le bouton de la souris est relâché.
case fig of
fSPRAY2
: timer1.enabled := false;
…
L'intervale du timer est réglé sur 10 millisecondes et le code qui lui est associé est le
suivant:
procedure TForm1.Timer1Timer(Sender: TObject);
var i : integer;
xx, yy : integer;
begin
for i := 1 to 10*dspr do begin
if fspr = spCARRE then
im.canvas.pixels[x0+tspr-random(2*tspr),y0+tspr-random(2*tspr)] :=
im.canvas.pen.color
else begin
xx := tspr - random(2*tspr);
yy := tspr - random(2*tspr);
if sqrt (xx*xx + yy*yy) < tspr then
im.canvas.pixels[x0+xx,y0+yy] := im.canvas.pen.color
end;
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 32 www.unvrai.com
end;
end;
Ce code ressemble beaucoup à celui du spray normal, à la différence qu'il est exécuté en
permanence aussi longtemps que le bouton de la souris est enfoncé.
La pipette
Cet outil permet de connaître la valeur de la couleur du point sur lequel il se trouve,
lorsque la souris est déplacée. De plus, en cliquant avec le bouton gauche de la souris, la
couleur du point sous-jacent est affectée au crayon, alors qu'en cliquant avec le bouton
droit de la souris, la couleur est affectée au pinceau.
Lorsque la souris est déplacée, la valeur RGB de la couleur est affichée dans le 4ème volet
de la barre d'état:
if fig = fPIPETTE then begin
coul := im.canvas.pixels[x,y];
statusbar1.panels[3].text := inttostr(getRvalue(coul)) + ':'
+inttostr(getGvalue(coul)) + ':'
+inttostr(getBvalue(coul));
pipettecoul.color := coul;
end;
Le composant Pipettecoul est un Panel dont la couleur suit le mouvement de la souris.
texte
fond
pipettecoul
Texte et fond sont deux Panels dont la couleur indique respectivement celle du crayon et
celle du pinceau.
Voici le code exécuté lorsque le bouton de la souris est appuyé et concernant l'outil
pipette:
if fig = fPIPETTE then begin
if [ssleft] = shift then begin
im.canvas.pen.color := pipettecoul.Color;
texte.color := pipettecoul.color;
end;
if [ssright] = shift then begin
im.canvas.brush.color := pipettecoul.Color;
fond.color := pipettecoul.color;
end;
pipettecoul.color := clSilver;
end;
Le zoom
Pour le zoom, plusieurs méthodes étaient utilisables. Nous avons opté pour une méthode
simple, qui met en jeu un second composant de type TImage invisible.
Nous nous sommes également limités à un zoom d'un facteur deux. Un premier clic sur la
loupe effectue un zoom avant d'un facteur deux, un second clic génère un zoom arrière
d'un facteur deux.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 33 www.unvrai.com
Le principe utilisé est le suivant. On choisit une dimension pour le composant im2
(TImage invisible), puis on copie l'image de im dans im2 à l'aide de la méthode
CopyRect. On affecte enfin l'image de im2 dans im. Ci-dessous on peut voir la procédure
de zoom, avec les quatre lignes importantes en gras:
procedure TForm1.btn07Click(Sender: TObject);
var rect1, rect2 : TRect;
begin
pencol0 := im.canvas.pen.color;
penwidth0 := im.canvas.pen.width;
brushcol0 := im.canvas.brush.color;
rect1 := rect (0,0,im.width, im.height);
if not bZoom then begin
rect2 := rect (0,0,im.width * 2, im.height *
im2.width := im.width*2;
im2.height := im.height*2;
end else begin
rect2 := rect (0,0,im.width div 2, im.height
im2.width := im.width div 2;
im2.height := im.height div 2;
end;
im2.Canvas.CopyRect (rect2, im.canvas, rect1);
im.picture := nil;
im.picture.bitmap.Assign (im2.picture.bitmap);
im2.picture := nil;
// en mode zoom on double l'épaisseur
im.canvas.pen.width := penwidth0;
if not bZoom then
im.canvas.Pen.width := im.canvas.Pen.width *
else
if im.canvas.pen.width > 1 then
im.canvas.Pen.width := im.canvas.Pen.width
bZoom := not bZoom;
im.canvas.pen.color := pencol0;
im.canvas.brush.color := brushcol0;
end;
2);
div 2);
2
div 2;
Il faut encore signaler que l'épaisseur et la couleur du crayon (Pen) ainsi que la couleur
du pinceau (Brush) doivent être sauvegardés, car ils ne sont pas transmis
automatiquement lors de l'affectation d'un canvas à l'autre. De plus, lors du zoom avant,
la taille du crayon est multipliée par deux, afin qu'une ligne tracée sur l'image zoomée
soit encore visible lors du zoom arrière. Lors d'un zoom arrière l'épaisseur du crayon est
inversement divisée par deux. Cette opération permet de garder une certaine cohérence;
il n'est toutefois pas interdit de choisir explicitement une épaisseur de 1 pixel dans le
mode zoom.
__________________________________________________________________________________________
Le graphisme (JC Armici)
- 34 www.unvrai.com

Documents pareils