Sign in Join
Wiki copytemporary

Developing with Graphics: what changed

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

Title: Developing with Graphics

OldNewLine
1+This page describes the basic classes and techniques regarding drawing graphics with Lazarus. Other more specific topics are in separate articles.
2+
3+## Libraries
4+
5+[[Graphics libraries]] - here you can see the main graphic libraries you can use to develop.
6+
7+## Introduction to the Graphics model of the LCL
8+
9+The Lazarus Component Library (LCL) provides two kinds of drawing class: Native classes and non-native classes. Native graphics classes are the most traditional way of drawing graphics in the LCL and are also the most important one, while the non-native classes are complementary, but also very important. The native classes are mostly located in the unit **Graphics** of the LCL. These classes are: [[TBitmap]], [[TCanvas]], [[TFont]], [[TBrush]], [[TPen]], [[TPortableNetworkGraphic]], etc.
10+
11+TCanvas is a class capable of executing drawings. It cannot exist alone and must either be attached to something visible (or at least which may possibly be visible), such as a visual control descending from TControl, or be attached to an off-screen buffer from a TRasterImage descendent (TBitmap is the most commonly used). TFont, TBrush and TPen describe how the drawing of various operations will be executed in the Canvas.
12+
13+TRasterImage (usually used via its descendant TBitmap) is a memory area reserved for drawing graphics, but it is created for maximum compatibility with the native Canvas and therefore in LCL-Gtk2 in X11 it is located in the X11 server, which makes pixel access via the Pixels property extremely slow. In Windows it is very fast because Windows allows creating a locally allocated image which can receive drawings from a Windows Canvas.
14+
15+Besides these there are also non-native drawing classes located in the units:
16+
17+- **graphtype**: [[TRawImage]] is the storage and description of a memory area which contains an image.
18+- **intfgraphics**: [[TLazIntfImage]] is an image which attaches itself to a TRawImage and takes care of converting between TFPColor and the real pixel format of the TRawImage.
19+- **lazcanvas**: [[TLazCanvas]] is a non-native Canvas which can draw to an image in a TLazIntfImage.
20+
21+The main difference between the native classes and the non-native ones is that the native ones do not perform exactly the same in all platforms, because the drawing is done by the underlying platform itself. The speed and also the exact final result of the image drawing can have differences. The non-native classes are guaranteed to perform exactly the same drawing in all platforms with a pixel level precision and they all perform reasonably fast in all platforms.
22+
23+In the widgetset LCL-CustomDrawn the native classes are implemented using the non-native ones.
24+
25+All of these classes will be better described in the sections below.
26+
27+## Working with TBitmap and other TGraphic descendents
28+
29+The TBitmap object stores a bitmap where you can draw before showing it to the screen. When you create a bitmap, you must specify the height and width, otherwise it will be zero and nothing will be drawn. And in general all other TRasterImage descendents provide the same capabilities. One should use the one which matches the format desired for output/input from the disk or TBitmap in case disk operations will not be performed as well as for the Windows Bitmap (*.bmp) format.
30+
31+### Loading/Saving an image from/to the disk
32+
33+To load an image from the disk use [TGraphic.LoadFromFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.loadfromfile.html) and to save it to another disk file use [TGraphic.SaveToFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.savetofile.html). Use the appropriate TGraphic descendent which matches the format expected. See [[Developing with Graphics|Developing_with_Graphics#Image_formats]] for a list of available image format classes.
34+
35+``` pascal
36+var
37+ MyBitmap: TBitmap;
38+begin
39+ MyBitmap := TBitmap.Create;
40+ try
41+ // Load from disk
42+ MyBitmap.LoadFromFile(MyEdit.Text);
43+
44+ // Here you can use MyBitmap.Canvas to read/write to/from the image
45+
46+ // Write back to another disk file
47+ MyBitmap.SaveToFile(MyEdit2.Text);
48+ finally
49+ MyBitmap.Free;
50+ end;
51+end;
52+```
53+
54+When using any other format the process is completely identical, just use the adequate class. For example, for PNG images:
55+
56+``` pascal
57+var
58+ MyPNG: TPortableNetworkGraphic;
59+begin
60+ MyPNG := TPortableNetworkGraphic.Create;
61+ try
62+ // Load from disk
63+ MyPNG.LoadFromFile(MyEdit.Text);
64+
65+ // Here you can use MyPNG.Canvas to read/write to/from the image
66+
67+ // Write back to another disk file
68+ MyPNG.SaveToFile(MyEdit2.Text);
69+ finally
70+ MyPNG.Free;
71+ end;
72+end;
73+```
74+
75+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:
76+
77+``` pascal
78+var
79+ MyPicture: TPicture;
80+begin
81+ MyPicture := TPicture.Create;
82+ try
83+ // Load from disk
84+ MyPicture.LoadFromFile(MyEdit.Text);
85+
86+ // Here you can use MyPicture.Graphic.Canvas to read/write to/from the image
87+
88+ // Write back to another disk file
89+ MyPicture.SaveToFile(MyEdit2.Text);
90+ finally
91+ MyPicture.Free;
92+ end;
93+end;
94+```
95+
96+The canvas of a form cannot be saved directly into a file and must fist be copied into an appropriate image. The following procedure copies a rectangle of the form's canvas into a .PNG file (which compresses text and graphs with good quality). If you wish to copy into a .JPG file (ideal for compressing photographs) instead of TPortableNetworkGraphic use tJPEGimage. If you wish to copy into a .BMP file (which does not compress the canvas) instead of TPortableNetworkGraphic use tBitmap:
97+
98+``` pascal
99+procedure saveCanvasToPNGfile(fromCanvas:tCanvas;left,bottom,right,top:integer;toFileName:string); //left,bottom,right,top define the rectangle of the canvas to be save into disk
100+var rectangleFrom,rectangleTo: TRect;
101+ myImage: TPortableNetworkGraphic; //use tJPEGimage to create JPGfile, or tBitmap to create BMP file
102+begin
103+ rectangleFrom:=rect(left,bottom,right,top);
104+ rectangleTo:=rect(0,0,right-left+1,top-bottom+1);
105+ myImage:=tPortableNetworkGraphic.create;
106+ myImage.width:=right-left+1;
107+ myImage.height:=top-bottom+1;
108+ myImage.canvas.copyRect(rectangleTo,fromCanvas,rectangleFrom);
109+ myImage.SaveToFile(toFileName);
110+ myImage.free;
111+end;
112+```
113+
114+### Additional file formats for TImage
115+
116+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
117+
118+### Indirect pixel access
119+
120+Color for pixels can be simply read or written using TBitmap.Canvas.Pixels property. Access using Pixels property is generally slow as lot of internal management operations need to be done on each execution. This can be speed up little bit be enclosing access to Pixels with TBitmap BeginUpdate and EndUpdate.
121+
122+### Direct pixel access
123+
124+To do directly access the pixels of bitmaps one can use:
125+
126+- TBitmap.ScanLine - which needs to be enclosed in BeginUpdate and EndUpdate. It uses internally same assess as with RAWImage so access needs to respect internal color channels structure according TRAWImage.Description.
127+- TBitmap.RAWImage - allows to directly access bitmap internal memory. Color channels can be swapped for some platforms so ReadChannels and WriteChannels need to be used to convert pixel color to TColor or TFPColor correctly. Or pixel data can be accessed directly with respect to TRAWImage.Description.
128+- external libraries - see [[Graphics libraries]]
129+- use the Lazarus native TLazIntfImage.
130+- implement custom optimized bitmap structure. For a comparison of pixel access methods, see [[Fast direct pixel access|fast direct pixel access]].
131+
132+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. There is a GetDataLineStart function, equivalent to Scanline, but only available for memory images like [[Developing with Graphics|TLazIntfImage]] which internally uses TRawImage.
133+
134+### Drawing color transparent bitmaps
135+
136+Bitmap files (*.bmp) usually do not store any information about transparency. However, a feature, implemented already in Lazarus 0.9.11, is "color transparency": This means that one particular color is defined to be transparent and is not painted. This common trick is applied when the `Transparent` property of the bitmap instance is set to `true`. Usually the color in the bottom-left corner of the bitmap will be taken to be the transparent color unless the property `TransparentColor` is set to another color value.
137+
138+The following example loads a bitmap from a resource named 'FOLDER_OPEN', selects a color to be transparent (clFuchsia) and then draws it to the form's Canvas in its `OnPaint` event. The assignment of the `TransparentColor` can be omitted if the bottom/left pixel does already contain the transparent color. In any case, it is highly recommended to change the `Transparent` property to `true` at the end of the assignment sequence.
139+
140+``` pascal
141+procedure TForm1.FormPaint(Sender: TObject);
142+var
143+ bmp: TBitmap;
144+begin
145+ bmp := TBitmap.Create;
146+ try
147+ bmp.LoadFromResourceName(HINSTANCE, 'FOLDER_OPEN');
148+ bmp.TransparentColor := clFuchsia;
149+ bmp.Transparent := true;
150+ Canvas.Draw(8, 8, bmp);
151+ finally
152+ bmp.Free;
153+ end;
154+end;
155+```
156+
157+### Drawing alpha-channel transparent bitmaps
158+
159+Descendants of TGraphic using a 32-bit-per-pixel pixelformat can have an "alpha" channel besides the normal red, green and blue color channels. This additional channel is used to provide transparency information in a gradual way. alpha=0 means "fully transparent", alpha=255 means "fully opaque", but any intermediate states are possible as well. bmp files usually do not come in this format, but the `TPortableNetworkGraphic` class (file extension: .png) is a very prominent example of an image format supporting an alpha channel by default. In the next code sample, we load a png file from the Lazarus images folder and draw it on the form in a transparent way:
160+
161+``` pascal
162+procedure TForm1.FormPaint(Sender: TObject);
163+var
164+ png: TCustomBitmap;
165+begin
166+ png := TPortableNetworkGraphic.Create;
167+ try
168+ png.LoadFromFile(LazarusDir + 'images/actions/execute.png');
169+ Canvas.Draw(8, 8, png);
170+ finally
171+ png.Free;
172+ end;
173+end;
174+```
175+
176+Note that the transparency-related settings discussed in the previous section are ignored this way.
177+
178+### Taking a screenshot of the screen
179+
180+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:
181+
182+``` pascal
183+uses Graphics, LCLIntf, LCLType;
184+ ...
185+var
186+ MyBitmap: TBitmap;
187+ ScreenDC: HDC;
188+begin
189+ MyBitmap := TBitmap.Create;
190+ ScreenDC := GetDC(0);
191+ MyBitmap.LoadFromDevice(ScreenDC);
192+ ReleaseDC(0, ScreenDC);
193+ ...
194+```
195+
196+Taking a screen shot but excluding your application from the screenshot:
197+
198+``` pascal
199+uses
200+ Graphics, LCLIntf, LCLType, ...;
201+
202+procedure TForm1.Button1Click(Sender: TObject);
203+var
204+ ScreenDC: HDC;
205+ LocalBitmap: TBitmap;
206+begin
207+ Hide;
208+ Application.ProcessMessages;
209+ LocalBitmap := TBitmap.Create;
210+ ScreenDC := GetDC(0);
211+ try
212+ LocalBitmap.LoadFromDevice(ScreenDC);
213+ LocalBitmap.SaveToFile('screenshot.bmp');
214+ finally
215+ LocalBitmap.Free;
216+ ReleaseDC(0, ScreenDC);
217+ Show;
218+ end;
219+end;
220+```
221+
222+## Working with TLazIntfImage, TRawImage and TLazCanvas
223+
224+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.
225+
226+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.
227+
228+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:
229+
230+``` pascal
231+uses graphtype, intfgraphics, lazcanvas;
232+
233+var
234+ AImage: TLazIntfImage;
235+ ACanvas: TLazCanvas;
236+ lRawImage: TRawImage;
237+begin
238+ lRawImage.Init;
239+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
240+ lRawImage.CreateData(True);
241+ AImage := TLazIntfImage.Create(0,0);
242+ AImage.SetRawImage(lRawImage);
243+ ACanvas := TLazCanvas.Create(AImage);
244+```
245+
246+### Initializing a TLazIntfImage
247+
248+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:
249+
250+1\. Attach it to a TRawImage
251+
252+2\. Load it from a TBitmap. Note that it will copy the memory of the TBitmap so it won't remain connected to it.
253+
254+``` pascal
255+ SrcIntfImg:=TLazIntfImage.Create(0,0);
256+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
257+```
258+
259+3\. Load it from a raw image description, like this:
260+
261+``` pascal
262+ IntfImg := TLazIntfImage.Create(0,0);
263+ IntfImg.DataDescription:=GetDescriptionFromDevice(0);
264+ IntfImg.SetSize(10,10);
265+```
266+
267+The 0 device in **GetDescriptionFromDevice(0)** uses the current screen format.
268+
269+### TLazIntfImage.LoadFromFile
270+
271+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.
272+
273+``` pascal
274+uses LazLogger, Graphics, IntfGraphics, GraphType;
275+procedure TForm1.FormCreate(Sender: TObject);
276+var
277+ AImage: TLazIntfImage;
278+ lRawImage: TRawImage;
279+begin
280+ // create a TLazIntfImage with 32 bits per pixel, alpha 8bit, red 8 bit, green 8bit, blue 8bit,
281+ // Bits In Order: bit 0 is pixel 0, Top To Bottom: line 0 is top
282+ lRawImage.Init;
283+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
284+ lRawImage.CreateData(false);
285+ AImage := TLazIntfImage.Create(0,0);
286+ try
287+ AImage.SetRawImage(lRawImage);
288+ // Load an image from disk.
289+ // It uses the file extension to select the right registered image reader.
290+ // The AImage will be resized to the width, height of the loaded image.
291+ AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
292+ debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
293+ finally
294+ AImage.Free;
295+ end;
296+end;
297+```
298+
299+### Loading a TLazIntfImage into a TImage
300+
301+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**:
302+
303+``` pascal
304+ Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
305+```
306+
307+**Notes:**
308+
309+- To load a **color-transparent** TLazIntfImage you have to set the **Image1.Transparent** to true. In "color-transparency", one color (usually the one of the pixel (0,0)) is declared to be transparent and is not drawn. This must be distinguished from "alpha-channel" transparency where a fourth channel is added to RGB images to describe the transparency of each pixel. See the next section for an example.
310+- 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.
311+
312+### Creating and drawing a transparent bitmap for a TImage
313+
314+"Transparency" in this section is understood as "alpha-channel transparency" where an additional color channel defines the transparency of each pixel. Usually, 24-bits-per-pixel images are extended to 32 bits-per-pixel to provide the alpha channel.
315+
316+In the following example we draw some semi-transparent shapes on a transparent image which then is displayed on a TImage component. A `TBitmap` is created for this purpose as buffer for the image. We activate its alpha channel by setting it `Pixelformat` to `pf32Bit` so that semi-transparent drawing is possible. Drawing itself it done on a `TLazIntfImage` and a `TLazCanvas` which give an easy access to the underlying fcl-image features, but simultaneously provide an easy interface to the LCL class `TBitmap`:
317+
318+``` pascal
319+uses
320+ fpImage, fpCanvas, IntfGraphics, LazCanvas;
321+
322+procedure TForm1.Button1Click(Sender: TObject);
323+var
324+ bmp: TBitmap;
325+ img: TLazIntfImage;
326+ cnv: TLazCanvas;
327+begin
328+ bmp := TBitmap.Create;
329+ try
330+ bmp.PixelFormat := pf32Bit; // IMPORTANT!
331+ bmp.SetSize(Image1.Width, Image1.Height);
332+ img := bmp.CreateIntfImage;
333+ try
334+ cnv := TLazCanvas.Create(img);
335+ try
336+ cnv.DrawingMode := dmAlphaBlend; // This activates the alpha-blend mode.
337+
338+ // Background
339+ cnv.Pen.Style := psClear;
340+ cnv.Brush.FPColor := colTransparent;
341+ cnv.FillRect(0, 0, img.Width, img.Height);
342+
343+ // Yellow opaque rectangle
344+ cnv.Brush.FPColor := FPColor($FFFF, $FFFF, 0);
345+ cnv.Rectangle(10, 10, 190, 100);
346+
347+ // Overlapping semi-transparent red circle
348+ cnv.Brush.FPColor := FPColor($FFFF, 0, 0, $4000);
349+ cnv.Ellipse(60, 60, 140, 140);
350+
351+ // Overlapping semi-transparent blue circle
352+ cnv.Brush.FPColor := FPColor(0, 0, $FFFF, $8000);
353+ cnv.Ellipse(0, 100, 100, 200);
354+ finally
355+ cnv.Free;
356+ end;
357+
358+ bmp.LoadFromIntfImage(img);
359+ Image1.Picture.Assign(bmp);
360+ finally
361+ img.Free;
362+ end;
363+ finally
364+ bmp.Free;
365+ end;
366+end;
367+```
368+
369+### Fading example
370+
371+A fading example with TLazIntfImage
372+
373+``` pascal
374+{ This code has been taken from the $LazarusPath/examples/lazintfimage/fadein1.lpi project. }
375+uses LCLType, // HBitmap type
376+ IntfGraphics, // TLazIntfImage type
377+ fpImage; // TFPColor type
378+...
379+ procedure TForm1.FadeIn(ABitMap: TBitMap);
380+ var
381+ SrcIntfImg, TempIntfImg: TLazIntfImage;
382+ ImgHandle,ImgMaskHandle: HBitmap;
383+ FadeStep: Integer;
384+ px, py: Integer;
385+ CurColor: TFPColor;
386+ TempBitmap: TBitmap;
387+ begin
388+ SrcIntfImg:=TLazIntfImage.Create(0,0);
389+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
390+ TempIntfImg:=TLazIntfImage.Create(0,0);
391+ TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
392+ TempBitmap:=TBitmap.Create;
393+ for FadeStep:=1 to 32 do begin
394+ for py:=0 to SrcIntfImg.Height-1 do begin
395+ for px:=0 to SrcIntfImg.Width-1 do begin
396+ CurColor:=SrcIntfImg.Colors[px,py];
397+ CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
398+ CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
399+ CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
400+ TempIntfImg.Colors[px,py]:=CurColor;
401+ end;
402+ end;
403+ TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
404+ TempBitmap.Handle:=ImgHandle;
405+ TempBitmap.MaskHandle:=ImgMaskHandle;
406+ Canvas.Draw(0,0,TempBitmap);
407+ end;
408+ SrcIntfImg.Free;
409+ TempIntfImg.Free;
410+ TempBitmap.Free;
411+ end;
412+```
413+
414+### Image format specific example
415+
416+If you know that the TBitmap is using blue 8bit, green 8bit, red 8bit you can directly access the bytes, which is somewhat faster:
417+
418+``` pascal
419+uses LCLType, // HBitmap type
420+ IntfGraphics, // TLazIntfImage type
421+ fpImage; // TFPColor type
422+...
423+type
424+ TRGBTripleArray = array[0..32767] of TRGBTriple;
425+ PRGBTripleArray = ^TRGBTripleArray;
426+
427+procedure TForm1.FadeIn2(aBitMap: TBitMap);
428+ var
429+ IntfImg1, IntfImg2: TLazIntfImage;
430+ ImgHandle,ImgMaskHandle: HBitmap;
431+ FadeStep: Integer;
432+ px, py: Integer;
433+ CurColor: TFPColor;
434+ TempBitmap: TBitmap;
435+ Row1, Row2: PRGBTripleArray;
436+ begin
437+ IntfImg1:=TLazIntfImage.Create(0,0);
438+ IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
439+
440+ IntfImg2:=TLazIntfImage.Create(0,0);
441+ IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
442+
443+ TempBitmap:=TBitmap.Create;
444+
445+ //with Scanline-like
446+ for FadeStep:=1 to 32 do begin
447+ for py:=0 to IntfImg1.Height-1 do begin
448+ Row1 := IntfImg1.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
449+ Row2 := IntfImg2.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
450+ for px:=0 to IntfImg1.Width-1 do begin
451+ Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
452+ Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Fading
453+ Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
454+ end;
455+ end;
456+ IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
457+
458+ TempBitmap.Handle:=ImgHandle;
459+ TempBitmap.MaskHandle:=ImgMaskHandle;
460+ Canvas.Draw(0,0,TempBitmap);
461+ end;
462+
463+ IntfImg1.Free;
464+ IntfImg2.Free;
465+ TempBitmap.Free;
466+ end;
467+```
468+
469+### Conversion between TLazIntfImage and TBitmap
470+
471+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(); Here's the sample on how to create TLazIntfImage from TBitmap, modify it and then go back to the TBitmap.
472+
473+``` pascal
474+uses
475+ ...GraphType, IntfGraphics, LCLType, LCLProc, LCLIntf ...
476+
477+procedure TForm1.Button4Click(Sender: TObject);
478+var
479+ b: TBitmap;
480+ t: TLazIntfImage;
481+begin
482+ b := TBitmap.Create;
483+ try
484+ b.LoadFromFile('test.bmp');
485+ t := b.CreateIntfImage;
486+
487+ // Read and/or write to the pixels
488+ t.Colors[10,20] := colGreen;
489+
490+ b.LoadFromIntfImage(t);
491+ finally
492+ t.Free;
493+ b.Free;
494+ end;
495+end;
496+```
497+
498+### Using the non-native StretchDraw from LazCanvas
499+
500+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:
501+
502+`TLazCanvas.Interpolation := TFPSharpInterpolation.Create;`
503+
504+There are other interpolations available in the unit fpcanvas.
505+
506+``` pascal
507+uses
508+ intfgraphics, lazcanvas;
509+
510+procedure TForm1.StretchDrawBitmapToBitmap(SourceBitmap, DestBitmap: TBitmap; DestWidth, DestHeight: integer);
511+var
512+ DestIntfImage, SourceIntfImage: TLazIntfImage;
513+ DestCanvas: TLazCanvas;
514+begin
515+ // Prepare the destination
516+ DestIntfImage := TLazIntfImage.Create(0, 0);
517+ try
518+ DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);
519+ DestCanvas := TLazCanvas.Create(DestIntfImage);
520+ try
521+ //Prepare the source
522+ SourceIntfImage := TLazIntfImage.Create(0, 0);
523+ try
524+ SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);
525+ // Execute the stretch draw via TFPSharpInterpolation
526+ DestCanvas.Interpolation := TFPSharpInterpolation.Create;
527+ try
528+ DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);
529+ // Reload the image into the TBitmap
530+ DestBitmap.LoadFromIntfImage(DestIntfImage);
531+ finally
532+ DestCanvas.Interpolation.Free;
533+ end;
534+ finally
535+ SourceIntfImage.Free;
536+ end;
537+ finally
538+ DestCanvas.Free;
539+ end;
540+ finally
541+ DestIntfImage.Free;
542+ end;
543+end;
544+
545+procedure TForm1.FormPaint(Sender: TObject);
546+var
547+ Bmp, DestBitmap: TBitmap;
548+begin
549+ // Prepare the destination
550+ DestBitmap := TBitmap.Create;
551+ try
552+ DestBitmap.SetSize(100, 100);
553+ // Create and draw the source bitmap: a small red rectangle
554+ Bmp := TBitmap.Create;
555+ try
556+ Bmp.SetSize(10, 10);
557+ Bmp.Canvas.Pen.Color := clBlack;
558+ Bmp.Canvas.Brush.Color := clRed;
559+ Bmp.Canvas.Rectangle(0, 0, 10, 10);
560+ Canvas.Draw(10, 10, Bmp);
561+ // Stretch-draw the source bitmap onto the destination bitmap canvas
562+ StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
563+ Canvas.Draw(100, 100, DestBitmap);
564+ finally
565+ Bmp.Free;
566+ end;
567+ finally
568+ DestBitmap.Free;
569+ end;
570+end;
571+```
572+
573+### Clipping
574+
575+#### Clipping by a rectangle
576+
577+Anything outside the specified `ClipRect` of the canvas is removed when `Clipping` is true:
578+
579+<figure>
580+<img src="LazCanvas_ClipRect.png" title="LazCanvas_ClipRect.png" />
581+<figcaption>LazCanvas_ClipRect.png</figcaption>
582+</figure>
583+
584+``` Pascal
585+uses
586+ FPImage, IntfGraphics, LazCanvas;
587+
588+procedure TForm1.FormPaint(Sender: TObject);
589+var
590+ bmp: TBitmap;
591+ img: TLazIntfImage;
592+ cnv: TLazCanvas;
593+ Rc: TRect;
594+ polyPts: array[0..4] of TPoint = (
595+ (x:100;y:10), (x:190;y:80), (x:170;y:190), (x:30;y:190), (x:10;y:80)
596+ );
597+begin
598+ // Buffer bitmap
599+ bmp := TBitmap.Create;
600+ try
601+ bmp.SetSize(Width, Height);
602+ img := bmp.CreateIntfImage;
603+ try
604+ cnv := TLazCanvas.Create(img);
605+ try
606+ cnv.Brush.FPColor := colWhite;
607+ cnv.Clear;
608+
609+ // To better understand what is removed, we draw the outline of the
610+ // polygon to be clipped
611+ cnv.Pen.Style := psDot;
612+ cnv.Pen.FPColor := colRed;
613+ cnv.Polygon(polyPts);
614+
615+ // Define the clipping rectangle
616+ Rc := Rect(50, 10, 180, 170);
617+ // To understand where the clipping rectangle is we draw its outline
618+ cnv.Brush.Style := bsClear;
619+ cnv.Pen.FPColor := colSilver;
620+ cnv.Rectangle(Rc);
621+
622+ // Activate clipping
623+ cnv.ClipRect := Rc;
624+ cnv.Clipping := true;
625+
626+ // Draw the polygon to be clipped
627+ cnv.Brush.Style := bsSolid;
628+ cnv.Brush.FPColor := colRed;
629+ cnv.Polygon(polyPts);
630+ finally
631+ cnv.Free;
632+ end;
633+ bmp.LoadFromIntfImage(img);
634+ Canvas.Draw(0, 0, bmp);
635+ finally
636+ img.Free;
637+ end;
638+ finally
639+ bmp.Free;
640+ end;
641+end;
642+```
643+
644+#### Clipping by a region
645+
646+For more complex clipping `TLazCanvas` provides several kinds of "regions". Pixels are drawn only when they are inside the parts added to the region. `TLazCanvas` supports rectangular, elliptical and polygonal parts. In the following example, a region is created which contains an ellipse and a rectangle. Clipping is activated by using this region in the `SetLazClipRegion()` method of the `LazCanvas`:
647+
648+<figure>
649+<img src="LazCanvas_ClipRegion.png" title="LazCanvas_ClipRegion.png" />
650+<figcaption>LazCanvas_ClipRegion.png</figcaption>
651+</figure>
652+
653+``` Pascal
654+uses
655+ FPImage, FPCanvas, IntfGraphics, LazCanvas, LazRegions;
656+
657+procedure TForm1.FormPaint(Sender: TObject);
658+var
659+ bmp: TBitmap;
660+ img: TLazIntfImage;
661+ cnv: TLazCanvas;
662+ rgn: TLazRegion;
663+ Rc1, Rc2: TRect;
664+ polyPts: array[0..4] of TPoint = (
665+ (x:100;y:10), (x:190;y:80), (x:170;y:190), (x:30;y:190), (x:10;y:80)
666+ );
667+begin
668+ // Buffer bitmap
669+ bmp := TBitmap.Create;
670+ try
671+ bmp.SetSize(ClientWidth, ClientHeight);
672+ img := bmp.CreateIntfImage;
673+ try
674+ cnv := TLazCanvas.Create(img);
675+ cnv.Brush.FPColor := colWhite;
676+ cnv.Clear;
677+
678+ // To better understand what is clipped, draw the outline of the polygon
679+ // to be clipped
680+ cnv.Pen.Style := psDot;
681+ cnv.Pen.FPColor := colRed;
682+ cnv.Brush.Style := bsClear;
683+ cnv.Polygon(polyPts);
684+
685+ // ... and we draw the outline of the parts in the clipping region:
686+ // We clip by an elliptical region...
687+ Rc1 := Rect(20, 20, 140, 140);
688+ cnv.Pen.FPColor := colSilver;
689+ cnv.Ellipse(Rc1);
690+ // ... and by a rectangular region
691+ Rc2 := Rect(100, 100, 200, 170);
692+ cnv.Rectangle(Rc2);
693+
694+ // Create the clipping region
695+ rgn := TLazRegion.Create;
696+ try
697+ // Show pixels that are inside the ellipse...
698+ rgn.AddEllipse(Rc1.Left, Rc1.Top, Rc1.Right, Rc1.Bottom);
699+ // ... and the rectangle.
700+ rgn.AddRectangle(Rc2);
701+ // Activate the clip region
702+ cnv.SetLazClipRegion(rgn);
703+
704+ // Draw the polygon to be clipped
705+ cnv.Brush.FPColor := colRed;
706+ cnv.Brush.Style := bsSolid;
707+ cnv.Pen.Style := psSolid;
708+ cnv.Pen.FPColor := colBlack;
709+ cnv.Polygon(polyPts);
710+ finally
711+ cnv.Free;
712+ end;
713+
714+ // Draw the buffer bitmap in the Paintbox
715+ bmp.LoadFromIntfImage(img);
716+ Canvas.Draw(0, 0, bmp);
717+ finally
718+ img.Free;
719+ end;
720+ finally
721+ bmp.Free;
722+ end;
723+end;
724+```
725+
726+## Motion Graphics - How to Avoid flickering
727+
728+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.
729+
730+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.
731+
732+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.
733+
734+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.
735+
736+Now we will examine the options we have for drawing to a Canvas:
737+
738+- Draw to a TImage
739+- Draw on the OnPaint event of the form, a TPaintBox or another control
740+- Create a custom control which draws itself
741+- Using A.J. Venter's gamepack
742+
743+### Draw to a TImage
744+
745+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.
746+
747+**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.
748+
749+#### Resizing the bitmap of a TImage
750+
751+> **Note:** Do not use this during OnPaint.
752+
753+``` pascal
754+with Image1.Picture.Bitmap do begin
755+ Width:=100;
756+ Height:=120;
757+end;
758+```
759+
760+Same in one step:
761+
762+``` pascal
763+with Image1.Picture.Bitmap do begin
764+ SetSize(100, 120);
765+end;
766+```
767+
768+#### Painting on the bitmap of a TImage
769+
770+> **Note:** Do not use this during OnPaint.
771+
772+``` pascal
773+with Image1.Picture.Bitmap.Canvas do begin
774+ // fill the entire bitmap with red
775+ Brush.Color := clRed;
776+ FillRect(0, 0, Width, Height);
777+end;
778+```
779+
780+> **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.
781+
782+Another example:
783+
784+``` pascal
785+procedure TForm1.BitBtn1Click(Sender: TObject);
786+var
787+ x, y: Integer;
788+begin
789+ // Draws the backgroung
790+ MyImage.Canvas.Pen.Color := clWhite;
791+ MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
792+
793+ // Draws squares
794+ MyImage.Canvas.Pen.Color := clBlack;
795+ for x := 1 to 8 do
796+ for y := 1 to 8 do
797+ MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
798+ Round(x * Image.Width / 8), Round(y * Image.Height / 8));
799+end;
800+```
801+
802+#### Painting on the volatile visual area of the TImage
803+
804+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.
805+
806+``` pascal
807+procedure TForm.Image1Paint(Sender: TObject);
808+begin
809+ // paint a line
810+ Canvas.Pen.Color := clRed;
811+ Canvas.Line(0, 0, Width, Height);
812+end;
813+```
814+
815+### Draw on the OnPaint event
816+
817+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.
818+
819+``` pascal
820+procedure TForm.Form1Paint(Sender: TObject);
821+begin
822+ // paint a line
823+ Canvas.Pen.Color := clRed;
824+ Canvas.Line(0, 0, Width, Height);
825+end;
826+```
827+
828+### Create a custom control which draws itself
829+
830+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.
831+
832+Here is an example custom control:
833+
834+``` pascal
835+uses
836+ Classes, SysUtils, Controls, Graphics, LCLType;
837+
838+type
839+ TMyDrawingControl = class(TCustomControl)
840+ public
841+ procedure EraseBackground(DC: HDC); override;
842+ procedure Paint; override;
843+ end;
844+
845+implementation
846+
847+procedure TMyDrawingControl.EraseBackground(DC: HDC);
848+begin
849+ // Uncomment this to enable default background erasing
850+ //inherited EraseBackground(DC);
851+end;
852+
853+procedure TMyDrawingControl.Paint;
854+var
855+ x, y: Integer;
856+ Bitmap: TBitmap;
857+begin
858+ Bitmap := TBitmap.Create;
859+ try
860+ // Initializes the Bitmap Size
861+ Bitmap.Height := Height;
862+ Bitmap.Width := Width;
863+
864+ // Draws the background
865+ Bitmap.Canvas.Pen.Color := clWhite;
866+ Bitmap.Canvas.Rectangle(0, 0, Width, Height);
867+
868+ // Draws squares
869+ Bitmap.Canvas.Pen.Color := clBlack;
870+ for x := 1 to 8 do
871+ for y := 1 to 8 do
872+ Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
873+ Round(x * Width / 8), Round(y * Height / 8));
874+
875+ Canvas.Draw(0, 0, Bitmap);
876+ finally
877+ Bitmap.Free;
878+ end;
879+
880+ inherited Paint;
881+end;
882+```
883+
884+and how we create it on the form:
885+
886+``` pascal
887+procedure TMyForm.FormCreate(Sender: TObject);
888+begin
889+ MyDrawingControl := TMyDrawingControl.Create(Self);
890+ MyDrawingControl.Height := 400;
891+ MyDrawingControl.Width := 500;
892+ MyDrawingControl.Top := 0;
893+ MyDrawingControl.Left := 0;
894+ MyDrawingControl.Parent := Self;
895+ MyDrawingControl.DoubleBuffered := True;
896+end;
897+```
898+
899+It is destroyed automatically, because we use Self as owner.
900+
901+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.
902+
903+"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
904+
905+"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
906+
907+## Image formats
908+
909+Here is a table with the correct class to use for each image format.
910+
911+| Format | Image class | Unit |
912+|--------------------------------|-------------------------|----------|
913+| Cursor (cur) | TCursor | Graphics |
914+| Bitmap (bmp) | TBitmap | Graphics |
915+| Windows icon (ico) | TIcon | Graphics |
916+| Mac OS X icon (icns) | TicnsIcon | Graphics |
917+| Pixmap (xpm) | TPixmap | Graphics |
918+| Portable Network Graphic (png) | TPortableNetworkGraphic | Graphics |
919+| JPEG (jpg, jpeg) | TJpegImage | Graphics |
920+| PNM (pnm) | TPortableAnyMapGraphic | Graphics |
921+| Tiff (tif, tiff) | TTiffImage | Graphics |
922+
923+See also the list of [[fcl-image|fcl-image supported formats]].
924+
925+### Converting formats
926+
927+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.
928+
929+Converting Bitmap to PNG and saving it to a file:
930+
931+``` pascal
932+procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
933+var
934+ png : TPortableNetworkGraphic;
935+begin
936+ png := TPortableNetworkGraphic.Create;
937+ try
938+ png.Assign(bmp);
939+ png.SaveToFile(PngFileName);
940+ finally
941+ png.Free;
942+ end;
943+end;
944+```
945+
946+## Pixel Formats
947+
948+### TColor
949+
950+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.
951+
952+To convert from separate RGB channels to TColor use:
953+
954+``` pascal
955+RGBToColor(RedVal, GreenVal, BlueVal);
956+```
957+
958+To get each channel of a TColor variable use the Red, Green and Blue functions:
959+
960+``` pascal
961+RedVal := Red(MyColor);
962+GreenVal := Green(MyColor);
963+BlueVal := Blue(MyColor);
964+```
965+
966+### TFPColor
967+
968+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.
969+
970+The unit Graphics provides routines to convert between TColor and TFPColor:
971+
972+``` pascal
973+function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
974+function FPColorToTColor(const FPColor: TFPColor): TColor;
975+function TColorToFPColor(const c: TColorRef): TFPColor; overload;
976+function TColorToFPColor(const c: TColor): TFPColor; overload; // does not work on system color
977+```
978+
979+## Drawing with fcl-image
980+
981+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.
982+
983+For more information, please read the article about [[fcl-image]].
984+
985+## Common OnPaint Error
986+
987+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.
988+
989+This is bad:
990+
991+``` pascal
992+procedure TForm1.Button1Click(Sender: TObject);
993+begin
994+ Shape1Paint(Self); // Call Shape1 OnPaint event
995+ Shape1.Invalidate; // Invoke actual painting
996+
997+ //more code for Button1
998+end;
999+```
1000+
1001+This is good:
1002+
1003+``` pascal
1004+procedure TForm1.Button1Click(Sender: TObject);
1005+begin
1006+ //code for Button1
1007+ //set some condition
1008+ Shape1.Invalidate; // May be necessary on some occasions
1009+end;
1010+
1011+// Shape1Paint should be attached to the OnPaint event of shape object !
1012+procedure TForm1.Shape1Paint(Sender: TObject);
1013+begin
1014+ if some_condition then
1015+ with Shape1.Canvas do
1016+ begin
1017+ //lots of stuff
1018+ end;
1019+end;
1020+```
1021+
1022+## Some useful examples
1023+
1024+### Example 1: Drawing on loaded JPEG with TImage
1025+
1026+Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:
1027+
1028+``` pascal
1029+procedure TForm1.LoadAndDraw(const sFileName: String);
1030+var
1031+ jpg: TPicture;
1032+begin
1033+ jpg := TPicture.Create;
1034+ try
1035+ jpg.LoadFromFile(sFileName);
1036+
1037+ Image.Picture.Bitmap.SetSize(jpg.Width, jpg.Height);
1038+ Image.Picture.Bitmap.Canvas.Draw(0, 0, jpg.Bitmap);
1039+
1040+ Image.Picture.Bitmap.Canvas.Pen.Color := clRed;
1041+ Image.Picture.Bitmap.Canvas.Line(0, 0, 140, 140);
1042+ finally
1043+ jpg.Free;
1044+ end;
1045+end;
1046+```
1047+
1048+### Example 2: Drawing on controls of Form
1049+
1050+1\) Create a New project - Application, add to uses section next modules if needed: Types, Controls, Graphics.
1051+
1052+2\) Place on form Button1, GroupBox1 and RadioGroup1
1053+
1054+3\) Place on GroupBox1 one more button - Button2
1055+
1056+4\) Your TForm1.Create should looks like:
1057+
1058+``` pascal
1059+procedure TForm1.FormCreate(Sender: TObject);
1060+var
1061+ i: Integer;
1062+begin
1063+ for i := 0 to Self.ControlCount - 1 do
1064+ RadioGroup1.Items.AddObject(Controls[i].Name, Controls[i]);
1065+
1066+ RadioGroup1.Items.AddObject(Button2.Name,Button2);
1067+end;
1068+```
1069+
1070+5\) For RadioGroup1 create handler of OnSelectionChanged event:
1071+
1072+``` pascal
1073+procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
1074+begin
1075+ Self.Repaint;
1076+end;
1077+```
1078+
1079+6\) Add to public section of your form procedure HighlightControl:
1080+
1081+``` pascal
1082+procedure TForm1.HighlightControl(AControl: TControl);
1083+var
1084+ R: Types.TRect;
1085+ aCC: TControlCanvas;
1086+begin
1087+ R := AControl.BoundsRect;
1088+ InflateRect(R, 2, 2); // make rect a bit bigger then control
1089+ aCC := TControlCanvas.Create;
1090+ aCC.Control := AControl.Parent;
1091+ aCC.Pen.Color := clGreen;
1092+ aCC.Pen.Width := 5;
1093+ aCC.Pen.Style := psSolid;
1094+ aCC.Brush.Style := bsClear;
1095+ aCC.Rectangle(R);
1096+ aCC.free;
1097+end;
1098+```
1099+
1100+## See also
1101+
1102+- [[Fast direct pixel access]]
1103+
1104+### 2D drawing
1105+
1106+- [[ZenGL]] - cross-platform game development library using OpenGL.
1107+- [[BGRABitmap]] - Drawing shapes and bitmaps with transparency, direct access to pixels, etc.
1108+- [[LazRGBGraphics]] - A package for fast in memory image processing and pixel manipulations (like scan line).
1109+- [[fpvectorial]] - Offers support to read, modify and write vectorial images.
1110+- [[Double Gradient]] - Draw 'double gradient' & 'n gradient' bitmaps easy.
1111+- [[Gradient Filler]] - TGradientFiller is the best way to create custom n gradients in Lazarus.
1112+- [[PascalMagick]] - an easy to use API for interfacing with [ImageMagick](http://www.imagemagick.org), a multiplatform free software suite to create, edit, and compose bitmap images.
1113+- [[Sample Graphics]] - graphics gallery created with Lazarus and drawing tools
1114+- [[Fast direct pixel access]] - speed comparison of some methods for direct bitmap pixel access
1115+- [AggPas](http://www.crossgl.com/aggpas/) - AggPas is an Object Pascal native port of the Anti-Grain Geometry library. It is fast and very powerful with anti-aliased drawing and subpixel accuracy. You can think of AggPas as of a rendering engine that produces pixel images in memory from some vectorial data. Both Lazarus and [[fpGUI]] bundle AggPas.
1116+
1117+### 3D drawing
1118+
1119+- [[OpenGL]] - OpenGL (Open Graphics Library) is a cross-platform API for producing 3D computer graphics.
1120+- [[GLScene]] - A port of the 3D visual OpenGL graphics Library [GLScene](http://www.glscene.org).
1121+- [[Metal Framework|macOS Metal Framework]] - Render advanced 3D graphics and perform data-parallel computations using graphics processors.
1122+- [[Castle Game Engine]] - An open-source cross-platform 3D and 2D game engine ([official website](https://castle-engine.io/)).
1123+
1124+### Charts
1125+
1126+- [[TAChart]] - Charting component for Lazarus
1127+- [[PlotPanel]] - A plotting and charting component for animated graphs
1128+- [[Perlin Noise]] - An article about using Perlin Noise on LCL applications.
1129+
1130+---
1131+*From the Free Pascal wiki, page "Developing with Graphics", as it stood on 2024-05-23 after a change by Wp, 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