Difference between revisions of "Developing with Graphics"
Silentcoder (talk | contribs) m |
Sekelsenmat (talk | contribs) (→Motion Graphics - How to Avoid flickering: Improved the text and moved the gamepack into a subsection) |
||
Line 96: | Line 96: | ||
==Motion Graphics - How to Avoid flickering== | ==Motion Graphics - How to Avoid flickering== | ||
− | + | There are a few options to draw quickly changing 2D graphics. To start with you need to choose whether you will work with hardware acceleration using something like OpenGL or simply use the standard Canvas. OpenGL has the best speed on new PCs, specially those with good video cards, but it is quite heavy for simple programs or old computers. This tutorial will focus on drawing to a Canvas. 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, witch provides a double-buffered canvas and a sprite component. | |
− | |||
− | |||
− | |||
− | Now we will examine the options we have for drawing | + | Now we will examine the options we have for drawing to a Canvas: |
* [[#Draw to a TImage|Draw to a TImage]] | * [[#Draw to a TImage|Draw to a TImage]] | ||
* [[#Draw on the OnPaint event|Draw on the OnPaint event of the form, a TPaintBox or another control]] | * [[#Draw on the OnPaint event|Draw on the OnPaint event of the form, a TPaintBox or another control]] | ||
* [[#Create a custom control which draws itself|Create a custom control which draws itself]] | * [[#Create a custom control which draws itself|Create a custom control which draws itself]] | ||
+ | * [[#Using A.J. Venter's gamepack|Using A.J. Venter's gamepack]] | ||
===Draw to a TImage=== | ===Draw to a TImage=== | ||
Line 200: | Line 198: | ||
"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk. | "MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk. | ||
+ | |||
+ | ===Using A.J. Venter's gamepack=== | ||
+ | |||
+ | The gamepack approach is to draw everything to one double-buffered canvas, which only gets updated to the visible canvas when you are ready. This takes quite a bit of code, but it has the advantage of being able to do large rapidly changing scenes with multiple sprites on them. If you wish to use this approach, you may be interested in A.J. Venter's gamepack, a set of components for game development in Lazarus, which provides a double-buffered display area component as well as a sprite component, designed to integrate well with one another. You can get gamepack via subversion: | ||
+ | <code> | ||
+ | svn co svn://silentcoder.co.za/lazarus/gamepack | ||
+ | </code> |
Revision as of 22:32, 10 November 2005
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
italiano (it) │
日本語 (ja) │
한국어 (ko) │
Nederlands (nl) │
português (pt) │
русский (ru) │
slovenčina (sk) │
中文(中国大陆) (zh_CN) │
中文(台灣) (zh_TW) │
Overview
This page will be the start for tutorials with regard to manipulating Bitmaps and other graphics in your program. As I'm not a graphics programmer I invite all who are, to share their expertise! Just add a link to the next section, add a page and create your own WiKi article. On this page some general information will be given.
Other graphics articles
- GLScene - A port of the visual OpenGL graphics Library GLScene
- TAChart - Charting component for Lazarus
Working with TBitmap
The first thing to remember is that Lazarus is meant to be platform independent, so any methods using Windows API functionality are out of the question. So a method like ScanLine is not supported by Lazarus because it is intended for Device Independant Bitmap and uses functions from the GDI32.dll.
Be careful that if you do not specify the width and height of your TBitmap it will have the standard one, witch is quite small.
A fading example
Say you want to make a Fading picture. In Delphi you could do something like:
type
PRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = array[0..32767] of TRGBTriple;
procedure TForm1.FadeIn(aBitMap: TBitMap);
var
Bitmap, BaseBitmap: TBitmap;
Row, BaseRow: PRGBTripleArray;
x, y, step: integer;
begin
Bitmap := TBitmap.Create;
try
Bitmap.PixelFormat := pf32bit; // or pf24bit
Bitmap.Assign(aBitMap);
BaseBitmap := TBitmap.Create;
try
BaseBitmap.PixelFormat := pf32bit;
BaseBitmap.Assign(Bitmap);
for step := 0 to 32 do begin
for y := 0 to (Bitmap.Height - 1) do begin
BaseRow := BaseBitmap.Scanline[y];
Row := Bitmap.Scanline[y];
for x := 0 to (Bitmap.Width - 1) do begin
Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;
Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading
Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;
end;
end;
Form1.Canvas.Draw(0, 0, Bitmap);
InvalidateRect(Form1.Handle, nil, False);
RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);
end;
finally
BaseBitmap.Free;
end;
finally
Bitmap.Free;
end;
end;
This function in Lazarus could be implemented like:
procedure TForm1.FadeIn(ABitMap: TBitMap);
var
SrcIntfImg, TempIntfImg: TLazIntfImage;
ImgHandle,ImgMaskHandle: HBitmap;
FadeStep: Integer;
px, py: Integer;
CurColor: TFPColor;
TempBitmap: TBitmap;
begin
SrcIntfImg:=TLazIntfImage.Create(0,0);
SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
TempIntfImg:=TLazIntfImage.Create(0,0);
TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
TempBitmap:=TBitmap.Create;
for FadeStep:=1 to 32 do begin
for py:=0 to SrcIntfImg.Height-1 do begin
for px:=0 to SrcIntfImg.Width-1 do begin
CurColor:=SrcIntfImg.Colors[px,py];
CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
TempIntfImg.Colors[px,py]:=CurColor;
end;
end;
TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);
TempBitmap.Handle:=ImgHandle;
TempBitmap.MaskHandle:=ImgMaskHandle;
Canvas.Draw(0,0,TempBitmap);
end;
SrcIntfImg.Free;
TempIntfImg.Free;
TempBitmap.Free;
end;
The Lazarus code on this page has been taken from the $LazarusPath/examples/lazintfimage/fadein1.lpi project. So if you want a flying start with graphics programming take a closer look at this example.
Motion Graphics - How to Avoid flickering
There are a few options to draw quickly changing 2D graphics. To start with you need to choose whether you will work with hardware acceleration using something like OpenGL or simply use the standard Canvas. OpenGL has the best speed on new PCs, specially those with good video cards, but it is quite heavy for simple programs or old computers. This tutorial will focus on drawing to a Canvas. 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, witch provides a double-buffered canvas and a sprite component.
Now we will examine the options we have for drawing to a Canvas:
- Draw to a TImage
- Draw on the OnPaint event of the form, a TPaintBox or another control
- Create a custom control which draws itself
- Using A.J. Venter's gamepack
Draw to a TImage
Never use the OnPaint event to draw to a TImage. 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.
procedure TForm1.BitBtn1Click(Sender: TObject);
var
x, y: Integer;
begin
// Draws the backgroung
Image.Canvas.Pen.Color := clWhite;
Image.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
// Draws squares
Bitmap.Canvas.Pen.Color := clBlack;
for x := 1 to 8 do
for y := 1 to 8 do
Image.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
Round(x * Image.Width / 8), Round(y * Image.Height / 8));
end;
Draw on the OnPaint event
In this case all the drawing has to be done on the OnPaint event. It doesn't remain on the buffer, like on the TImage.
Create a custom control which draws itself
Creating a custom control has the advantage of structuring your code and you can reuse the control. This approach is quite 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, the use of the OnPaint event of the custom control will probably generate flickering.
Here is an example custom control:
type
TMyDrawingControl = class(TCustomControl)
public
procedure Paint; override;
end;
implementation
procedure TMyDrawingControl.Paint;
var
x, y: Integer;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
// Initializes the Bitmap Size
Bitmap.Height := Height;
Bitmap.Width := Width;
// Draws the background
Bitmap.Canvas.Pen.Color := clWhite;
Bitmap.Canvas.Rectangle(0, 0, Width, Height);
// Draws squares
Bitmap.Canvas.Pen.Color := clBlack;
for x := 1 to 8 do
for y := 1 to 8 do
Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
Round(x * Width / 8), Round(y * Height / 8));
Canvas.Draw(0, 0, Bitmap);
finally
Bitmap.Free;
end;
inherited Paint;
end;
and how we create it on the form:
procedure TMyForm.FormCreate(Sender: TObject);
begin
MyDrawingControl:= TMyDrawingControl.Create(Self);
MyDrawingControl.Height := 400;
MyDrawingControl.Width := 500;
MyDrawingControl.Top := 0;
MyDrawingControl.Left := 0;
MyDrawingControl.Parent := Self;
MyDrawingControl.DoubleBuffered := True;
end;
just don´t forget to destroy it:
procedure TMyForm.FormDestroy(Sender: TObject);
begin
MyDrawingControl.Free;
end;
Setting Top and Left to zero is not necessary, since this is the standard position, but is done so to reinforce where the control will be put.
"MyDrawingControl.Parent := Self;" is very important and you won't see your control if you don't do so.
"MyDrawingControl.DoubleBuffered := True;" is required to avoid flickering on Windows. It has no effect on gtk.
Using A.J. Venter's gamepack
The gamepack approach is to draw everything to one double-buffered canvas, which only gets updated to the visible canvas when you are ready. This takes quite a bit of code, but it has the advantage of being able to do large rapidly changing scenes with multiple sprites on them. If you wish to use this approach, you may be interested in A.J. Venter's gamepack, a set of components for game development in Lazarus, which provides a double-buffered display area component as well as a sprite component, designed to integrate well with one another. You can get gamepack via subversion:
svn co svn://silentcoder.co.za/lazarus/gamepack