Példa: bmp_one

GDI

 

A windóz valódi nagy dobása a DOS-hoz képest, hogy nem kell ismernünk a hardvert ami a grafikáért felelős. Ehelyett egy grafikus eszköz illesztő felületet (Graphics Device Interface - GDI) biztosít számunkra. A GDI olyan általános grafikai készletet használ, amivel rajzolhatunk a képernyőre, a memóriába, vagy akár a nyomtatóra.

Eszköz környezetek

 

Van egy úgynevezett eszköz környezet (device context - DC), amit a HDC (Handle of Device Context) adattípus képvisel számunkra. Ez egy olyan kezelő, ami a képernyőt szimbolizálja, vagy az ablakot, a memóriát, egy képet, vagy éppen a nyomtatót. A szép az egészben az, hogy nem is kell érdekeljen hogy ténylegesen melyik, hiszen ugyanúgy kell kezelni mindet.

Ha például az ablakra akarunk rajzolni, akkor fogjuk a HDC-t amit a GetDC() szolgáltat (ez képviseli az ablakot), és ezzel a HDC-vel használhatjuk a GDI bármelyik funkcióját. Kitehetünk képet ( BitBlt() ),  írhatunk szöveget ( TextOut() ), húzhatunk vonalat ( LineTo() ) ésatöbbiésatöbbi.

Bitképek

 

A bitképeket ugyanúgy tölthetjük be mint a korábbi példákban az ikonokat, erre a LoadBitmap() szolgál. Ez egyszerűen betölti a bitkép erőforrást. Használhatjuk a LoadImage() hívást is, egy képfájlokhoz van (*.bmp).

Most jön a csavar!

A bitkép objektumokat (HBITMAP típus) nem tudjuk közvetlenül rajzoltatni! Ahhoz hogy a rajzoló funkciókat a bitképen használni tudjuk, az kell hogy létrehozzunk egy Memory DC-t, majd a SelectObject()-tel kiválasztni a HBITMAP-ot. TODO: Ezt most nem értem.

Ennek az lesz a hatása, hogy ha valamit ügyködünk a HDC eszközön, akkor az megjelenik a bitképen is.

GDI szivárgás

 

Van egy kis gond a HDC-kkel. A számuk korlátozott. Ez főképpen a Windows95 előtti verzióknál volt nagyon durva, mert a programunk megosztva használja a rendszerrel. Akár működésképtelenné is teheti azt! Szerencsére a Windows2000 és XP óta ez már nem játszik, mert ha gáz van, akkor felszabadítják maguknak amit kell. NT alapú rendszereknél már elméletileg nem tudjuk lenyúlni a rendszer erőforrásait. Kivéve ha hiba csúszik a gépezetbe. Ha egy ideig jól működik a programunk, aztán egyszercsak elkezd furcsaágokat művelni rajzolás közben, akkor ez utalhat arra, hogy GDI szivárgás esete áll fenn.

Itt kifejtjük hogy vissza kell hozni a már használt GDI erőforrást hogy aztán felszabadíthassuk, ellenkező esetben az újból és újból foglalás miatt kifogyhatunk a GDI erőforrásokból. Volt is gond ebből egy képernyővédőnél.

Bitképek megjelenítése

 

A rajzoló műveleteket a WM_PAINT végzi el. Amikor az ablakunk először jelenik meg, vagy lecsukott állapotból újra kinagyítjuk, akkor a WM_PAINT üzenet indul útjára, hogy az ablak tartalmát újra kell rajzolni. Amikor valamit kirajzoltunk, akkor az nem marad úgy! Minden alkalommal amikor kérés érkezik, sajnos újra kell rajzolni!

 HBITMAP g_hbmBall = NULL;

    case WM_CREATE:
        g_hbmBall = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BALL));
        if(g_hbmBall == NULL)
            MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK | MB_ICONEXCLAMATION);
    break;

 Első lépés természetesen a bitkép betöltése. Ez ugyanúgy megy mint erőforrás esetén, gyerünk tovább!

   case WM_PAINT:
    {
        BITMAP bm;
        PAINTSTRUCT ps;

        HDC hdc = BeginPaint(hwnd, &ps);

        HDC hdcMem = CreateCompatibleDC(hdc);
        HBITMAP hbmOld = SelectObject(hdcMem, g_hbmBall);

        GetObject(g_hbmBall, sizeof(bm), &bm);

        BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);

        SelectObject(hdcMem, hbmOld);
        DeleteDC(hdcMem);

        EndPaint(hwnd, &ps);
    }
    break;

