WM_PAINT für doofe

Thomasio

Erfahrenes Mitglied
Ich sitze davor und verstehe Bahnhof, sieht ganz leicht aus, aber irgendwie fällt der Groschen trotzdem nicht

Ziel: Ich will im Speicher aus mehreren Bitmaps die nachher teilweise übereinander liegen eine einzige bauen, und diese dann am Stück ins Fenster zeichnen
Ich habe 100 Artikel zu WM_PAINT, BitBlt und double buffering gelesen, auch in diesem Forum, aber ich verstehe es nicht, irgendwie fehlt mir die Verbindung der Teile

Mit einer Grafik und ohne Zwischenspeichern habe ich es so:

Code:
case WM_PAINT:
{

BITMAP bm;
PAINTSTRUCT ps;

HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, meineBMP);
GetObject(meineBMP, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);

return 0;

}
break;

Wenn ich nun mehrere Grafiken zeichnen will, verdoppele ich einfach alles was zwischen BeginPaint und EndPaint steht, nur mit einer anderen BMP und anderen Koordinaten im BitBlt, so weit so einfach

Aber wenn ich das ganze vorher off screen zusammen bauen will stehe ich auf dem Schlauch und weiss nicht mal genau warum
BitBlt braucht eine QuellDC und eine ZielDC also brauche ich dann im Ganzen 3 DC´s, ausserdem muss ich mit CreateCompatibleBitmap() einen Bereich definieren, wo der off screen Inhalt erstmal rein soll

THEORETISCH .......
Wenn ich das habe, kann ich mit BitBlt sämtliche nötigen Grafiken der Reihe nach in die off screen Bitmap zeichnen und hinterher wieder mit BitBlt die off screen Bitmap ins Fenster zeichnen

ABER ........
So einfach das auch klingt, ich verliere jedesmal zwischen den ganzen Create, Select, Get, HDC, BITMAP und HBITMAP die Übersicht, oder kurz, ich krieg es nicht auf die Reihe

Kann mir jemand auf die Sprünge helfen?
 
Hallo,

mit den 3 DCs bist du schon auf dem richtigen Weg. Der Trick besteht eigentlich nur darin, mit
"CreateCompatibleBitmap" im Speicher die Größe des Zielbitmaps festzulegen. Der Rest wie gehabt: Statt auf den "richtigen" DC kopierst du
die einzelnen Bitmaps auf den Zwischenspeicher-DC und diesen dann am Schluss auf den Ziel-DC.
Das könnte dann etwa so aussehen:
C++:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// Zwischenspeicher anlegen

HDC     hdcMemAll = CreateCompatibleDC(hdc);
HBITMAP hbmAll    = CreateCompatibleBitmap( hdc,
                                            WW,   // Breite des Gesamtbitmaps
                                            HH    // Höhe des Gesamtbitmaps
                                           );

HBITMAP hbmAllOld = (HBITMAP)SelectObject(hdcMemAll, hbmAll);

// Einzelnes Bitmap in Zwischenspeicher kopieren

HDC     hdcMemBmp = CreateCompatibleDC(hdc);
HBITMAP hbmOld    = (HBITMAP)SelectObject(hdcMemBmp, meineBMP);
BITMAP bm;
GetObject(meineBMP, sizeof(bm), &bm);
BitBlt( hdcMemAll,
        XX,          // Einfügeposition X
        YY,          // Einfügeposition Y
        bm.bmWidth,
        bm.bmHeight,
        hdcMemBmp,
        0,
        0,
        SRCCOPY );

SelectObject(hdcMemBmp, hbmOld);
DeleteDC(hdcMemBmp);

// TODO: Nächstes Bitmap

// Fertiges Gesamtbild kopieren

BitBlt( hdc,
        XX,   // Einfügeposition X
        YY,   // Einfügeposition Y
        WW,   // Breite des Gesamtbitmaps
        HH    // Höhe des Gesamtbitmaps
        hdcMemAll,
        0,
        0,
        SRCCOPY );

SelectObject(hdcMemAll, hbmAllOld);
DeleteDC(hdcMemAll);

EndPaint(hwnd, &ps);
Gruß
MCoder
 
Herzlichen Dank, das war wirklich sehr übersichtlich und klasse erklärt

Noch eine kleine Zusatzfrage, bezüglich der Grösse des Fensters
Irgendwie komme ich immer ins Schleudern, bzw. ich habe nicht völlig begriffen, was GetWindowRect() mir da ausgibt
Wenn rect.top == 0 und rect.left == 0 dann ist rect.right die Breite und rect.bottom die Höhe, so weit einfach
Aber wenn top und/oder left != 0 und dann mein BitBlt nicht oben links beginnen soll sondern z.B. bei 50, 50, dann begreife ich nicht, wie ich aus &rect die richtigen Werte errechnen kann
Da fange ich jedesmal an mir mit MessageBox die Werte anzeigen zu lassen und dann mit Trial & Error experimentieren, bis es passt, aber ich hätte gerne den Sinn dahinter verstanden
 
GetWindowRect() liefert immer absolute Koordinaten, bezogen auf den Desktop. Außerdem werden dabei auch die Größen
von Fenstertitel und Rahmen mit berücksichtigt. Im Gegensatz dazu liefert GetClientRect() die Größe der nutzbaren
Fenstergröße, also ohne Titel und Rahmen. Top/left ist dann immer 0.
Will man die Größe und Position eines Controls auf einem Fenster bestimmen, ermittelt man mit GetWindowRect() die
absoluten Koordinaten des Controls und rechnet diese dann mit ScreenToClient() auf die relativen Koordinaten des Fensters um.
Zeichenfunktionen verwenden immer relative Koordinaten.

Gruß
MCoder
 
Der Vollständigkeit halber sei angemerkt:

1) Vor EndPaint fehlt noch

Code:
DeleteObject(hbmAll);

2) Wenn man mit

Code:
case WM_ERASEBKGND:
{
return 1;
}
break;

verhindert dass der Hintergrund gelöscht wird und die fertige Grafik am Ende mit:

Code:
BitBlt(hdc,
         ps.rcPaint.left,
         ps.rcPaint.top,
         ps.rcPaint.right,
         ps.rcPaint.bottom,
         hdcMemAll,
         ps.rcPaint.left,
         ps.rcPaint.top,
         SRCCOPY);

überträgt, dann geht das selbst bei grossen Grafiken auf langsamen Rechnern ohne Flackern
(rcPaint is ein RECT was die minimale update Region des Fensters enthält)
 
Zuletzt bearbeitet:
Zurück