depuis que je touche à AutoIt, j'essaye de me former sur tous les types de scripts que l'on peut faire avec ce langage. Script de gestion de fichiers, script réseau, avec ou sans interface graphique... mais jamais je n'ai eu l'audace de m'attaquer au GDI+...
Bien c'est décidé, je m'attaque (ou plutôt je me suis attaqué) à cela, et je compte bien vous partagez l'expérience que je vais acquérir au fur et à mesure.
Comme vous pouvez vous en douter, ce tutoriel va être assez spécial puisque je ne maîtrise pas parfaitement le GDI+ dans toutes ses fonctionnalités. Aussi je vais vous demander de partager vos codes, vos projets (touchant à une spécificité du GDI+) ou même vos idées, afin que je puisse quand le temps me le permet, les placer dans le tutoriel voir même leur attribuer une partie.
Donc, ce dont vous pouvez attendre de ce tutoriel, c'est d'apprendre à coder proprement en utilisant GDI+, assimiler les bases de tracer avec quelques fonctions essentiels et je l'espère, plus encore !
Si vous êtes partant pour vous plonger dans l'aventure de ce merveilleux monde du GDI+, armez-vous de votre SciTE et allons partager idées, codes et surtout... effroyables problèmes !
I) Qu'est-ce que GDI+ ?
Avant de programmer quoique ce soit avec, il vaudrait mieux commencer par un chapitre de présentation du GDI+.
GDI+ pour Graphic Design Interface Plus va nous permettre de manipuler des images, dessiner des traits, des courbes, des cercles, des arcs mais aussi du texte, dans nos applications. GDI+ est signé Windows dont les fonctions se trouvent dans gdiplus.dll.
Pour des informations plus précises, voir l'article dédié dans la MSDN.
Une fois bien maîtrisée, nous pourrons nous permettre beaucoup de folies notamment dans nos interfaces graphiques. En effet nous serons en mesure de modifier des images (couleurs, inclinaison) ou bien animer des dessins un peu comme les écrans de veilles, bref, tout ce que l'on pourrait imaginer... Le principal problème sera très certainement de réaliser nos idées plutôt que de les trouver !
II) Les choses à savoir pour débuter !
Pour mettre à profit ce que nous propose GDI+, il va nous falloir dans un premier temps, ressortir nos vieux cours de mathématiques de notre tête et placards.
1) Au niveau des maths :
Voici ce qu'il faut à tout prix savoir mathématiquement pour mieux appréhender GDI+ :
- [IMPORTANT] Le plan : Un plan est dirigé par deux vecteurs directeurs (des flèches qui ne sont pas parallèles, qui se coupent à un endroit). Dans ce plan, pour pouvoir se repérer et y tracer des choses, on met en place un système de coordonnées.
Ainsi, à chaque intervention nous aurons à référencer ces coordonnées qui sont les suivantes :
x pour l'axe des abscisses (horizontal)
y pour l'axe des ordonnées (vertical) - [IMPORTANT] Un point : Un point est un objet (qui sera représenté pour nous par un pixel) pouvant être placé dans un plan et défini par deux coordonnées notées (x;y)
- [CONSEILLÉ] Un vecteur : Il s'agit d'un outil mathématiques représentant un déplacement. Un vecteur est défini par 4 choses :
De coordonnées (x;y) : elles symbolisent un déplacement de x unités sur l'axes des abscisses et y unités sur l'axe des ordonnées, partant d'un point quelconque.
D'une longueur : Qui est déterminée par la formule suivante : V(x²+y²) avec V pour racine carrée.
D'une direction : Elle peut-être représentée par l'extension de part et autre de la flèche.
D'un sens : Représenté par l'extrémité de la flèche.
Ci-dessous une image pour éclaircir un peu plus les choses.
Le GDI+ n'étant pas inclus dans les fonctions primaires, il va falloir inclure le fichier GDIPlus.au3.
Aussi, l'objectif étant de programmer proprement, il faut donc savoir que le GDI+ va devoir être initialisé en début de script et arrêté en fin de script un peu comme le TCP/IP ou UDP.
Code : Tout sélectionner
_GDIPlus_Startup() ; autorise l'utilisation de GDI+
_GDIPlus_Shutdown() ; met fin à l'utilisation de GDI+
- Un plan, qui est appelé "Graphique" et que l'on créera tout simplement dans une GUI préalablement créée de dimensions quelconques.
- D'un stylo d'une couleur quelconque
Code : Tout sélectionner
;~ on suppose que $GUI est une fenêtre créée
_GDIPlus_Startup() ; on active GDI+
$graphicHdl = _GDIPlus_GraphicsCreateFromHWND($GUI) ; on créé le plan à partir de la fenêtre
$redPenHdl = _GDIPlus_PenCreate(0xFFFF0000) ; on créé un stylo rouge (couleur au format ARGB)
;~...
_GDIPlus_GraphicsDispose() ; on détruit le plan
_GDIPlus_PenDispose() ; on détruit le stylo
_GDIPlus_Shutdown() ; on arrête GDI+
Vous pouvez maintenant partir sur un code propre de base, ci-dessous le mien.
III) Les fonctions essentielles de tracer !
Cette partie du tutoriel va être dédiée à ce qu'on pourrait appeler le dessin. En effet je vais essayer d'introduire un maximum de fonctions essentielles pour dessiner de choses simples, pour pouvoir par la suite, effectuer des choses plus complexes. On ne devrait pas avoir trop de soucis, la seule difficulté sera très probablement de savoir faire l'analogie entre un plan, notre interface de dessin, coordonnées et points etc...
1) Colorier un ou des pixels :
Comment changeons nous la couleur d'un pixel ?.. Bonne question. D'autant plus que cela peut s'avérer très utiles, en effet, une telle fonction pourrait nous permettre de pouvoir pointer des endroits dans notre GUI, pouvoir créer un logiciel de dessin pourquoi pas ?
Il est temps d'introduire notre première fonction de dessin :
Code : Tout sélectionner
_GDIPlus_GraphicsDrawRect( $hGraphics, $iX, $iY, $iWidth, $iHeight [, $hPen = 0] )
- $hGraphics : Vous l'avez très certainement compris, il s'agit du Handle du graphique que nous avons créé dans notre GUI.
- $iX : Il va s'agir des coordonnées en X (abscisse ou encore axe horizontal) du point que vous voulez placer.
- $iY : Il va s'agir des coordonnées en Y (ordonnée ou encore axe vertical) du point que vous voulez placer.
- $iWidth : Pour colorier un pixel (ou groupe de pixels), il vous faudra systématiquement mettre '1' comme argument.
- $iHeight : Pour colorier un pixel (ou un groupe de pixels), il vous faudra systématiquement mettre '1' comme argument.
- $hPen : Tout comme pour le graphique, il est facile de comprendre que vous donnerez ici comme argument le Handle du stylo.
Votre écran est composé de pixels, de très petits carrés ayant la caractéristique de pouvoir s'allumer de couleurs différentes, en combinant trois différentes couleurs : Rouge Vert Bleu. De plus, le GDI+ utilise par défaut le pixel comme unité. Maintenant vous devriez comprendre là où je veux en venir.
Dessiner un rectangle de longueur 1 pixel et de largeur 1 pixel, c'est tout simplement colorier un pixel.
Mais dans ces cas là, si je veux faire un point plus gros ? Et si je veux changer d'unité ? Il s'agit certainement d'une question que vous vous posez, bien c'est simple, pour y répondre, il faut jeter un oeil à notre stylo, car comme dans la réalité, notre stylo peut aussi avoir une mine de taille différente !
Code : Tout sélectionner
_GDIPlus_PenCreate ( [$iARGB = 0xFF000000 [, $iWidth = 1 [, $iUnit = 2]]] )
- $iARGB : Vous savez déjà, la couleur voulue au format A[lpha]R[ouge]V[ert]B[leu]
- $iWidth : la dimension de votre mine
- $iUnit : L'unité à prendre en compte
Code : Tout sélectionner
Case -7 ; constante d'un clique gauche
_GDIPlus_GraphicsDrawRect($graphicHdl, $nMsg[3], $nMsg[4], 1, 1, $redPenHdl)
2) Tracer un segment :
Il est temps pour nous d'apprendre à tracer un segment. Certes ce n'est pas trop reluisant, mais à partir de plusieurs segments, il est possible de créer des formes ! Des triangles mais aussi des polygones. Je suis certain que vous trouverez un tas d'applications à cette fonction.
Il est donc temps d'accueillir notre seconde fonction de dessin !
Code : Tout sélectionner
_GDIPlus_GraphicsDrawLine ( $hGraphics, $iX1, $iY1, $iX2, $iY2 [, $hPen = 0] )
- $Graphics : vous commencez par vous y habituez.
- $iX1 : Il va s'agir là des coordonnées en X du premier point
- $iY1 : Il va s'agir là des coordonnées en Y du premier point
- $iX2 : Il va s'agir là des coordonnées en X du second point
- $iY2 : Il va s'agir là des coordonnées en Y du second point
- $hPen : Le stylo vous le savez.
3) Tracer un rectangle :
Dans cette partie j'aimerais revenir à la fonction que nous avons tout juste utilisé pour colorier des pixels, mais cette fois-ci pour tracer un rectangle. Je vous représente notre fonction :
Code : Tout sélectionner
_GDIPlus_GraphicsDrawRect( $hGraphics, $iX, $iY, $iWidth, $iHeight [, $hPen = 0] )
- $iWidth : Cela va être la longueur du rectangle que l'on va former.
- $iHeight : Cela va être la largeur du rectangle que l'on va former.
Alors pour utiliser :
Code : Tout sélectionner
GDIPlus_GraphicsFillRect ( $hGraphics, $iX, $iY, $iWidth, $iHeight [, $hBrush = 0] )
Code : Tout sélectionner
_GDIPlus_BrushCreateSolid ( [$iARGB = 0xFF000000] )
La brosse est un objet, il va donc falloir la créer et la détruire. Elle prend comme argument uniquement une couleur au format ARGB.
La brosse va avoir un Handle, écrivons tout ça au même en droit où vous créez votre stylo :
Code : Tout sélectionner
$redBrushHdl = _GDIPlus_BrushCreateSolid(0xFFFF0000)
Code : Tout sélectionner
_GDIPlus_BrushDispose($redBrushHdl)
Donc en ce qui concerne notre rectangle plein, la fonction ne change pas trop par rapport à un simple contour, vous devez simplement faire attention à passer le Handle de la brosse et non du stylo pour $hBrush.
4) Tracer une élipse :
L'élipse est la dernière partie qui compose notre chapitre sur les fonctions de tracé, pour la bonne et simple raison qu'après ça, vous saurez utiliser toutes les fonctions élémentaires de dessin. A partir de toutes ces fonctions, vous serez en mesure de dessiner des choses de plus en plus complexes à conditions de passer du temps avec.
Mais avant de passer la fonction en revue, parlons (très) légèrement des élipses.
Il existe plusieurs méthodes pour tracer une élipse, on peut passer par son équation, par un cercle directeur ou bien par un rectangle. Avec AutoIt, nous travaillerons avec le rectangle inscrit. En effet, la courbure de l'élipse se fera en fonction des dimensions de votre rectangle. Ci-dessous une image pour illustrer mon charabia.
De plus vous êtes capables de remarquer lorsque la longueur est égale à la largeur, on obtient un cercle parfait. Le cercle est donc un cas particulier de l'élipse.
Passons donc en revue la fonction :
Code : Tout sélectionner
_GDIPlus_GraphicsDrawEllipse ( $hGraphics, $iX, $iY, $iWidth, $iHeight [, $hPen = 0] )
- $hGraphics : vous connaissez la chanson.
- $iX : L'abscisse du coin supérieur gauche du rectangle qui courbera l'élipse.
- $iY : L'ordonnée du coin supérieur gauche du rectangle qui courbera l'élipse.
- $iWidth : Longueur de ce rectangle.
- $iHeight : Largeur de ce rectangle.
- $hPen : Le handle de notre stylo.
Code : Tout sélectionner
_GDIPlus_GraphicsFillEllipse ( $hGraphics, $iX, $iY, $iWidth, $iHeight [, $hPen = 0] )
C'est terminé pour cette grande partie de dessin. Vous avez toutes les cartes en mains pour vous amusez un peu avec tout ça. La seconde partie portera sur la manipulation des images. Je ne sais pas encore comment tout va se passer puisque je n'ai pas encore essayé, mais une chose est sûre, les possibilités sont nombreuses, alors soyez patients !
IV) Manipulations du graphique et d'images
Comme vous l'avez compris, cette partie est entièrement consacrée à la manipulations des images et l'intégration de ces dernières dans nos interfaces graphiques. Cela va pouvoir nous donner de nombreuses possibilités, tant en matière de styles qu'en fonctionnalités. Si vous êtes prêts, alors n'attendons plus et parlons images !
1) Qu'est-ce qu'une image ?
Cette question peut vous paraître évidente, mais elle ne l'est pas tout à fait. Une image, quand vous l'affichez à l'écran, qu'est-ce que c'est ? Il s'agit tout simplement d'un agencement ordonné de pixels (vous vous rappelez de sa définition n'est-ce pas ?). L'image a une résolution (longueur et largeur) et une définition (longueur par largeur). Si la résolution est exprimée en pixels, alors la définition sera finalement le nombre de pixels qui composent l'image.
Une image peut être stockée dans votre ordinateur, ce n'est pas une surprise, tout comme le fait qu'il existe plusieurs extensions relatives aux images. En réalité, il existe deux types d'image :
- Image
- Bitmap
Une image de type Bitmap est une image dite matricielle, c'est à dire que grosso modo, pour chaque pixel composant l'image, la couleur est donnée. Ainsi quand l'ordinateur doit dessiner l'image, il doit tout simplement mettre la couleur à la place qui lui est indiquée. Les images de ce type sont donc plus lourdes, et leur poids augmentera quand leurs définition augmentera.
Bien sûr, les formats évoluent et on a pu les « compresser » pour avoir une même (ou presque) qualité que les non compressées en gagnant de la place.
Quand aux Images, on appelle ça des images vectorielles. Les extensions, vous connaissez très certainement le .SWF mais il y a aussi le .EMF bref, je présume que vous ne les manipulez pas consciemment tous les jours. Le poids de ces images va varier selon la complexité à convertir l'image de façon mathématique (vectorielle). Typiquement, si vous avez l'image d'un cercle rouge sur fond blanc, l'image sera sans doute beaucoup moins lourde en format vectoriel que matriciel, car le cercle est un objet mathématiques que l'on peut construire « facilement » avec des vecteurs.
Attention, tout au long de ce tutoriel, j'utiliserai donc quelques abus de langage, notamment je confondrai dans les mots bitmap et image. Mais il faut bien avoir dans l'esprit que ces deux types d'images sont différents, et que ces derniers représentent des classes par la même occasion dans GDI+.
2) Dessiner une image :
Dans un premier temps, pour ceux qui n'ont pas lu la partie II de ce tutoriel, recopiez le code que j'ai proposé pour partir sur une base sûre. Pour les autres, conservez votre code.
La fonction qui va nous permettre de dessiner notre image est la suivante :
Code : Tout sélectionner
_GDIPlus_GraphicsDrawImage ( $hGraphics, $hImage, $iX, $iY )
Code : Tout sélectionner
_GDIPlus_BitmapCreateFromFile ( $sFileName )
Code : Tout sélectionner
_GDIPlus_ImageLoadFromFile ( $sFileName )
Avant que vous ne vous hâtiez dans votre document "Mes Images" et que vous choisissiez celle que vous dessinerez dans votre fenêtre, il faut quand même que je vous prévienne : au même titre que la plupart des outils que nous avons manipulés jusqu'à maintenant, les images et les bitmaps sont des objets que l'on crée et que l'on détruit. C'est à dire avec :
Code : Tout sélectionner
_GDIPlus_BitmapDispose ( $hBitmap )
Code : Tout sélectionner
_GDIPlus_ImageDispose ( $hImage )
Pour synthétiser tout ça, faisons un rapide tour de ces fonctions et leurs arguments.
- $sFileName : Il s'agit du chemin complet de l'image, c'est à dire de la racine jusqu'à son nom comprenant son extension. Il est bien évidemment possible de construire avec des macros comme @ScriptDir par exemple.
- $hBitmap : Il s'agit du handle d'un bitmap que vous avez créé. Ce dernier est renvoyé par la fonction _GDIPlus_BitmapCreateFromFile dans notre exemple.
- $hImage : Il s'agit du handle d'une image que vous avez créée, et qui est renvoyé par la fonction _GDIPlus_ImageLoadFromFile
- $iX et $iY : Vous commencez à vous habituez de ces coordonnées, dans notre exemple elles représenteront l'endroit où l'on dessinera le coin supérieur gauche de l'image. En (0 ; 0), vous dessinerez l'image à partir du coin supérieur gauche de votre fenêtre.
3) Cloner une image chargée, dessiner une portion d'image :
Ici, il va être question de ne plus dessiner une image mais d'en dessiner une portion. Aussi, il nous sera utile pour plus tard de savoir comment cloner une image pour pouvoir par exemple modifier et afficher son clone, plutôt que de modifier l'image originale et de la recharger à chaque fois que l'on ne veut plus tenir compte des modifications opérées.
Ci-dessous, deux fonctions de clonage :
Code : Tout sélectionner
_GDIPlus_BitmapCreateFromHBITMAP ( $hBitmap [, $hPal = 0] )
_GDIPlus_BitmapCloneArea ( $hBitmap, $iLeft, $iTop, $iWidth, $iHeight [, $iFormat = 0x00021808] )
Code : Tout sélectionner
_GDIPlus_BitmapDispose ( $hBitmap )
- $hBitmap : celui-ci n'a plus aucun secret pour vous, il s'agit du handle d'un Bitmap, qui peut être donné par un _GDIPlus_BitmapCreateFromFile, _GDIPlus_BitmapCreateFromHBITMAP ou bien encore _GDIPlus_BitmapCloneArea.
- $iLeft : cet argument demande de combien de pixels doit se décaler vers la gauche la fonction pour commencer à copier (coordonnée en x dans l'image du point gauche supérieur du rectangle)
- $iTop : cet argument demande de combien de pixels doit se décaler vers le bas la fonction pour commencer à copier (coordonnée en y dans l'image du point gauche supérieur du rectangle)
- $iWidth : représente la longueur en pixels du rectangle de sélection de l'image à cloner
- $iHeight : représente la largeur en pixels du rectangle de sélection de l'image à cloner
- $hPal et $iFormat : je ne donnerai pas d'information sur ces arguments car je n'en ai pas encore trouvé une définition et utilité exacte. Par défaut, le format donné par $iFormat représente la constante $GDIP_PXF24RGB. Voir le fichier aide pour _GDIPlus_BitmapCloneArea et le fichier include GDIPlusConstants.
Code : Tout sélectionner
$hBitmap = _GDIPlus_BitmapCreateFromFile($sFileName)
$hClone = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, _GDIPlus_ImageGetWidth($hBitmap), _GDIPlus_ImageGetHeight($hBitmap))
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_BitmapDispose($hClone)
Bref, vous pouvez maintenant dessiner à l'aide de la fonction _GDIPlus_GraphicsDrawImage votre image, votre clone. Aussi, vous n'êtes pas obligé de ne copier qu'un rectangle, en utilisant des astuces comme l'on faisait en début de tutoriel avec _GDIPlus_GraphicsDrawRec, il est facile de copier un pixel spécifique d'une image, une ligne de pixel, une colonne etc...
Code : Tout sélectionner
$hClone = _GDIPlus_BitmapCloneArea($hBitmap,50,78,1,1) ; pixel de coordonnées (50 ; 78) dans l'image
$hClone = _GDIPlus_BitmapCloneArea($hBitmap,0,10,_GDIPlus_ImageGetWidth($hBitmap),1) ; dixième ligne de pixels de l'image
$hClone = _GDIPlus_BitmapCloneArea($hBitmap,2,0,1,_GDIPlus_ImageGetHeight($hBitmap)) ; deuxième colonne de pixels de l'image
Jusqu'à présent, pour créer notre zone de dessin, nous prenions notre GUI entière. Or, la plupart de nos programmes utilisent des GUI assez développées et qui contiennent donc des champs, des groupes, des arbres et j'en passe... en effet, si on veut dessiner sur une portion de la GUI, il paraît plus judicieux de créer un graphique à notre guise, plutôt que de finalement devoir dessiner manuellement des contrôles etc...
Comment fait-on ? Bien d'abord il nous faut un "control", un élément dans la GUI que l'on va "transformer" en zone de dessin, ou en tout cas sur le quel on appliquera une zone de dessin. Il est conseillé de prendre une image vide, mais sachez qu'il est aussi possible de prendre un bouton par exemple.
Ensuite, il faut récupérer le "handle" de l'élément créé. Et on passe tout simplement ce "handle" ci à la fonction qui crée le graphique plutôt que celui de la fenêtre. Ci-dessous un code qui fait tout ça, bon je ne me suis pas foulé stylistiquement parlant, mais ça fait ce que l'on veut, et c'est propre !
5) Du graphique au bitmap et du bitmap au fichier :
Qui à ses débuts dans l'ordinateur n'a jamais ouvert un logiciel tel que paint, fait un dessin, l'a sauvegardé puis la ré-ouvert un moment après ? Vous vous souvenez de votre graphique depuis le début du tutoriel, que vous créez, sur lequel vous dessinez, effacez puis qu'à la fin vous détruisez ? Hé bien il est possible d'en obtenir un handle de bitmap. Et vous savez quoi ? Il est également possible de créer une image à partir d'un bitmap. Ci-dessous une de mes créations !
- $hWnd : Alors, quel handle on vous demande ici ? Celui du graphique ? *buzz* Perdu ! En fait, le handle du graphique vous permet de dessiner, de faire plein de choses, mais il ne conserve aucune trace de dessins. L'élément dont on demande le handle est celui sur lequel vous avez fixé votre graphique. Ainsi, si vous avez utilisé le handle de votre GUI, alors ça sera celui-ci a passé en argument, si vous avez passé celui d'une image, ou d'un bouton, bien ce sera lui et non la GUI a passé en argument.
- $iWidth/$iHeight : Ce coup-ci pas de pièges, il s'agit de la taille de l'image que vous voulez créer, mais vu que vous avez en tête de sauvegarder votre graphique, il faut donner ses dimensions.
- $sFileName : Le chemin et le nom de votre image, sans oublier l'extension : .jpg, .bmp ou encore .png par exemple.
En résumé, pour obtenir un bitmap à partir du graphique, il faut utiliser la fonction ci-dessus. Et pour créer un fichier, à partir d'un bitmap, il faut utiliser la fonction :
Code : Tout sélectionner
_GDIPlus_ImageSaveToFile ( $hImage, $sFileName )
Oh non... c'est déjà la fin ! Oui mais non, c'est seulement la fin de cette grande partie. L'idée était de vous initier à la manipulation des images, des bitmap et du graphique. Mine de rien, vous êtes capables d'un tas de choses, rien qu'en arrivant ici dans ce tutoriel. Vous savez dessiner à la fois des formes, mais des images et donc dans des images. Avec ça, il y a un tas de projets intéressants qui peuvent pointer le bout de leur nez.
Donc comme vous l'avez compris, le tutoriel n'est pas encore terminé. Il y a un dernier aspect que j'aimerais voir, il s'agit de l'écriture de texte et la manipulation avancées des bitmap, permettant d'appliquer des effets... Je pense que c'est possible mais je n'ai encore rien pratiqué donc mystère ! J'espère que ce qui est présent ci-dessus vous a néanmoins aidé.
Je remercie UEZ et mikell pour l'aide à l'élaboration de ce tutoriel, qui n'est pas encore terminé !
