Grafické aplikace ve Visual C++ (2.)
I přes všechny nevýhody GDI, je ve Windows takřka všudypřítomné.
Jakékoliv kreslení ve Windows je dílem právě GDI. Myslím, že princip
GDI je potřeba alespoň částečně chápat pro úplné pochopení DirectDraw.
Ve skutečnosti na tom nic není. Potřebujete vědět, co jednotlivé
funkce ve skutečnosti dělají.
2.1. Funkce OnDraw()
Funkce OnDraw() je jedna z nejdůležitěších funkcí GDI.
Váš program jí automaticky
volá pokaždé, když chce překreslit okno tzn. při změně velikosti,
maximalizaci nebo minimalizaci okna atd. Jedná se o členskou funkci
třídy pohledu CView . Přijímá jeden parametr a to je právě ukazatel
na kontext zařízení (DC) vašeho okna. Funkce kontextu zařízení
zapouzdřuje jiná třída CDC . Pomocí tohoto parametru můžete
provádět veškeré kreslení do okna.
Nejjednodušší řešení je, že zapíšete váš kód přímo do této funkce,
případně si vytvoříte vlastní funkci, která bude mít jako parametr
ukazatel na DC a budete ji volat z funkce OnDraw() .
2.2. Jednoduchý příklad kreslení do okna
Vytvořte si standardní projekt SDI (Single Document Interface)
MFC pomocí AppWizardu.
Najděte funkci OnDraw() ve třídě pohledu CView. Vaše třída pohledu
se samozřejmě jmenuje podle jména projektu např. projekt Pepa bude
mít třídu pohledu CPepaView.
Funkce CPepaView::OnDraw() po vygenerováni AppWizardem:
void CPepaView::OnDraw(CDC* pDC)//ukazatel na kontext zařízení
vašeho okna
{
CGDIDoc* pDoc = GetDocument();//ukazatel na váš dokument tzn.
na CPepaDoc
ASSERT_VALID(pDoc);//ověření pDoc
// TODO: add draw code for native data here
}
Přepište funkci následovně:
void CPepaView::OnDraw(CDC* pDC)
{
CGDIDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//deklarace promenych
CRect client, user;
CPen pen;
//tato funkce vrati velikost okna pohledu
GetClientRect(&client);
// vytvori pero o urcite sirce a barve
pen.CreatePen(PS_SOLID, 5, RGB(255,0,0));
//od kazde souradnice odectu 25 pixelu
user.top = client.top + 25;
user.left = client.left + 25;
user.right = client.right - 25;
user.bottom = client.bottom - 25;
pDC->SelectObject(&pen); //vybere nase pero do kontextu zarizeni
pDC->Rectangle(&user); // tato funkce nakresli obdelnik
//napise text doprostred okna
pDC->TextOut(client.right / 2, client.bottom / 2, "GDI v praxi");
}
Program nakreslí do okna červený obdélník a doprostřed napíše text.
Do kontextu zařízení vždy kreslíte právě vybraným tzv. GDI
objektem jako jsou pera, štětce, fonty, bitmapy atd. Tyto
objekty jsou v MFC za prvé předem připraveny jako Stock
Objects a za druhé si je můžete vytvořit samy, což je
samozřejmě běžnější způsob práce z GDI objekty.
2.3. GDI objekty
GDI objekty zapouzdřuje několik tříd (my si uvedeme jen ty nejběžnější) :
Pero
CPen - třída, ze které se vytvoří pero o daném typu, šířce a barvě. Perem
kreslíme čáry různé tloušťky a typu.
Postup vytvoření pera :
-
Vytvoříte objekt typu CPen
-
Zavoláte členskou funkci třídy CPen::CreatePen()
Takto vypada deklarace funkce pro vytvoření pera:
BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor);
-
nPenStyle - styl pera může být například:
-
nWidth - šířka pera je celočíselná hodnota v pixelech (výše
uvedené typy per kromě PS_SOLID fungují pouze je-li šířka
pera 1 pixel).
-
crColor - barva pera. Tento parametr se předává pomocí makra RGB,
které přijímá tři hodnoty barev RGB (Red, Green, Blue) v rozsahu 0 - 255
např. když napíšete RGB(255,0,0) funkce vytvoří sytě červené pero.
Tento objekt jsme použili ve výše uvedeném příkladu.
Štětec - Brush
CBrush - třída, která vytvoří štětec pro vykreslování velkých
ploch v okně libovolnou barvou nebo vzorem.
Postup vytvoření štětce :
-
Vytvoříte objekt typu CBrush
-
Zavoláte členskou funkci třídy CBrush::CreateSolidBrush() .
Funkce CreateSolidBrush() vytvoří štětec o jedné barvě tzn. že
má jeden parametr, který se zadává podobně jako u pera pomocí makra RGB.
Font
CFont - třída, která vytvoří font pro psaní do okna. Můžete si
vytvořit zcela vlastní font, ale myslím, že spíše využijete
fontů, které jsou nainstalované ve Windows.
Postup vytvoření fontu :
-
Vytvoříte objekt typu CFont
-
Zavoláte členskou funkci třídy CFont::CreatePointFont() ,
která má následující parametry :
-
velikost fontu v desetinách bodu tzn. pokud chcete písmo
o velikosti 12 bodů musíte zadat tento parametr 120
-
tvář (face) fontu. Tento parametr je řetězec, který obsahuje
jméno vybraného fontu tzn. pokud chcete Arial dosadíte "Arial"
nebo "Times New Roman" pro Times New Roman. Pokud není vámi vybrané
písmo ve Windows program použije standardní písmo.
Dalšími GDI objekty jsou například CBitmap,
CPalette nebo CRgn .
2.4. Třída CDC
Poté, co si vytvoříte svůj GDI objekt musíte ho vybrat do kontextu
zařízení vašeho okna. To obstará funkce třídy CDC::SelectObject() ,
která přijímá ukazatel na váš GDI objekt. Od této chvíle se bude
kreslit vaším perem nebo štětcem a psát vaším fontem. Důležité je,
že může být vybrán jen jeden objekt. Pokud tedy zavoláte opakovaně
tuto funkci, předchozí objekt se vyjme a vloží se jiný. Funkce
SelectObjekt() vrací ukazatel předešlý vybraný objekt GDI.
Třída CDC obsahuje velké množství funkcí pro kreslení, takže je
nemohu všechny popsat.
Funkce pro objekt CPen :
CPoint MoveTo( int x, int y );
CPoint MoveTo( POINT point );
Tyto dvě funkce pouze přesouvají aktuální pozici odkud se bude příště
kreslit. Vrací předešlou pozici.
BOOL LineTo( int x, int y );
BOOL LineTo( POINT point );
Tyto dvě funkce kreslí do okna právě vybraným perem čáru. Kreslí se z
aktuální pozice předem nastavené funkcí MoveTo() na pozici určenou
parametry. Aktuální souřadnice se přesouvá tam, kde skončí pero
s kreslením. Vrací nenulovou hodnotu pokud je úspěšná jinak 0.
Poznámka: Všimněte si, že každá funkce má dvě varianty,
to umožňuje C++ díky přetěžování funkcí (overloading). Vidíte, že první varianta
přijímá jednoduché typy, zatímco druhá varianta objekt
POINT respektive RECT .
To jsou objekty Win32 API. Ekvivalentní objekt v MFC je
CPoint respektive CRect .
Tento objekt za prvé uchovává informaci o souřadnici bodu respektive obdélníku a
za druhé obsahuje mnoho členských funkcí, které podstatně
zjednodušují práci programátora.
BOOL Rectangle( int x1, int y1, int x2, int y2);
BOOL Rectangle( LPRECT lpRect);
Tyto funkce nakreslí obdélník do okna vybraným perem. Jako parametry
můžete zadat čtyř souřadnice tzn. horní levý roh a dolní pravý roh
nebo předáte ukazatel na objekt typu CRect
respektive RECT . Vrací
nenulovou hodnotu pokud je úspěšná jinak 0.
BOOL Ellipse ( int x1, int y1, int x2, int y2 );
BOOL Ellipse ( LPRECT lpRect );
Tyto funkce kreslí obecně elipsu, ale dá se s ní samozřejmě nakreslit
i kružnice. Má čtyři parametry, které jsou vlastně stejné jako u
předešlé funkce. Vrací nenulovou hodnotu pokud je úspěšná jinak 0.
Funkce pro objekt CBrush
BOOL FillRect ( LPRECT lpRect, CBrush * pBrush );
Tato funkce trochu popírá, co jsme si řekli o vkládání objektů,
protože přijímá parametr typu CBrush , což je jak jistě víte GDI
objekt. No nevím, jak to vývojáři MFC mysleli. Každopádně tato
funkce funguje tak, že vyplní obdélníkovou oblast právě tím štětcem,
který je zadán jako parametr a ne tím, který je vybrán v DC. První
parametr je ukazatel na objekt typu CRect respektive
RECT .
BOOL FloodFill ( int x, int y , COLORREF crColor);
Tato funkce je velmi podobná funkci ze starého Pascalu. Začne
vyplňovat plochu od určeného bodu, dokud nenarazí barvu určenou
parametrem crColor . První dva parametry jsou ten bod a druhy ta
barva, která se opět zadává pomocí makra RGB (viz výše). Vyplňuje
právě vybraným štětcem. Vrací nenulovou hodnotu pokud je úspěšná
jinak 0.
Funkce pro objekt CFont
BOOL TextOut (int x, int y, CString& str );
Tato funkce napíše text v bodě o souřadnicíchx a y .
Samotný text je uložen ve
třetím parametru typu CString respektive referenci na
CString .
Píše se právě vybraným fontem a návratová hodnota je stejná jako
ve všech předchozích případech.
Třída CDC má spoustu dalších funkcí. Pokud byste měli nějaký
speciální zájem, stačí napsat.
2.5. Příklad
Připravil jsem pro vás jednoduchý příklad, který použije všechny
výše popsané funkce. Je to standardní aplikace
MFC AppWizard [exe]. Přesný postup, jak
vytvořit tento typ aplikace najdete ve třetím kurzu tohoto dílu
a vy akorát upravíte funkci OnDraw() . Tento
příklad si můžete stáhnout z CD zde
nebo v sekci Downloads.
void CGDIView::OnDraw(CDC* pDC)
{
CGDIDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//
// Procvicujeme funkce GDI
CRect rcClient;
// Ulozime si velikost okna
GetClientRect(&rcClient);
//
// 1. Vytvorme si objekt pera CPen
CPen penBlue;
penBlue.CreatePen(PS_SOLID, 2, RGB(0, 0, 255));
// 2. Vytvorme si objekt stetce CBrush
CBrush brushRed, brushGreen;
brushRed.CreateSolidBrush(RGB(255, 0, 0));
brushGreen.CreateSolidBrush(RGB(0, 255, 0));
// 3. Do tretice si vytvorime font CFont
CFont fontTimes;
fontTimes.CreatePointFont(120, "Times New Roman");
//
// Vybereme pero
pDC->SelectObject(penBlue);
// Nyni muzeme kreslit modrym perem
// 1. Vytvorime diagonalni caru
pDC->MoveTo(0, 0);
pDC->LineTo(rcClient.right, rcClient.bottom);
// 2. Nakreslime obdelnik
pDC->Rectangle(rcClient.left + 25, rcClient.top + 25,
rcClient.right - 25, rcClient.bottom - 25);
// 3. Nakreslime elipsu uprostred okna
pDC->Ellipse(rcClient.left + 50, rcClient.top + 50,
rcClient.right - 50, rcClient.bottom - 50);
// 4. Pouzijeme funkci FillRect() k vybarveni obdelnicku
// Vsimnete si, ze nevybirame stetec!!!
pDC->FillRect(CRect(150, 150, 200, 200), &brushGreen);
//
// Nyni jiz musime vybrat stetec do DC
pDC->SelectObject(brushRed);
// 5. Pouzijeme funkci FloodFill() k vybarveni obdelnicku
pDC->FloodFill(30, 30, RGB(0, 0, 255));
//
// 6. Nakonec vybereme font do DC a napiseme text doprostred okna
pDC->SelectObject(fontTimes);
pDC->TextOut(rcClient.right / 2, rcClient.bottom / 2, "GDI v praxi");
//
}
Poznámka:
-
Zkuste si zmenšit či zvětšit okno. Všimněte si, že obsah
okna se nehezky překresluje pokaždé, když změníte velikost okna.
To je dáno tím, že funkce OnDraw() je volána pokaždé, když se
má okno překreslit a také samozřejmě tím, že GDI je pomalé.
-
Jak vidíte u funkce FillRect() , není potřeba
vybírat žádný štětec, protože se k vybarvení použije
štětec, který je dán v druhém parametru.
-
Vzápětí ale vidíte, že u funkce FloodFill()
již štětec vybrat musíme, jinak se použije tzv. NULL BRUSH, který
nenakreslí vůbec nic.
-
Text, který má být uprostřed okna, vlastně není přesně uprostřed.
Zkuste program upravit tak, aby text byl pokaždé uprostřed. Je nutné
do souřadnic textu zahrnout také velikost samotného textu.
Takto vypadá okno naší aplikace:
2.6. Závěr
Tato část vás tedy seznámila s úplnými základy GDI a měli byste si
alespoň něco z toho pamatovat, ačkoliv to v DirectDraw přímo nepoužijeme.
V DirectDraw budeme tu a tam používat kontexty zařízení, ale to uvidíte
až v příští lekci.
V příští lekci už začneme skutečné DirectDraw. Pokud byste měli nějaké
speciální požadavky nebo chtěli poradit, můžete mi napsat.
Těším se příště nashledanou.
|