- Format du fichier Bitmap :
-
Maintenant parlons un peu plus en détail des différentes structures qui nous permettrons de stocker le Bitmap lui-même.
Chaque structure possède une forme et elle est responsable de récupérer certaines informations relatives au Bitmap mais pour cela il est nécessaire de poursuivre un Ordre précis pour la récupération des données.
Tout d'abord nous parlerons seulement des structures qui seront utilisées dans ce tutoriel. La première :
TBITMAPFILEHEADER = Packed Record
bfType :Word;
bfSize :DWORD;
bfReserved1 :Word;
bfReserved2 :Word;
bfOffBits :DWORD;
End;
TBITMAPFILEHEADER
- bfType
- Spécifie le type de Bitmap, sa Signature. cette valeur doit contenir 'BM' ou sous format DWord ($424D).
Ce qui équivaut aussi à Chr( $42 ) + Chr($4D );
- bfSize
- Grosseur total du fichier.
- bfReserved1
- Réservé.
- bfReserved2
- Réservé
- bfOffBits
- Spécifie en Octet(Byte) la distance dans le fichier entre cette structure et le commencement des pixels, donc ceux qui forme l'image contenu dans le Bitmap.
Voici trois autres structures qui nous serons utile, Trois car elles se rappel une à une.
TBITMAPINFO = Packed Record
bmiHeader :TBitmapInfoHeader;
bmiColors :Array[0..0] of TRGBQuad;
End;
TRGBQUAD = Packed Record
rgbBlue : Byte;
rgbGreen : Byte;
rgbRed : Byte;
rgbReserved : Byte;
End;
TBITMAPINFOHEADER = Packed Record
biSize : DWORD;
biWidth : Longint;
biHeight : Longint;
biPlanes : Word;
biBitCount : Word;
biCompression : DWORD;
biSizeImage : DWORD;
biXPelsPerMeter : Longint;
biYPelsPerMeter : Longint;
biClrUsed : DWORD;
biClrImportant : DWORD;
End;
TBITMAPINFO
- bmiHeader
- Ce membre pointe vers la structure :TBITMAPINFOHEADER
- bmiColors
- Ce membre est un tableau vide représentant la palette de couleur du Bitmap.
La Palette contient biClrUsed couleurs. Si l'on veut la parcourir alors il faut se servir de la structure TRGBQuad pour stocker chacune des couleurs (Quad = 4 Octets).
TBITMAPINFOHEADER
- biSize
- Spécifie la dimension de cette structure en Octet.
- biWidth
- Largeur de l'image.
- biHeight
- Hauteur de l'image.
- biPlanes
- Nombre de plans doit être '0001'.
- biBitCount
- Nombre de Bits par Pixel.
- biCompression
- Compression s'il en existe une. Si cette valeur est BI_RGB, c'est que le Bitmap n'est pas compressé. BI_RLE8 signifie qu'il est compressé (RLE) 8 bits par pixel et BI_RLE4 qu'il est compressé (RLE) 4 bits par pixel.
- biSizeImage
- Spécifie la taille de l'image en octet. Pour bien récupérer cette valeur le bitmap ne doit pas être compressé donc biCompression <> BI_RGB.
- biXPelsPerMeter
- Résolution Horizontale en pixels par mètre
- biYPelsPerMeter
- Résolution Verticale en pixels par mètre
- biClrUsed
- Spécifie le nombre de couleur dans la table qui sont présentement utilisées par le Bitmap. Ce cette valeur est égale à 0, c'est que le Bitmap utilise le maximum des couleurs présentes dans sa table.
- biClrImportant
- Spécifie le nombre de couleur requise pour afficher le bitmap.
Je ne l'ai pas mentionné encore mais pour ceux qui ne serais pas au courant, Packet Record n'est pas tout à fait pareil à Record.
Cela indique au compilateur qu'il doit compresser le stockage des données au format de la structure, même au prix d'un ralentissement de l'accès à un composant d'une variable de ce type.
Bon et maintenant je vous affiche un petit dessin très explicatif du format d'un fichier Bitmap. L'auteur est Microsoft, on le devine bien bien que ce n'est pas moi parce que ça dépasse pas de chaque côté.. :-)
|
- Récupération des informations à l'intérieur du BitMap :
-
Premièrement je vous présente un petit bout de code qui m'aidera très bien à vous expliquer :
Procedure RecupererInfo( Chemin :String;
Var FileSize, Largeur, Hauteur, BitsPerPixel,
NbCouleur :Integer );
Const
FICHEADER = SizeOf( TBITMAPFILEHEADER );
BITMAPINFO = SizeOf( TBITMAPINFOHEADER );
Var
FichierTemp :File;
InfoFichier :TBITMAPFILEHEADER;
InfoImage :TBITMAPINFOHEADER;
Begin
AssignFile( FichierTemp, Chemin );
Reset( FichierTemp, 1 );
BlockRead( FichierTemp, InfoFichier, FICHEADER );
BlockRead( FichierTemp, InfoImage, BITMAPINFO );
CloseFile( FichierTemp );
FileSize := InfoFichier.bfSize;
Largeur := InfoImage.biWidth;
Hauteur := InfoImage.biHeight;
BitsPerPixel:= InfoImage.biBitCount;
NbCouleur := InfoImage.biClrUsed;
End;
Les deux constantes en haut contiennent la grosseur en Octet des deux structures principales d'un Bitmap.
Si vous connaissez un peut le fonctionnement de la lecture d'un fichier alors vous comprendrez vite
que l'on ouvre le fichier et on positionne le curseur au début.
Ensuite on récupère les informations dans la première structure et ensuite la deuxième.
C'est ici que le mot réservé Packed Record est important, car on compresse les informations lues dans le format que la structure demande...
Par la suite on ferme le fichier et il nous restes plus qu'a renvoyer les informations récupérées par l'entremise des paramètres de la procédure.
Voici une autre manière plus complex de récupérer chaque informations relatives au Bitmap :
Procedure LireBitmap( Chemin :String );
Var
BmpFichier :File; {Fichier Bitmap}
BmpINFOFichier :TBITMAPFILEHEADER; {Structure #1}
BmpINFO :TBITMAPINFOHEADER; {Structure #2}
GrosseurPal :Cardinal; {Grosseur de la palette}
PtrDebutPal :Pointer; {Pointeur sur le début de la palette}
DimBitmapInfo :Cardinal; {Grosseur de la structure TBITMAPINFO}
pBmpINFO :PBITMAPINFO; {Pointeur sur une structure TBITMAPINFO}
pTabPixels :PByte; {Pointeur sur une région de pixels en mémoire}
Begin
{Vérification de l'existance du fichier}
If Not FileExists( Chemin ) Then
Exit;
{Ouverture du fichier}
AssignFile( BmpFichier, Chemin );
Reset( BmpFichier, 1);
{Lecture de l'entête TBITMAPFILEHEADER.
Vérifier si c'est bien un bitmap "BM".}
BlockRead( BmpFichier, BmpINFOFichier, SizeOf(BmpINFOFichier) );
If BmpINFOFichier.bfType <> ( Ord('M') Shl 8 + Ord('B') ) Then
Exit;
{Lecture de l'entête TBITMAPINFO.
Vérifier si la dimension/version de l'entête est correct.}
BlockRead( BmpFichier, BmpINFO, SizeOf(BmpINFO) );
If BmpINFO.biSize <> SizeOf(BmpINFO) Then
Exit;
{Déterminer le nombre de couleur dans la palette}
If BmpINFO.biClrUsed = 0 Then
PalNbCouleurs := 1 Shl BmpINFO.biBitCount
Else
PalNbCouleurs := BmpINFO.biClrUsed;
{Déterminer la grosseur de la palette}
If BmpINFO.biBitCount < 16 Then
GrosseurPal := PalNbCouleurs * SizeOf( TRGBQUAD )
Else
GrosseurPal := 256 * SizeOf( TRGBQUAD );
{Déterminer la dimension de la table de couleur ainsi
que la structure BITMAPINFOHEADER pour enfin connaître
la vraie dimension de la structure
TBITMAPINFO + La table de couleur (TRGBQUAD)
}
DimBitmapInfo := Sizeof( TBITMAPINFOHEADER ) + GrosseurPal;
{Déterminer où débute le tableau de pixel dans le fichier bitmap.
On aurait pu aussi utiliser cette ligne qui calcul l'"offset" déplacement
entre la structure TBITMAPINFO et la fin du fichier, dont ce qui nous
donnes les donnés pixel. Ne pas oublier que TBITMAPINFO contient aussi
un tableau de couleur pour la palette (Si existante)
DimTableauPixel := BmpINFOFichier.bfSize - BmpINFOFichier.bfOffBits;
}
DimTableauPixels := BmpINFO.biHeight *
((((BmpINFO.biWidth * BmpINFO.biBitCount ) + 31) And -31) Shr 3);
{Allocation de mémoire pour récupérer la structure : "TBITMAPINFO"
BITMAPINFOHEADER + Table de couleur (TRGBQUAD)}
GetMem( pBmpINFO, DimBitmapInfo );
{Copier la structure BITMAPINFOHEADER à l'intérieur de TBITMAPINFO}
CopyMemory( pBmpINFO, @BmpInfo, SizeOf(TBITMAPINFOHEADER) );
{Ajouter la palette(TRGBQUAD) à la structure TBITMAPINFO et
par la même occasion récupérer un pointeur sur la palette}
PtrDebutPal := Pointer( Cardinal(pBmpINFO) + SizeOf(TBITMAPINFOHEADER) );
BlockRead( BmpFichier, TBITMAPINFO( PtrDebutPal^ ), GrosseurPal );
{Allouer de la mémoire pour récupérer les pixels du Bitmap}
GetMem( pTabPixels, DimTableauPixels );
{Positionner le curseur de lecture du fichier, sur le début du
tableau de pixels}
Seek( BmpFichier, BmpINFOFichier.bfOffBits );
{Récupérer le tableau de pixels en mémoire}
BlockRead( BmpFichier, pTabPixels^, DimTableauPixels );
{Fermeture du fichier}
CloseFile( BmpFichier );
End;
Ouff! Ça fait pas mal de code tout ça...
J'utilise les pointeurs et la mémoire directement que pour des raisons de rapidité. L'utilisation de variables statiques telque des tableaux Ex :MaPalette = Array[0..256] Of TRGBQuad, ralenti de beaucoup le l'accès aux pixels ou la palette.
Pour des traitements rapide et efficaces il n'y a pas mieux que les pointeurs et la mémoire allouée dynamiquement !
Si vous êtes à l'aise avec les pointeurs et la lecture des fichiers alors je ne crois pas que vous ayez des problèmes mais pour
ceux qui ont de la difficulté alors dites le moi et je verrai ce que je peux faire pou vous aider, peut-être un tutoriel sur le sujet :-)
|