Bonjour à tous
Qui aurais pensé que lire un simple fichier BMP sois si compliqué ?
Alors voila après la prise en charge du 24bits je suis passé au 32bits
J'ai trouvé cette suite de test pour fichiers BMP : http://entropymine.com/jason/bmpsuite/
pour tester et prendre en charge le maximum de cas possible.
Au cas ou voici les liens sur lesquel j'essaye de m'appuyer
Informations sur le format BMP :
- https://fr.wikipedia.org/wiki/Windows_bitmap
- https://en.wikipedia.org/wiki/BMP_file_format (plus complet que la version française)
- http://www.alrj.org/docs/formats/bmp/BMP.htm
- http://netghost.narod.ru/gff/graphic...ary/os2bmp.htm
- http://www.drdobbs.com/the-bmp-file-...4409533?pgno=5
Compression/Decompression :
- http://netghost.narod.ru/gff/graphics/book/ch09_01.htm
- https://fr.wikipedia.org/wiki/Codage_de_Huffman
- http://tcharles.developpez.com/Huffman/
Et là bien ; c'est du 50% de réussite et 50% d'échec. grosso modo.
Voilà un capture d'écran des fichiers BMP 32Bits de test, vus dans l'explorateur de Windows.
Entouré en jaune ce sont les fichiers que je charge et j'affiche correctement.
Je n'arrive pas à comprendre comment Lazarus affiche correctement tous les fichiers.
Les procédures et fonctions sur lesquelles je me suis basé sont celles présentes dans
la classe TLazReadDIB de l'unité IntGraphics et dans l'unité fpBMPReader.
1er cas
avec les BMP encodés à 100% correctement
- le fichier rgb32bfdef.bmp, c'est Ok
- le fichier rgb32bf.bmp, paf
Avez vous remarquez la valeur de "Depth" et d' "AlphaShift" pour le TBitmap ?
Il le traite comme un 24bits !! et l'alphashift est à zero. Comparez avec "TGLZBitmap" à droite.
Nb :
- regardez la version du fichier BMP dans le memo à droite et en bas.
- Les valeurs de RedMask, GreenMask, BlueMask, sont les valeurs lues dans
le fichier bmp si la version de l'en-tête > 1. sinon valeur par defaut
AlphaMask est lues dans le fichier bmp si la version de l'en-tête > 2, sinon valeur par defaut
- Les valeurs de Shift et MaskSize sont calculées
Ici je n'arrive pas à savoir comment le RawImage image du bitmap passe à 24bits ? ou plus
précisément comment il determine l'AlphaShift à zero ?
mon code de récupération des données pour le BitField:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283 Function TGLZBitmapBMPImage.GetHeaderRedMask: LongWord; Begin if FHeaderType>= bmpht_WindowsV2 then begin Case FHeaderType Of bmpht_WindowsV2: Result := LEToN(FInfoHeader.WindowsV2.biRedMask); bmpht_WindowsV3: Result := LEToN(FInfoHeader.WindowsV3.biRedMask); bmpht_WindowsV4: Result := LEToN(FInfoHeader.WindowsV4.biRedMask); bmpht_WindowsV5: Result := LEToN(FInfoHeader.WindowsV5.biRedMask); End; end else begin {$ifdef ENDIAN_BIG} Result := $0000FF00; {$else} Result :=$00FF0000; {$endif} end; End; Function TGLZBitmapBMPImage.GetHeaderGreenMask: LongWord; Begin if FHeaderType>= bmpht_WindowsV2 then begin Case FHeaderType Of bmpht_WindowsV2: Result := LEToN(FInfoHeader.WindowsV2.biGreenMask); bmpht_WindowsV3: Result := LEToN(FInfoHeader.WindowsV3.biGreenMask); bmpht_WindowsV4: Result := LEToN(FInfoHeader.WindowsV4.biGreenMask); bmpht_WindowsV5: Result := LEToN(FInfoHeader.WindowsV5.biGreenMask); End; end else begin {$ifdef ENDIAN_BIG} Result := $00FF0000; {$else} Result := $0000FF00; {$endif} end; End; Function TGLZBitmapBMPImage.GetHeaderBlueMask: LongWord; Begin if FHeaderType>= bmpht_WindowsV2 then begin Case FHeaderType Of bmpht_WindowsV2: Result := LEToN(FInfoHeader.WindowsV2.biBlueMask); bmpht_WindowsV3: Result := LEToN(FInfoHeader.WindowsV3.biBlueMask); bmpht_WindowsV4: Result := LEToN(FInfoHeader.WindowsV4.biBlueMask); bmpht_WindowsV5: Result := LEToN(FInfoHeader.WindowsV5.biBlueMask); End; end else begin {$ifdef ENDIAN_BIG} Result := $00FF0000; {$else} Result := $000000FF; {$endif} end; End; Function TGLZBitmapBMPImage.GetHeaderAlphaMask: LongWord; Begin if FHeaderType>= bmpht_WindowsV3 then begin Case FHeaderType Of bmpht_WindowsV3: Result := LEToN(FInfoHeader.WindowsV3.biAlphaMask); bmpht_WindowsV4: Result := LEToN(FInfoHeader.WindowsV4.biAlphaMask); bmpht_WindowsV5: Result := LEToN(FInfoHeader.WindowsV5.biAlphaMask); Else Result:=0 End; end else begin {$ifdef ENDIAN_BIG} Result := $000000FF; {$else} Result := $FF000000; {$endif} end; End; ... function TGLZBitmapBMPImage.ReadImageProperties:Boolean; function GetMaskShift(AMask: LongWord): ShortInt; begin Result := 0; while ((AMask and (1 shl Result)) = 0) and (Result < 32) do inc(Result); // if result = 32 then result:=0; end; function GetMaskSize(AMask: LongWord; AShift:ShortInt): Byte; begin Result := 0; while (AShift + Result < 32) and ((AMask and (1 shl (AShift + Result))) <> 0) do Inc(Result); end; begin ... { On a récupéré nos informations, on met à jour le RawImage. de TGLZBitmap On charge la palette de couleur si besoins On initialise quelques variables utiles pour la lecture des données suivant le "PixelFormat" } With RawImage do begin UsedColors:=GetHeaderUsedColors; UsePalette:=False; With Description do begin // Le decalage par defaut pour le bitfield est le format de couleur (A)RGB (*RedShift := 16; GreenShift := 8; BlueShift := 24; AlphaShift :=0;RedMaskSize := 8; GreenMaskSize := 8;// Taille de valeur en Bit BlueMaskSize := 8; AlphaMaskSize := 8;*) // On Initialise le format de couleur gràce au "masque" // Format couleur par Defaut pour le BitField : ARGB RedMask := GetHeaderRedMask; GreenMask := GetHeaderGreenMask; BlueMask := GetHeaderBlueMask; RedShift := GetMaskShift(RedMask); //ShiftCount(RedMask); GreenShift := GetMaskShift(GreenMask); BlueShift := GetMaskShift(BlueMask); RedMaskSize := GetMaskSize(RedMask,RedShift); GreenMaskSize := GetMaskSize(GreenMask,GreenShift); BlueMaskSize := GetMaskSize(BlueMask,BlueShift); AlphaMask := 0; //GetMaskSize(AlphaMask); AlphaShift := 0; AlphaMaskSize :=0; if FHeaderType>bmpht_WindowsV2 then begin AlphaMask := GetHeaderAlphaMask; AlphaShift := GetMaskShift(AlphaMask); AlphaMaskSize := GetMaskSize(AlphaMask,AlphaShift); end else begin // En-tête Version 1, on initialise avec la valeur par defaut if BitCount = 32 then begin {$ifdef ENDIAN_BIG} AlphaMask := := $000000FF; {$else} AlphaMask := $FF000000; {$endif} AlphaShift := GetMaskShift(AlphaMask); AlphaMaskSize := GetMaskSize(AlphaMask,AlphaShift); end; end; end; ... FRowSize:=ComputeBytesPerLine(bmpWidth, bitCount, bleDWordBoundary); Case bitCount of ... 32: begin Description.ColorFormat:=cfBGRA; if Not(Compression=BMP_COMPRESSION_NONE) and not(Compression=BMP_COMPRESSION_BITF) then begin ShowMessage('Bad Compression'); Result:=False; end; if Description.AlphaMaskSize > 0 then begin // Le Mask Alpha cache-t-il une autre composante de la couleur if (Description.RedMask or Description.GreenMask or Description.BlueMask) and Description.AlphaMask <> 0 then begin Description.AlphaMask := 0; Description.AlphaShift := 0; Description.AlphaMaskSize := 0; end; end; // La taille du masque alpha est 0,. C'est un format 24bits. if Description.AlphaMaskSize = 0 then begin //Description.ColorFormat:=cfBGR; BitCount:=24;// Description.AlphaMask := $FF000000; Description.AlphaShift := 0;//GetMaskShift(Description.AlphaMask); // if Description.AlphaShift = 24 then Description.AlphaShift := 0; end; end; end; procedure TGLZBitmapBMPImage.LoadFromMemory(); var YY,Y : Integer; X:Integer; DstColor: TGLZColor; SrcPtr : PLongWord; DstLine : PGLZColor; SrcColor :LongWord; LineBuffer : PByte; function ExpandColor(Value: LongWord): TGLZColor; var tmpr, tmpg, tmpb, tmpa: LongWord; begin tmpr := value and RawImage.Description.RedMask; tmpg := value and RawImage.Description.GreenMask; tmpb := value and RawImage.Description.BlueMask; tmpa := value and RawImage.Description.AlphaMask; Result.Alpha := 255; if RawImage.Description.RedShift < 0 then Result.Red := byte(tmpr shl (-RawImage.Description.RedShift)) else Result.Red := byte(tmpr shr RawImage.Description.RedbitShift); if RawImage.Description.GreenShift < 0 then Result.Green := byte(tmpg shl (-RawImage.Description.GreenShift)) else Result.Green := byte(tmpg shr RawImage.Description.GreenShift); if RawImage.Description.BluebitShift < 0 then Result.Blue := byte(tmpb shl (-RawImage.Description.BlueShift)) else Result.Blue := byte(tmpb shr RawImage.Description.BlueShift); if RawImage.Description.AlphaMaskSize>0 then begin if RawImage.Description.AlphaShift < 0 then Result.Alpha := byte(tmpa shl (-RawImage.Description.AlphaShift)) else Result.Alpha := byte(tmpa shr RawImage.Description.AlphaShift); end; end; begin // On initialise les dimensions de notre bitmap SetSize(bmpWidth, bmpHeight); Case RawImage.Description.PixelFormat of ... pf32Bits: // Pas de compression, formats couleurs suivant "BitField" sinon RGBA begin LineBuffer:=nil; ReallocMem(LineBuffer,FRowSize); Y:=0; repeat Memory.Read(LineBuffer^,FRowSize); if TopDown then YY:=Y else YY:=MaxHeight-Y; if (Compression = BMP_COMPRESSION_BITF) then //LineDecodeBITFIELD32 begin DstLine:=GetScanLine(YY); SrcPtr := PLongWord(LineBuffer); For X:=0 to MaxWidth do begin SrcColor:=(SrcPtr+X)^; DstColor := ExpandColor(SrcColor); if (FHeaderType<bmpht_WindowsV3) then DstColor.Alpha:=255; DstLine^:=DstColor; Inc(DstLine); end; end else LineDecodeBGRA32(LineBuffer,YY,false); Inc(Y); Until (Y>MaxHeight); ReAllocMem(LineBuffer, 0); FreeMem(LineBuffer); LineBuffer:=nil; end; end; end;
2eme Cas avec les BMP encodés non standard et ou avec des erreurs d'encodage
Exemples avec les fichiers BMP : rgb32h56.bmp et rbg32-xbgr.bmp, idem c'est ok ici
Avec le fichier rgba32-1010102.bmp, paf
Ici j'ai également essayé ça sans succes
idem resultat foireux
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 { Counts how many bits are set } function MyCountBits(Value : byte) : shortint; var i,bits : shortint; begin bits:=0; for i:=0 to 7 do begin if (value mod 2)<>0 then inc(bits); value:=value shr 1; end; Result:=bits; end; { If compression is bi_bitfields, there could be arbitrary masks for colors. Although this is not compatible with windows9x it's better to know how to read these bitmaps We must determine how to switch the value once masked Example: 0000 0111 1110 0000, if we shr 5 we have 00XX XXXX for the color, but these bits must be the highest in the color, so we must shr (5-(8-6))=3, and we have XXXX XX00. A negative value means "shift left" } /// ! \\\\ Provient de fpBMPReader que j'ai modifié function ShiftCountEx(Mask : longword; MaskSize:Byte) : shortint; var tmp : shortint; begin tmp:=0; if Mask=0 then begin Result:=0; exit; end; while (Mask mod 2)=0 do { rightmost bit is 0 } begin inc(tmp); Mask:= Mask shr 1; end; tmp:=tmp-(MaskSize-MyCountBits(Mask and $FF)); // tmp:=tmp-(8-MyCountBits(Mask and $FF)); // CODE ORIGINAL Result:=tmp; end; ... RedBitShift:=ShiftCountEx(RedMask,RedMaskSize); GreenBitShift:=ShiftCountEx(GreenMask,GreenMaskSize); BlueBitShift:=ShiftCountEx(BlueMask,BlueMaskSize); ... function ExpandColor(Value: LongWord): TGLZColor; var tmpr, tmpg, tmpb, tmpa: LongWord; begin tmpr := value and RawImage.Description.RedMask; tmpg := value and RawImage.Description.GreenMask; tmpb := value and RawImage.Description.BlueMask; tmpa := value and RawImage.Description.AlphaMask; Result.Alpha := 255; if RedbitShift < 0 then Result.Red := byte(tmpr shl (-RedbitShift)) else Result.Red := byte(tmpr shr RedbitShift); if GreenbitShift < 0 then Result.Green := byte(tmpg shl (-GreenbitShift)) else Result.Green := byte(tmpg shr GreenbitShift); if BluebitShift < 0 then Result.Blue := byte(tmpb shl (-BluebitShift)) else Result.Blue := byte(tmpb shr BluebitShift); if RawImage.Description.AlphaMaskSize>0 then begin if AlphabitShift < 0 then Result.Alpha := byte(tmpa shl (-AlphabitShift)) else Result.Alpha := byte(tmpa shr AlphabitShift); end; end;
Valeurs des "BitShifts"
Pour le fichier rgb32-xbgr.bmp
[STATUS] Blue Mask = $FF000000
[STATUS] Blue Mask = $00FF0000
[STATUS] Blue Mask = $0000FF00
[STATUS] Alpha Mask = $00000000
[STATUS] Red Shift = 24
[STATUS] Green Shift = 16
[STATUS] Blue Shift = 8
[STATUS] Alpha Shift = 32 //---> Ici l'alpha shift est "correct sans être juste"
[STATUS] Red Size = 8
[STATUS] Green Size = 8
[STATUS] Blue Size = 8
[STATUS] Alpha Size = 0
----> Nouvelles variable testés :
[STATUS] Red Bit Shift = 24
[STATUS] Green Bit Shift = 16
[STATUS] Blue Bit Shift = 8
[STATUS] Alpha Bit Shift = 0 //---> Ici l'alpha shift est correct
[STATUS] BitCount = 24
et celle de rgba32-1010102.bmp
[STATUS] Blue Mask = $3FF00000
[STATUS] Blue Mask = $000FFC00
[STATUS] Blue Mask = $000003FF
[STATUS] Alpha Mask = $C0000000
[STATUS] Red Shift = 20
[STATUS] Green Shift = 10
[STATUS] Blue Shift = 0
[STATUS] Alpha Shift = 30
[STATUS] Red Size = 10
[STATUS] Green Size = 10
[STATUS] Blue Size = 10
[STATUS] Alpha Size = 2
[STATUS] BitCount = 32
----> Nouvelles variable testés :
[STATUS] Red Bit Shift = 18
[STATUS] Green Bit Shift = 8
[STATUS] Blue Bit Shift = -2
[STATUS] Alpha Bit Shift = 30
[STATUS] BitCount = 32
Si vous avez des idées ou une explications je suis perdu. Je suis sur que c'est tout bête mais je vois pas, je suis un peu perdu avec tous ces décalges de bits.
Merci d'avance
Partager