Office : Utilisation de la classe clGdiplus

Image non disponible

Dans cet article nous allons parcourir les principales fonctionnalités de clGdiplus.

clGdiplus est un module de classe VBA pour faciliter l'utilisation de la librairie graphique gdiplus.dll (GDI+).

Consultez la page dédiée à clGdiplus ; vous y trouverez la référence des méthodes et propriétés, ainsi que de nombreux tutoriels et exemples.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction à GDI+

GDI et GDI+ sont des librairies graphiques Windows.
Ce sont des fonctions que l'on peut appeler depuis nos programmes en ayant pris soin auparavant de les déclarer : on les connaît également sous le nom API (Application Programming Interface).

La librairie GDI est intégrée à toutes les versions de Windows, elle contient des fonctions de dessins basiques et ne sait gérer que le format Bitmap (BMP).

Pour pallier le manque de fonctionnalités de la librairie GDI, Microsoft a développé une autre librairie : GDI+ (ou GdiPlus).
Cette seconde librairie (fournie à partir de Windows XP) est beaucoup plus puissante et reconnaît en autre les formats Jpeg/Png/Gif et les données Exif intégrées aux images Jpeg, notamment par les appareils photos numériques.

Le module de classe clGdiPlus que nous utiliserons dans cet article encapsule ces API pour faciliter leur utilisation.

II. Exemples d'utilisations

Grâce aux fonctions de Gdi+ nous allons pouvoir faire du traitement d'images (redimensionnement, rotation, filtres…).

Il est également possible de dessiner des formes géométriques (lignes, ellipses, rectangles…) ou du texte.

Les images générées par Gdi+ peuvent être affichées dans des contrôles image (sur formulaire Access ou UserForm) ou sauvegardées dans un fichier.

Et enfin la création de régions va nous permettre d'ajouter de l'interactivité en déterminant par exemple où l'utilisateur clique sur l'image.

Il y a de nombreuses possibilités, y compris de programmer un jeu !

Image non disponible
Montage avec plusieurs images et du texte.
Image non disponible
Horloge analogique interactive.
Image non disponible
Carte de France interactive.
Image non disponible
Affichage d'images avec barres de défilement.
Image non disponible
Formulaire avec une forme personnalisée.
Image non disponible
Et des jeux !

Cet article ne couvre pas toutes les méthodes et propriétés de clGdiplus.

Il fait un tour d'horizon des fonctionnalités les plus importantes et permet de démarrer ses premiers développements avec Gdi+.

III. Installation et paramétrage

Il est nécessaire de posséder la librairie GDI+ de Microsoft.
Cette librairie est incorporée à Windows à partir de la version XP.
Sinon besoin la librairie est en téléchargement :
Lien vers la libraire en téléchargement sur Microsoft.com

Le fichier gdiplus.dll doit dans ce cas être placé dans le répertoire du fichier Office en développement.

Puis le module de classe suivant doit être importé dans le projet VBA : Télécharger le module clGdiplus

Ce module contient toutes les déclarations des API Gdi+ ainsi que de nombreuses fonctions avancées plus pratiques que les fonctions de base.

Si vous ne l'avez pas déjà fait, créez un fichier Access, Word, Excel ou Powerpoint.
Vous pouvez ensuite accéder au projet VBA par le raccourci ALT + F11.

Pour importer le module de classe clGdiplus.cls, allez dans le menu Fichier => Importer un fichier….

Ensuite il faut mettre à jour une constante de compilation dans le module clGdiplus importé.
Recherchez la ligne :

 
Sélectionnez
#Const AppName = "A"

Et remplacez la valeur A par l'initiale de l'application hôte :

  • A pour Access ;
  • W pour Word ;
  • E pour Excel ;
  • P pour Powerpoint.

Pour l'article je crée un classeur Excel ; je modifie donc la constante dans le module clGdiPlus pour lui donner la valeur E.

 
Sélectionnez
#Const AppName = "E"

L'article s'applique également aux autres applications Office, remplacez la valeur en fonction de l'application que vous utilisée.

Sauf pour Access, la référence à Microsoft Forms 2.0 Object Library est requise.
Vous pouvez ajouter cette référence dans le menu Outils => Références….
Sinon si vous ne la trouvez pas, cette référence sera ajoutée automatiquement lors de la création d'un UserForm.

Vérifier enfin que tout est correct en compilant : menu Débogage => Compiler….

Vous êtes maintenant prêt à utiliser Gdi+.

IV. Système de coordonnées

Il est important de savoir que l'origine pour Gdi+ est le coin en haut à gauche de l'image.
Un pixel en position (0,0) est donc en haut à gauche.
Le pixel en bas à droite a pour coordonnées (largeur de l'image -1, hauteur de l'image -1), soit (oGdi.ImageWidth -1, oGdi.ImageHeight - 1).

V. Traitement d'images

VBA ne sait pas nativement modifier les fichiers image.
Avec Gdi+, cela va être simple.

Créez un nouveau module : menu Insertion => Module.
Si ce n'est pas déjà fait, ajoutez en tout début de module l'instruction Open Explicit.
Cette instruction qui rend nécessaire la déclaration de toutes les variables utilisées permet d'éviter de nombreuses erreurs de saisie.

Dans le menu Outils => Options, onglet Editeur : cochez Déclaration des variables obligatoire pour que cette instruction soit automatiquement ajoutée dans chaque nouveau module.

Créez une procédure nommée par exemple TestImage :

 
Sélectionnez
Option Explicit
Sub TestImage()
	Dim oGdi As clGdiplus
	Set oGdi = New clGdiplus
End Sub

Dans cette procédure on commence par déclarer l'objet oGdi de type clGdiplus.
Mais il ne suffit pas de déclarer l'objet, il faut ensuite l'instancier avec Set et New.

Évitez de déclarer l'objet avec le mot-clé New (Dim oGdi As New clGdiplus).

En effet en déclarant l'objet de cette manière, VBA va vérifier à chaque appel de l'objet s'il a bien été créé et cela ralenti l'exécution.

Préférez la déclaration et l'instanciation de l'objet en deux temps.

V-A. Chargement d'un fichier

Choisissez maintenant un fichier image.
Gdi+ supporte notamment les formats BMP, GIF, JPEG, PNG, TIFF et EMF.
Pour l'exemple je choisis une photo JPEG que je copie dans le répertoire de mon fichier Office.
Pour charger l'image du PC, on utilise la fonction LoadFile.

 
Sélectionnez
Dim oGdi As clGdiplus
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "P1010940.jpg") Then
    ' Chargement réussi
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If

Mon image étant dans le même dossier que mon fichier Excel, j'utilise la fonction ApplicationPath qui est dépendante de l'application utilisée.
Pour mon fichier Excel j'obtiens ThisWorkbook.Path (+ un antislash final).
Dans une application Access par exemple, la valeur serait celle de CurrentProject.Path (+ un antislash final).
Ainsi on peut utiliser des chemins relatifs sans avoir besoin de modifier le code en fonction de l'application ciblée.
Notez qu'il est bien sûr également possible d'utiliser un chemin absolu du type C:\Documents and Settings\monuser\Mes documents\monimage.jpg.
Ou même un chemin réseau du type \\serveur.fr\images\monimage.jpg.

La fonction de chargement, comme beaucoup de fonctions de clGdiplus, retourne Vrai si elle s'est correctement exécutée.
Il convient de vérifier ce retour pour continuer l'exécution ou afficher un message d'erreur par exemple.

Notez que la fonction LoadFile accepte deux autres paramètres :

  • pThumbNail pour charger la miniature EXIF ;
  • pIcon pour charger l'icône associée au fichier.

Nous verrons l'utilisation de ces deux paramètres dans le chapitre suivant.

V-B. Lecture d'informations

Une fois l'image chargée, on peut commencer par lire ses informations.

Les informations les plus courantes sont :

  • le format de l'image ;
  • la largeur de l'image ;
  • la hauteur de l'image.

