Programmieren mit C++

Borland C++

Grafik

Bitmap von BMP-Datei laden

Wie wird ein geräteabhängiges Bitmap-Objekt aus einer BMP-Datei erzeugt?

Frage

Zum Erzeugen eines CBitmap-Objekts aus einer BMP-Datei muß die Datei in den Speicher eingelesen werden, damit via CreateDIBitmap() ein HBITMAP-Handle erzeugt und dem CBitmap-Objekt zugewiesen werden kann. Das Hauptproblem dabei besteht darin, die Farben korrekt zu übernehmen. Für Farbcodierungen mit mehr als 8 Bits pro Pixel sind die Farben in den Bitmapdaten kodiert. Für 256 oder weniger Farben zeigt der nachfolgende Beispielquellcode die Vorgehensweise.

Eine BMP-Datei besteht aus vier logischen Blöcken. Am Anfang der Datei steht eine BITMAPFILEHEADER-Struktur, die neben der BMP-Signatur die Größe der Bitmap sowie den Offset auf den Beginn der Bitmapdaten speichert.

Die BITMAPINFOHEADER-Struktur liefert Informationen über die Höhe und Breite der Bitmap sowie die Anzahl der benutzten Farben. Die Farben selbst folgen anschließend in Form einer Farbtabelle, die zwei oder mehr RGBQUAD- Strukturen zur Definition von Farben enthält. Zumindest eine Vorder- und eine Hintergrundfarbe müssen immer vorhanden sein, woraus sich ergibt, daß zumindest zwei Farben definiert sind, auch wenn es sich um eine monochrome Bitmap handelt.

Als letztes folgen die Bitmapdaten, die als geräteunabhängige Bitmap (DIB) abgelegt sind. Die nachfolgend beschriebene Funktion LoadBMPImage() liest eine BMP-Datei ein und erzeugt daraus eine geräteabhängige Bitmap, die einem CBitmap-Objekt zugewiesen wird. Wurde ein gültiger Zeiger auf eine logische Palette übergeben, wird zusätzlich eine Palette erzeugt und returniert. Der Parameter pPal darf jedoch auch NULL sein, wenn keine Palette returniert werden soll.

Im Erfolgsfall lautet das Funktionsergebnis TRUE, ansonsten FALSE. Sofern TRUE returniert wird, ist in bitmap ein gültiges CBitmap-Objekt enthalten.

Der Name der BMP-Datei wird in sBMPFile übergeben.

Lösung

BOOL LoadBMPImage(LPCTSTR sBMPFile, 
                  CBitmap& bitmap, 
                  CPalette *pPal)
{
 CFile file;
 if( !file.Open( sBMPFile, CFile::modeRead) )
  return FALSE;
 
 BITMAPFILEHEADER bmfHeader;

Nach dem Öffnen der Datei wird zunächst überprüft, ob es sich um eine BMP-Datei handelt, was an der Signatur „BM“ im ersten Wort zu erkennen ist.

 if (file.Read((LPSTR)&bmfHeader, 
               sizeof(bmfHeader)) != sizeof(bmfHeader))
  return FALSE;
 
 if (bmfHeader.bfType != ((WORD) ('M' << 8) | 'B'))
   return FALSE;

Liegt eine gültige BMP-Datei vor, kann nun die Länge der verbleibenden Datei bestimmt und ausreichend Speicher zur Aufnahme der Datei allokiert und der Rest der Datei anschließend in diesen Speicherbereich geladen werden.

 DWORD nPackedDIBLen = file.GetLength() - 
                       sizeof(BITMAPFILEHEADER);
 HGLOBAL hDIB = ::GlobalAlloc(GMEM_FIXED, nPackedDIBLen);
 if (hDIB == 0)
  return FALSE;
 
 if (file.ReadHuge((LPSTR)hDIB, nPackedDIBLen) != nPackedDIBLen )
 {
  ::GlobalFree(hDIB);
  return FALSE;
 }

Ist das Feld bmiHeader.biClrUsed Null, muß die Anzahl der Farben aus der Anzahl der Bits pro Pixel bestimmt werden.

 BITMAPINFOHEADER &bmiHeader = *(LPBITMAPINFOHEADER)hDIB ;
 BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
 
 int nColors = bmiHeader.biClrUsed ? bmiHeader.biClrUsed : 
                1 << bmiHeader.biBitCount;
 
 LPVOID lpDIBBits;
 if( bmInfo.bmiHeader.biBitCount > 8 )
  lpDIBBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors +  
              bmInfo.bmiHeader.biClrUsed) + 
              ((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 
                3 : 0));
 else
  lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);

