Welcome to the new Friends-of-FPC!

Here you can find all kinds of information about the FreePascal Compiler. We have many tutorials and howtos as well as a selection of tools to help you with your programming. We also have some example codes for you. And if you want to contribute some information/ sources/ tools yourself you can do so.
Also we have finally relaunched the FoFPC forum. It's your chance for some Q&A about everything FreePascal.

Friends-of-FPC

Tutorials: Learn how to code with FreePascal.

Source Codes: A collection of examples, miscellaneous source codes and open source stuff.

Tools and Help Files: Intro- duction of some tools that might help you with FPC.

Community

Forum: Ask or answer questions about the FreePascal Compiler, programming or just babble about coding.

Contribute! Contribute your own Tutorial, Source Codes or Tools and send them to us!

Website

About: Information about Friends-of-FPC.org.

Grundlagen der Grafikprogrammierung - Teil 6 - by Delax

Hallo! Manche haben sich vielleicht den Windows API Kram gespart und sind direkt hier eingestiegen - auch an euch ein Hallo! Nein, ich werde keine Gnade walten lassen und noch einmal auf den Windows API Kram eingehen.

Dieses Mal wollen wir also mal richtig einsteigen und endlich ein bischen Grafik machen. Doch macht euch erst mal locker - als erstes werden wir nur in einen Grafikmodus wechseln und nicht viel mehr.

Früher war Mode13h ja ein beliebter Grafikmodus. Heute reißen die 320x200 Pixel mit 256 Farben wenige von Hocker. Man kann zwar gut darauf lernen und noch erstaunliche Dinge vollbringen, aber der Standard sind heute eher 640x480 bis 800x600 in Windows - am besten noch DirectX. Eben das wollen wir auch.

Ich sagte schon im ersten Teil, wir werden uns am Anfang manches leicht machen und Hilfe von außen nutzen. In diesem Falle geht es um fpcX. Zitat aus der beiliegenden Readme: "fpcX is a Windows-DLL mainly written for use with the FreePascalCompiler to access Fullscreen-Video-Modes using DirectX..". Geschrieben wurde sie von Toxic Avenger/ AINC.

Zuerst solltet ihr auf www.ainc.de gehen, euch etwas umschauen (evtl auch mal die Demos downloaden ;) ) und euch die fpcX zip besorgen und entpacken. Dann schaut euch mal die eigentlichen Dateien an. Im "Lieferumfang" sind enthalten: Der Source der DLL, zwei Beispiele (eins für Delphi, eins für FPC), eine Textdatei und die DLL selbst.

Die DLL stellt uns drei Funktionen (wobei ich Funktionen im Sinne von Funktionalität meine und keine function) zur Verfügung:

  • GetDefaultDirectDraw : Dient dazu in einen Vollbild Modus zu gelangen.
  • CloseDirectDraw : Ist dazu da, den Vollbild Modus wieder zu verlassen.
  • DirectDrawBlit : Wird genutzt, um etwas auf dem Monitor darzustellen.

So wenig das klingt: öffnen, schließen, blitten ist alles, was man im Grunde braucht. Schließlich wollen wir ja den Rest selbst machen.

Am besten ist, wir schauen uns mal das mitgelieferte Beispiel an und gehen es dann durch. Allerdings werden wir nicht alles auf einmal durchgehen, sondern Schritt für Schritt alles aufteilen. Den Disclaimer spare ich mir mal - lest ihn selbst.

{$APPTYPE GUI}
uses windows,sysutils;

PROCEDURE GetDefaultDirectDraw(ParentWindow : hWND; x,y : longint);stdcall;
 external 'fpcx' name 'GetDefaultDirectDraw';

PROCEDURE CloseDirectDraw;stdcall;
 external 'fpcx' name 'CloseDirectDraw';

FUNCTION DirectDrawBlit(buf : pointer) : boolean;stdcall;
 external 'fpcx' name 'DirectDrawBlit';

Nun, {$APPTYPE GUI} kennen wir ja bereits. Als Units wird windows (klar) und systutils eingebunden. Sysutils dient hauptsächlich zum "Exception Handling". Wenn also eine Exception nicht innerhalb des Programmes von uns abgefangen wird, kümmert sich Sysutils darum. Mal abgesehen davon stellt sie ein paar nette Funktionen zur Verfügung.