Ces informations sont respectivement accessibles par les propriétés ImageFormat (ou ImageFormatText), ImageWidth et ImageHeight.

 
Sélectionnez
Dim oGdi As clGdiplus
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "P1010940.jpg") Then
    ' Chargement réussi
    Debug.Print "Format :", oGdi.ImageFormatText
    Debug.Print "Taille :", oGdi.ImageWidth & " x " & oGdi.ImageHeight    
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If

Pour afficher des informations lors du déroulement du code, on utilise Debug.Print pour écrire dans la fenêtre d'exécution.
Si elle n'est pas affichée, vous la trouverez dans le menu Affichage => Fenêtre Exécution.

Le code précédent affiche par exemple :

Format : JPEG
Taille : 3264 x 2448

Le format de l'image est JPEG, ce qui est logique lors d'un chargement de fichier jpg.
Quant à la taille elle est donnée en pixels.

Utilisons maintenant les paramètres supplémentaires de la fonction LoadFile.

C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\images\image1.gif Avec pThumbnail pour chargement de la miniature EXIF :

 
Sélectionnez
[...]
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "P1010940.jpg", True) Then
[...]
Format : JPEG
Taille : 160 x 120

On a donc chargé une image JPEG de faible taille.
Cette option est très pratique pour rapidement charger et afficher des miniatures de photos.
Les appareils photos utilisent cette miniature EXIF, ainsi que l'explorateur Windows.
Si l'image ne contient pas de miniature EXIF, alors l'image complète est chargée.

C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\images\image1.gif Ou avec pIcon pour chargement de l'icône associée :

 
Sélectionnez
[...]
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "P1010940.jpg", , GdipLargeIcon) Then
[...]
Format : MemoryBMP
Taille : 32 x 32

On a ici chargé une image de 32 pixels (GdipSmallIcon donnerait une image de 16 pixels).
Notez que ce n'est pas une vrai icone, c'est un format BMP (il y a une conversion de format par clGdiplus lors de la recherche de l'icône associée au fichier).

Pour une image JPEG (ou TIFF), on peut également accéder aux données EXIF.

Consultez le tutoriel dédié à la gestion des données EXIF.

V-C. Sauvegarde de l'image

Pour pouvoir observer les modifications appliquées à l'image, nous allons ajouter une sauvegarde à l'aide la fonction SaveFile.

 
Sélectionnez
Sub TestImage()
Dim oGdi As clGdiplus
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "P1010940.jpg") Then
    ' Chargement réussi
    Debug.Print "Format :", oGdi.ImageFormatText
    Debug.Print "Taille :", oGdi.ImageWidth & " x " & oGdi.ImageHeight
    ' Sauvegarde de l'image
    If oGdi.SaveFile(oGdi.ApplicationPath & "tuto.jpg") Then
        Debug.Print "Sauvegarde OK"
    Else
        Debug.Print "Sauvegarde KO"
    End If
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If
End Sub

Ici on sauvegarde une image au format JPEG.
Les différents formats de sauvegarde possibles sont : BMP, EMF, JPEG, PNG, GIF et TIFF.
Si le paramètre pFormat n'est pas précisé (comme c'est le cas ici), le format est déterminé en fonction de l'extension du fichier cible.

Le format BMP est volumineux mais n'altère pas l'image.
Le format JPEG est peu volumineux mais altère l'image.
Le format PNG est un bon compromis pour des images ayant des zones unies (donc généralement pas pour des photographies) et supporte la transparence.
Évitez le format GIF qui ne donne pas de bons résultats avec Gdi+.

Notez la possibilité pour le format JPEG de préciser une qualité de compression entre 0 et 100, à l'aide du troisième paramètre pQuality de la fonction SaveFile.
Par défaut ce paramètre de qualité est à 75.
Ici le paramètre n'est pas précisé, mais aucune compression n'a eu lieu.
L'image n'ayant pas été modifiée, elle n'a pas été altérée.
En regardant de plus près les données dans le fichier (avec un éditeur hexa par exemple) on s'aperçoit qu'il est identique à l'original, à quelques données d'en-tête près.

Maintenant on va modifier l'image.

V-D. Modification de l'image

Les modifications que l'on peut appliquer à l'image sont :

  • les transformations (redimensionnement, recadrage, rotation…) ;
  • la modification des couleurs ;
  • le dessin de formes (rectangle, ellipse…) ou de textes.

Pour illustrer ce chapitre, nous utilisons comme image source le logo de la FAQ Access :

Image non disponible

Cette image au format GIF a un fond transparent.
L'enregistrement au format GIF n'étant pas très bien géré par Gdi+ (perte de couleurs), nous allons la sauvegarder dans un autre format.
Si on charge l'image et qu'on la sauvegarde ensuite au format JPEG, on perd la transparence.

Image non disponible

Pour obtenir de la transparence, il faut le préciser avec la propriété HasTransparency et sauvegarder dans un format qui gère la transparence (PNG par exemple).

 
Sélectionnez
Sub TestImage()
Dim oGdi As clGdiplus
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "faqaccess.gif") Then
    ' Active la transparence
    oGdi.HasTransparency = True
    ' Sauvegarde de l'image au format png
    If oGdi.SaveFile(oGdi.ApplicationPath & "tuto.png") Then
        Debug.Print "Sauvegarde OK"
    Else
        Debug.Print "Sauvegarde KO"
    End If
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If
End Sub

On obtient bien la transparence avec le format PNG.

Image non disponible

V-D-1. Transformations

Toutes les transformations de ce chapitre créent une nouvelle image.
Si vous travaillez sur un fichier JPEG contenant des données EXIF, celles-ci ne seront recopiées dans la nouvelle image, sauf si vous passez en paramètre pKeepExifData la valeur True.
Si vous modifiez un fichier JPEG, celui-ci sera à nouveau compressé et l'image s'en trouvera altérée (sauf utilisation de SaveTransformFile).

Ces codes de transformation de l'image seront à écrire juste avant l'instruction de sauvegarde (fonction SaveFile).

Image non disponible Le redimensionnement :
Il est possible de modifier la taille de l'image avec les fonctions Resize ou ScaleI.
Resize défini une nouvelle taille en pixels :

 
Sélectionnez
oGdi.Resize 60

Le code précédent redimensionne l'image à 60 pixels en largeur.
La hauteur n'étant pas précisée, elle sera calculée en fonction de la nouvelle largeur pour conserver les proportions de l'image originale.

Image non disponible

ScaleI défini un facteur de redimensionnement :

 
Sélectionnez
oGdi.ScaleI 0.5, 0.5

Le code précédent redimensionne l'image à 50% de sa taille sur les deux dimensions.

Image non disponible


Image non disponible Le recadrage :

La fonction Crop permet de recadrer l'image :

 
Sélectionnez
oGdi.Crop 50, 40, 80, 70

Le code précédent extrait une portion de l'image originale selon les coordonnées précisées.

Image non disponible


Image non disponible La rotation :

La fonction Rotate permet de tourner l'image d'un angle quelconque :

 
Sélectionnez
oGdi.Rotate 30

Le code précédent tourne l'image d'un angle de 30° (vers la droite car positif).

L'image résultante est un rectangle contenant l'image tournée.
Il y a donc des zones non remplies par l'image après rotation.
Ces zones sont noires et transparentes.
La transparence est obtenue car on a précisé la propriété HasTransparency = True et sauvegardé dans un format qui gère la transparence (PNG).

Image non disponible

Image non disponible Sont également disponibles des transformations standards pour les rotations multiples de 90° et les retournements (flip) de l'image.

Utilisez pour cela la fonction RotateFlip :

 
Sélectionnez
oGdi.RotateFlip Rotate90FlipY

Le code précédent tourne l'image d'un angle de 90° (vers la droite) et retourne l'image sur l'axe vertical.

Image non disponible

Image non disponible Il reste une dernière subtilité pour les images au format JPEG (la plupart des photographies de nos appareils), la transformation sans perte.

