Difference between revisions of "Developing with Graphics/it"

From Lazarus wiki
Jump to navigationJump to search
m (→‎Example 2: Drawing on controls of Form: Fixed syntax highlighting)
 
(47 intermediate revisions by 2 users not shown)
Line 4: Line 4:
 
__TOC__
 
__TOC__
  
== Librerie ==
+
==Librerie ==
  
 
[[Librerie grafiche]] - qui si possono osservare le principali librerie utilizzabili per lo sviluppo.
 
[[Librerie grafiche]] - qui si possono osservare le principali librerie utilizzabili per lo sviluppo.
  
==Altri articoli di grafica==
+
===Altri articoli di grafica===
 +
 
 
===Disegno 2D===
 
===Disegno 2D===
 
* [[ZenGL]] - libreria multi-piattaforma per lo sviluppo di giochi mediante OpenGL.
 
* [[ZenGL]] - libreria multi-piattaforma per lo sviluppo di giochi mediante OpenGL.
Line 30: Line 31:
 
* [[Perlin Noise]] - Un articolo sull'uso di Perlin Noise in applicazioni LCL.
 
* [[Perlin Noise]] - Un articolo sull'uso di Perlin Noise in applicazioni LCL.
  
==Introduzione al modello Grafico della LCL==
+
===Introduzione al modello Grafico della LCL===
 
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.
 
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.
  
Line 39: Line 40:
 
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.
 
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.
  
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. L
+
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.
  
 
Nel widgeset LCL-CustomDrawn le classi native sono implementate usando le non-native.
 
Nel widgeset LCL-CustomDrawn le classi native sono implementate usando le non-native.
Line 45: Line 46:
 
Tutte queste classi saranno meglio descritte nelle sezioni seguenti.
 
Tutte queste classi saranno meglio descritte nelle sezioni seguenti.
  
==Lavorare con TCanvas==
+
===Lavorare con TCanvas===
  
{{:Graphics - Working with TCanvas}}
+
{{:Graphics - Working with TCanvas/it}}
  
==Working with TBitmap and other TGraphic descendents==
+
==Lavorare con TBitmap e altri discendenti di TGraphic==
 
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.
 
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.
  
===Caricare/Salvare un'immagine da/su disco===
+
==Caricare/Salvare un'immagine da/su disco==
Per caricare un'immagine da disco usare [http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.loadfromfile.html TGraphic.LoadFromFile] e per salvarla in un altro file usare [http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.savetofile.html TGraphic.SaveToFile]. Tra le classi discendenti da TGraphic usare la classe appropriata per il formato di file. Vedere[[Developing_with_Graphics#Image_formats]] per una lista delle classi disponibili per i vari formati.
+
Per caricare un'immagine da disco usare [http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.loadfromfile.html TGraphic.LoadFromFile] e per salvarla in un altro file usare [http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.savetofile.html TGraphic.SaveToFile]. Tra le classi discendenti da TGraphic usare la classe appropriata per il formato di file. Vedere [[Developing_with_Graphics#Image_formats]] per una lista delle classi disponibili per i vari formati.
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
var
 
var
 
   MyBitmap: TBitmap;
 
   MyBitmap: TBitmap;
Line 74: Line 76:
 
Per altri formati la procedura è completamente identica, usando la classe appropriata. Ad esempio per immagini PNG:
 
Per altri formati la procedura è completamente identica, usando la classe appropriata. Ad esempio per immagini PNG:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
var
 
var
 
   MyPNG: TPortableNetworkGraphic;
 
   MyPNG: TPortableNetworkGraphic;
Line 92: Line 94:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
If you do not know beforehand the format of the image, use TPicture which will determine the format based in the file extension. Note that TPicture does not support all formats supported by Lazarus, as of Lazarus 0.9.31 it supports BMP, PNG, JPEG, Pixmap and PNM while Lazarus also supports ICNS and other formats:
+
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:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
var
 
var
 
   MyPicture: TPicture;
 
   MyPicture: TPicture;
Line 112: Line 114:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
=== Additional file formats for TImage ===
+
=== File format addizionali per TImage ===
You can add additional file format support by adding the [[fcl-image]] fpread* and/or fpwrite* units to your uses clause. In this way, you can e.g. add support for TIFF for TImage
+
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
 +
 
 +
===Accesso diretto ai pixel===
 +
Per accedere direttamente ai pixel di un bitmap si può usare sia una libreria esterna, come [[BGRABitmap]], [[LazRGBGraphics]] e [[Current conversion projects#Graphics32|Graphics32]], oppure usare la classe nativa di Lazarus  TLazIntfImage. Per un confronto dei vari metodi vedere [[Fast direct pixel access|fast direct pixel access]].
  
===Direct pixel access===
+
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.
To do directly access the pixels of bitmaps one can either use external libraries, such as [[BGRABitmap]], [[LazRGBGraphics]] and [[Current conversion projects#Graphics32|Graphics32]] or use the Lazarus native TLazIntfImage. For a comparison of pixel access methods, see [[Fast direct pixel access|fast direct pixel access]].
 
  
On some Lazarus widgetsets (notably LCL-Gtk2), the bitmap data is not stored in memory location which can be accessed by the application and in general the LCL native interfaces draw only through native Canvas routines, so each SetPixel / GetPixel operation involves a slow call to the native Canvas API. In LCL-CustomDrawn this is not the case since the bitmap is locally stored for all backends and SetPixel / GetPixel is fast. For obtaining a solution which works in all widgetsets one should use TLazIntfImage. 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#Working with TLazIntfImage|TLazIntfImage]] which internally uses TRawImage.
+
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#Working with TLazIntfImage|TLazIntfImage]] which internally uses TRawImage.
  
 
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.
 
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.
Line 134: Line 138:
 
'''PLEASE REVISE THIS SECTION!'''
 
'''PLEASE REVISE THIS SECTION!'''
  
===Drawing color transparent bitmaps===
+
===Disegnare bitmap trasparenti a colori===
 
+
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.
A new feature, implemented on Lazarus 0.9.11, is color transparent bitmaps. Bitmap files (*.BMP) cannot store any information about transparency, but they can work as they had if you select a color on them to represent the transparent area. This is a common trick used on Win32 applications.
 
  
The following example loads a bitmap from a Windows resource, selects a color to be transparent (clFuchsia) and then draws it to a canvas.
+
Il seguente esempio carica una bitmap da una risorsa di Windows, seleziona il colore clFucsia come colore trasparente, e poi disegna su canvas.
  
<syntaxhighlight>procedure MyForm.MyButtonOnClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>
 +
procedure MyForm.MyButtonOnClick(Sender: TObject);
 
var
 
var
 
   buffer: THandle;
 
   buffer: THandle;
Line 170: Line 174:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
Notice the memory operations performed with the [[doc:rtl/classes/tmemorystream.html|TMemoryStream]]. They are necessary to ensure the correct loading of the image.
+
Notare le operazioni in memoria eseguite con [[doc:rtl/classes/tmemorystream.html|TMemoryStream]], necessarie per caricare correttamente l'immagine.
  
===Taking a screenshot of the screen===
+
===Catturare un'immagine dello schermo===
 
+
Since Lazarus 0.9.16 you can use LCL to take screenshots of the screen in a cross-platform way. The following example code does it:
+
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:  
  
<syntaxhighlight>uses Graphics, LCLIntf, LCLType;
+
<syntaxhighlight lang=pascal>
 +
uses Graphics, LCLIntf, LCLType;
 
   ...
 
   ...
 
var
 
var
Line 189: Line 194:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Working with TLazIntfImage, TRawImage and TLazCanvas==
+
===Lavorare con TLazIntfImage, TRawImage e TLazCanvas===
  
TLazIntfImage is a non-native equivalent of TRasterImage (more commonly utilized in the form of it's descendent TBitmap). The first thing to be aware about this class is that unlike TBitmap it will not automatically allocate a memory area for the bitmap, one should first initialize a memory area and then give it to the TLazIntfImage. Right after creating a TLazIntfImage one should either connect it to a TRawImage or load it from a TBitmap.
+
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.
  
TRawImage is of the type object and therefore does not need to be created nor freed. It can either allocate the image memory itself when one calls TRawImage.CreateData or one can pass a memory block allocated for examply by a 3rd party library such as the Windows API of the Cocoa Framework from Mac OS X and pass the information of the image in TRawImage.Description, TRawImage.Data and TRawImage.DataSize. Instead of attaching it to a RawImage one could also load it from a TBitmap which will copy the data from the TBitmap and won't be syncronized with it afterwards. The TLazCanvas cannot exist alone and must always be attached to a TLazIntfImage.
+
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.
  
The example below shows how to choose a format for the data and ask the TRawImage to create it for us and then we attach it to a TLazIntfImage and then attach a TLazCanvas to it:
+
L'esempio sottostante mostra come scegliere il formato dei dati, richiedere a TRawImage di crearlo, collegarlo a TLazIntfImage; quindi collega un TLazCanvas a TLazIntfImage:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
uses graphtype, intfgraphics, lazcanvas;
 
uses graphtype, intfgraphics, lazcanvas;
  
Line 213: Line 219:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Initializing a TLazIntfImage===
+
====Inizializzare una TLazIntfImage====
 +
 
 +
Non si può semplicemente creare una istanza di TLazIntfImage e cominciare ad utilizzarla. E' necessario aggiungervi spazio di memoria. Ci sono tre modi:
  
One cannot simply create an instance of TLazIntfImage and start using it. It needs to add a storage to it. There are 3 ways to do this:
+
1. Collegare TLazIntfImage ad una TRawImage
  
1. Attach it to a TRawImage
+
2. Caricare la memoria da una TBitmap. (TLazIntfImage copierà la memoria da TBitmap e se nescollegherà).  
  
2. Load it from a TBitmap. Note that it will copy the memory of the TBitmap so it won't remain connected to it.
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
   SrcIntfImg:=TLazIntfImage.Create(0,0);
 
   SrcIntfImg:=TLazIntfImage.Create(0,0);
 
   SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
 
   SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
3. Load it from a raw image description, like this:
+
3. Creare la memoria sulla base di una descrizione di un'immagine grezza, in questo modo:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
   IntfImg := TLazIntfImage.Create(0,0);
 
   IntfImg := TLazIntfImage.Create(0,0);
 
   IntfImg.DataDescription:=GetDescriptionFromDevice(0);
 
   IntfImg.DataDescription:=GetDescriptionFromDevice(0);
Line 233: Line 240:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The 0 device in '''GetDescriptionFromDevice(0)''' uses the current screen format.
+
Il device 0 in '''GetDescriptionFromDevice(0)''' fa riferimento al formato corrente dello schermo.
  
===TLazIntfImage.LoadFromFile===
+
====TLazIntfImage.LoadFromFile====
  
Here is an example how to load an image directly into a TLazIntfImage. It initializes the TLazIntfImage to a 32bit RGBA format. Keep in mind that this is probably not the native format of your screen.
+
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.
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
uses LazLogger, Graphics, IntfGraphics, GraphType;
 
uses LazLogger, Graphics, IntfGraphics, GraphType;
 
procedure TForm1.FormCreate(Sender: TObject);
 
procedure TForm1.FormCreate(Sender: TObject);
Line 265: Line 272:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Loading a TLazIntfImage into a TImage===
+
====Caricare una TLazIntfImage in una TImage====
  
The pixel data of a '''TImage''' is the '''TImage.Picture''' property, which is of type ''TPicture''. '''TPicture''' is a multi format container containing one of several common image formats like Bitmap, Icon, Jpeg or PNG . Usually you will use the ''TPicture.Bitmap'' to load a '''TLazIntfImage''':
+
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''':
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
     Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
 
     Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
'''Notes:'''
+
'''Osservazioni:'''
*To load a '''transparent''' TLazIntfImage you have to set the '''Image1.Transparent''' to true.
+
*Per caricare una '''transparent''' TLazIntfImage bisogna impostare '''Image1.Transparent''' a true.
*TImage uses the screen format. If the TLazIntfImage has a different format then the pixels will be converted. Hint: You can use '''IntfImg.DataDescription:=GetDescriptionFromDevice(0);''' to initialize the TLazIntfImage with the screen format.
+
*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.
  
===Fading example===
+
====Esempio di sfumatura====
  
A fading example with TLazIntfImage
+
Un esempio di sfumatura con TLazIntfImage
  
<syntaxhighlight>{ This code has been taken from the $LazarusPath/examples/lazintfimage/fadein1.lpi project. }
+
<syntaxhighlight lang=pascal>
 +
{ Questo codice è stato ripreso dal progetto
 +
$LazarusPath/examples/lazintfimage/fadein1.lpi . }
 
uses LCLType, // HBitmap type
 
uses LCLType, // HBitmap type
 
     IntfGraphics, // TLazIntfImage type
 
     IntfGraphics, // TLazIntfImage type
Line 320: Line 329:
 
  end;</syntaxhighlight>
 
  end;</syntaxhighlight>
  
===Image format specific example===
+
====Esempio di immagine con uno spefifico formato====
  
If you know that the TBitmap is using blue 8bit, green 8bit, red 8bit you can directly access the bytes, which is somewhat faster:
+
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:
  
<syntaxhighlight>uses LCLType, // HBitmap type
+
<syntaxhighlight lang=pascal>
 +
uses LCLType, // HBitmap type
 
     IntfGraphics, // TLazIntfImage type
 
     IntfGraphics, // TLazIntfImage type
 
     fpImage; // TFPColor type
 
     fpImage; // TFPColor type
Line 373: Line 383:
 
  end;</syntaxhighlight>
 
  end;</syntaxhighlight>
  
===Conversion between TLazIntfImage and TBitmap===
+
====Conversione tra TLazIntfImage e TBitmap====
  
Since Lazarus has no TBitmap.ScanLines property, the best way to access the pixels of an image in a fast way for both reading and writing is by using TLazIntfImage. The TBitmap can be converted to a TLazIntfImage by using TBitmap.CreateIntfImage() and after modifying the pixels it can be converted back to a TBitmap by using TBitmap.LoadFromIntfImage();
+
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  
Here's the sample on how to create TLazIntfImage from TBitmap, modify it and then go back to the 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.
  
<syntaxhighlight>uses
+
<syntaxhighlight lang=pascal>
 +
uses
 
   ...GraphType, IntfGraphics, LCLType, LCLProc,  LCLIntf ...
 
   ...GraphType, IntfGraphics, LCLType, LCLProc,  LCLIntf ...
  
Line 401: Line 413:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===Using the non-native StretchDraw from LazCanvas===
+
====Usare StretchDraw non-nativo da LazCanvas====
  
Just like TCanvas.StretchDraw there is TLazCanvas.StretchDraw but you need to specify the interpolation which you desire to use. The interpolation which provides a Windows-like StretchDraw with a very sharp result (the opposite of anti-aliased) can be added with: TLazCanvas.Interpolation := TFPSharpInterpolation.Create;
+
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;
  
There are other interpolations available in the unit fpcanvas.
+
Nell'unità fpcanvas sono disponibili altri tipo di interpolazione.
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
uses intfgraphics, lazcanvas;
 
uses intfgraphics, lazcanvas;
  
Line 460: Line 472:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Motion Graphics - How to Avoid flickering==
+
==Grafica nei Movimenti - Come Evitare lo Sfarfallio==
 
 
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 see the whole images and sometimes only when it is partially drawn. It occurs because the painting process requires time.
 
  
How can you 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.  
+
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.
  
Another solution is 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.
+
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.  
  
A brief and very helpful article on avoiding flicker can be found at http://delphi.about.com/library/bluc/text/uc052102g.htm. Although written for Delphi, the techniques work well with Lazarus.
+
Se si disegna direttamente su TCanvas, si hanno a disposizione le seguenti opzioni:
  
Now we will examine the options we have for drawing to a Canvas:
+
* [[#Draw to a TImage|Disegnare su TImage]]
* [[#Draw to a TImage|Draw to a TImage]]
 
 
* [[#Draw on the OnPaint event|Draw on the OnPaint event of the form, a TPaintBox or another control]]
 
* [[#Draw on the OnPaint event|Draw on the OnPaint event of the form, a TPaintBox or another control]]
 
* [[#Create a custom control which draws itself|Create a custom control which draws itself]]
 
* [[#Create a custom control which draws itself|Create a custom control which draws itself]]
 
* [[#Using A.J. Venter's gamepack|Using A.J. Venter's gamepack]]
 
* [[#Using A.J. Venter's gamepack|Using A.J. Venter's gamepack]]
 +
 +
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.
  
 
===Draw to a TImage===
 
===Draw to a TImage===
Line 488: Line 499:
 
{{Note| Do not use this during OnPaint.}}
 
{{Note| Do not use this during OnPaint.}}
  
<syntaxhighlight>with Image1.Picture.Bitmap do begin
+
<syntaxhighlight lang=pascal>with Image1.Picture.Bitmap do begin
 
   Width:=100;
 
   Width:=100;
 
   Height:=120;
 
   Height:=120;
Line 495: Line 506:
 
Same in one step:
 
Same in one step:
  
<syntaxhighlight>with Image1.Picture.Bitmap do begin
+
<syntaxhighlight lang=pascal>with Image1.Picture.Bitmap do begin
 
   SetSize(100, 120);
 
   SetSize(100, 120);
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
Line 503: Line 514:
 
{{Note| Do not use this during OnPaint.}}
 
{{Note| Do not use this during OnPaint.}}
  
<syntaxhighlight>with Image1.Picture.Bitmap.Canvas do begin
+
<syntaxhighlight lang=pascal>
 +
with Image1.Picture.Bitmap.Canvas do begin
 
   // fill the entire bitmap with red
 
   // fill the entire bitmap with red
 
   Brush.Color := clRed;
 
   Brush.Color := clRed;
Line 513: Line 525:
 
Another example:
 
Another example:
  
<syntaxhighlight>procedure TForm1.BitBtn1Click(Sender: TObject);
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.BitBtn1Click(Sender: TObject);
 
var
 
var
 
   x, y: Integer;
 
   x, y: Integer;
Line 533: Line 546:
 
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.
 
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.
  
<syntaxhighlight>procedure TForm.Image1Paint(Sender: TObject);
+
<syntaxhighlight lang=pascal>
 +
procedure TForm.Image1Paint(Sender: TObject);
 
begin
 
begin
 
   // paint a line
 
   // paint a line
Line 544: Line 558:
 
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.
 
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.
  
<syntaxhighlight>procedure TForm.Form1Paint(Sender: TObject);
+
<syntaxhighlight lang=pascal>
 +
procedure TForm.Form1Paint(Sender: TObject);
 
begin
 
begin
 
   // paint a line
 
   // paint a line
Line 556: Line 571:
 
Here is an example custom control:
 
Here is an example custom control:
  
<syntaxhighlight>uses
+
<syntaxhighlight lang=pascal>
 +
uses
 
   Classes, SysUtils, Controls, Graphics, LCLType;
 
   Classes, SysUtils, Controls, Graphics, LCLType;
 
   
 
   
Line 605: Line 621:
  
 
and how we create it on the form:
 
and how we create it on the form:
<syntaxhighlight>procedure TMyForm.FormCreate(Sender: TObject);
+
 
 +
<syntaxhighlight lang=pascal>
 +
procedure TMyForm.FormCreate(Sender: TObject);
 
begin
 
begin
 
   MyDrawingControl := TMyDrawingControl.Create(Self);
 
   MyDrawingControl := TMyDrawingControl.Create(Self);
Line 660: Line 678:
 
Converting Bitmap to PNG and saving it to a file:
 
Converting Bitmap to PNG and saving it to a file:
  
<syntaxhighlight>procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
+
<syntaxhighlight lang=pascal>
 +
procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
 
var
 
var
 
   png : TPortableNetworkGraphic;  
 
   png : TPortableNetworkGraphic;  
Line 681: Line 700:
 
To convert from separate RGB channels to TColor use:
 
To convert from separate RGB channels to TColor use:
  
<syntaxhighlight>RGBToColor(RedVal, GreenVal, BlueVal);</syntaxhighlight>
+
<syntaxhighlight lang=pascal>RGBToColor(RedVal, GreenVal, BlueVal);</syntaxhighlight>
  
 
To get each channel of a TColor variable use the Red, Green and Blue functions:
 
To get each channel of a TColor variable use the Red, Green and Blue functions:
  
<syntaxhighlight>RedVal := Red(MyColor);
+
<syntaxhighlight lang=pascal>
 +
RedVal := Red(MyColor);
 
GreenVal := Green(MyColor);
 
GreenVal := Green(MyColor);
 
BlueVal := Blue(MyColor);</syntaxhighlight>
 
BlueVal := Blue(MyColor);</syntaxhighlight>
Line 695: Line 715:
 
The unit Graphics provides routines to convert between TColor and TFPColor:
 
The unit Graphics provides routines to convert between TColor and TFPColor:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
 
function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
 
function FPColorToTColor(const FPColor: TFPColor): TColor;
 
function FPColorToTColor(const FPColor: TFPColor): TColor;
Line 714: Line 734:
 
This is bad:
 
This is bad:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 726: Line 746:
 
This is good:
 
This is good:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 750: Line 770:
 
===Example 1: Drawing on loaded JPEG with TImage===
 
===Example 1: Drawing on loaded JPEG with TImage===
 
Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:
 
Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
function TForm1.LoadAndDraw(const sFileName: String): Boolean;
 
function TForm1.LoadAndDraw(const sFileName: String): Boolean;
 
var jpg: TPicture;
 
var jpg: TPicture;
Line 779: Line 800:
 
4) Your TForm1.Create should looks like:
 
4) Your TForm1.Create should looks like:
  
 +
<syntaxhighlight lang=pascal>
 
  procedure TForm1.FormCreate(Sender: TObject);
 
  procedure TForm1.FormCreate(Sender: TObject);
 
  var
 
  var
Line 788: Line 810:
 
   RadioGroup1.Items.AddObject(Button2.Name,Button2);
 
   RadioGroup1.Items.AddObject(Button2.Name,Button2);
 
  end;
 
  end;
 
+
</syntaxhighlight>
  
 
5) For RadioGroup1 create handler of OnSelectionChanged event:
 
5) For RadioGroup1 create handler of OnSelectionChanged event:
  
 +
<syntaxhighlight lang=pascal>
 
  procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
 
  procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
 
  begin
 
  begin
 
   Self.Repaint;
 
   Self.Repaint;
 
  end;   
 
  end;   
 +
</syntaxhighlight>
  
 
6) Add to public section of your form procedure HighlightControl:
 
6) Add to public section of your form procedure HighlightControl:
  
 +
<syntaxhighlight lang=pascal>
 
   procedure TForm1.HighlightControl(AControl: TControl);
 
   procedure TForm1.HighlightControl(AControl: TControl);
 
   var
 
   var
Line 815: Line 840:
 
     aCC.free;
 
     aCC.free;
 
   end;
 
   end;
 +
</syntaxhighlight>
  
 
==See also==
 
==See also==
 
* [[Fast direct pixel access]]
 
* [[Fast direct pixel access]]

Latest revision as of 06:56, 13 February 2020

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)

Questa pagina descrive le classi e le tecniche di base riguardanti l'uso della grafica in Lazarus. Approfondimenti sono trattati in articoli più specifici.

Librerie

Librerie grafiche - qui si possono osservare le principali librerie utilizzabili per lo sviluppo.

Altri articoli di grafica

Disegno 2D

  • ZenGL - libreria multi-piattaforma per lo sviluppo di giochi mediante OpenGL.
  • BGRABitmap - Disegno di forme e bitmap con funzioni di trasparenza, accesso diretto ai pixel, ecc.
  • LazRGBGraphics - Package per l'elaborazione veloce di immagini in memoria e per la manipolazione a livello di pixel (es. scan line).
  • fpvectorial - Supporto per leggere, modificare e salvare immagini vettoriali.
  • Double Gradient - Disegnare facilmente bitmap con 'doppio gradiente' e gradienti di 'grado n'.
  • Gradient Filler - TGradientFiller é il miglior modo di creare gradienti di grado n personalizzati in Lazarus.
  • PascalMagick - una API di semplice uso per interfacciarsi con ImageMagick, una suite multi-piattaforma free per creare, editare e comporre immagini bitmap.
  • Sample Graphics - collezioni di grafica create con Lazarus e strumenti di disegno.
  • Fast direct pixel access - confronto di velocità di alcuni metodi di accesso diretto ai pixel di immagini bitmap.
  • 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.

Disegno 3D

Grafici

  • TAChart - Componente per tracciamento di grafici in Lazarus
  • PlotPanel - Componente per disegnare e tracciare grafici con effetti di animazione
  • Perlin Noise - Un articolo sull'uso di Perlin Noise in applicazioni LCL.

Introduzione al modello Grafico della LCL

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.

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.

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.

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.

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.

Nel widgeset LCL-CustomDrawn le classi native sono implementate usando le non-native.

Tutte queste classi saranno meglio descritte nelle sezioni seguenti.

Lavorare con TCanvas

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)

Usare il font di default della GUI

Il font di default si ottiene con il seguente codice:

SelectObject(Canvas.Handle, GetStockObject(DEFAULT_GUI_FONT));

Disegnare un testo delimitato in larghezza

Usare la routine DrawText due volte, prima con l'opzione DT_CALCRECT e poi senza.
Esempio (Lazarus v1.6 con windows vista):

..
uses ... windows ...
..
var Image1: TImage;
    TextBox: TRect;
    Text: String;
...
Text:= ...;
// Prima calcoliamo la dimensione del testo, poi lo disegniamo:
TextBox:= Rect(10, 10, Image1.Width - 10, High(Integer));
// questa istruzione ricalcola TextBox.Bottom:
DrawText(Image1.canvas.Handle, PChar(Text), Length(Text),
  TextBox, DT_WORDBREAK or DT_INTERNAL or DT_CALCRECT);
// questa istruzione scrive il testo
DrawText(Image1.Canvas.Handle, PChar(Text), Length(Text),
  TextBox, DT_WORDBREAK or DT_INTERNAL);

Disegnare un testo con bordi netti (non antialiased)

Alcuni widgetsets lo supportano, nel qual caso si usa il seguente codice:

Canvas.Font.Quality := fqNonAntialiased;

Altri widgetsets come il gtk2 non lo supportano e disegnano sempre antialiased. Di seguito una semplice procedura per disegnare un testo a bordi netti sotto gtk2. Non tiene conto di tutti i casi, ma rende l'idea:

procedure PaintAliased(Canvas: TCanvas; x,y: integer; const TheText: string);
var
  w,h: integer;
  IntfImg: TLazIntfImage;
  Img: TBitmap;
  dy: Integer;
  dx: Integer;
  col: TFPColor;
  FontColor: TColor;
  c: TColor;
begin
  w:=0;
  h:=0;
  Canvas.GetTextSize(TheText,w,h);
  if (w<=0) or (h<=0) then exit;
  Img:=TBitmap.Create;
  IntfImg:=nil;
  try
    // paint text to a bitmap
    Img.Masked:=true;
    Img.SetSize(w,h);
    Img.Canvas.Brush.Style:=bsSolid;
    Img.Canvas.Brush.Color:=clWhite;
    Img.Canvas.FillRect(0,0,w,h);
    Img.Canvas.Font:=Canvas.Font;
    Img.Canvas.TextOut(0,0,TheText);
    // get memory image
    IntfImg:=Img.CreateIntfImage;
    // replace gray pixels
    FontColor:=ColorToRGB(Canvas.Font.Color);
    for dy:=0 to h-1 do begin
      for dx:=0 to w-1 do begin
        col:=IntfImg.Colors[dx,dy];
        c:=FPColorToTColor(col);
        if c<>FontColor then
          IntfImg.Colors[dx,dy]:=colTransparent;
      end;
    end;
    // create bitmap
    Img.LoadFromIntfImage(IntfImg);
    // paint
    Canvas.Draw(x,y,Img);
  finally
    IntfImg.Free;
    Img.Free;
  end;
end;

Lavorare con TBitmap e altri discendenti di TGraphic

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.

Caricare/Salvare un'immagine da/su disco

Per caricare un'immagine da disco usare TGraphic.LoadFromFile e per salvarla in un altro file usare TGraphic.SaveToFile. Tra le classi discendenti da TGraphic usare la classe appropriata per il formato di file. Vedere Developing_with_Graphics#Image_formats per una lista delle classi disponibili per i vari formati.

var
  MyBitmap: TBitmap;
begin
  MyBitmap := TBitmap.Create;
  try
    // Load from disk
    MyBitmap.LoadFromFile(MyEdit.Text);

    // Here you can use MyBitmap.Canvas to read/write to/from the image

    // Write back to another disk file
    MyBitmap.SaveToFile(MyEdit2.Text);
  finally
    MyBitmap.Free;
  end;
end;

Per altri formati la procedura è completamente identica, usando la classe appropriata. Ad esempio per immagini PNG:

var
  MyPNG: TPortableNetworkGraphic;
begin
  MyPNG := TPortableNetworkGraphic.Create;
  try
    // Load from disk
    MyPNG.LoadFromFile(MyEdit.Text);

    // Here you can use MyPNG.Canvas to read/write to/from the image

    // Write back to another disk file
    MyPNG.SaveToFile(MyEdit2.Text);
  finally
    MyPNG.Free;
  end;
end;

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:

var
  MyPicture: TPicture;
begin
  MyPicture := TPicture.Create;
  try
    // Load from disk
    MyPicture.LoadFromFile(MyEdit.Text);

    // Here you can use MyPicture.Graphic.Canvas to read/write to/from the image

    // Write back to another disk file
    MyPicture.SaveToFile(MyEdit2.Text);
  finally
    MyPicture.Free;
  end;
end;

File format addizionali per TImage

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

Accesso diretto ai pixel

Per accedere direttamente ai pixel di un bitmap si può usare sia una libreria esterna, come BGRABitmap, LazRGBGraphics e Graphics32, oppure usare la classe nativa di Lazarus TLazIntfImage. Per un confronto dei vari metodi vedere fast direct pixel access.

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.

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 TLazIntfImage which internally uses TRawImage.

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.

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!

Disegnare bitmap trasparenti a colori

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.

Il seguente esempio carica una bitmap da una risorsa di Windows, seleziona il colore clFucsia come colore trasparente, e poi disegna su 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;

Notare le operazioni in memoria eseguite con TMemoryStream, necessarie per caricare correttamente l'immagine.

Catturare un'immagine dello schermo

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:

uses Graphics, LCLIntf, LCLType;
  ...
var
  MyBitmap: TBitmap;
  ScreenDC: HDC;
begin
  MyBitmap := TBitmap.Create;
  ScreenDC := GetDC(0);
  MyBitmap.LoadFromDevice(ScreenDC);
  ReleaseDC(0,ScreenDC);
  ...

Lavorare con TLazIntfImage, TRawImage e TLazCanvas

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.

TRawImage è derivato dal tipo 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.

L'esempio sottostante mostra come scegliere il formato dei dati, richiedere a TRawImage di crearlo, collegarlo a TLazIntfImage; quindi collega un TLazCanvas a TLazIntfImage:

uses graphtype, intfgraphics, lazcanvas;

var
  AImage: TLazIntfImage;
  ACanvas: TLazCanvas;
  lRawImage: TRawImage;
begin
    lRawImage.Init;
    lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
    lRawImage.CreateData(True);
    AImage := TLazIntfImage.Create(0,0);
    AImage.SetRawImage(lRawImage);
    ACanvas := TLazCanvas.Create(AImage);

Inizializzare una TLazIntfImage

Non si può semplicemente creare una istanza di TLazIntfImage e cominciare ad utilizzarla. E' necessario aggiungervi spazio di memoria. Ci sono tre modi:

1. Collegare TLazIntfImage ad una TRawImage

2. Caricare la memoria da una TBitmap. (TLazIntfImage copierà la memoria da TBitmap e se nescollegherà).

   SrcIntfImg:=TLazIntfImage.Create(0,0);
   SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);

3. Creare la memoria sulla base di una descrizione di un'immagine grezza, in questo modo:

   IntfImg := TLazIntfImage.Create(0,0);
   IntfImg.DataDescription:=GetDescriptionFromDevice(0);
   IntfImg.SetSize(10,10);

Il device 0 in GetDescriptionFromDevice(0) fa riferimento al formato corrente dello schermo.

TLazIntfImage.LoadFromFile

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.

uses LazLogger, Graphics, IntfGraphics, GraphType;
procedure TForm1.FormCreate(Sender: TObject);
var
  AImage: TLazIntfImage;
  lRawImage: TRawImage;
begin
  // create a TLazIntfImage with 32 bits per pixel, alpha 8bit, red 8 bit, green 8bit, blue 8bit,
  // Bits In Order: bit 0 is pixel 0, Top To Bottom: line 0 is top
  lRawImage.Init;
  lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
  lRawImage.CreateData(false);
  AImage := TLazIntfImage.Create(0,0);
  try
    AImage.SetRawImage(lRawImage);
    // Load an image from disk.
    // It uses the file extension to select the right registered image reader.
    // The AImage will be resized to the width, height of the loaded image.
    AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
    debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
  finally
    AImage.Free;
  end;
end;

Caricare una TLazIntfImage in una TImage

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:

    Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);

Osservazioni:

  • Per caricare una transparent TLazIntfImage bisogna impostare Image1.Transparent a true.
  • 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.

Esempio di sfumatura

Un esempio di sfumatura con TLazIntfImage

{ Questo codice è stato ripreso dal progetto
$LazarusPath/examples/lazintfimage/fadein1.lpi . }
uses LCLType, // HBitmap type
     IntfGraphics, // TLazIntfImage type
     fpImage; // TFPColor type
...
 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.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
     TempBitmap.Handle:=ImgHandle;
     TempBitmap.MaskHandle:=ImgMaskHandle;
     Canvas.Draw(0,0,TempBitmap);
   end;
   SrcIntfImg.Free;
   TempIntfImg.Free;
   TempBitmap.Free;
 end;

Esempio di immagine con uno spefifico formato

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:

uses LCLType, // HBitmap type
     IntfGraphics, // TLazIntfImage type
     fpImage; // TFPColor type
...
type
  TRGBTripleArray = array[0..32767] of TRGBTriple;
  PRGBTripleArray = ^TRGBTripleArray;

procedure TForm1.FadeIn2(aBitMap: TBitMap);
 var
   IntfImg1, IntfImg2: TLazIntfImage;
   ImgHandle,ImgMaskHandle: HBitmap;
   FadeStep: Integer;
   px, py: Integer;
   CurColor: TFPColor;
   TempBitmap: TBitmap;
   Row1, Row2: PRGBTripleArray;
 begin
   IntfImg1:=TLazIntfImage.Create(0,0);
   IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);

   IntfImg2:=TLazIntfImage.Create(0,0);
   IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);

   TempBitmap:=TBitmap.Create;
   
   //with Scanline-like
   for FadeStep:=1 to 32 do begin
     for py:=0 to IntfImg1.Height-1 do begin
       Row1 := IntfImg1.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
       Row2 := IntfImg2.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
       for px:=0 to IntfImg1.Width-1 do begin
         Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
         Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Fading
         Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
       end;
     end;
     IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
     
     TempBitmap.Handle:=ImgHandle;
     TempBitmap.MaskHandle:=ImgMaskHandle;
     Canvas.Draw(0,0,TempBitmap);
   end; 

   IntfImg1.Free;
   IntfImg2.Free;
   TempBitmap.Free;
 end;

Conversione tra TLazIntfImage e TBitmap

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.

uses
  ...GraphType, IntfGraphics, LCLType, LCLProc,  LCLIntf ...

procedure TForm1.Button4Click(Sender: TObject);
var
  b: TBitmap;
  t: TLazIntfImage;
begin
  b := TBitmap.Create;
  try
    b.LoadFromFile('test.bmp');
    t := b.CreateIntfImage;

    // Read and/or write to the pixels
    t.Colors[10,20] := colGreen;

    b.LoadFromIntfImage(t);
  finally
    t.Free;
    b.Free;
  end;
end;

Usare StretchDraw non-nativo da LazCanvas

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;

Nell'unità fpcanvas sono disponibili altri tipo di interpolazione.

uses intfgraphics, lazcanvas;

procedure TForm1.StretchDrawBitmapToBitmap(SourceBitmap, DestBitmap: TBitmap; DestWidth, DestHeight: integer);
var
  DestIntfImage, SourceIntfImage: TLazIntfImage;
  DestCanvas: TLazCanvas;
begin
  // Prepare the destination

  DestIntfImage := TLazIntfImage.Create(0, 0);
  DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);

  DestCanvas := TLazCanvas.Create(DestIntfImage);

  //Prepare the source
  SourceIntfImage := TLazIntfImage.Create(0, 0);
  SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);

  // Execute the stretch draw via TFPSharpInterpolation
  DestCanvas.Interpolation := TFPSharpInterpolation.Create;
  DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);

  // Reload the image into the TBitmap
  DestBitmap.LoadFromIntfImage(DestIntfImage);

  SourceIntfImage.Free;
  DestCanvas.Interpolation.Free;  
  DestCanvas.Free;
  DestIntfImage.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  Bmp, DestBitmap: TBitmap;
begin
  // Prepare the destination
  DestBitmap := TBitmap.Create;
  DestBitmap.Width := 100;
  DestBitmap.Height := 100;

  Bmp := TBitmap.Create;
  Bmp.Width := 10;
  Bmp.Height := 10;
  Bmp.Canvas.Pen.Color := clYellow;
  Bmp.Canvas.Brush.Color := clYellow;
  Bmp.Canvas.Rectangle(0, 0, 10, 10);
  StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
  Canvas.Draw(0, 0, Bmp);
  Canvas.Draw(100, 100, DestBitmap);
end;

Grafica nei Movimenti - Come Evitare lo Sfarfallio

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.

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.

Se si disegna direttamente su TCanvas, si hanno a disposizione le seguenti opzioni:

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.

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

Light bulb  Note: Do not use this during OnPaint.
with Image1.Picture.Bitmap do begin
  Width:=100;
  Height:=120;
end;

Same in one step:

with Image1.Picture.Bitmap do begin
  SetSize(100, 120);
end;

Painting on the bitmap of a TImage

Light bulb  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;
Light bulb  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 the area manually with Image1.Invalidate. This will not immediately call OnPaint and you can call Invalidate as many times as you want.

procedure TForm.Image1Paint(Sender: TObject);
begin
  // paint a line
  Canvas.Pen.Color := clRed;
  Canvas.Line(0, 0, Width, Height);
end;

Draw on the OnPaint event

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.

procedure TForm.Form1Paint(Sender: TObject);
begin
  // paint a line
  Canvas.Pen.Color := clRed;
  Canvas.Line(0, 0, Width, Height);
end;

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;

It is destroyed automatically, because we use Self as owner.

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.

Image formats

Here is a table with the correct class to use for each image format.

Format Image class Unit
Cursor (cur) TCursor Graphics
Bitmap (bmp) TBitmap Graphics
Windows icon (ico) TIcon Graphics
Mac OS X icon (icns) TicnsIcon Graphics
Pixmap (xpm) TPixmap Graphics
Portable Network Graphic (png) TPortableNetworkGraphic Graphics
JPEG (jpg, jpeg) TJpegImage Graphics
PNM (pnm) TPortableAnyMapGraphic Graphics
Tiff (tif, tiff) TTiffImage Graphics

See also the list of fcl-image supported formats.

Converting formats

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.

Converting Bitmap to PNG and saving it to a file:

procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
var
  png : TPortableNetworkGraphic; 
begin 
  png := TPortableNetworkGraphic.Create;
  try
    png.Assign(bmp);
    png.SaveToFile(PngFileName);
  finally 
    png.Free;
  end;
end;

Pixel Formats

TColor

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.

To convert from separate RGB channels to TColor use:

RGBToColor(RedVal, GreenVal, BlueVal);

To get each channel of a TColor variable use the Red, Green and Blue functions:

RedVal := Red(MyColor);
GreenVal := Green(MyColor);
BlueVal := Blue(MyColor);

TFPColor

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.

The unit Graphics provides routines to convert between TColor and TFPColor:

function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
function FPColorToTColor(const FPColor: TFPColor): TColor;
function TColorToFPColor(const c: TColorRef): TFPColor; overload;
function TColorToFPColor(const c: TColor): TFPColor; overload; // does not work on system color

Drawing with fcl-image

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.

For more information, please read the article about fcl-image.

Common OnPaint Error

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,

This is bad:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Shape1Paint(Self); // Call Shape1Onpaint event
  Shape1.Invalidate; // Invoke actual painting

  ... more code for Button1 ...  
end;

This is good:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ... code for Button1 ... 
  Set some condition; 
  // Shape1.Invalidate; // May be necessary on some occasions
end;

// Shape1Paint should be attached to the OnPaint event of shape object !
procedure TForm1.Shape1Paint(Sender: TObject);
var
  Myrect: TRect;
begin   
  if some condition then 
    with Shape1.Canvas do
    begin
      ... lots of stuff ...
    end;
end;

Some useful examples

Example 1: Drawing on loaded JPEG with TImage

Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:

function TForm1.LoadAndDraw(const sFileName: String): Boolean;
var jpg: TPicture;
begin
  bm:=TBitmap.Create;
  jpg:=TPicture.Create;
  jpg.LoadFromFile(sFileName);
  bm.SetSize(jpg.Width, jpg.Height);
  bm.Canvas.Draw(0, 0, jpg.Bitmap);
 
  image.Picture.Bitmap.SetSize(bm.Width, bm.Height);
  Image.Picture.Bitmap.Canvas.Draw(0, 0, bm);
 
  Image.Picture.Bitmap.Canvas.Pen.Color:=clRed;
  Image.Picture.Bitmap.Canvas.Line(0,0,140,140);
 
  jpg.Free;
end;

Example 2: Drawing on controls of Form

1) Create a New project - Application, add to uses section next modules if needed: Types,Controls,Graphics.

2) Place on form Button1, GroupBox1 and RadioGroup1

3) Place on GroupBox1 one more button - Button2

4) Your TForm1.Create should looks like:

 procedure TForm1.FormCreate(Sender: TObject);
 var
   i: Integer;
 begin
   for i:=0 to Self.ControlCount-1 do
       RadioGroup1.Items.AddObject(Controls[i].Name,Controls[i]);
 
   RadioGroup1.Items.AddObject(Button2.Name,Button2);
 end;

5) For RadioGroup1 create handler of OnSelectionChanged event:

 procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
 begin
   Self.Repaint;
 end;

6) Add to public section of your form procedure HighlightControl:

  procedure TForm1.HighlightControl(AControl: TControl);
  var
    R: Types.TRect;
    aCC: TControlCanvas;
  begin
    R:=AControl.BoundsRect;
    InflateRect(R,2,2);          // make rect a bit bigger then control
    aCC:=TControlCanvas.Create;
    aCC.Control:=AControl.Parent;
    aCC.Pen.Color:=clGreen;
    aCC.Pen.Width:=5;
    aCC.Pen.Style:=psSolid;
    aCC.Brush.Style:=bsClear;
    aCC.Rectangle(R);
    aCC.free;
  end;

See also