int21h

Grafika pod Free Pascalem

Máme tu nový díl seriálu, který navazuje na tradici seriálu o grafice v TP7, který navazuje na tradici mých keců o programování. Tentokrát nás čeká málo samostatného programování, které bude zahrnovat využití Linear Frame Bufferu, ale spíše využívání cizích jednotek, mezi které zahrneme Graph (z FP, ne tu ubohou z TP7), WinGraph a velmi okrajově také SD, Direct Draw (z Direct X 7) a spol.

Než ale začneme, rád bych doplnil některý z předchozích dílů, kde jsme se naučili provádět rotace ve 2D. Pokud budete někdy dělat 3D hru, tak budete potřebovat rotovat ve 3D. A nejen to, u objektů, které budou popsány pomocí 3D, budete muset provádět různé transformace při jejich otáčení, zrcadlení nebo přesouvání (až už po mapě (na což vám ale stačí pouhé INC a DEC funkce), nebo před kameru). Většinu z nich najdete v časopisu Výheň v článcích Vektorová grafika a 3D engine. Pro rotace uvedu vzoreček zde:

Chcete-li otočit pixel se souřadnicemi XYZ, musíte provést toto:

	Y1 = Y*cos(a)+Z*sin(a)
	Z1 = -Y*sin(a)+Z*cos(a)
	X1 = X*cos(b)+Z1*sin(b)


	X' = X1*cos(c)+Y1*sin(c)
	Y' = -X1*sin(c)+Y1*cos(c)
	Z' = -X*sin(b)+Z1*cos(b)

Kde nové souřadnice budou X'Y'Z'. ABC jsou úhly, o které chcete rotovat v osách XYZ (tedy se nejedná jen o 1 úhel, jako v případě 2D). Pokud si pamatujete na náš přepočet z 3D na 2D, tak se vlastně jedná o výpočet perspektivy. Ta vypadá pro XYZ na X'Y' takto:


	X' = (X SHL ShiftFactor) div (Z+ZDelta)
	Y' = (Y SHL ShiftFactor) div (Z+ZDelta)

Kde ShiftFactor je míra zkreslení a ZDelta je relativní prodloužení na ose Z. To jsou základní výpočty, které budete ve 3D potřebovat. Ale ono jich časem přibyde víc. Toto by Vám ale mělo pomoci hlavně do začátku. Pro zajímavost ještě přikládám tabulku grafických režimů pod nativním Linuxem (abyste viděli, jak je moc "nebezpečné" se držet statických čísel, která Vám např. poskytne ATHelp):

Colours   640x400 640x480 800x600 1024x768 1152x864 1280x1024 1600x1200
--------+--------------------------------------------------------------
 4 bits |    ?       ?     0x302      ?        ?        ?         ?
 8 bits |  0x300   0x301   0x303    0x305    0x161    0x307     0x31C
15 bits |    ?     0x310   0x313    0x316    0x162    0x319     0x31D
16 bits |    ?     0x311   0x314    0x317    0x163    0x31A     0x31E
24 bits |    ?     0x312   0x315    0x318      ?      0x31B     0x31F
32 bits |    ?       ?       ?        ?      0x164      ?

Teď se na chvíli odmlčíme od Turbo Pascalu 7. Vrátíme se k němu na chvíli jen u zvukových karet a sítě IPX (nebo ještě sériového kabelu). Pravda, dá se v něm učit, a je i celkem snadný, ale nedají se v něm dělat profesionální aplikace (dají, pokud oželíte rychlost). Stejně tak v něm můžete udělat i Dooma, pokud Vám nebude vadit, že si ho na 486 a nižších moc nezahrajete. Chce to už něco lepšího a když už umíme Pascal, proč se mořit s učením nějakého Visual Basicu, Delphi (ač je Pascalu velmi podobné) nebo C (C++, Java (už existuje i kompilátor, který Vaše programy z Pascalu zkompiluje do Javy pro mobilní telefony!), atd.), když tu máme Free Pascal. Já jen podotknu, že jelikož jsem začal v jedné firmě zavádět Linux přes server, a programovat databázový síťový software, asi už nebudu mít čas (a náladu) psát články každý týden (někomu tím udělám možná radost). Jde o to, že člověk musí mít náladu po 10 hodinách práce s PC v práci ještě něco psát o víkendu. Ale tím Vás nechci zatěžovat (navíc se během práce mohu něco naučit a určitě o tom později napíšu).