En effet si vous souhaitez tourner une photo de 90° par exemple et que vous sauvegardez ensuite l'image, celle-ci est altérée par une compression.
La fonction SaveTransformFile permet de transformer une image et la sauvegarder sans perte.
Les transformations sont toutefois limitées aux rotations multiples de 90° et aux retournements (flip).
De plus la largeur et la hauteur de l'image doivent toutes deux être des multiples de 16 pixels.

 
Sélectionnez
oGdi.SaveTransformFile(oGdi.ApplicationPath & "tuto.jpg", EncoderValueTransformRotate90)

Le code précédent tourne l'image de 90° et la sauvegarde sans altérer les données de l'image.
C'est-à-dire que l'image sera tournée mais il n'y aura pas nouvelle compression JPEG.

V-D-2. Couleurs

Image non disponible La fonction ReplaceColor remplace une couleur par une autre dans l'image.

 
Sélectionnez
oGdi.ReplaceColor RGB(164, 192, 239), vbGreen

Le code précédent remplace la couleur de composantes RGB (164, 192, 239) par du vert.
J'ai lu les composantes de la couleur avec un logiciel tiers (msPaint).
On peut également lire la couleur d'un pixel à partir de ses coordonnées (fonction GetPixel).

 
Sélectionnez
oGdi.ReplaceColor oGdi.GetPixel(50, 100), vbGreen
Image non disponible

Les troisièmes et quatrièmes paramètres définissent les transparences des couleurs.

On peut donc remplacer par une couleur transparente :

 
Sélectionnez
oGdi.ReplaceColor oGdi.GetPixel(50, 100), vbGreen, , 0
Image non disponible

Notez également les paramètres pX1, pY1, pX2, pY2 qui définissent un rectangle qui limite la zone à traiter.

Image non disponible La fonction ApplyColorMatrix applique une matrice de couleur à l'image.

Cette matrice est divisée en 4 paramètres : pRed, pGreen, pBlue et pAlpha.
Chacun de ces paramètres est un tableau de 5 valeurs comprises entre 0 et 1.
Les 4 premières valeurs sont les multiplicateurs respectivement des composantes Rouge, Vert, Bleu et Alpha (transparence) du pixel.
La 5e valeur est une constante à ajouter (1 étant la valeur maximum).

Pour l'exemple, voici un appel à cette fonction … qui ne fait aucune modification :

Matrice identité
Sélectionnez
oGdi.ApplyColorMatrix Array(1, 0, 0, 0, 0), _
                                Array(0, 1, 0, 0, 0), _
                                Array(0, 0, 1, 0, 0), _
                                Array(0, 0, 0, 1, 0)

En effet :

  • le paramètre pRed est Array(1, 0, 0, 0, 0), c'est-à-dire qu'on multiplie la composante rouge par 1 ;
  • le paramètre pGreen est Array(0, 1, 0, 0, 0), c'est-à-dire qu'on multiplie la composante verte par 1 ;
  • le paramètre pBlue est Array(0, 0, 1, 0, 0), c'est-à-dire qu'on multiplie la composante bleue par 1 ;
  • le paramètre pAlpha est Array(0, 0, 0, 1, 0), c'est-à-dire qu'on multiplie la composante alpha par 1 ;

Un autre filtre :

Inverse la transparence
Sélectionnez
oGdi.ApplyColorMatrix Array(1, 0, 0, 0, 0), _
                          Array(0, 1, 0, 0, 0), _
                          Array(0, 0, 1, 0, 0), _
                          Array(0, 0, 0, -1, 1)

Ici on multiplie la composante alpha par -1 et on lui ajoute 1.
Donc pour une couleur transparente, la composante alpha est 0 et est transformée en 1.
Pour une couleur opaque, la composante alpha est 1 et est transformée en 0.
Ce qui était transparent devient opaque et vice-versa.

Image non disponible

Et comme on peut combiner les couleurs entre elles, on peut écrire un filtre noir et blanc.

Appliquer un filtre noir et blanc
Sélectionnez
oGdi.ApplyColorMatrix Array(0.222, 0.707, 0.071, 0, 0), _
                                Array(0.222, 0.707, 0.071, 0, 0), _
                                Array(0.222, 0.707, 0.071, 0, 0), _
                                Array(0, 0, 0, 1, 0)

Les nuances de gris sont obtenues en multipliant chacune des composantes de manière à ce qu'il y ait autant de rouge, de vert et de bleu dans chaque pixel.
En effet : 0.222 + 0.707 + 0.071 = 1

On aurait pu utiliser des multiplicateurs 0.333 par exemple, mais les valeurs ci-dessus donnent de meilleurs résultats.

Image non disponible

Image non disponible Si les modifications de couleurs souhaitées ne sont pas possibles avec une matrice, vous pouvez utiliser les fonctions SavePixels et LoadPixels.

SavePixels permet de récupérer les couleurs des pixels dans un tableau.
Ce tableau peut alors être modifié puis réinjecté dans l'image avec la fonction LoadPixel.

 
Sélectionnez
' Tableau pour recevoir les pixels
    Dim lPixels() As Byte
    ' Compteurs
    Dim lCptX As Long, lCptY As Long
    ' Lecture des pixels de l'image
    lPixels = oGdi.SavePixels
    ' Boucle sur les pixels
    For lCptX = LBound(lPixels(), 2) To UBound(lPixels(), 2)
        For lCptY = LBound(lPixels(), 3) To UBound(lPixels(), 3)
            ' Composante bleue = lPixels(1, lCptX, lCptY)
            ' Composante verte = lPixels(2, lCptX, lCptY)
            ' Composante rouge = lPixels(3, lCptX, lCptY)
            ' Composante alpha = lPixels(4, lCptX, lCptY)
            If lCptX > lCptY Then lPixels(3, lCptX, lCptY) = 255
        Next
    Next
    ' Réinjecte les pixels dans l'image
    oGdi.LoadPixels lPixels

lPixels est un tableau à 4 dimensions retourné par la fonction SavePixels.
Quel que soit le format de l'image, il y a toujours 3 composantes de couleur (rouge, vert et bleu) et une composante alpha. Si l'image ne gère pas la transparence alors cette composante est ignorée.
Les valeurs vont de 0 à 255.

Il suffit alors de parcourir le tableau comme dans l'exemple ci-dessus et remplacer les valeurs de chaque composante.

Dans l'exemple on remplace la composante Rouge par 255 (c'est-à-dire le plus de rouge possible) pour les pixels dont X est supérieur à Y.
On conserve intact les autres composantes de la couleur.

Une fois les pixels modifiés dans le tableau, on le réinjecte dans l'image avec LoadPixels.
On obtient une image passée dans un filtre rouge sur la moitié en haut à droite :

Image non disponible

Si possible, utilisez la fonction ApplyColorMatrix qui est plus rapide.

V-D-3. Dessin

Dans ce chapitre nous allons dessiner sur l'image : des rectangles, des ellipses, des polygones, du texte…

Les noms des fonctions de dessin de clGdiplus commencent par Draw, par exemple Drawline, DrawRectangle

V-D-3-a. Précision : Long ou Single

Dans le module de classe clGdiplus, vous trouverez une constante de compilation #Const UseSingle

Cette constante permet de définir la précision des coordonnées utilisées : Long (entier) ou Single (virgule flottante).

Donnez à cette constante la valeur True pour utiliser des coordonnées Single ou False pour des valeurs Long.

C'est-à-dire qu'en précision Long, les coordonnées seront des entiers, alors qu'en précision Single les cordonnées pourront être des nombres avec décimales.

Cependant, l'unité de base d'une image reste le pixel que vous voyez à l'écran.
Il n'est pas possible de dessiner réellement à une position de 10.3 pixels par exemple ; l'utilisation de coordonnées avec décimales induit un mélange des pixels pour faire croire à une précision supérieure.

En précision Single, le dessin est un peu plus lent mais plus précis.

Pour obtenir de bons résultats avec des Single, définissez la propriété SmoothingMode à GdipSmoothingAntialias ou GdipSmoothingHighQuality.

Sinon par défaut l'effet de lissage n'est pas présent.

