netlib.narod.ru | < Назад | Оглавление | Далее > |
В оставшейся части главы мы изучим код приложения FullScreen. FullScreen — это приложение Direct3D, работающее в полноэкранном режиме. Приложение использует полноэкранную версию класса RMWin, чтобы получить возможность переключать видеорежимы и выполнять переключение страниц. Вид экрана приложения показан на рис. 10.2.
Рис. 10.2. Приложение FullScreen
Приложение FullScreen осуществляет анимацию знакомой нам сетки завитка в полноэкранном режиме, используя интерфейс Direct3DRMAnimation. Более интересен тот факт, что приложение отображает список всех обнаруженных видеорежимов и позволяет активировать любой из них. Для выбора видеорежима используются клавиши управления курсором, выбранный режим включается при нажатии на клавишу ENTER.
Когда вы запускаете приложение FullScreen, на экране отображаются все видеорежимы, поддерживаемые вашей видеокартой. Меню, изображенное на рис. 10.2 показывает видеорежимы, поддерживаемые видеокартой ATI Mach 64.
СОВЕТ |
Ограничения, накладываемые монитором. Чтобы видеорежим работал правильно, его должны поддерживать и видеокарта, и монитор. Приложение FullScreen отображает видеорежиы, поддерживаемые видеокартой. Они могут поддерживаться, а могут и не поддерживаться монитором. Если вы выбрали видеорежим, и экран остается темным более 10 секунд, нажмите клавишу ESC, чтобы завершить работу приложения и вернуться к рабочему столу Windows. |
Приложение также отображает индикатор скорости работы — счетчик частоты кадров (FPS).
Приложение FullScreen демонстрирует применение следующих технологий:
Функциональность приложения FullScreen сосредоточена в классе FullScreenWin. В качестве базового класса для FullScreenWin используется класс RMWin, что можно увидеть в листинге 10.8.
Листинг 10.8. Класс FullScreenWin |
class FullScreenWin : public RMWin { public: FullScreenWin(); protected: //{{AFX_MSG(FullScreenWin) afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: void OnRenderWireframe(); void OnRenderFlat(); void OnRenderGouraud(); BOOL CreateScene(); void Render(); static void UpdateAnimation(LPDIRECT3DRMFRAME, void*, D3DVALUE); BOOL CreateMenuSurface(); BOOL UpdateMenuSurface(); BOOL CreateFPSSurface(); BOOL UpdateFPSSurface(); private: LPDIRECT3DRMMESHBUILDER meshbuilder; static LPDIRECT3DRMANIMATION animation; HFONT smallfont, largefont; LPDIRECTDRAWSURFACE menusurf; RECT menurect; int selectmode; LPDIRECTDRAWSURFACE fpssurf; RECT fpsrect; BOOL displayfps; }; |
Единственной открытой функцией класса является конструктор, который отвечает за инициализацию динамических переменных класса.
Также объявлен единственный обработчик сообщений: OnKeyDown(). MFC вызывает эту функцию каждый раз, когда нажимается клавиша на клавиатуре. Мы воспользуемся этой функцией для реализации меню приложения.
Обратите внимание, что обычные функции реализации меню Render отсутствуют в секции MFC afx_msg. Вместо этого, они объявлены как три обычные закрытые функции класса. Это связано с тем, что мы не используем стандартное меню Windows. Нам придется самим вызывать эти функции, вместо того чтобы позволить MFC выполнить эту работу за нас.
Затем объявлена функция CreateScene(). Как и в приложениях из предыдущих глав, здесь функция CreateScene() отвечает за создание видимых объектов приложения.
Далее объявлена функция Render(). Вспомните, что в классе RMWin функция Render() объявлена как чисто виртуальная функция. Поэтому мы должны предоставить версию этой функции в классе FullScreenWin. Данная функция используется для визуализации и отображения выходных данных приложения.
Также объявлена статическая функция обратного вызова UpdateAnimation(). Она применяется для обновления анимационной последовательности приложения.
Следующие четыре функции, объявленные в классе FullScreenWin заслуживают более подробного обсуждения:
BOOL CreateMenuSurface(); BOOL UpdateMenuSurface(); BOOL CreateFPSSurface(); BOOL UpdateFPSSurface();
Когда вы запускаете приложение FullScreen в верхнем левом углу экрана появляется список доступных видеорежимов, а примерно через две секунды, в нижнем правом углу появляется счетчик частоты кадров (FPS).
Каждое из этих изображений отображается с использованием поверхностей, создаваемых и поддерживаемых классом FullScreenWin. Функции CreateMenuSurface() и UpdateMenuSurface() создают список (или меню) видеорежимов и управляют им. Функции CreateFPSSurface() и UpdateFPSSurface() создают и управляют счетчиком FPS.
В оставшейся части класса содержатся объявления переменных. Здесь объявлены указатели на интерфейсы Direct3DRMMeshBuilder и Direct3DRMAnimation. Следом расположены объявления двух экземпляров HFONT. Тип HFONT — это дескриптор шрифта Windows. Мы будем использовать эти дескрипторы для вывода текста меню видеорежимов и счетчика частоты кадров.
Следом объявлены переменные, которые будут использоваться для реализации меню видеорежимов:
LPDIRECTDRAWSURFACE menusurf; RECT menurect; int selectmode;
Указатель menusurf используется для управления поверхностью, содержащей меню видеорежимов. Структура menurect хранит размеры поверхности меню. Целочисленная переменная selectmode применяется для отслеживания выделенного в данный момент видеорежима. Не следует путать выделенный видеорежим с активным видеорежимом. Соответствующий видеорежиму пункт меню может быть выделен с помощью клавиш управления курсором, однако выделенный видеорежим не станет активным, пока пользователь не нажмет клавишу ENTER.
Оставшиеся переменные предназначены для вывода счетчика FPS:
LPDIRECTDRAWSURFACE fpssurf; RECT fpsrect; BOOL displayfps;
Указатель fpssurf используется для обновления и отображения поверхности счетчика FPS. Структура fpsrect хранит размеры поверхности fpssurf. Логическая переменная displayfps применяется для реализации задержки отображения счетчика FPS при каждой смене видеорежима. Это сделано по той причине, что начальные значения счетчика FPS обычно неправильны. Поэтому значение счетчика FPS отображается только после того, как скорость работы приложения стабилизируется.
Код конструктора класса FullScreenWin приведен в листинге 10.9.
Листинг 10.9. Функция FullScreenWin() |
FullScreenWin::FullScreenWin() { meshbuilder = 0; animation = 0; menusurf = 0; fpssurf = 0; selectmode = -1; displayfps = FALSE; UsePalette("palette.bmp"); largefont = CreateFont(28, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, "Arial"); smallfont = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, "Arial"); } |
Сначала выполняется инициализация переменных класса. Затем вызывается функция UsePalette(), чтобы указать имя файла BMP, содержащего палитру, которая будет использоваться в 8-разрядных видеорежимах.
Два дескриптора шрифтов, largefont и smallfont, инициализируются функцией Win32 CreateFont(). Единственное различие между этими двумя шрифтами — их размер. Крупный шрифт используется для отображения названия приложения (в верхней части меню видеорежимов). Мелкий шрифт используется для вывода элементов меню видеорежимов и для вывода счетчика FPS.
Функция CreateScene() представлена в листинге 10.10.
Листинг 10.10. Функция CreateScene() |
BOOL FullScreenWin::CreateScene() { //-------- ПОВЕРХНОСТИ МЕНЮ И FPS -------- selectmode = GetCurDisplayMode(); CreateMenuSurface(); UpdateMenuSurface(); CreateFPSSurface(); // ------- СЕТКА -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = 0; resinfo.lpName = MAKEINTRESOURCE(IDR_SWIRLMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, 0, D3DRMLOAD_FROMRESOURCE, 0, 0); ScaleMesh(meshbuilder, D3DVALUE(15)); //------- ФРЕЙМ СЕТКИ -------- LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.1)); meshframe->AddMoveCallback(UpdateAnimation, 0); //-------- АНИМАЦИЯ -------- d3drm->CreateAnimation(&animation); for (int i = 0; i < 11; i++) { D3DRMQUATERNION quat; D3DRMQuaternionFromRotation(&quat, &vect[i], rot[i]); animation->AddRotateKey(D3DVALUE(i), &quat); animation->AddPositionKey(D3DVALUE(i), trans[i].x, trans[i].y, trans[i].z); } animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION | D3DRMANIMATION_SCALEANDROTATION); animation->SetFrame(meshframe); meshframe->Release(); meshframe = 0; // -------- НАПРАВЛЕННЫЙ СВЕТ -------- LPDIRECT3DRMFRAME dlightframe; LPDIRECT3DRMLIGHT dlight; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &dlight); d3drm->CreateFrame(scene, &dlightframe); dlightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); dlightframe->AddLight(dlight); dlightframe->Release(); dlightframe = 0; dlight->Release(); dlight = 0; //------ КАМЕРА ---------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; } |
Функция CreateScene() выполняет следующие шесть действий:
Мы не будем обсуждать этапы со 2 по 6, поскольку они неоднократно рассматривались в предыдущих главах. Сейчас для нас более интересен первый этап, на котором осуществляется инициализация поверхностей для меню видеорежимов и счетчика FPS. Поверхности инициализируются следующими вызовами функций:
selectmode = GetCurDisplayMode(); CreateMenuSurface(); UpdateMenuSurface(); CreateFPSSurface();
Функция GetCurDisplayMode() используется для инициализации переменной selectmode. Это делается для того, чтобы в меню был выделен текущий видеорежим. Функция CreateMenuSurface() создает поверхность, которая будет использована для отображения меню видеорежимов. Функция UpdateMenuSurface() формирует содержимое для поверхности меню видеорежимов. Функция CreateFPSSurface() создает поверхность для счетчика FPS, но само значение FPS пока не рассчитывается, поскольку еще не было выведено ни одного кадра.
Функция CreateMenuSurface() выглядит следующим образом:
BOOL FullScreenWin::CreateMenuSurface() { menusurf = CreateSurface(menuwidth, menuheight); menurect.left = 0; menurect.top = 0; menurect.right = menuwidth; menurect.bottom = menuheight; DDCOLORKEY ddck; ddck.dwColorSpaceLowValue = 0; ddck.dwColorSpaceHighValue = 0; menusurf->SetColorKey(DDCKEY_SRCBLT, &ddck); return TRUE; }
Сначала для создания поверхности используется функция RMWin::CreateSurface(). В качестве аргументов функции используются константы menuwidth и menuheight. Функция CreateSurface() возвращает указатель на новую поверхность.
Затем инициализируется структура menurect. Прямоугольник не описывает местоположение поверхности, а только ее размеры.
Затем поверхности назначается цветовой ключ. Цветовой ключ (color key) указывает, какие значения пикселей должны интерпретироваться как прозрачные. В нашем случае мы указываем, что прозрачными должны считаться любые пиксели поверхности меню видеорежимов с нулевым значением. Если вы взглянете на работающее приложение FullScreen, то заметите, что вся поверхность меню, за исключением текста, прозрачна. Это вызвано тем, что перед выводом текста всем пикселям поверхности присваивается нулевое значение.
Говоря о содержимом поверхности, следует упомянуть, что только что созданная поверхность содержит случайное содержимое. При создании поверхности выделяется память для ее размещения, но инициализация этой памяти не производится.
Задача функции UpdateMenuSurface() (код которой приведен в листинге 10.11) — инициализировать содержимое поверхности меню видеорежимов.
Листинг 10.11. Функция UpdateMenuSurface() |
BOOL FullScreenWin::UpdateMenuSurface() { char buf[80]; int len; RECT rect; ClearSurface(menusurf, 0); HDC hdc; menusurf->GetDC(&hdc); SelectObject(hdc, largefont); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, textshadow); ExtTextOut(hdc, 1, 1, 0, 0, headertext, strlen(headertext), 0); SetTextColor(hdc, textcolor); ExtTextOut(hdc, 0, 0, 0, 0, headertext, strlen(headertext), 0); SelectObject(hdc, smallfont); int nmodes = GetNumDisplayModes(); if (nmodes > maxmodes) nmodes = maxmodes; int rows = nmodes / menucols; if (nmodes % menucols) rows++; for (int i = 0; i < nmodes; i++) { rect.left = (i / rows) * colwidth; rect.top = (i % rows) * rowheight + reservedspace; rect.right = rect.left + colwidth; rect.bottom = rect.top + rowheight; DWORD w,h,d; GetDisplayModeDims(i, w, h, d); len = sprintf(buf, "%dx%dx%d", w, h, d); SetTextColor(hdc, textshadow); ExtTextOut(hdc, rect.left + 1, rect.top + 1, 0, &rect, buf, len, 0); if (i == selectmode) SetTextColor(hdc, highlightcolor); else SetTextColor(hdc, textcolor); ExtTextOut(hdc, rect.left, rect.top, 0, &rect, buf, len, 0); } rect.left = 0; rect.right = 319; rect.top = 179; rect.bottom = 199; len=sprintf(buf, "[Arrows] [Enter] [W] [F] [G] [Escape]"); SetTextColor(hdc, textshadow); ExtTextOut(hdc, 1, 180, 0, &rect, buf, len, 0); SetTextColor(hdc, textcolor); ExtTextOut(hdc, 0, 179, 0, &rect, buf, len, 0); menusurf->ReleaseDC(hdc); return TRUE; } |
Сперва функция UpdateMenuSurface() очищает всю поверхность, заполняя отведенную для нее память нулями. Это делается с помощью функции RMWin::ClearSurface(). Ноль, передаваемый во втором аргументе ClearSurface(), задает желаемое значение пикселей поверхности. Поскольку ноль задан в качестве цветового ключа данной поверхности, вся поверхность станет прозрачной.
Затем вызывается функция GetDC() интерфейса DirectDrawSurface, чтобы получить контекст устройства Windows для поверхности. Полученный в результате HDC (дескриптор контекста устройства) позволяет нам пользоваться функциями контекста устройства Windows. В нашем случае мы воспользуемся дескриптором контекста устройства для вывода текста на поверхность.
Большая часть кода функции UpdateMenuSurface() осуществляет вывод текста на поверхность menusurf. Сначала используется функция Win32 ExtTextOut() для отображения заголовка приложения. Затем создаются и выводятся строки для каждого обнаруженного видеорежима. И, наконец, в нижней части поверхности выводится список клавиш, на которые реагирует программа.
Обратите внимание, что текст выводится дважды, разным цветом и с небольшим сдвигом. Благодаря этому создается эффект тени.
Перед возвратом из функции UpdateMenuSurface() вызывается функция ReleaseDC() интерфейса DirectDrawSurface. Это очень важно, поскольку часть механизмов управления Windows отключается между вызовами функций GetDC() и ReleaseDC(). Если вы забудете освободить контекст устройства для поверхности DirectDraw, то это приведет к впечатляющему краху системы (поверьте мне — я знаю!).
Функция CreateFPSSurface() подходит к созданию поверхности немного по другому, чем функция CreateMenuSurface(). Функция CreateFPSSurface() сначала определяет размер поверхности, и только потом создает ее, как показано в листинге 10.12. Размеры поверхности зависят от размера выводимого текста.
Листинг 10.12. Функция CreateFPSSurface() |
BOOL FullScreenWin::CreateFPSSurface() { static const char dummystr[] = "FPS: 0000"; HDC hdc = ::GetDC(0); SelectObject(hdc, smallfont); SIZE size; GetTextExtentPoint(hdc, dummystr, strlen(dummystr), &size); ::ReleaseDC(0, hdc); fpsrect.left = 0; fpsrect.top = 0; fpsrect.right = size.cx; fpsrect.bottom = size.cy; fpssurf = CreateSurface(size.cx, size.cy); DDCOLORKEY ddck; ddck.dwColorSpaceLowValue = 0; ddck.dwColorSpaceHighValue = 0; fpssurf->SetColorKey(DDCKEY_SRCBLT, &ddck); return TRUE; } |
Для определения размера текста используется строка-макет, созданная с учетом худшего случая. Эта строка содержит больше символов, чем строки, которые будут действительно отображаться на экране. Поэтому размер текста будет достаточен для вывода любого значения частоты кадров (вплоть до 9 999 кадров в секунду).
Для вычисления размеров текста создается временный контекст устройства. Выбирается меньший из двух шрифтов приложения, и вызывается функция GetTextExtentPoint() для получения размера текста в пикселях. Затем размер текста используется при инициализации структуры fpsrect и создании поверхности fpssurf.
Затем поверхности назначается цветовой ключ. Как и в случае с поверхностью меню видеорежимов, прозрачными будут считаться пиксели с нулевым значением.
Функция UpdateFPSSurface() вызывается при каждом обновлении экрана. Ее код показан в листинге 10.13.
Листинг 10.13. Функция UpdateFPSSurface() |
BOOL FullScreenWin::UpdateFPSSurface() { static const long interval = 100; static long framecount; framecount++; if (framecount == interval) { static DWORD timenow; static DWORD timethen; timethen = timenow; timenow = timeGetTime(); double seconds = double(timenow - timethen) / (double)1000; int fps = (int)((double)framecount / seconds); static char buf[10]; int len = sprintf(buf, "FPS: %d", fps); HDC hdc; fpssurf->GetDC(&hdc); SelectObject(hdc, smallfont); SetTextColor(hdc, RGB(255,255,255)); SetBkColor(hdc, RGB(0,0,0)); SetBkMode(hdc, OPAQUE); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &fpsrect, buf, len, 0); fpssurf->ReleaseDC(hdc); displayfps = TRUE; framecount = 0; } return TRUE; } |
Функция использует статическую переменную (framecount) для подсчета количества обновлений экрана, или кадров. Когда счетчик кадров достигнет 100 (это значение выбрано произвольно), будет вычислено и выведено на экран значение FPS для последних 100 кадров.
Для определения времени, прошедшего с предыдущего вычисления FPS используется быстродействующая мультимедийная функция timeGetTime(). Затем создается строка, содержащая вычисленное значение FPS, которая выводится на поверхность fpssurf. Сразу после обновления поверхности счетчик framecount обнуляется.
Теперь пришло время посмотреть, как в нашем приложении осуществляется вывод на экран. Функция Render() вызывается из функции RMApp::OnIdle() и отвечает за обновление поверхности вторичного буфера и выполнение переключения страниц. Код функции Render() приведен в листинге 10.14.
Листинг 10.14. Функция Render() |
void FullScreenWin::Render() { if (primsurf->IsLost() == DDERR_SURFACELOST) { TRACE("Restoring primsurf...\n"); primsurf->Restore(); } if (menusurf->IsLost() == DDERR_SURFACELOST) { TRACE("Restoring menusurf...\n"); menusurf->Restore(); UpdateMenuSurface(); } if (fpssurf->IsLost() == DDERR_SURFACELOST) { TRACE("Restoring fpssurf...\n"); fpssurf->Restore(); } DDBLTFX bltfx; memset(&bltfx, 0, sizeof(bltfx)); bltfx.dwSize = sizeof(bltfx); bltfx.dwFillColor = 0; backsurf->Blt(0, 0, 0, DDBLT_COLORFILL | DDBLT_WAIT, &bltfx); scene->Move(D3DVALUE(1.0)); viewport->Clear(); viewport->Render(scene); device->Update(); UpdateFPSSurface(); if (displayfps) { DWORD w, h, d; GetCurDisplayModeDims(w, h, d); backsurf->BltFast(w - fpsrect.right, h - fpsrect.bottom, fpssurf, &fpsrect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT); } backsurf->BltFast(0, 0, menusurf, &menurect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT); primsurf->Flip(0, DDFLIP_WAIT); } |
В первой части функции производится проверка потери поверхностей. Потеря поверхностей может произойти, если память, используемая поверхностью, потребуется Windows для других целей. Теряется только память поверхности, а не сама поверхность. Обычно потеря поверхностей происходит, когда пользователь нажимает комбинацию клавиш ALT+TAB для переключения на другую программу.
Функция IsLost() интерфейса DirectDrawSurface возвращает TRUE если память поверхности была потеряна. Восстановить память поверхности очень просто — достаточно вызвать функцию Restore() интерфейса DirectDrawSurface. Это возвратит утраченную память поверхности, но не восстановит содержимое памяти. Обратите внимание, как восстанавливается поверхность меню видеорежимов:
if (menusurf->IsLost() == DDERR_SURFACELOST) { TRACE("Restoring menusurf...\n"); menusurf->Restore(); UpdateMenuSurface(); }
Если произошла потеря поверхности, вызывается функция Restore(). Она выполняет восстановление памяти поверхности, а вызов функции UpdateMenuSurface() необходим для восстановления содержимого поверхности.
Затем стирается содержимое поверхности backsurf:
DDBLTFX bltfx; memset(&bltfx, 0, sizeof(bltfx)); bltfx.dwSize = sizeof(bltfx); bltfx.dwFillColor = 0; backsurf->Blt(0, 0, 0, DDBLT_COLORFILL | DDBLT_WAIT, &bltfx);
Аналогичный код используется в функции RMWin::ClearSurface(). Функция Blt() применяется для заливки поверхности указанным цветом. Поверхность заполняется нулевыми байтами, но для данной поверхности не был назначен цветовой ключ, так что все пиксели поверхности остаются нулевыми.
Теперь можно нарисовать часть сцены, относящуюся к Direct3D:
scene->Move(D3DVALUE(1.0)); viewport->Clear(); viewport->Render(scene); device->Update();
Функция Move() интерфейса Direct3DRMFrame обновляет внутренние значения иерархии фреймов и выполняет вызов любых функций обратного вызова, выполняющих перемещение входящих в иерархию фреймов. Вызов функций интерфейса Direct3DRMViewport (Clear() и Render()) очищает содержимое порта просмотра (это не оказывает никакого действия на наши поверхности) и вычисляет новое выводимое изображение. Функция Update() интерфейса Direct3DRMDevice копирует сформированное изображение на поверхность backsurf. Это может ускользнуть от вашего взора, поскольку поверхность backsurf не упоминается в данном фрагменте кода. Однако, следует вспомнить, что поверхность backsurf была использована при создании устройства Direct3D. Эта связь объясняет, почему сформированное изображение передается функцией Update() непосредственно на поверхность backsurf.
Затем поверх сформированного Direct3D изображения выводится поверхность для вывода FPS:
UpdateFPSSurface(); if (displayfps) { DWORD w, h, d; GetCurDisplayModeDims(w, h, d); backsurf->BltFast(w - fpsrect.right, h - fpsrect.bottom, fpssurf, &fpsrect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT); }
Вызов функции UpdateFPSSurface() обновляет содержимое поверхности fpssurf. Затем, если значение логической переменной displayfps равно TRUE, содержимое поверхности копируется на поверхность backsurf функцией BltFast() (BltFast() — это оптимизированная версия функции Blt()).
Поверхность fpssurf размещается в нижнем правом углу экрана, поэтому при вычислении местоположения копируемой поверхности во вторичном буфере используются размеры экрана.
Потом на поверхность backsurf копируется поверхность меню видеорежимов:
backsurf->BltFast(0, 0, menusurf, &menurect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
Этот код очень простой, поскольку поверхность меню видеорежимов всегда видна и располагается в левом верхнем углу экрана.
В конце функции содержимое поверхности backsurf перемещается на первичную поверхность операцией переключения страниц:
primsurf->Flip(0, DDFLIP_WAIT);
Этот вызов функции делает видимым содержимое поверхности backsurf.
Перед тем, как мы закончим изучение приложения FullScreen нам надо обсудить еще одну функцию. Функция KeyDown() является обработчиком сообщений, вызываемым каждый раз при нажатии клавиши. Код функции KeyDown() приведен в листинге 10.15.
Листинг 10.15. Функция KeyDown() |
void FullScreenWin::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { static int screencapture; int newindex; int nmodes = GetNumDisplayModes(); if (nmodes > maxmodes) nmodes = maxmodes; int rows = nmodes / menucols; if (nmodes % menucols) rows++; switch (nChar) { case VK_ESCAPE: PostMessage(WM_CLOSE); break; case VK_UP: newindex = selectmode - 1; if (newindex >= 0) { selectmode = newindex; UpdateMenuSurface(); } break; case VK_DOWN: newindex = selectmode + 1; if (newindex < nmodes) { selectmode = newindex; UpdateMenuSurface(); } break; case VK_LEFT: newindex = selectmode - rows; if (newindex >= 0) { selectmode = newindex; UpdateMenuSurface(); } break; case VK_RIGHT: newindex = selectmode + rows; if (newindex < nmodes) { selectmode = newindex; UpdateMenuSurface(); } break; case VK_RETURN: if (menusurf) { menusurf->Release(); menusurf = 0; } if (fpssurf) { fpssurf->Release(); fpssurf = 0; } ActivateDisplayMode(selectmode); displayfps = FALSE; CreateMenuSurface(); UpdateMenuSurface(); CreateFPSSurface(); break; case 'W': OnRenderWireframe(); break; case 'F': OnRenderFlat(); break; case 'G': OnRenderGouraud(); break; } RMWin::OnKeyDown(nChar, nRepCnt, nFlags); } |
Если говорить по существу, функция KeyDown() использует клавиши управления курсором для выделения видеорежима в меню видеорежимов. Клавиша ENTER используется для активации выбранного видеорежима. Клавиши W, F и G применяются для изменения метода визуализации единственной сетки приложения (каркасный, плоский или Гуро). Нажатие на клавишу ESC завершает работу приложения.
netlib.narod.ru | < Назад | Оглавление | Далее > |