Tutorial
Lutz Ifers WinAPI Tutorial
von MOD | Seite 4 von 16 |
Kapitel 2.1: Pixeloperationen
Ziemlich schnell nach Erstellen des ersten Fensters mit der WinApi wird einem klar, dass es mit dem reinen Erzeugen des Fensters nicht getan ist, wir müssen uns ebenfalls nach neuen Methoden umsehen, um mit diesem Fenster zu interagieren.
Weder PSet noch Line noch Circle oder irgendein anderer Grafikbefehl ("Drawing Primitives") von FreeBasic lassen sich auf diesem Fenster anwenden, noch lässt sich per GetMouse die Mausposition abfragen, noch lassen sich Tastaturabfragen mit Inkey, MultiKey oder Input bewerkstelligen.
Als erstes wollen wir uns darum kümmern, ein wenig Farbe auf das Fenster zu bekommen:
''' Lutz Ifers WinAPI-Tutorial
''' Lizenz: WTFPL
'''
''' Kapitel 2.1 - "Pixeloperationen"
#include "windows.bi"
const ProgrammName = "Pixeloperationen"
declare function Fenster(byval hWnd as HWND, byval message as UINTEGER,_
byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
dim as WNDCLASS wcMeinFenster
with wcMeinFenster
.style = CS_HREDRAW or CS_VREDRAW
.lpfnWndProc = ProcPtr(Fenster)
.cbClsExtra = 0
.cbWndExtra = 0
.hInstance = GetModuleHandle(NULL)
.hCursor = LoadCursor(NULL, IDC_ARROW)
.hIcon = LoadIcon(NULL, IDI_APPLICATION)
.hbrBackground = GetStockObject(WHITE_BRUSH)
.lpszClassName = StrPtr(ProgrammName)
.lpszMenuName = NULL
end with
RegisterClass @wcMeinFenster
dim as HWND hMeinFenster = CreateWindow(_
ProgrammName, "Titelzeile", WS_OVERLAPPEDWINDOW,_
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,_
NULL, NULL, GetModuleHandle(NULL), NULL)
ShowWindow hMeinFenster, SW_NORMAL
UpdateWindow hMeinFenster
dim as MSG msg
do while getmessage(@msg, NULL, 0, 0) <> 0
DispatchMessage @msg
loop
end msg.wParam
Der Beginn unseres Programmes ist unverändert. Wir deklarieren die Callbackfunktion des Fensters, seine Fensterklasse, erzeugen es, zeigen es an, lassen es neuzeichnen, und leiten die Nachrichten weiter. Und die sehen wir uns jetzt genauer an:
function Fenster(byval hWnd as HWND, byval message as UINTEGER,_
byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
select case message
case WM_DESTROY
PostQuitMessage 0
return 0
Dieser Teil ist uns nicht unbekannt: Wenn das Fenster die Schließen-Nachricht bekommt, soll das Programm beendet werden. In diesem Programm kümmern wir uns noch um eine zweite Nachricht, die WM_PAINT-Nachricht. Sie wird dem Fenster jedesmal zugeschickt, wenn der Inhalt des Fensters ungültig ist, also neugezeichnet werden muss. Weil ein verdeckendes Fenster verschoben wurde, sich die Fenstergröße ändert oder wir ein Neuzeichnen angefordert haben zum Beispiel.
case WM_PAINT
dim as PAINTSTRUCT pnt
dim as HDC hDC = BeginPaint(hWnd, @pnt)
Wir definieren als erstes eine Paintstruktur. Dieses UDT enthält wichtige Informationen über die Clippingregion. Als nächstes erzeugen wir einen sogenannten DeviceContext, das ist eine Oberfläche eines Fensters, vergleichbar mit dem Screen[res]-Befehl in FreeBasic, angepasst an die Vorraussetzungen, die ein Mehrfenster-System mit sich bringt.
dim as INTEGER ix, iy
for ix = 0 to 255
for iy = 0 to 255
SetPixel hDC, ix, iy, RGBA(ix, iy, 128,0)
next
next
Der erste Befehl, den wir näher betrachen wollen, ist der SetPixel()-Befehl, der im Grunde genommen wie pset() in einem 24Bit FreeBasic-Screen funktioniert. Bei jedem Grafikbefehl müssen wir allerdings noch mit angeben, auf welchen DC gezeichnet werden soll. Durch die beiden for-Schleifen erzeugen wir einen einfachen Farbverlauf.
GetPixel ist in seiner Funktionsweise dem FreeBasic-Befehl point() identisch, zu Demonstrationsszwecken benutzen wir ihn, um einen Teil einer Zeile aus dem Farbverlauf zu kopieren:
for iy = 100 to 200
dim as INTEGER iColor = GetPixel(hDC, 100, iy)
SetPixel hDC, 299, iy, iColor
next
Das Pendant zu "line" heißt LineTo, funktioniert allerdings ein wenig anders, als der FB-Befehl. Der wahrscheinlich größte Unterschied ist, dass wir mehr als nur die Farbe der Linie bestimmen können, in dem wir sogenannte pens, also Stifte definieren. Die mit diesen Stiften gezogenen Linien können unterschiedlich breit, unterschiedlich in der Farbe und unerschiedlich im Füllmuster, durchgehend, gepunktelt, gestrichelt, gepunkt-strich-punkt-strichelt usw. sein. Wir legen zwei Stifte mit der Funktion CreatePen(Stil, Dicke, Farbe) fest:
dim as HPEN hpenDickRot = CreatePen(ps_solid, 3, RGBA(0,0,255,0))
dim as HPEN hpenBlau = CreatePen(ps_dot, 1, RGBA(255,0,0,0))
Um eine Linie zu zeichnen, müssen wir erst den virtuellen Cursor verschieben, und dann eine Linie zu einem bestimmten Punkt ziehen. Diese Vorgehensweise könnte man mit einem Line-Aufruf a la
line step(0,0)-step(x,y)
vergleichen. Als erstes Zeichnen wir eine Linie mit dem Standardstift, schwarz, durchgehend, ein Pixel breit, dann eine Linie mit dem roten durchgehenden, drei Pixel breiten Stift, und zu guter letzt eine Linie mit dem gepunkteten blauen Stift:
MoveToEx hDC, 5, 260, NULL : LineTo hDC, 250, 260
SelectObject hDC, hpenDickRot
MoveToEx hDC, 5, 270, NULL : LineTo hDC, 250, 270
SelectObject hDC, hpenBlau
MoveToEx hDC, 5, 280, NULL : LineTo hDC, 250, 280
DeleteObject hpenDickRot
DeleteObject hpenBlau
EndPaint(hWnd, @pnt)
return 0
end select
return DefWindowProc(hWnd, message, wParam, lParam)
end function
Jeden Pen, den wir erstellt haben, müssen wir auch wieder löschen, sobald wir mit zeichnen fertig sind. Würden wir das nicht tun, würde unser Programm irgendwann durch einen Pufferüberlauf zusammenbrechen. Noch ein Wort zur Verwendung von BeginPaint ... EndPaint: Auch wenn es so aussehen mag, als würde das Programm ohne diese Befehle auskommen und auch die Dimensierung der Paintstruct überflüssig scheint, so sind sie dennoch nötig. Windows merkt sich, ob das Fenster noch ungültig ("invalid") ist, dieser Status wird erst durch BeginPaint zurückgesetzt. Würden wir BeginPaint nicht verwenden, würde Windows unser Programm mit WM_PAINT-Nachricht bombadieren.
Besonders auf älteren Rechnern wird einem auffallen, dass das Gezeichne nicht allzuschnell ist. Die GDI ("Graphics Device Interface") zeichnet nicht Pixel, sondern Objekte, daher ist das Zeichnen eines einzelnen Pixels (mehr oder minder) genausoschnell wie das Zeichnen einer bildschirmfüllenden Bitmap. Diesen Umstand werden wir in Kapitel 2.3 benutzen, um uns des Flackerns zu entledigen.
Links:
In der MSDN: WM_PAINT, PAINTSTRUCT, HDC, BeginPaint, EndPaint, SetPixel, GetPixel, CreatePen, MoveToEx, LineTo
In der FreeBasic-Referenz: LINE, PSET, POINT
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|