Графика для Windows библиотека программиста средствами DirectDraw
bda5893f

Инициализация


В полноэкранном варианте класса DirectDrawWin функция OnCreate() инициализирует DirectDraw за несколько этапов. Оконный вариант выглядит проще, потому что ему не приходится перечислять драйверы DirectDraw или видеорежимы. Оконная версия функции OnCreate() выглядит так:

int DirectDrawWin::OnCreate(LPCREATESTRUCT) { LPDIRECTDRAW ddraw1; DirectDrawCreate( 0, &ddraw1, 0 ); ddraw1->QueryInterface(IID_IDirectDraw2, (void**)&ddraw2); ddraw1->Release(), ddraw1=0; ddraw2->SetCooperativeLevel( GetSafeHwnd(), DDSCL_NORMAL ); DetectDisplayMode(); if (CreateFlippingSurfaces()==FALSE) { 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(), - создание поверхности вторичного буфера. В идеальном варианте нам удастся найти свободную видеопамять в объеме, достаточном для создания внеэкранной поверхности, которая по ширине и высоте совпадает с первичной поверхностью. Я называю такой вариант идеальным из-за преимущества по скорости, характерного для блит-операций в пределах видеопамяти. Кроме того, поскольку вторичный буфер по размерам совпадает с первичной поверхностью, он подойдет для окна любого размера.
Функция CreateFlippingSurfaces() пытается создать «идеальный» вторичный буфер, для чего используются флаг DDSCAPS_VIDEOMEMORY и функция CreateSurface(). Если вызов заканчивается успешно, флаг videobacksurf получает значение TRUE, а функция завершает работу. В противном случае вторичный буфер не создается, а флагу videobacksurf присваивается значение FALSE.
В том варианте вторичный буфер создается приложением в системной памяти позднее, в обработчике OnSize(). Функция OnSize() вызывается при изменении размеров окна приложения. Создавая вторичный буфер по размерам клиентской области окна, мы экономим память. Функция OnSize() выглядит так:

void DirectDrawWin::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); CFrameWnd::GetClientRect( &clientrect ); CFrameWnd::ClientToScreen( &clientrect ); if (videobacksurf) return; DDSURFACEDESC desc; ZeroMemory( &desc, sizeof(desc) ); desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; desc.dwWidth = clientrect.Width(); desc.dwHeight = clientrect.Height(); desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; if (backsurf) backsurf->Release(), backsurf=0; HRESULT r=ddraw2->CreateSurface( &desc, &backsurf, 0 ); if (r!=DD_OK) { TRACE("failed to create 'backsurf'\n"); return; } else TRACE("backsurf w=%d h=%d\n", clientrect.Width(), clientrect.Height() ); }

Инициализация приложения завершается вызовом функций StorePixelFormatData() и CreateCustomSurfaces(), происходящим в обработчике OnCreate(). Обе функции ведут себя точно так же, как и в полноэкранном приложении.


Содержание раздела