Difference between revisions of "Developing with Graphics/de"
m (beautified) |
|||
Line 1: | Line 1: | ||
{{Developing with Graphics}} | {{Developing with Graphics}} | ||
− | Diese Seite wird der Anfang von Tutorials sein bezüglich der Manipulation von Bitmaps und anderen Grafiken. Da ich kein Grafikprogrammierer bin, lade ich alle ein ihr Wissen zu teilen! Fügen | + | Diese Seite wird der Anfang von Tutorials sein bezüglich der Manipulation von Bitmaps und anderen Grafiken. Da ich kein Grafikprogrammierer bin, lade ich alle ein ihr Wissen zu teilen! Fügen Sie einfach einen Link zum nächsten Abschnitt hinzu, fügen Sie eine Seite hinzu und erzeugen Sie Ihren eigenen Wiki Artikel. |
Auf dieser Seite werden einige allgemeine Informationen geboten. | Auf dieser Seite werden einige allgemeine Informationen geboten. | ||
Line 18: | Line 18: | ||
== Arbeiten mit TBitmap == | == Arbeiten mit TBitmap == | ||
− | Das erste woran | + | Das erste woran Sie denken sollten ist, dass Lazarus plattformunabhängig sein soll. Daher kommen alle Methoden, welche die Windows API Funktionalität nutzen, nicht in Frage. Daher wird eine Methode wie ScanLine nicht von Lazarus unterstützt, weil sie für Geräte-unabhängige Bitmaps gedacht ist und Funktionen von GDI32.dll verwendet. |
− | Beachten | + | Beachten Sie, dass die Standardwerte von Breite und Höhe Ihrer [[doc:lcl/graphics/tbitmap.html|TBitmap]] ziemlich klein sind. Es ist daher wichtig, diese Maße immer genau zu spezifizieren. |
=== Ein Beispiel: "Fading Image" === | === Ein Beispiel: "Fading Image" === | ||
− | Nehmen wir an, | + | Nehmen wir an, Sie wollen ein verblassendes Bild erstellen. In Delphi geschriebener Code könnte in etwa folgendermaßen aussehen: |
− | < | + | <delphi> |
type | type | ||
PRGBTripleArray = ^TRGBTripleArray; | PRGBTripleArray = ^TRGBTripleArray; | ||
Line 64: | Line 64: | ||
end; | end; | ||
end; | end; | ||
− | </ | + | </delphi> |
Diese Funktion könnte in Lazarus so implementiert werden: | Diese Funktion könnte in Lazarus so implementiert werden: | ||
− | < | + | <delphi> |
procedure TForm1.FadeIn(ABitMap: TBitMap); | procedure TForm1.FadeIn(ABitMap: TBitMap); | ||
var | var | ||
Line 100: | Line 100: | ||
TempBitmap.Free; | TempBitmap.Free; | ||
end; | end; | ||
− | </ | + | </delphi> |
− | Der Lazarus Code auf dieser Seite wurde dem $LazarusPath/examples/lazintfimage/fadein1.lpi Projekt entnommen. Wenn | + | Der Lazarus Code auf dieser Seite wurde dem $LazarusPath/examples/lazintfimage/fadein1.lpi Projekt entnommen. Wenn Sie einen einfachen Start in die Grafikprogrammierung suchen, dann werfen Sie einen näheren Blick auf dieses Beispiel. |
=== Transparent zeichnen mit Bitmaps === | === Transparent zeichnen mit Bitmaps === | ||
Line 110: | Line 110: | ||
Das folgende Beispiel lädt eine Bitmap aus einer Windows Ressource, wählt eine Farbe für die Transparenz (clFuchsia) und zeichnet es auf die Zeichenfläche (Canvas). | Das folgende Beispiel lädt eine Bitmap aus einer Windows Ressource, wählt eine Farbe für die Transparenz (clFuchsia) und zeichnet es auf die Zeichenfläche (Canvas). | ||
− | < | + | <delphi> |
procedure MyForm.MyButtonOnClick(Sender: TObject); | procedure MyForm.MyButtonOnClick(Sender: TObject); | ||
var | var | ||
Line 140: | Line 140: | ||
bmp.Free; // Gibt belegte Ressourcen frei | bmp.Free; // Gibt belegte Ressourcen frei | ||
end; | end; | ||
− | </ | + | </delphi> |
− | Beachten | + | Beachten Sie, dass mit [[doc:rtl/classes/tmemorystream.html|TMemoryStream]] die Speicheroperationen ausgeführt werden. Sie sind notwendig, um das korrekte Laden des Bildes sicherzustellen. |
=== Ein Bildschirmfoto erstellen === | === Ein Bildschirmfoto erstellen === | ||
− | Seit Lazarus 0.9.16 können | + | Seit Lazarus 0.9.16 können Sie die LCL verwenden, um plattformunabhängig Bildschirmfotos aufzunehmen. Der folgende Beispielcode zeigt es (funktioniert unter GTK2 und Win32, aber gegenwärtig nicht unter GTK1): |
− | < | + | <delphi> |
uses LCLIntf, LCLType; | uses LCLIntf, LCLType; | ||
Line 163: | Line 163: | ||
... | ... | ||
− | </ | + | </delphi> |
− | == Motion Graphics - Wie man | + | == Motion Graphics - Wie man Flimmern vermeidet == |
− | Viele Programme zeichnen ihre Ausgabe als 2D Grafik in die GUI. Wenn diese Grafiken schnell geändert werden müssen, werden | + | Viele Programme zeichnen ihre Ausgabe als 2D Grafik in die GUI. Wenn diese Grafiken schnell geändert werden müssen, werden Sie bald einem Problem begegnen: sich schnell ändernde Grafiken flimmern oft auf dem Bildschirm. Das passiert, weil der Zeichenprozess Zeit benötigt und der Benutzer deshalb manchmal ein vollständiges Bild, manchmal aber auch nur ein teilweise gezeichnetes Bild sieht. |
− | Aber wie kann ich das Flimmern vermeiden und das beste Zeichentempo erreichen? Natürlich können | + | Aber wie kann ich das Flimmern vermeiden und das beste Zeichentempo erreichen? Natürlich können Sie mit Hardwarebeschleunigung unter Verwendung von OpenGL arbeiten, aber dieser Ansatz ist für kleine Programme oder alte Computer ziemlich ungeeignet. Dieses Tutorial konzentriert sich auf das Zeichnen in ein TCanvas. Wenn Sie Hilfe brauchen zu OpenGL, werfen Sie einen Blick auf das Beispiel, das mit Lazarus geliefert wird oder auf [[GLScene]]. Sie können auch A.J. Venter's Gamepack verwenden, das eine doppelt gebufferte Zeichenfläche (double-buffered canvas) und eine Sprite-Komponente bereitstellt. |
Jetzt wollen wir die Optionen untersuchen, die wir für das Zeichnen auf eine Zeichenfläche (Canvas) haben: | Jetzt wollen wir die Optionen untersuchen, die wir für das Zeichnen auf eine Zeichenfläche (Canvas) haben: | ||
Line 179: | Line 179: | ||
=== Zeichnen in ein TImage === | === Zeichnen in ein TImage === | ||
− | + | Ein TImage-Objekt besteht aus zwei Teilen: Ein TGraphic-Objekt, üblicherweise ein TBitmap, das das persistente Bild und die sichtbare Fläche enthält, die bei jedem OnPaint neu gezeichnet wird. Eine Änderung der Größe von TImage ändert '''nicht''' die Größe der Bitmap. | |
Auf die Grafik (oder die Bitmap) kann man über Image1.Picture.Graphic (oder Image1.Picture.Bitmap) zugreifen. Die Zeichenfläche (Canvas) ist Image1.Picture.Bitmap.Canvas. | Auf die Grafik (oder die Bitmap) kann man über Image1.Picture.Graphic (oder Image1.Picture.Bitmap) zugreifen. Die Zeichenfläche (Canvas) ist Image1.Picture.Bitmap.Canvas. | ||
Auf die Zeichenfläche mit dem sichtbaren Bereich eines TImage-Objekts kann man nur über Image1.Canvas zugreifen, während Image1.OnPaint aktiv ist. | Auf die Zeichenfläche mit dem sichtbaren Bereich eines TImage-Objekts kann man nur über Image1.Canvas zugreifen, während Image1.OnPaint aktiv ist. | ||
− | '''Wichtig''': Verwenden Sie nie das OnPaint-Event von Image1, um auf die Graphik, das Bild von TImage zu zeichnen. Die Graphik von TImage wird zwischengespeichert, | + | '''Wichtig''': Verwenden Sie nie das OnPaint-Event von Image1, um auf die Graphik, das Bild von TImage zu zeichnen. Die Graphik von TImage wird zwischengespeichert, wenn Sie also darauf zeichnen, bleiben die Änderungen für immer dort. Außerdem wird, wenn Sie ständig neuzeichnen lassen, das Bild flickern. In diesem Fall können Sie einen anderen Ansatz versuchen. Zeichnen auf ein TImage ist langsamer als die anderen Verfahren. |
==== Ändern der Größe einer Bitmap in TImage ==== | ==== Ändern der Größe einer Bitmap in TImage ==== | ||
Line 189: | Line 189: | ||
Anmerkung: Tun Sie das nicht während OnPaint. | Anmerkung: Tun Sie das nicht während OnPaint. | ||
+ | <delphi> | ||
with Image1.Picture.Bitmap do begin | with Image1.Picture.Bitmap do begin | ||
Width:=100; | Width:=100; | ||
Height:=120; | Height:=120; | ||
end; | end; | ||
+ | </delphi> | ||
==== Zeichnen auf eine Bitmap in TImage ==== | ==== Zeichnen auf eine Bitmap in TImage ==== | ||
Line 198: | Line 200: | ||
Anmerkung: Tun Sie das nicht während OnPaint. | Anmerkung: Tun Sie das nicht während OnPaint. | ||
+ | <delphi> | ||
with Image1.Picture.Bitmap.Canvas do begin | with Image1.Picture.Bitmap.Canvas do begin | ||
// fill the entire bitmap with red | // fill the entire bitmap with red | ||
Line 203: | Line 206: | ||
FillRect(0,0,Width,Height); | FillRect(0,0,Width,Height); | ||
end; | end; | ||
+ | </delphi> | ||
− | Anmerkung: Bei Image1.OnPaint zeigt Image1.Canvas | + | Anmerkung: Bei Image1.OnPaint zeigt Image1.Canvas auf den flüchtig sichtbaren Bereich. Außerhalb von Image1.OnPaint zeigt Image1.Canvas auf Image1.Picture.Bitmap.Canvas. |
Ein anderes Beispiel: | Ein anderes Beispiel: | ||
− | < | + | <delphi> |
procedure TForm1.BitBtn1Click(Sender: TObject); | procedure TForm1.BitBtn1Click(Sender: TObject); | ||
var | var | ||
Line 224: | Line 228: | ||
Round(x * Image.Width / 8), Round(y * Image.Height / 8)); | Round(x * Image.Width / 8), Round(y * Image.Height / 8)); | ||
end; | end; | ||
− | </ | + | </delphi> |
==== Im flüchtig sichtbaren Bereich einer TImage zeichnen ==== | ==== Im flüchtig sichtbaren Bereich einer TImage zeichnen ==== | ||
Line 230: | Line 234: | ||
Sie können nur während des OnPaint-Ereignisses in diesen Bereich zeichnen. OnPaint wird eventuell automatisch von der LCL aufgerufen, wenn der Bereich invalidiert wurde. Sie können den Bereich mit Image1.Invalidate manuell invalidieren. Dies ruft OnPaint nicht unmittelbar auf und Sie können Invalidate so oft Sie wollen aufrufen. | Sie können nur während des OnPaint-Ereignisses in diesen Bereich zeichnen. OnPaint wird eventuell automatisch von der LCL aufgerufen, wenn der Bereich invalidiert wurde. Sie können den Bereich mit Image1.Invalidate manuell invalidieren. Dies ruft OnPaint nicht unmittelbar auf und Sie können Invalidate so oft Sie wollen aufrufen. | ||
+ | <delphi> | ||
procedure TForm.Image1Paint(Sender: TObject); | procedure TForm.Image1Paint(Sender: TObject); | ||
begin | begin | ||
Line 238: | Line 243: | ||
end; | end; | ||
end; | end; | ||
+ | </delphi> | ||
=== Zeichnen im OnPaint-Ereignis des Formulars, der TPaintBox oder einem anderen Control === | === Zeichnen im OnPaint-Ereignis des Formulars, der TPaintBox oder einem anderen Control === | ||
Line 247: | Line 253: | ||
Hier ist ein Beispiel CustomControl: | Hier ist ein Beispiel CustomControl: | ||
− | < | + | <delphi> |
uses | uses | ||
Classes, SysUtils, Controls, Graphics, LCLType; | Classes, SysUtils, Controls, Graphics, LCLType; | ||
Line 288: | Line 294: | ||
inherited Paint; | inherited Paint; | ||
end; | end; | ||
− | </ | + | </delphi> |
und wie wir es auf dem Formular erzeugen: | und wie wir es auf dem Formular erzeugen: | ||
− | < | + | <delphi> |
procedure TMyForm.FormCreate(Sender: TObject); | procedure TMyForm.FormCreate(Sender: TObject); | ||
begin | begin | ||
Line 301: | Line 307: | ||
MyDrawingControl.DoubleBuffered := True; | MyDrawingControl.DoubleBuffered := True; | ||
end; | end; | ||
− | </ | + | </delphi> |
nur nicht vergessen es zu löschen: | nur nicht vergessen es zu löschen: | ||
− | < | + | <delphi> |
procedure TMyForm.FormDestroy(Sender: TObject); | procedure TMyForm.FormDestroy(Sender: TObject); | ||
begin | begin | ||
MyDrawingControl.Free; | MyDrawingControl.Free; | ||
end; | end; | ||
− | </ | + | </delphi> |
Das Setzen von Top und Left auf 0 ist nicht notwendig, da dies die Standardposition ist, aber es ist sinnvoll, um diese Position zu verdeutlichen. | Das Setzen von Top und Left auf 0 ist nicht notwendig, da dies die Standardposition ist, aber es ist sinnvoll, um diese Position zu verdeutlichen. | ||
− | "MyDrawingControl.Parent := Self;" ist sehr wichtig und | + | "MyDrawingControl.Parent := Self;" ist sehr wichtig und Sie werden Ihr Bedienelement nicht sehen, wenn Sie es nicht tun. |
"MyDrawingControl.DoubleBuffered := True;" wird benötigt, um das Flimmern unter Windows zu vermeiden. Es hat keinen Effekt unter gtk. | "MyDrawingControl.DoubleBuffered := True;" wird benötigt, um das Flimmern unter Windows zu vermeiden. Es hat keinen Effekt unter gtk. | ||
Line 318: | Line 324: | ||
=== A.J. Venter's Gamepack benutzen === | === A.J. Venter's Gamepack benutzen === | ||
− | Der Gamepack-Vorstoß bedeutet, alles in ein doppelt gebuffertes Canvas zu zeichnen, dessen sichtbarer Bereich erst dann aktualisiert wird, wenn Sie fertig sind. Dies benötigt etwas mehr Code, aber es schafft die Fähigkeit, große sich dynamisch ändernde Grafiken mit mehreren Elementen zu verarbeiten. Wenn Sie diese Technik verwenden möchten, sollten Sie sich das A.J. Venter's Gamepack ansehen, ein Set von Komponenten zur Entwicklung von Spielen mit Lazarus, das eine doppelt gebufferte Anzeigeflächen-Komponente sowie eine Sprite-Komponente bietet, designed um sich gegenseitig ideal zu | + | Der Gamepack-Vorstoß bedeutet, alles in ein doppelt gebuffertes Canvas zu zeichnen, dessen sichtbarer Bereich erst dann aktualisiert wird, wenn Sie fertig sind. Dies benötigt etwas mehr Code, aber es schafft die Fähigkeit, große, sich dynamisch ändernde Grafiken mit mehreren Elementen zu verarbeiten. Wenn Sie diese Technik verwenden möchten, sollten Sie sich das A.J. Venter's Gamepack ansehen, ein Set von Komponenten zur Entwicklung von Spielen mit Lazarus, das eine doppelt gebufferte Anzeigeflächen-Komponente sowie eine Sprite-Komponente bietet, designed um sich gegenseitig ideal zu integrieren. Sie können Gamepack mittels subversion erhalten:<br /> |
<code> | <code> | ||
svn co svn://silentcoder.co.za/lazarus/gamepack | svn co svn://silentcoder.co.za/lazarus/gamepack | ||
</code> | </code> |
Revision as of 00:46, 18 February 2011
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
italiano (it) │
日本語 (ja) │
한국어 (ko) │
Nederlands (nl) │
português (pt) │
русский (ru) │
slovenčina (sk) │
中文(中国大陆) (zh_CN) │
中文(台灣) (zh_TW) │
Diese Seite wird der Anfang von Tutorials sein bezüglich der Manipulation von Bitmaps und anderen Grafiken. Da ich kein Grafikprogrammierer bin, lade ich alle ein ihr Wissen zu teilen! Fügen Sie einfach einen Link zum nächsten Abschnitt hinzu, fügen Sie eine Seite hinzu und erzeugen Sie Ihren eigenen Wiki Artikel.
Auf dieser Seite werden einige allgemeine Informationen geboten.
Andere Grafikartikel
- BGRABitmap - Drawing shapes and bitmaps with transparency, direct access to pixels, etc.
- GLScene - Eine Portierung der visuellen OpenGL-Graphikbibliothek GLScene
- TAChart - Chartkomponente für Lazarus
- PascalMagick - ein einfach zu verwendendes API für die Kopplung mit ImageMagick, einer freien multiplattform Softwaresammlung um Bitmaps zu erzeugen, zu bearbeiten und zu entwerfen.
- PlotPanel - Eine Komponente zum Darstellen( Plotting / Charting) von animierten Graphen. Ähnlich wie TAChart.
- LazRGBGraphics - A package for fast in memory image processing and pixel manipulations (like scan line).
- Perlin Noise - Ein Artikel über die Verwendung von Perlin Noise in LCL Anwendungen.
- Double Gradient/de - Doppelte Farbverläufe leicht in Bitmaps gezeichnet.
Arbeiten mit TBitmap
Das erste woran Sie denken sollten ist, dass Lazarus plattformunabhängig sein soll. Daher kommen alle Methoden, welche die Windows API Funktionalität nutzen, nicht in Frage. Daher wird eine Methode wie ScanLine nicht von Lazarus unterstützt, weil sie für Geräte-unabhängige Bitmaps gedacht ist und Funktionen von GDI32.dll verwendet.
Beachten Sie, dass die Standardwerte von Breite und Höhe Ihrer TBitmap ziemlich klein sind. Es ist daher wichtig, diese Maße immer genau zu spezifizieren.
Ein Beispiel: "Fading Image"
Nehmen wir an, Sie wollen ein verblassendes Bild erstellen. In Delphi geschriebener Code könnte in etwa folgendermaßen aussehen: <delphi>
type PRGBTripleArray = ^TRGBTripleArray; TRGBTripleArray = array[0..32767] of TRGBTriple; procedure TForm1.FadeIn(aBitMap: TBitMap); var Bitmap, BaseBitmap: TBitmap; Row, BaseRow: PRGBTripleArray; x, y, step: integer; begin Bitmap := TBitmap.Create; try Bitmap.PixelFormat := pf32bit; // oder pf24bit Bitmap.Assign(aBitMap); BaseBitmap := TBitmap.Create; try BaseBitmap.PixelFormat := pf32bit; BaseBitmap.Assign(Bitmap); for step := 0 to 32 do begin for y := 0 to (Bitmap.Height - 1) do begin BaseRow := BaseBitmap.Scanline[y]; Row := Bitmap.Scanline[y]; for x := 0 to (Bitmap.Width - 1) do begin Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5; Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5; end; end; Form1.Canvas.Draw(0, 0, Bitmap); InvalidateRect(Form1.Handle, nil, False); RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW); end; finally BaseBitmap.Free; end; finally Bitmap.Free; end; end;
</delphi> Diese Funktion könnte in Lazarus so implementiert werden: <delphi>
procedure TForm1.FadeIn(ABitMap: TBitMap); var SrcIntfImg, TempIntfImg: TLazIntfImage; ImgHandle,ImgMaskHandle: HBitmap; FadeStep: Integer; px, py: Integer; CurColor: TFPColor; TempBitmap: TBitmap; begin SrcIntfImg:=TLazIntfImage.Create(0,0); SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); TempIntfImg:=TLazIntfImage.Create(0,0); TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); TempBitmap:=TBitmap.Create; for FadeStep:=1 to 32 do begin for py:=0 to SrcIntfImg.Height-1 do begin for px:=0 to SrcIntfImg.Width-1 do begin CurColor:=SrcIntfImg.Colors[px,py]; CurColor.Red:=(CurColor.Red*FadeStep) shr 5; CurColor.Green:=(CurColor.Green*FadeStep) shr 5; CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5; TempIntfImg.Colors[px,py]:=CurColor; end; end; TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false); TempBitmap.Handle:=ImgHandle; TempBitmap.MaskHandle:=ImgMaskHandle; Canvas.Draw(0,0,TempBitmap); end; SrcIntfImg.Free; TempIntfImg.Free; TempBitmap.Free; end;
</delphi>
Der Lazarus Code auf dieser Seite wurde dem $LazarusPath/examples/lazintfimage/fadein1.lpi Projekt entnommen. Wenn Sie einen einfachen Start in die Grafikprogrammierung suchen, dann werfen Sie einen näheren Blick auf dieses Beispiel.
Transparent zeichnen mit Bitmaps
Ein neues Feature, implementiert in Lazarus 0.9.11, sind farbtransparente Bitmaps. Bitmap-Dateien (*.BMP) können keine Informationen über Transparenz speichern, aber Sie können dies zur Laufzeit umgehen, indem Sie eine Transparenz-Farbe wählen, mit der Sie die transparenten Bereiche markieren. Bei Win32-Anwendungen ist dies ein üblicher Trick.
Das folgende Beispiel lädt eine Bitmap aus einer Windows Ressource, wählt eine Farbe für die Transparenz (clFuchsia) und zeichnet es auf die Zeichenfläche (Canvas).
<delphi> procedure MyForm.MyButtonOnClick(Sender: TObject); var
buffer: THandle; bmp: TBitmap; memstream: TMemoryStream;
begin
bmp := TBitmap.Create;
buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
if (buffer = 0) then exit; // Fehler beim Laden der Bitmap
bmp.Handle := buffer; memstream := TMemoryStream.create; try bmp.SaveToStream(memstream); memstream.position := 0; bmp.LoadFromStream(memstream); finally memstream.free; end;
bmp.Transparent := True; bmp.TransparentColor := clFuchsia;
MyCanvas.Draw(0, 0, bmp);
bmp.Free; // Gibt belegte Ressourcen frei
end; </delphi>
Beachten Sie, dass mit TMemoryStream die Speicheroperationen ausgeführt werden. Sie sind notwendig, um das korrekte Laden des Bildes sicherzustellen.
Ein Bildschirmfoto erstellen
Seit Lazarus 0.9.16 können Sie die LCL verwenden, um plattformunabhängig Bildschirmfotos aufzunehmen. Der folgende Beispielcode zeigt es (funktioniert unter GTK2 und Win32, aber gegenwärtig nicht unter GTK1):
<delphi>
uses LCLIntf, LCLType;
...
var
MyBitmap: TBitmap ScreenDC: HDC;
begin
MyBitmap := TBitmap.Create; ScreenDC := GetDC(0); MyBitmap.LoadFromDevice(ScreenDC); ReleaseDC(ScreenDC);
...
</delphi>
Motion Graphics - Wie man Flimmern vermeidet
Viele Programme zeichnen ihre Ausgabe als 2D Grafik in die GUI. Wenn diese Grafiken schnell geändert werden müssen, werden Sie bald einem Problem begegnen: sich schnell ändernde Grafiken flimmern oft auf dem Bildschirm. Das passiert, weil der Zeichenprozess Zeit benötigt und der Benutzer deshalb manchmal ein vollständiges Bild, manchmal aber auch nur ein teilweise gezeichnetes Bild sieht.
Aber wie kann ich das Flimmern vermeiden und das beste Zeichentempo erreichen? Natürlich können Sie mit Hardwarebeschleunigung unter Verwendung von OpenGL arbeiten, aber dieser Ansatz ist für kleine Programme oder alte Computer ziemlich ungeeignet. Dieses Tutorial konzentriert sich auf das Zeichnen in ein TCanvas. Wenn Sie Hilfe brauchen zu OpenGL, werfen Sie einen Blick auf das Beispiel, das mit Lazarus geliefert wird oder auf GLScene. Sie können auch A.J. Venter's Gamepack verwenden, das eine doppelt gebufferte Zeichenfläche (double-buffered canvas) und eine Sprite-Komponente bereitstellt.
Jetzt wollen wir die Optionen untersuchen, die wir für das Zeichnen auf eine Zeichenfläche (Canvas) haben:
- Zeichnen in ein TImage
- Zeichnen im OnPaint-Ereignis des Formulars, der TPaintBox oder einem anderen Control
- Ein CustomControl erzeugen, das sich selbst zeichnet
- A.J. Venter's Gamepack benutzen
Zeichnen in ein TImage
Ein TImage-Objekt besteht aus zwei Teilen: Ein TGraphic-Objekt, üblicherweise ein TBitmap, das das persistente Bild und die sichtbare Fläche enthält, die bei jedem OnPaint neu gezeichnet wird. Eine Änderung der Größe von TImage ändert nicht die Größe der Bitmap. Auf die Grafik (oder die Bitmap) kann man über Image1.Picture.Graphic (oder Image1.Picture.Bitmap) zugreifen. Die Zeichenfläche (Canvas) ist Image1.Picture.Bitmap.Canvas. Auf die Zeichenfläche mit dem sichtbaren Bereich eines TImage-Objekts kann man nur über Image1.Canvas zugreifen, während Image1.OnPaint aktiv ist.
Wichtig: Verwenden Sie nie das OnPaint-Event von Image1, um auf die Graphik, das Bild von TImage zu zeichnen. Die Graphik von TImage wird zwischengespeichert, wenn Sie also darauf zeichnen, bleiben die Änderungen für immer dort. Außerdem wird, wenn Sie ständig neuzeichnen lassen, das Bild flickern. In diesem Fall können Sie einen anderen Ansatz versuchen. Zeichnen auf ein TImage ist langsamer als die anderen Verfahren.
Ändern der Größe einer Bitmap in TImage
Anmerkung: Tun Sie das nicht während OnPaint.
<delphi>
with Image1.Picture.Bitmap do begin Width:=100; Height:=120; end;
</delphi>
Zeichnen auf eine Bitmap in TImage
Anmerkung: Tun Sie das nicht während OnPaint.
<delphi>
with Image1.Picture.Bitmap.Canvas do begin // fill the entire bitmap with red Brush.Color:=clRed; FillRect(0,0,Width,Height); end;
</delphi>
Anmerkung: Bei Image1.OnPaint zeigt Image1.Canvas auf den flüchtig sichtbaren Bereich. Außerhalb von Image1.OnPaint zeigt Image1.Canvas auf Image1.Picture.Bitmap.Canvas.
Ein anderes Beispiel:
<delphi>
procedure TForm1.BitBtn1Click(Sender: TObject); var x, y: Integer; begin // Zeichnet den Hintergrund MyImage.Canvas.Pen.Color := clWhite; MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height); // Zeichnet Rechtecke MyImage.Canvas.Pen.Color := clBlack; for x := 1 to 8 do for y := 1 to 8 do MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8), Round(x * Image.Width / 8), Round(y * Image.Height / 8)); end;
</delphi>
Im flüchtig sichtbaren Bereich einer TImage zeichnen
Sie können nur während des OnPaint-Ereignisses in diesen Bereich zeichnen. OnPaint wird eventuell automatisch von der LCL aufgerufen, wenn der Bereich invalidiert wurde. Sie können den Bereich mit Image1.Invalidate manuell invalidieren. Dies ruft OnPaint nicht unmittelbar auf und Sie können Invalidate so oft Sie wollen aufrufen.
<delphi>
procedure TForm.Image1Paint(Sender: TObject); begin with Image1.Canvas do begin // paint a line Pen.Color:=clRed; Line(0,0,Width,Height); end; end;
</delphi>
Zeichnen im OnPaint-Ereignis des Formulars, der TPaintBox oder einem anderen Control
In diesem Fall muss alles Zeichnen im OnPaint-Ereignis des Formulars passieren. Die Elemente bleiben nicht im buffer erhalten, wie bei TImage.
Ein CustomControl erzeugen, das sich selbst zeichnet
Die Erzeugung eines CustomControl hat den Vorteil der Strukturierung ihres Codes und sie können das Bedienelement wieder verwenden. Dieser Ansatz ist sehr schnell, aber er kann dennoch Flimmern erzeugen, wenn Sie nicht erst in ein TBitMap und dann in das Canvas zeichnen. In diesem Fall besteht keine Notwendigkeit, das OnPaint Ereignis des Bedienelements zu verwenden.
Hier ist ein Beispiel CustomControl:
<delphi>
uses Classes, SysUtils, Controls, Graphics, LCLType; type TMyDrawingControl = class(TCustomControl) public procedure Paint; override; end; implementation procedure TMyDrawingControl.Paint; var x, y: Integer; Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try // Initialisiert die Bitmap Größe Bitmap.Height := Height; Bitmap.Width := Width; // Zeichnet den Hintergrund Bitmap.Canvas.Pen.Color := clWhite; Bitmap.Canvas.Rectangle(0, 0, Width, Height); // Zeichnet Rechtecke Bitmap.Canvas.Pen.Color := clBlack; for x := 1 to 8 do for y := 1 to 8 do Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8), Round(x * Width / 8), Round(y * Height / 8)); Canvas.Draw(0, 0, Bitmap); finally Bitmap.Free; end; inherited Paint; end;
</delphi> und wie wir es auf dem Formular erzeugen: <delphi>
procedure TMyForm.FormCreate(Sender: TObject); begin MyDrawingControl:= TMyDrawingControl.Create(Self); MyDrawingControl.Height := 400; MyDrawingControl.Width := 500; MyDrawingControl.Top := 0; MyDrawingControl.Left := 0; MyDrawingControl.Parent := Self; MyDrawingControl.DoubleBuffered := True; end;
</delphi> nur nicht vergessen es zu löschen: <delphi>
procedure TMyForm.FormDestroy(Sender: TObject); begin MyDrawingControl.Free; end;
</delphi>
Das Setzen von Top und Left auf 0 ist nicht notwendig, da dies die Standardposition ist, aber es ist sinnvoll, um diese Position zu verdeutlichen.
"MyDrawingControl.Parent := Self;" ist sehr wichtig und Sie werden Ihr Bedienelement nicht sehen, wenn Sie es nicht tun.
"MyDrawingControl.DoubleBuffered := True;" wird benötigt, um das Flimmern unter Windows zu vermeiden. Es hat keinen Effekt unter gtk.
A.J. Venter's Gamepack benutzen
Der Gamepack-Vorstoß bedeutet, alles in ein doppelt gebuffertes Canvas zu zeichnen, dessen sichtbarer Bereich erst dann aktualisiert wird, wenn Sie fertig sind. Dies benötigt etwas mehr Code, aber es schafft die Fähigkeit, große, sich dynamisch ändernde Grafiken mit mehreren Elementen zu verarbeiten. Wenn Sie diese Technik verwenden möchten, sollten Sie sich das A.J. Venter's Gamepack ansehen, ein Set von Komponenten zur Entwicklung von Spielen mit Lazarus, das eine doppelt gebufferte Anzeigeflächen-Komponente sowie eine Sprite-Komponente bietet, designed um sich gegenseitig ideal zu integrieren. Sie können Gamepack mittels subversion erhalten:
svn co svn://silentcoder.co.za/lazarus/gamepack