Szkielet aplikacji Directdraw.pdf
(
256 KB
)
Pobierz
Grafika komputerowa – Szkielet aplikacji
1.1 Szkielet aplikacji
Poniżej przedstawiony jest szkielet aplikacji, na którym oparte są wszystkie programy
pisane w ramach
ćwiczeń.
Główna klasa okna programu może być wyprowadzona z klasy
TForm, tak jak to jest zaprezentowane poniżej. Wytłuszczeniem zaznaczone są prywatne
składowe klasy, potrzebne do zainicjowania DirectDraw. Typy danych:
LPDIRECTDRAW,
DDSURFACEDESC, LPDIRECTDRAWSURFACE, DDSCAPS
zdefiniowane są w pliku
nagłówkowym
ddraw.h.
class TForm1 : public TForm
{
__published:
// IDE-managed Components
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
void __fastcall FormKeyDown(TObject *Sender, WORD
&Key,
TShiftState Shift);
private: // User declarations
LPDIRECTDRAW
lpDD;
DDSURFACEDESC
ddsd;
LPDIRECTDRAWSURFACE
FrontBuffer;
LPDIRECTDRAWSURFACE
BackBuffer;
DDSCAPS
ddscaps;
bool InitDDraw();
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
1.2 Obiekty COM
Budowa bibliotek DirectX oparta jest bezpośrednio na obiektach COM (Component
Object Model), stanowiących rozszerzenie pojęcia klas abstrakcyjnych języka C++. Obiekt
COM z punktu widzenia aplikacji jest „czarną skrzynką” wykonującą
ściśle
określone opera-
cje. Główna różnica między obiektami COM, a klasami języka C++ polega na sposobie wy-
woływania metod, w przypadku języka C++ wystarczy utworzyć egzemplarz danej klasy i
bezpośrednio wywoływać jej funkcję. Natomiast publiczne metody obiektów COM zgrupo-
wane są w jeden lub kilka interfejsów, a sam obiekt jest w zasadzie strukturą przechowującą
wskaźniki do funkcji z nim powiązanych. Także w celu skorzystania z określonej metody,
musimy utworzyć obiekt COM i uzyskać wskaźnik do odpowiedniego interfejsu. Olbrzymią
zaleta obiektów COM jest możliwość ich użycia z poziomu języków nie zorientowanych
obiektowo. Wszystkie interfejsy DirectX wyprowadzane są z interfejsu IUnknown, posiadają-
cego jedynie trzy funkcje
AddRef(), QueryInterface( REFIID riid, LPVOID* ppvObj )
oraz
Release().
Głównymi zadaniami metod tego interfejsu jest określanie liczby odwołań aplikacji
do danego obiektu COM. W przypadku języka C++ każda aplikacja tworzy niezależnie eg-
zemplarze obiektów np. przy pomocy operatora
new,
w chwili gdy przestaje korzystać z da-
nego obiektu wywołuje operator
delete
i usuwa obiekt z pamięci. Z obiektami COM jest zu-
pełnie inaczej, gdyż w pamięci znajduje się pojedynczy egzemplarz danego obiektu COM, a
każda z aplikacji chcących z obiektu COM skorzystać otrzymuje wskaźnik do niego. Za każ-
-1-
Grafika komputerowa – Szkielet aplikacji
dym razem, gdy aplikacja tworzy wybrany obiekt COM, tak naprawdę wywoływana jest
funkcja AddRef(), zwiększająca ilość odwołań do znajdującego się w pamięci obiektu i apli-
kacji zwracany jest wskaźnik. Po skończonym działaniu aplikacja powinna wywoływać me-
todę Release() zmniejszającą o 1 liczbę odwołań do obiektu, w momencie osiągnięcia przez
liczbę odwołań wartości 0, obiekt może być usunięty z pamięci.
1.3 Tworzenie obiektu DirectDraw
Utworzenie obiektu DirectDraw polega na wywołaniu funkcji
DirectDrawCre-
ate,
która ma następującą postać:
HRESULT WINAPI DirectDrawCreate( GUID FAR
*lpGUID,
*lplpDD,
IUnknown FAR
*pUnkOuter);
LPDIRECTDRAW FAR
Parametr
lpGUID
określa wskaźnik na identyfikator karty, w przypadku podania wartości
NULL wybrana zostaje podstawowa karta graficzna. W
*lplpDD
podaje się adres wskaźnika
LPDIRECTDRAW, który zostanie ustawiony na interfejs IDirectDraw. Ostatni parametr po-
winien mieć wartość NULL. W przypadku poprawnego utworzenia obiektu DirectDraw funk-
cja zwróci wartość DD_OK.
Jeżeli utworzymy obiekt DirectDraw możemy przystąpić do ustawienia rodzaju
współpracy (tryb okienkowy lub pełnoekranowy) urządzenia (DirectDraw) z naszą aplikacją.
Czynności tej dokonujemy za pomocą funkcji.
HRESULT SetCooperativeLevel( HWND
hwnd,
DWORD
dwFlags
);
Pierwszym parametrem jaki musimy podać tej funkcji jest uchwyt naszego okna. Drugi para-
metr określa rodzaj współpracy, aplikacja może działać w trybie okienkowym lub pełnoekra-
nowym, możemy zabronić systemowi reakcji na kombinację klawiszy Ctrl+Alt+Del. W
związku z tym,
że
flag jest dość dużo opiszę jedynie dwie, których będziemy używać.
DDSCL_EXCLUSIVE oznacza,
że
nasza aplikacja ma prawo do wyłączności ekranu. Flaga
ta musi być używana z DDSCL_FULLSCREEN, która wymusza działanie aplikacji w trybie
pełno ekranowym.
Kolejną czynnością jest ustawienie odpowiedniego trybu graficznego, w którym pra-
cować będzie nasz program. Rozdzielczość ekranu ustawiamy za pomocą funkcji
HRESULT SetDisplayMode(DWORD
dwWidth,
DWORD
dwHeight,
DWORD
dwBPP
);
Kolejne parametry tej funkcji to po prostu szerokość, wysokość i liczba bitów na piksel trybu
graficznego , jaki chcemy uzyskać. Prawidłowo napisane aplikacje korzystające z DirectX
powinny oferować użytkownikowi zarówno możliwość karty graficznej (lub trybu jej pracy
np. emulacja) jak i trybu graficznego. Jednak dla naszych potrzeb wystarczające będzie poda-
nie parametrów jednego z trybów, które są obsługiwane przez posiadaną kartę graficzną. W
związku z tym,
że
duża część kart nie obsługuje trybu 24-bitowego (mają tryby 32-bitowe) w
ramach zajęć używać będziemy trybów 8 i 16- bitowych.
Wspomniane tryby graficzne wymagają szerszego omówienia. Pierwszy z nich jest
trybem indeksowym, czyli takim, w którym piksel w pamięci komputera reprezentowany jest
bezpośrednio przez indeks określający numer koloru w palecie barw. Jak
łatwo
obliczyć w
trybie 8-bitowym dostępne mamy 256 kolorów 24-bitowych. W związku z tym,
że
paletę ko-
lorów przypisuje się bezpośrednio do
powierzchni
sposób jej tworzenia przedstawiony będzie
poniżej. W drugim z trybów piksel opisywany jest bezpośrednio wartościami kolorów RGB, i
-2-
Grafika komputerowa – Szkielet aplikacji
tak na składową czerwoną oraz niebieską przeznaczono 5 bitów, natomiast na zieloną 6 bi-
tów.
1.4 Powierzchnie
Powierzchnie (obiekt
DirectDrawSurface)
są obiektami reprezentującymi liniowe ob-
szary pamięci graficznej, lub systemowej. Podstawowym zadaniem tych obiektów jest prze-
chowywanie danych reprezentujących obraz, widziany na ekranie monitora. Powierzchnie
mogą być również używane jako różnego typu bufory pomocnicze, służące np. jako
źródło
operacji kopiujących grafikę do innych powierzchni lub realizować zadanie z-bufora.
Chcąc wyświetlać jakąkolwiek grafikę za pomocą DirectDraw musimy utworzyć przy naj-
mniej jedną powierzchnię (podstawową). Dokonać możemy tego za pomocą funkcji
HRESULT IDirectDraw::CreateSurface(LPDDSURFACEDESC
lpDDSurfaceDesc,
LPDIRECTDRAWSURFACE FAR
*lplpDDSurface,
IUnknown FAR
*pUnkOuter);
Pierwszym parametrem tej funkcji jest struktura typu
DDSURFACEDESC,
opisująca wła-
ściwości
tworzonej powierzchni. W związku z tym,
że
struktura ta posiada dużą liczbę pól
omówione zostaną tylko niezbędne. Bardzo istotnym polem jest
dwSize,
w zmiennej tej na-
leży podać rozmiar struktury (przykład użycia powierzchni znajduje się poniżej). W polu
dwFlags podajemy kombinację flag informującą DirectDraw, które z pól całej struktury nale-
ży
wziąć pod uwagę, w naszym przypadku należy podać DDSD_CAPS| DDSD_BACK-
BUFFERCOUNT, kombinacja ta określa,
że
należy prawidłowo zdefiniować pola
dwBack-
BufferCount
i ddsCaps. Pierwsze z wymienionych pól określa liczbę tylnych buforów po-
wierzchni, natomiast drugie jest strukturą typu
DDSCAPS,
określającą właściwości samej po-
wierzchni. W naszych programach będziemy podawać
ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARY-
SURFACE |DDSCAPS_VIDEOMEMORY;
DDSCAPS_COMPLEX określa,
że
wraz z powierzchnią podstawową tworzone są tylne bu-
fory. Flaga ta dodatkowo powoduje,
że
wszystkie powierzchnie mogą być usunięte tylko po-
przez usuniecie powierzchni podstawowej. DDSCAPS_FLIP oznacza,
że
powierzchnia bę-
dzie przełączana z tylnymi buforami.
DDSCAPS_PRIMARSURFACE
określa,
że
tworzona
powierzchnia jest podstawową. DDSCAPS_VIDEOMEMORY flaga ta wymusza przydziele-
nie pamięci na powierzchnie w przestrzeni karty graficznej. Zmienna
lplpDDSurface
po-
winna zawierać adres wskaźnika na interfejs IDirectDrawSurface. Ostatni parametr powinien
mieć wartość NULL.
Po utworzeniu podstawowej powierzchni możemy uzyskać dostęp do tylnego bufora,
utworzonego wraz z nią. Można tego dokonać za pomocą funkcji
HRESULT IDirectDrawSurface::GetAttachedSurface( LPDDSCAPS
lpDDSCaps,
RECTDRAWSURFACE3 FAR
*lplpDDAttachedSurface
);
LPDI-
Pierwszy z parametrów jest adresem struktury DDSCAPS, opisującym właściwości po-
wierzchni, którą chcemy uzyskać. W naszym wypadku pole dwCaps tej struktury powinno
mieć wartość DDSCAPS_BACKBUFFER. Ostatni parametr powinien zawierać adres
wskaźnika na interfejs IDirectDrawSurface.
-3-
Grafika komputerowa – Szkielet aplikacji
1.5 Palety kolorów
Paletę kolorów w DirectX reprezentuje interfejs IDirectDrawPalette. Dostęp do tego
interfejsu można uzyskać za pomocą funkcji
HRESULT IDirectDraw::CreatePalette( DWORD
dwFlags,
LPPALETTEENTRY
lpColor-
Table,
LPDIRECTDRAWPALETTE FAR
*lplpDDPalette,
IUnknown FAR
*pUnkOuter);
Pierwszym parametrem jest flaga określająca typ palety, w naszym przypadku (dla trybu 8-
bitowego) należy podać DDPCAPS_8BIT. Kolejny parametr jest adresem do tablicy eleman-
tów PALETTEENTRY, opisujących kolor w postaci RGB. Kolejny argument powinien za-
wierać adres wskaźnika na interfejs IDirectDrawPalette. Ostatni parametr powinien mieć
wartość NULL.
Utworzoną w ten sposób paletę możemy skojarzyć z wybraną powierzchnią za pomo-
cą funkcji
HRESULT IDirectDrawSurface::SetPalette(LPDIRECTDRAWPALETTE
lpDDPalette
);
Jedynym argumentem tej funkcji jest adres wskaźnika na interfejs IdirectDrawPalette.
Poniżej zamieszczony jest kod funkcji inicjującej obiekt DirectDraw oraz tworzącej podsta-
wową oraz tylną powierzchnię.
HRESULT
LPDIRECTDRAW
DDSURFACEDESC
HWND hWnd;
LPDIRECTDRAWSURFACE
LPDIRECTDRAWSURFACE
DDSCAPS
ddrval;
lpDD;
ddsd;
FrontBuffer=NULL;
BackBuffer=NULL;
ddscaps;
...
bool TForm1::InitDDraw()
{
ddrval =
DirectDrawCreate(
NULL, &lpDD, NULL );
if (ddrval!=DD_OK) return false;
ddrval=lpDD->SetCooperativeLevel(hWnd,DDSCL_EXCLUSIVE| DDSCL_FULLSCREEN );
if (ddrval!=DD_OK) return false;
ddrval =
lpDD->SetDisplayMode(
640, 480, 16 );
if (ddrval!=DD_OK) return false;
ZeroMemory(&ddsd, sizeof(ddsd));//zawsze należy wyzerować wszystkie pola
ddsd.dwSize = sizeof(ddsd);
//ustawiamy rozmiary struktury
ddsd.dwFlags =
DDSD_CAPS |
DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX |
DDSCAPS_FLIP |
DDSCAPS_PRIMARYSURFACE |
DDSCAPS_VIDEOMEMORY;
ddsd.dwBackBufferCount = 1;
ddrval =
lpDD->CreateSurface(
&ddsd, &FrontBuffer, NULL );
if (ddrval!=DD_OK) return false;
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
-4-
Grafika komputerowa – Szkielet aplikacji
ddrval = FrontBuffer->GetAttachedSurface( &ddscaps, &BackBuffer );
return true;
}
1.6 Dostęp bezpośredni do zawartości powierzchni oraz rysowanie na po-
wierzchni
Najprostszą metodą rysowania obrazu na powierzchniach jest użycie standardowych
funkcji GDI. Zasadnicza różnica w stosunku do rysowania w zwykłym oknie Windows pole-
ga na pobieraniu uchwytu kontekstu urządzenia. W zwykłej aplikacji wystarczy pobrać
wspomniany uchwyt raz (np. po utworzeniu okna) i stosując zmienną globalną odwoływać się
do niej za każdym razem, gdy chcemy użyć funkcji GDI. W przypadku powierzchni takie
podejście jest niedopuszczalne, gdyż powierzchnia zostaje zablokowana po pobraniu uchwytu
kontekstu i
żadne
operacje na niej nie będą mogły być przeprowadzone do czasu zwolnienia
uchwytu. Funkcja pobierająca uchwyt jest bardzo podobna do standardowej funkcji API i ma
następującą składnie:
HRESULT IDirectDrawSurface::GetDC(HDC FAR
*lphDC
);
Jedynym parametrem tej metody jest adres zmiennej, w której ma zostać zwrócony uchwyt.
Do zwalniania kontekstu powierzchni używa się następującej funkcji
HRESULT IDirect-
DrawSurface::ReleaseDC(HDC
hDC
);
Poniżej znajduje się przykład wydrukowania na-
pisu na powierzchni
LPDIRECTDRAWSURFACE
FrontBuffer;
HDC
hDC;
...
FrontBuffer->GetDC(&hDC);
//pobieramy
SetBkColor(hDC,0);
//ustawiamy
SetTextColor(hDC,0xffffff);
//ustawiamy
TextOut(hDC,
0,0,”Tekst”,5); //drukujemy
FrontBuffer->ReleaseDC(hDC); //zwalniamy
...
uchwyt
kolor tła
kolor tekstu
napis
uchwyt
Inną i znacznie ciekawszą metodą rysowania grafiki jest bezpośrednie odwoływanie się do
obszarów pamięci powierzchni. Jednak aby móc odwołać się do jakiegokolwiek obszaru pa-
mięci należy znać jego adres. W przypadku powierzchni adres uzyskuje się po wywołaniu
następującej funkcji
Lock()
o następującej składni:
HRESULT IdirectDrawSurface::Lock( LPRECT
lpDestRect,
LPDDSURFACEDESC
lpDDSurfaceDesc,
DWORD
dwFlags,
HANDLE
hEvent);
Pierwszym argumentem jest wskaźnik na strukturę
RECT
opisującą prostokątny obszar, któ-
ry ma być zablokowany. Jeżeli podamy
NULL
blokowana jest cała powierzchnia. Kolejnym
parametrem jest wskaźnik na strukturę
DDSURFACEDESC,
w której zostaną zwrócone in-
formacje dotyczące powierzchni (między innymi adres pamięci). Struktura ta ma szereg pól
jednak dla naszych potrzeb istotne są dwa. Pierwsze z nich to
lpSurface
w przypadku po-
prawnego wywołania funkcji pole to będzie adresem pamięci powierzchni. Drugie z pól to
lPitch
i określa ono odstęp między początkami dwóch kolejnych linii wyrażony w bajtach.
Należy o tym pamiętać, gdyż często zdarza się,
że
w trybie graficznym 640x480x8 pojedyn-
cza linia ekranu ma więcej niż 640 bajtów. Wynika to bezpośrednio z mechanizmów zarzą-
dzania pamięcią. Kolejny parametr określa sposób blokowania powierzchni dla naszych po-
trzeb należy podać wartość DDLOCK_WAIT, co oznacza,
że
powierzchnia nie zostanie za-
blokowana jeżeli jakiekolwiek operacje graficzne są na niej w danej chwili wykonywane.
-5-
Plik z chomika:
jacekplacekjacek
Inne pliki z tego folderu:
professional-c-4-0-and-net-4.pdf
(44260 KB)
addison-wesley-windows-system-programming-4ed-mtshart2010.pdf
(10460 KB)
Visual C++ and MFC Programming 2nd.pdf
(9754 KB)
chand_colorfigs.pdf
(5736 KB)
Introduction to C++ Programming and Graphics.pdf
(3705 KB)
Inne foldery tego chomika:
Arduino
Asembler
C++
DirectX
GDB
Zgłoś jeśli
naruszono regulamin