Die drei externals beziehen sich auf die 3 Funktionen, die uns die DLL zur Verfügung stellt. Damit wir sie benutzen können, müssen wir sie als entsprechend extern definieren.

VAR   hWindow: HWnd;
      hHDC: HDC;
      thisistheend : boolean;
      AMessage : Msg;

CONST TestX=640;
      TestY=480;

Dies sind die nötigen Variablen und Konstanten. hWindow ist unser Window Handle, hHDC der Device Context (jedes Fenster braucht einen). thisistheend ist eine Flag um zu überprüfen, ob das Programm beendet ist und AMessage die Variable, in der WinProc die Messages verarbeitet. (siehe vorherige WinAPI Teile).

Die Konstanten sind dazu da die Bildschirmgröße unseres Modus festzulegen.

FUNCTION WindowProc(Window: HWnd; AMessage, WParam, LParam: Longint): 
Longint; stdcall; export;

BEGIN

  WindowProc:=0;
  case AMessage of
    wm_Destroy,  //KeyDown or Closed ??!
    wm_KeyDown : begin
                  thisistheend:=true;
                  ReleaseDC(hWindow,hHDC);
                  hHDC:=0;
                  PostQuitMessage(0);
                  Exit;
                 end;
  end;
  WindowProc:=DefWindowProc(Window, AMessage, WParam, LParam);

END;

Unsere WinProc. Sie sieht nicht viel anders aus, als unsere letzte, nur das wir diesmal nicht nur auf wm_Destroy achten, sondern auch auf wm_keydown (also Tastendruck). Falls der User also das Fenster (besser gesagt das Vollbildfenster) oder eine Taste drückt passiert folgendes: die Variable thisistheend wird auf true gesetzt und zeigt so an, das das Programm beendet ist. ReleaseDC(hWindow,hHDC) löst den vorher an unser Fenster gebundenen Device Context und setzt ihn eine Zeile später zurück.

FUNCTION WinRegister: Boolean;  //Register Window Class
 Var WindowClass: WndClass;
BEGIN

  WindowClass.Style:=cs_ByteAlignClient;
  WindowClass.lpfnWndProc:=WndProc(@WindowProc);
  WindowClass.cbClsExtra:=0;
  WindowClass.cbWndExtra:=0;
  WindowClass.hInstance:=system.MainInstance;
  WindowClass.hIcon:=LoadIcon(0, idi_Application);
  WindowClass.hCursor:=LoadCursor(0, idc_Arrow);
  WindowClass.hbrBackground:=GetStockObject(BLACK_BRUSH);
  WindowClass.lpszMenuName:=nil;
  WindowClass.lpszClassName:='AINC.'; //Class Name

  WinRegister:=RegisterClass(WindowClass)<>0;

END;

Hier registrieren wir unsere Windows Klasse Im Prinzip das gleiche wie in Teil 2 erklärt.

FUNCTION WinCreate: HWnd;  //Create Window Class
 Var hWindow: HWnd;
BEGIN

  hwindow:=createwindowex(0,'AINC.',pchar('DirectX'),
   WS_VISIBLE or WS_POPUP,0,0,50,50,0,0,system.MainInstance,nil);

  if hWindow<>0 then begin
   ShowWindow(hWindow, CmdShow);
   UpdateWindow(hWindow);
   hHDC:=GetDC(hWindow);
  end;

  WinCreate:=hWindow;

END;

Hier haben das Fenster mit unserer Windows Klasse erzeugt. Aber achtet mal genau auf den Aufruf. Hier nutzen wir CreateWindowEx - vorher nutzten wir CreateWindow. Was das EX soll? Die Aufrufe mit dem -Ex am Ende sind einfach nur die "Extended" Versionen, also neuere, erweiterte Aufrufe.

Ansonsten nix neues - wenn das Fenster erzeugt wurde, zeige es, stelle es dar und schnappe dir einen Device Context dafür.

Var vidvscr : pointer; BEGIN WinRegister; hWindow:=WinCreate;

Hier beginnt das Hauptprogramm. Erst registrieren wir die Windos Klasse und erzeugen dann unser Fenster. Wir definieren außerdem einen Pointer - später dazu mehr.

 GetDefaultDirectDraw(hWindow,testx,testy); //Init with Handle of our Window

Und jetzt zu fpcX. Wir öffnen jetzt mit dem Handle unseres Fensters einen Vollbildmodus mit testx und testy als Größe. Die haben wir ja am Anfang als Konstanten 640 x 480 festgelegt.

 getmem(vidvscr,testx*testy*2);

Jetzt zu dem vorher definierten Pointer. Das ist unser Grafikspeicher. Alles, was wir da hinein schreiben wird auch auf dem Bildschirm ausgegeben - zumindest, wenn wir das so wollen.

Wie kann man sich diesen Grafikspeicher denn nun vorstellen? Am besten ich erkläre es euch an Mode 13h. Wie schon erwähnt war dieser Modus genau 320x200 groß. Er benötigte also genau 320x200 Speicherstellen. Da er nur 256 Farben hatte, benötigte er nur ein Byte pro Speicherstelle = 64000 Byte. Stellt euch das mal als Zahlenstrahl vor. Und nun wandert von 0 ab 320 Stellen nach rechts. An Speicherstelle 320 beginnt nämlich Zeile 2 des Bildschirmes. Dann wieder 320 Stellen nach rechts und dann beginnt die nächste Zeile und so weiter...In ASCII dargestellt etwa so:

0	.......	319
320	.......	639
640	.......	959
960	.. ->

Klingt logisch, oder? Damals galt also folgende Formel um einen Punkt (X,Y) zu bestimmen: X+(Y*320). Auch logisch: Für jede Zeile (Y) müssen wir 320 mal X zurücklegen. Dann noch den angegebenen X Wert dazu und man ist an der aktuellen Position. Um eine Farbe darzustellen muß man nur an dem entsprechenden Punkt einen Wert hinein schreiben. Jeder Wert symbolisiert eine Farbe.

Soweit zu Mode X. Nun sind wir hier aber nicht mehr bei 320x200 und 256 Farben haben wir auch nicht, sondern bei 16 Bit 65536. Den Unterschied von 320x200 auf 640x480 ist ganz einfach: für jede Zeile müssen wir dann halt 480 weiter wandern. Doch was für Auswirkungen hat die Farbtiefe? Vorher genügte uns ein Byte zum angeben der Farbe - nun benötigen wir dazu zweimal ein Byte, da 256 x 256 = 65536. Also um ein Pixel zu bestimmen benötigen wir zweimal ein Byte: Erster Pixel - erstes Byte (0), erster Pixel - zweites Byte (1), zweiter Pixel - erstes Byte (2), zweiter Pixel - zweites Byte (3), ... also verdoppelt sich die Länge des Strahls. Genau deshalb haben wir auch 640x480x2 als Größe angegeben.

 while GetMessage(@AMessage,0,0,0) do begin
  fillchar(vidvscr^,testx*testy*2,random(255)); //"Draw" something
  DirectDrawBlit(vidvscr);                  //Show it

  TranslateMessage(AMessage);
  DispatchMessage(AMessage);
 end;

Hier haben wir wieder das ganz normale Muster von abholen und verarbeiten der Windows Messages. Das fillchar(vidvscr^,testx*testy*2,random(255)); füllt jetzt den Bildschirmspeicher (vidvcsr) durchgehend mit einem zufälligem Wert und setzt damit den ganzen Bildschirm auf eine Farbe. Das passiert jedesmal, wenn eine Nachricht an unser Programm geschickt wird, also auch, wenn der User die Maus benutzt, eine Taste drückt oder ähnliches.

DirectDrawBlit(vidvscr); sorgt dafür, das alles dargestellt wird. Die Prozedur sorgt einfach nur dafür, das der angegebene Bildschirmspeicher auf dem Monitor angeigt wird.

 CloseDirectDraw; //Close
END.

Tja, und hier endet alles, indem CloseDirectDraw den Vollbildmodus beendet.

Alles in allem war es doch gar nicht so schwer, oder? Ungewohnt vielleicht, aber je mehr wir damit rumspielen, desto leichter fällt es euch.

Bis denn dann,

Delax/ Sundancer Inc.
[delax@sundancerinc.de]

Back to previous page

Useful Links









Link to us