Sur l'image ci-dessous nous pouvons voir comment Gdi+ procède pour dessiner un carré décalé de un dixième de pixel à chaque image.

Image non disponible

À chaque image il y a un mélange entre le noir, le rouge, et le fond blanc.

À la dixième image, le carré est complément décalé de un pixel.

Si on affiche un tel déplacement à l'écran, l'œil ne verra pas de saccades car les positions intermédiaires nous trompent et nous font penser que le carré est déplacé d'un dixième de pixel à chaque image.

Avec des coordonnées entières, le déplacement est beaucoup moins fluide.

Dans le cas de l'affichage d'un défilement à l'écran, il faut évidemment trouver un juste milieu entre fluidité et rapidité.

V-D-3-b. Dessin de formes

Image non disponible Commençons par dessiner tout simplement une ligne :

 
Sélectionnez
oGdi.DrawLine 10, 10, 150, 100, vbRed, 10

Ce code dessine une ligne rouge de 10 pixels de large entre les points de coordonnées (10,10) et (150,100).

Image non disponible

Le dessin de lignes n'est pas lissé par défaut. Pour appliquer un lissage, définissez la propriété SmoothingMode.

 
Sélectionnez
oGdi.SmoothingMode = GdipSmoothingAntialias
oGdi.DrawLine 10, 10, 150, 100, vbRed, 10 ', GdipDashDASH
Image non disponible

Pour chaque dessin à base de lignes, vous pouvez définir plusieurs paramètres à l'appel des fonctions :

  • pDash pour lignes pointillées ;
  • pAlpha pour des lignes transparentes.
 
Sélectionnez
oGdi.SmoothingMode = GdipSmoothingAntialias
oGdi.DrawLine 10, 10, 150, 100, vbRed, 10, GdipDashDASH

Et voici une ligne pointillée avec une transparence de 150 (sur un maximum de 255).

Image non disponible

Et toujours pour chaque dessin à base de lignes, vous pouvez définir plusieurs propriétés (à définir une fois pour toutes les fonctions), notamment :

  • LineStart pour le type de départ de ligne (rond, carré, flèche…) ;
  • LineEnd pour le type de fin de ligne ;
  • LineJoin pour le type de jointure entre chaque ligne.

Par exemple pour une fin de ligne en flèche pleine de taille 3 (c'est-à-dire 3 fois l'épaisseur du trait) :

 
Sélectionnez
oGdi.LineEnd = LineCapArrowAnchor
oGdi.LineArrowLength = 3
oGdi.LineArrowFill = True
oGdi.DrawLine 10, 10, 150, 100, vbRed, 10
Image non disponible

LineJoin est inutile dans le cas d'un dessin d'une unique ligne.
Il sera utile pour dessiner des rectangles, des polygones…

Image non disponible Le dessin d'un rectangle est tout aussi facile :

Par exemple pour un rectangle rouge dessiner avec un trait rouge de 10 pixels :

 
Sélectionnez
oGdi.DrawRectangle 10, 10, 150, 100, , vbRed, 10
Image non disponible

Il est ensuite facile de colorer l'intérieur du rectangle, de jaune par exemple :

 
Sélectionnez
oGdi.DrawRectangle 10, 10, 150, 100, vbYellow, vbRed, 10
Image non disponible

Vous vous en doutez, il est possible de rendre transparent le rectangle :

 
Sélectionnez
oGdi.DrawRectangle 10, 10, 150, 100, vbYellow, vbRed, 10, , 150
Image non disponible

Pour dessiner le cadre opaque et l'intérieur transparent, il faut dessiner en deux temps :

  • d'abord l'intérieur en précisant une épaisseur de trait à zéro ;
  • ensuite le cadre en ne précisant pas de couleur de fond.

Image non disponible Et pour un dessin de rectangle avec des coins arrondis :

 
Sélectionnez
oGdi.DrawRoundRectangle 10, 10, 150, 100, 30, 30, vbYellow, vbRed, 10, , 150
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Les paramètres à 30 définissent le rayon des arrondis.

Image non disponible Poursuivons avec le dessin d'une ellipse :

Ce code dessine une ellipse qui s'inscrit dans un rectangle de coordonnées 10, 10, 150, 100.

 
Sélectionnez
oGdi.DrawEllipse 10, 10, 150, 100, TypeEllipseRectangle, vbRed, vbBlue, 5, GdipDashDASH, , , 150
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Et le code suivant dessine une ellipse de centre 100, 50 et de rayons 60 et 40.

 
Sélectionnez
oGdi.DrawEllipse 100, 50, 60, 40, TypeEllipseCenter, vbRed, vbBlue, 5, GdipDashDASH, , , 150
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Image non disponible Un peu plus complexe avec le dessin d'une courbe de Bézier :

Une courbe de Bézier nécessite au minimum quatre points : le point de départ, deux points de contrôle et un point d'arrivée.

La courbe ne passe pas par les points de contrôle.

 
Sélectionnez
oGdi.DrawCurve Array(20, 60, 50, 10, 80, 100, 150, 60), , vbBlue, 5
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Sur cette image, la courbe de Bézier est en bleu.

J'ai ajouté les points en rouge et les lignes joignant ces points en noir.

Notez comme la courbe s'applique à être tangente au segment entre les points.

Image non disponible Il est également possible de dessiner une courbe cardinale :

Cette courbe passe par tous les points définis.

 
Sélectionnez
oGdi.DrawCardinal Array(20, 60, 50, 10, 80, 100, 150, 60), 0.7, , vbBlue, 5
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

J'ai ici aussi ajouté les points en rouge et les lignes joignant ces points en noir.

Le deuxième paramètre est la tension de la courbe, par défaut égale à 0,5.

Plus la tension est grande, plus la courbe sera arrondie.

Une tension à zéro dessine des segments droits joignant les points.

Image non disponible Pour dessiner un polygone :

 
Sélectionnez
oGdi.DrawPolygon Array(20, 60, 50, 10, 80, 100, 150, 60), , vbBlue, 5
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

C'est ici que la propriété LineJoin est utile.

Avec par exemple oGdi.LineJoin = LineJoinRound, on obtient des angles arrondis.

C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

V-D-3-c. Dessin de texte

Afficher des informations sur l'image peut être utile, la méthode DrawText est faite pour cela.

Le code suivant dessine du texte avec une police de caractères Arial (la police est optionnelle) de taille 20.

Le texte est ici positionné centré en position 100, 50.

Pour modifier l'alignement, modifier les paramètres pAlignHoriz et pAlignVert.

 
Sélectionnez
oGdi.DrawText "Essais gdi+", 20, "Arial", 100, 50
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Pour mieux voir le texte vous pouvez ajouter un fond de couleur :

 
Sélectionnez
oGdi.DrawText "Essais gdi+", 20, "Arial", 100, 50, , , , , , , vbRed, 150
C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

Si vous précisez les deux autres coordonnées, le texte est alors positionné dans le rectangle défini.

 
Sélectionnez
oGdi.DrawText "Essais gdi+", 20, "Arial", 50, 50, 150, 100, , , , , , vbRed, 150
Image non disponible

J'ai ajouté ici le rectangle de positionnement en noir qui ne s'affiche pas automatiquement.

Notez bien que le surlignage du texte ne se fait que sur la zone écrite et pas sur tout le rectangle.

Le texte étant trop grand, il y a un retour à la ligne automatique.

Si on agrandit le texte (taille 40 par exemple), il ne tient plus dans le rectangle :

Utilisez les propriétés MinTextSize et/ou MaxTextSize pour définir une taille minimale ou maximale du texte.

 
Sélectionnez
oGdi.MinTextSize = 5
oGdi.DrawText "Essais gdi+", 40, "Arial", 50, 50, 150, 100, , , , , vbRed, 150

Le texte est dimensionné pour tenir dans le rectangle.

C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\faqaccess.png

La propriété LastTextSize contient alors la taille utilisée pour dessiner le texte.

Notez également les propriétés LastTextRight et LastTextBottom qui contiennent la dernière position droite et base du texte et sont utiles pour dessiner du texte à la suite d'un autre.

La méthode DrawText a également des paramètres pour écrire en gras, italique, souligné ou barré.

VI. Charger une image

Nous avons vu précédemment la méthode LoadFile pour charger une image à partir d'un fichier stocké sur le PC.

Il est également possible de charger d'autres images.

Image non disponible LoadControl charge l'image contenue dans un contrôle.

Charge le contenu du contrôle Image0
Sélectionnez
oGdi.LoadControl Me.Image0

Cette méthode peut être utile pour charger une image (qui peut être invisible) intégrée dans un formulaire au lieu de la stocker dans un fichier externe.

Image non disponible LoadPixels charge un tableau de couleurs ARGB.

Création d'une image en définissant chaque pixel
Sélectionnez
Dim oGdi As clGdiplus
Dim lPixels() As Byte
Dim lX As Long, lY As Long
' Objet Gdi+
Set oGdi = New clGdiplus
' Tableau de pixels
' 4 composantes Bleu, Vert, Rouge, Alpha
' 20 de largeur, 20 de hauteur
ReDim lPixels(1 To 4, 1 To 20, 1 To 20)
' Par défaut les pixels sont noirs transparents
' Parcours des pixels
For lX = 1 To 20
    For lY = 1 To 20
        ' Si pixel sur la diagonale haut-gauche => bas-droite
        If lX = lY Then
            ' Point Bleu avec Alpha = 255 pour opaque
            lPixels(1, lX, lY) = 255 ' Bleu
            lPixels(2, lX, lY) = 0 ' Vert
            lPixels(3, lX, lY) = 0 ' Rouge
            lPixels(4, lX, lY) = 255 ' Alpha
        End If
    Next
Next
' Charge les pixels dans une image
oGdi.LoadPixels lPixels

Le code précédent génère une image avec une ligne bleue en diagonale :C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\tuto.png

Image non disponible LoadArray charge l'image à partir d'un tableau binaire (tableau de bytes) ou d'une pièce-jointe.

Par tableau binaire on entend un tableau de bytes qui contient l'image telle que stockée dans un fichier, au format JPEG, PNG…

Cette fonction est notamment utile pour charger une image stockée dans une colonne de type pièce-jointe (Access >= 2007).

Charge une image à partir d'un champ pièce-jointe
Sélectionnez
' Timages est le nom de la table
' data est le champ pièce-jointe
' id est un champ texte identifiant de l'image
oGdi.LoadArray DLookup("data.filedata", "Timages", "id=""btnGIF"""), True

Image non disponible LoadPictureMask charge un bitmap (et un mask) Gdi32.

Cette méthode est particulièrement utile pour charger des images des menus ou du ruban.

Chargement d'une image du ruban
Sélectionnez
Dim oGdi As clGdiplus
Dim oCmdBars As Office.CommandBars
Dim oImg As Object
' Objet barres de commande
Set oCmdBars = Application.CommandBars
' Objet Gdi+
Set oGdi = New clGdiplus
' Récupère l'image du bouton de ruban "ReviewDeleteComment"
Set oImg = oCmdBars.GetImageMso("ReviewDeleteComment", 32, 32)
' Si image OK
If Not oImg Is Nothing Then
    ' Charge l'image dans clGdiplus
    oGdi.LoadPictureMask oImg.handle
End If

Avec le code précédent, on a chargé l'image du bouton du ruban « ReviewDeleteComment » : C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\tuto.png

Chargement d'une image du menu (anciens menus)
Sélectionnez
Dim oGdi As clGdiplus
Dim oCmdBars As Office.CommandBarButton
Dim oImg As Object
' Objet barres de commande
Set oCmdBars = Application.CommandBars("Worksheet Menu Bar").Controls("Fichier").Controls("&Nouveau...")
' Objet Gdi+
Set oGdi = New clGdiplus
' Si image OK
If Not oCmdBars Is Nothing Then
    ' Charge l'image dans clGdiplus
    oGdi.LoadPictureMask oCmdBars.Picture, oCmdBars.Mask
End If

Avec le code précédent, on a chargé l'image du bouton « Nouveau… » du menu « Fichier » du menu d'Excel : C:\Documents and Settings\u71e039\Mes documents\personnel\Perso\DVP\kitword\kit\documents\tutoclgdiplus\work\tuto.png

