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




Графический вывод



Графическим выводом в программе Switch занимается функция SwitchWin::DrawScene(). Она отвечает за подготовку кадра во вторичном буфере и переключение страниц, благодаря которому новый кадр отображается на экране. Код функции DrawScene() содержится в листинге 4.4.
Листинг 4.4. Функция SwitchWin::DrawScene()

void SwitchWin::DrawScene() { ClearSurface( backsurf, 0 ); BltSurface( backsurf, bmpsurf, x, y ); x+=xinc; y+=yinc; const CRect& displayrect=GetDisplayRect(); if (x<-160 || x>displayrect.right-160) { xinc=-xinc; x+=xinc; } if (y<-100 || y>displayrect.bottom-100) { yinc=-yinc; y+=yinc; } backsurf->BltFast( 0, 0, menusurf, 0, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT ); UpdateFPSSurface(); if (displayfps) { int x=displayrect.right-fpsrect.right-1; int y=displayrect.bottom-fpsrect.bottom-1; backsurf->BltFast( x, y, fpssurf, 0, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT ); } primsurf->Flip( 0, DDFLIP_WAIT ); }

Черный цвет не гарантирован
По умолчанию DirectDraw резервирует два элемента палитры: для черного (индекс 0) и для белого (индекс 255). Поэтому обычно заполнение поверхности нулями равносильно ее заливке черным цветом. Тем не менее в палитрах, созданных с флагом DDSCAPS_ALLOW256, можно задавать все 256 элементов.
Функция DirectDrawWin::CreateSurface() при создании и установке палитры (когда необязательный аргумент use_palette равен TRUE) использует флаг DDSCAPS_ALLOW256, поэтому первый элемент в палитрах наших приложений может быть отличен от черного цвета. Флаг можно удалить, но это нарушит цветопередачу при отображении BMP-файлов, у которых первый и последний элементы палитры отличны от черного и белого цветов соответственно.
В программах этой книги используются BMP-файлы, для которых положение черного и белого цвета в палитре совпадает с принятым в DirectDraw по умолчанию. В этом случае растры будут правильно отображаться независимо от флага DDSCAPS_ALLOW256.
Другая причина, по которой первому элементу палитры следует назначать черный цвет, в том, что первый элемент палитры совпадает с цветом, используемым на экране за пределами нормальной области рисования (overscan color — цвет внешней рамки). Хотя не существует никаких формальных причин, по которым этим цветом должен быть именно черный, другие варианты обычно выглядят довольно странно.

После завершения очистки поверхность bmpsurf (анимационная поверхность) копируется на вторичный буфер функцией DirectDrawWin::BltSurface() (мы используем функцию BltSurface() из-за наличия в ней встроенной поддержки отсечения, что позволяет перемещаемому растру частично выходить за пределы экрана). После выполнения блиттинга рассчитывается новое положение растра, при этом размеры текущего видеорежима используются для ограничения перемещений.
Затем копируется поверхность меню. Она всегда выводится в левом верхнем углу экрана, а ее размеры совпадают с размерами видеорежима с наименьшим разрешением (320x200), так что отсечение не понадобится. Следовательно, мы можем воспользоваться функцией BltFast() интерфейса DirectDrawSurface. Первые два аргумента BltFast() определяют область приемника для наложения копии (оба аргумента равны нулю, что означает левый верхний угол). Третий аргумент является указателем на поверхность-источник, а четвертый описывает копируемую прямоугольную область источника. Вместо прямоугольника мы передаем 0, тем самым показывая, что копироваться должна вся поверхность.
В последний аргумент функции BltFast() включены флаги DDBLTFAST_SRCCOLORKEY и DDBLTFAST_WAIT. Первый флаг активизирует цветовой ключ поверхности-источника. Если бы он не был указан, то во время блиттинга цветовой ключ поверхности menusurf был бы проигнорирован, а пиксели с нулевыми значениями выводились бы черным цветом. Второй флаг показывает, что выход из функции BltFast() должен произойти лишь после завершения копирования.
Вернемся к функции DrawScene(). Наша следующая задача - обновление и отображение поверхности FPS (fpssurf). Однако, как было сказано выше, поверхность fpssurf обновляется лишь после того, как закончится очередной интервал хронометража.
Вычисление FPS и подготовка поверхности осуществляются функцией UpdateFPSSurface(), вызываемой функцией DrawScene() при каждом обновлении экрана. Функция UpdateFPSSurface() выглядит так:

BOOL SwitchWin::UpdateFPSSurface() { static const long interval=100; 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, "%d FPS", fps); ClearSurface( fpssurf, 0 ); HDC hdc; fpssurf->GetDC( &hdc ); SelectObject( hdc, smallfont ); SetBkMode( hdc, TRANSPARENT ); SetBkColor( hdc, RGB(0,0,0) ); SetTextColor( hdc, textshadow ); TextOut(hdc, 1, 1, buf, len ); SetTextColor( hdc, brighttextcolor ); TextOut(hdc, 0, 0, buf, len ); fpssurf->ReleaseDC( hdc ); displayfps=TRUE; framecount=0; } return TRUE; }

Функция UpdateFPSSurface() использует переменную framecount для подсчета выведенных кадров. Переменная framecount обнуляется в двух случаях: при изменении видеорежима и при обновлении поверхности fpssurf заново вычисленным значением FPS.
Каждый раз, когда заданное количество кадров будет подготовлено и выведено на экран, функция timeGetTime() подсчитывает количество прошедших миллисекунд. По этой величине определяется текущий FPS приложения.
Значение FPS преобразуется в строку и выводится на поверхность FPS (после предварительной очистки поверхности функцией ClearSurface()). После вывода текста переменная framecount обнуляется, и начинается новый интервал хронометража. Наконец, переменной displayfps присваивается значение TRUE; оно говорит о том, что на поверхности FPS находится допустимое значение, которое следует вывести на экран.
Возвращаясь к функции DrawScene() (
см. листинг 4.4), мы видим, что код отображения fpssurf и переключения страниц выглядит так:

if (displayfps) { int x=displayrect.right-fpsrect.right-1; int y=displayrect.bottom-fpsrect.bottom-1; backsurf->BltFast( x, y, fpssurf, 0, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT ); } primsurf->Flip( 0, DDFLIP_WAIT );

Если флаг displayfps равен TRUE, поверхность FPS следует вывести на экран. Однако сначала мы рассчитываем ее положение по известным размерам видеорежима и поверхности. Затем мы копируем поверхность fpssurf функцией BltFast(), после чего выводим на экран вторичный буфер функцией Flip() интерфейса DirectDrawSurface.
Задача функции DrawScene() выполнена - все три поверхности программы Switch выведены на экран. Тем не менее изучение приложения еще не закончено. Мы должны рассмотреть обработку пользовательского ввода.
Но перед тем как продолжить, я должен сделать одно замечание относительно программы Switch. Как мы видели во время рассмотрения ее кода, две из трех поверхностей программы имеют цветовые ключи для отображения прозрачных пикселей. Однако при запуске программы кажется, что анимационная поверхность (не имеющая цветового ключа) тоже является прозрачной. Почему? Потому что цвет фона растра (черный) совпадает с цветом вторичного буфера. Если изменить значение для заливки вторичного буфера, станет ясно, что анимационная поверхность на самом деле непрозрачна.


Содержание Назад Вперед