Transparente
Bitmap
Anstelle vieler Vorworte steigen wir gleich ein die Praxis ein und gehen ans Darstellen
einer transparenten Bitmap. Zuerst muss die Bitmap natürlich wieder geladen werden. Wir tun dies
im Beispiel im Konstruktor der Ansichtsklasse mit der bereits bekannten API-Funktion LoadImage(...).
Und da alles, was irgendwie einmal erzeugt bzw. geladen wurde auch wieder freigegeben
werden sollte, löschen wir die Bitmap entsprechend mittels DeleteObject(...) im Destruktor.
CTransBmpView::CTransBmpView()
{
// ZU ERLEDIGEN: Hier Code zur Konstruktion einfügen,
// Bitmap fuer transparent
Darstellung laden
m_hTransBmp =
(HBITMAP)::LoadImage(NULL,"wizard.bmp",
IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
ASSERT(m_hTransBmp);
}
CTransBmpView::~CTransBmpView()
{
// geladene Bitmap wieder freigeben
::DeleteObject(m_hTransBmp);
} |
Damit haben wir zunächst ein Handle auf die darzustellende Bitmap. Den Rest der
Aufgabe erledigen wir in den beiden CWnd-Methoden OnInitialUpdate(...)
und OnDestroy(...). In der Methode OnInitialUpdate(...) erstellen wir
zunächst (wie gewohnt) einen Speicher-DC, in den die geladene Bitmap selektiert wird.
void CTransBmpView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
BITMAP BitmapParam;
// Parameter der Bitmaps
// DC des Views holen
CDC *pViewDC = GetDC();
// --- Speicher-DC fuer transparente
Bitmap erstellen ---
// zuerst temp. CBitmap-Objekt aus HBITMAP erstellen
CBitmap *pCBmp =
CBitmap::FromHandle(m_hTransBmp);
// Bitmap-Groesse auslesen
pCBmp->GetBitmap(&BitmapParam);
m_nTransBmpWidth = BitmapParam.bmWidth;
m_nTransBmpHeight = BitmapParam.bmHeight;
// Speicher-DC erstellen
m_pTransMemDC = new CDC;
m_pTransMemDC->CreateCompatibleDC(pViewDC);
// und geladene Bitmap darin auswaehlen
m_pTransMemDC->SelectObject(pCBmp);
....
} |
Soweit im Prinzip nichts Neues. Der Trick beim Darstellen einer transparenten Bitmap
besteht nun darin, sich eine monochrome Bitmap-Maske zu erzeugen, bei der die transparent
erscheinende Farbe durch die Farbe weiß ersetzt wird und die restlichen Farben der Bitmap
durch die Farbe schwarz. Erstellen wir jetzt zuerst eine monochrome Bitmap mit den
gleichen Abmessungen wie die Original-Bitmap. Anschließend wir ein zweiter Speicher-DC
erstellt, in dem die erstellte monochrome Bitmap selektiert wird.
void CTransBmpView::OnInitialUpdate()
{
....
// und geladene Bitmap darin auswaehlen
m_pTransMemDC->SelectObject(pCBmp);
// --- Speicher-DC fuer Bitmap-Maske
erstellen ---
// zuerst monochromes (s/w) CBitmap-Objekt in der gleichen
// Groesse wie eingelesen Bitmap erstellen
CBitmap CMaskBmp;
CMaskBmp.CreateBitmap( m_nTransBmpWidth, m_nTransBmpHeight, 1, 1, NULL
);
// Speicher-DC erstellen
m_pMaskMemDC = new CDC;
m_pMaskMemDC->CreateCompatibleDC(pViewDC);
// und Masken-Bitmap darin auswaehlen
m_pMaskMemDC->SelectObject(&CMaskBmp);
....
} |
Diese monochrome Bitmap ist bis jetzt aber noch mit einem zufälligen Pixelmuster versehen.
Würden Sie die Bitmap jetzt mittels BitBlt(..., SRCCOPY) ausgeben, so würden
Sie etwa das nachfolgende Ergebnis erhalten. Beachten Sie bitte, dass die dargestellte
'Bitmap' nur einfarbig ist.

 |
