Память под Surface
От: dronA Россия  
Дата: 31.07.02 15:17
Оценка:
Приветствую всех,

Вопрос по использованию DirectX не полноэкранный режиме.
Есть два PrimySurface BackSurface. Изменеие размера окна должно сооветсвенно отражаться на памяти под эти Surfaces. Как правильно это делать? Какой способ предлагается DirectX ? Длина и ширина поверхности определяется формой окна. Что сообственно поставить в обработчике окна для первой и второй поверхности.

Дрон
Re: Память под Surface
От: IgorK Россия  
Дата: 02.08.02 08:52
Оценка: 18 (2)
Здравствуйте 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: вот меня прорвало :))
Re[2]: Память под Surface
От: dronA Россия  
Дата: 06.08.02 09:07
Оценка:
Здравствуйте IgorK,

Спасибо большое за исчерпывающий ответ на мой вопросу.
Техника работы в целом понятна. Использовать фиксированный размер backbuffer & clipping & blt.
Теперь ясно,
Спасибо
— Андрей
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.