ініціалізація directdraw

Ця книга починається з того, на чому інші книги зазвичай закінчувалися. Ми поговоримо про основи DirectDraw, але лише в загальних рисах. Читач - досвідчений програміст, але незнайомий з DirectDraw - зможе з ходу увійти в курс справи. Потім ми перейдемо до інших тем, настільки ж цікавим, як і корисним.

Мета цієї книги - навчити вас працювати з DirectDraw, а не надати деяку «структурну основу» або нестандартний API, який би виконував за вас всю роботу. Демонстраційні програми написані на C ++ і використовують MFC, але зовсім не для того, щоб приховати всі технічні подробиці. С ++ і MFC - чудові інструменти, тому що з їх допомогою будь-який додаток можна написати декількома різними способами. Приклади для цієї книги були написані так, щоб при цьому виходили структуровані і зручні для читання проекти, які наочно показують, що і чому відбувається в програмі.

Крім DirectDraw, у багатьох прикладах використовується бібліотека DirectInput. Строго кажучи, при програмуванні графіки для Windows можна обійтися і без DirectInput, але їй все ж варто скористатися. Вона працює швидше традиційних засобів вводу Windows і до того ж входить в DirectX, так що для роботи з нею не потрібно ніяких додаткових SDK.

Книга: Графіка для Windows засобами DirectDraw

ініціалізація DirectDraw

Розділи на цій сторінці:

Фактичне створення вікна (виклик функції CreateEx ()) змушує Windows послати нашому додатком повідомлення WM_CREATE. Клас DirectDrawWin перехоплює це повідомлення у обробнику OnCreate (). створеному ClassWizard (див. лістинг 3.1).

Лістинг 3.1. Функція DirectDrawWin :: OnCreate ()

int DirectDrawWin :: OnCreate (LPCREATESTRUCT) DirectDrawEnumerate (DriverAvailable, this);
if (totaldrivers == 0) AfxMessageBox ( "No DirectDraw drivers detected");
return -1;
>
int driverindex = SelectDriver ();
if (driverindex<0) TRACE("No DirectDraw driver selectedn");
return -1;
> Else if (driverindex> totaldrivers-1) AfxMessageBox ( "Invalid DirectDraw driver selectedn");
return -1;
>
LPDIRECTDRAW ddraw1;
DirectDrawCreate (driver [driverindex] .guid, ddraw1, 0);
HRESULT r;
r = ddraw1-> QueryInterface (IID_IDirectDraw2, (void **) ddraw2);
if (r! = S_OK) AfxMessageBox ( "DirectDraw2 interface not supported");
return -1;
>
ddraw1-> Release (), ddraw1 = 0;
ddraw2-> SetCooperativeLevel (GetSafeHwnd (), DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
ddraw2-> EnumDisplayModes (0, 0, this, DisplayModeAvailable);
qsort (displaymode, totaldisplaymodes, sizeof (DisplayModeInfo), CompareModes);
int initmode = SelectInitialDisplayMode ();
if (ActivateDisplayMode (initmode) == FALSE) return -1;
return 0;
>

Вся ініціалізація DirectDraw виконується у функції OnCreate () (за підтримки декількох допоміжних функцій). Процес ініціалізації складається з семи етапів:

• Отримання списку всіх драйверів DirectDraw.

• Вибір драйвера DirectDraw.

• Ініціалізація DirectDraw з використанням обраного драйвера.

• Створення поверхонь додатки.

Всі ці етапи розглядаються в наступних розділах.

Отримання списку драйверів DirectDraw

Функція DirectDrawEnumerate () отримує два аргументи: покажчик на побічно спричинюється (callback) функцію і покажчик на дані, які визначаються додатком, які передаються цій функції при виклику. У нашому випадку аргументами є побічно викликається, DriverAvailable () і покажчик на клас DirectDrawWin (this). Функція DriverAvailable () визначається так:

BOOL WINAPI DirectDrawWin :: DriverAvailable (LPGUID guid, LPSTR desc, LPSTR name, LPVOID p) DirectDrawWin * win = (DirectDrawWin *) p;
if (win-> totaldrivers> = MAXDRIVERS) return DDENUMRET_CANCEL;
DriverInfo info = win-> driver [win-> totaldrivers];
if (guid) info.guid = (GUID *) new BYTE [sizeof (GUID)];
memcpy (info.guid, guid, sizeof (GUID));
> Else info.guid = 0;
info.desc = strdup (desc);
info.name = strdup (name);
win-> totaldrivers ++;
return DDENUMRET_OK;
>

Спочатку покажчик на дані, які визначаються додатком (p), перетворюється в покажчик на клас DirectDrawWin (win). Оскільки функція DriverAvailable () оголошена як статична (побічно викликаються функції повинні бути статичними), на неї на відміну від звичайних функцій класу не поширюються правила автоматичного доступу; відповідно доступ до змінних і функцій класу доводиться здійснювати через покажчик win.

DirectDraw викликає функцію DriverAvailable () один раз для кожного виявленого драйвера. При кожному виклику передаються три інформаційних об'єкта: GUID, опис та ім'я. GUID (глобально-унікальний ідентифікатор) однозначно ідентифікує драйвер. Опис і ім'я є рядки для неформальної ідентифікації драйвера. Функція DriverAvailable () зберігає відомості про кожного драйвера в масиві з ім'ям driver і відстежує кількість драйверів в змінної totaldrivers. Нарешті, функція DriverAvailable () повертає DDNUMRET_OK. показуючи, що перерахування драйверів повинно тривати. При отриманні коду повернення DDENUMRET_CANCEL DirectDraw припиняє перерахування драйверів.

вибір драйвера

Після того як всі драйвери DirectDraw будуть перераховані, функція OnCreate () вибирає один з них. Вибір драйвера за замовчуванням може бути перевизначений в похідних класах з допомогою віртуальної функції SelectDriver (). Повертаючись до лістингу 3.1, ми бачимо, що величина, яка повертається функцією SelectDriver (). використовується в якості індексу масиву driver (причому значення індексу починаються з нуля). Індекс показує, який GUID (і, отже, драйвер) повинен використовуватися для ініціалізації DirectDraw. Версія SelectDriver () з класу DirectDrawWin виглядає так:

virtual int SelectDriver () return 0;
>

int bounceWin :: SelectDriver () int numdrivers = GetNumDrivers ();
if (numdrivers == 1) return 0;
CArray drivers;
for (int i = 0; i GetDriverInfo (i, 0, desc, name);
drivers.Add (desc);
>
DriverDialog dialog;
dialog.SetContents (drivers);
if (dialog.DoModal ()! = IDOK) return -1;
return dialog.GetSelection ();
>

Ця функція спочатку визначає кількість виявлених драйверів за допомогою функції GetNumDrivers (). яка просто повертає значення закритою змінної totaldrivers. Якщо в системі виявлено всього один драйвер, виводити меню нема чого, тому функція повертає 0, щоб використовувався первинний драйвер.

Класи, похідні від DirectDrawWin. можуть реалізувати функцію SelectDriver () і іншими способами. Наведена тут реалізація відрізняється простотою і гнучкістю, але можливо, вам захочеться форматувати кожен драйвер і перевірити його на наявність конкретних можливостей. У деяких додатках функція SelectDriver () може використовуватися для вибору драйвера, найкраще відповідає заданим критеріям.

ініціалізація DirectDraw

Третє завдання, що виконується функцією OnCreate (), - ініціалізація DirectDraw. Я знову наводжу відповідний фрагмент лістингу 3.1:

LPDIRECTDRAW ddraw1;
DirectDrawCreate (driver [driverindex] .guid, ddraw1, 0);
HRESULT r;
r = ddraw1-> QueryInterface (IID_IDirectDraw2, (void **) ddraw2);
if (r! = S_OK) AfxMessageBox ( "DirectDraw2 interface not supported");
return -1;
>
ddraw1-> Release (), ddraw1 = 0;
ddraw2-> SetCooperativeLevel (GetSafeHwnd (), DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);

ініціалізація directdraw

Мал. 3.9. Діалогове вікно для вибору драйвера

Після того як інтерфейс DirectDraw буде инициализирован, їм можна скористатися для отримання покажчика на інтерфейс DirectDraw2. Для цього слід викликати функцію QueryInterface () і передати їй GUID інтерфейсу DirectDraw2. певний під ім'ям IID_IDirectDraw2. Якщо виклик QueryInterface () закінчується невдало, програма виводить діалогове вікно і завершує роботу. Фактично ми вимагаємо присутності бібліотеки DirectX версії 2 і вище (бо інтерфейс DirectDraw2 вперше з'явився в DirectX 2). Якщо виклик QueryInterface () виявиться успішним, покажчик ddraw1 звільняється. Поперемінний виклик функцій інтерфейсів DirectDraw і DirectDraw2 не рекомендується, тому звільнення покажчика на інтерфейс DirectDraw гарантує, що в решти коду буде використовуватися тільки інтерфейс DirectDraw2.

HRESULT WINAPI DirectDrawWin :: DisplayModeAvailable (LPDDSURFACEDESC desc, LPVOID p) DirectDrawWin * win = (DirectDrawWin *) p;
int count = win-> totaldisplaymodes;
if (count == MAXDISPLAYMODES) return DDENUMRET_CANCEL;
win-> displaymode [count] .width = desc-> dwWidth;
win-> displaymode [count] .height = desc-> dwHeight;
win-> displaymode [count] .depth = desc-> ddpfPixelFormat.dwRGBBitCount;
count ++;
return DDENUMRET_OK;
>

virtual int SelectInitialDisplayMode () = 0;

int BounceWin :: SelectInitialDisplayMode () int i, nummodes = GetNumDisplayModes ();
DWORD w, h, d;
for (i = 0; i if (w == desiredwidth h == desiredheight d == desireddepth) return i;
>
for (i = 0; i> nummodes; i ++) GetDisplayModeDimensions (i, w, h, d);
if (d == desireddepth) return i;
>
return 0;
>

На передостанньому етапі відбувається активізація обраного режиму. Для цього використовується функція ActivateDisplayMode (). яка насправді виконує і завдання останнього етапу (створення поверхонь додатки). Код цієї функції наведено в лістингу 3.2.

Лістинг 3.2. Функція ActivateDisplayMode ()