Damit die Transparenz der Bitmap später besser ersichtlich wird, wurde Fenster mit einem
rot/gelb schraffiertem Hintergrund versehen. |
Sollten Sie beim Ausprobieren eine andere Farbkombination als
schwarz/weiß erhalten so liegt das daran, dass Sie noch eine andere Text- und
Hintergrundfarbe eingestellt haben. Aber was passiert eigentlich, wenn Sie die soeben neu
erzeugte Bitmap mittels BitBlt(...) darstellen? Sie kopieren letztendlich eine
Bitmap aus einem monochromen (einfarbigen) DC in einen mehrfarbigen DC um. Da aber beim
Kopieren die Anzahl der Farben erhöht werden muss, damit die Bitmap wieder richtig
erscheint, werden alle '1' Pixel der Bitmap auf die aktuelle Textfarbe gesetzt und alle
'0' Pixel auf die aktuelle Hintergrundfarbe.
Und nun kommt der eigentliche 'Trick'. Wir kopieren umgekehrt die geladene Bitmap aus dem
mehrfarbigen DC in den vorhin erstellen monochromen DC um.
void CTransBmpView::OnInitialUpdate()
{
....
// und Masken-Bitmap darin auswaehlen
m_pMaskMemDC->SelectObject(&CMaskBmp);
// --- S/W Bitmap erstellen ----
// Farbe des ersten Pixels soll die transp. Farbe sein
// zum Versuchen: Pixel-Position auch einmal auf 200,250 aendern
COLORREF BkgndColor =
m_pTransMemDC->GetPixel(1,1);
m_pTransMemDC->SetBkColor(BkgndColor);
// geladene Bitmap in monochrome Bitmap
umkopieren
// -> alles ausser HG erscheint nun in schwarzer Farbe
m_pMaskMemDC->BitBlt(0,0,m_nTransBmpWidth,m_nTransBmpHeight,
m_pTransMemDC,0,0,
SRCCOPY );
....
} |
Beim Umkopieren erfolgt eine Farbreduktion die nach folgenden Schema durchgeführt
wird. Alle Pixel, die der aktuellen Hintergrundfarbe entsprechen, werden zu weißen Pixel
und alle anderen Farben zu schwarzen Pixel. Damit ergibt sich dann folgende Bitmap im
monochromen DC.

Und mit dieser s/w Maske werden nun alle weiteren Operationen vorgenommen. Zuerst
entfernen wir aus der Original-Bitmap den Hintergrund. Dazu wird zunächst die Maske
invertiert, d.h. der Hintergrund erscheint schwarz (RGB:0x00,0x00,0x00) und der Umriss
der
Bitmap weiß (RGB: 0xFF,0xFF,0xFF). Die so invertierte Maske wird mit der Original-Bitmap
verundet. Bei dieser Verundung wird aus der Original-Bitmap der Hintergrund entfernt, d.h.
er wird durch die Farbe schwarz ersetzt (0x00 AND 0x.. ergibt immer 0x00!).
void CTransBmpView::OnInitialUpdate()
{
....
// geladene Bitmap in monochrome Bitmap umkopieren
// -> alles ausser HG erscheint nun in schwarzer Farbe
m_pMaskMemDC->BitBlt(0,0,m_nTransBmpWidth,m_nTransBmpHeight,
m_pTransMemDC,0,0,
SRCCOPY );
// Aus Orignal-Bitmap Hintergrund
entfernen
m_pTransMemDC->SetBkColor(RGB(255,255,255));
// Negativ von S/W Bitmap, HG ist nun
schwarz (RGB:0,0,0)
m_pMaskMemDC->BitBlt(0,0,m_nTransBmpWidth,m_nTransBmpHeight,
m_pMaskMemDC,0,0,
DSTINVERT );
// geladene Bitmap mit negativ von
S/W-Bitmap verunden
// --> HG der geladenen Bitmap ist nun schwarz
m_pTransMemDC->BitBlt(0,0,m_nTransBmpWidth,m_nTransBmpHeight,
m_pMaskMemDC,0,0,
SRCAND );
// Mask wieder restaurieren (HG ist weiss
= 0xFFFFFF)
m_pMaskMemDC->BitBlt(0,0,m_nTransBmpWidth,m_nTransBmpHeight,
m_pMaskMemDC,0,0,
DSTINVERT );
// DC des Views wieder freigeben
ReleaseDC(pViewDC);
} |
Somit ergibt sich jetzt folgende darzustellende Bitmap bei der die Hintergrundfarbe
durch die Farbe schwarz ersetzt wurde:

