24
Tags:
c++
Skrevet af
Bruger #8985
@ 12.05.2009
Forord
Hej, og velkommen til min første artikel på Udvikleren. Måden, jeg vil lære dig GDI på, er ved at vise en masse kodeeksempler, hvis resultater jeg har taget billeder af og indsat under de fleste af koderne. Disse eksempler gør brug af grundlæggende elementer indenfor GDI, som jeg så giver en forklaring af nedenunder billedet med resultatet. Artiklen er skrevet for begyndere indenfor GDI, men et godt kendskab til Win32-API’et er krævet. Sidst men ikke mindst vil jeg bede dig om at læse artiklen grundigt, inden du afgiver din bedømmelse eller kommentar. Ikke at disse er uønskede – det modsatte er faktisk tilfældet – men jeg har før oplevet, at jeg har skrevet en artikel, hvorefter der har været en eller anden torsk, der har brokket sig over, hvorfor jeg ikke har oplyst, hvad GDI står for. Det er lidt småirriterende
!
Hvad er GDI og hvad er formålet?
GDI står for Graphics Device Interface, og består som alle andre biblioteker af en masse typer, funktioner og konstanter. GDI er hverken et API eller et framework, hvilket tillader det at være en kerne "bygget" direkte ind i Win32-API'et, så hvis du har det (altså Win32-API'et) liggende, er du klar til anvende GDI. Hvis ikke Win32-API'et fulgte med din IDE, kan du downloade Windows Platform SDK. Der er forkellige versioner, så hvis du vil have et link, må du søge på Google.
Før jeg kan forklare alle tre bogstaver i GDI tilsammen, er jeg nødt til at forklare de to første. GD står for Graphics Device, og i GDI bruger man noget, der hedder en Device Context. Sådan en har sin egen type i Win32-API'et, og den hedder HDC (Handle to Device Context). Navnet "Device Context" er måske ikke så ligetil, men det er temmelig præcist: En DC repræsenterer det grafiske indhold af en 'device'.
I GDI kan man udføre grafiske operationer på 3 forskellige 'devices': Computerens skærm, computerens hukommelse og en tilsluttet printer. Eksempelvis benytter Windows GDI til at tegne menuerne (HMENU) i et program, en tekstboks ("EDIT") bruger GDI til at vise teksten, og det skulle ikke undre mig, om et spil som Mario er lavet i GDI.
Vores første GDI program
Forestil dig, vi har kodet et lille program, der opretter et tomt GUI vindue. Som alle, der er i stand til at drage nytte af denne artikel, ved, har et Win32-vindue en handle på nøjagtig samme måde, som en device context. Den hedder HWND. Hvis vi opretter en HDC af vores HWND tilhørende GUI vinduet, kan vi med GDI tegne streger, cirkler, firkanter, bitmaps, pixels, skrive tekt og meget mere i vinduet.
Nå, vi må hellere hoppe ud i det, inden du mister interessen, så her kommer et eksempel.
Obs.: For at kompilere koder, der benytter GDI, skal du lænke med gdi32.lib. windows.h er dog stadig den eneste headerfil, vi inkluderer.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// Et Win32-vindues baggrund er af GDI-typen HBRUSH
// Med funktionen GetStockObject kan vi hurtigt få
// fat i de mest normale GDI objekter. Mere om det snart.
// WHITE_BRUSH, en solid, hvid brush, er en af dem.
HBRUSH g_hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow)
{
const TCHAR szAppname[] = TEXT("GdiApp");
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = g_hbrBackground;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szAppname;
wc.lpszMenuName = NULL;
wc.style = CS_DBLCLKS;
RegisterClass(&wc);
hwnd = CreateWindowEx(0L, szAppname,
TEXT("Vinduets proportioner"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_SIZE:
{
// Anskaf en device context for vores vindue
HDC dc = GetDC(hwnd);
TCHAR szProp[32]; // Et char-array, der skal indeholde dimensionerne som tekst
RECT rcClientArea;
// Opfang vinduets indholdsområdes dimensioner
// og fyld vores device context med vinduets
// baggrundsfarve indenfor proportionerne
// gemt i rcClientarea.
// Hvis vi ikke gør dette, vil teksten fra
// forrige WM_SIZE stadig stå der, og teksten
// vil stå oveni hinanden, hvilket ser dumt ud.
GetClientRect(hwnd, &rcClientArea);
FillRect(dc, &rcClientArea, g_hbrBackground);
// Gem en formateret streng i szProp. Prop står for proportioner.
wsprintf(szProp, TEXT("{ %d, %d, %d, %d }"), rcClientArea.left, rcClientArea.top, rcClientArea.right, rcClientArea.bottom);
// Udskriv teksten på vores device context,
// men stadig indenfor proportionerne i rcClientArea.
// DT_CENTER centrerer teksten horisontalt. DT_VCENTER vertikalt.
// DT_SINGLEINE er nødvendig, hvis DT_VCENTER skal påvirke
// tekstens vertikale position på vinduet.
DrawText(dc, szProp, lstrlen(szProp), &rcClientArea, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// Vi er færdige med vores operationer, og vores DC frigøres.
// Ingen grund til, at den fortsat optager plads i hukommelsen.
ReleaseDC(hwnd, dc);
return 0L;
}
case WM_DESTROY:
// Vi har ikke længere brug for vinduets baggrund gemt i en brush,
// da vinduet er ved at lukke. For at forhindre GDI objektet i at
// optage plads i hukommelsen, sletter vi det med GDI-funktionen
// DeleteObject
DeleteObject(g_hbrBackground);
PostQuitMessage(0);
return 0L;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Her ser du resultatet:
Al den kode kan i første omgang virke uoverskuelig og håbløs, så lad os skære al den regulære Win32-kode væk, så vi står tilbage med ren GDI-kode.
HBRUSH g_hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
HDC dc = GetDC(hwnd);
DrawText(dc, szProp, lstrlen(szProp), &rcClientArea, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
ReleaseDC(hwnd, dc);
DeleteObject(g_hbrBackground);
To variabler, to typer, fire funktionskald og fire konstanter. Nu er det ligefrem enkelt. Måske vil jeg endda være i stand til at forklare linjerne.
HBRUSH er en type ligesom int, men en lidt speciel en. Hvis du opretter en variabel af typen HBRUSH, kan du (i Visual Studio og, hvis du er heldig, også i Dev-C++) se, at den kun har ét medlem. Strukturen ser nogenlunde således ud:
typedef struct tagHBRUSH
{
int unused;
} *HBRUSH;
HWND, HDC, HMENU og alle andre "handles" har samme struktur, så du kan i princippet gøre således, uden din IDE brokker sig:
HBRUSH hbr = (HBRUSH)GetDC(hwnd);Det gør dog ikke ligefrem din kode mere overskuelig, og GDI bruger de forskellige typer handles til at skelne mellem GDI-objekterne, så det frarådes.
GetStockObject() er en hurtig måde at få fingre i et bestemt GDI-objekt. Ulempen er, at den ikke har adgang til særlig mange af dem. Her er en liste over værdier, du kan kalde
GetStockObject med:
http://msdn.microsoft.com/en-us/library/dd144925(VS.85).aspxAlt, du kan opnå med
GetStockObject, kan du opnå med kald til andre GDI-funktioner. Hvis du virkelig ønsker at forstå det smarte i at bruge den, kan du sammenligne det med en butik.
GetStockObject viser dig de varer, der er stillet frem, hvilket betyder hurtig betjening men et begrænset udvalg. Med kald til et par andre GDI-funktioner får du adgang til butikkens lager.
Mens en HWND repræsenterer selve vinduet, giver en HDC adgang til vinduets grafiske indhold. Der er et par måder, vi kan få fat i et vindues DC. Et kald til
GetDC er det enkleste. Da et vindue og dets grafiske indhold går hånd i hånd, er det en handle til vores vindue (en HWND), vi giver
GetDC.
DrawText er en funktion som alle andre. Den skriver en tekst på din DC. Du kan med andre GDI-funktioner styre font og farve på teksten. Det kommer vi ind på senere.
DrawText er defineret således:
int DrawText(
HDC hDC,
LPCTSTR lpString,
int nCount,
LPRECT lpRect,
UINT uFormat
);På MSDN står der, at sidste argument er af typen UNIT, men det er en fejl! Den er af typen UINT, som ser således ud:
typedef unsigned int UINT;
lpString er den tekst, du vil skrive på din DC.
nCount er antal tegn, der skal udskrives. Du kan få fat i en strengs længde uden at inkludere iostream således:
lstrlen("Hello, World!");lpRect er en pointer af typen RECT. LP står for Long Pointer, som var et begreb i de tidligere versioner af Windows. Dette argument indeholder tekstens "bounding box". Det er den, der afgør, hvor teksten skal stå i DC’en.
uFormat tillader dig at give funktionen et par ekstra instrukser om, hvordan teksten skal opstilles. Du kan se en liste over mulige værdier her:
http://msdn.microsoft.com/en-us/library/ms901121.aspx .
ReleaseDC sletter en device context oprettet med et kald til
GetDC. Som sagt er der flere måder at oprette en DC på. Hver måde har sin tilsvarende funktion til slettelse af en DC. Mere om det senere.
DeleteObject sletter et GDI-objekt fra hukommelsen. Du bør altid kalde
DeleteObject på et GDI-objekt, når du er færdig med det. Hvis dit program opretter for mange GDI-objekter uden at slette dem, crasher det.
Der er ikke mange ben i at udskrive tekst, når først man har DC'en. Det samme gælder firkanter, cirkler, linjer og så videre. De fleste af dem er blot et kald til en funktion. Hvis vi vil tegne en firkant, kalder vi funktionen
Rectangle. I den type GDI-funktioner er DC'en altid det første argument. Eksempel:
Rectangle(dc, 100, 100, 200, 200);100, 100 er koordinaterne for øverste venstre hjørne af firkanten. 200, 200 er ikke bredde og højde; det er koordinater for nederste højre hjørne. Ønsker vi at tegne en cirkel, gør vi således:
Ellipse(dc, 10, 10, 50, 50);Nøjagtig samme liste af argumenter. Her er en liste over funktioner, der bruges til at tegne former.
Chord
Ellipse
FillRect
FrameRect
InvertRect
Pie
Polygon
PolyPolygon
Rectangle
RoundRect
RoundRect bruges til at tegne firkanter med runde hjørne. Der står meget mere om disse funktioner her:
http://msdn.microsoft.com/en-us/library/dd162715(VS.85).aspx .
Vedhæftede filer:
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (13)
Virker dette kun i Windows?
Så vidt jeg ved, ja. Desværre
Men jeg er overbevist om, at der findes alternativer til andre operativsystemer.
Ikke lige mit fagområde
Men det ser rigtig godt ud!
Tak!
windows.h - duer kun på windows?
Jeg har prøvet at smide ind i CodeBlocks og compiled på en Mac, og får en hel del errors
Hvordan kan man omskrive så den virker på mac?
Jeg ville gerne hjælpe dig, men jeg har ingen idé om det.
Jeg har aldrig skrevet programmer til andet end Windows. Faktisk har jeg aldrig ejet et operativsystem andet end Windows (eksempelvis Linux på en Virtual Machine for en kort stund). Jeg er bange for, der skal nogle helt andre biblioteker til, hvis du skal kode til Mac. Altså jeg tror ikke, der findes noget der hedder GDI ved Mac.
Sakl lige teste noget håber det er ok jeg er nu på siden
#include <iostream>
std::cout << "test" << std::endl;
[code][#include <iostream>]
Download: " Qt Creator "
- for at lave din egen Gui/ Graphical user interface med c++
Nemt og lækkert
Med Wine kan programmet linkes og koeres i Mac og mange POSIX-systemer.
Bemaerk at GDI er foraeldet af Microsoft til fordel for Direct2D.
Du skal være
logget ind for at skrive en kommentar.