Ist pPal ungleich NULL und beträgt die Anzahl der Farben maximal 256, wird eine Palette erzeugt und mit den Werten der Farbtabelle belegt.

 if( pPal != NULL )
 {
  if( nColors <= 256 )
  {
   UINT nSize = sizeof(LOGPALETTE) + 
                (sizeof(PALETTEENTRY) * nColors);
   LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
 
   pLP->palVersion = 0x300;
   pLP->palNumEntries = nColors;
 
   for( int i=0; i < nColors; i++)
   {
    pLP->palPalEntry[i].peRed   = bmInfo.bmiColors[i].rgbRed;
    pLP->palPalEntry[i].peGreen = bmInfo.bmiColors[i].rgbGreen;
    pLP->palPalEntry[i].peBlue  = bmInfo.bmiColors[i].rgbBlue;
    pLP->palPalEntry[i].peFlags = 0;
   }
 
   pPal->CreatePalette( pLP );
 
   delete[] pLP;
  }
 }

Die erzeugte Palette wird nicht nur zurückgeliefert, sondern auch für das Erzeugen des Bitmap-Objekts verwendet. Dazu muß die Palette in den temporären Ausgabekontext selektiert und realisiert werden. Der Kontext nimmt Bezug auf Gerät NULL, also den Default-Bildschirm.

 CClientDC dc(NULL);
 CPalette* pOldPalette = NULL;
 if( pPal )
 {
  pOldPalette = dc.SelectPalette( pPal, FALSE );
  dc.RealizePalette();
 }

Die eigentliche Umwandlung erledigt die Funktion CreateDIBitmap, deren Parameter in ihrer Bedeutung im nachfolgenden Aufruf als Kommentar dokumentiert sind.

HBITMAP hBmp = CreateDIBitmap( 
     dc.m_hDC,         // Handle auf Ausgabekontext 
     &bmiHeader,       // Zeiger auf Bitmapgröße und Formatdaten
     CBM_INIT,         // Flag: Initialisieren 
     lpDIBBits,        // Zeiger auf Initialisierungdaten
     &bmInfo,          // Zeiger auf Bitmap Farbformatdaten
     DIB_RGB_COLORS);  // Farbcodierung
 bitmap.Attach( hBmp );
if( pOldPalette )
  dc.SelectPalette( pOldPalette, FALSE );
 
 ::GlobalFree(hDIB);
 return TRUE;
}

Funktion LoadBMPImage

Wie wird das aus einer BMP-Datei geladene CBitmap-Objekt angezeigt?

Frage

Grundsätzlich ist es nicht notwendig, ein CBitmap-Objekt zu erzeugen, um die Bitmap anzuzeigen, da die Anzeige auch aus einer DIB heraus möglich ist. Allerdings kann eine geräteabhängige Bitmap deutlich schneller angezeigt werden, was speziell bei mehrfacher Ausgabe von Vorteil ist.

Die nachfolgend vorgestellte Funktion DrawBitmap() ist eine einfache Funktion zur Ausgabe einer Bitmap in einen vorgegebenen Ausgabekontext unter Verwendung einer spezifizierten Palette. Die Ausgabe erfolgt hier in der linken oberen Ecke des Ausgabekontextes. Es ist jedoch ohne weiteres möglich, die Funktion so abzuändern, daß die Plazierung frei wählbar ist. Ebenso läßt sich anstelle von BitBlt() auch StrechBlt() zur Größenänderung der Bitmap während der Anzeige einsetzen. Die hier gezeigte Funktion bildet somit lediglich die Grundlage einer individuell gestaltbaren Servicefunktion, die hier den Ausgabekontext, die Bitmap und eine Palette als Parameter übernimmt.

Lösung

void DrawBitmap(CDC* pDC, 
                CBitmap& bitmap, 
                CPalette *pPal )
{

Für die Ausgabe wird als erstes ein kompatibler Speicherkontext erzeugt.

 CDC memDC;
 memDC.CreateCompatibleDC( pDC );
 memDC.SelectObject( &bitmap );

Sofern eine Palette übergeben wurde, wird diese selektiert und realisiert.

 if( pPal != NULL && pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE )
 {
  pDC->SelectPalette( pPal, FALSE );
  pDC->RealizePalette();
 }

Abschließend erfolgt die Ausgabe der Bitmap über BitBlt().

 BITMAP bm;
 bitmap.GetBitmap( &bm );
 
 pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, 
             &memDC, 0, 0,SRCCOPY);
}

Funktion DrawBitmap





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 01:20:50 von textarchiv.alojado.de