Affichage du Bitmap-
Comme nous ne savons pas d'où provient le bitmap et sous quelle type de résolution il à été créé alors nous devons
lui assuré une parfaite compatibilité avec le contrôle fenêtré où il sera dessiner. Pour cela nous allons le dessiner sur
un DC, qui sera juste créer en mémoire, mais qui sera aussi compatible avec le contrôle fenêtré où nous voulons le dessiner.
Ensuite nous copierons le contenu du DC temporaire vers le DC où nous voulons dessiner le Bitmap. Voici le code associé à mon explication qui semble très abstraite...
Function DessinerSur( PlancheADessin :HDC;
DestLargeur,
DestHauteur :Integer ) :Boolean;
Var
hBMP :HBITMAP;
MemoireDC :HDC;
Begin
If GetObjectType( PlancheADessin ) = 0 Then
Exit;
hBMP := CreateCompatibleBitmap( PlancheADessin,
BmpINFO.biWidth, BmpINFO.biHeight );
If hBMP = NULL Then
Exit;
MemoireDC := CreateCompatibleDC( PlancheADessin );
If MemoireDC = NULL Then
Exit;
If SetDIBits( MemoireDC, hBMP, 0, BmpINFO.biHeight,
pTabPixels, pBmpINFO^, DIB_RGB_COLORS ) <= 0 Then
Exit;
SelectObject( MemoireDC , hBMP );
If StretchBlt( PlancheADessin,
0, 0, DestLargeur, DestHauteur,
MemoireDC,
0, 0, BmpINFO.biWidth, BmpINFO.biHeight,
SRCCOPY ) <> False Then
Result := True ;
DeleteObject( hBMP );
DeleteDC( MemoireDC );
End;
End;
- hBMP :HBITMAP;
- Handle sur un Bitmap. Genre de pointeur sur un Bitmap.
- MemoireDC :HDC;
- Contexte Graphique(DC). C'est par l'entremise de cette variable que nous allons créer un DC en mémoire seulement.
- If GetObjectType( PlancheADessin )...;
- Vérification du type de contexte graphique(DC) passé en paramètre.
- hBMP := CreateCompatibleBitmap( PlancheADessin, BitMapTemp.Width, BitMapTemp.Height );
- Création d'un Bitmap compatible avec le DC où nous voulons l'afficher. Supposons que nous voudrions dessiner notre Bitmap sur un DC qui
ne supporte que deux couleur (Monocrome) alors le Bitmap compatible serait créer de manière à ce qu'il suporte le mode monocrome.
Pas trop compliqué n'est-ce pas ?
- MemoireDC := CreateCompatibleDC( PlancheADessin );
- Création d'un DC compatible aussi avec notre DC où nous voulons dessiner notre Bitmap.
- SetDIBits( MemoireDC, hBMP, ..., DIB_RGB_COLORS );
- Cette fonction dessine le contenu d'un tableau de pixel sur un DC. Voici la description de cette fonction :
SetDIBits( hdc :HDC; // DC de destination
hbmp :HBITMAP; // Handle du Bitmap
uStartScan, // Première ligne à lire dans le tableau de Pixels
cScanLines :Cardinal; // Dernière ligne à lire dans le tableau de Pixels
lpvBits :Pointer; // Pointer sur le tableau de pixels
Const lpbmi :BITMAPINFO; // Pointeur sur la Structure d'info TBitMapInfo
fuColorUse :Cardinal // Type de gestion de pixel RGB ou Indexé
) :Integer;
- SelectObject( MemoireDC , hBMP );
- Liasons des deux objet créés. Donc on selectionne le Bitmap(hBMP) dans le DC en mémoire.
- StretchBlt( PlancheADessin,...
- Cette fonction sert à dessiner un Bitmap sur un DC passé en paramètre. Si le Bitmap à dessiner est plus large que sa destination alors cette fonction le rétréssira en revanche s'il est trop petit cette fonction l'agrandira. Description de cette fonction :
StretchBlt( hdcDest :HDC; // DC de destination
nXOriginDest :Integer; // Coordonnée X Haut-Gauche de destination
nYOriginDest :Integer; // Coordonnée Y Haut-Gauche de destination
nWidthDest :Integer; // Largeur de la destination
nHeighDest :Integer; // Hauteur de la destination
hdcSrc :HDC; // DC de la source
nXOriginSrc :Integer; // Coordonnée X Haut-Gauche de l'origine
nYOriginSrc :Integer; // Coordonnée Y Haut-Gauche de l'origine
nWidthSrc :Integer; // Largeur de l'origine
nHeightSrc :Integer; // Hauteur de l'origine
dwRop :DWORD; // Type de traitement
): Boolean;
- DeleteObject( hBMP );
- Très important quand nous avons fini d'utiliser un objet il faut libérer la mémoire prise par celui-ci.
- DeleteDC( MemoireDC );
- Encore plus important quand on à plus besion d'un DC il faut absolument le détruire si on ne veut pas avoir de surprises avec Windows.
|
Manipulation des pixels d'un Bitmap en mémoire-
La manipulation des pixels en mémoire est n'est pas extremmement difficile mais il y a tout de fois quelque chose qui la rend difficile et c'est le résultat que l'on désire avoir.
Vu que la manipulation se fait en mémoire alors je n'ai pas besion de vous dire que celle-ci se fera par l'entremise de pointer sur des Octets, DWord, TRGBTriple ou TRGBQuad et tout cela dépendemment du nombre de bits/Pixel du Bitmap.
Le manière d'accéder au tableau de pixel en mémoire se fera à l'aide de transtypage et de déplacements d'octets en octets ou autres selon le cas(bpp).
Voici un bout de code qui vous donnera sûrement d'autres idées :
Procedure InverserCouleur;
Var
LongeurDuneLigne :Integer; {Longeur d'une ligne de pixel en octets}
Parcours :Integer; {Ligne parcourue (Partant du bas du bitmap)}
X :Integer; {Parcourir chaque octet d'une ligne de pixels}
PointeurTemp :Pointer; {Pointeur temporaire sur les pixels du Bitmap}
CouleurIndex :Integer; {Incrémenteur pour parcourir la palette}
begin
Case BmpINFO.biBitCount Of
1, 4, 8 :
Begin
For CouleurIndex := 0 To PalNbCouleurs - 1 Do
Begin
PointeurTemp := Pointer( Integer(PtrDebutPal) + CouleurIndex * SizeOf(TRGBQuad));
TRGBQuad(PointeurTemp^).rgbRed := Not TRGBQuad(PointeurTemp^).rgbRed;
TRGBQuad(PointeurTemp^).rgbGreen := Not TRGBQuad(PointeurTemp^).rgbGreen;
TRGBQuad(PointeurTemp^).rgbBlue := Not TRGBQuad(PointeurTemp^).rgbBlue;
End;
End;
16 :
Begin
LongeurDuneLigne := ((( BmpINFO.biWidth * BmpINFO.biBitCount ) + 31) And -31) Shr 3;
Parcours := 0;
While Parcours < LongeurDuneLigne * (BmpINFO.biHeight - 1) Do
Begin
For X := 0 To LongeurDuneLigne - 1 Do
Begin
PointeurTemp := Pointer(Integer(pTabPixels) + Parcours + X );
Byte( PointeurTemp^ ) := Not Byte( PointeurTemp^ );
End;
Inc(Parcours, LongeurDuneLigne );
End;
End;
24 :
Begin
For X := 0 To (BmpINFO.biHeight * BmpINFO.biWidth) Do
Begin
PointeurTemp := Pointer( Integer(pTabPixels) + X * SizeOf(TRGBTriple) );
TRGBTriple( PointeurTemp^ ).rgbtBlue := Not TRGBTriple( PointeurTemp^ ).rgbtBlue;
TRGBTriple( PointeurTemp^ ).rgbtGreen := Not TRGBTriple( PointeurTemp^ ).rgbtGreen;
TRGBTriple( PointeurTemp^ ).rgbtRed := Not TRGBTriple( PointeurTemp^ ).rgbtRed;
End;
End;
32 :
Begin
For X := 0 To (BmpINFO.biHeight * BmpINFO.biWidth) Do
Begin
PointeurTemp := Pointer( Integer(pTabPixels) + X * SizeOf(TRGBQuad));
TRGBQuad( PointeurTemp^ ).rgbBlue := Not TRGBQuad( PointeurTemp^ ).rgbBlue;
TRGBQuad( PointeurTemp^ ).rgbGreen := Not TRGBQuad( PointeurTemp^ ).rgbGreen;
TRGBQuad( PointeurTemp^ ).rgbRed := Not TRGBQuad( PointeurTemp^ ).rgbRed;
End;
End;
End;
end;
Alors pour le cas des bitmap possèdant un Table de couleur(Palette), nous ne pouvons pas inverser la couleur du pixel directement mais les couleur de sa Palette.
Et pour les autre bitmap n'ayant pas de Palette de couleur on peut se permettre de spécifier la couleur contraire pour chaque pixels.
Remarquer le travail splandide du transtypage de valeur, quand nous avons besion que de récupérer un Byte alors on le précise devant le pointer :BYTE( Adresse... ).
Une autre chose, il est très important de calculer le nombre d'octet par ligne pour un Bitmap moins de 8bpp.
Prenons par exemple un bitmap 4 bits par pixel qui aurait 15 pixel en largeur. Quand nous récupèrerions une ligne, il faudrait récupérer 8octets. Pourtant 7 octets + 4 bits aurait suffit ?
Mais comme on ne peut pas vraiment récupérer 7 octets + 4 bits alors il faut TOUT récupérer. Ensuite il sera important de ne pas prendre le dernier 4 bits du 8ième octets comme étant une couleur car il ne sert à rien. Pour une ceux qui sont visuel :-),
une ligne du bitmap est représentée dans l'image ci-dessus et le carré qui contient un signe d'intérogation représente le dernier 4 bits du dernier octets.
Petit trucs :
- Byte
- Utile pour travailler sur des bitmap ayant 1, 4, 8 bpp.
- Dword
- Gérer des bitmaps ayant 16bpp, parce qu'un pixel à besion de deux octets alors on récupère deux octets DWORD = 2 octets.
- TRGBTRIPLE
- Utile sur les 24bpp, 3 octets pour 1 pixels.
- TRGBQUAD
- Utile sur les 32bpp, 4 octets pour 1 pixels et dont un qui sert complètement à rien.
|
- Le cadeaux :
-
Voici un petit programme démonstrateur qui à pour but de démontrer l'utilisation des Bitmaps. Par la même occasion j'ai conçu une class TFichierBitmap qui contient tout le nécessaire pour débuter avec les Bitmaps.
Bien sur je n'ai pas le temps de vous l'expliquer en tutoriel mais elle est fortement commentée. Et surtout s'il y a des questions, ne vous gênez pas...
- Chargement d'un bitmap.
- Afficher la palette du bitmap.
- Renverser l'image (Horizontal ou vertical).
- Ajustement des couleurs Rouge, Vert et bleu selon un pourcentage.
- Griser une image (GrayScale).
- Inverser toutes les couleurs d'un bitmap.
- Récupérer les informations en rapport avec le Bitmap.
- Possibilité de charger ou enregistrer la palette du Bitmap.
- Conversion
-
1bpp à 24bpp
4bpp à 24bpp
8bpp à 24bpp
16bpp à 24bpp
32pp à 24bpp
1bpp à 32bpp avec valeur Alpha
4bpp à 32bpp avec valeur Alpha
8bpp à 32bpp avec valeur Alpha
16bpp à 32bpp avec valeur Alpha
24pp à 32bpp avec valeur Alpha
- Eregistrement sous le format courant.
|
- Programme relié
-
Le code source de ma CLASS TBitmapFichier vous ai totalement livré gratuitement. Mais ce que vous y trouverai n'est pas tout expliqué dans ce tutoriel.
Si vous avez suivi le tutoriel jusqu'ici vous devriez être en mesure d'utiliser cette Class très facilement.
Source = > Programme démo + Source de la class TFichierBitmap
source = > Seulement la class TFichierBitmap
|