Nach der Verundung wird die Maske wieder hergestellt und zum Schluss noch der am Anfang
erstellte ViewDC freigegeben.
Bevor wird jetzt auf die eigentliche Darstellung der Bitmap zu sprechen kommen,
vervollständigen wir noch die Methode OnDestroy(...). Da wir in OnInitialUpdate(...)
zwei Speicher-DC erstellt haben, sollten wir diese auch an geeigneter Stelle wieder
freigeben.
void CTransBmpView::OnDestroy()
{
CView::OnDestroy();
// TODO: Code für die Behandlungsroutine für Nachrichten hier
einfügen
// Speicher-DCs loeschen
delete m_pTransMemDC;
delete m_pMaskMemDC;
} |
So, jetzt ist es endlich soweit, wir stellen die Bitmap transparent dar. Sehen wir uns
zunächst nochmals an was wir bisher haben:
 |
 |
Bitmap-Maske
|
Original-Bitmap
mit
schwarzem Hintergrund
|
Was wir jetzt 'nur' noch tun müssen, sind diese beiden Bitmaps durch geeignete
Kopier-Operationen ins Fenster zu bringen. Zuerst kopieren wir die Bitmap-Maske so ins
Fenster, dass alle Pixel im Fenster, die nicht im schwarzen Bereich liegen, ihren Farbwert
behalten und der Rest, das ist der Umriss der Bitmap, schwarz wird. Dies erreichen Sie
durch den BitBlt(...,SRCAND) Befehl.
void CTransBmpView::OnDraw(CDC* pDC)
{
CTransBmpDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen Daten
hinzufügen
// Bitmap-Umrisse ausschneiden aus
Hintergrund
// Zum Versuchen: Textfarbe einmal auf einen anderen Wert setzen
// Umrisse der Bitmap aus dem HG ausschneiden
pDC->SetTextColor(RGB(0,0,0));
pDC->SetBkColor(RGB(255,255,255));
pDC->BitBlt(20,30,m_nTransBmpWidth,m_nTransBmpHeight,m_pMaskMemDC,0,0,SRCAND);
} |
Die Verundung des Fensterinhalts mit der Bitmap-Maske bringt folgendes Ergebnis:

Zum Schluss wird dann die Original-Bitmap, in der der Hintergrund durch die Farbe
schwarz (RGB:0x00,0x00,0x00!) ersetzt wurde, ins Fenster verodert.
void CTransBmpView::OnDraw(CDC* pDC)
{
....
pDC->BitBlt(20,30,m_nTransBmpWidth,m_nTransBmpHeight,m_pMaskMemDC,0,0,SRCAND);
// Bitmap einkopieren
// Zum Versuchen: auch einmal diesen Aufruf entfernen
pDC->BitBlt(20,30,m_nTransBmpWidth,m_nTransBmpHeight,m_pTransMemDC,0,0,SRCPAINT);
} |
Und als Ergebnis erhalten Sie ein Bitmap deren Hintergrund entfernt wurde.
 |
 |
Original
Bitmap
|
Transparent
einkopierte Bitmap
|
Das fertige Beispiel finden Sie unter 05GDI\TransBmp.
|