Sign in Join
Wiki copytemporary

Developing with Graphics/zh CN: what changed

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

Title: Developing with Graphics/zh CN

OldNewLine
1+本页介绍Lazarus图形绘制的基本类和技术。其他更详情介绍在主题文章里。
2+
3+## 库
4+
5+[[Graphics libraries|图形库]] - 在这里你可以看到主要的图形库用于开发。
6+
7+## 其他图形文章
8+
9+### 2D 绘图
10+
11+- [[ZenGL]] - 利用 OpenGL 跨平台的游戏开发库
12+- [[BGRABitmap]] - 绘制图形与位图透明效果,直接存取像素等
13+- [[LazRGBGraphics/zh CN|LazRGBGraphics]] - 一个快速进行内存图像处理与像素操作
14+- [[fpvectorial]] - 矢量图像读写支持
15+- [[Double Gradient]] - 易于绘制“双梯度”和“n梯度”位图
16+- [[Gradient Filler]] - TGradientFiller 是Lazarus中创建自定义N梯度最佳方法
17+- [[PascalMagick]] - 针对[ImageMagick](http://www.imagemagick.org)易于使用的API, 在多平台下用来创建、编辑及合成位图文件的自由软件包
18+
19+- [[Sample Graphics]] - Lazarus和绘图工具创建的图库
20+- [[Fast direct pixel access]] - speed comparison of some methods for direct bitmap pixel access(~~位图像素访问速度对比的一些方法~~)
21+- [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.(~~AggPas是Anti-Grain Object Pascal的本地端口。它快速、功能强大反锯齿绘图和亚像素精度。你可以认为AggPas渲染引擎是矢量图像在内存中进行的。~~)
22+
23+### 3D 绘图
24+
25+- [[GLScene]] - 一个可视化的OpenGL图形库的接口 [GLScene](http://www.glscene.org)
26+
27+### 图表
28+
29+- [[TAChart/zh CN|TAChart]] - Lazarus图表组件。
30+- [[PlotPanel/zh CN|PlotPanel]] - 一个绘制图表的组件。
31+- [[Perlin Noise/zh CN|Perlin Noise]] - 一篇关于在LCL上使用柏林噪声的文章。
32+
33+## LCL图形模型简介
34+
35+LCL提供两种绘图类:原生类和非原生类。原生类是是LCL绘图最传统的方式,也是最重要的一条,而对于非原生类是互补的,也是非常重要的。原生类大分布在**Graphics**面板里,众所周知的类:TBitmap, TCanvas, TFont, TBrush, TPen, TPortableNetworkGraphic等等。
36+
37+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.
38+
39+~~TCanvas是能够执行绘图的类。它不能单独存在,需要附加到一些可见的(至少它可能是可见的),如,TControl,或附加到TRasterImage(TBitmap是最常用的)。TFont、TBrush和TPen 用来在画布上绘制。~~
40+
41+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.
42+
43+~~TRasterImage(通常其后代TBitmap)是一个内存区域绘制图形,但它为最大兼容本地创建画布,因此在X11 LCL-Gtk2它位于X11服务器,这使得通过像素像素访问属性极其缓慢。在Windows中它是非常快的,因为Windows允许创建一个本地分配图像可以从Windows接收的绘制画布。~~
44+
45+Besides these there are also non-native drawing classes located in the units graphtype (TRawImage), intfgraphics (TLazIntfImage) and lazcanvas (TLazCanvas, this one exists in Lazarus 0.9.31+). TRawImage is the storage and description of a memory area which contains an image. 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. TLazCanvas is a non-native Canvas which can draw to an image in a TLazIntfImage.
46+
47+~~除了这些还有非原生的绘图类 ,graphtype (TRawImage), intfgraphics (TLazIntfImage) 和lazcanvas (TLazCanvas, 在Lazarus 0.9.31+存在)单元。TRawImage是存储和存储器区域的描述,其中包含的图像。 TLazIntfImage是依附于一个TRawImage并采取TFPColor和TRawImage的实际像素格式之间进行转换的护理形象。TLazCanvas是一种非原生画布,可以在TLazIntfImage上绘制图像。~~
48+
49+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.
50+
51+~~原生类和非原生类之间的主要区别是:原生的方式并不能在所有平台上执行一样,因为绘图是由底层平台完成的。绘图速度,精确度也有差别。非原生类,保证了像素级精度执行在所有平台上,他的表现快于所有平台。~~
52+
53+In the widgetset LCL-CustomDrawn the native classes are implemented using the non-native ones.
54+
55+~~在LCL-CustomDrawn部件工具箱原生类使用非原生类来实现。~~
56+
57+所有这些类将在下面的章节中做更详细的描述。
58+
59+## 使用TCanvas
60+
61+### 使用默认GUI字体
62+
63+可以使用更简单的代码来完成:
64+
65+``` pascal
66+SelectObject(Canvas.Handle, GetStockObject(DEFAULT_GUI_FONT));
67+```
68+
69+### Drawing a text limited on the width
70+
71+Use the DrawText routine, first with DT_CALCRECT and then without it.
72+
73+``` pascal
74+// 首先计算文本大小,然后绘制它
75+TextBox := Rect(0, currentPos.Y, Width, High(Integer));
76+DrawText(ACanvas.Handle, PChar(Text), Length(Text),
77+ TextBox, DT_WORDBREAK or DT_INTERNAL or DT_CALCRECT);
78+
79+DrawText(ACanvas.Handle, PChar(Text), Length(Text),
80+ TextBox, DT_WORDBREAK or DT_INTERNAL);
81+```
82+
83+### 锐化文本边缘 (非抗锯齿)
84+
85+一些组件工具通过
86+
87+``` pascal
88+Canvas.Font.Quality := fqNonAntialiased;
89+```
90+
91+Some widgetsets like the gtk2 do not support this and always paint antialiased. Here is a simple procedure to draw text with sharp edges under gtk2. It does not consider all cases, but it should give an idea:
92+
93+``` pascal
94+procedure PaintAliased(Canvas: TCanvas; x,y: integer; const TheText: string);
95+var
96+ w,h: integer;
97+ IntfImg: TLazIntfImage;
98+ Img: TBitmap;
99+ dy: Integer;
100+ dx: Integer;
101+ col: TFPColor;
102+ FontColor: TColor;
103+ c: TColor;
104+begin
105+ w:=0;
106+ h:=0;
107+ Canvas.GetTextSize(TheText,w,h);
108+ if (w<=0) or (h<=0) then exit;
109+ Img:=TBitmap.Create;
110+ IntfImg:=nil;
111+ try
112+ // paint text to a bitmap
113+ Img.Masked:=true;
114+ Img.SetSize(w,h);
115+ Img.Canvas.Brush.Style:=bsSolid;
116+ Img.Canvas.Brush.Color:=clWhite;
117+ Img.Canvas.FillRect(0,0,w,h);
118+ Img.Canvas.Font:=Canvas.Font;
119+ Img.Canvas.TextOut(0,0,TheText);
120+ // get memory image
121+ IntfImg:=Img.CreateIntfImage;
122+ // replace gray pixels
123+ FontColor:=ColorToRGB(Canvas.Font.Color);
124+ for dy:=0 to h-1 do begin
125+ for dx:=0 to w-1 do begin
126+ col:=IntfImg.Colors[dx,dy];
127+ c:=FPColorToTColor(col);
128+ if c<>FontColor then
129+ IntfImg.Colors[dx,dy]:=colTransparent;
130+ end;
131+ end;
132+ // create bitmap
133+ Img.LoadFromIntfImage(IntfImg);
134+ // paint
135+ Canvas.Draw(x,y,Img);
136+ finally
137+ IntfImg.Free;
138+ Img.Free;
139+ end;
140+end;
141+```
142+
143+## 使用 TBitmap 和其他 TGraphic 后代
144+
145+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.
146+
147+### 加载(保存)一个图像从(到)磁盘
148+
149+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.
150+
151+``` pascal
152+var
153+ MyBitmap: TBitmap;
154+begin
155+ MyBitmap := TBitmap.Create;
156+ try
157+ // Load from disk
158+ MyBitmap.LoadFromFile(MyEdit.Text);
159+
160+ // Here you can use MyBitmap.Canvas to read/write to/from the image
161+
162+ // Write back to another disk file
163+ MyBitmap.SaveToFile(MyEdit2.Text);
164+ finally
165+ MyBitmap.Free;
166+ end;
167+end;
168+```
169+
170+When using any other format the process is compelely identical, just use the adequate class. For example, for PNG images:
171+
172+``` pascal
173+var
174+ MyPNG: TPortableNetworkGraphic;
175+begin
176+ MyPNG := TPortableNetworkGraphic.Create;
177+ try
178+ // Load from disk
179+ MyPNG.LoadFromFile(MyEdit.Text);
180+
181+ // Here you can use MyPNG.Canvas to read/write to/from the image
182+
183+ // Write back to another disk file
184+ MyPNG.SaveToFile(MyEdit2.Text);
185+ finally
186+ MyPNG.Free;
187+ end;
188+end;
189+```
190+
191+If you don't 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:
192+
193+``` pascal
194+var
195+ MyPicture: TPicture;
196+begin
197+ MyPicture := TPicture.Create;
198+ try
199+ // Load from disk
200+ MyPicture.LoadFromFile(MyEdit.Text);
201+
202+ // Here you can use MyPicture.Graphic.Canvas to read/write to/from the image
203+
204+ // Write back to another disk file
205+ MyPicture.SaveToFile(MyEdit2.Text);
206+ finally
207+ MyPicture.Free;
208+ end;
209+end;
210+```
211+
212+### 其他TImage文件格式
213+
214+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
215+
216+### 直接存取像素
217+
218+To do directly access the pixels of bitmaps one can either use external libraries, such as [[BGRABitmap]], [[LazRGBGraphics]] and [[Current conversion projects|Graphics32]] or use the Lazarus native TLazIntfImage. For a comparison of pixel access methods, see [[Fast direct pixel access|fast direct pixel access]].
219+
220+On some Lazarus widgetsets (notably LCL-Gtk2), the bitmap data is not stored in memory location which can be accessed by the application and in general the LCL native interfaces draw only through native Canvas routines, so each SetPixel / GetPixel operation involves a slow call to the native Canvas API. In LCL-CustomDrawn this is not the case since the bitmap is locally stored for all backends and SetPixel / GetPixel is fast. For obtaining a solution which works in all widgetsets one should use TLazIntfImage. As Lazarus is meant to be platform independent and work in gtk2, the TBitmap class does not provide a property like Scanline. There is a GetDataLineStart function, equivalent to Scanline, but only available for memory images like [[Developing with Graphics|TLazIntfImage]] which internally uses TRawImage.
221+
222+To sum it up, with the standard TBitmap, you can only change pixels indirectly, by using TCanvas.Pixels. Calling a native API to draw / read an individual pixel is course slower than direct pixel access, notably so in LCL-gtk2 and LCL-Carbon.
223+
224+### 位图颜色透明
225+
226+A new feature, implemented on Lazarus 0.9.11, is color transparent bitmaps. Bitmap files (*.BMP) cannot store any information about transparency, but they can work as they had if you select a color on them to represent the transparent area. This is a common trick used on Win32 applications.
227+
228+The following example loads a bitmap from a Windows resource, selects a color to be transparent (clFuchsia) and then draws it to a canvas.
229+
230+``` pascal
231+procedure MyForm.MyButtonOnClick(Sender: TObject);
232+var
233+ buffer: THandle;
234+ bmp: TBitmap;
235+ memstream: TMemoryStream;
236+begin
237+ bmp := TBitmap.Create;
238+
239+ buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
240+
241+ if (buffer = 0) then exit; // Error loading the bitmap
242+
243+ bmp.Handle := buffer;
244+ memstream := TMemoryStream.create;
245+ try
246+ bmp.SaveToStream(memstream);
247+ memstream.position := 0;
248+ bmp.LoadFromStream(memstream);
249+ finally
250+ memstream.free;
251+ end;
252+
253+ bmp.Transparent := True;
254+ bmp.TransparentColor := clFuchsia;
255+
256+ MyCanvas.Draw(0, 0, bmp);
257+
258+ bmp.Free; // Release allocated resource
259+end;
260+```
261+
262+Notice the memory operations performed with the [[doc:rtl/classes/tmemorystream.html|TMemoryStream]]. They are necessary to ensure the correct loading of the image.
263+
264+### 屏幕截图
265+
266+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:
267+
268+``` pascal
269+uses Graphics, LCLIntf, LCLType;
270+ ...
271+var
272+ MyBitmap: TBitmap;
273+ ScreenDC: HDC;
274+begin
275+ MyBitmap := TBitmap.Create;
276+ ScreenDC := GetDC(0);
277+ MyBitmap.LoadFromDevice(ScreenDC);
278+ ReleaseDC(0,ScreenDC);
279+ ...
280+```
281+
282+## 使用 TLazIntfImage、TRawImage和TLazCanvas
283+
284+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.
285+
286+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.
287+
288+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:
289+
290+``` pascal
291+uses graphtype, intfgraphics, lazcanvas;
292+
293+var
294+ AImage: TLazIntfImage;
295+ ACanvas: TLazCanvas;
296+ lRawImage: TRawImage;
297+begin
298+ lRawImage.Init;
299+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
300+ lRawImage.CreateData(True);
301+ AImage := TLazIntfImage.Create(0,0);
302+ AImage.SetRawImage(lRawImage);
303+ ACanvas := TLazCanvas.Create(AImage);
304+```
305+
306+### 初始化 TLazIntfImage
307+
308+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:
309+
310+1\. Attach it to a TRawImage
311+
312+2\. Load it from a TBitmap. Note that it will copy the memory of the TBitmap so it won't remain connected to it.
313+
314+``` pascal
315+ SrcIntfImg:=TLazIntfImage.Create(0,0);
316+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
317+```
318+
319+3\. Load it from a raw image description, like this:
320+
321+``` pascal
322+ IntfImg := TLazIntfImage.Create(0,0);
323+ IntfImg.DataDescription:=GetDescriptionFromDevice(0);
324+ IntfImg.SetSize(10,10);
325+```
326+
327+The 0 device in **GetDescriptionFromDevice(0)** uses the current screen format.
328+
329+### TLazIntfImage.LoadFromFile
330+
331+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.
332+
333+``` pascal
334+uses LazLogger, Graphics, IntfGraphics, GraphType;
335+procedure TForm1.FormCreate(Sender: TObject);
336+var
337+ AImage: TLazIntfImage;
338+ lRawImage: TRawImage;
339+begin
340+ // create a TLazIntfImage with 32 bits per pixel, alpha 8bit, red 8 bit, green 8bit, blue 8bit,
341+ // Bits In Order: bit 0 is pixel 0, Top To Bottom: line 0 is top
342+ lRawImage.Init;
343+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
344+ lRawImage.CreateData(false);
345+ AImage := TLazIntfImage.Create(0,0);
346+ try
347+ AImage.SetRawImage(lRawImage);
348+ // Load an image from disk.
349+ // It uses the file extension to select the right registered image reader.
350+ // The AImage will be resized to the width, height of the loaded image.
351+ AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
352+ debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
353+ finally
354+ AImage.Free;
355+ end;
356+end;
357+```
358+
359+### 加载 TLazIntfImage 到 TImage
360+
361+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**:
362+
363+``` pascal
364+ Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
365+```
366+
367+**说明:**
368+
369+- To load a **transparent** TLazIntfImage you have to set the **Image1.Transparent** to true.
370+- 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.
371+
372+### 淡出效果
373+
374+A fading example with TLazIntfImage
375+
376+``` pascal
377+{ This code has been taken from the $LazarusPath/examples/lazintfimage/fadein1.lpi project. }
378+uses LCLType, // HBitmap type
379+ IntfGraphics, // TLazIntfImage type
380+ fpImage; // TFPColor type
381+...
382+ procedure TForm1.FadeIn(ABitMap: TBitMap);
383+ var
384+ SrcIntfImg, TempIntfImg: TLazIntfImage;
385+ ImgHandle,ImgMaskHandle: HBitmap;
386+ FadeStep: Integer;
387+ px, py: Integer;
388+ CurColor: TFPColor;
389+ TempBitmap: TBitmap;
390+ begin
391+ SrcIntfImg:=TLazIntfImage.Create(0,0);
392+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
393+ TempIntfImg:=TLazIntfImage.Create(0,0);
394+ TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
395+ TempBitmap:=TBitmap.Create;
396+ for FadeStep:=1 to 32 do begin
397+ for py:=0 to SrcIntfImg.Height-1 do begin
398+ for px:=0 to SrcIntfImg.Width-1 do begin
399+ CurColor:=SrcIntfImg.Colors[px,py];
400+ CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
401+ CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
402+ CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
403+ TempIntfImg.Colors[px,py]:=CurColor;
404+ end;
405+ end;
406+ TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
407+ TempBitmap.Handle:=ImgHandle;
408+ TempBitmap.MaskHandle:=ImgMaskHandle;
409+ Canvas.Draw(0,0,TempBitmap);
410+ end;
411+ SrcIntfImg.Free;
412+ TempIntfImg.Free;
413+ TempBitmap.Free;
414+ end;
415+```
416+
417+### 图像格式示例
418+
419+If you know that the TBitmap is using blue 8bit, green 8bit, red 8bit you can directly access the bytes, which is somewhat faster:
420+
421+``` pascal
422+uses LCLType, // HBitmap type
423+ IntfGraphics, // TLazIntfImage type
424+ fpImage; // TFPColor type
425+...
426+type
427+ TRGBTripleArray = array[0..32767] of TRGBTriple;
428+ PRGBTripleArray = ^TRGBTripleArray;
429+
430+procedure TForm1.FadeIn2(aBitMap: TBitMap);
431+ var
432+ IntfImg1, IntfImg2: TLazIntfImage;
433+ ImgHandle,ImgMaskHandle: HBitmap;
434+ FadeStep: Integer;
435+ px, py: Integer;
436+ CurColor: TFPColor;
437+ TempBitmap: TBitmap;
438+ Row1, Row2: PRGBTripleArray;
439+ begin
440+ IntfImg1:=TLazIntfImage.Create(0,0);
441+ IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
442+
443+ IntfImg2:=TLazIntfImage.Create(0,0);
444+ IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
445+
446+ TempBitmap:=TBitmap.Create;
447+
448+ //with Scanline-like
449+ for FadeStep:=1 to 32 do begin
450+ for py:=0 to IntfImg1.Height-1 do begin
451+ Row1 := IntfImg1.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
452+ Row2 := IntfImg2.GetDataLineStart(py); //like Delphi TBitMap.ScanLine
453+ for px:=0 to IntfImg1.Width-1 do begin
454+ Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
455+ Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Fading
456+ Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
457+ end;
458+ end;
459+ IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
460+
461+ TempBitmap.Handle:=ImgHandle;
462+ TempBitmap.MaskHandle:=ImgMaskHandle;
463+ Canvas.Draw(0,0,TempBitmap);
464+ end;
465+
466+ IntfImg1.Free;
467+ IntfImg2.Free;
468+ TempBitmap.Free;
469+ end;
470+```
471+
472+### TLazIntfImage和TBitmap相互转换
473+
474+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.
475+
476+``` pascal
477+uses
478+ ...GraphType, IntfGraphics, LCLType, LCLProc, LCLIntf ...
479+
480+procedure TForm1.Button4Click(Sender: TObject);
481+var
482+ b: TBitmap;
483+ t: TLazIntfImage;
484+begin
485+ b := TBitmap.Create;
486+ try
487+ b.LoadFromFile('test.bmp');
488+ t := b.CreateIntfImage;
489+
490+ // Read and/or write to the pixels
491+ t.Colors[10,20] := colGreen;
492+
493+ b.LoadFromIntfImage(t);
494+ finally
495+ t.Free;
496+ b.Free;
497+ end;
498+end;
499+```
500+
501+### 使用LazCanvas中非原生的StretchDraw
502+
503+Just like TCanvas.StretchDraw there is TLazCanvas.StretchDraw but you need to specify the interpolation which you desire to use. The interpolation which provides a Windows-like StretchDraw with a very sharp result (the opposite of anti-aliased) can be added with: TLazCanvas.Interpolation := TFPSharpInterpolation.Create;
504+
505+There are other interpolations available in the unit fpcanvas.
506+
507+``` pascal
508+uses 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+
517+ DestIntfImage := TLazIntfImage.Create(0, 0);
518+ DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);
519+
520+ DestCanvas := TLazCanvas.Create(DestIntfImage);
521+
522+ //Prepare the source
523+ SourceIntfImage := TLazIntfImage.Create(0, 0);
524+ SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);
525+
526+ // Execute the stretch draw via TFPSharpInterpolation
527+ DestCanvas.Interpolation := TFPSharpInterpolation.Create;
528+ DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);
529+
530+ // Reload the image into the TBitmap
531+ DestBitmap.LoadFromIntfImage(DestIntfImage);
532+
533+ SourceIntfImage.Free;
534+ DestCanvas.Interpolation.Free;
535+ DestCanvas.Free;
536+ DestIntfImage.Free;
537+end;
538+
539+procedure TForm1.FormPaint(Sender: TObject);
540+var
541+ Bmp, DestBitmap: TBitmap;
542+begin
543+ // Prepare the destination
544+ DestBitmap := TBitmap.Create;
545+ DestBitmap.Width := 100;
546+ DestBitmap.Height := 100;
547+
548+ Bmp := TBitmap.Create;
549+ Bmp.Width := 10;
550+ Bmp.Height := 10;
551+ Bmp.Canvas.Pen.Color := clYellow;
552+ Bmp.Canvas.Brush.Color := clYellow;
553+ Bmp.Canvas.Rectangle(0, 0, 10, 10);
554+ StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
555+ Canvas.Draw(0, 0, Bmp);
556+ Canvas.Draw(100, 100, DestBitmap);
557+end;
558+```
559+
560+## 动画 - 如何避免闪烁
561+
562+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 sees the whole images and sometimes only when it is partially drawn. It occurs because the painting process requires time.
563+
564+But how can I 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. This tutorial will focus on 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.
565+
566+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.
567+
568+Now we will examine the options we have for drawing to a Canvas:
569+
570+- Draw to a TImage
571+- Draw on the OnPaint event of the form, a TPaintBox or another control
572+- Create a custom control which draws itself
573+- Using A.J. Venter's gamepack
574+
575+### TImage绘图
576+
577+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.
578+
579+**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.
580+
581+#### 调整TImage位图
582+
583+> **Note:** Do not use this during OnPaint.
584+
585+``` pascal
586+with Image1.Picture.Bitmap do begin
587+ Width:=100;
588+ Height:=120;
589+end;
590+```
591+
592+Same in one step:
593+
594+``` pascal
595+with Image1.Picture.Bitmap do begin
596+ SetSize(100, 120);
597+end;
598+```
599+
600+#### 绘制TImage位图
601+
602+> **Note:** Do not use this during OnPaint.
603+
604+``` pascal
605+with Image1.Picture.Bitmap.Canvas do begin
606+ // fill the entire bitmap with red
607+ Brush.Color := clRed;
608+ FillRect(0, 0, Width, Height);
609+end;
610+```
611+
612+> **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.
613+
614+Another example:
615+
616+``` pascal
617+procedure TForm1.BitBtn1Click(Sender: TObject);
618+var
619+ x, y: Integer;
620+begin
621+ // Draws the backgroung
622+ MyImage.Canvas.Pen.Color := clWhite;
623+ MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
624+
625+ // Draws squares
626+ MyImage.Canvas.Pen.Color := clBlack;
627+ for x := 1 to 8 do
628+ for y := 1 to 8 do
629+ MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
630+ Round(x * Image.Width / 8), Round(y * Image.Height / 8));
631+end;
632+```
633+
634+#### Painting on the volatile visual area of the TImage
635+
636+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.
637+
638+``` pascal
639+procedure TForm.Image1Paint(Sender: TObject);
640+begin
641+ // paint a line
642+ Canvas.Pen.Color := clRed;
643+ Canvas.Line(0, 0, Width, Height);
644+end;
645+```
646+
647+### OnPaint事件是绘制
648+
649+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.
650+
651+``` pascal
652+procedure TForm.Form1Paint(Sender: TObject);
653+begin
654+ // paint a line
655+ Canvas.Pen.Color := clRed;
656+ Canvas.Line(0, 0, Width, Height);
657+end;
658+```
659+
660+### 创建自定义控件绘制自身
661+
662+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.
663+
664+Here is an example custom control:
665+
666+``` pascal
667+uses
668+ Classes, SysUtils, Controls, Graphics, LCLType;
669+
670+type
671+ TMyDrawingControl = class(TCustomControl)
672+ public
673+ procedure EraseBackground(DC: HDC); override;
674+ procedure Paint; override;
675+ end;
676+
677+implementation
678+
679+procedure TMyDrawingControl.EraseBackground(DC: HDC);
680+begin
681+ // Uncomment this to enable default background erasing
682+ //inherited EraseBackground(DC);
683+end;
684+
685+procedure TMyDrawingControl.Paint;
686+var
687+ x, y: Integer;
688+ Bitmap: TBitmap;
689+begin
690+ Bitmap := TBitmap.Create;
691+ try
692+ // Initializes the Bitmap Size
693+ Bitmap.Height := Height;
694+ Bitmap.Width := Width;
695+
696+ // Draws the background
697+ Bitmap.Canvas.Pen.Color := clWhite;
698+ Bitmap.Canvas.Rectangle(0, 0, Width, Height);
699+
700+ // Draws squares
701+ Bitmap.Canvas.Pen.Color := clBlack;
702+ for x := 1 to 8 do
703+ for y := 1 to 8 do
704+ Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
705+ Round(x * Width / 8), Round(y * Height / 8));
706+
707+ Canvas.Draw(0, 0, Bitmap);
708+ finally
709+ Bitmap.Free;
710+ end;
711+
712+ inherited Paint;
713+end;
714+```
715+
716+and how we create it on the form:
717+
718+``` pascal
719+procedure TMyForm.FormCreate(Sender: TObject);
720+begin
721+ MyDrawingControl := TMyDrawingControl.Create(Self);
722+ MyDrawingControl.Height := 400;
723+ MyDrawingControl.Width := 500;
724+ MyDrawingControl.Top := 0;
725+ MyDrawingControl.Left := 0;
726+ MyDrawingControl.Parent := Self;
727+ MyDrawingControl.DoubleBuffered := True;
728+end;
729+```
730+
731+It is destroyed automatically, because we use Self as owner.
732+
733+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.
734+
735+"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
736+
737+"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
738+
739+## 图像格式
740+
741+这张表有足够的类为每个图像格式使用。
742+
743+| 格式 | 图像类 class | 单元 |
744+|--------------------------------|-------------------------|----------|
745+| Cursor (cur) | TCursor | Graphics |
746+| Bitmap (bmp) | TBitmap | Graphics |
747+| Windows icon (ico) | TIcon | Graphics |
748+| Mac OS X icon (icns) | TicnsIcon | Graphics |
749+| Pixmap (xpm) | TPixmap | Graphics |
750+| Portable Network Graphic (png) | TPortableNetworkGraphic | Graphics |
751+| JPEG (jpg, jpeg) | TJpegImage | Graphics |
752+| PNM (pnm) | TPortableAnyMapGraphic | Graphics |
753+| Tiff (tif) | TTiffImage | Graphics |
754+
755+See also the list of [[fcl-image|fcl-image supported formats]].
756+
757+### 格式转换
758+
759+Sometimes it must be 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.
760+
761+Converting Bitmap to PNG and saving it to a file:
762+
763+``` pascal
764+procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
765+var
766+ png : TPortableNetworkGraphic;
767+begin
768+ png := TPortableNetworkGraphic.Create;
769+ try
770+ png.Assign(bmp);
771+ png.SaveToFile(PngFileName);
772+ finally
773+ png.Free;
774+ end;
775+end;
776+```
777+
778+## Pixel Formats
779+
780+### TColor
781+
782+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.
783+
784+To convert from separate RGB channels to TColor use:
785+
786+``` pascal
787+RGBToColor(RedVal, GreenVal, BlueVal);
788+```
789+
790+To get each channel of a TColor variable use the Red, Green and Blue functions:
791+
792+``` pascal
793+RedVal := Red(MyColor);
794+GreenVal := Green(MyColor);
795+BlueVal := Blue(MyColor);
796+```
797+
798+### TFPColor
799+
800+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.
801+
802+The unit Graphics provides routines to convert between TColor and TFPColor:
803+
804+``` pascal
805+function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
806+function FPColorToTColor(const FPColor: TFPColor): TColor;
807+function TColorToFPColor(const c: TColorRef): TFPColor; overload;
808+function TColorToFPColor(const c: TColor): TFPColor; overload; // does not work on system color
809+```
810+
811+## 绘制FCL图像
812+
813+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.
814+
815+For more information, please read the article about [[fcl-image]].
816+
817+---
818+*From the Free Pascal wiki, page "Developing with Graphics/zh CN", as it stood on 2020-02-13 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history