AfxMessageBox("CreateFlippingSurfaces() failed");
return FALSE;
}
if (CreateCustomSurfaces()==FALSE) {
AfxMessageBox("CreateCustomSurfaces() failed");
return FALSE;
}
return 0;
}
Сначала указатель на интерфейс DirectDraw(ddraw1) инициализируется функцией DirectDrawCreate(). Указатель ddraw1, как и в полноэкранной версии, используется только для получения указателя на интерфейс DirectDraw2, после чего освобождается.
Затем функция OnCreate() вызывает функцию SetCooperativeLevel(). В полноэкранном приложении уровень кооперации определялся тремя флагами: DDSCL_EXCLUSIVE, DDSCL_FULLSCREEN и DDSCL_ALLOWMODEX. В данном случае используется только флаг DDSCL_NORMAL.
Функция DetectDisplayMode() инициализирует некоторые переменные класса DirectDrawWin. Она выглядит так:
BOOL DirectDrawWin::DetectDisplayMode() {
DDSURFACEDESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.dwSize=sizeof(desc);
if (ddraw2->GetDisplayMode(&desc)!=DD_OK) {
TRACE("GetDisplayMode() failed\n");
return FALSE;
}
displayrect.left=0;
displayrect.top=0;
displayrect.right=desc.dwWidth;
displayrect.bottom=desc.dwHeight;
displaydepth=desc.ddpfPixelFormat.dwRGBBitCount;
return TRUE;
}
Функция DetectDisplayMode() с помощью функции GetDisplayMode() интерфейса DirectDraw получает информацию о текущем видеорежиме Windows. Говоря точнее, разрешение экрана и глубина пикселей текущего видеорежима сохраняются в переменных displayrect и displaydepth.
Далее OnCreate() вызывает функцию CreateFlippingSurfaces(). Хотя оконное приложение не может выполнять настоящего переключения страниц (как можно было бы решить, исходя из имени функции), имя было сохранено, потому что создаваемые в ней поверхности эмулируют переключение страниц. Код функции приведен в листинге 3.4.
Листинг 3.4. Функция CreateFlippingSurfaces() в оконном приложении
BOOL DirectDrawWin::CreateFlippingSurfaces() {
HRESULT r;
DDSURFACEDESC desc;
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_CAPS;
desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
r=ddraw2->CreateSurface(&desc, &primsurf, 0);
if (r!=DD_OK) {
TRACE("FAILED to create 'primsurf'\n");
return FALSE;
}
r=ddraw2->CreateClipper(0, &clipper, 0);
if (r!=DD_OK) {
TRACE("CreateClipper() failed\n");
return FALSE;
}
r=clipper->SetHWnd(0, GetSafeHwnd());
if (r!=DD_OK) {
TRACE("SetHWnd() failed\n");
return FALSE;
}
r=primsurf->SetClipper(clipper);
if (r!=DD_OK) {
TRACE("SetClipper() failed\n");
return FALSE;
}
ZeroMemory(&desc, sizeof(desc));
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
desc.dwWidth = displayrect.Width();
desc.dwHeight = displayrect.Height();
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
r=ddraw2->CreateSurface(&desc, &backsurf, 0);
if (r!=DD_OK) {
TRACE("failed to create 'backsurf' in video\n");
videobacksurf=FALSE;
} else {
TRACE("Created backsurf in video\n");
videobacksurf=TRUE;
}
return TRUE;
}
Сначала мы создаем первичную поверхность. В полноэкранном варианте код выглядит по-другому, потому что здесь создается обычная, несоставная первичная поверхность. В структуре DDSURFACEDESC мы описываем первичную поверхность, используя только флаг DDSCAPS_PRIMARYSURFACE. Затем описанная поверхность создается функцией CreateSurface() интерфейса DirectDraw.
Далее функция CreateClipper() интерфейса DirectDraw создает объект отсечения. CreateClipper() получает три аргумента, однако первый и последний из них чаще всего равны нулю. Второй аргумент представляет собой адрес указателя на интерфейс DirectDrawClipper. В нашем случае используется переменная класса DirectDrawWin с именем clipper.
Объект отсечения нужен для ограничения вывода в программе. Поскольку наше приложение работает в окне, которое находится на рабочем столе вместе с другими окнами, при обновлении изображения необходимо учитывать присутствие этих окон. Чтобы объект отсечения автоматически выполнял свою работу, его необходимо присоединить к окну функцией SetHWnd() интерфейса DirectDrawClipper. Функция SetHWnd() получает два аргумента — двойное слово (DWORD), которое зарезервировано для будущего использования и пока должно быть равно нулю, и логический номер окна приложения.
Далее объект отсечения присоединяется к первичной поверхности приложения функцией SetClipper() интерфейса DirectDrawSurface. После такого присоединения можно осуществлять блиттинг на первичную поверхность с помощью функции Blt() интерфейса DirectDrawSurface. Использовать функцию BltFast() нельзя, потому что она не поддерживает отсечения.
Последнее, что происходит в функции CreateFlippingSurface(), - создание поверхности вторичного буфера. В идеальном варианте нам удастся найти свободную видеопамять в объеме, достаточном для создания внеэкранной поверхности, которая по ширине и высоте совпадает с первичной поверхностью. Я называю такой вариант идеальным из-за преимущества по скорости, характерного для блит-операций в пределах видеопамяти. Кроме того, поскольку вторичный буфер по размерам совпадает с первичной поверхностью, он подойдет для окна любого размера.