Стэн Трухильо - Графика для Windows средствами DirectDraw
В Visual C++ 5.0 ClassView отображает лишь те классы, чьи заголовочные файлы были явно включены в проект (командой Project|Add to Project|Files). Если в проект не включено ни одного H-файла, в ClassView не будет ни одного класса. Этим он отличается от Visual C++ 4.x, где выводились все классы из файлов проекта и тех файлов, от которых зависят файлы проекта. Это может показаться шагом назад, но на самом деле это новое поведение позволяет управлять составом классов, отображаемых в ClassView (в предыдущих версиях такого выбора не было).
При импортировании старых проектов в Visual C++ 5.0 по умолчанию отображаются все классы. Класс отсутствует в ClassView лишь в том случае, если CPP-файл был включен в проект без соответствующего H-файла.
Работа с Visual C++ 4.0Хотя Visual C++ 4.0 не используется в этой книге напрямую, ничто не помешает вам работать с ним. Но так как на CD-ROM находятся файлы проектов только для Visual C++ 5.0 (для которого обратная совместимость не предусмотрена), вам придется создать файлы рабочей области самостоятельно. Это делается так:
1. Создайте пустое приложение Win32.
2. Скопируйте CPP-, H- и RC-файлы из каталога с компилируемой программой в каталог с новой, пустой рабочей областью.
3. Включите файлы в пустой проект (командой Project|Add to project|Files).
Заодно можно настроить прекомпилированные заголовки (см. выше). Проект готов к компиляции.
Хотя пользователи Visual C++ 4.x смогут пользоваться программами из этой книги, DirectDraw AppWizard работать не будет. Если читатели проявят достаточный интерес, я могу создать соответствующую версию и поместить ее на Web-узел.
Варианты Visual C++И последнее замечание о Visual C++. Поскольку этот продукт выпускается в трех вариантах (Learning, Professional и Enterprise), иногда читателям не удается скомпилировать проект. Для написания и тестирования программ этой книги я пользовался вариантом Professional, так что при работе с другими вариантами могут возникнуть проблемы. Впрочем, в основном это проблемы мелкие и легко излечимые. Если у вас возникнут трудности и вы решите обратиться ко мне, пожалуйста, укажите версию и вариант Visual C++.
Советы и рекомендации
В этом разделе рассматривается несколько не связанных друг с другом, но полезных тем. Сначала мы поговорим об одной ошибке DirectDraw, а затем узнаем кое-что о файлах DirectX. Напоследок я скажу пару слов о видеокартах на базе чипов 3Dfx.
Ошибка переключения режимов DirectDrawВ DirectDraw есть одна ошибка, связанная с переключением режимов, которую никак не может исправить Microsoft (или тот, кто для них пишет видеодрайверы). Впервые я столкнулся с ней во время работы над предыдущей книгой. Я надеялся, что к моменту написания этой книги ошибку уже исправят, но она так и осталась.
Проблемы возникают при активизации видеорежимов, которые отличаются по глубине пикселей от текущего активного режима Windows. Например, если Windows работает в 8-битном видеорежиме, а ваше приложение DirectDraw попытается активизировать 16-битный видеорежим, ничего не получится, даже когда новый режим вполне допустим. Если попытаться установить 8-битный видеорежим при 16-битном режиме Windows, результат будет тем же. В таких случаях DirectX выдает отладочное сообщение следующего вида:
DDHEL: ChangeDisplaySettings LIED!!!
DDHEL: Wanted 640x480x16 got 1024x768x8
DDHEL: ChangeDisplaySettings FAILED: returned -1
К счастью, это происходит только при первой попытке изменения видеорежима. Если эта попытка удалась, после этого можно установить любой видеорежим. Следовательно, у нас появляется обходной путь: если текущий видеорежим отличается по глубине пикселей от желаемого, переключение следует производить в два этапа; сначала перейдите к видеорежиму, который совпадает по глубине пикселей с текущим, а затем — к видеорежиму, нужному вам.
Такой обходной маневр нетрудно реализовать с помощью класса DirectDrawWin, представленного в этой книге. В функции SelectInitialDisplayMode() (которая вызывается до переключения видеорежима) следует проверить глубину пикселей текущего режима. Если она отличается от требуемой, выполните «фиктивное» переключение. Программа может выглядеть так:
int SampleWin::SelectInitialDisplayMode() {
DWORD curdepth=GetDisplayDepth();
int i, nummodes=GetNumDisplayModes();
DWORD w,h,d;
if (curdepth!=16) ddraw2->SetDisplayMode(640, 480, curdepth, 0, 0 );
// Искать режим 640x480x16 после смены видеорежима
for (i=0;i>nummodes;i++) {
GetDisplayModeDimensions(i, w, h, d);
if (w==640 && h==480 && d==16) return i;
}
return -1;
}
Мы проверяем глубину пикселей текущего видеорежима Windows функцией DirectDrawWin::GetDisplayDepth(). Нас интересует режим с 16-битными пикселями, поэтому при использовании другой глубины мы активизируем режим 640×480 с текущей глубиной, вызывая функцию SetDisplayMode() интерфейса DirectDraw. Затем можно переходить к поиску нужного режима в традиционном цикле.
Другой выход — просто воспользоваться той глубиной пикселей, которая в данный момент установлена в Windows. Этот вариант не подойдет в тех случаях, когда работа программы зависит от определенных параметров видеорежима, но неплохо работает, если приложение поддерживает видеорежимы с различными глубинами пикселей. В частности, он встречается в программе Switch (см. главу 4), разработанной специально для поддержки любого режима. Функция SelectInitialDisplayMode() в программе Switch выглядит так:
int SwitchWin::SelectInitialDisplayMode() {
DWORD curdepth=GetDisplayDepth();
int i, nummodes=GetNumDisplayModes();
DWORD w,h,d;
// Искать режим 640x480 с текущей глубиной пикселей
for (i=0;i<nummodes;i++) {
GetDisplayModeDimensions(i, w, h, d);
if (w==640 && h==480 && d==curdepth) return i;
}
return 0;
}
Эта функция ищет режим 640×480 с текущей глубиной пикселей. Такой режим наверняка найдется, потому что 640×480 — «общий знаменатель» для всех видеорежимов и нам известно, что нужная глубина пикселей уже установлена в Windows.
Следует заметить, что такое поведение характерно лишь для некоторых конфигураций. Иногда нужную глубину пикселей можно установить сразу, без глупых обходных маневров (например, я еще не видел, чтобы эта ошибка проявлялась на компьютерах с Windows NT). Но чтобы расширить круг рабочих конфигураций вашего приложения, пожалуй, стоит помнить об этой ошибке.
Символическая константа INITGUID (устаревшая)Как вы уже знаете, библиотека DirectX построена на базе спецификации COM, а для однозначной и систематизированной идентификации интерфейсов в COM применяются GUID. Один из заголовочных файлов COM содержит код, в котором инициализируются все конструкции, относящиеся к GUID. Такой метод инициализации COM требует, чтобы символическая константа INITGUID была определена в одном и только одном кодовом модуле, до включения заголовочных файлов COM (для DirectX заголовочные файлы COM включаются косвенно, из заголовочных файлов DirectX). Следовательно, в некоторых программах можно встретить константу INITGUID. Этот метод вызывает немало хлопот, особенно при работе с прекомпилированными заголовками, потому что он не позволяет включить заголовочные файлы DirectX в состав прекомпилированного заголовка.
Вы не встретите INITGUID в программах на CD-ROM, потому что, начиная с DirectX 3, появилось более удачное решение — вместо того, чтобы определять INITGUID, достаточно подключить к проекту файл DXGUID.LIB. На случай, если вы не знали…
Эмуляция версийОдна из широко разрекламированных возможностей COM — управление версиями. COM-объекты устроены так, что доступ к ним может осуществляться только через строго определенные интерфейсы. В соответствии с правилами COM, интерфейс не может изменяться после его определения. Вместо этого приходится вводить новый интерфейс, который поддерживает как старые, так и новые возможности. При этом новые программы могут без опасений пользоваться новыми возможностями, а старые — работать со старыми интерфейсами, которые заведомо не изменятся. Эта схема неплохо работает и помогает обеспечить совместимость приложений DirectX со старыми и новыми runtime-частями библиотеки.
К сожалению, в DirectX API часто используются структуры. Эти структуры являются «открытыми» — доступ к ним осуществляется непосредственно, а не через интерфейс, как для COM-объектов. Размер этих структур может изменяться (и часто изменяется) при переходе к новой версии DirectX. По этой причине каждая функция DirectX, которой в качестве аргумента передается указатель на структуру, должна обязательно получать и размер передаваемой структуры. Благодаря этому runtime-часть DirectX всегда может узнать, какая версия DirectX SDK применялась для компиляции приложения, и следовательно — какие поля входят в структуру. Проблема решена, не так ли?
А что вы скажете насчет программы, которая была откомпилирована в DirectX 5 SDK, но затем запущена с runtime-частью DirectX 3? Если одна или несколько структур DirectX 5 были дополнены новыми полями и флагами, runtime-часть не сможет обработать эту структуру, потому что ничего не знает о появившихся в ней расширениях.