Здравствуйте dronA, Вы писали:
A>Приветствую всех,
A>Вопрос по использованию DirectX не полноэкранный режиме.
A>Есть два PrimySurface BackSurface. Изменеие размера окна должно сооветсвенно отражаться на памяти под эти Surfaces. Как правильно это делать? Какой способ предлагается DirectX ? Длина и ширина поверхности определяется формой окна. Что сообственно поставить в обработчике окна для первой и второй поверхности.
Интересный вопрос. Интересен он тем, что все достаточно просто, но в MSDN описание использования DirectDraw для оконнных не полноэкранных приложений несколько размазанно :). Давай разберем все по полочкам. Итак:
Во-первых нужно выяснить чем же отличаются полноэкранные приложения от неполноэкранных. Полноэкранные приложения устанавливают эксклюзивный полноэкранный режим использования дисплея и могут менять видеорежимы. Оконным приложениям это не нужно. Поэтому при создании обьекта DirectDraw мы поступаем так:
LPDIRECTDRAW7 pDD;
DirectDrawCreateEx(NULL, (VOID**)&pDD, IID_IDirectDraw7, NULL);
// устанавливаем режим кооперации
m_pDD->SetCooperativeLevel( hWnd, DDSCL_NORMAL );
В результате мы получаем обьек DirectDraw в неэксклюзивном режиме (DDSCL_NORMAL).
Далее, для того чтобы можно было куда-нибудь рисовать, мы должны создать Primary Surface — основную поверхность, которую непосредственно будет наблюдать пользователь. Тут появляется еще одна особенность, связанная с архитектурой API — создать основную поверхность с размером, отличающимся от текущего разрешения экрана нельзя. Значит, мы должны создать Primary Surface размером текущего разрешения. Создание выглядит так:
LPDIRECTDRAWSURFACE7 pPriSurface; // наша основная повехность
DDSURFACEDESC2 ddsd; // структура, описывающая поверхность
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
pDD->CreateSurface(&ddsd, &pPriSurface, NULL);
Здесь видно, что при создании поверхности мы не указываем ее размеры.
Далее — хуже :). Так как поверхность на весь экран, то нам нужно нужно ограничить прорисовку до размеров и положения нашего окна. Делается это в два этапа:
1. Нам нужно создать так называемый клиппер (Clipper). Клипперы в DirectDraw достаточно широкопрофильная весчь. Вкратце — клипперы ограничивают вывод изображения на поверхность, создавая область на поверхности внутри которой данные могут изменяться, а за ее (областью) пределами — нет. Простой пример — у нас есть поверхность размером 200х200, мы хотим, чтобы в результате действий с этой поверхностью изображение изменялось только в области [(0, 0) : (99, 99)], для этого можно создать клиппер в виде прямоугольника 100х100 установленным в позицию (0, 0). Допутим, после этого мы копируем (Blt) на нашу поверхность в точку (50, 50) поверхность размером 100х100 — в результате будет отрисовано только в области [(0, 0) : (99, 99)]. Короче: Clipper — это обрезатель :).
Как нам может он понадобиться: у клиппера есть метод SetHWnd, одним из параметров которого является хендл окна. С помощью вызова этого метода мы привязываем клиппер к окну и, при перемещении или изменении размеров окна клиппер будет сам пересчимтывать свое положение и размеры. Применение клиппера в нашем случае не даст "замусоривать" часть экрана, где не находится наше окно. Клиппер нужно создать один раз и привязать его к нашей основной поверхности:
LPDIRECTDRAWCLIPPER pClipper;
// Создаем клиппер
pDD->CreateClipper(0, &pcClipper, NULL); // количество ссылок на Clipper становится = 1
pcClipper->SetHWnd( 0, hWnd ); // Присваиваем ему хендл нашего окна
// устанавливаем этот клиппер для основоной поверхности
pPriSurface->SetClipper(pClipper); // количество ссылок на Clipper становится = 2
// лично нам больше этот клиппер не нужен, а pPriSurface теперь имеет свою копию указателя
// на обьект клиппера, поэтому:
pcClipper->Release(); // количество ссылок на Clipper опять становится = 1
2. Второй этап заключается в том, чтобы отследить то место экрана куда нам в дальнейшем делать blit. Здесь все очень просто — так как BltFast нельзя делать на поверхность, которая имеет clipper, нам ничего не остается кроме просто Blt (если еще конечно BltBatch, но это просто пакетный Blt), а у Blt мы можем указать место и размер (RECT) куда конкретно на поверхность мы хотим скопировать изображение. Рассчитать эту зону вывода можно так:
RECT rc;
GetClientRect(hWnd, &rc);
ClientToScreen(hWnd, (POINT*)&rc);
ClientToScreen(hWnd, (POINT*)&rc+1);
При перемещении окна или изменении его размеров нужно все пересчитать заново.
Теперь про back buffer. Back buffer в нашем случае это внеэкранная поверхность (offscreen surface). Создается так:
LPDIRECTDRAWSURFACE7 pBackBuffer;
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = dwWidth; // размер в пикселах
ddsd.dwHeight = dwHeight;
m_pDD->CreateSurface( &ddsd, &pBackBuffer, NULL);
Для получения нормальной картинки размер копируемой части из back buffer`а должен совпадать с размером отображаемого изображения. Иначе будет наблюдаться так называемый stretch — растяжение/сжатие картинки.
Выводы по твоему вопросу:
1. Изменение размеров окна никак не влияет на количесво памяти для primary surface и back buffer. Ничего само по себе не перевыделяется.
2. При изменении размера окна возможен stretch если мы не увиличиваем размер копируемой области из back buffer. Иногда это выглядит прикольно :) Особенно, если карточка при растяжении делает билинейную фильтрацию.
3. Если размер окна изменяемый и необходимо при увеличении окна копировать большую часть бекбуффера, чтобы картинка не "тянулась" — надо создать сразу большой бэкбуффер :). Пересоздание бэкбуффера на каждое изменение размера окна — это слишком сильно :)
Надеюсь теперь все стало ясно и понятно :shuffle:
ps: вот меня прорвало :))