Felvezetjük a használni kívánt változóinkat. Figyeljük meg, hogy az első típusa BITMAP, és nem HBITMAP!  A BITMAP egy olyan struktúra, ami információt tartalmaz az aktuális GDI objektum HBITMAP-járól. Szükséges megállapítnunk a HBITMAP szélességét és magasságát, erre a GetObject()-et használjuk. Ez nem magát az objektumot adja vissza, csak infókat róla. A helyes név a GetObjectInfo() lett volna. Ezt kaptuk, használjuk. A GetObject() különböző GDI aobjektumokkal együtt tud működni, ami a második paraméter alapján képes a struktúra méretét megállapítani.

A PAINTSTRUCT egy olyan struktúra, ami az ablak festéséről tartalmaz információkat. Most egyszerűen nem foglalkozunk vele, de a BeginPaint(), és EndPaint() működéséhez szükséges lesz. A BeginPaint() ahogy a neve is jelzi, a WM_PAINT üzenetet kezeli. Ha nem kezeljük a WM_PAINT üzenetet, akkor a GetDC()-re lesz szükségünk, ahogyan azt az időzítős animációs példánál látni fogjuk. Addig viszont használjuk a BeginPaint(), és EndPaint() párost!

A BeginPaint() visszatér a HDC-vel, ami a HWND-t képviseli. Minden rajzolási művelet amit a HDC-vel végzünk, azonnal megjelenik a képernyőn.

A Memória DC bitképre állítása

 

Annak érdekében hogy rajzolhassunk a bitképre, szükséges egy DC-t létrehozni a memóriában. A könnyebb út a CreateCompatibleDC(). Kapunk egy Memory DC-t, ami átveszi a képernyő színmélységét és beállításait. Most meghívjuk a SelectObject()-et hogy kiválasszuk a bitképet, ezzel megkapjuk az alap képet, amit később kicserélhetünk. Ezzel elkerülhetjük a GDI szivárgást.

Rajzolás

 

Ahogy megvannak a bitkép méretei, kitöltjük a BITMAP struktúrát. Meghívjuk a BitBlt()-t, ezzel átmásoljuk a képet a memória DC-ből az ablak DC-jébe, amivel megjelenítjük a képernyőn. Mint mindig, ennek is utána nézhetünk az MSDN-en, de röviden itt is összefoglaljuk a lényeget: A cél, a pozíció, a méret, a forrás, a forrás pozíciója, és végül a "raszter művelet" (ROP code) határozzák meg, hogyan történjen a másolás. Most egyszerűen átmásoljuk és kész.

Takarítás

 

Ezen a ponton már kint lehet kép a képernyőn, ki kell takarítnunk magunk után. Először a Memory DC-t visszaállítjuk az eredeti állapotába, majd kicseréljük a bitmapunkat az egyik tárolt változatra. Ezután a DeleteDC()-vel végleg törölhetjük. Az EndPaint()-tel eldobjuk a Window DC-t, amit a BegintPaint() adott.

A HDC megsemmisítése kissé problémás, mert a létrehozásától függően három féleképpen is megtehetjük. Lássuk a párosokat:

GetDC() - ReleaseDC()
BeginPaint() - EndPaint()
CreateCompatibleDC() - DeleteDC()

Végezetül, amikor a programunk véget ér, felszabadítjuk a lefoglalt erőforrásokat. Elvileg ez nem feltétlenül szükséges, hiszen a modern windózok már megteszik ezt maguktól is. Elvileg. Gyakorlatilag azonban elképzelhető hogy vannak még hibák a GDI megvalósításában, szabadítsuk csak fel!

    case WM_DESTROY:
        DeleteObject(g_hbmBall);
        PostQuitMessage(0);
    break;

 

A bejegyzés trackback címe:

https://win32learning.blog.hu/api/trackback/id/tr166300841

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása