Tutorial 25: Un Simple Bitmap

Dans ce tutorial, nous allons voir comment utiliser des bitmaps dans notre programme. Pour être exact, nous allons apprendre à afficher un bitmap dans le secteur client de notre fenêtre.
Downloadez l'exemple.

Théorie

Les simples sont de simples images stockées dans l'ordinateur. Il y a beaucoup de formats d'images utilisés pour les ordinateurs, mais à l'origine Windows ne traitait qu'uniquement les fichiers Graphiques (.bmp). Les Bitmaps dont nous allons parler dans ce tutorial sont des bitmap Windows *.bmp. La plus simple façon d'utiliser un bitmap est de l'employer en tant que ressource. Il y a deux façons pour faire ça. Vous pouvez inclure le bitmap dans un fichier de définition de ressource (.rc) comme suit :
 
#define IDB_MYBITMAP   100
IDB_MYBITMAP  BITMAP  "c:\project\example.bmp"
Cette méthode emploie une constante pour représenter le bitmap. La première ligne crée juste une constante nommé IDB_MYBITMAP qui pour valeur 100. Nous utiliserons ce label (ce nom) pour nous référer à ce bitmap dans le programme. La ligne suivante déclare un bitmap ressource. On indique au compilateur de ressource à quel endroit retrouver le fichier bmp réel.
L'autre méthode utilise un nom pour représenter le bitmap comme suit :
MyBitMap  BITMAP "c:\project\example.bmp"
Cette méthode exige que vous vous référiez au bitmap dans votre programme grâce au nom "MyBitMap" au lieu d'une valeur.
L'unes ou l'autres de ces méthodes restent excellentes tant que vous sachiez bien quelle méthode vous employez.
Maintenant que nous avons placé le bitmap dans le fichier ressource, nous pouvons continuer les étapes de l'explication pour afficher ce bitmap dans le secteur client de notre fenêtre.
  1. Appelez LoadBitmap pour obtenir l'handle du bitmap. LoadBitmap possède le prototype de fonction suivant :
      LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR


    Cette fonction renvoie l'handle du bitmap. hInstance est l'instance handle de notre programme. LpBitmapName est un pointer qui est sur la châine de caractères qui définit le nom du bitmap (dans le cas où vous employez la deuxième méthode pour se référer au bitmap). Si vous employez une constante pour vous référer au bitmap (comme IDB_MYBITMAP), vous pouvez mettre sa valeur ici. (Dans l'exemple au-dessus, ce serait 100). Voici un court exemple:

       
      First Method:

      .386
      .model flat, stdcall
      ................
      .const
      IDB_MYBITMAP    equ 100
      ...............
      .data?
      hInstance  dd ?
      ..............
      .code
      .............
          invoke GetModuleHandle,NULL
          mov hInstance,eax
      ............
          invoke LoadBitmap,hInstance,IDB_MYBITMAP
      ...........

      Second Method:

      .386
      .model flat, stdcall
      ................
      .data
      BitmapName  db "MyBitMap",0
      ...............
      .data?
      hInstance  dd ?
      ..............
      .code
      .............
          invoke GetModuleHandle,NULL
          mov hInstance,eax
      ............
          invoke LoadBitmap,hInstance,addr BitmapName
      ...........




    Pour info: un 'contexte de dispositif' c'est en quelque sorte la zone blanche principale de votre fenêtre sur laquelle on affichera le dessin bitmap. Elle est réelle quand on parle d'une page physiquement afficher sur notre écran. Mais on peut aussi se le réprésenter comme une page fictive n'exsitant que sous forme de données en mémoire.

  1. Récupérez l'handle du contexte de dispositif (DC=device context ). Vous pouvez obtenir cet handle en appelant BeginPaint après le retour du message WM_PAINT ou en appelant GetDC à n'importe quel moment.
  2. Créez un contexte de dispositif en mémoire qui a le même attribut que le contexte de dispositif que nous venons juste d'obtenir. L'idée ici est de créer une sorte de feuille de dessin "cachée" sur laquelle nous pouvons dessiner le bitmap. Quand nous avons finis cette opération, il nous suffit de copier le contenu de la feuille de dessin (cachée) sur le contexte de dispositif réel, grâce à un appel de fonction. C'est un exemple technique de double-affichage, utilisé pour une rapide exposition d'images à l'écran. Vous pouvez créer cette feuille de dessin "cachée" en appelant CreateCompatibleDC.
      CreateCompatibleDC  proto  hdc:HDC


    Si cette fonction réussit, elle renvoie l'handle du contexte de dispositif de mémoire dans eax. hdc est l'handle du contexte de dispositif pour lequel vous souhaitez que la mémoire DC soit compatible avec lui.

  1. Maintenant que vous avez obtenu une feuille de dessin cachée, vous pouvez dessiner dessus en choisissant le bitmap. On fait ça en appelant SelectObject avec l'handle de la mémoire DC en tant que premier paramètre et l'handle de bitmap comme deuxième paramètre. SelectObject est définit ainsi :
      SelectObject   proto  hdc:HDC, hGdiObject:DWORD
  1. Le bitmap est dessiné sur le contexte de dispositif en mémoire maintenant. Tout ce que nous avons besoin de faire ici, c'est de le recopier sur le dispositif d'affichage réel, à savoir (qui est) le vrai contexte de dispositif. Il y a plusieurs fonctions qui peuvent exécuter cette opération comme BitBlt et StretchBlt. BitBlt recopie seulement le contenu d'un DC dans un autre donc c'est rapide, tandis que StretchBlt peut décompresser ou compresser le bitmap pour le redimensionner à la taille du secteur d'affichage de votre fenêtre. Ici, nous emploierons BitBlt pour simplifier. BitBlt a la définition suivante :
      BitBlt  proto  hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, nySrc:DWORD, dwROP:DWORD
       
    hdcDest est l'handle du contexte de dispositif réel qui sert de destination pour l'opération de transfert du bitmap.
    nxDest, nyDest sont les coordonnées du coin supérieur gauche du secteur d'affichage.
    nWidth, nHeight sont la largeur et la hauteur du secteur d'affichage.
    hdcSrc est l'handle du contexte de dispositif qui sert de source dans l'opération de transfert du bitmap.
    nxSrc, nySrc sont les coordonnées du coin supérieur gauche du rectangle source.
    dwROP est le code de l'opération qui permet d'appliquer au mieux les données de couleur du bitmap avec les données de couleur mis à disposition par le secteur d'affichage, pour réaliser le résultat final. La plupart du temps, on souhaite seulement recopier les données de couleur existantes dans le nouveau.
  1. Quand vous avez affiché le bitmap, vous pouvez le supprimer avec l'appel d'API DeleteObject.
Voilà! Pour récapituler, vous avez besoin de placer le bitmap dans un script de ressource (voir plus bas, après la source du programme). Chargez-le alors en ressource avec LoadBitmap. Vous obtiendrez l'handle du bitmap. Ensuite vous obtenez l'handle du contexte de dispositif du secteur sur lequel vous souhaitez redessiner le bitmap. Alors vous créez un contexte de dispositif en mémoire qui est compatible avec le contexte de dispositif que vous venez juste d'obtenir. Choisissez le bitmap dans la mémoire DC et recopiez alors le contenu de la mémoire DC vers votre DC réel.

Example Code:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
IDB_MAIN   equ 1

.data
ClassName db "SimpleWin32ASMBitmapClass",0
AppName  db "Win32ASM Simple Bitmap Example",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hBitmap dd ?

.code
start:
 invoke GetModuleHandle, NULL
 mov    hInstance,eax
 invoke GetCommandLine
 mov    CommandLine,eax
 invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
 invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
 LOCAL wc:WNDCLASSEX
 LOCAL msg:MSG
 LOCAL hwnd:HWND
 mov   wc.cbSize,SIZEOF WNDCLASSEX
 mov   wc.style, CS_HREDRAW or CS_VREDRAW
 mov   wc.lpfnWndProc, OFFSET WndProc
 mov   wc.cbClsExtra,NULL
 mov   wc.cbWndExtra,NULL
 push  hInstance
 pop   wc.hInstance
 mov   wc.hbrBackground,COLOR_WINDOW+1
 mov   wc.lpszMenuName,NULL
 mov   wc.lpszClassName,OFFSET ClassName
 invoke LoadIcon,NULL,IDI_APPLICATION
 mov   wc.hIcon,eax
 mov   wc.hIconSm,eax
 invoke LoadCursor,NULL,IDC_ARROW
 mov   wc.hCursor,eax
 invoke RegisterClassEx, addr wc
 INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
 mov   hwnd,eax
 invoke ShowWindow, hwnd,SW_SHOWNORMAL
 invoke UpdateWindow, hwnd
 .while TRUE
  invoke GetMessage, ADDR msg,NULL,0,0
  .break .if (!eax)
  invoke TranslateMessage, ADDR msg
  invoke DispatchMessage, ADDR msg
 .endw
 mov     eax,msg.wParam
 ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   LOCAL ps:PAINTSTRUCT
   LOCAL hdc:HDC
   LOCAL hMemDC:HDC
   LOCAL rect:RECT
   .if uMsg==WM_CREATE
      invoke LoadBitmap,hInstance,IDB_MAIN
      mov hBitmap,eax
   .elseif uMsg==WM_PAINT
      invoke BeginPaint,hWnd,addr ps
      mov    hdc,eax
      invoke CreateCompatibleDC,hdc
      mov    hMemDC,eax
      invoke SelectObject,hMemDC,hBitmap
      invoke GetClientRect,hWnd,addr rect
      invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
      invoke DeleteDC,hMemDC
      invoke EndPaint,hWnd,addr ps
 .elseif uMsg==WM_DESTROY
  invoke DeleteObject,hBitmap
  invoke PostQuitMessage,NULL
 .ELSE
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam
  ret
 .ENDIF
 xor eax,eax
 ret
WndProc endp
end start

;---------------------------------------------------------------------
;                            le script resource
;---------------------------------------------------------------------
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"

Analyse:

Il n'y a pas grand chose à analyser dans ce tutorial ;)
 
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"
Ça définit la constante nommée IDB_MAIN, on lui associe la valeur 1. Et on emploie ensuite cette constante en tant qu'identificateur du bitmap en ressource. Le fichier bitmap qui est inclus dans la ressource est "tweety78.bmp" lequel réside dans le même dossier que le script de ressource.

   .if uMsg==WM_CREATE
      invoke LoadBitmap,hInstance,IDB_MAIN
      mov hBitmap,eax

En réponse à WM_CREATE, nous appelons LoadBitmap pour charger le bitmap en ressource, en passant l'identificateur du bitmap en ressource comme deuxième paramètre de l'API. Nous obtenons l'handle du bitmap au moment du retour de la fonction.
Maintenant que le bitmap est chargé, nous pouvons le dessiner dans le secteur client de notre fenêtre principale.

   .elseif uMsg==WM_PAINT
      invoke BeginPaint,hWnd,addr ps
      mov    hdc,eax
      invoke CreateCompatibleDC,hdc
      mov    hMemDC,eax
      invoke SelectObject,hMemDC,hBitmap
      invoke GetClientRect,hWnd,addr rect
      invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
      invoke DeleteDC,hMemDC
      invoke EndPaint,hWnd,addr ps

Nous décidons de repeindre le bitmap après la réponse du message WM_PAINT. Nous appelons d'abord BeginPaint pour obtenir l'handle du contexte de dispositif. Ensuite, nous créons une mémoire compatible DC avec CreateCompatibleDC. Puis on sélectionne le bitmap à l'intérieur de la mémoire DC grâce à SelectObject. Déterminez la dimension de votre secteur client avec GetClientRect. Maintenant nous pouvons faire apparaître le bitmap dans le secteur client en appelant BitBlt, lequel recopie le bitmap de la mémoire DC vers le DC réel. Lorsque la repeinte est effectue, nous n'avons plus besoin de la mémoire DC donc nous le supprimons grâce à DeleteDC. On fini la session de peinture avec EndPaint.

 .elseif uMsg==WM_DESTROY
  invoke DeleteObject,hBitmap
  invoke PostQuitMessage,NULL
Dès lors que nous n'avons plus besoin du bitmap, nous le supprimons avec DeleteObject.

[Iczelion's Win32 Assembly HomePage]


Traduit par Morgatte