Kreslení v C Obsah Kreslení – reakce na zprávu WM_PAINT ................................................................................. 5 Seznámení s rozhraním grafického zařízení (GDI) ................................................................ 6 Kontext zařízení ........................................................................................................................... 7 Nastavení fontu písma................................................................................................................ 9 Struktura GDI (rozhraní grafického zařízení) ........................................................................ 15 Filozofie GDI .............................................................................................................................. 15 Volání funkcí GDI ..................................................................................................................... 15 Stavební kameny GDI ............................................................................................................... 15 Čáry a křivky ............................................................................................................................ 15 Vyplněné oblasti ....................................................................................................................... 16 Text........................................................................................................................................... 16 Kontext zařízení ....................................................................................................................... 16 Získávání handle kontextu zařízení .......................................................................................... 16 Proměnná ps je struktura typu PAINTSTRUCT,která obsahuje:............................................. 16 Uložení a obnovení kontextu zařízení ...................................................................................... 17 Nastavení barvy ......................................................................................................................... 17 Kreslení teček a čar .................................................................................................................. 17 Přímé čáry a křivky .................................................................................................................. 18 Použití předdefinovaných per ................................................................................................... 19 Vytváření, volba a rušení per ................................................................................................... 19 Kreslení vyplněných oblastí..................................................................................................... 25 Seznam obrázků ........................................................................................................................ 27 Seznam programů ..................................................................................................................... 27 Použitá literatura ....................................................................................................................... 27 Cílem kapitoly je ukázat možnosti kreslení čar, křivek a výplní ploch s využitém možností vektorové grafiky jak je implementovaná v MS Windows API a GDI+ ve spojení s jazykem C. Všechny uvedené programy byly odladěny v Microsoft Visual Studiu 2008. Klíčové pojmy: Události, handlery, třída Graphics, pera, stětce, kreslení vyplněných a nevyplněných objektů.
Grafický výstup byl možný i pod operačním systémem MS DOS. Společnost Borland distribuovala potřebné rutiny v knihovně a jejich použití bylo umožněno pouhým vložením potřebného hlavičkového souboru: #include
2.11.2011
Kreslení v C
1/27
Pěknou ukázkou grafikých možností byl program bgidemo.c, který je součástí instalace tohoto vývojového prostředí od Borlandu. Pro kreslení ve Windows je možné použít různé jazyky i vývojová prostředí: Jazyk C a Windows API Microsoft Visual Basic Borland Delphi C++ spolu s knihovnou MFC Java C# V této kapitole proberu základy prvního přístupu: jazyk C a Windows API, což je klasika v programování Windows. Zvládnout tento způsob programování je ovšem dost obtížné – z uvedených přístupů je to ten nejnáročnější. Má ovšem své výhody - vytvořené programy mají: nejlepší výkon nejlepší možnosti velkou pružnost malé spustitelné soubory nepotřebují další knihovny Dokumentace k API je na webových stránkách: http://www.microsoft.com/msdn/ Jestliže vytváříme aplikaci pro Windows je vhodné pro vytvoření nového projektu použít průvodce a aplikaci založit jako nový projekt typu Win32 Project. Po zadání vhodného jména projektu se v dalším kroku průvodce dostáváme do okna pro nastavení aplikace, kde můžeme znovu zvolit typ aplikace, přídavné volby a připojené hlavičkové soubory. Postup vytvoření programu pro Windows 1. Po spuštění Microsoft Visual Studia zvolíme: File – New – Project 2. Potom zvolíme programovací jazyk Visual C++ a typ aplikace Win32 3. V pravé části okna (Templates) zvolíme šablonu Win32 Console Application 4. Do pole Name napíšeme vhodné jméno projektu 5. Potvrzením aktivujeme průvodce (Wizard) a klikneme na Next (další krok) 6. Zde zvolíme typ aplikace: Windows application 7. Po kliknutí na Finish průvodce vygeneruje kód pro vytvoření okna Průvodcem vygenerovaný kód je dost rozsáhlý, obsahuje vstupní bod programu WinMain, funkce MyRegisterClass, InitInstance a tzv. proceduru okna WndProc. Procedura okna zpracovává zprávy určené oknu, které informují okno o zadání vstupu od uživatele: z klávesnice 2.11.2011
Kreslení v C
2/27
myši změně velikosti okna potřebě překreslit okno a další Windows vytvoří pro program tzv. „frontu zpráv“. WinMain obsahuje krátký úsek kódu, který se nazývá „smyčka zpráv“, který tyto zprávy vybírá z fronty a odesílá je odpovídající proceduře okna: // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hWnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
Komunikace Windows s programem Windows komunikují s programem tak, že zašlou zprávu oknu voláním procedury okna. Procedura okna provede podle typu zprávy činnost a předá řízení zpět Windows.
Vstupní bod programu: int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
1. parametr WinMain - handleinstance je identifikační číslo instance spuštěného programu 2. parametr WinMain je identifikační číslo předchozí instance 3. parametr WinMain je příkazový řádek pro spuštění programu 4. parametr WinMain je jak bude program poprvé zobrazen (max./min.) Programátoři Windows zpravidla začínají nový program zkopírováním existujícího programu a provedením změn, nebo použijí průvodce pro vygenerování základního kódu. Spustíme-li vygenerovaný program, objeví se prázdné okno, které můžeme ovládat pomocí tlačítek v titulkovém pruhu, můžeme jím pohybovat, měnit jeho velikost, dokonc má i nabídky File a Help. Kreslící příkazy budeme zapisovat do procedury okna WndProc. Tato metoda obsahuje příkaz switch, v němž se program větví podle přijaté zprávy. Chceme-li do okna kreslit, musíme zapsat obsluhu zprávy WM_PAINT. Tam je zapsaný komentář:
2.11.2011
Kreslení v C
3/27
// TODO: Add any drawing code here... A na toto místo napíšeme náš obslužný kód. Program 1 Výpis pozdravu do okna
Obrázek 1 Výpis pozdravu do okna
Chceme-li vytvořit okno s pozdravem podle obrázku, napíšeme do větve příkazu switch, která obsluhuje zprávu WM_PAINT následující kód: case WM_PAINT: RECT rect; hdc = BeginPaint(hWnd, &ps); GetClientRect (hWnd, &rect) ; DrawText (hdc, TEXT ("Nazdar, Windows!"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint(hWnd, &ps); break;
Úsek programu začíná voláním funkce BeginPaint. Windows vymažou pozadí klientské oblasti štětcem definovaným v položce ps. Funkce vrátí handle kontextu zařízení (hdc - handle devide context), který program potřebuje pro zobrazení textu a grafiky v klientské oblasti okna. EndPaint uvolní hdc. Funkce GetClientRect (hWnd, &rect) nastaví rozměry obdélníku pro dané okno – levý horní roh bude [0,0]. Tato funkce je vhodná ke zjišťování velikosti klientské oblasti okna. Funkce GetClientRect má dva parametry: hWnd - handle okna programu &rect - ukazatel na strukturu obdélníku typu RECT, která má 4 položky LONG: left, top, right, bottom Tento ukazatel je čtvrtým parametrem funkce DrawText, která vykreslí do okna zadaný text. Poslední parametr funkce DrawText udává způsob zobrazení textu: DT_SINGLELINE - text se zobrazí v jedno řádku
2.11.2011
Kreslení v C
4/27
DT_CENTER - text se vystředí ve vodorovném směru DT_VCENTER - text se vystředí ve svislém směru Skutečné činnosti programu jsou v proceduře okna WndProc. Většinu toho, co program pro Windows dělá, jsou reakce na zprávy, které dostává procedura okna. V DOSu programátoři v případě potřeby volali služby operačního systému. Zde operační systém Windows volá funkci vašeho programu (proceduru okna). Kdy volají Windows proceduru okna: při vzniku okna při zrušení okna při změně velikosti okna a jeho přesunu uživatel klepne na okno myší uživatel napíše na klávesnici nějaké znaky uživatel vybere některou položku nabídky uživatel posune, nebo klepne myší na posuvník Všechna volání procedury okna probíhají formou zpráv. Program je koncipován jako reakce na zprávy. Prototyp procedury okna: LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1. param. - handle okna 2. param. - zpráva 3. a 4. param. - záleží na zprávě Při tvorbě programů pro Windows dodržujte následující zásady: Nenechávejte okna nečinně spočívat na obrazovce. Snažte se rychle dokončit zpracování všech zpráv.
Kreslení – reakce na zprávu WM_PAINT Program může kreslit do tzv. klientské oblasti. Okna ve Windows nemají pevnou velikost - o jejich uspořádání rozhoduje uživatel. Pro zobrazení grafiky mají Windows rozsáhlý soubor funkcí -tzv. rozhraní grafického zařízení (GDI). Programy pro Windows nemohou předvídat velikost své klientské oblasti nebo textu musí využívat funkce a nástroje, které Windows poskytují pro získání informací o prostředí, kde program právě běží.
2.11.2011
Kreslení v C
5/27
Zajištění překreslování klientské oblasti Ve znakově orientovaném prostředí to, co program zobrazí, tam zůstane a nemusí si to pamatovat. Ve Windows to tak není - přesune-li se okno, bude Windows vyžadovat, aby váš program překreslil tuto část klientské oblasti. Zpráva WM_PAINT informuje proceduru okna, že klientská oblast musí být překreslena. Kdy procedura okna obdrží WM_PAINT: Při přesunu okna Při změně velikosti okna Při posunutí klientské oblasti pomocí posuvníku Jestliže program použije InvalidateRect nebo InvalidateRgn pro explicitní generování WM_PAINT Program kreslí "na požádání" - zašlou-li Windows zprávu WM_PAINT proceduře okna Potřebuje-li váš program aktualizovat klientskou oblast jindy, může přinutit Windows, aby generovaly WM_PAINT. Platné a neplatné obdélníky Procedura okna po obdržení WM_PAINT aktualizuje celou klientskou oblast. Často je třeba aktualizovat menší oblast uvnitř klientské oblasti. Tato oblast se nazývá neplatná oblast, nebo oblast pro aktualizaci. Přítomnost neplatné oblasti v klientské oblasti přiměje Windows vyslat WM_PAINT. Windows pro každé okno udržují "informační strukturu pro kreslení" - obsahuje souřadnice nejmenšího obdélníku oblasti, jež obklopuje neplatnou oblast – tzv. - "neplatný obdélník". Když procedura okna obdrží WM_PAINT může získat souřadnice neplatného obdélníku voláním funkce GetUpdateRect. Poté, co procedura okna zavolá BeginPaint, celá klientská oblast se uzná za platnou. Program takto může označit jakoukoli obdélníkovou oblast uvnitř klientské oblasti voláním ValidateRect.
Seznámení s rozhraním grafického zařízení (GDI) Pro kreslení v klientské oblasti se používají funkce GDI. Pro vykreslení textových řetězců: DrawText, která už byla vysvětlena v programu Program 1. TextOut (hdc, x , y, psText, iLenght); psText je ukazatel na řetězec iLenght je délka
2.11.2011
Kreslení v C
6/27
x a y určují výchozí polohu řetězce hdc “handle kontextu zařízení"
Kontext zařízení Handle: je číslo, které Windows používá pro interní odkaz na nějaký objekt získáte ho od Windows a můžete ho použít v dalších funkcích je vstupenka okna pro přístup k funkcím GDI. kontext zařízení – DC (device context) - struktura, kterou GDI interně spravuje DC je přiřazený zobrazovacímu zařízení (displeji , tiskárně) Některé z hodnot v DC jsou grafické atributy- určují podrobnosti o činnosti kreslících funkcí GDI. Například u funkce TextOut určují atributy : barvu textu barvu pozadí textu mapování souřadnic písmo Windows vyplní interní strukturu DC standardními hodnotami atributů. Funkce GDI umožní: změnit standardní hodnoty získat aktuální hod. vykreslit klientskou oblast okna Po vykreslení klientské oblasti by měl program hdc zase uvolnit. Program by měl získat a uvolnit handle během zpracování každé jednotlivé zprávy. Aplikace Windows používají dvě metody pro získávání hdc. První metoda získávání hdc: použ. se při zprac. WM_PAINT case WM_PAINT: hdc = BeginPaint (hWnd,&ps); //použijte funkce GDI EndPaint (hWnd, &ps); return 0;
hWnd - handle okna &ps - adresa strukturované proměnné typu PAINTSRTUCT - definuje se uvnitř procedury okna: PAINTSTRUCT ps ; Hodnota vrácená funkcí BeginPaint je handle kontextu zařízení. HDC hdc ; Je to 32bitové celé číslo bez znaménka. Program může potom použít funkce GDI, například TextOut, které vyžadují hdc.
2.11.2011
Kreslení v C
7/27
Volání EndPaint hdc uvolní. Informační struktura pro kreslení Typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT; Windows vyplní položky této struktury, když váš program volá BeginPaint. Váš program může použít pouze první tři položky. Ostatní jsou určeny pro interní využití Windows. Získání handle kontextu zařízení druhou metodou Chceme-li překreslit část klientské oblasti během zpracování jiné zprávy než je zpráva WM_PAINT nebo získat hdc z jiných důvodů hdc = GetDC (hWnd); //použití funkcí GDI ReleaseDC (hWnd, hdc); Rozdíly mezi použ. BeginPaint a GetDC: u BeginPaint hdc má ořezávající obdélník, odpovídající neplatné oblasti u GetDC hdc má ořezávající obdélník celé klientské oblasti GetDc a ReleaseDC se používá při odezvách na zprávy klávesnice nebo myši ValidateRect (hWnd, NULL) označí za platnou celou klientskou oblast Podobná GetDC je GetWindowDC - vrací hdc, které umožní zapisovat do celého okna (i do pruhu záhlaví). Funkce TextOut - detaily Funkce TextOut je nejběžnější funkce GDI pro zobrazování textu. TextOut (hdc, x, y, psText, iLenght) souřadnice - tzv. logické souřadnice psText - ukazatel na řetězec znaků a iLength - počet znaků v řetězci Program 2 Použití funkce TextOut pro výpis textu
Následující úsek programu ilustruje použití funkcí DrawText a TextOut pro výpis textu. Před voláním každé funkce je nastavená barva textu funkcí SetTextColor, kde barva je namíchána z jednotlivých složek pomocí makra RGB.
2.11.2011
Kreslení v C
8/27
Funkcí GetClientRect je určena aktuální velikost obdélníku klientské oblasti, z níž jsou odvozeny souřadnice x, y, kde začíná vypisovaný text. Výstup programu je vidět nenásledujícím obrázku:
Obrázek 2 Použití TextOut a DrawText pro výpis textu case WM_PAINT: RECT rect; hdc = BeginPaint(hWnd, &ps); GetClientRect (hWnd, &rect) ; SetTextColor(hdc, RGB(0,255,0)); DrawText (hdc, TEXT ("Nazdar, Windows!"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; x=(rect.right-rect.left)/4; y=(rect.bottom-rect.top)/4; SetTextColor(hdc, RGB(0,0,255)); TextOut (hdc, x, y, TEXT ("Nazdar, Windows!"), strlen("Nazdar, Windows!")); EndPaint(hWnd, &ps); break;
Nastavení fontu písma Výše uvedený program nemění standardně nastavený font písma. Chceme-li změnit font, je třeba nastavit požadované hodnoty ve struktuře LOGFONT, kde jsou definovány atributy fontu: typedef LONG LONG LONG LONG
2.11.2011
struct tagLOGFONT { // lf lfHeight; lfWidth; lfEscapement; lfOrientation;
Kreslení v C
9/27
LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT; Program 3 Změna fontu s využitím struktury LOGFONT
Výstup programu:
Obrázek 3 Použití struktury LOGFONT pro změnu fontu
Funkce GetStockObject obnoví přednastavený font (nebo také pero, štětec, paletu), abychom nemuseli nastavit všechny položky struktury LOGFONT a pouze měnit ty, které chceme. Typ požadovaného fontu zkopírujeme do položky logfont.lfFaceName funkcí lstrcpy . Výšku písma nastavíme do položky logfont.lfHeight . Funkce CreateFontIndirect vytvoří logický font, který má charakteristiky, specifikované strukturou LOGFONT. Funkce vrací handle kontextu zařízení, který uložíme do proměnné hFont . Získaný handle kontextu zařízení použijeme jako druhý parametr funkce SelectObject , která vybírá objekt do specifikovaného kontextu – tento nový objekt nahradí předchozí objekt stejného typu. Barva písma je nastavená funkcí SetTextColor . case WM_PAINT: LOGFONT logfont; HFONT hFont, hfPuvodni; RECT rect; GetClientRect(hWnd, &rect);
2.11.2011
Kreslení v C
10/27
GetObject(GetStockObject(DEFAULT_GUI_FONT),sizeof(logfont), &logfont); logfont.lfItalic = FALSE; lstrcpy(logfont.lfFaceName, TEXT("Mistral")); logfont.lfHeight = hi; hFont = CreateFontIndirect(&logfont); hdc = BeginPaint(hWnd, &ps); SelectObject(hdc, hFont); SetTextColor(hdc, RGB(0,255,0)); DrawText (hdc, TEXT ("Vypsáno funkcí DrawText"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; x=(rect.right-rect.left)/4; y=(rect.bottom-rect.top)/4; SetTextColor(hdc, RGB(0,0,255)); lstrcpy(logfont.lfFaceName, TEXT("Arial Black")); logfont.lfHeight = hi/2; hFont = CreateFontIndirect(&logfont); SelectObject(hdc, hFont); TextOut (hdc, x, y, TEXT ("Vypsáno funkcí TextOut"), strlen("Vypsáno funkcí TextOut")); EndPaint(hWnd, &ps); break;
Systémové písmo Výchozím SYSTEM_FONT Windows ho standardně používá pro textové řetězce v záhlavích, nabídkách a dialozích. Velikost znaku Pro zobrazení více řádků textu je třeba znát rozměry znaku - od výšky znaku se odvozuje mezera pro oddělení řádků Informaci o metrikách textu může program získat voláním: GetSystemMetrics Struktura TEXTMETRIC má 20 položek, ale nás bude zajímat pouze prvních 7: typedef struct tagTEXTMETRIC { LONG tmHEIGHT; LONG tmAscent; LONG tmDescent; LONG tmInternaiLeading; LONG tmExternaiLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; (další položky struktury) } TEXTMETRIC, * PTEXTMETRIC;
Před voláním GetTextMetrics je třeba definovat proměnnou struktury, obvykle s názvem tm: TEXTMETRIC tm; hdc=GetDC (hWnd);
2.11.2011
Kreslení v C
11/27
GetTextMetrics (hdc, &tm); ReleaseDC (hWnd, hdc); Chceme-li zajistit, aby se nějaké příkazy spustily při vytváření okna, doplníme do příkazu switch část: case WM_CREATE:, kde uvedeme příkazy, které mají proběhnout. Doplnění kódu, který aktivuje zpráva WM_CREATE ilustruje následující úsek programu:
2.11.2011
Kreslení v C
12/27
Program 4 Kód, který se spustí při vytváření okna
Při vytváření okna se zobrazí okno se zprávou podle obrázku:
Obrázek 4 Reakce na zprávu WM_CREATE switch (message) { case WM_CREATE: MessageBox(hWnd,TEXT("Vytvářím okno"),TEXT("Zpráva WM_CREATE"),0); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections:
Formátování textu Jelikož se rozměry systémového písma ve Windows nemění, stačí, když zavoláte GetTextMetrics jednou. Dobré místo pro volání je během zpracování zprávy WM_CREATE v proceduře okna. WM_CREATE je první zpráva, kterou procedura okna obdrží. Windows volají proceduru okna touto zprávou, když se vytváří okno. Program 5 Výpis textu na více řádků
Po obdržení zprávy WM_CREATE se vytvoří okno a nastaví se hodnoty jednotlivých položek strukturované proměnné tm. ¨ Proměnné cxChar,cyChar je třeba deklarovat jako globální, aby uchovávaly své hodnoty, nastavené na odpovídající hodnoty položek struktury tm. Po obdržení zprávy WM_PAINT se v cyklu for vypisují jednotlivé řádky v okně. Pro přechod na další řádek nehodnota řídicí proměnné cyklu zvýšená o hodnotu cyChar, což je výška znaku, zvýšená o meziřádkovou mezeru: cyChar = tm.tmHeight + tm.tmExternalLeading.
Počet vypsaných řádků závisí na výšce okna, kterou můžeme za běhu programu měnit. Pro převod čísla řádku na řetězec je použita funkce wsprintf. Výstup programu:
2.11.2011
Kreslení v C
13/27
Obrázek 5 Výpis řádků v okně // Global Variables: HINSTANCE hInst; // TCHAR szTitle[MAX_LOADSTRING]; // TCHAR szWindowClass[MAX_LOADSTRING]; // int cxChar,cyChar; /* Část kódu, vygenerovaná průvodcem */ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, { int wmId, wmEvent; PAINTSTRUCT ps; TEXTMETRIC tm; HDC hdc; int x=10; int y=0; int radek=0; int hi=50; switch (message) { case WM_CREATE: hdc = GetDC (hWnd) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight + tm.tmExternalLeading ReleaseDC (hWnd, hdc) ; return 0 ; case WM_PAINT: RECT rect; GetClientRect(hWnd, &rect); hdc = BeginPaint(hWnd, &ps); int delka ; TCHAR Text_v_radku [40] ; for(y=0;y
2.11.2011
Kreslení v C
current instance The title bar text the main window class name
WPARAM wParam, LPARAM lParam)
;
řádek"),radek) ;
14/27
Struktura GDI (rozhraní grafického zařízení) Z pohledu programátora GDI sestává: z několika stovek volání funkcí některých přidružených datových typů, maker struktur
Filozofie GDI Jedním ze základních cílů GDI je schopnost podpory grafiky nezávislé na zařízení. Grafická výstupní zařízení mohou být: rastrová zařízení - většina vektorová (plottery) GDI je obecně statický zobrazovací systém s omezenou podporou animací. Vyspělé animace se používají pro hry (program Microsoft DirectX).
Volání funkcí GDI Funkce GDI lze rozdělit do několika skupin: Funkce, které získávají (nebo vytvářejí) a uvolňují (nebo ruší) kontext zařízení BeginPaint/EndPaint , GetDC/ReleaseDC Funkce, které získávají informace o kontextu zařízení - GetTextMetrics Funkce které něco kreslí - TextOut Funkce, které nastavují a získávají atributy kontextu zařízení - SetTextColor Funkce, které pracují s "objekty" GDI: o pera o štětce o písma o bitmapové obrázky o další
Stavební kameny GDI Čáry a křivky přímé čáry, obdélníky, 2.11.2011
Kreslení v C
15/27
elipsy oblouky Bezierovy křivky GDI kreslí čáry pomocí aktuálního pera zadaného v kontextu Vyplněné oblasti výplň podle aktuálního objektu štětce barvu vzorek bitmapový obrázek Bitmapy představují obdélníkové pole bitů, které odpovídá bodům zobrazovacího zařízení Text Kontext zařízení Získávání handle kontextu zařízení Získávání informací z kontextu zařízení Získávání handle kontextu zařízení Když chceme kreslit, nejprve musíme získat handle kontextu zařízení (neboli DC). Když Windows předávají handle vašemu programu, zároveň tímto dávají svolení k práci se zařízením. Handle potom můžete použít jako parametr funkcí GDI. Kontext zařízení obsahuje řadu „atributů" TextOut(hdc,x,y,szBuffer,iLength); Získávání handle kontextu zařízení: Windows nabízejí řadu metod získávání kontextu zařízení. Před opuštěním procedury okna byste měli také handle uvolnit. 1. Nejobvyklejší metoda získávání handle kontextu zařízení a následného uvolňování sestává z volání funkcí BeginPaint a EndPaint během zpracování zprávy WM_PAINT: hdc = BeginPaint (hWnd, &ps) ; // další řádky programu EndPaint (hWnd, &ps) ; Proměnná ps je struktura typu PAINTSTRUCT,která obsahuje: položku hdc strukturu RECT pojmenovanou rcPaint, která definuje obdélník ohraničující neplatnou oblast klientské oblasti okna. Můžete kreslit pouze uvnitř této oblasti
2.11.2011
Kreslení v C
16/27
2.
Druhá metoda získání handle kontextu zařízení:
hdc = GetDC (hWnd) ; // další řádky programu ReleaseDC (hWnd, hdc) ; 3. Třetí metoda umožňuje získat handle kontextu zařízení, který se odkazuje na celé okno, ne jen na klientskou oblast okna: hdc - GetWindowDC (hWnd) ; // další řádky programu ReleaseDC (hWnd, hdc) ; 4.
Čtvrtá obecná funkce pro získání handle kontextu zařízení je funkce CreateDC:
hdc = CreateDC (pszDriver, pszDevice, pszOutput, pData) ; // další řádky programu DeleteDC (hdc) ; Handle kontextu zařízení celého displeje můžete např. získat : hdc = CreateDC("DISPLAY", NULL, NULL, NULL) ; 5. Někdy potřebujete pouze získat informaci o kontextu zařízení, aniž byste cokoli kreslili hdc = CreateIC("DISPLAY", NULL. NULL, NULL);
Uložení a obnovení kontextu zařízení Kontext zařízení můžeme uložit a následně obnovit: hdcUloz = SaveDC (hdc) ; RestoreDC (hdc, hdcUloz) ;
Nastavení barvy S využitím COLORREF, což je 32-bitová hodnota, definující barvu ve tvaru RGB, tj. smíchanou ze tří složek: red, green, blue. Zadáváme ji v šestnáctkovém tvaru: 0x00bbggrr (pozor – složky modré a červené jsou zde prohozeny). Pro nastavení barev máme k dispozici několik maker. Makro RGB očekává tři argumenty, které představují hodnoty červené, zelené a modré složky a sloučí je do dlouhého čísla (bez znaménka): Kreslení teček a čar SetPixel a GetPixel - problémem je rychlost SetPixel (hdc, x, y, barva) ;
2.11.2011
Kreslení v C
17/27
barva = GetPixel (hdc, x, y) ; Program 6 Použití funkce SetPixel pro kreslení bodů
Následující program kreslí body náhodně generované barvy do klientské oblasti na náhodně generované souřadnice (použita funkce rand).
Obrázek 6 Výstup programu pro náhodné generování bodů case WM_PAINT: HDC hdc; RECT rect; int cxClient,cyClient,barva; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x,y; hdc=GetDC(hWnd); x=rand()%cxClient; y=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); SetPixel (hdc, x, y, barva) ; ReleaseDC(hWnd,hdc); break;
Přímé čáry a křivky Lineto Kreslí přímou čáru. Polyline a PolylineTo Kreslí posloupnost spojených přímých čar (lomenou řáru). PolyPolyline Kreslí více lomených čar. Arc Kreslí epiptické čáry. PolyBezier a PolyBezierto Kreslí Bezierovy křivky Pro nakreslení přímé čáry musíte zavolat dvě funkce: MoveToEx (hdc, xBeg, yBeg, NULL) ; LineTo (hdc, xEnd, yEnd) ;
2.11.2011
Kreslení v C
18/27
Použití předdefinovaných per Pero určuje: barvu čáry šířku čáry styl čáry - zda má být souvislá, tečkovaná či čárkovaná Pero ve výchozím kontextu zařízení má název BLACK_PEN. Další dvě předdefinovaná pera jsou WHITE_PEN a NULL_PEN Na pera se ve Windows odkazuje prostřednictvím handle. WINDEF.H definuje datový typ HPEN, což je handle pera hPen = GetStockObject (WHITE_PEN); Vytváření, volba a rušení per Vytvoříte ,,logické pero“, což je pouze popis pera, a to pomocí fce: CreatePen nebo CreatePenIndirect. Toto pero pak vložíte do kontextu zařízení voláním fce: SelectObject. Potom již můžete kreslit čáry tímto novým perem. V jednom okamžiku může kontext zařízení používat jen nedno pero. Existuje 6 objektů GDI, které program může vytvořit: pera štětce bitmapy, oblasti písma palety Až na palety se všechny tyto objekty pro kontext zařízení vybírají pomocí funkce SelectObject. Používání objektů GDI, což jsou třeba pera, se řídí třemi pravidly: Nakonec byste měli zrušit všechny objekty GDI, které vytvoříte. Nikdy nerušte objekty GDI, které jsou vybrány v platném kontextu zařízení. Nikdy se nepokoušejte zrušit předdefinováné objekty. Syntaxe funkce CreatePen: CreatePen hPen = CreatePen (iPenStyle, iWidth, crColor);
Styl čáry můžeme nastavit pomocí předdefinovaných konstant, nebo celočíselných hodnot: PS_SOLID 0 plná čára PS_DASH 1 čárkovaná čára PS_DOT 2 tečkovaná čára PS_DASHDOT 3 čárka, tečka PS_ DASHDOTDOT 4 čárka, tečka, tečka 2.11.2011
Kreslení v C
19/27
PS_NULL
5
neviditelná čára
Nastavený styl čáry se použije, má-li čára hodnotu šířky iWidth=1. Program 7 Kreslení náhodných čar
Následující program kreslí čáry náhodně generované barvy, jejichž počáteční i koncový bod má náhodně generované souřadnice. Pro přesun do počátečního bodu bez kreslení se používá funkce MoveToEx. Po vykreslení čáry je použita funkce Sleep pro zpomalení vykreslování.
Obrázek 7 Výstup programu pro kreslení náhodných čar case WM_PAINT: HDC hdc; RECT rect; int cxClient,cyClient,barva; HPEN hp; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x1,y1,x2,y2; hdc=GetDC(hWnd); x1=rand()%cxClient; y1=rand()%cyClient; x2=rand()%cxClient; y2=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); hp=CreatePen(1,2,barva); SelectObject(hdc,hp); MoveToEx(hdc, x1, y1,NULL); LineTo (hdc, x2, y2) ; Sleep(100);
2.11.2011
Kreslení v C
20/27
DeleteObject (hp); ReleaseDC(hWnd,hdc); break;
Program 8 Nastavení různých stylů čar
V následujícím úseku programu jsou generovány různé styly čar do proměnné iPenStyle, která je následně použita pro vytvoření pera hp.
Obrázek 8 Výstup programu pro nastavení různých stylů čar case WM_PAINT: HDC hdc; RECT rect; int cxClient,cyClient,barva,iPenStyle; HPEN hp; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x1,y1,x2,y2; hdc=GetDC(hWnd); x1=rand()%cxClient; y1=rand()%cyClient; x2=rand()%cxClient; y2=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); iPenStyle=rand()%5; hp=CreatePen(iPenStyle,1,barva); SelectObject(hdc,hp); MoveToEx(hdc, x1, y1,NULL); LineTo (hdc, x2, y2) ; Sleep(100); DeleteObject (hp); ReleaseDC(hWnd,hdc);
2.11.2011
Kreslení v C
21/27
break;
Pero také můžete vytvořit nastavením struktury datového typu LOGPEN a voláním funkce CreatePenIndirect. Pokud váš program bude používat řadu různých per, která inicializuje ve vašem zdroj. kódu, tento postup je nejúčinnější. Při použ. fce CreatePenIndirect definujete nejprve strukturu typu LOGPEN: LOGPEN logpen ; Tato struktura má tři položky: lopnStyle je styl pera, lopnWidth je šířka pera v logických jednotkách lopnColor je barva pera. Pero vytvoříte předáním adresy struktury funkci CreatePenIndirect: hPen = CreatePenIndirect (&logpen)
;
Následující program ukazuje vytvoření pera s využitím struktury LOGPEN. Program 9 Vytvoření pera s využitím struktury LOGPEN
Výstup programu je stejný, jako u programu Program 8. case WM_PAINT: HDC hdc; RECT rect; LOGPEN logpen ; int cxClient,cyClient,barva,iPenStyle; HPEN hp; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x1,y1,x2,y2; hdc=GetDC(hWnd); x1=rand()%cxClient; y1=rand()%cyClient; x2=rand()%cxClient; y2=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); iPenStyle=rand()%5; logpen.lopnStyle=iPenStyle; logpen.lopnColor=barva; logpen.lopnWidth.x=0; hp = CreatePenIndirect (&logpen) ; SelectObject(hdc,hp); MoveToEx(hdc, x1, y1,NULL); LineTo (hdc, x2, y2) ; Sleep(100); DeleteObject (hp); ReleaseDC(hWnd,hdc); break;
2.11.2011
Kreslení v C
22/27
Další funkce vyplní uzavřenou oblast v obrazci, který nakreslí: Rectangle Kreslí obdélník. Ellipse Kreslí elipsu. RoundRect Kreslí obdélník se zaoblenými rohy. Pie Kreslí část elipsy, která vypadá jako část koláčového grafu. Chord Kreslí část elipsy určenou tětivou. Polygon Kreslí vícestranný obrazec (polygon) PolyPolygon Kreslí více polygonů Program 10 Kreslení obdélníka
Následující úsek programu v sekci obsluhy zprávy WM_PAINT nakreslí ohraničený vyplněný obdélník podle obrázku:
Obrázek 9 Výstup programu Kreslení obdélníka
Obdélník se kreslí funkcí Rectangle aktuálním perem a štětcem. Aktuální pero a štětec se nastavují funkcí SelectObject pro oblast, určenou handlem hdc, což je prvním parametrem funkce Rectangle. Další čtyři parametry jsou souřadnice obdélníku. Pero je vytvořené funkcí CrestePen a štětec funkcí CreateSolidBrush. Do proměnné rect jsou pomocí funkce GetClientRect sejmuty aktuální rozměry okna. case WM_PAINT: HPEN hp; HBRUSH hb; RECT rect; int a,b; hdc = BeginPaint(hWnd, &ps); hp=CreatePen(1,4,RGB(255,0,0)); hb=CreateSolidBrush(RGB(0,255,0)); GetClientRect(hWnd, &rect); a=(rect.right-rect.left)/4; b=(rect.bottom-rect.top)/4;
2.11.2011
Kreslení v C
23/27
SelectObject(hdc,hp); SelectObject(hdc,hb); Rectangle(hdc,rect.right/2- a,rect.bottom/2-b,rect.right/2+a, rect.bottom/2+b); EndPaint(hWnd, &ps); break; Program 11 Náhodné generování obdélníků
Následující program generuje náhodné obdélníky s výstupem podle obrázku. Po nastavení náhodných hodnot jsou obdélníky vykreslovány funkcí Rectangle (hdc, x1, y1, x2, y2).
Bod (x1, y1) je levý horní roh obdélníku a bod (x2, y2) je pravý dolní roh obdélníku.
Obrázek 10 Výstup programu pro náhodné generování obdélníků case WM_PAINT: HDC hdc; RECT rect; int cxClient,cyClient,barva; HPEN hp; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x1,y1,x2,y2; hdc=GetDC(hWnd); x1=rand()%cxClient; y1=rand()%cyClient; x2=rand()%cxClient; y2=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); hp=CreatePen(1,2,barva); SelectObject(hdc,hp); Rectangle (hdc,x1,y1, x2, y2) ; Sleep(100); DeleteObject (hp); ReleaseDC(hWnd,hdc); break;
2.11.2011
Kreslení v C
24/27
Kreslení vyplněných oblastí Rectangle, Ellipse, RoundRect, Chord a Pie kreslí čáry, ale také vyplňují uzavřenou oblast aktuálním štětcem výplně. jsou postaveny na obdélníkovém „ohraničujícím rámu“ Nejjednodušší z těchto funkcí nakreslí obdélník: Program 12 Náhodné generování vyplněných obdélníků
Následující úsek programu náhodně generuje vyplněné obdélníky s náhodně generovanou barvou výplně i obrysu.
Obrázek 11 Výstup programu pro náhodné generování vyplněných obdélníků case WM_PAINT: HDC hdc; RECT rect; int cxClient,cyClient,barva; HPEN hp; HBRUSH hb; GetClientRect(hWnd, &rect); cxClient=rect.right-rect.left; cyClient=rect.bottom-rect.top; int x1,y1,x2,y2; hdc=GetDC(hWnd); x1=rand()%cxClient; y1=rand()%cyClient; x2=rand()%cxClient; y2=rand()%cyClient; barva=RGB(rand()%255,rand()%255,rand()%255); hp=CreatePen(1,2,barva); barva=RGB(rand()%255,rand()%255,rand()%255); hb=CreateSolidBrush(barva);
2.11.2011
Kreslení v C
25/27
SelectObject(hdc,hp); SelectObject(hdc,hb); Rectangle (hdc,x1,y1, x2, y2) ; Sleep(100); DeleteObject (hp); DeleteObject (hb); ReleaseDC(hWnd,hdc); break; Program 13 Kreslení vyplněných elips
Jestiže v předchozím programu pro kreslení vyplněných obdélníků nahradíme příkaz: Rectangle (hdc,x1,y1, x2, y2) ; příkazem: Ellipse (hdc,x1,y1, x2, y2), budou se kreslit náhodné vyplněné elipsy, jak je vidět na následujícím obrázku:
Obrázek 12 Kreslení vyplněných elips
Shrnutí: Uvedený studijní materiál seznamuje žáky s některými kreslícími funkcemi rozhraní API a GDI+ ve spojení s jazykem C. Je ukázána práce s barvami, tvorba per a štětců, kreslení bodů, čar a vyplněných a nevyplněných obdélníků a elips. Součástí materiálu jsou spustitelné programy exe a jejich zdrojové kódy.
2.11.2011
Kreslení v C
26/27
Seznam obrázků Obrázek 1 Výpis pozdravu do okna .................................................................................................... 4 Obrázek 2 Použití TextOut a DrawText pro výpis textu ..................................................................... 9 Obrázek 3 Použití struktury LOGFONT pro změnu fontu ............................................................... 10 Obrázek 4 Reakce na zprávu WM_CREATE .................................................................................... 13 Obrázek 5 Výpis řádků v okně .......................................................................................................... 14 Obrázek 6 Výstup programu pro náhodné generování bodů ........................................................... 18 Obrázek 7 Výstup programu pro kreslení náhodných čar ............................................................... 20 Obrázek 8 Výstup programu pro nastavení různých stylů čar ......................................................... 21 Obrázek 9 Výstup programu Kreslení obdélníka ............................................................................. 23 Obrázek 10 Výstup programu pro náhodné generování obdélníků ................................................. 24 Obrázek 11 Výstup programu pro náhodné generování vyplněných obdélníků .............................. 25 Obrázek 12 Kreslení vyplněných elips ............................................................................................. 26
Seznam programů Program 1 Výpis pozdravu do okna .................................................................................................. 4 Program 2 Použití funkce TextOut pro výpis textu ........................................................................... 8 Program 3 Změna fontu s využitím struktury LOGFONT .............................................................. 10 Program 4 Kód, který se spustí při vytváření okna ......................................................................... 13 Program 5 Výpis textu na více řádků .............................................................................................. 13 Program 6 Použití funkce SetPixel pro kreslení bodů ..................................................................... 18 Program 7 Kreslení náhodných čar ................................................................................................. 20 Program 8 Nastavení různých stylů čar........................................................................................... 21 Program 9 Vytvoření pera s využitím struktury LOGPEN ............................................................. 22 Program 10 Kreslení obdélníka ........................................................................................................ 23 Program 11 Náhodné generování obdélníků ................................................................................... 24 Program 12 Náhodné generování vyplněných obdélníků ............................................................... 25 Program 13 Kreslení vyplněných elips ........................................................................................... 26
Použitá literatura [1] Charles Petzold: Programování ve Windows, Computer Press, 1999
2.11.2011
Kreslení v C
27/27