Image non disponible LoadBitmap charge un bitmap Gdi+ (peut être utile pour cloner une image d'une instance de clGdiplus vers une autre par exemple).

VII. Créer une image vierge

On a vu comment charger une image.

Il est souvent utile de créer une image vierge de dimensions précises.

Les méthodes CreateBitmap* sont faite pour cela.

Ces méthodes créent une image noire et transparente.

Image non disponible CreateBitmap crée une image avec définition de la largeur, de la hauteur, et de la résolution (96 par défaut).

Pour créer une image de taille 300x200 et 72 dpi (=ppp = points par pouces).

 
Sélectionnez
oGdi.CreateBitmap 300, 200, 72

La résolution peut être modifiée ultérieurement avec les propriétés DpiX et DpiY.

Cette résolution défini l'affichage sur une imprimante, sur une page internet, ou même dans Word.

Image non disponible CreateBitmapForControl est très utile et permet de créer une image dont la taille et la résolution sont optimales pour un affichage sur un contrôle.

Une image créée avec cette méthode aura comme taille la taille exacte de l'intérieur du contrôle, et aura un format identique à celui de l'écran ce qui accélère l'affichage.

Image non disponible CreateBitmapForImg crée une image dont le format est identique à celui d'une autre image.

Là encore cela permet d'accélérer le dessin.

VIII. Image dans un formulaire ou UserForm

Jusqu'ici nous avons chargé puis sauvegardé une image dans un fichier.

Il est possible d'afficher l'image dans un contrôle.

Créez un formulaire sous Access, ou un UserForm (« Insertion » => « UserForm » dans le menu de l'éditeur VBA) pour les autres applications.

Placez-y un contrôle image nommé Image0.

Nous allons écrire du code dans l'événement de chargement du formulaire Access (Form_Load) ou du UserForm (UserForm_Initialize).

Pour Access
Sélectionnez
Private Sub Form_Load()
…
End Sub
Pour les autres applications
Sélectionnez
Private Sub UserForm_Initialize()
…
End Sub

Débutons avec un simple chargement de fichier que nous injectons dans le contrôle :

 
Sélectionnez
Dim oGdi As clGdiplus
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "faqaccess.gif") Then
    ' Injecte l'image dans le contrôle
    oGdi.Repaint Me.Image0
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If

On obtient le même résultat sur toutes les applications :

Image non disponible

L'image est positionnée et redimensionnée selon les propriétés du contrôle : ici le contrôle est en mode « découpage » et « centré ».

On note par contre que bien que l'image soit transparente, le fond est restitué en noir à l'écran.

La solution pour corriger cela est différente selon les applications.

Image non disponiblePour Access :

Il faut injecter une image EMF (Métafichier amélioré) à l'aide du paramètre pUseEMF.

On prendra soin d'activer la transparence au préalable.

Transparence pour Access
Sélectionnez
' Active la transparence
    oGdi.HasTransparency = True
    ' Injecte l'image dans le contrôle
    oGdi.Repaint Me.Image0, , , Me.BackColor
Image non disponible

Image non disponiblePour les autres applications avec un UserForm :

Il n'est pas possible d'avoir de la réelle transparence mais il est possible de définir la couleur de fond (par exemple identique à la couleur de fond du UserForm) avec le paramètre pBackgroundColor :

Pseudo-transparence pour les UserForm
Sélectionnez
' Injecte l'image dans le contrôle
    oGdi.Repaint Me.Image0, , , Me.BackColor
Image non disponible

Il y a en fait trois méthodes de dessin d'une image sur un contrôle :

  • Repaint
  • RepaintFast
  • RepaintNoFormRepaint

VIII-A. Repaint

Cette méthode que nous avons utilisée précédemment injecte l'image dans le contrôle.

Le dessin de l'image est ensuite effectué par l'application Office.

Cette méthode n'est pas très rapide et peut provoquer des clignotements de l'image (plus ou moins visibles selon la configuration).

VIII-B. RepaintFast

Comme son nom l'indique, cette méthode est la plus rapide.

Elle se contente de dessiner l'image sur l'écran, à l'emplacement actuel du contrôle ciblé.

Le dessin est alors temporaire ; si vous affichez une autre fenêtre et que vous revenez sur votre formulaire, les modifications apportées à l'image avec RepaintFast ne seront plus là.

On utilisera cette méthode par exemple pour une animation ou pour dessiner sur un événement « sur souris déplacée ».

VIII-C. RepaintNoFormRepaint

Cette méthode injecte l'image dans le contrôle mais ne laisse pas l'application Office la redessiner.

Le dessin est ensuite effectué sur l'écran comme avec RepaintFast.

Il n'y a donc pas d'effet de clignotement de l'image, tout en ayant un dessin permanent.

On utilisera cette méthode pour finaliser un dessin par exemple à la fin d'une animation ou sur un événement « sur souris relâchée ».

IX. Interaction avec la souris

Vous trouverez un autre exemple d'interaction dans cet article.

Pour ce chapitre nous allons utiliser cette image :

Image non disponible

Elle va être chargée puis affichée dans le contrôle image.

Ensuite on va permettre la sélection d'un rectangle à la souris et recadrer l'image selon cette sélection.

IX-A. Préparation du formulaire

Préparez un formulaire Access (ou un UserForm) contenant une image nommée image0 et un bouton nommé btnRecadre.

Image non disponible

Nous allons devoir agir sur l'image dans différentes procédures ; l'objet oGdi (de type clGdiplus) sera donc créé en entête du module du formulaire pour pouvoir être accessible dans toutes les procédures du formulaire.

Il nous sera utile de conserver également les coordonnées du rectangle de sélection ; on ajoute donc quatre variables gX1, gY1 (coordonnées du premier point) et gX2, gY2 (coordonnées du second point).
Ces variables contiendront des valeurs en pixels, nous les déclarons donc en entiers longs.

Déclarations
Sélectionnez
Option Explicit
' Objet clGdiplus
Private oGdi As clGdiplus
' Coordonnées de sélection
Private gX1 As Long, gY1 As Long
Private gX2 As Long, gY2 As Long

IX-B. Chargement de l'image avec GDI+

L'image est chargée dans l'événement « sur chargement » du formulaire Access :

Chargement de l'image
Sélectionnez
' Sur chargement du formulaire
Private Sub Form_Load()
Set oGdi = New clGdiplus
' Chargement de l'image
If oGdi.LoadFile(oGdi.ApplicationPath & "image3.gif") Then
    ' Conserve l'image initiale
    oGdi.ImageKeep
    ' Injecte l'image dans le contrôle
    oGdi.Repaint Me.Image0
Else
    ' Erreur de chargement
    MsgBox "Erreur au chargement de l'image"
End If
End Sub

Image non disponiblePour un UserForm, utilisez la procédure UserForm_Initialize.

L'image s'affichage alors dans le contrôle, par défaut non redimensionnée et centrée (mode découpage et alignement au centre).

Image non disponible

Un appel à la méthode ImageKeep a été inséré juste après le chargement de l'image.

Cela nous sera utile par la suite pour rappeler cette image de la mémoire sans recharger le fichier.

IX-C. Capture de la sélection à la souris

Nous utilisons tout simplement les événements souris du contrôle image.

  1. Sur souris appuyée, nous sauvegardons les coordonnées dans gX1 et gY1.
  2. Sur souris déplacée, nous sauvegardons les coordonnées dans gX2 et gY2 et dessinons le rectangle sur l'image.
  3. Sur souris relâchée, nous sauvegardons les coordonnées dans gX2 et gY2.

IX-C-1. Sur souris appuyée

On teste si le bouton gauche est appuyé et on sauvegarde les coordonnées.

Office nous renvoi des coordonnées en twips ou en points par rapport au contrôle ; nous les convertissons en pixels sur l'image grâce aux méthodes CtrlToImgX et CtrlToImgY.

Sur souris appuyée
Sélectionnez
' Sur souris appuyée
Private Sub Image0_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbKeyLButton Then
    gX1 = oGdi.CtrlToImgX(X, Me.Image0)
    gY1 = oGdi.CtrlToImgY(Y, Me.Image0)
End If
End Sub

Pour un UserForm, la déclaration de la procédure est différente :

 
Sélectionnez
Private Sub Image0_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

IX-C-2. Sur souris déplacée

On teste si le bouton gauche est appuyé et on sauvegarde les coordonnées (converties en pixels sur l'image) dans gX2 et gY2.

Ensuite on dessine un rectangle pointillé sur l'image à l'aide des coordonnées sauvegardées.

Sur souris déplacée
Sélectionnez
' Sur souris déplacée
Private Sub Image0_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbKeyLButton Then
    gX2 = oGdi.CtrlToImgX(X, Me.Image0)
    gY2 = oGdi.CtrlToImgY(Y, Me.Image0)
    Render
    oGdi.RepaintFast Me.Image0
End If
End Sub

Pour un UserForm, la déclaration de la procédure est différente :

 
Sélectionnez
Private Sub Image0_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

Après avoir sauvegardé les coordonnées converties en pixels, on effectue le rendu (image + rectangle) à l'aide la procédure Render.

L'image est ensuite redessinée sur le contrôle avec la méthode RepaintFast.
Ce dessin est temporaire mais durant un déplacement souris c'est la meilleure méthode.

Voici la procédure de rendu :

Procédure de rendu
Sélectionnez
' Dessine l'image avec le rectangle de sélection
Private Sub Render()
    ' Rétabli l'image initiale
    oGdi.ImageReset
    ' Dessine le rectangle de sélection
    oGdi.DrawRectangle gX1, gY1, gX2, gY2, , vbRed, 1, GdipDashDash
End Sub

On commence par rappeler l'image originale qu'on a sauvegardée précédemment en mémoire (avec la méthode ImageKeep juste après le chargement de l'image).

Sur cette image, on dessine le rectangle correspondant aux coordonnées de sélection.

Ce rectangle est rouge (vbRed), de taille 1 et pointillé (GdipDashDash).

IX-C-3. Sur souris relâchée

On teste si le bouton gauche est relâché et on sauvegarde les coordonnées (converties en pixels sur l'image) dans gX2 et gY2.

Ensuite on dessine un rectangle pointillé sur l'image à l'aide des coordonnées sauvegardées.

Ce sont les mêmes opérations que sur souris déplacée, sauf qu'on va ici utiliser RepaintNoFormRepaint pour dessiner l'image en l'injectant dans le contrôle de manière permanente.

Sur souris relachée
Sélectionnez
' Sur souris relachée
Private Sub Image0_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbKeyLButton Then
    gX2 = oGdi.CtrlToImgX(X, Me.Image0)
    gY2 = oGdi.CtrlToImgY(Y, Me.Image0)
    Render
    oGdi. RepaintNoFormRepaint Me.Image0
End If
End Sub

Pour un UserForm, la déclaration de la procédure est différente :

 
Sélectionnez
Private Sub Image0_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

IX-D. Recadrage de l'image

Pour un recadrage, il suffit d'utiliser la méthode Crop sur l'image originale avec en paramètres les coordonnées sauvegardées.

On effectue cette opération sur clic sur le bouton btnRecadre.

Recadrage
Sélectionnez
' Clic sur bouton de recadrage
Private Sub btnRecadre_Click()
' Rétabli l'image initiale
oGdi.ImageReset
' Recadre l'image
oGdi.Crop gX1, gY1, gX2, gY2
' Dessine l'image recadrée
oGdi.Repaint Me.Image0
End Sub

Un clic sur le bouton « Recadre » remplace l'image originale par l'image recadrée.

Image non disponible

X. Barres de défilement

Dans ce chapitre nous souhaitons afficher une grande image dans un contrôle, et utiliser les barres de défilement pour afficher les parties invisibles.

Les barres de défilement utilisées sont intégrées à clGdiplus.

Elles sont en fait dessinées sur le contrôle image, ce qui nous permet d'utiliser un contrôle image de taille raisonnable et de gérer éventuellement plusieurs ensembles de barres de défilement sur un même contrôle.

X-A. Préparation du formulaire

Préparez un formulaire Access (ou un UserForm) contenant une image nommée image0.

Image non disponible

Nous allons devoir agir sur l'image dans différentes procédures ; l'objet oGdi (de type clGdiplus) sera donc créé en entête du module du formulaire pour pouvoir être accessible dans toutes les procédures du formulaire.

L'utilisation des barres de défilement implique de réagir à des événements ; l'objet oGdi est donc déclaré avec le mot-clé WithEvents.

Déclarations
Sélectionnez
Option Explicit
' Objet clGdiplus
Private WithEvents oGdi As clGdiplus

X-B. Fonctionnement des barres de défilement de clGdiplus

Les barres de défilement vont par paire : une horizontale et une verticale.

Elles sont attachées à un objet clGdiplus, et il est possible de gérer plusieurs barres de défilements par objet.

Image non disponible

Sur cette image on voit le contrôle image avec un fond blanc.

On a dessiné dessus deux images ayant chacune ses barres de défilement.
On a donc une image principale (celle dessinée dans le contrôle) et deux images secondaires (qui sont dessinées sur l'image principale).
Les barres de défilement sont attachées à l'image principale.

Le texte en bleu sur fond jaune est écrit une seule fois lors de l'initialisation de l'image.
Lors du défilement d'une image, seule cette image et ses barres de défilement sont redessinées.
Le reste de l'image est inchangé.

Mais nous allons commencer en douceur avec une seule image recouvrant tout le contrôle :

Image non disponible

X-C. Une image recouvrant tout le contrôle

X-C-1. Initialisation des images et barres de défilement

Dans la procédure d'initialisation (UserForm_Initialize pour un UserForm, Sur ouverture ou Sur chargement pour un formulaire Access), il faut d'abord créer l'image principale et charger l'image secondaire.

Bien qu'on n'ait qu'une seule image, on en utilise deux :

  • l'image principale est le reflet de ce qu'on verra à l'écran ;
  • l'image secondaire est l'image complète que l'on dessine sur l'image principale.

Ensuite on crée un ensemble de barres de défilement.

 
Sélectionnez
' Sur chargement du formulaire
Private Sub UserForm_Initialize()
' Crée l'objet clGdiplus
Set oGdi = New clGdiplus
' Crée l'image principale de la taille du contrôle Image
If oGdi.CreateBitmapForControl(Me.Image1) Then
    ' Remplit l'image de blanc
    oGdi.Clear vbWhite
    ' Crée une image secondaire
    With oGdi.ImgNew("photo")
        ' Charge le fichier image dans l'image secondaire
        .LoadFile oGdi.ApplicationPath & "P1010940.jpg"
        ' Crée un ensemble de barres de défilement
        oGdi.BarNew
        ' Définit la barre horizontale
        oGdi.BarScaleX .ImageWidth
        ' Définit la barre verticale
        oGdi.BarScaleY .ImageHeight
        ' Définit le contrôle source d'événement
        oGdi.BarObject = Me.Image1
    End With
End If
' Dessine l'image secondaire et les barres
PaintImage
' Dessine l'image sur le contrôle
oGdi.RepaintNoFormRepaint Me.Image1
End Sub
            
' Dessine l'image secondaire et les barres
Private Sub PaintImage()
' Dessine l'image en mode découpage et alignée en haut à gauche
' La position de dessin est fonction des positions de la barre de défilement
oGdi.DrawImg "photo", oGdi.BarStartX, oGdi.BarStartY, , , , GdipSizeModeClip, GdipAlignTopLeft
' Dessine les barres
oGdi.BarDraw
End Sub
            
' Fermeture du formulaire
Private Sub Form_Close()
' Libère l'objet oGdi
Set oGdi = Nothing
End Sub

Dans le code précédent on a trois procédures :

  • UserForm_Initialize (ou Form_Load ou Form_Open pour Access) qui s'exécute à l'ouverture du formulaire ;
  • PaintImage qui dessine l'image et les barres.
  • UserForm_Terminate (ou Form_Unload ou Form_Close pour Access) qui s'exécute à la fermeture du formulaire pour libérer l'objet oGdi.

Il est important, voir indispensable, de libérer l'objet oGdi explicitement à la fermeture du formulaire.

Le fait d'avoir dans l'objet oGdi une référence à un contrôle du formulaire contenant cet objet oGdi (référence circulaire) risque de bloquer l'application lors de sa fermeture.

Image non disponibleÀ l'ouverture du formulaire, on crée l'image principale avec CreateBitmapForControl.

Cette image est de la taille du contrôle image.

Puis on remplit l'image de blanc.

Image non disponibleEnsuite on crée une image secondaire, c'est le rôle de la fonction ImgNew : l'image est nommée « photo ».
Cette image est chargée à partir d'un fichier à l'aide de la fonction LoadFile.

Image non disponibleLes barres de défilement sont initialisées dans la suite avec les méthodes Bar* :

  • BarNew initialise un nouvel ensemble de barres ;
  • BarScaleX et BarScaleY définissent ensuite la taille totale du contenu à faire défiler : ici la largeur et la hauteur de l'image ;
  • BarObject définit le contrôle qui recevra les événements de défilement.

Ces quatre méthodes sont obligatoires ; c'est le strict minimum pour gérer des barres de défilement.

Image non disponibleEnfin on effectue le rendu avec la procédure PaintImage et on dessine l'image principale dans le contrôle avec RepaintNoFormRepaint.

La procédure de rendu PaintImage dessine l'image secondaire et les barres sur l'image principale.

L'image « photo » est d'abord dessinée à une position définie par les propriétés BarStartX et BarStartY.

On la dessine en mode découpage (GdipSizeModeClip) et alignée en haut à gauche (GdipAlignTopLeft).

L'image suivante illustre commence s'effectue ce rendu :

Image non disponible

On termine enfin le rendu avec la méthode BardDraw qui dessine les deux barres horizontale et verticale.

X-C-2. Faire défiler l'image

Pour que les barres de défilement soient opérationnelles, il faut gérer un événement important : BarOnRefreshNeeded.

Pour créer la procédure gérant cet événement sélectionnez oGdi en haut à gauche de la barre de code, puis BarOnRefreshNeeded à droite.

Image non disponible

Notez que l'objet oGdi est disponible dans la liste déroulante car il a été déclaré avec le mot-clé WithEvents.

 
Sélectionnez
' Sur demande de rafraichissement des barres de défilement
Private Sub oGdi_BarOnRefreshNeeded(BarName As String, MouseUp As Boolean)
' Redesine l'image et les barres à nouvelle position
PaintImage
' Test si souris relâchée
If MouseUp Then
    ' Dessin permanent si souris relâchée
    oGdi.RepaintNoFormRepaint Me.Image1
Else
    ' Dessin temporaire si souris appuyée
    oGdi.RepaintFast Me.Image1
End If
End Sub

À chaque déplacement des barres de défilement à l'aide de la souris, cet événement est déclenché.

Il suffit alors d'exécuter la procédure de rendu PaintImage, puis de dessiner l'image principale sur le contrôle.

Pour plus de fluidité, on effectue un dessin temporaire avec RepaintFast si la souris est appuyée.

Lorsque la souris est relâchée, on effectue un dessin permanent.

Tout est maintenant opérationnel, on peut faire défiler l'image en cliquant sur les barres de défilement.

X-D. Plusieurs images sur un même contrôle

Commençons par placer la première image à un endroit précis de l'image principale.

Pour cela on définit la position des barres de défilement lors de l'appel de BarScaleX et BarScaleY :

  • BarScaleX définit les positions gauche et droite ;
  • BarScaleY définit les positions hautes et basses.

On reprend le code du chapitre précédent et on modifie ainsi :

 
Sélectionnez
' Définit la barre horizontale
        oGdi.BarScaleX .ImageWidth, , 50, 200
        ' Définit la barre verticale
        oGdi.BarScaleY .ImageHeight, , 100, 250

Ici on place notre image et ses barres dans un rectangle de coordonnées (50,100) et (200,250).

Ensuite il faut modifier la procédure de rendu PaintImage pour dessiner l'image au bon endroit, en ajoutant la position des barre de défilement BarLeft et BarTop :

 
Sélectionnez
' Dessine l'image secondaire et les barres
Private Sub PaintImage()
' Dessine l'image en mode découpage et alignée en haut à gauche
' La position de dessin est fonction des positions de la barre de défilement
' On ajoute la position des barres de défilement pour afficher l'image au bon endroit
oGdi.DrawImg "photo", oGdi.BarLeft + oGdi.BarStartX, oGdi.BarTop + oGdi.BarStartY, , , , GdipSizeModeClip, GdipAlignTopLeft
' Dessine les barres
oGdi.BarDraw
End Sub

Mais il y a un petit problème : l'image « déborde » :

Image non disponible

Il faut en effet limiter le dessin pour qu'il ne dépasse pas.

La méthode DrawClipRectangle nous permet de définir sur l'image principale un rectangle dans lequel sera limité le dessin : les zones extérieures à ce rectangle ne seront pas dessinées.

Puis on annule cette limitation après avoir dessiner l'image secondaire avec la méthode DawClipRegion.

Pour faire la finition, on ajoute le dessin d'un rectangle qui encadre l'image et ses barres.

 
Sélectionnez
' Dessine l'image secondaire et les barres
Private Sub PaintImage()
' Limite le dessin à la zone souhaitée
oGdi.DrawClipRectangle oGdi.BarLeft, oGdi.BarTop, oGdi.BarRight, oGdi.BarBottom
' Dessine l'image en mode découpage et alignée en haut à gauche
' La position de dessin est fonction des positions de la barre de défilement
' On ajoute la position des barres de défilement pour afficher l'image au bon endroit
oGdi.DrawImg "photo", oGdi.BarLeft + oGdi.BarStartX, oGdi.BarTop + oGdi.BarStartY, , , , GdipSizeModeClip, GdipAlignTopLeft
' Retire la limite de dessin
oGdi.DrawClipRegion = ""
' Dessine les barres
oGdi.BarDraw
' Dessine en rectangle qui encadre l'image et ses barres
oGdi.DrawRectangle oGdi.BarLeft, oGdi.BarTop, oGdi.BarRight, oGdi.BarBottom
End Sub

Et voici le résultat :

Image non disponible

Nous souhaitons maintenant ajouter une deuxième image.

Dans le code d'initialisation, il faut créer deux images secondaires et deux ensembles de barres de défilement :

 
Sélectionnez
' Sur chargement du formulaire
Private Sub UserForm_Initialize()
' Crée l'objet clGdiplus
Set oGdi = New clGdiplus
' Crée l'image principale de la taille du contrôle Image
If oGdi.CreateBitmapForControl(Me.Image1) Then
    ' Remplit l'image de blanc
    oGdi.Clear vbWhite
    ' Ecrit un texte en haut
    oGdi.DrawText "Tutoriel barres de défilement", 20, , 0, 0, oGdi.ImageWidth - 1, 100, , , vbBlue, , vbYellow, 150    ' Crée une image secondaire
    With oGdi.ImgNew("photo1")
        ' Charge le fichier image dans l'image secondaire
        .LoadFile oGdi.ApplicationPath & "P1010940.jpg"
        ' Crée un ensemble de barres de défilement
        oGdi.BarNew .ImgName
        ' Définit la barre horizontale
        oGdi.BarScaleX .ImageWidth, , 50, 200, .ImgName
        ' Définit la barre verticale
        oGdi.BarScaleY .ImageHeight, , 100, 250, .ImgName
        ' Dessine l'image secondaire et les barres
        PaintImage .ImgName
    End With
    ' Crée une image secondaire
    With oGdi.ImgNew("photo2")
        ' Charge le fichier image dans l'image secondaire
        .LoadFile oGdi.ApplicationPath & "P1020363.jpg"
        ' Crée un ensemble de barres de défilement
        oGdi.BarNew .ImgName
        ' Définit la barre horizontale
        oGdi.BarScaleX .ImageWidth, , 250, 400, .ImgName
        ' Définit la barre verticale
        oGdi.BarScaleY .ImageHeight, , 100, 250, .ImgName
        ' Dessine l'image secondaire et les barres
        PaintImage .ImgName
    End With
    ' Définit le contrôle source d'événement (un seul contrôle pour les deux images)
    oGdi.BarObject = Me.Image1
End If
' Dessine l'image sur le contrôle
oGdi.RepaintNoFormRepaint Me.Image1
End Sub

Nous ajoutons donc deux images secondaires « photo1 » et « photo2 ».

Pour chacune de ces images, on ajoute un ensemble de barres de défilement.
Dans les méthodes BarNew, BarScaleX et BarScaleY, il faut préciser un nom de barre.
Pour faciliter la suite on utilise le nom de chaque image (.ImgName) pour nommer chaque barre « photo1 » et « photo2 ».

Ensuite on dessine chacune des deux barres en exécutant la procédure de rendu PaintImage avec en paramètre le nom de l'image (nous allons ajouter ce paramètre ensuite dans la procédure).

Par contre BarObject n'est défini qu'une seule fois : il n'y a qu'un contrôle pour les deux images.

Notez qu'on en a profité pour ajouter un appel à DrawText afin d'écrire du texte en haut de l'image principale.

La procédure de rendu doit également être mise à jour pour dessiner une des deux images en ajoutant un paramètre pImage.

 
Sélectionnez
' Dessine l'image secondaire et les barres
Private Sub PaintImage(pImage As String)
' Limite le dessin à la zone souhaitée
oGdi.DrawClipRectangle oGdi.BarLeft(pImage), oGdi.BarTop(pImage), oGdi.BarRight(pImage), oGdi.BarBottom(pImage)
' Dessine l'image en mode découpage et alignée en haut à gauche
' La position de dessin est fonction des positions de la barre de défilement
' On ajoute la position des barres de défilement pour afficher l'image au bon endroit
oGdi.DrawImg pImage, oGdi.BarLeft(pImage) + oGdi.BarStartX(pImage), oGdi.BarTop(pImage) + oGdi.BarStartY(pImage), , , , GdipSizeModeClip, GdipAlignTopLeft
' Retire la limite de dessin
oGdi.DrawClipRegion = ""
' Dessine les barres
oGdi.BarDraw pImage
' Dessine en rectangle qui encadre l'image et ses barres
oGdi.DrawRectangle oGdi.BarLeft(pImage), oGdi.BarTop(pImage), oGdi.BarRight(pImage), oGdi.BarBottom(pImage)
End Sub

DrawImg dessinera l'image passée en paramètre.

Les méthodes Bar* doivent toutes préciser le nom de la barre de défilement concernée.
Comme on a nommé nos barres comme nos images c'est facile.

Pour terminer on met à jour l'événement BarOnRefreshNeeded.

Celui-ci possède un paramètre BarName qui est le nom de la barre de défilement déplacée.

Il suffit donc de passer ce nom en paramètre de la procédure de rendu :

 
Sélectionnez
' Sur demande de rafraichissement des barres de défilement
Private Sub oGdi_BarOnRefreshNeeded(BarName As String, MouseUp As Boolean)
' Redesine l'image et les barres à nouvelle position
PaintImage BarName
' Test si souris relâchée
If MouseUp Then
    ' Dessin permanent si souris relâchée
    oGdi.RepaintNoFormRepaint Me.Image1
Else
    ' Dessin temporaire si souris appuyée
    oGdi.RepaintFast Me.Image1
End If
End Sub

C'est enfin terminé :

Image non disponible

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.