Quali sono le differenze tra una bitmap di Windows e DIBSection?
Domanda
Sto caricando una DIBSection da un file con quanto segue:
HBITMAP bmpIn = (HBITMAP) LoadImage(NULL, _T("c:\\Temp\\Temp.bmp"), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
Empiricamente ho scoperto le seguenti differenze tra la bitmap caricata e le bitmap che ho usato in passato, ma non riesco a trovare alcuna documentazione che indichi che dovrebbe esserci una differenza.
- Le righe sono ordinate in memoria dall'alto verso il basso anziché dal basso verso l'alto. Ho verificato che il file .bmp stesso sia ordinato dal basso verso l'alto.
- Il riempimento della riga è un multiplo di 2 byte anziché 4.
Ho anche scoperto una differenza documentata quando utilizzi CreateDIBSection
per creare una DIBSection da zero.
- I valori DIBSECTION.dsHandle e BITMAP.bmBits restituiti da
GetObject
saranno NULL.
Dov'è la documentazione per le prime due differenze e mi manca qualcosa? Questo è con Windows 7 ma non riesco a immaginare che sarebbe diverso per altre versioni di Windows.
Modifica: alcuni dettagli aggiuntivi. Ecco un dump esadecimale di temp.bmp
; è un'immagine 7x7 con una striscia bianca lungo il lato destro e valori blu che aumentano lungo il lato sinistro (0x10,0x20, ecc.). Puoi vedere che l'ultima riga (00,00,70) è la prima e che ci sono 3 byte di riempimento.
00: 42 4d de 00 00 00 00 00 00 00 36 00 00 00 28 00
10: 00 00 07 00 00 00 07 00 00 00 01 00 18 00 00 00
20: 00 00 a8 00 00 00 00 00 00 00 00 00 00 00 00 00
30: 00 00 00 00 00 00 70 00 00 00 00 00 00 00 00 00
40: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 60 00
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
60: ff ff ff 00 00 00 50 00 00 00 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 40 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: ff ff ff 00 00 00 30 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 20 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: ff ff ff 00 00 00 10 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00
Ecco un programma di esempio per leggere il file .bmp e scriverne il contenuto. Ho rimosso il controllo degli errori per la brevità.
int _tmain(int argc, _TCHAR* argv[])
{
HBITMAP bmpIn = (HBITMAP) LoadImage(NULL, argv[1], IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
FILE * out = _tfopen(argv[2], _T("wb"));
DIBSECTION obj = {0};
GetObject(bmpIn, sizeof(obj), &obj);
cout << "dsBm.bmHeight = " << obj.dsBm.bmHeight << endl;
cout << "dsBmih.biHeight = " << obj.dsBmih.biHeight << endl;
cout << "sizeof(DIBSECTION) = " << sizeof(DIBSECTION) << endl;
fwrite(&obj, sizeof(DIBSECTION), 1, out);
int stride = (((obj.dsBmih.biWidth * obj.dsBmih.biBitCount) + 15) / 16) * 2;
int bytecount = abs(obj.dsBmih.biHeight) * stride;
vector<BYTE> bits(bytecount);
GetBitmapBits(bmpIn, bytecount, &bits[0]);
fwrite(&bits[0], 1, bytecount, out);
fclose(out);
return 0;
}
Ed ecco l'output del programma precedente insieme a un dump esadecimale del file prodotto:
dsBm.bmHeight = 7
dsBmih.biHeight = 7
sizeof(DIBSECTION) = 84
00: 00 00 00 00 07 00 00 00 07 00 00 00 18 00 00 00
10: 01 00 18 00 00 00 11 00 28 00 00 00 07 00 00 00
20: 07 00 00 00 01 00 18 00 00 00 00 00 a8 00 00 00
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00
60: 00 00 00 00 00 00 ff ff ff 00 20 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff 00
80: 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 00 00 ff ff ff 00 40 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 ff ff ff 00 50 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff
c0: ff 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 ff ff ff 00 70 00 00 00 00 00 00 00
e0: 00 00 00 00 00 00 00 00 00 00 ff ff ff 00
Soluzione
Chiama GetDIBits invece di GetBitmapBits.La documentazione per GetBitmapBits ( qui )indica che questo restituisce dati per una bitmap dipendente dal dispositivo, mentre tu hai una bitmap indipendente dal dispositivo.Indicano anche che questa chiamata non deve essere utilizzata ed è disponibile solo per la compatibilità a 16 bit.Quindi, usare GetDIBits dovrebbe fare il trucco.