Далее объект отсечения присоединяется к первичной поверхности приложения функцией 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(). Обе функции ведут себя точно так же, как и в полноэкранном приложении.
Графический вывод
Как и в полноэкранном варианте, для обновления экрана класс DirectDrawWinвызывает функцию DrawScene(). Ее реализация для оконных приложений отличается от полноэкранного варианта по двум причинам. Во-первых, поскольку в оконном приложении не выполняется переключение страниц, содержимое вторичного буфера приходится копировать на первичную поверхность. Во-вторых, местонахождение выводимых данных на первичной поверхности должно определяться текущим положением и размерами окна. Помните — первичная поверхность в данном случае изображает весь экран, а не только клиентскую область окна. Оконный вариант DrawScene()выглядит так:
void BounceWin::DrawScene() {
ClearSurface(backsurf, 0);
CRect client=GetClientRect();
int width=client.Width();
int height=client.Height();
x+=xinc;
y+=yinc;
if (x<-160 || x>width-160) {
xinc=-xinc;
x+=xinc;
}
if (y<-100 || y>height-100) {
yinc=-yinc;
y+=yinc;
}
BltSurface(backsurf, surf1, x, y);
int offsetx=client.left;
int offsety=client.top;
RECT srect;
srect.left=0;
srect.top=0;
srect.right=client.Width();
srect.bottom=client.Height();
RECT drect;
drect.left=offsetx;
drect.top=offsety;
drect.right=offsetx+client.Width();
drect.bottom=offsety+client.Height();
primsurf->Blt(&drect, backsurf, &srect, DDBLT_WAIT, 0);
}
Функция DrawScene()выполняет две блит-операции. Первая копирует содержимое поверхности surf1на внеэкранную поверхность, которая используется в качестве вторичного буфера. Обратите внимание на применение функции BltSurface(), рассмотренной нами выше. Автоматическое отсечение, выполняемое BltSurface(), позволяет произвольно выбирать позицию на поверхности surf1.
Вторая блит-операция копирует содержимое вторичного буфера на первичную поверхность. На этот раз используется функция Blt(), поскольку к первичной поверхности присоединен объект отсечения. Структуры srectи drectтипа RECTопределяют области источника и приемника, участвующие в блиттинге. Заметьте, что при вычислении области приемника используются переменные offsetxи offsety, в которых хранятся координаты клиентской области окна. Если убрать эти смещения из структуры drect, программа всегда будет выводить изображение в левом верхнем углу экрана независимо от расположения окна.
Читать дальше