Začneme metodou zobrazování, která se nazývá linear frame buffer. Podotýkám pár věcí: pod Windows XP to nemusí fungovat, pokud jedete v DOSovém programu. A také nejprve trochu teorie: LFB podporují hlavně novější karty (tím myslím cca. 10 let a více), hlavně na to musí být VESA verze 2.0 a vyšší, pokud se nepletu. Nejprve se tedy přepnete do režimu, který bude využívat LFB. To provedete tak, že k číslu režimu přičtete do registru BX hodnotu $4000:


function VESA_LFB(rezim : word) : word; assembler;
asm  
	mov	eax,$4f02
	mov	ebx,rezim
	or	ebx,$4000
	int	10h  
end;  

Podle výstupu funkce pak poznáte, zda daný režim jde nastavit, nebo zda je vůbec podporovaná funkce LFB (a pokud ne, ale daný režim ano, budete ho muset nastavit ještě jednou, ale viz. minule). Adresa VRAM nyní neleží na $a000 (i když by ještě stále mohla ležet), ale někde nad 1. MB paměti. Pokud si necháte zjistit informace o tomto režimu (viz. minule), tak v LFB najdete fyzickou adresu VRAM pro tento režim (nezapomeňte, že tento blok informací MUSÍ ležet v konvenční paměti, takže nejprve musíte alokovat přes "global_dos_alloc" funkci dolní paměť, tam zjistit informace a pak tuto paměť uvolnit (není jí zase tolik, aby se s ní mohlo plýtvat). Budeme také potřebovat velikost této paměti, a tu najdeme v položce PAMET v informacích o celé kartě. Tyto dva údaje si uložte někam stranou (nazvu je např. _LFB a _PAMET) do normálních VAR proměnných.

Tuto adresu však nemůžete přímo použít. Musíte nejprve namapovat fyzickou adresu do lineární pomocí DPMI, najít základní adresu DS selektoru, který používáte pro data, a odečíst tuto bázovou adresu od lineární, abyste dostali blízký offset VRAM, kam můžete psát pixely:


function ZjistiAdrLFB(Fyzicka : longint) : longint;
var	Linear : longint;
begin
 Linear := get_linear_addr(_LFB,_PAMET);
 Set_Segment_Limit(get_ds,$FFFFFFFF);
 lock_linear_region(_LFB,_PAMET);
 Linear:=Linear-get_segment_base_address(get_ds);
 ZjistiAdrLFB := Linear;
end;  

Nyní už víte, kam můžete zapisovat svoje data (_LFBlin). Velikost X,Y,BPP a počet bytů na řádku (_BYTESLINE) máte uloženo opět v informacích o módu. Nyní je adresování už velmi jednoduché. Celá VRAM je totiž přístupná bez nějakého přepínání banků, takže adresa Vašeho pixelu je (X+Y*MaxX)*BPP. Zjednodušeně (bez optimalizací, které jsou závislé právě na tom MaxX) se to dá napsat takto (příklady pro 8, 15/16 a 24/32 bitů):


procedure PutPixel8(x,y : longint; barva : byte); assembler;
asm  
	mov	eax,y
	mul	_bytesline
	add	eax,x
	mov	bl,barva
	add	eax,_lfblin
	mov	[eax],bl  
end;


procedure PutPixel16(x,y : longint; barva : word); assembler;
asm  
	mov	eax,y
	mul	_bytesline
	add	eax,x
	mov	bx,barva
	add	eax,_lfblin
	mov	[eax],bx  
end;


procedure PutPixel32(x,y : longint; barva : longint); assembler;
asm  
	mov	eax,y
	mul	_bytesline
	add	eax,x
	mov	ebx,barva
	add	eax,_lfblin
	mov	[eax],ebx  
end;  

Všechno ostatní zůstává stejné. Jen nezapomeňte při přepínání režimu (ukončování VESA) provést ještě toto (INLINE používám proto, aby FP tuto proceduru vložil na místo jejího volání jako makro a ne jako volání procedury, čímž je urychlí vykonání jejího kódu (za cenu, že kód bude velký jako její příkazy a ne jako CALL)):


procedure KonecLFB; inline;
begin
 unlock_linear_region(_LFB,_PAMET);
end;  

Tak, teď se trochu začneme spoléhat na cizí jednotky. Výhoda je jasná: pokud chcete programovat pro Windows (nebo i Linux), tak tam to, co jsme se doposud naučili, nemůžete použít (tím myslím, že budete vytvářet nativní WIN32 aplikace: DOSová aplikace spuštěná pod WIN se bude chovat naprosto stejně jako pod DOSem, za předpokladu, že je Windows dobře napsán - když ne, použijte emulátor, např. DOSbox (s tím můžete spustit Vaše DOSovky i pod Linuxem)).

Free Pascal nám pro základ poskytuje jednotku GRAPH. Je sice pomalejší než to, co si můžeme napsat vlastními silami, nebo za použití dalších jednotek (budeme brát dále alespoň co se týče Windows), ale má dvě výhody: a) podporuje i VESA režimy, jako 800x600/TC, b) běží pod DOSem, Windows a i Linuxem (zkrátka je určena pro všechny platformy, které FP podporuje). Pokud nepotřebujete psát náročné 3D hry, ale jen např. grafické účetní/databázové/grafové aplikace, jistě Vám přijde vhod, že Váš program budete moci zkompilovat pro 3 systémy současně. Protože jednotka GRAPH je dobře popsaná v Turbo Pascalu 7 (navíc i v nápovědě pro FP) a používá stejné funkce (OutText,SetColor,atd.), nebudu jí rozebírat do hloubky. Ukážu Vám jen příklad, jak jednoduše zapnout rozlišení 800x600 v 8bitových barvách:


uses Graph;
begin
 initgraph(d8bit,m800x600,'');	(* nepotřebujeme žádné BGI *)
 if error = grOk then
 begin
  (* zde si nakreslete něco na obrazovku, atd. *)
  closegraph;
 end;
end;  

Jak vidíte, je to velice snadné. Možná ne tak rychlé, ale pro vykreslení něčeho jako je Windows 3.11 bez nároku na přehrávání videa a animací dostačující. Pokud je Vašim primárním cílem hlavně Windows, můžete využít jednotku WinGRAPH. Ta přistupuje přímo přes Direct Draw a umožní Vám používat i Open GL, takže tedy i 3D objekty! Používá téměř stejné funkce jako jednotka GRAPH.

Můžete zde využít pár nových funkcí (jako např. reagovat na zavření aplikace vynucené uživatelem, podpora bézeriových křivek a jakžtakž i animací). Nevýhoda je, že Vaše aplikace může běžet buď v okně, kde si rozměry můžete nadefinovat sami, nebo ve Full Screen, kde jsou ovšem rozměry obrazovky závislé na aktuálním nastavení plochy Windows. Režimy, které můžete využívat se zadávají podobně jako v FP. Zde máte 2,16 a 256 barevné palety, nebo True Color (ovšem pravděpodobně 24 bitový). Rozlišení v okně sahá od m320x200 až po m1280x1024. Pokud pracujete na Full Screen (mFullSc), musíte se přizpůsobovat podle MaxX a MaxY. Podporovany jsou i původní zastaralé režimy, jako např. m640x350. Navíc si můžete pojmenovat titulek okna, pokud v něm bežíte. Můžete používat také všechny fonty, které jsou instalované ve Windows! Jednotka podporuje až 4 grafické stránky, do kterých můžete kreslit. Výběrem aktivní si určíte, která se ale bude zobrazovat. Jednotka podporuje obnovování VRAM podle určitých pravidel. Standardně se kreslí přímo do VRAM. Můžete si ale zapnout, abyste kreslili nejprve do Vašich stránek (UpdateOff), a teprve po kompletním dokreslení je zobrazíte (UpdateNow). To provádí funkce UpdateGraph. K jednotce se dodávají i jednotky pro práci s myší, zvukem (píp) a klávesnicí. Zde je nějaký ten příklad (k jednotce se dodává dost dobře popsaná dokumentace a spousta příkladů):


