RussianDelphiProgrammingAlliance

Главная | О проекте | Новости | Статьи | Проекты | Исходники | Файлы | ЧаВо | Ссылки

Это (очень :) краткое описание DirectDraw - by Alex Key

Это демоверсия моей демки. Она представляет собой звезды летящие на вас, режим 640х480х256.

Одна маленькая особенность: чтоб выйти из нее, нужно нажать Alt-F4 :-)

Описание работы с DirectDraw. В качестве тестирующей версии использовался DirectX5.

Для чего нужен DirectDraw надеюсь понятно, так как этот текст предназначен для ПРОГРАММИСТОВ!!!

Для работы с DirectDraw вам нужен модуль DDraw.pas и Delphi 2.0 and higher. Нужен также DDCanvas.dcu.

Итак, Неоходимо подключить модуль Ddraw,Ole2 (ole2 необходим потому, что все объекты DirectX доступны через OLE контейнер)
В разделе PUBLIC описать переменную типа IDirectDraw, например:
...
DD:IDirectDraw;
...
этим вы опишите интерфейс DirectDraw, но он не позволяет непосредственно работать с видеопамятью, зато он позволяет создать объект IDDSurface, который и позволяет непосредственно работать с экранной поверхностью.
Например так:
...
DDS:IDDSurface;
....
Еще нужно описать поинтер, который будет указывать на начало видеопамяти, например так:
...
P_v:pointer;
...
Далее работа будет протекать в три этапа -
  • 1-создание объектов и инициализация
  • 2-выполнение основной задачи
  • 3-удаление объектов и завершение работы
создание DirectDraw лучше производить в при создании формы например так:
procedure TForm1.FormCreate(Sender: TObject);
begin
 if DirectDrawCreate(nil, DD, Nil)<>DD_OK then
  beep; //какие-то действия по обработке
end;
затем в обработчике OnShow создать остальное и произвести инициализацию. Для приличной графики, нам необходим весь экран, да еще и в удобном нам видео режиме. DirectDraw позволяет и то и другое. Первая команда в следующем примере это настройка системы таким образом, что-бы весь экран принадлежал мне. А затем устанавливаю видео режим. Далее нам необходимо получить доступ к поверхности, но сначала создать ее. ddscaps_primarysurface означает, что поверхность будет расположена на экране. Чтобы получить адрес начала видеопамяти, отведенной этой поверхности, я сначала блокирую, а затем снова разблокирую ее. И наконец создаю новую цепочку...
Для чего она нужна? Логично, вы можете и не делать этого, все зависит от того что вам нужно, но в программе Летящие Звезды я использую цепочку, с основным циклом программы, и выставляю ее приоритет в наивысший, с помощью команды API Win32.
procedure TForm1.FormShow(Sender: TObject);
var y:integer;
    ddscaps:TDDscaps;
    dr:Prect;
    a:pointer;
    ddsd:TDDSD;
begin
 if dd.SetCooperativeLevel(handle,
  ddscl_exclusive or ddscl_fullscreen)<>dd_ok then close;

 if dd.SetDisplayMode(640,480,8)<>dd_ok then close;
{setdisplaymode(x,y,c) - где х и y и c - параметры экрана. }

 fillchar(ddsd,sizeof(ddsd),0);
 with ddsd do begin
  dwSize:=sizeof(ddsd);
  dwFlags:=ddsd_caps;
  ddscaps.dwCaps:=ddscaps_primarysurface end;
 if dd.createsurface(ddsd,dds,nil)<>dd_ok then close;
 dr:=nil;
 dds.Lock(dr,@ddsd,DDLOCK_SURFACEMEMORYPTR,y);
 P_v:=ddsd.lpSurface;
 dds.Unlock(dr);
 ht:=createthread(nil,0,@StarThread,nil,0,TID);
end;

//Ну и теперь удаление объектов:

procedure TForm1.FormDestroy(Sender: TObject);
begin
 if assigned(dds) then p_s.release;
 if assigned(DD) then DD.release;
end;
Что вы теперь можете? ну например вы можете получить DC на поверхность...
DDS.getdc(dc);
предварительно описав
dc:hdc;
и с помощью API функций рисовать примитивы... это достаточно легко сделать, в помощи Delphi это есть...

еще вы имеете DD для работы с DirectDraw.
например синхронизация с лучем
DD.WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN ,0);
и т.д.

поверхность DDS для работы с поверхностью, например копирование, создание цветового ключа, создание палитры...
также вы имеете указатель на видео память
P_v
для прямого доступа к видеопамяти...
asm mov esi,p_v
    mov ecx,640*480
    mov eax,0
@l: mov [esi],al
    inc eax
    inc esi
    loop @L
end;
если вы знакомы с assembler то вы найдете применение этой возможности...
но нужно быть осторожным - P_v внутри процедуры не всегда содержит корректную информацию при обращении с помощью asm, я рекомендую завести локальный поинтер, и присвоить его глобальному, и в ассемблерной вставке использовать уже локальный указатель на видеопамять.

создание палитры производится таким образом:
создается массив элементов палитры TPaletteEntry, например
ttt:array[0..255] of TPaletteEntry;
создается объект палитра:
...
DDP:IdirectDrawPalette;
...
потом у этого массива устанавливаются необходимые значения, и
dd.CreatePalette(DDPCAPS_8BIT,@ttt[0],ddp,nil);
dds.SetPalette(ddp);

В прошлый раз я говорил о том, что можно пользоватся Canvas, и даже показал как.
Да, можно. Но я не советую... Когда я пользовался Canvas, я перезагружался каждые 5 минут, когда я от него отказался - каждые 15 :-)

