Sign in Join
Wiki copytemporary

Developing with Graphics/it: what changed

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

Title: Developing with Graphics/it

OldNewLine
1+Questa pagina descrive le classi e le tecniche di base riguardanti l'uso della grafica in Lazarus. Approfondimenti sono trattati in articoli più specifici.
2+
3+## Librerie
4+
5+[[Librerie grafiche]] - qui si possono osservare le principali librerie utilizzabili per lo sviluppo.
6+
7+### Altri articoli di grafica
8+
9+### Disegno 2D
10+
11+- [[ZenGL]] - libreria multi-piattaforma per lo sviluppo di giochi mediante OpenGL.
12+- [[BGRABitmap]] - Disegno di forme e bitmap con funzioni di trasparenza, accesso diretto ai pixel, ecc.
13+- [[LazRGBGraphics]] - Package per l'elaborazione veloce di immagini in memoria e per la manipolazione a livello di pixel (es. scan line).
14+- [[fpvectorial]] - Supporto per leggere, modificare e salvare immagini vettoriali.
15+- [[Double Gradient]] - Disegnare facilmente bitmap con 'doppio gradiente' e gradienti di 'grado n'.
16+- [[Gradient Filler]] - TGradientFiller é il miglior modo di creare gradienti di grado n personalizzati in Lazarus.
17+- [[PascalMagick]] - una API di semplice uso per interfacciarsi con [ImageMagick](http://www.imagemagick.org), una suite multi-piattaforma free per creare, editare e comporre immagini bitmap.
18+- [[Sample Graphics]] - collezioni di grafica create con Lazarus e strumenti di disegno.
19+- [[Fast direct pixel access]] - confronto di velocità di alcuni metodi di accesso diretto ai pixel di immagini bitmap.
20+- [AggPas](http://www.crossgl.com/aggpas/) - AggPas è la traduzione in Object Pascal nativo port della libreria Anti-Grain Geometry. Veloce e potente, con funzioni di disegno anti-aliased ed accuratezza al subpixel. Si può pensare ad AggPas come ad un motore di rendering che produce immagini in pixel, in memoria, a partire da dati vettoriali.
21+
22+### Disegno 3D
23+
24+- [[GLScene]] - Porting della Libreria visuale 3D di OpenGL [GLScene](http://www.glscene.org)
25+- [[Castle Game Engine]] - Un motore 3D e 2D multi-piattaforma per giochi FPC/Lazarus ([sito ufficiale](http://castle-engine.sourceforge.net/engine.php))
26+
27+### Grafici
28+
29+- [[TAChart]] - Componente per tracciamento di grafici in Lazarus
30+- [[PlotPanel]] - Componente per disegnare e tracciare grafici con effetti di animazione
31+- [[Perlin Noise]] - Un articolo sull'uso di Perlin Noise in applicazioni LCL.
32+
33+### Introduzione al modello Grafico della LCL
34+
35+La Lazarus Component Library (LCL) fornisce due tipi di classi di disegno: native e non-native. Le classi native sono il modo più tradizionale per implementare grafica nella LCL e sono le più importanti; le classi non native possono essere considerate come complementari. Le classi native si trovano, per la maggior parte, nell'unità **Graphics** della LCL. Esse sono: [[TBitmap]], [[TCanvas]], [[TFont]], [[TBrush]], [[TPen]], [[TPortableNetworkGraphic]], etc.
36+
37+TCanvas è una classe in grado di eseguire disegni. Essa non può esistere da sola, bensì deve essere collegata a qualcosa di visibile (o almeno potenzialmente visibile), come un controllo visuale discendente da TControl, oppure deve essere collegata ad un buffer off-screen discendente da TRasterImage (TBitmap è quello usato più comunemente). TFont, TBrush and TPen descrivono come le varie operazioni di disegno saranno eseguite nel Canvas.
38+
39+TRasterImage (usualmente utilizzata per via del suo discendente TBitmap) è un'area di memoria riservata per la tracciatura di disegni, ma è creata per assicurare la massima compatibilità con il Canvas nativo; pertanto in LCL-Gtk2 in X11 si trova localizzato nel server X11, il che rende l'accesso ai pixel tramite la proprietà Pixels estremamente lento. In Windows al contrario è molto veloce poiché Windows permette la creazione di immagine allocata localmente, che può ricevere disegni da un Canvas di Windows.
40+
41+Oltre a queste vi sono pure classi non native localizzate nelle unità graphtype (TRawImage), intfgraphics (TLazIntfImage) e lazcanvas (TLazCanvas, esiste in Lazarus 0.9.31+). [[TRawImage]] è l'archivio e la descrizione di un'area di memoria che contiene un'immagine. [[TLazIntfImage]] è un'immagine agganciata a TRawImage che si incarica della conversione tra TFPColor e l'effetivo formato del pixel di TRawImage. [[TLazCanvas]] è un Canvas non-nativo che può disegnare un'immagine in un oggetto TLazIntfImage.
42+
43+La principale differenza tra classi native e non native sta nel fatto che le prime possono avere una resa diversa su piattaforme diverse, dato che il disegno viene eseguito in ultima istanza dalla piattaforma sottostante. In particolare la velocitá di resa oltreché l'esatto aspetto finale delle immagini disegnate possono presentare differenze. Le classi non-native garantiscono invece risultati eguali e prestazioni accettabili su tutte le piattaforme.
44+
45+Nel widgeset LCL-CustomDrawn le classi native sono implementate usando le non-native.
46+
47+Tutte queste classi saranno meglio descritte nelle sezioni seguenti.
48+
49+### Lavorare con TCanvas
50+
51+## Lavorare con TBitmap e altri discendenti di TGraphic
52+
53+L'oggetto TBitmap gestisce una bitmap dove è possibile disegnare prima di riprodurre sullo schermo. Creando una bitmap bisogna specificarne altezza e larghezza, altrimenti essa rimarrà vuota nulla verrà riprodotto. In generale i discendenti di TRasterImage forniscono funzionalità simili, ma le prestazioni possono essere diverse per le operazioni di I/O su disco.
54+
55+## Caricare/Salvare un'immagine da/su disco
56+
57+Per caricare un'immagine da disco usare [TGraphic.LoadFromFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.loadfromfile.html) e per salvarla in un altro file usare [TGraphic.SaveToFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.savetofile.html). Tra le classi discendenti da TGraphic usare la classe appropriata per il formato di file. Vedere [[Developing with Graphics|Developing_with_Graphics#Image_formats]] per una lista delle classi disponibili per i vari formati.
58+
59+``` pascal
60+var
61+ MyBitmap: TBitmap;
62+begin
63+ MyBitmap := TBitmap.Create;
64+ try
65+ // Load from disk
66+ MyBitmap.LoadFromFile(MyEdit.Text);
67+
68+ // Here you can use MyBitmap.Canvas to read/write to/from the image
69+
70+ // Write back to another disk file
71+ MyBitmap.SaveToFile(MyEdit2.Text);
72+ finally
73+ MyBitmap.Free;
74+ end;
75+end;
76+```
77+
78+Per altri formati la procedura è completamente identica, usando la classe appropriata. Ad esempio per immagini PNG:
79+
80+``` pascal
81+var
82+ MyPNG: TPortableNetworkGraphic;
83+begin
84+ MyPNG := TPortableNetworkGraphic.Create;
85+ try
86+ // Load from disk
87+ MyPNG.LoadFromFile(MyEdit.Text);
88+
89+ // Here you can use MyPNG.Canvas to read/write to/from the image
90+
91+ // Write back to another disk file
92+ MyPNG.SaveToFile(MyEdit2.Text);
93+ finally
94+ MyPNG.Free;
95+ end;
96+end;
97+```
98+
99+Se il formato dell'immagine non è noto, usare TPicture che determinerà il formato in base all'estensione del file. Si noti che TPicture non supporta tutti i formati supportati da Lazarus: alla versione 0.9.31 TPicture supporta BMP, PNG, JPEG, Pixmap and PNM mentre Lazarus supporta anche ICNS e altri formati:
100+
101+``` pascal
102+var
103+ MyPicture: TPicture;
104+begin
105+ MyPicture := TPicture.Create;
106+ try
107+ // Load from disk
108+ MyPicture.LoadFromFile(MyEdit.Text);
109+
110+ // Here you can use MyPicture.Graphic.Canvas to read/write to/from the image
111+
112+ // Write back to another disk file
113+ MyPicture.SaveToFile(MyEdit2.Text);
114+ finally
115+ MyPicture.Free;
116+ end;
117+end;
118+```
119+
120+### File format addizionali per TImage
121+
122+Si può avere supporto per formati addizionali aggiungendo le unità [[fcl-image]] fpread* e/o fpwrite* nella dichiarazione uses. In tal modo si aggiunge il supporto TIFF per TImage
123+
124+### Accesso diretto ai pixel
125+
126+Per accedere direttamente ai pixel di un bitmap si può usare sia una libreria esterna, come [[BGRABitmap]], [[LazRGBGraphics]] e [[Current conversion projects|Graphics32]], oppure usare la classe nativa di Lazarus TLazIntfImage. Per un confronto dei vari metodi vedere [[Fast direct pixel access|fast direct pixel access]].
127+
128+In alcuni widgetsets (specificamente LCL-Gtk2), i dati bitmap non sono posti in un'area di memoria accessibile dall'applicazione e in generale le interfacce native della LCL disegnano per mezzo di routine che operano sul Canvas, cosicché ogni operazione SetPixel / GetPixel implica una chiamata lenta alle Canvas-API. Questo non succede per i controlli della libreria LCL CustomDrawn, poiché la bitmap è memorizzata localmente per tutti i backend e le istruzioni SetPixel / GetPixel sono veloci. Per avere una soluzione valida per tutti i widgetset si dovrebbe usare TLazIntfImage.
129+
130+As Lazarus is meant to be platform independent and work in gtk2, the TBitmap class does not provide a property like Scanline. There is a GetDataLineStart function, equivalent to Scanline, but only available for memory images like [[Developing with Graphics|TLazIntfImage]] which internally uses TRawImage.
131+
132+To sum it up, with the standard TBitmap, you can only change pixels indirectly, by using TCanvas.Pixels. Calling a native API to draw / read an individual pixel is course slower than direct pixel access, notably so in LCL-gtk2 and LCL-Carbon.
133+
134+Note: what about this bug report: <http://bugs.freepascal.org/view.php?id=1958> with comment: I tested with trunk on Qt. ScanLine is here and works. It has to be used like this: Bitmap.BeginUpdate; //do some ScanLine job Bitmap.EndUpdate; **PLEASE REVISE THIS SECTION!**
135+
136+### Disegnare bitmap trasparenti a colori
137+
138+Una nuova caratteristica, implementata in Lazarus 0.9.11, è il disegno di bitmap trasparenti a colori. I file bitmap (*.BMP) non possono memorizzare alcuna informazione circa la trasparenza, ma possono simulare questa funzione selezionando un colore per rappresentare l'area trasparente. Questo aggiramento del problema è usato comunemente nelle applicazioni Win32.
139+
140+Il seguente esempio carica una bitmap da una risorsa di Windows, seleziona il colore clFucsia come colore trasparente, e poi disegna su canvas.
141+
142+``` pascal
143+procedure MyForm.MyButtonOnClick(Sender: TObject);
144+var
145+ buffer: THandle;
146+ bmp: TBitmap;
147+ memstream: TMemoryStream;
148+begin
149+ bmp := TBitmap.Create;
150+
151+ buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
152+
153+ if (buffer = 0) then exit; // Error loading the bitmap
154+
155+ bmp.Handle := buffer;
156+ memstream := TMemoryStream.create;
157+ try
158+ bmp.SaveToStream(memstream);
159+ memstream.position := 0;
160+ bmp.LoadFromStream(memstream);
161+ finally
162+ memstream.free;
163+ end;
164+
165+ bmp.Transparent := True;
166+ bmp.TransparentColor := clFuchsia;
167+
168+ MyCanvas.Draw(0, 0, bmp);
169+
170+ bmp.Free; // Release allocated resource
171+end;
172+```
173+
174+Notare le operazioni in memoria eseguite con [[doc:rtl/classes/tmemorystream.html|TMemoryStream]], necessarie per caricare correttamente l'immagine.
175+
176+### Catturare un'immagine dello schermo
177+
178+A partire da Lazarus 0.9.16 si può usare la libreria LCL per catturare una schermata indipendentemente dalla piattaforma. L'esempio seguente mostra come:
179+
180+``` pascal
181+uses Graphics, LCLIntf, LCLType;
182+ ...
183+var
184+ MyBitmap: TBitmap;
185+ ScreenDC: HDC;
186+begin
187+ MyBitmap := TBitmap.Create;
188+ ScreenDC := GetDC(0);
189+ MyBitmap.LoadFromDevice(ScreenDC);
190+ ReleaseDC(0,ScreenDC);
191+ ...
192+```
193+
194+### Lavorare con TLazIntfImage, TRawImage e TLazCanvas
195+
196+TLazIntfImage è un equivalente non nativo di TRasterImage (comunemente usato nelle vesti del suo discendente TBitmap). Questa classe, diversamente da TBitmap, non alloca un'area di memoria per la bitmap; bisogna prima inizializzare un'area di memoria e poi assegnarla a TLazIntfImage. Subito dopo aver creato TLazIntfImage bisogna collegarlo a TRawImage o caricare da TBitmap.
197+
198+TRawImage è derivato dal tipo [[Programming Using Objects|Object]] e quindi non necessita di creazione e distruzione. Esso alloca la memoria per l'immagine quando si chiama TRawImage.CreateData o, in alternativa, si può passare un blocco di memoria allocato con una libreria di terze parti (Windows API, Cocoa Framework); in quest'ultimo caso si passano le informazioni del caso a TRawImage.Description, TRawImage.Data e TRawImage.DataSize. Se si carica TLazIntfImage da TBitmap i dati di TBitmap vengono copiati e quindi in seguito TLazIntfImage non sarà sincronizzato con TBitmap. Si noti che TLazCanvas non può esistere da solo, ma deve essere sempre collegato ad un TLazIntfImage.
199+
200+L'esempio sottostante mostra come scegliere il formato dei dati, richiedere a TRawImage di crearlo, collegarlo a TLazIntfImage; quindi collega un TLazCanvas a TLazIntfImage:
201+
202+``` pascal
203+uses graphtype, intfgraphics, lazcanvas;
204+
205+var
206+ AImage: TLazIntfImage;
207+ ACanvas: TLazCanvas;
208+ lRawImage: TRawImage;
209+begin
210+ lRawImage.Init;
211+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
212+ lRawImage.CreateData(True);
213+ AImage := TLazIntfImage.Create(0,0);
214+ AImage.SetRawImage(lRawImage);
215+ ACanvas := TLazCanvas.Create(AImage);
216+```
217+
218+#### Inizializzare una TLazIntfImage
219+
220+Non si può semplicemente creare una istanza di TLazIntfImage e cominciare ad utilizzarla. E' necessario aggiungervi spazio di memoria. Ci sono tre modi:
221+
222+1\. Collegare TLazIntfImage ad una TRawImage
223+
224+2\. Caricare la memoria da una TBitmap. (TLazIntfImage copierà la memoria da TBitmap e se nescollegherà).
225+
226+``` pascal
227+ SrcIntfImg:=TLazIntfImage.Create(0,0);
228+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
229+```
230+
231+3\. Creare la memoria sulla base di una descrizione di un'immagine grezza, in questo modo:
232+
233+``` pascal
234+ IntfImg := TLazIntfImage.Create(0,0);
235+ IntfImg.DataDescription:=GetDescriptionFromDevice(0);
236+ IntfImg.SetSize(10,10);
237+```
238+
239+Il device 0 in **GetDescriptionFromDevice(0)** fa riferimento al formato corrente dello schermo.
240+
241+#### TLazIntfImage.LoadFromFile
242+
243+L'esempio che segue mostra come caricare un'immagine direttamente in una TLazIntfImage. Nell'esempio si inizializza TLazIntfImage per un formato RGBA a 32 bit. Si ricordi che questo non sarà, probabilmente, il formato del vostro schermo.
244+
245+``` pascal
246+uses LazLogger, Graphics, IntfGraphics, GraphType;
247+procedure TForm1.FormCreate(Sender: TObject);
248+var
249+ AImage: TLazIntfImage;
250+ lRawImage: TRawImage;
251+begin
252+ // create a TLazIntfImage with 32 bits per pixel, alpha 8bit, red 8 bit, green 8bit, blue 8bit,
253+ // Bits In Order: bit 0 is pixel 0, Top To Bottom: line 0 is top
254+ lRawImage.Init;
255+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
256+ lRawImage.CreateData(false);
257+ AImage := TLazIntfImage.Create(0,0);
258+ try
259+ AImage.SetRawImage(lRawImage);
260+ // Load an image from disk.
261+ // It uses the file extension to select the right registered image reader.
262+ // The AImage will be resized to the width, height of the loaded image.
263+ AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
264+ debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
265+ finally
266+ AImage.Free;
267+ end;
268+end;
269+```
270+
271+#### Caricare una TLazIntfImage in una TImage
272+
273+I pixel di una **TImage** si trovano nella proprietà **TImage.Picture** che è del tipo *TPicture*. **TPicture** è un contenitore multi-formato che può rappresentare diversi formati come Bitmap, Icon, JPeg o PNG. Usualmente si userà *TPicture.Bitmap* per caricare una **TLazIntfImage**:
274+
275+``` pascal
276+ Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
277+```
278+
279+**Osservazioni:**
280+
281+- Per caricare una **transparent** TLazIntfImage bisogna impostare **Image1.Transparent** a true.
282+- TImage usa il formato dello schermo. Se la TLazIntfImage ha un formato diverso bisogna convertire i pixel. Nota: si può usare **IntfImg.DataDescription:=GetDescriptionFromDevice(0);** per inizializzare TLazIntfImage con il formato dello schermo.
283+
284+#### Esempio di sfumatura
285+
286+Un esempio di sfumatura con TLazIntfImage
287+
288+``` pascal
289+{ Questo codice è stato ripreso dal progetto
290+$LazarusPath/examples/lazintfimage/fadein1.lpi . }
291+uses LCLType, // HBitmap type
292+ IntfGraphics, // TLazIntfImage type
293+ fpImage; // TFPColor type
294+...
295+ procedure TForm1.FadeIn(ABitMap: TBitMap);
296+ var
297+ SrcIntfImg, TempIntfImg: TLazIntfImage;
298+ ImgHandle,ImgMaskHandle: HBitmap;
299+ FadeStep: Integer;
300+ px, py: Integer;
301+ CurColor: TFPColor;
302+ TempBitmap: TBitmap;
303+ begin
304+ SrcIntfImg:=TLazIntfImage.Create(0,0);
305+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
306+ TempIntfImg:=TLazIntfImage.Create(0,0);
307+ TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
308+ TempBitmap:=TBitmap.Create;
309+ for FadeStep:=1 to 32 do begin
310+ for py:=0 to SrcIntfImg.Height-1 do begin
311+ for px:=0 to SrcIntfImg.Width-1 do begin
312+ CurColor:=SrcIntfImg.Colors[px,py];
313+ CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
314+ CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
315+ CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
316+ TempIntfImg.Colors[px,py]:=CurColor;
317+ end;
318+ end;
319+ TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
320+ TempBitmap.Handle:=ImgHandle;
321+ TempBitmap.MaskHandle:=ImgMaskHandle;
322+ Canvas.Draw(0,0,TempBitmap);
323+ end;
324+ SrcIntfImg.Free;
325+ TempIntfImg.Free;
326+ TempBitmap.Free;
327+ end;
328+```
329+
330+#### Esempio di immagine con uno spefifico formato
331+
332+Se si sa che TBitmap usa blu a 8bit, verde a 8 bit, rosso a 8 bit, si può accedere direttamente ai byte, con velocità superiore:
333+
334+``` pascal
335+uses LCLType, // HBitmap type
336+ IntfGraphics, // TLazIntfImage type
337+ fpImage; // TFPColor type
338+...
339+type
340+ TRGBTripleArray = array[0..32767] of TRGBTriple;
341+ PRGBTripleArray = ^TRGBTripleArray;
342+
343+procedure TForm1.FadeIn2(aBitMap: TBitMap);
344+ var
345+ IntfImg1, IntfImg2: TLazIntfImage;
346+ ImgHandle,ImgMaskHandle: HBitmap;
347+ FadeStep: Integer;
348+ px, py: Integer;
349+ CurColor: TFPColor;
350+ TempBitmap: TBitmap;
351+ Row1, Row2: PRGBTripleArray;
352+ begin
353+ IntfImg1:=TLazIntfImage.Create(0,0);
354+ IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
355+
356+ IntfImg2:=TLazIntfImage.Create(0,0);
357+ IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
358+
359+ TempBitmap:=TBitmap.Create;
360+
361+ //with Scanline-like
362+ for FadeStep:=1 to 32 do begin
363+ for py:=0 to IntfImg1.Height-1 do begin
364+ Row1 := IntfImg1.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
365+ Row2 := IntfImg2.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
366+ for px:=0 to IntfImg1.Width-1 do begin
367+ Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
368+ Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Fading
369+ Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
370+ end;
371+ end;
372+ IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
373+
374+ TempBitmap.Handle:=ImgHandle;
375+ TempBitmap.MaskHandle:=ImgMaskHandle;
376+ Canvas.Draw(0,0,TempBitmap);
377+ end;
378+
379+ IntfImg1.Free;
380+ IntfImg2.Free;
381+ TempBitmap.Free;
382+ end;
383+```
384+
385+#### Conversione tra TLazIntfImage e TBitmap
386+
387+Poichè Lazarus non ha la proprietà TBitmap.ScanLines, il miglior modo per accedere ai pixel di un'immagine sia in lettura che in scrittura è di usare TLazIntfImage. TBitmap può essere convertita a TLazIntfImage usando TBitmap.CreateIntfImage() e, dopo aver operato sui pixel, si può riconvertire a TBitmap usando TBitmap.LoadFromIntfImage(); ecco un esempio in cui si applica la procedura.
388+
389+``` pascal
390+uses
391+ ...GraphType, IntfGraphics, LCLType, LCLProc, LCLIntf ...
392+
393+procedure TForm1.Button4Click(Sender: TObject);
394+var
395+ b: TBitmap;
396+ t: TLazIntfImage;
397+begin
398+ b := TBitmap.Create;
399+ try
400+ b.LoadFromFile('test.bmp');
401+ t := b.CreateIntfImage;
402+
403+ // Read and/or write to the pixels
404+ t.Colors[10,20] := colGreen;
405+
406+ b.LoadFromIntfImage(t);
407+ finally
408+ t.Free;
409+ b.Free;
410+ end;
411+end;
412+```
413+
414+#### Usare StretchDraw non-nativo da LazCanvas
415+
416+In modo analogo a TCanvas.StretchDraw esiste TLazCanvas.StretchDraw, ma è necessario specificare quale interpolazione usare. Quella che produce risultati simili a StretchDraw di Windows con passaggi di pixel bruschi, scalettati, l'opposto di metodi anti-aliased, può essere specificata con: TLazCanvas.Interpolation := TFPSharpInterpolation.Create;
417+
418+Nell'unità fpcanvas sono disponibili altri tipo di interpolazione.
419+
420+``` pascal
421+uses intfgraphics, lazcanvas;
422+
423+procedure TForm1.StretchDrawBitmapToBitmap(SourceBitmap, DestBitmap: TBitmap; DestWidth, DestHeight: integer);
424+var
425+ DestIntfImage, SourceIntfImage: TLazIntfImage;
426+ DestCanvas: TLazCanvas;
427+begin
428+ // Prepare the destination
429+
430+ DestIntfImage := TLazIntfImage.Create(0, 0);
431+ DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);
432+
433+ DestCanvas := TLazCanvas.Create(DestIntfImage);
434+
435+ //Prepare the source
436+ SourceIntfImage := TLazIntfImage.Create(0, 0);
437+ SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);
438+
439+ // Execute the stretch draw via TFPSharpInterpolation
440+ DestCanvas.Interpolation := TFPSharpInterpolation.Create;
441+ DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);
442+
443+ // Reload the image into the TBitmap
444+ DestBitmap.LoadFromIntfImage(DestIntfImage);
445+
446+ SourceIntfImage.Free;
447+ DestCanvas.Interpolation.Free;
448+ DestCanvas.Free;
449+ DestIntfImage.Free;
450+end;
451+
452+procedure TForm1.FormPaint(Sender: TObject);
453+var
454+ Bmp, DestBitmap: TBitmap;
455+begin
456+ // Prepare the destination
457+ DestBitmap := TBitmap.Create;
458+ DestBitmap.Width := 100;
459+ DestBitmap.Height := 100;
460+
461+ Bmp := TBitmap.Create;
462+ Bmp.Width := 10;
463+ Bmp.Height := 10;
464+ Bmp.Canvas.Pen.Color := clYellow;
465+ Bmp.Canvas.Brush.Color := clYellow;
466+ Bmp.Canvas.Rectangle(0, 0, 10, 10);
467+ StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
468+ Canvas.Draw(0, 0, Bmp);
469+ Canvas.Draw(100, 100, DestBitmap);
470+end;
471+```
472+
473+## Grafica nei Movimenti - Come Evitare lo Sfarfallio
474+
475+Molti programmi producono output verso la GUI sotto forma di disegni 2D. Se tali disegni cambiano rapidamente ci si presenterà il problema dello sfarfallio dello schermo. Questo accade perché le immagini sono riprodotte a volte in modo completo, a volte no.
476+
477+Come evitare lo sfarfallio ed ottenere la miglior velocità di riproduzione? Ovviamente si può sfruttare l'accelerazione hardware usando OpenGL, ma l'approccio è considerevolmente pesante per piccoli programmi e/o vecchi computer. Per un aiuto all'uso di OpenGL si veda l'esempio distribuito con Lazarus.
478+
479+Se si disegna direttamente su TCanvas, si hanno a disposizione le seguenti opzioni:
480+
481+- Disegnare su TImage
482+- Draw on the OnPaint event of the form, a TPaintBox or another control
483+- Create a custom control which draws itself
484+- Using A.J. Venter's gamepack
485+
486+Utili considerazioni sul contenimento dello sfarfallio si trovano nell'articolo seguente, <http://delphi.about.com/library/bluc/text/uc052102g.htm>, scritto per Delphi ma egualmente applicabile a Lazarus.
487+
488+### Draw to a TImage
489+
490+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.
491+
492+**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.
493+
494+#### Resizing the bitmap of a TImage
495+
496+> **Note:** Do not use this during OnPaint.
497+
498+``` pascal
499+with Image1.Picture.Bitmap do begin
500+ Width:=100;
501+ Height:=120;
502+end;
503+```
504+
505+Same in one step:
506+
507+``` pascal
508+with Image1.Picture.Bitmap do begin
509+ SetSize(100, 120);
510+end;
511+```
512+
513+#### Painting on the bitmap of a TImage
514+
515+> **Note:** Do not use this during OnPaint.
516+
517+``` pascal
518+with Image1.Picture.Bitmap.Canvas do begin
519+ // fill the entire bitmap with red
520+ Brush.Color := clRed;
521+ FillRect(0, 0, Width, Height);
522+end;
523+```
524+
525+> **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.
526+
527+Another example:
528+
529+``` pascal
530+procedure TForm1.BitBtn1Click(Sender: TObject);
531+var
532+ x, y: Integer;
533+begin
534+ // Draws the backgroung
535+ MyImage.Canvas.Pen.Color := clWhite;
536+ MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
537+
538+ // Draws squares
539+ MyImage.Canvas.Pen.Color := clBlack;
540+ for x := 1 to 8 do
541+ for y := 1 to 8 do
542+ MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
543+ Round(x * Image.Width / 8), Round(y * Image.Height / 8));
544+end;
545+```
546+
547+#### Painting on the volatile visual area of the TImage
548+
549+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 the area manually with Image1.Invalidate. This will not immediately call OnPaint and you can call Invalidate as many times as you want.
550+
551+``` pascal
552+procedure TForm.Image1Paint(Sender: TObject);
553+begin
554+ // paint a line
555+ Canvas.Pen.Color := clRed;
556+ Canvas.Line(0, 0, Width, Height);
557+end;
558+```
559+
560+### Draw on the OnPaint event
561+
562+In this case all the drawing has to be done on the OnPaint event of the form, or of another control. The drawing isn't buffered like in the TImage, and it needs to be fully redrawn in each call of the OnPaint event handler.
563+
564+``` pascal
565+procedure TForm.Form1Paint(Sender: TObject);
566+begin
567+ // paint a line
568+ Canvas.Pen.Color := clRed;
569+ Canvas.Line(0, 0, Width, Height);
570+end;
571+```
572+
573+### Create a custom control which draws itself
574+
575+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.
576+
577+Here is an example custom control:
578+
579+``` pascal
580+uses
581+ Classes, SysUtils, Controls, Graphics, LCLType;
582+
583+type
584+ TMyDrawingControl = class(TCustomControl)
585+ public
586+ procedure EraseBackground(DC: HDC); override;
587+ procedure Paint; override;
588+ end;
589+
590+implementation
591+
592+procedure TMyDrawingControl.EraseBackground(DC: HDC);
593+begin
594+ // Uncomment this to enable default background erasing
595+ //inherited EraseBackground(DC);
596+end;
597+
598+procedure TMyDrawingControl.Paint;
599+var
600+ x, y: Integer;
601+ Bitmap: TBitmap;
602+begin
603+ Bitmap := TBitmap.Create;
604+ try
605+ // Initializes the Bitmap Size
606+ Bitmap.Height := Height;
607+ Bitmap.Width := Width;
608+
609+ // Draws the background
610+ Bitmap.Canvas.Pen.Color := clWhite;
611+ Bitmap.Canvas.Rectangle(0, 0, Width, Height);
612+
613+ // Draws squares
614+ Bitmap.Canvas.Pen.Color := clBlack;
615+ for x := 1 to 8 do
616+ for y := 1 to 8 do
617+ Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
618+ Round(x * Width / 8), Round(y * Height / 8));
619+
620+ Canvas.Draw(0, 0, Bitmap);
621+ finally
622+ Bitmap.Free;
623+ end;
624+
625+ inherited Paint;
626+end;
627+```
628+
629+and how we create it on the form:
630+
631+``` pascal
632+procedure TMyForm.FormCreate(Sender: TObject);
633+begin
634+ MyDrawingControl := TMyDrawingControl.Create(Self);
635+ MyDrawingControl.Height := 400;
636+ MyDrawingControl.Width := 500;
637+ MyDrawingControl.Top := 0;
638+ MyDrawingControl.Left := 0;
639+ MyDrawingControl.Parent := Self;
640+ MyDrawingControl.DoubleBuffered := True;
641+end;
642+```
643+
644+It is destroyed automatically, because we use Self as owner.
645+
646+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.
647+
648+"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
649+
650+"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
651+
652+## Image formats
653+
654+Here is a table with the correct class to use for each image format.
655+
656+| Format | Image class | Unit |
657+|--------------------------------|-------------------------|----------|
658+| Cursor (cur) | TCursor | Graphics |
659+| Bitmap (bmp) | TBitmap | Graphics |
660+| Windows icon (ico) | TIcon | Graphics |
661+| Mac OS X icon (icns) | TicnsIcon | Graphics |
662+| Pixmap (xpm) | TPixmap | Graphics |
663+| Portable Network Graphic (png) | TPortableNetworkGraphic | Graphics |
664+| JPEG (jpg, jpeg) | TJpegImage | Graphics |
665+| PNM (pnm) | TPortableAnyMapGraphic | Graphics |
666+| Tiff (tif, tiff) | TTiffImage | Graphics |
667+
668+See also the list of [[fcl-image|fcl-image supported formats]].
669+
670+### Converting formats
671+
672+Sometimes it is necessary to convert one graphic type to another. One of the ways is to convert a graphic to intermediate format, and then convert it to TBitmap. Most of the formats can create an image from TBitmap.
673+
674+Converting Bitmap to PNG and saving it to a file:
675+
676+``` pascal
677+procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
678+var
679+ png : TPortableNetworkGraphic;
680+begin
681+ png := TPortableNetworkGraphic.Create;
682+ try
683+ png.Assign(bmp);
684+ png.SaveToFile(PngFileName);
685+ finally
686+ png.Free;
687+ end;
688+end;
689+```
690+
691+## Pixel Formats
692+
693+### TColor
694+
695+The internal pixel format for TColor in the LCL is the XXBBGGRR format, which matches the native Windows format and is opposite to most other libraries, which use AARRGGBB. The XX part is used to identify if the color is a fixed color, which case XX should be 00 or if it is an index to a system color. There is no space reserved for an alpha channel.
696+
697+To convert from separate RGB channels to TColor use:
698+
699+``` pascal
700+RGBToColor(RedVal, GreenVal, BlueVal);
701+```
702+
703+To get each channel of a TColor variable use the Red, Green and Blue functions:
704+
705+``` pascal
706+RedVal := Red(MyColor);
707+GreenVal := Green(MyColor);
708+BlueVal := Blue(MyColor);
709+```
710+
711+### TFPColor
712+
713+TFPColor uses the AARRGGBB format common to most libraries, but it uses 16-bits for the depth of each color channel, totaling 64-bits per pixel, which is unusual. This does not necessarily mean that images will consume that much memory, however. Images created using TRawImage+TLazIntfImage can have any internal storage format and then on drawing operations TFPColor is converted to this internal format.
714+
715+The unit Graphics provides routines to convert between TColor and TFPColor:
716+
717+``` pascal
718+function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
719+function FPColorToTColor(const FPColor: TFPColor): TColor;
720+function TColorToFPColor(const c: TColorRef): TFPColor; overload;
721+function TColorToFPColor(const c: TColor): TFPColor; overload; // does not work on system color
722+```
723+
724+## Drawing with fcl-image
725+
726+You can draw images which won't be displayed in the screen without the LCL, by just using fcl-image directly. For example a program running on a webserver without X11 could benefit from not having a visual library as a dependency. FPImage (alias fcl-image) is a very generic image and drawing library written completely in Pascal. In fact the LCL uses FPImage too for all the loading and saving from/to files and implements the drawing function through calls to the widgetset (winapi, gtk, carbon, ...). Fcl-image on the other hand also has drawing routines.
727+
728+For more information, please read the article about [[fcl-image]].
729+
730+## Common OnPaint Error
731+
732+A common error that causes many false bug reports is to call an Onpaint event for one object from another object. When using the LCL, this may work in GTK2 and Windows but will probably fail with Qt, Carbon and Cocoa. It is not normally necessary to call Invalidate. However, it may sometimes be needed in the Button1Click procedure,
733+
734+This is bad:
735+
736+``` pascal
737+procedure TForm1.Button1Click(Sender: TObject);
738+begin
739+ Shape1Paint(Self); // Call Shape1Onpaint event
740+ Shape1.Invalidate; // Invoke actual painting
741+
742+ ... more code for Button1 ...
743+end;
744+```
745+
746+This is good:
747+
748+``` pascal
749+procedure TForm1.Button1Click(Sender: TObject);
750+begin
751+ ... code for Button1 ...
752+ Set some condition;
753+ // Shape1.Invalidate; // May be necessary on some occasions
754+end;
755+
756+// Shape1Paint should be attached to the OnPaint event of shape object !
757+procedure TForm1.Shape1Paint(Sender: TObject);
758+var
759+ Myrect: TRect;
760+begin
761+ if some condition then
762+ with Shape1.Canvas do
763+ begin
764+ ... lots of stuff ...
765+ end;
766+end;
767+```
768+
769+## Some useful examples
770+
771+### Example 1: Drawing on loaded JPEG with TImage
772+
773+Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:
774+
775+``` pascal
776+function TForm1.LoadAndDraw(const sFileName: String): Boolean;
777+var jpg: TPicture;
778+begin
779+ bm:=TBitmap.Create;
780+ jpg:=TPicture.Create;
781+ jpg.LoadFromFile(sFileName);
782+ bm.SetSize(jpg.Width, jpg.Height);
783+ bm.Canvas.Draw(0, 0, jpg.Bitmap);
784+
785+ image.Picture.Bitmap.SetSize(bm.Width, bm.Height);
786+ Image.Picture.Bitmap.Canvas.Draw(0, 0, bm);
787+
788+ Image.Picture.Bitmap.Canvas.Pen.Color:=clRed;
789+ Image.Picture.Bitmap.Canvas.Line(0,0,140,140);
790+
791+ jpg.Free;
792+end;
793+```
794+
795+### Example 2: Drawing on controls of Form
796+
797+1\) Create a New project - Application, add to uses section next modules if needed: Types,Controls,Graphics.
798+
799+2\) Place on form Button1, GroupBox1 and RadioGroup1
800+
801+3\) Place on GroupBox1 one more button - Button2
802+
803+4\) Your TForm1.Create should looks like:
804+
805+``` pascal
806+ procedure TForm1.FormCreate(Sender: TObject);
807+ var
808+ i: Integer;
809+ begin
810+ for i:=0 to Self.ControlCount-1 do
811+ RadioGroup1.Items.AddObject(Controls[i].Name,Controls[i]);
812+
813+ RadioGroup1.Items.AddObject(Button2.Name,Button2);
814+ end;
815+```
816+
817+5\) For RadioGroup1 create handler of OnSelectionChanged event:
818+
819+``` pascal
820+ procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
821+ begin
822+ Self.Repaint;
823+ end;
824+```
825+
826+6\) Add to public section of your form procedure HighlightControl:
827+
828+``` pascal
829+ procedure TForm1.HighlightControl(AControl: TControl);
830+ var
831+ R: Types.TRect;
832+ aCC: TControlCanvas;
833+ begin
834+ R:=AControl.BoundsRect;
835+ InflateRect(R,2,2); // make rect a bit bigger then control
836+ aCC:=TControlCanvas.Create;
837+ aCC.Control:=AControl.Parent;
838+ aCC.Pen.Color:=clGreen;
839+ aCC.Pen.Width:=5;
840+ aCC.Pen.Style:=psSolid;
841+ aCC.Brush.Style:=bsClear;
842+ aCC.Rectangle(R);
843+ aCC.free;
844+ end;
845+```
846+
847+## See also
848+
849+- [[Fast direct pixel access]]
850+
851+---
852+*From the Free Pascal wiki, page "Developing with Graphics/it", 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