Получение средствами DirectDraw прямого доступа к видеопамяти
Страница 2.


 

Удаление объектов DirectDraw

Перед уничтожением главного окна приложения нужно удалить созданные объекты DirectDraw, чтобы освободить занимаемые ими системные ресурсы. Обычно все делается в обработчике события WM_DESTROY. Главное, о чем всегда следует помнить при работе с COM-объектами, - это то, что недопустимо применять метод delete() непосредственно. Дело в том, что удаляемый объект может использоваться другим приложением, ведь мы работаем в многозадачной среде. С подобной проблемой легко справляется метод Release(), который просто уменьшает счетчик ссылок на объект (reference count) и вызывает метод delete() лишь тогда, когда значение этого счетчика равно нулю. Это свидетельствует о том, что удаляемый объект не используется ни одним приложением.

В данном примере мы создали два COM-объекта, причем указатели на них содержатся в переменных lpDDraw и lpPrimarySurface. Значит, при обработке системного события WM_DESTROY нам требуется применить всего два метода:

lpPrimarySurface->Release();
lpDDraw->Release();

Простейшая программа, демонстрирующая прямой доступ к видеопамяти средствами DirectDraw, приведена в листинге.

Листинг

#include <windows.h>
#include <ddraw.h>

const PHYSICAL_WIDTH = 800;
const PHYSICAL_HEIGHT = 600;

LPDIRECTDRAW lpDDraw;
LPDIRECTDRAWSURFACE lpPrimarySurface;

LRESULT CALLBACK DDrawWndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam);
BOOL DDrawInit(HWND hWnd);
void DDrawDone();
void DrawScreen();

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{

WNDCLASS wndClass;
HWND hWnd;
MSG msg;

ZeroMemory(&wndClass, sizeof(wndClass));
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = DDrawWndProc;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.lpszClassName = <DDrawApp>;

RegisterClass(&wndClass);

hWnd = CreateWindowEx(
WS_EX_TOPMOST,
wndClass.lpszClassName,
<DirectDraw Application>,
WS_POPUP | WS_MAXIMIZE,
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

LRESULT CALLBACK DDrawWndProc(HWND hWnd,
UINT message, WPARAM wParam,
LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
DDrawInit(hWnd);
SetTimer(hWnd, 1, 50, 0);
return 0;

case WM_TIMER:
DrawScreen();
return 0;

case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;

case WM_DESTROY:
KillTimer(hWnd, 1);
DDrawDone();
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}

BOOL DDrawInit(HWND hWnd)
{
DDSURFACEDESC ddsd;
DDSCAPS ddsc;

if (DirectDrawCreate(NULL, &lpDDraw, NULL) != DD_OK)
return FALSE;

if (lpDDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE |
DDSCL_FULLSCREEN) != DD_OK)
return FALSE;

if (lpDDraw->SetDisplayMode(PHYSICAL_WIDTH,
PHYSICAL_HEIGHT,
8) != DD_OK)
return FALSE;

ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
ddsd.dwFlags = DDSD_CAPS;
if (lpDDraw->CreateSurface(&ddsd, &lpPrimarySurface,
NULL) != DD_OK)
return FALSE;

return TRUE;
}

void DDrawDone()
{
if (lpPrimarySurface != NULL)
lpPrimarySurface->Release();

if (lpDDraw != NULL)
lpDDraw->Release();
}

void DrawScreen()
{
DDSURFACEDESC ddsd;
static int pos;

ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);

if (lpPrimarySurface->Lock(NULL, &ddsd,
DDLOCK_WAIT, NULL) == DD_OK)
{
char* buffer = (char*)ddsd.lpSurface;
for (int i = 0; i < PHYSICAL_HEIGHT / 2; i++)
{
memset(buffer + (i * ddsd.lPitch), i + pos,
PHYSICAL_WIDTH);
memset(buffer + ((PHYSICAL_HEIGHT / 2 + i)
* ddsd.lPitch), i - pos,
PHYSICAL_WIDTH);
}

pos++;

lpPrimarySurface->Unlock(ddsd.lpSurface);
}
}

Cтандарт VESA/VBE отображает видеопамять в виде отдельных окон (банков) размерами не более 64 Кбайт, причем только одно из них может быть активным в заданный момент времени. Переключение таких окон значительно усложняет алгоритмы обработки графики, а также замедляет скорость работы всего приложения. Стандарт VESA/VBE, начиная с версии 2.0, позволяет добиться линейного доступа к видеопамяти в режиме LFB (Linear Flat-frame Buffer) аналогично тому, как это делает DirectDraw. К сожалению, приложения MS-DOS, использующие режим LFB, не могут работать под управлением ОС семейства Windows NT (Windows NT/2000/XP). Это значительно ограничивает область применения LFB и практически сводит на нет все его преимущества перед обычными "оконными" режимами VESA/VBE.

В отличие от упомянутых режимов компонент DirectDraw представляет видеопамять в виде непрерывного линейного массива (вектора), напрямую определяющего цвета отображаемых пикселов. Таким образом, программирование графики в режиме прямого доступа к видеопамяти в Win32-приложениях гораздо более простая задача, чем использование средств VESA/VBE в среде MS-DOS.

 
« Предыдущая статья   Следующая статья »