Также, если вы хотите использовать DX в реал-тайме, не используйте ТТimer, это будет ужасно медленно. Создайте лучше цепочку, и все, что вы хотели сделать в обработчике таймера, сделайте в этой цепочке. Это сделать легко, но есть одна проблема - Canvas в цепочке не работает, и многие другие компоненты тоже...
Придется пользоватся DC.

Как создать цепочку?
начать нужно с того, что вы заводите процедуру в которую прыгнет цепочка при создании:
procedure StartThread(p:Pointer);
begin
 repeat
// какие-то действия
 until stopwork;
 threadstopped:=true;
end;
процедура обязательно должна иметь входной параметр pointer. Все остальное нет.
Но я делаю так: сам я получать сообщения Windows не умею, и использую стандартные события формы для получения информации о действиях пользователя. Так вот, самой необходимой информацией является информация о том что форма закрывается. так вот, в момент закрытия, в обработчике OnDestroy, я присваиваю stopwork:=true; и делаю цикл:
repeat until threadstopped;

естественно, что при создании я устанавливаю обе булевские переменные в соответствующие значения. теперь о создании цепочки:
ht:=createthread(nil,0,@StartThread,nil,0,TID);
где ht и TID простой integer.
Все. В момент выполнения этой команды ваша процедура запускается на исполнение параллельно с цепочкой формы.

Но вот как теперь быть без Canvas? проблема...
придется получить DC
dds.getdc(dc);
и с помощью него рисовать примитивы.

Нужно ли отказыватся от формы?
решать вам, но мой совет такой - если вы хотите сделать что-то более менее серьезное, не тряситесь над каждым байтом...

Сейчас я начну потихоньку рассказ о том как отказатся от формы. Вообще форма в Delphi жрет много памяти, и отказавшись от нее, вы наверняка уменьшите объем программы раз в 10. Не хило :-).
Но что-бы отказатся от формы, нужно создавать окно самому, заводить цепочку под основной цикл программы, и получать сообщения от Windows, типа нажатых клавиш, и передвинутой мышки...

Чтоб сделать это, вам нужно компилировать исходники самому, с помощью Dcc32.exe из подкаталога bin. Я использую FAR менеджер для этих целей, но можно любую DOS оболочку, запущенную под Windows. Ключей указывать не надо. Единственное условие - все файлы лучше держать в одном каталоге. Может и можно по другому, но я не знаю.

В поле uses подключайте следующие модули:
uses windows,messages,ddraw,ole2;
это минимум, для большего, нужно больше модулей :-) (я имею в виду больше того, что предоставляют эти модули.)

Дальше, нужно описать функцию примерно такого вида:
function winreg:boolean;
var wc:TWndClass;
begin
 wc.style:=cs_hredraw or cs_vredraw;
 wc.lpfnwndproc:=@windowproc;//имя процедуры
// в которую проинициализируется цепочка окна.
 wc.cbclsextra:=0;
 wc.cbwndextra:=0;
 wc.hinstance:=HInstance;
 wc.hicon:=loadicon(100,0);
 wc.hcursor:=loadcursor(0,IDC_NO);
 wc.hbrbackground:=hbrush(nil);
 wc.lpszmenuname:=nil;
 wc.lpszclassname:=a_name;
 result:=registerclass(wc)<>0;
end;
поэксперементируйте с параметрами, для вашего удобства...

Опишите глобальные переменные
...
am:TMsg;
hw:hwnd;
...
и еще одну функцию:
function wincreate:hwnd;
var hW:hwnd;
begin
 hw:=createwindow(a_name,'Ваше имя',ws_popup or WS_EX_TOPMOST,
  cw_usedefault,cw_usedefault,cw_usedefault,cw_usedefault,
  0,0,hinstance,nil);
 if hw<>0 then begin
  showwindow(hw,cmdshow);
  updatewindow(hw) end;
 result:=hw;
end;
третий параметр функции createwindow, характеризует стиль окна, в данном случае окно будет без рамки, и постоянно на поверхности.

Третья обязательная функция, которую вы должны описать это -
function windowproc(w:HWnd;AM,WP,LP:Longint):longint;stdcall;export;
begin
 windowproc:=0;
 case am of
  wm_destroy:begin postquitmessage(0);exit end end;
 windowproc:=DefWindowProc(w,am,wp,lp);
end;
она получает и обрабатывает только одно сообщение, о разрушении окна.

Вот, теперь, в теле программы можно сделать так:
if not winreg then begin
 messagebox(0,'Не могу зарегестрировать окно.',nil,mb_ok);
 exit end;
hw:=wincreate;
if hw=0 then begin
 messagebox(0,'Не могу создать окно.',nil,mb_ok);
 exit end;
Тут вроде не сложно, регистрируем и создаем.

и под конец транслируем сообщения ядра, главной процедуре.
while getmessage(am,0,0,0) do begin
 translatemessage(am);
 dispatchmessage(am) end;
halt(am.wparam);
после этого можно ставить End.

Это первая часть моего личного опыта работы с ДД. В следующей я расскажу как использовать GDI Сейчас я все это уже умею, но не тиснул клаву :-)...
Учучь работать с D3D, DirectSound, DirectPlay...
Как научусь, расскажу...

P.S. Много информации по DX есть в SDK, в основном в хелп-файле...

Исходный документ находится на Alex Key Homepage

Перечисленные программы и модули можно скачать оттуда!

Последнее обновление - 20 декабря 1998

Чужая реклама: