SetColorKey() интерфейса DirectDrawSurface). Наконец, переменная framecount (предназначенная для подсчета кадров за текущий интервал хронометража) обнуляется, а логической переменной displayfps присваивается значение FALSE, согласно которому поверхность FPS пока не должна отображаться на экране.
Хотя мы создали поверхность fpssurf, она осталась неинициализированной. В отличие от поверхности menusurf, инициализируемой функцией UpdateMenuSurface(), мы пока не можем инициализировать поверхность FPS, потому что у нас еще нет выводимого значения. Приложение только что было запущено (или только что перешло в другой видеорежим), так что вывод любого значения FPS был бы необоснованным.
К этому моменту инициализация программы Switch завершается. Наши поверхности (три вспомогательные, плюс первичная поверхность со вторичным буфером) были созданы и подготовлены к работе. Давайте посмотрим, как они отображаются на экране.
Графический вывод
Графическим выводом в программе 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);
}
Сначала функция DrawScene() подготавливает вторичный буфер, стирая его содержимое функцией ClearSurface(). Мы заполняем вторичный буфер нулями, но, поскольку он не имеет цветового ключа, 0 в данном случае имеет иной смысл, чем для поверхностей menusurf и fpssurf. Для вторичных буферов 0 означает черный цвет (в большинстве случаев).
Черный цвет не гарантирован
По умолчанию DirectDraw резервирует два элемента палитры: для черного (индекс 0) и для белого (индекс 255). Поэтому обычно заполнение поверхности нулями равносильно ее заливке черным цветом. Тем не менее в палитрах, созданных с флагом DDSCAPS_ALLOW256, можно задавать все 256 элементов.
Функция DirectDrawWin::CreateSurface() при создании и установке палитры (когда необязательный аргумент use_palette равен TRUE) использует флаг DDSCAPS_ALLOW256, поэтому первый элемент в палитрах наших приложений может быть отличен от черного цвета. Флаг можно удалить, но это нарушит цветопередачу при отображении BMP-файлов, у которых первый и последний элементы палитры отличны от черного и белого цветов соответственно.
В программах этой книги используются BMP-файлы, для которых положение черного и белого цвета в палитре совпадает с принятым в DirectDraw по умолчанию. В этом случае растры будут правильно отображаться независимо от флага DDSCAPS_ALLOW256.
Другая причина, по которой первому элементу палитры следует назначать черный цвет, в том, что первый элемент палитры совпадает с цветом, используемым на экране за пределами нормальной области рисования (overscan color — цвет внешней рамки). Хотя не существует никаких формальных причин, по которым этим цветом должен быть именно черный, другие варианты обычно выглядят довольно странно.
После завершения очистки поверхность bmpsurf (анимационная поверхность) копируется на вторичный буфер функцией DirectDrawWin::BltSurface() (мы используем функцию BltSurface() из-за наличия в ней встроенной поддержки отсечения, что позволяет перемещаемому растру частично выходить за пределы экрана). После выполнения блиттинга рассчитывается новое положение растра, при этом размеры текущего видеорежима используются для ограничения перемещений.
Затем копируется поверхность меню. Она всегда выводится в левом верхнем углу экрана, а ее размеры совпадают с размерами видеорежима с наименьшим разрешением (320×200), так что отсечение не понадобится. Следовательно, мы можем воспользоваться функцией 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);