Sign in Join
Wiki copytemporary

Developing with Graphics: what changed

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

OldNewLine
11This page describes the basic classes and techniques regarding drawing graphics with Lazarus. Other more specific topics are in separate articles.
22
33## Libraries
44
55[[Graphics libraries]] - here you can see the main graphic libraries you can use to develop.
66
77## Introduction to the Graphics model of the LCL
88
99The 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.
1010
1111TCanvas 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.
1212
1313TRasterImage (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.
1414
1515Besides these there are also non-native drawing classes located in the units:
1616
1717- **graphtype**: [[TRawImage]] is the storage and description of a memory area which contains an image.
1818- **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.
1919- **lazcanvas**: [[TLazCanvas]] is a non-native Canvas which can draw to an image in a TLazIntfImage.
2020
2121The 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.
2222
2323In the widgetset LCL-CustomDrawn the native classes are implemented using the non-native ones.
2424
2525All of these classes will be better described in the sections below.
2626
2727## Working with TBitmap and other TGraphic descendents
2828
2929The 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.
3030
3131### Loading/Saving an image from/to the disk
3232
3333To 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.
3434
3535``` pascal
3636var
3737 MyBitmap: TBitmap;
3838begin
3939 MyBitmap := TBitmap.Create;
4040 try
4141 // Load from disk
4242 MyBitmap.LoadFromFile(MyEdit.Text);
4343
4444 // Here you can use MyBitmap.Canvas to read/write to/from the image
4545
4646 // Write back to another disk file
4747 MyBitmap.SaveToFile(MyEdit2.Text);
4848 finally
4949 MyBitmap.Free;
5050 end;
5151end;
5252```
5353
5454When using any other format the process is completely identical, just use the adequate class. For example, for PNG images:
5555
5656``` pascal
5757var
5858 MyPNG: TPortableNetworkGraphic;
5959begin
6060 MyPNG := TPortableNetworkGraphic.Create;
6161 try
6262 // Load from disk
6363 MyPNG.LoadFromFile(MyEdit.Text);
6464
6565 // Here you can use MyPNG.Canvas to read/write to/from the image
6666
6767 // Write back to another disk file
6868 MyPNG.SaveToFile(MyEdit2.Text);
6969 finally
7070 MyPNG.Free;
7171 end;
7272end;
7373```
7474
7575If 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:
7676
7777``` pascal
7878var
7979 MyPicture: TPicture;
8080begin
8181 MyPicture := TPicture.Create;
8282 try
8383 // Load from disk
8484 MyPicture.LoadFromFile(MyEdit.Text);
8585
8686 // Here you can use MyPicture.Graphic.Canvas to read/write to/from the image
8787
8888 // Write back to another disk file
8989 MyPicture.SaveToFile(MyEdit2.Text);
9090 finally
9191 MyPicture.Free;
9292 end;
9393end;
9494```
9595
9696The 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:
9797
9898``` pascal
9999procedure 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
100100var rectangleFrom,rectangleTo: TRect;
101101 myImage: TPortableNetworkGraphic; //use tJPEGimage to create JPGfile, or tBitmap to create BMP file
102102begin
103103 rectangleFrom:=rect(left,bottom,right,top);
104104 rectangleTo:=rect(0,0,right-left+1,top-bottom+1);
105105 myImage:=tPortableNetworkGraphic.create;
106106 myImage.width:=right-left+1;
107107 myImage.height:=top-bottom+1;
108108 myImage.canvas.copyRect(rectangleTo,fromCanvas,rectangleFrom);
109109 myImage.SaveToFile(toFileName);
110110 myImage.free;
111111end;
112112```
113113
114114### Additional file formats for TImage
115115
116116You 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
117117
118118### Indirect pixel access
119119
120120Color 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.
121121
122122### Direct pixel access
123123
124124To do directly access the pixels of bitmaps one can use:
125125
126126- 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.
127127- 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.
128128- external libraries - see [[Graphics libraries]]
129129- use the Lazarus native TLazIntfImage.
130130- implement custom optimized bitmap structure. For a comparison of pixel access methods, see [[Fast direct pixel access|fast direct pixel access]].
131131
132132On 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.
133133
134134### Drawing color transparent bitmaps
135135
136136Bitmap 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.
137137
138138The 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.
139139
140140``` pascal
141141procedure TForm1.FormPaint(Sender: TObject);
142142var
143143 bmp: TBitmap;
144144begin
145145 bmp := TBitmap.Create;
146146 try
147147 bmp.LoadFromResourceName(HINSTANCE, 'FOLDER_OPEN');
148148 bmp.TransparentColor := clFuchsia;
149149 bmp.Transparent := true;
150150 Canvas.Draw(8, 8, bmp);
151151 finally
152152 bmp.Free;
153153 end;
154154end;
155155```
156156
157157### Drawing alpha-channel transparent bitmaps
158158
159159Descendants 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:
160160
161161``` pascal
162162procedure TForm1.FormPaint(Sender: TObject);
163163var
164164 png: TCustomBitmap;
165165begin
166166 png := TPortableNetworkGraphic.Create;
167167 try
168168 png.LoadFromFile(LazarusDir + 'images/actions/execute.png');
169169 Canvas.Draw(8, 8, png);
170170 finally
171171 png.Free;
172172 end;
173173end;
174174```
175175
176176Note that the transparency-related settings discussed in the previous section are ignored this way.
177177
178178### Taking a screenshot of the screen
179179
180180Since 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:
181181
182182``` pascal
183183uses Graphics, LCLIntf, LCLType;
184184 ...
185185var
186186 MyBitmap: TBitmap;
187187 ScreenDC: HDC;
188188begin
189189 MyBitmap := TBitmap.Create;
190190 ScreenDC := GetDC(0);
191191 MyBitmap.LoadFromDevice(ScreenDC);
192192 ReleaseDC(0, ScreenDC);
193193 ...
194194```
195195
196196Taking a screen shot but excluding your application from the screenshot:
197197
198198``` pascal
199199uses
200200 Graphics, LCLIntf, LCLType, ...;
201201
202202procedure TForm1.Button1Click(Sender: TObject);
203203var
204204 ScreenDC: HDC;
205205 LocalBitmap: TBitmap;
206206begin
207207 Hide;
208208 Application.ProcessMessages;
209209 LocalBitmap := TBitmap.Create;
210210 ScreenDC := GetDC(0);
211211 try
212212 LocalBitmap.LoadFromDevice(ScreenDC);
213213 LocalBitmap.SaveToFile('screenshot.bmp');
214214 finally
215215 LocalBitmap.Free;
216216 ReleaseDC(0, ScreenDC);
217217 Show;
218218 end;
219219end;
220220```
221221
222222## Working with TLazIntfImage, TRawImage and TLazCanvas
223223
224224TLazIntfImage 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.
225225
226226TRawImage 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.
227227
228228The 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:
229229
230230``` pascal
231231uses graphtype, intfgraphics, lazcanvas;
232232
233233var
234234 AImage: TLazIntfImage;
235235 ACanvas: TLazCanvas;
236236 lRawImage: TRawImage;
237237begin
238238 lRawImage.Init;
239239 lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
240240 lRawImage.CreateData(True);
241241 AImage := TLazIntfImage.Create(0,0);
242242 AImage.SetRawImage(lRawImage);
243243 ACanvas := TLazCanvas.Create(AImage);
244244```
245245
246246### Initializing a TLazIntfImage
247247
248248One 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:
249249
2502501\. Attach it to a TRawImage
251251
2522522\. Load it from a TBitmap. Note that it will copy the memory of the TBitmap so it won't remain connected to it.
253253
254254``` pascal
255255 SrcIntfImg:=TLazIntfImage.Create(0,0);
256256 SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
257257```
258258
2592593\. Load it from a raw image description, like this:
260260
261261``` pascal
262262 IntfImg := TLazIntfImage.Create(0,0);
263263 IntfImg.DataDescription:=GetDescriptionFromDevice(0);
264264 IntfImg.SetSize(10,10);
265265```
266266
267267The 0 device in **GetDescriptionFromDevice(0)** uses the current screen format.
268268
269269### TLazIntfImage.LoadFromFile
270270
271271Here 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.
272272
273273``` pascal
274274uses LazLogger, Graphics, IntfGraphics, GraphType;
275275procedure TForm1.FormCreate(Sender: TObject);
276276var
277277 AImage: TLazIntfImage;
278278 lRawImage: TRawImage;
279279begin
280280 // create a TLazIntfImage with 32 bits per pixel, alpha 8bit, red 8 bit, green 8bit, blue 8bit,
281281 // Bits In Order: bit 0 is pixel 0, Top To Bottom: line 0 is top
282282 lRawImage.Init;
283283 lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
284284 lRawImage.CreateData(false);
285285 AImage := TLazIntfImage.Create(0,0);
286286 try
287287 AImage.SetRawImage(lRawImage);
288288 // Load an image from disk.
289289 // It uses the file extension to select the right registered image reader.
290290 // The AImage will be resized to the width, height of the loaded image.
291291 AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
292292 debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
293293 finally
294294 AImage.Free;
295295 end;
296296end;
297297```
298298
299299### Loading a TLazIntfImage into a TImage
300300
301301The 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**:
302302
303303``` pascal
304304 Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
305305```
306306
307307**Notes:**
308308
309309- 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.
310310- 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.
311311
312312### Creating and drawing a transparent bitmap for a TImage
313313
314314"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.
315315
316316In 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`:
317317
318318``` pascal
319319uses
320320 fpImage, fpCanvas, IntfGraphics, LazCanvas;
321321
322322procedure TForm1.Button1Click(Sender: TObject);
323323var
324324 bmp: TBitmap;
325325 img: TLazIntfImage;
326326 cnv: TLazCanvas;
327327begin
328328 bmp := TBitmap.Create;
329329 try
330330 bmp.PixelFormat := pf32Bit; // IMPORTANT!
331331 bmp.SetSize(Image1.Width, Image1.Height);
332332 img := bmp.CreateIntfImage;
333333 try
334334 cnv := TLazCanvas.Create(img);
335335 try
336336 cnv.DrawingMode := dmAlphaBlend; // This activates the alpha-blend mode.
337337
338338 // Background
339339 cnv.Pen.Style := psClear;
340340 cnv.Brush.FPColor := colTransparent;
341341 cnv.FillRect(0, 0, img.Width, img.Height);
342342
343343 // Yellow opaque rectangle
344344 cnv.Brush.FPColor := FPColor($FFFF, $FFFF, 0);
345345 cnv.Rectangle(10, 10, 190, 100);
346346
347347 // Overlapping semi-transparent red circle
348348 cnv.Brush.FPColor := FPColor($FFFF, 0, 0, $4000);
349349 cnv.Ellipse(60, 60, 140, 140);
350350
351351 // Overlapping semi-transparent blue circle
352352 cnv.Brush.FPColor := FPColor(0, 0, $FFFF, $8000);
353353 cnv.Ellipse(0, 100, 100, 200);
354354 finally
355355 cnv.Free;
356356 end;
357357
358358 bmp.LoadFromIntfImage(img);
359359 Image1.Picture.Assign(bmp);
360360 finally
361361 img.Free;
362362 end;
363363 finally
364364 bmp.Free;
365365 end;
366366end;
367367```
368368
369369### Fading example
370370
371371A fading example with TLazIntfImage
372372
373373``` pascal
374374{ This code has been taken from the $LazarusPath/examples/lazintfimage/fadein1.lpi project. }
375375uses LCLType, // HBitmap type
376376 IntfGraphics, // TLazIntfImage type
377377 fpImage; // TFPColor type
378378...
379379 procedure TForm1.FadeIn(ABitMap: TBitMap);
380380 var
381381 SrcIntfImg, TempIntfImg: TLazIntfImage;
382382 ImgHandle,ImgMaskHandle: HBitmap;
383383 FadeStep: Integer;
384384 px, py: Integer;
385385 CurColor: TFPColor;
386386 TempBitmap: TBitmap;
387387 begin
388388 SrcIntfImg:=TLazIntfImage.Create(0,0);
389389 SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
390390 TempIntfImg:=TLazIntfImage.Create(0,0);
391391 TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
392392 TempBitmap:=TBitmap.Create;
393393 for FadeStep:=1 to 32 do begin
394394 for py:=0 to SrcIntfImg.Height-1 do begin
395395 for px:=0 to SrcIntfImg.Width-1 do begin
396396 CurColor:=SrcIntfImg.Colors[px,py];
397397 CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
398398 CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
399399 CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
400400 TempIntfImg.Colors[px,py]:=CurColor;
401401 end;
402402 end;
403403 TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
404404 TempBitmap.Handle:=ImgHandle;
405405 TempBitmap.MaskHandle:=ImgMaskHandle;
406406 Canvas.Draw(0,0,TempBitmap);
407407 end;
408408 SrcIntfImg.Free;
409409 TempIntfImg.Free;
410410 TempBitmap.Free;
411411 end;
412412```
413413
414414### Image format specific example
415415
416416If you know that the TBitmap is using blue 8bit, green 8bit, red 8bit you can directly access the bytes, which is somewhat faster:
417417
418418``` pascal
419419uses LCLType, // HBitmap type
420420 IntfGraphics, // TLazIntfImage type
421421 fpImage; // TFPColor type
422422...
423423type
424424 TRGBTripleArray = array[0..32767] of TRGBTriple;
425425 PRGBTripleArray = ^TRGBTripleArray;
426426
427427procedure TForm1.FadeIn2(aBitMap: TBitMap);
428428 var
429429 IntfImg1, IntfImg2: TLazIntfImage;
430430 ImgHandle,ImgMaskHandle: HBitmap;
431431 FadeStep: Integer;
432432 px, py: Integer;
433433 CurColor: TFPColor;
434434 TempBitmap: TBitmap;
435435 Row1, Row2: PRGBTripleArray;
436436 begin
437437 IntfImg1:=TLazIntfImage.Create(0,0);
438438 IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
439439
440440 IntfImg2:=TLazIntfImage.Create(0,0);
441441 IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
442442
443443 TempBitmap:=TBitmap.Create;
444444
445445 //with Scanline-like
446446 for FadeStep:=1 to 32 do begin
447447 for py:=0 to IntfImg1.Height-1 do begin
448448 Row1 := IntfImg1.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
449449 Row2 := IntfImg2.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
450450 for px:=0 to IntfImg1.Width-1 do begin
451451 Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
452452 Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Fading
453453 Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
454454 end;
455455 end;
456456 IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
457457
458458 TempBitmap.Handle:=ImgHandle;
459459 TempBitmap.MaskHandle:=ImgMaskHandle;
460460 Canvas.Draw(0,0,TempBitmap);
461461 end;
462462
463463 IntfImg1.Free;
464464 IntfImg2.Free;
465465 TempBitmap.Free;
466466 end;
467467```
468468
469469### Conversion between TLazIntfImage and TBitmap
470470
471471Since 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.
472472
473473``` pascal
474474uses
475475 ...GraphType, IntfGraphics, LCLType, LCLProc, LCLIntf ...
476476
477477procedure TForm1.Button4Click(Sender: TObject);
478478var
479479 b: TBitmap;
480480 t: TLazIntfImage;
481481begin
482482 b := TBitmap.Create;
483483 try
484484 b.LoadFromFile('test.bmp');
485485 t := b.CreateIntfImage;
486486
487487 // Read and/or write to the pixels
488488 t.Colors[10,20] := colGreen;
489489
490490 b.LoadFromIntfImage(t);
491491 finally
492492 t.Free;
493493 b.Free;
494494 end;
495495end;
496496```
497497
498498### Using the non-native StretchDraw from LazCanvas
499499
500500Just 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:
501501
502502`TLazCanvas.Interpolation := TFPSharpInterpolation.Create;`
503503
504504There are other interpolations available in the unit fpcanvas.
505505
506506``` pascal
507507uses
508508 intfgraphics, lazcanvas;
509509
510510procedure TForm1.StretchDrawBitmapToBitmap(SourceBitmap, DestBitmap: TBitmap; DestWidth, DestHeight: integer);
511511var
512512 DestIntfImage, SourceIntfImage: TLazIntfImage;
513513 DestCanvas: TLazCanvas;
514514begin
515515 // Prepare the destination
516516 DestIntfImage := TLazIntfImage.Create(0, 0);
517517 try
518518 DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);
519519 DestCanvas := TLazCanvas.Create(DestIntfImage);
520520 try
521521 //Prepare the source
522522 SourceIntfImage := TLazIntfImage.Create(0, 0);
523523 try
524524 SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);
525525 // Execute the stretch draw via TFPSharpInterpolation
526526 DestCanvas.Interpolation := TFPSharpInterpolation.Create;
527527 try
528528 DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);
529529 // Reload the image into the TBitmap
530530 DestBitmap.LoadFromIntfImage(DestIntfImage);
531531 finally
532532 DestCanvas.Interpolation.Free;
533533 end;
534534 finally
535535 SourceIntfImage.Free;
536536 end;
537537 finally
538538 DestCanvas.Free;
539539 end;
540540 finally
541541 DestIntfImage.Free;
542542 end;
543543end;
544544
545545procedure TForm1.FormPaint(Sender: TObject);
546546var
547547 Bmp, DestBitmap: TBitmap;
548548begin
549549 // Prepare the destination
550550 DestBitmap := TBitmap.Create;
551551 try
552552 DestBitmap.SetSize(100, 100);
553553 // Create and draw the source bitmap: a small red rectangle
554554 Bmp := TBitmap.Create;
555555 try
556556 Bmp.SetSize(10, 10);
557557 Bmp.Canvas.Pen.Color := clBlack;
558558 Bmp.Canvas.Brush.Color := clRed;
559559 Bmp.Canvas.Rectangle(0, 0, 10, 10);
560560 Canvas.Draw(10, 10, Bmp);
561561 // Stretch-draw the source bitmap onto the destination bitmap canvas
562562 StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
563563 Canvas.Draw(100, 100, DestBitmap);
564564 finally
565565 Bmp.Free;
566566 end;
567567 finally
568568 DestBitmap.Free;
569569 end;
570570end;
571571```
572572
573573### Clipping
574574
575575#### Clipping by a rectangle
576576
577577Anything outside the specified `ClipRect` of the canvas is removed when `Clipping` is true:
578578
579-<figure>
580-<img src="LazCanvas_ClipRect.png" title="LazCanvas_ClipRect.png" />
581-<figcaption>LazCanvas_ClipRect.png</figcaption>
582-</figure>
579+![](/wiki-images/a/a8/LazCanvas_ClipRect.png)
583580
584581``` Pascal
585582uses
586583 FPImage, IntfGraphics, LazCanvas;
587584
588585procedure TForm1.FormPaint(Sender: TObject);
589586var
590587 bmp: TBitmap;
591588 img: TLazIntfImage;
592589 cnv: TLazCanvas;
593590 Rc: TRect;
594591 polyPts: array[0..4] of TPoint = (
595592 (x:100;y:10), (x:190;y:80), (x:170;y:190), (x:30;y:190), (x:10;y:80)
596593 );
597594begin
598595 // Buffer bitmap
599596 bmp := TBitmap.Create;
600597 try
601598 bmp.SetSize(Width, Height);
602599 img := bmp.CreateIntfImage;
603600 try
604601 cnv := TLazCanvas.Create(img);
605602 try
606603 cnv.Brush.FPColor := colWhite;
607604 cnv.Clear;
608605
609606 // To better understand what is removed, we draw the outline of the
610607 // polygon to be clipped
611608 cnv.Pen.Style := psDot;
612609 cnv.Pen.FPColor := colRed;
613610 cnv.Polygon(polyPts);
614611
615612 // Define the clipping rectangle
616613 Rc := Rect(50, 10, 180, 170);
617614 // To understand where the clipping rectangle is we draw its outline
618615 cnv.Brush.Style := bsClear;
619616 cnv.Pen.FPColor := colSilver;
620617 cnv.Rectangle(Rc);
621618
622619 // Activate clipping
623620 cnv.ClipRect := Rc;
624621 cnv.Clipping := true;
625622
626623 // Draw the polygon to be clipped
627624 cnv.Brush.Style := bsSolid;
628625 cnv.Brush.FPColor := colRed;
629626 cnv.Polygon(polyPts);
630627 finally
631628 cnv.Free;
632629 end;
633630 bmp.LoadFromIntfImage(img);
634631 Canvas.Draw(0, 0, bmp);
635632 finally
636633 img.Free;
637634 end;
638635 finally
639636 bmp.Free;
640637 end;
641638end;
642639```
643640
644641#### Clipping by a region
645642
646643For 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`:
647644
648-<figure>
649-<img src="LazCanvas_ClipRegion.png" title="LazCanvas_ClipRegion.png" />
650-<figcaption>LazCanvas_ClipRegion.png</figcaption>
651-</figure>
645+![](/wiki-images/a/af/LazCanvas_ClipRegion.png)
652646
653647``` Pascal
654648uses
655649 FPImage, FPCanvas, IntfGraphics, LazCanvas, LazRegions;
656650
657651procedure TForm1.FormPaint(Sender: TObject);
658652var
659653 bmp: TBitmap;
660654 img: TLazIntfImage;
661655 cnv: TLazCanvas;
662656 rgn: TLazRegion;
663657 Rc1, Rc2: TRect;
664658 polyPts: array[0..4] of TPoint = (
665659 (x:100;y:10), (x:190;y:80), (x:170;y:190), (x:30;y:190), (x:10;y:80)
666660 );
667661begin
668662 // Buffer bitmap
669663 bmp := TBitmap.Create;
670664 try
671665 bmp.SetSize(ClientWidth, ClientHeight);
672666 img := bmp.CreateIntfImage;
673667 try
674668 cnv := TLazCanvas.Create(img);
675669 cnv.Brush.FPColor := colWhite;
676670 cnv.Clear;
677671
678672 // To better understand what is clipped, draw the outline of the polygon
679673 // to be clipped
680674 cnv.Pen.Style := psDot;
681675 cnv.Pen.FPColor := colRed;
682676 cnv.Brush.Style := bsClear;
683677 cnv.Polygon(polyPts);
684678
685679 // ... and we draw the outline of the parts in the clipping region:
686680 // We clip by an elliptical region...
687681 Rc1 := Rect(20, 20, 140, 140);
688682 cnv.Pen.FPColor := colSilver;
689683 cnv.Ellipse(Rc1);
690684 // ... and by a rectangular region
691685 Rc2 := Rect(100, 100, 200, 170);
692686 cnv.Rectangle(Rc2);
693687
694688 // Create the clipping region
695689 rgn := TLazRegion.Create;
696690 try
697691 // Show pixels that are inside the ellipse...
698692 rgn.AddEllipse(Rc1.Left, Rc1.Top, Rc1.Right, Rc1.Bottom);
699693 // ... and the rectangle.
700694 rgn.AddRectangle(Rc2);
701695 // Activate the clip region
702696 cnv.SetLazClipRegion(rgn);
703697
704698 // Draw the polygon to be clipped
705699 cnv.Brush.FPColor := colRed;
706700 cnv.Brush.Style := bsSolid;
707701 cnv.Pen.Style := psSolid;
708702 cnv.Pen.FPColor := colBlack;
709703 cnv.Polygon(polyPts);
710704 finally
711705 cnv.Free;
712706 end;
713707
714708 // Draw the buffer bitmap in the Paintbox
715709 bmp.LoadFromIntfImage(img);
716710 Canvas.Draw(0, 0, bmp);
717711 finally
718712 img.Free;
719713 end;
720714 finally
721715 bmp.Free;
722716 end;
723717end;
724718```
725719
726720## Motion Graphics - How to Avoid flickering
727721
728722Many 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.
729723
730724How 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.
731725
732726Another 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.
733727
734728A 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.
735729
736730Now we will examine the options we have for drawing to a Canvas:
737731
738732- Draw to a TImage
739733- Draw on the OnPaint event of the form, a TPaintBox or another control
740734- Create a custom control which draws itself
741735- Using A.J. Venter's gamepack
742736
743737### Draw to a TImage
744738
745739A 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.
746740
747741**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.
748742
749743#### Resizing the bitmap of a TImage
750744
751745> **Note:** Do not use this during OnPaint.
752746
753747``` pascal
754748with Image1.Picture.Bitmap do begin
755749 Width:=100;
756750 Height:=120;
757751end;
758752```
759753
760754Same in one step:
761755
762756``` pascal
763757with Image1.Picture.Bitmap do begin
764758 SetSize(100, 120);
765759end;
766760```
767761
768762#### Painting on the bitmap of a TImage
769763
770764> **Note:** Do not use this during OnPaint.
771765
772766``` pascal
773767with Image1.Picture.Bitmap.Canvas do begin
774768 // fill the entire bitmap with red
775769 Brush.Color := clRed;
776770 FillRect(0, 0, Width, Height);
777771end;
778772```
779773
780774> **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.
781775
782776Another example:
783777
784778``` pascal
785779procedure TForm1.BitBtn1Click(Sender: TObject);
786780var
787781 x, y: Integer;
788782begin
789783 // Draws the backgroung
790784 MyImage.Canvas.Pen.Color := clWhite;
791785 MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
792786
793787 // Draws squares
794788 MyImage.Canvas.Pen.Color := clBlack;
795789 for x := 1 to 8 do
796790 for y := 1 to 8 do
797791 MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
798792 Round(x * Image.Width / 8), Round(y * Image.Height / 8));
799793end;
800794```
801795
802796#### Painting on the volatile visual area of the TImage
803797
804798You 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.
805799
806800``` pascal
807801procedure TForm.Image1Paint(Sender: TObject);
808802begin
809803 // paint a line
810804 Canvas.Pen.Color := clRed;
811805 Canvas.Line(0, 0, Width, Height);
812806end;
813807```
814808
815809### Draw on the OnPaint event
816810
817811In 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.
818812
819813``` pascal
820814procedure TForm.Form1Paint(Sender: TObject);
821815begin
822816 // paint a line
823817 Canvas.Pen.Color := clRed;
824818 Canvas.Line(0, 0, Width, Height);
825819end;
826820```
827821
828822### Create a custom control which draws itself
829823
830824Creating 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.
831825
832826Here is an example custom control:
833827
834828``` pascal
835829uses
836830 Classes, SysUtils, Controls, Graphics, LCLType;
837831
838832type
839833 TMyDrawingControl = class(TCustomControl)
840834 public
841835 procedure EraseBackground(DC: HDC); override;
842836 procedure Paint; override;
843837 end;
844838
845839implementation
846840
847841procedure TMyDrawingControl.EraseBackground(DC: HDC);
848842begin
849843 // Uncomment this to enable default background erasing
850844 //inherited EraseBackground(DC);
851845end;
852846
853847procedure TMyDrawingControl.Paint;
854848var
855849 x, y: Integer;
856850 Bitmap: TBitmap;
857851begin
858852 Bitmap := TBitmap.Create;
859853 try
860854 // Initializes the Bitmap Size
861855 Bitmap.Height := Height;
862856 Bitmap.Width := Width;
863857
864858 // Draws the background
865859 Bitmap.Canvas.Pen.Color := clWhite;
866860 Bitmap.Canvas.Rectangle(0, 0, Width, Height);
867861
868862 // Draws squares
869863 Bitmap.Canvas.Pen.Color := clBlack;
870864 for x := 1 to 8 do
871865 for y := 1 to 8 do
872866 Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
873867 Round(x * Width / 8), Round(y * Height / 8));
874868
875869 Canvas.Draw(0, 0, Bitmap);
876870 finally
877871 Bitmap.Free;
878872 end;
879873
880874 inherited Paint;
881875end;
882876```
883877
884878and how we create it on the form:
885879
886880``` pascal
887881procedure TMyForm.FormCreate(Sender: TObject);
888882begin
889883 MyDrawingControl := TMyDrawingControl.Create(Self);
890884 MyDrawingControl.Height := 400;
891885 MyDrawingControl.Width := 500;
892886 MyDrawingControl.Top := 0;
893887 MyDrawingControl.Left := 0;
894888 MyDrawingControl.Parent := Self;
895889 MyDrawingControl.DoubleBuffered := True;
896890end;
897891```
898892
899893It is destroyed automatically, because we use Self as owner.
900894
901895Setting 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.
902896
903897"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
904898
905899"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
906900
907901## Image formats
908902
909903Here is a table with the correct class to use for each image format.
910904
911905| Format | Image class | Unit |
912906|--------------------------------|-------------------------|----------|
913907| Cursor (cur) | TCursor | Graphics |
914908| Bitmap (bmp) | TBitmap | Graphics |
915909| Windows icon (ico) | TIcon | Graphics |
916910| Mac OS X icon (icns) | TicnsIcon | Graphics |
917911| Pixmap (xpm) | TPixmap | Graphics |
918912| Portable Network Graphic (png) | TPortableNetworkGraphic | Graphics |
919913| JPEG (jpg, jpeg) | TJpegImage | Graphics |
920914| PNM (pnm) | TPortableAnyMapGraphic | Graphics |
921915| Tiff (tif, tiff) | TTiffImage | Graphics |
922916
923917See also the list of [[fcl-image|fcl-image supported formats]].
924918
925919### Converting formats
926920
927921Sometimes 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.
928922
929923Converting Bitmap to PNG and saving it to a file:
930924
931925``` pascal
932926procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
933927var
934928 png : TPortableNetworkGraphic;
935929begin
936930 png := TPortableNetworkGraphic.Create;
937931 try
938932 png.Assign(bmp);
939933 png.SaveToFile(PngFileName);
940934 finally
941935 png.Free;
942936 end;
943937end;
944938```
945939
946940## Pixel Formats
947941
948942### TColor
949943
950944The 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.
951945
952946To convert from separate RGB channels to TColor use:
953947
954948``` pascal
955949RGBToColor(RedVal, GreenVal, BlueVal);
956950```
957951
958952To get each channel of a TColor variable use the Red, Green and Blue functions:
959953
960954``` pascal
961955RedVal := Red(MyColor);
962956GreenVal := Green(MyColor);
963957BlueVal := Blue(MyColor);
964958```
965959
966960### TFPColor
967961
968962TFPColor 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.
969963
970964The unit Graphics provides routines to convert between TColor and TFPColor:
971965
972966``` pascal
973967function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
974968function FPColorToTColor(const FPColor: TFPColor): TColor;
975969function TColorToFPColor(const c: TColorRef): TFPColor; overload;
976970function TColorToFPColor(const c: TColor): TFPColor; overload; // does not work on system color
977971```
978972
979973## Drawing with fcl-image
980974
981975You 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.
982976
983977For more information, please read the article about [[fcl-image]].
984978
985979## Common OnPaint Error
986980
987981A 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.
988982
989983This is bad:
990984
991985``` pascal
992986procedure TForm1.Button1Click(Sender: TObject);
993987begin
994988 Shape1Paint(Self); // Call Shape1 OnPaint event
995989 Shape1.Invalidate; // Invoke actual painting
996990
997991 //more code for Button1
998992end;
999993```
1000994
1001995This is good:
1002996
1003997``` pascal
1004998procedure TForm1.Button1Click(Sender: TObject);
1005999begin
10061000 //code for Button1
10071001 //set some condition
10081002 Shape1.Invalidate; // May be necessary on some occasions
10091003end;
10101004
10111005// Shape1Paint should be attached to the OnPaint event of shape object !
10121006procedure TForm1.Shape1Paint(Sender: TObject);
10131007begin
10141008 if some_condition then
10151009 with Shape1.Canvas do
10161010 begin
10171011 //lots of stuff
10181012 end;
10191013end;
10201014```
10211015
10221016## Some useful examples
10231017
10241018### Example 1: Drawing on loaded JPEG with TImage
10251019
10261020Add procedure LoadAndDraw to the public section of your form, and paste next code to implemantation section:
10271021
10281022``` pascal
10291023procedure TForm1.LoadAndDraw(const sFileName: String);
10301024var
10311025 jpg: TPicture;
10321026begin
10331027 jpg := TPicture.Create;
10341028 try
10351029 jpg.LoadFromFile(sFileName);
10361030
10371031 Image.Picture.Bitmap.SetSize(jpg.Width, jpg.Height);
10381032 Image.Picture.Bitmap.Canvas.Draw(0, 0, jpg.Bitmap);
10391033
10401034 Image.Picture.Bitmap.Canvas.Pen.Color := clRed;
10411035 Image.Picture.Bitmap.Canvas.Line(0, 0, 140, 140);
10421036 finally
10431037 jpg.Free;
10441038 end;
10451039end;
10461040```
10471041
10481042### Example 2: Drawing on controls of Form
10491043
105010441\) Create a New project - Application, add to uses section next modules if needed: Types, Controls, Graphics.
10511045
105210462\) Place on form Button1, GroupBox1 and RadioGroup1
10531047
105410483\) Place on GroupBox1 one more button - Button2
10551049
105610504\) Your TForm1.Create should looks like:
10571051
10581052``` pascal
10591053procedure TForm1.FormCreate(Sender: TObject);
10601054var
10611055 i: Integer;
10621056begin
10631057 for i := 0 to Self.ControlCount - 1 do
10641058 RadioGroup1.Items.AddObject(Controls[i].Name, Controls[i]);
10651059
10661060 RadioGroup1.Items.AddObject(Button2.Name,Button2);
10671061end;
10681062```
10691063
107010645\) For RadioGroup1 create handler of OnSelectionChanged event:
10711065
10721066``` pascal
10731067procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
10741068begin
10751069 Self.Repaint;
10761070end;
10771071```
10781072
107910736\) Add to public section of your form procedure HighlightControl:
10801074
10811075``` pascal
10821076procedure TForm1.HighlightControl(AControl: TControl);
10831077var
10841078 R: Types.TRect;
10851079 aCC: TControlCanvas;
10861080begin
10871081 R := AControl.BoundsRect;
10881082 InflateRect(R, 2, 2); // make rect a bit bigger then control
10891083 aCC := TControlCanvas.Create;
10901084 aCC.Control := AControl.Parent;
10911085 aCC.Pen.Color := clGreen;
10921086 aCC.Pen.Width := 5;
10931087 aCC.Pen.Style := psSolid;
10941088 aCC.Brush.Style := bsClear;
10951089 aCC.Rectangle(R);
10961090 aCC.free;
10971091end;
10981092```
10991093
11001094## See also
11011095
11021096- [[Fast direct pixel access]]
11031097
11041098### 2D drawing
11051099
11061100- [[ZenGL]] - cross-platform game development library using OpenGL.
11071101- [[BGRABitmap]] - Drawing shapes and bitmaps with transparency, direct access to pixels, etc.
11081102- [[LazRGBGraphics]] - A package for fast in memory image processing and pixel manipulations (like scan line).
11091103- [[fpvectorial]] - Offers support to read, modify and write vectorial images.
11101104- [[Double Gradient]] - Draw 'double gradient' & 'n gradient' bitmaps easy.
11111105- [[Gradient Filler]] - TGradientFiller is the best way to create custom n gradients in Lazarus.
11121106- [[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.
11131107- [[Sample Graphics]] - graphics gallery created with Lazarus and drawing tools
11141108- [[Fast direct pixel access]] - speed comparison of some methods for direct bitmap pixel access
11151109- [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.
11161110
11171111### 3D drawing
11181112
11191113- [[OpenGL]] - OpenGL (Open Graphics Library) is a cross-platform API for producing 3D computer graphics.
11201114- [[GLScene]] - A port of the 3D visual OpenGL graphics Library [GLScene](http://www.glscene.org).
11211115- [[Metal Framework|macOS Metal Framework]] - Render advanced 3D graphics and perform data-parallel computations using graphics processors.
11221116- [[Castle Game Engine]] - An open-source cross-platform 3D and 2D game engine ([official website](https://castle-engine.io/)).
11231117
11241118### Charts
11251119
11261120- [[TAChart]] - Charting component for Lazarus
11271121- [[PlotPanel]] - A plotting and charting component for animated graphs
11281122- [[Perlin Noise]] - An article about using Perlin Noise on LCL applications.
11291123
11301124---
11311125*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