uses WinGraph;
begin
 InitGraph(D8bit,_m640x480,'Test rozlišení 640x480');
 UpdateGraph(UpdateOff);
 {nastavíme jeden z předvolených fontů na tučný a velikost 16 pixelů}
 {cokoliv pod 6 není vel.v pixelech, ale násobič 8 pixelů}
 SetTextStyle(TimesNewRomanFont or BoldFont,HorizDir,16);
 (* tady si něco vykreslete (používáme stránku 0) *)
 UpdateGraph(UpdateNow);
 (* tady můžeme třeba chvíli počkat *)
 CloseGraph;
end;  

Pokud Vám toto nevyhovuje, můžete použít rovnou Direct Draw. Budete potřebovat jednotku (z balíku "gx106") DXCommon a DirectDrawFPC společně se souborem Switches.inc (vše si zkompilujete). Zde jsou implementovány všechny funkce, které byste mohli volat ve Windows programovacích jazycích. Jednotky u sebe obsahují velkou spoustu příkazů. Nejhorší na vysvětlení je asi to, že ten nejblbější příklad ukazující metodu PageFlipp má 10 kB zdrojového textu! Naštěstí ale můžete využít i tutorialy Direct X, které jsou např. na adrese www.CeskeHry.cz, ale i v těch příkladech přímo u jednotky je popsán kód celkem přehledně. Problém je v tom, že než v Direct Draw vůbec inicializujete režim, trochu se zapotíte. Pokud chcete pracovat s grafikou o trochu lépe, než Vám nabízí např. WinGraph, budete se do toho muset ponořit (pod Linuxem Vám poběží takové programy samozřejmě také, pokud použijete WINE). Stručně byste mohli potřebovat informace o těchto věcech:


	IDirectDraw4			objekty
	IDirectDrawSurface4		obrazovka


	IDirectDrawSurface4_Blt		vykreslí obrazovku
	IDirectDrawSurface4_GetDC	?
	IDirectDrawSurface4_ReleaseDC	?
	PostMessage			zprávy
	SetCursor			kurzor
	IDirectDrawSurface4_Flip	přesune obrazovku do VRAM
	IDirectDrawSurface4__Restore	?
	LoadIcon			nahraje ikonu
	LoadCursor			nahraje kurzor myši
	GetSystemMetrics		informace o XY obrazovky
	ShowWindow			zobrazí okno
	UpdateWindow, SetFocus		?
	IDirectDraw_QueryInterface	?
	IDirectDraw4_SetCooperativeLevel
	IDirectDraw4_SetDisplayMode	mění rozlišení
	IDirectDraw4_CreateSurface	vytvoří "obrazovku"
	IDirectDrawSurface4_GetAttachedSurface
	SetTimer			nastavuje časovač  

Pokud se nechcete mořit s Direct X a chcete raději zkusit něco, co Vám nabízí komfort, můžete zkusit jednotky VGFX (obsahuje i tutorial), která nabízí celkem rychlé zobrazování efektní grafiky pod Windows; grafickou část jednotek lx5suite_0760, které nabízí opravdu kompletní ovládání počítače (včetně zvuku (na ten Vám mohu ještě doporučit např. Bass20 nebo Fmod350)); Qgtk-Graphic; FPCx-dx (který zjednoduší používání Direct Draw); nebo využít docela oblíbené JediSDL, které obsahuje podporuje grafiky, sítě, ale i zvuků. Navíc SDL běží zároveň jak pod Windows, tak i pod Linuxem, tedy pokud napíšete aplikaci chodící pod jedním, můžete ji rovnou jen změnou targetu překompilovat pro druhou platformu. Samozřejmě, že naučit se plně využívat možností těchto jednotek nebude snadné, ale pokud to myslíte s programováním opravdu vážně, měli byste s tím začít. Pokud se rozhodnete pro některou z těchto věcí, asi budete potřebovat následující funkce a procedury:


	--- VGFX ---
	(viz. "VGFX - making first steps.doc")


	Window_RegisterClass		Window_CreateClass
	CheckWMCreate			Init_Graph
	Load_Fonts			Load_Pcx
	Init_VW				ScaleSprite
	CursorVisible			Flip_VW
	PutSprite			Flip_DD
	GETALLMESSAGES			HANDLEMINIMIZED
	ApplicationRunning : boolean
	Kill_VW				Kill_Graph
	CheckMemory			TERMINATE
	atd.




	--- LX5SUITE ---
	(dema jsou dostatečně přehledná)


	video_open			video.hline
	LoadImage			tsprite.create_assign
	sprite.show			sprite.move
	video_update			sprite.hide
	lx5suite_process		keywaiting
	keyread				mouse.leftbutton
	sprite.free			video_close
	LoadAlpha			CreateImage
	backdrop.paste_area		workspace.alpha_tint
	video_lock			video_unlock
	video.cls			workspace.paste
	workspace.paste_region		backdrop.convert_depth
	video.putpixel			video.line
	video.box			video.bar
	video.ellipse			video.polygon
	video.circle			backdrop.paste_scale
	scalar.resize			scalar.paste_area


	- ovládání videa a grafiky
	- ovládání zvuků
	- práce s fonty TTF
	- vytváření GUI (okna, tlačítka, plochy)
	- efekty s grafikou
	- přístup na web




	--- SDL Jedi ---
	(obsahuje podrobnou HTML dokumentaci s příklady u každé funkce)


	- grafika a video
	- klávesnice
	- joystick
	- zvuková zařízení (WAV)
	- ovládání CD-ROM
	- časovač
	- Open GL
	- TCP přes sockety  

