Sign in Join
Wiki copytemporary

Developing with Graphics/fr: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Cette page décrit les classes de base et les techniques pour le dessin avec Lazarus. Les questions plus spécifiques sont traitées dans des articles séparés.
22
33## Libraries
44
55[[Graphics libraries]] - vous pouvez voir ici la liste des bibliothèques que vous pouvez utiliser pour développer.
66
77## Autres articles sur les graphiques
88
99### Dessin 2D
1010
1111- [[ZenGL/fr|ZenGL]] - Bibliothèque de développement de jeux multiplateformes utilisant OpenGL.
1212- [[BGRABitmap]] - Dessiner des formes et des images avec transparence, accéder directement aux pixels, etc.
1313- [[LazRGBGraphics]] - Un paquet pour de rapides manipulations de pixels et de traitement d'image en mémoire.
1414- [[Double Gradient]] - Dessiner un gradient double et des gradients multiples facilement.
1515- [[Gradient Filler]] - TGradientFiller permet de dessiner des gradients multiples avec Lazarus.
1616- [[PascalMagick]] - une API facile à utiliser pour s'interfacer avec [ImageMagick](http://www.imagemagick.org), une suite libre de logiciel multiplateforme pour créer, éditer, et composer des images bitmap.
1717
1818### 3D
1919
2020- [[GLScene/fr|GLScene]] - Portage de la librairie graphique visual OpenGL [GLScene](http://www.glscene.org).
2121- [[Castle Game Engine/fr|Castle Game Engine]] - Un moteur libre et multiplateforme pour des jeux 3D and 2D sous FPC/Lazarus ([site officiel](http://castle-engine.sourceforge.net/engine.php)).
2222
2323### Diagrammes
2424
2525- [[TAChart]] - Composant de diagrammes pour Lazarus
2626- [[PlotPanel]] - Un composant de tracé de diagrammes pour des graphes animés
2727- [[Perlin Noise/fr|Bruit de Perlin]] - Article concernant l'emploi de bruit de Perlin
2828
2929## Travailler avec TBitmap
3030
3131Sur certains systèmes, les données des images ne sont pas stockées dans la mémoire locale si bien qu'il n'est pas possible d'y accéder directement. Comme Lazarus est fait pour être indépendant de la plateforme, la classe *TBitmap* ne fournit pas de propriété comme *Scanline*. Ce serait possible, mais cela nécessiterait une conversion implicite de l'image. Il y a une fonction *GetDataLineStart*, équivalente à *Scanline*, mais disponible uniquement pour les images en mémoire comme *TLazIntfImage* qui utilise en interne un objet *TRawImage*.
3232
3333En résumé, on ne peut changer une image qu'indirectement, en modifiant une image en mémoire, et ensuite en la convertissant en une image dessinable. Ce processus est bien entendu plus lent. Il y a des librairies qui permettent d'accéder directement à des images, mais elles peuvent utiliser implicitement une conversion lorsqu'on les dessine (voir [[BGRABitmap/fr|BGRABitmap]], [[LazRGBGraphics/fr|LazRGBGraphics]] et peut-être un jour [[Current conversion projects/fr|Graphics32]]).
3434
3535Note : quand vous créez une image, vous devez spécifier une hauteur et une largeur, sinon sa taille sera égale à zéro et rien ne sera dessiné.
3636
3737### Un exemple de fading
3838
3939Voilà un exemple où l'on fait apparaître progressivement une image (*fade in*). Sous Delphi, vous pouvez le faire ainsi :
4040
4141``` pascal
4242procedure TForm1.FadeIn(ABitmap: TBitmap);
4343Const NbSteps = 32; //pour simplifier l'instruction 'div'
4444var
4545 FadeBmp, CopyBmp: TBitmap;
4646 PFade, PCopy: PRGBTriple;
4747 x, y: integer;
4848 Step: Longword;
4949begin
5050 //faire une copie 24-bit de l'image à faire apparaître
5151 CopyBmp := TBitmap.Create;
5252 CopyBmp.PixelFormat := pf24bit;
5353 CopyBmp.Width := ABitmap.Width;
5454 CopyBmp.Height := ABitmap.Height;
5555 CopyBmp.Canvas.Draw(0,0,ABitmap);
5656
5757 //créer une image pour le fading
5858 FadeBmp := TBitmap.Create;
5959 FadeBmp.PixelFormat := pf24bit;
6060 FadeBmp.Width := ABitmap.Width;
6161 FadeBmp.Height := ABitmap.Height;
6262
6363 for step := 0 to NbSteps do //boucle principale
6464 begin
6565 for y := 0 to FadeBmp.Height - 1 do
6666 begin
6767 PCopy := CopyBmp.Scanline[y];
6868 PFade := FadeBmp.Scanline[y];
6969 for x := 0 to FadeBmp.Width - 1 do
7070 begin
7171 PFade^.rgbtRed := (step * PCopy^.rgbtRed) div NbSteps;
7272 PFade^.rgbtGreen := (step * PCopy^.rgbtGreen) div NbSteps; // calcul de fading
7373 PFade^.rgbtBlue := (step * PCopy^.rgbtBlue) div NbSteps;
7474 inc(PCopy); // aller au pixel suivant
7575 inc(PFade);
7676 end;
7777 end;
7878 Form1.Canvas.Draw(0, 0, FadeBmp);
7979 Sleep(16); // attendre un cycle d'écran
8080 Application.ProcessMessages; // gérer les messages Windows
8181 end;
8282
8383 FadeBmp.Free;
8484 CopyBmp.Free;
8585end;
8686```
8787
8888Cette fonction peut être implémentée comme suit avec les bibliothèques incluses dans Lazarus :
8989
9090``` pascal
9191{ Ce code a été copié du projet à l'emplacement $LazarusPath/examples/lazintfimage/fadein1.lpi }
9292uses LCLType, // HBitmap type
9393 IntfGraphics, // TLazIntfImage type
9494 fpImage; // TFPColor type
9595...
9696 procedure TForm1.FadeIn(ABitMap: TBitMap);
9797 var
9898 SrcIntfImg, TempIntfImg: TLazIntfImage;
9999 ImgHandle,ImgMaskHandle: HBitmap;
100100 FadeStep: Integer;
101101 px, py: Integer;
102102 CurColor: TFPColor;
103103 TempBitmap: TBitmap;
104104 begin
105105 //crée une copie de l'image dans un objet TLazIntfImage
106106 SrcIntfImg:=TLazIntfImage.Create(0,0);
107107 SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
108108 //crée une image avec les mêmes dimensions pour faire le fading
109109 TempIntfImg:=TLazIntfImage.Create(0,0);
110110 TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
111111
112112 //crée un objet Bitmap de destination pour l'affichage
113113 TempBitmap:=TBitmap.Create;
114114 for FadeStep:=1 to 32 do begin //boucle principale
115115 for py:=0 to SrcIntfImg.Height-1 do begin
116116 for px:=0 to SrcIntfImg.Width-1 do begin
117117 CurColor:=SrcIntfImg.Colors[px,py]; //lire la couleur source
118118 CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
119119 CurColor.Green:=(CurColor.Green*FadeStep) shr 5; //calcul de fading
120120 CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
121121 TempIntfImg.Colors[px,py]:=CurColor;
122122 end;
123123 end;
124124 //création des handles pour l'image temporaire
125125 TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
126126 TempBitmap.Handle:=ImgHandle;
127127 TempBitmap.MaskHandle:=ImgMaskHandle;
128128 Canvas.Draw(0,0,TempBitmap); //dessin de l'image
129129 end;
130130 SrcIntfImg.Free;
131131 TempIntfImg.Free;
132132 TempBitmap.Free;
133133 end;
134134```
135135
136136En utilisant BGRABitmap, le fading peut être implémenté comme suit :
137137
138138``` pascal
139139procedure TForm1.FadeIn3(ABitmap: TBitmap);
140140Const NbSteps = 32;
141141var
142142 CopyBmp,FadeBmp: TBGRABitmap;
143143 PFade, PSource: PBGRAPixel;
144144 n, step: Longword;
145145begin
146146 //crée une copie de l'image avec accès direct aux pixels
147147 CopyBmp := TBGRABitmap.Create(ABitmap.Canvas);
148148
149149 //crée une image pour le fading
150150 FadeBmp := TBGRABitmap.Create(ABitmap.Width,ABitmap.Height);
151151
152152 for step := 0 to NbSteps do //boucle principale
153153 begin
154154 PSource := CopyBmp.Data;
155155 PFade := FadeBmp.Data;
156156 for n := 0 to FadeBmp.NbPixels - 1 do
157157 begin
158158 PFade^.red := (step * PSource^.red) div NbSteps;
159159 PFade^.green := (step * PSource^.green) div NbSteps; // calcul de fading
160160 PFade^.blue := (step * PSource^.blue) div NbSteps;
161161 inc(PSource); // aller au pixel suivant
162162 inc(PFade);
163163 end;
164164 FadeBmp.InvalidateBitmap; // note qu'on a changé par accès direct
165165 FadeBmp.Draw(Form1.Canvas,0,0,true); // affiche l'image calculée en mode opaque
166166 Sleep(16); // attendre un cycle d'écran
167167 Application.ProcessMessages; // gérer les événements
168168 end;
169169
170170 FadeBmp.Free;
171171 CopyBmp.Free;
172172end;
173173```
174174
175175### Tracer des bitmaps avec une couleur transparente
176176
177177Un nouveau dispositif, implémenté avec Lazarus 0.9.11, réside dans les bitmaps en couleurs 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 une astuce courante utilisée dans les applications Win32.
178178
179179L'exemple suivant charge un Bitmap à partir d'une ressource Windows, choisit une couleur pour être transparente (*clFuchsia*) et le dessine ensuite sur le canevas.
180180
181181``` pascal
182182procedure MyForm.MyButtonOnClick(Sender: TObject);
183183var
184184 buffer: THandle;
185185 bmp: TBitmap;
186186 memstream: TMemoryStream;
187187begin
188188 bmp := TBitmap.Create;
189189
190190 buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
191191
192192 if (buffer = 0) then exit; // Error loading the bitmap
193193
194194 bmp.Handle := buffer;
195195 memstream := TMemoryStream.create;
196196 try
197197 bmp.SaveToStream(memstream);
198198 memstream.position := 0;
199199 bmp.LoadFromStream(memstream);
200200 finally
201201 memstream.free;
202202 end;
203203
204204 bmp.Transparent := True;
205205 bmp.TransparentColor := clFuchsia;
206206
207207 MyCanvas.Draw(0, 0, bmp);
208208
209209 bmp.Free; // Release allocated resource
210210end;
211211```
212212
213213Notez les opérations de mémoire effectuées avec [[doc:rtl/classes/tmemorystream.html|TMemoryStream]]. Elles sont nécessaires pour assurer le chargement correct de l'image.
214214
215215### Faire une capture d'image de l'écran
216216
217217Depuis Lazarus 0.9.16, vous pouvez employer la LCL pour faire des captures d'images de l'écran multiplateforme. Le code d'exemple suivant le fait (fonctionne avec gtk2 et win32, mais non gtk1 actuellement) :
218218
219219``` pascal
220220 uses LCLIntf, LCLType;
221221
222222 ...
223223
224224var
225225 MyBitmap: TBitmap
226226 ScreenDC: HDC;
227227begin
228228 MyBitmap := TBitmap.Create;
229229 ScreenDC := GetDC(0);
230230 MyBitmap.LoadFromDevice(ScreenDC);
231231 ReleaseDC(ScreenDC);
232232
233233 ...
234234```
235235
236236## Graphiques en mouvement - Comment éviter le clignotement
237237
238238Beaucoup de programmes dessinent leur sortie sur le GUI sous forme de graphiques 2D. Si ces graphiques ont besoin de changer rapidement vous ferez face bientôt à un problème : les graphiques changeant rapidement clignotent souvent sur l'écran. Ceci se produit quand les utilisateurs voient parfois des images entières et parfois seulement quand c'est dessiné partiellement. Cela se produit parce que le procédé d'affichage a besoin de temps.
239239
240240Mais comment puis je éviter le clignotement et obtenir la meilleure vitesse de dessin ? Naturellement vous pourriez travailler avec l'accélération matérielle en utilisant OpenGL, mais cette approche est tout à fait lourde pour de petits programmes ou de vieux ordinateurs. Ce tutoriel se concentrera sur la façon de dessiner sur un TCanvas. Si vous avez besoin d'aide avec OpenGL, jeter un coup d'oeil sur l'exemple qui vient avec Lazarus. Vous pouvez également employer le gamepack d'A.J. Venter, qui fourni un canvas à double-tampon et un composant de sprite.
241241
242242Maintenant nous examinerons les options que nous avons pour dessiner un canvas :
243243
244244- Dessiner sur un Timage
245245- Dessiner sur l'evènement OnPaint de la fiche, un contrôle TPaintBox ou un autre
246246- Créer un contrôle à soi qui se dessine lui-même
247247- Employer le gamepack d'A.J. Venter
248248
249249### Dessiner sur un Timage
250250
251251Un TImage se compose en 2 parts : un TGraphic, habituellement un TBitmap, contenant l'image persistante et l'aire visuelle, laquelle est peinte à chaque OnPaint. Redimensionner le TImage **ne** redimensionne pas le bitmap. Le graphique (ou bitmap) est accessible par l'intermédiaire de Image1.Picture.Graphic (ou Image1.Picture.Bitmap). La canvas est Image1.Picture.Bitmap.Canvas. Le canvas de l'aire visuelle d'une TImage est seulement accessible pendant Image1.OnPaint via Image1.Canvas.
252252
253253**Important** : Ne jamais employer le OnPaint de l'évènement Image1 pour dessiner le graphique/bitmap d'une TImage. Le graphique d'une TImage est en mémoire tampon aussi tout que vous devez faire est de dessiner vers elle de n'importe où et le changement est là pour toujours. Cependant, si vous redessinez constamment, l'image clignotera. Dans ce cas-ci vous pouvez essayer les autres options. Dessin sur une TImage est considéré comme étant plus lent que les autres approches.
254254
255255#### Redimensionner le bitmap d'un TImage
256256
257257Note : Ne pas employer ceci pendant OnPaint.
258258
259259` with Image1.Picture.Bitmap do begin`\
260260`   Width:=100;`\
261261`   Height:=120;`\
262262` end;`
263263
264264#### Peindre sur le bitmap d'un TImage
265265
266266Note: Ne pas employer ceci pendant OnPaint.
267267
268268` with Image1.Picture.Bitmap.Canvas do begin`\
269269`   // fill the entire bitmap with red`\
270270`   Brush.Color:=clRed;`\
271271`   FillRect(0,0,Width,Height);`\
272272` end;`
273273
274274Note : À l'intérieur de Image1.OnPaint Image1.Canvas pointe vers l'aire visible volatile. En dehors de Image1.OnPaint l'Image1.Canvas pointe vers Image1.Picture.Bitmap.Canvas.
275275
276276Un autre exemple :
277277
278-<code>
279-
280278`procedure TForm1.BitBtn1Click(Sender: TObject);`\
281279`var`\
282280`  x, y: Integer;`\
283281`begin`\
284282`  // Draws the backgroung`\
285283`  MyImage.Canvas.Pen.Color := clWhite;`\
286284`  MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);`\
287285`  `\
288286`  // Draws squares`\
289287`  MyImage.Canvas.Pen.Color := clBlack;`\
290288`  for x := 1 to 8 do`\
291289`   for y := 1 to 8 do`\
292290`    MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),`\
293291`       Round(x * Image.Width / 8), Round(y * Image.Height / 8));`\
294292`end;`
295293
296-</code>
297-
298294#### Peindre sur l'aire visuelle volatile de TImage
299295
300296Vous pouvez seulement peindre sur ce secteur pendant OnPaint. OnPaint est éventuellement appellé automatiquement par la LCL quand le secteur a été invalidé. Vous pouvez invalidé manuellement le secteur avec Image1.Invalidate. Ceci n'appellera pas immédiatement OnPaint et vous pouvez appeler Invalidate autant de fois que vous le voulez.
301297
302298` procedure TForm.Image1Paint(Sender: TObject);`\
303299` begin`\
304300`   with Image1.Canvas do begin`\
305301`     // paint a line`\
306302`     Pen.Color:=clRed;`\
307303`     Line(0,0,Width,Height);`\
308304`   end;`\
309305` end;`
310306
311307### Dessiner sur l'évênement OnPaint
312308
313309Dans ce cas tout le dessin doit être fait sur l'évênement OnPaint de la fiche. Il ne reste pas sur le tampon, comme sur la TImage.
314310
315311### Créer un contrôle à soi qui se dessine lui-même
316312
317313Créer un contrôle à soi a l'avantage de structurer votre code, et vous pouvez réutiliser le contrôle. Cette approche est très rapide, mais elle peut toujours produire du clignotement si vous ne dessinez pas sur un TBitmap en premier et dessinez ensuite sur le canvas. Dans ce cas, il n'est pas besoin d'employer l'évènement OnPaint du contrôle.
318314
319315Voici un exemple de contrôle personnalisé:
320316
321-<code>
322-
323317`uses`\
324318` Classes, SysUtils, Controls, Graphics, LCLType;`\
325-\
319+
326320`type`\
327321`  TMyDrawingControl = class(TCustomControl)`\
328322`  public`\
329323`    procedure EraseBackground(DC: HDC); override;`\
330324`    procedure Paint; override;`\
331325`  end;`\
332-\
326+
333327`implementation`\
334-\
328+
335329`procedure TMyDrawingControl.EraseBackground(DC: HDC);`\
336330`begin`\
337331`  // Uncomment this to enable default background erasing`\
338332`  //inherited EraseBackground(DC);`\
339333`end; `\
340-\
334+
341335`procedure TMyDrawingControl.Paint;`\
342336`var`\
343337`  x, y: Integer;`\
344338`  Bitmap: TBitmap;`\
345339`begin`\
346340`  Bitmap := TBitmap.Create;`\
347341`  try`\
348342`    // Initializes the Bitmap Size`\
349343`    Bitmap.Height := Height;`\
350344`    Bitmap.Width := Width;`\
351345` `\
352346`    // Draws the background`\
353347`    Bitmap.Canvas.Pen.Color := clWhite;`\
354348`    Bitmap.Canvas.Rectangle(0, 0, Width, Height);`\
355-\
349+
356350`    // Draws squares`\
357351`    Bitmap.Canvas.Pen.Color := clBlack;`\
358352`    for x := 1 to 8 do`\
359353`     for y := 1 to 8 do`\
360354`      Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),`\
361355`       Round(x * Width / 8), Round(y * Height / 8));`\
362356`      `\
363357`    Canvas.Draw(0, 0, Bitmap);`\
364358`  finally`\
365359`    Bitmap.Free;`\
366360`  end;`\
367-\
361+
368362`  inherited Paint;`\
369363`end;`
370364
371-</code> ...et la manière de le créer dans la fiche. <code>
365+...et la manière de le créer dans la fiche.
372366
373367`procedure TMyForm.FormCreate(Sender: TObject);`\
374368`begin`\
375369`  MyDrawingControl:= TMyDrawingControl.Create(Self);`\
376370`  MyDrawingControl.Height := 400;`\
377371`  MyDrawingControl.Width := 500;`\
378372`  MyDrawingControl.Top := 0;`\
379373`  MyDrawingControl.Left := 0;`\
380374`  MyDrawingControl.Parent := Self;`\
381375`  MyDrawingControl.DoubleBuffered := True;`\
382376`end;`
383377
384-</code> N'oubliez pas de le détruire : <code>
378+N'oubliez pas de le détruire :
385379
386380`procedure TMyForm.FormDestroy(Sender: TObject);`\
387381`begin`\
388382`  MyDrawingControl.Free;`\
389383`end;`
390-
391-</code>
392384
393385Paramétrer Top et Left à zéro n'est pas nécessaire, puisque c'est la position standard, mais est fait pour renforcer l'endroit où le contrôle sera mis.
394386
395387"MyDrawingControl.Parent := Self;" est très important et vous ne verrez pas votre contrôle si vous ne faites pas ainsi.
396388
397389"MyDrawingControl.DoubleBuffered := True;" est nécessaire pour éviter le clignotement sur Windows. Il n'a aucun effet sur gtk.
398390
399391### Employer le gamepack d'A.J. Venter
400392
401393L'approche par gamepack est de tout dessiner sur un canvas à double-tampon, lequel se met seulement à jour sur le canvas visible quand vous êtes prêt. Ceci demande beaucoup de code, mais il a l'avantage de pouvoir faire de grandes scènes avec de multiples sprites changeant rapidement. Si vous souhaitez employer cette approche, vous pouvez être intéressés par le A.J. Venter's gamepack, un ensemble de composants pour le développement de jeu dans Lazarus, qui fournit un composant avec une zone de visualisation avec double-tampon ainsi qu'un composant de sprite, conçu pour bien s'intégrer entre eux. Vous pouvez obtenir gamepack via SVN :\
402394`svn co `[`svn://silentcoder.co.za/lazarus/gamepack`](svn://silentcoder.co.za/lazarus/gamepack)
403395
404396---
405397*From the Free Pascal wiki, page "Developing with Graphics/fr", as it stood on 2020-02-13 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history