Developing with Graphics/fr
│
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) │
Cette page sera un lancement de tutoriels concernant la manipulation d'images et autres graphiques. Car je ne suis pas un programmeur en graphisme , j'invite tous ceux qui sont prêt à partager leur compétence! Ajouter juste un lien à la prochaine section , ajouter une page et créer votre propre article WiKi .
Sur cette page quelques informations générales seront fournies .
D'autres articles sur le graphisme
- GLScene - Un portage de la librairie graphique visual OpenGL GLScene
- TAChart - composant de diagrammes pour Lazarus
- PascalMagick - une API facile à utiliser pour s'interfacer avec ImageMagick, une suite libre de logiciel multiplateforme pour créer , éditer, et composer des images bitmap.
- PlotPanel - un composant de traçage et de tracage de diagrammes pour des graphes animés
- LazRGBGraphics - Un paquet pour de rapides manipulations de pixels et de traitement d'image en mémoire (comme le balayage de ligne).
- bruit de Perlin - Un article concernant l'emploi de bruit de Perlin avec les applications LCL.
Travailler avec TBitmap
La première chose à se rappeler est que Lazarus est censé être indépendant de la plateforme, ainsi toutes méthodes utilisant la fonctionnalité de l'API Windows sont inadmissibles. Ainsi une méthode comme ScanLine n'est pas supportée par Lazarus parce qu'il est destiné à être indépendant du dispositif de bitmap et utilise des fonctions de la bibliothèque GDI32.dll.
Considérer que si vous n'indiquez pas la taille et la largeur du votre TBitmap il aura le standard , lequel est tout à fait petit .
Un exemple de fading
Supposez que vous voulez faire disparaître(fading) une image . Dans Delphi vous pourriez faire quelque chose comme :
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; // or 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;
Cette fonction dans Lazarus pourrait être implémentée comme:
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;
Le code Lazarus sur cette page a été pris du projet $LazarusPath/examples/lazintfimage/fadein1.lpi. Ainsi si vous voulez une initiation avec la programmation de graphiques jetez un coup d'oeil plus étroit sur cet exemple .
Tracer des bitmaps en couleurs transparentes
Un nouveau dispositif , implémenté avec Lazarus 0.9.11, sont les bitmap en couleur transparentes. les fichiers Bitmap (*.BMP)ne peuvent stocker aucune information sur la transparence , mais ils peuvent fonctionner comme s'ils en avaient si vous choisissez une couleur sur eux pour représenter le secteur transparent . C'est un tour courant utilisé dans les applications Win32.
L'exemple suivant charge un Bitmap à partir d'une ressource Windows, choisit une couleur pour être transparente (clFuchsia) et le dessine ensuite sur le canvas.
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; // Error loading the 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; // Release allocated resource end;
Noter les opérations de mémoire effectuées avec TMemoryStream. Elles sont nécessaires pour assurer le chargement correct de l'image .
Taking a screenshot of the screen
Since Lazarus 0.9.16 you can use LCL to take screenshots of the screen on a cross-platform way. The following example code does it (works on gtk2 and win32, but not gtk1 currently):
uses LCLIntf, LCLType; ... var MyBitmap: TBitmap ScreenDC: HDC; begin MyBitmap := TBitmap.Create; ScreenDC := GetDC(0); MyBitmap.LoadFromDevice(ScreenDC); ReleaseDC(ScreenDC); ...
Motion Graphics - How to Avoid flickering
Many programs draw their output to the GUI as 2D graphics. If those graphics need to change quickly you will soon face a problem: quickly changing graphics often flicker on the screen. This happens when users sometimes sees the whole images and sometimes only when it is partially drawn. It occurs because the painting process requires time.
But how can I avoid the flickering and get the best drawing speed? Of course you could work with hardware acceleration using OpenGL, but this approach is quite heavy for small programs or old computers. This tutorial will focus on drawing to a TCanvas. If you need help with OpenGL, take a look at the example that comes with Lazarus. You can also use A.J. Venter's gamepack, which provides a double-buffered canvas and a sprite component.
Now we will examine the options we have for drawing to a Canvas:
- Draw to a TImage
- Draw on the OnPaint event of the form, a TPaintBox or another control
- Create a custom control which draws itself
- Using A.J. Venter's gamepack
Draw to a TImage
A TImage consists of 2 parts: A TGraphic, usually a TBitmap, holding the persistent picture and the visual area, which is repainted on every OnPaint. Resizing the TImage does not resize the bitmap. The graphic (or bitmap) is accessible via Image1.Picture.Graphic (or Image1.Picture.Bitmap). The canvas is Image1.Picture.Bitmap.Canvas. The canvas of the visual area of a TImage is only accessible during Image1.OnPaint via Image1.Canvas.
Important: Never use the OnPaint of the Image1 event to draw to the graphic/bitmap of a TImage. The graphic of a TImage is buffered so all you need to do is draw to it from anywhere and the change is there forever. However, if you are constantly redrawing, the image will flicker. In this case you can try the other options. Drawing to a TImage is considered slower then the other approaches.
Resizing the bitmap of a TImage
Note: Do not use this during OnPaint.
with Image1.Picture.Bitmap do begin Width:=100; Height:=120; end;
Painting on the bitmap of a TImage
Note: Do not use this during OnPaint.
with Image1.Picture.Bitmap.Canvas do begin // fill the entire bitmap with red Brush.Color:=clRed; FillRect(0,0,Width,Height); end;
Note: Inside of Image1.OnPaint the Image1.Canvas points to the volatile visible area. Outside of Image1.OnPaint the Image1.Canvas points to Image1.Picture.Bitmap.Canvas.
Another example:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
x, y: Integer;
begin
// Draws the backgroung
MyImage.Canvas.Pen.Color := clWhite;
MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
// Draws squares
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;
Painting on the volatile visual area of the TImage
You can only paint on this area during OnPaint. OnPaint is eventually called automatically by the LCL when the area was invalidated. You can invalidate manually the area with Image1.Invalidate. This will not call immediately OnPaint and you can call Invalidate as mny times as you want.
procedure TForm.Image1Paint(Sender: TObject); begin with Image1.Canvas do begin // paint a line Pen.Color:=clRed; Line(0,0,Width,Height); end; end;
Draw on the OnPaint event
In this case all the drawing has to be done on the OnPaint event of the form. It doesn't remain on the buffer, like on the TImage.
Create a custom control which draws itself
Creating a custom control has the advantage of structuring your code and you can reuse the control. This approach is very fast, but it can still generate flickering if you don't draw to a TBitmap first and then draw to the canvas. On this case there is no need to use the OnPaint event of the control.
Here is an example custom control:
uses
Classes, SysUtils, Controls, Graphics, LCLType;
type
TMyDrawingControl = class(TCustomControl)
public
procedure EraseBackground(DC: HDC); override;
procedure Paint; override;
end;
implementation
procedure TMyDrawingControl.EraseBackground(DC: HDC);
begin
// Uncomment this to enable default background erasing
//inherited EraseBackground(DC);
end;
procedure TMyDrawingControl.Paint;
var
x, y: Integer;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
// Initializes the Bitmap Size
Bitmap.Height := Height;
Bitmap.Width := Width;
// Draws the background
Bitmap.Canvas.Pen.Color := clWhite;
Bitmap.Canvas.Rectangle(0, 0, Width, Height);
// Draws squares
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;
and how we create it on the form:
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;
just don´t forget to destroy it:
procedure TMyForm.FormDestroy(Sender: TObject);
begin
MyDrawingControl.Free;
end;
Setting Top and Left to zero is not necessary, since this is the standard position, but is done so to reinforce where the control will be put.
"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
Using A.J. Venter's gamepack
The gamepack approach is to draw everything to one double-buffered canvas, which only gets updated to the visible canvas when you are ready. This takes quite a bit of code, but it has the advantage of being able to do large rapidly changing scenes with multiple sprites on them. If you wish to use this approach, you may be interested in A.J. Venter's gamepack, a set of components for game development in Lazarus, which provides a double-buffered display area component as well as a sprite component, designed to integrate well with one another. You can get gamepack via subversion:
svn co svn://silentcoder.co.za/lazarus/gamepack