Protože nám tento seriál vyšel tentokrát docela krátký (omlouvám se za to, že zde nejsou příklady pro předchozí dva odstavce, ale to bych se to musel nejprve všechno naučit, což udělám, až to osobně budu potřebovat), přidám sem jako bonbónek krátkou (!) informaci ovládání WinAPI, které Vám s pomocí FP umožní vytvářet aplikace typu Poznámkový blok (běžící ve Windows a s WINE i pod Linuxem). Pro tyto účely se nemusíte shánět po žádných extra jednotkách. Pokud si stáhnete FP pro Windows, tak v adresáři DEMOS a WIN32 najdete pár ukázek, jak vytvořit DLL knihovnu, a popř. i nějaký ten jednodušší program. Myslím, že je celkem zbytečné vysvětlovat princip těchto programů, neboť pokud se o ně budete chvíli zajímat, pochopíte jej sami:


	SendMessage			GetOpenFileName
	GetSaveFileName			GetWindowTextLength
	GetWindowText			SetStatusText
	MessageBox			SetWindowText
	ChooseFont			PostQuitMessage
	SetFocus			GetClientRect
	MoveWindow			GetSysColorBrush
	DefWindowProc			GetStockObject
	LoadIcon			LoadCursor
	WndProc				RegisterClass
	CreateWindowEx			ShowWindow
	UpdateWindow			CreateMenu
	AppendMenu			SetMenu
	CreateStatusWindow		TranslateMessage
	DispatchMessage			DeleteObject  

Samozřejmě, budete-li mít zájem, napište na INT21H (někomu jinému než mě), zda-li by se nechtěl ujmout nějakého článku, který by vysvětloval některou konkrétní věc z posledních třech až čtyřech odstavců (např. grafika v SDL; pokud Vám nebudou stačit dodávaná efektní grafická dema a příklady); budu mezi těmi, kteří si dané články rádi přečtou.


Já tímto můj seriál o grafice uzavřu. Zároveň konstatuji, že pokud chcete programovat pod DOSem, můžete se nyní plně spolehnout na své síly. Pokud byste ale chtěli Váš program distribuovat a provozovat pod Windows (či Linuxem), využijte rozhodně raději nějakou s cizích jednotek (popř. WinAPI). Doporučuji buď SDL nebo lx5suite_0760 či VGFX-vesa3 podle toho, co Vám přijde jednodušší na naučení se (ideální je samozřejmě balík, který podporuje zároveň i zvuky, jako je lx5suite_0760 nebo JediSDL-Delphi, pokud se ovšem nespolehnete na zvukové jednotky cizích stran - ale o tom snad někdy příště).

2006-11-30 | Martin Lux
Reklamy:
„Nejkrásnější ze všech tajemství je být géniem a vědět to jen sám.“ Mark Twain