Sign in Join
Wiki copytemporary

Developing with Graphics/pt: what changed

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

OldNewLine
11## Visão Geral
22
33Esta página é o início de uma série de tutoriais a respeito da manipulação de Bitmaps e outros graficos. Como não sou um programador gráfico, eu convido a todos para contribuir. Basta apenas adicionar um link na próxima seção, adicionar uma página e criar seu próprio artigo Wiki.
44
55Nesta página algumas informações gerais serão fornecidas.
66
77## Outros Tutoriais Gráficos
88
99- [[GLScene]] - Uma porta para a biblioteca de gráficos visuais OpenGL [GLScene](http://www.glscene.org)
1010- [[TAChart/pt]] - Componente gráfico para Lazarus
1111- [[PascalMagick]] - Uma forma fácil de usar a API com interface para [ImageMagick](http://www.imagemagick.org), um pacote livre multi-plataforma para criar, editar e organizar imagens bitmaps.
1212- [[PlotPanel]] - Um componente para traçar e realizar gráficos animados.
1313
1414## Trabalhando com TBitmap
1515
1616A primeira idéia a ser lembrada é que Lazarus foi desenvolvido para ser multiplataforma, assim o uso de métodos da API do windows estão fora de questão. Deste modo o método ScanLine não é suportado pelo Lazarus pois ele é planejado para Dispositivo de Bitmap Independente (DIB) e utiliza uma função da \[biblioteca\] GDI32.dll
1717
1818### Um exemplo de desvanescimento (fading)
1919
2020Digamos que você deseja fazer uma figura desaparecer lentamente. No Delphi você pode fazer algo como:
2121
22-`type`\
23-`  PRGBTripleArray = ^TRGBTripleArray;`\
24-`  TRGBTripleArray = array[0..32767] of TRGBTriple;`\
22+``` pascal
23+type
24+ PRGBTripleArray = ^TRGBTripleArray;
25+ TRGBTripleArray = array[0..32767] of TRGBTriple;
2526
26-`procedure TForm1.FadeIn(aBitMap: TBitMap);`\
27-`var`\
28-`  Bitmap, BaseBitmap: TBitmap;`\
29-`  Row, BaseRow: PRGBTripleArray;`\
30-`  x, y, step: integer;`\
31-`begin`\
32-`  Bitmap := TBitmap.Create;`\
33-`  try`\
34-`    Bitmap.PixelFormat := pf32bit;  //  ou pf24bit`\
35-`    Bitmap.Assign(aBitMap);`\
36-`    BaseBitmap := TBitmap.Create;`\
37-`    try`\
38-`      BaseBitmap.PixelFormat := pf32bit;`\
39-`      BaseBitmap.Assign(Bitmap);`\
40-`      for step := 0 to 32 do begin`\
41-`        for y := 0 to (Bitmap.Height - 1) do begin`\
42-`          BaseRow := BaseBitmap.Scanline[y];`\
43-`          Row := Bitmap.Scanline[y];`\
44-`          for x := 0 to (Bitmap.Width - 1) do begin`\
45-`            Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;`\
46-`            Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading`\
47-`            Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;`\
48-`          end;`\
49-`        end;`\
50-`        Form1.Canvas.Draw(0, 0, Bitmap);`\
51-`        InvalidateRect(Form1.Handle, nil, False);`\
52-`        RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);`\
53-`      end;`\
54-`    finally`\
55-`      BaseBitmap.Free;`\
56-`    end;`\
57-`  finally`\
58-`    Bitmap.Free;`\
59-`  end;`\
60-`end;`
27+procedure TForm1.FadeIn(aBitMap: TBitMap);
28+var
29+ Bitmap, BaseBitmap: TBitmap;
30+ Row, BaseRow: PRGBTripleArray;
31+ x, y, step: integer;
32+begin
33+ Bitmap := TBitmap.Create;
34+ try
35+ Bitmap.PixelFormat := pf32bit; // ou pf24bit
36+ Bitmap.Assign(aBitMap);
37+ BaseBitmap := TBitmap.Create;
38+ try
39+ BaseBitmap.PixelFormat := pf32bit;
40+ BaseBitmap.Assign(Bitmap);
41+ for step := 0 to 32 do begin
42+ for y := 0 to (Bitmap.Height - 1) do begin
43+ BaseRow := BaseBitmap.Scanline[y];
44+ Row := Bitmap.Scanline[y];
45+ for x := 0 to (Bitmap.Width - 1) do begin
46+ Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;
47+ Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading
48+ Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;
49+ end;
50+ end;
51+ Form1.Canvas.Draw(0, 0, Bitmap);
52+ InvalidateRect(Form1.Handle, nil, False);
53+ RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);
54+ end;
55+ finally
56+ BaseBitmap.Free;
57+ end;
58+ finally
59+ Bitmap.Free;
60+ end;
61+end;
62+```
6163
6264Esta função no Lazarus pode ser implementada como:
6365
64-`procedure TForm1.FadeIn(ABitMap: TBitMap);`\
65-`var`\
66-`  SrcIntfImg, TempIntfImg: TLazIntfImage;`\
67-`  ImgHandle,ImgMaskHandle: HBitmap;`\
68-`  FadeStep: Integer;`\
69-`  px, py: Integer;`\
70-`  CurColor: TFPColor;`\
71-`  TempBitmap: TBitmap;`\
72-`begin`\
73-`  SrcIntfImg:=TLazIntfImage.Create(0,0);`\
74-`  SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);`\
75-`  TempIntfImg:=TLazIntfImage.Create(0,0);`\
76-`  TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);`\
77-`  TempBitmap:=TBitmap.Create;`\
78-`  for FadeStep:=1 to 32 do begin`\
79-`    for py:=0 to SrcIntfImg.Height-1 do begin`\
80-`      for px:=0 to SrcIntfImg.Width-1 do begin`\
81-`        CurColor:=SrcIntfImg.Colors[px,py];`\
82-`        CurColor.Red:=(CurColor.Red*FadeStep) shr 5;`\
83-`        CurColor.Green:=(CurColor.Green*FadeStep) shr 5;`\
84-`        CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;`\
85-`        TempIntfImg.Colors[px,py]:=CurColor;`\
86-`      end;`\
87-`    end;`\
88-`    TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);`\
89-`    TempBitmap.Handle:=ImgHandle;`\
90-`    TempBitmap.MaskHandle:=ImgMaskHandle;`\
91-`    Canvas.Draw(0,0,TempBitmap);`\
92-`  end;`\
93-`  SrcIntfImg.Free;`\
94-`  TempIntfImg.Free;`\
95-`  TempBitmap.Free;`\
96-`end;`
66+``` pascal
67+procedure TForm1.FadeIn(ABitMap: TBitMap);
68+var
69+ SrcIntfImg, TempIntfImg: TLazIntfImage;
70+ ImgHandle,ImgMaskHandle: HBitmap;
71+ FadeStep: Integer;
72+ px, py: Integer;
73+ CurColor: TFPColor;
74+ TempBitmap: TBitmap;
75+begin
76+ SrcIntfImg:=TLazIntfImage.Create(0,0);
77+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
78+ TempIntfImg:=TLazIntfImage.Create(0,0);
79+ TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
80+ TempBitmap:=TBitmap.Create;
81+ for FadeStep:=1 to 32 do begin
82+ for py:=0 to SrcIntfImg.Height-1 do begin
83+ for px:=0 to SrcIntfImg.Width-1 do begin
84+ CurColor:=SrcIntfImg.Colors[px,py];
85+ CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
86+ CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
87+ CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
88+ TempIntfImg.Colors[px,py]:=CurColor;
89+ end;
90+ end;
91+ TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);
92+ TempBitmap.Handle:=ImgHandle;
93+ TempBitmap.MaskHandle:=ImgMaskHandle;
94+ Canvas.Draw(0,0,TempBitmap);
95+ end;
96+ SrcIntfImg.Free;
97+ TempIntfImg.Free;
98+ TempBitmap.Free;
99+end;
100+```
97101
98102O código Lazarus desta página foi pego do projeto \$LazarusPath/examples/lazintfimage/fadein1.lpi. Deste modo se você quiser começar bem com programação gráfica veja este exemplo.
99103
100104### Desenhando bitmaps com cor transparente
101105
102106Uma nova habilidade, implementada no Lazarus 0.9.11, é os bitmaps com cor transparente. Arquivos de Bitmap (*.BMP) não podem guardar informações sobre transparencia, mas podem trabalhar como se contivessem se definirmos uma cor para representar a área transparente. Este é um truque comum utilizado por aplicativos do Windows.
103107
104108O exemplo a seguir carrega um bitmap guardado dentro do executável como um recurso do Windows, seleciona uma cor para ser transparente (clFuchsia) e desenha a imagem para um Canvas.
105109
106110 procedure MyForm.MyButtonOnClick(Sender: TObject);
107111 var
108112 buffer: THandle;
109113 bmp: TBitmap;
110114 memstream: TMemoryStream;
111115 begin
112116 bmp := TBitmap.Create;
113117
114118 buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
115119
116120 if (buffer = 0) then exit; // Error loading the bitmap
117121
118122 bmp.Handle := buffer;
119123 memstream := TMemoryStream.create;
120124 try
121125 bmp.SaveToStream(memstream);
122126 memstream.position := 0;
123127 bmp.LoadFromStream(memstream);
124128 finally
125129 memstream.free;
126130 end;
127131
128132 bmp.Transparent := True;
129133 bmp.TransparentColor := clFuchsia;
130134
131135 MyCanvas.Draw(0, 0, bmp);
132136
133137 bmp.Free; // Release allocated resource
134138 end;
135139
136140Note as operações de memória executadas com o [[doc:rtl/classes/tmemorystream.html|TMemoryStream]]. Elas são necessárias para assegurar que a imagem seja carregada corretamente.
137141
138142## Gráficos em movimento - Como evitar a tremulação
139143
140144Muitos programas desenham gráficos 2D na interface de usuário. Caso estes gráficos precisem mudar rapida e continuamente você logo irá se deparar com um problema: A tremulação. Isto ocorre quando o usuário as vezes vê a imagem inteira e as vezes a imagem apenas parcialmente desenhada. Isto ocorre pois o processo de pintura demanda tempo.
141145
142146Mas como evitar a tremulação e obter a melhor performance de desenho? É claro que você pode trabalhar com aceleração de hardware usando o OpenGL, mas esta abordagem pode ser muito pesada para programas pequenos ou computadores antigos. Este tutorial foca no desenho utilizando o objeto TCanvas. Se você precisa de ajuda com o OpenGL observe os exemplos que vêm com o Lazarus. Você também pode usar o A.J. Venter's gamepack, que provê um canvas com buffer duplo e um componente de sprite.
143147
144148Agora nós examinaremos as opções que temos para desenhar na tela:
145149
146150- Desenhando num TImage
147151- Desenhando no evento OnPaint de uma janela, TPaintBox ou outro controle
148152- Criando um controle personalizado que se desenha
149153- Utilizando o A.J. Venter's gamepack
150154
151155### Desenhando num TImage
152156
153157Nunca use o evento OnPaint para desenhar na TImage. O TImage é armazenado temporariamente , e assim tudo que você precisa fazer é desenhar em qualquer lugar e e a mudança será para sempre. Todavia, se você está constantemente redesenhando, a imagem tremulará. Neste caso pode tentar outras opções. Desenhar com o TImage é considerado lento comparado a outras abordagens.
154158
155-`procedure TForm1.BitBtn1Click(Sender: TObject);`\
156-`var`\
157-`  x, y: Integer;`\
158-`begin`\
159-`  // Draws the backgroung`\
160-`  Image.Canvas.Pen.Color := clWhite;`\
161-`  Image.Canvas.Rectangle(0, 0, Image.Width, Image.Height);`\
162-`  `\
163-`  // Draws squares`\
164-`  Bitmap.Canvas.Pen.Color := clBlack;`\
165-`  for x := 1 to 8 do`\
166-`   for y := 1 to 8 do`\
167-`    Image.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),`\
168-`       Round(x * Image.Width / 8), Round(y * Image.Height / 8));`\
169-`end;`
159+``` pascal
160+procedure TForm1.BitBtn1Click(Sender: TObject);
161+var
162+ x, y: Integer;
163+begin
164+ // Draws the backgroung
165+ Image.Canvas.Pen.Color := clWhite;
166+ Image.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
167+
168+ // Draws squares
169+ Bitmap.Canvas.Pen.Color := clBlack;
170+ for x := 1 to 8 do
171+ for y := 1 to 8 do
172+ Image.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
173+ Round(x * Image.Width / 8), Round(y * Image.Height / 8));
174+end;
175+```
170176
171177### Desenhando no evento OnPaint
172178
173179Neste caso todo o desenho tem que ser feito no evento OnPaint. Não permanece no armazenamento temporário, como no evento TImage.
174180
175181### Criando um controle personalizado que se desenha
176182
177183Utilizar um controle personalizado possuí a vantagem de estruturar o código e permitir o reuso do controle. Esta abordagem é muito rápida, mas pode gerar tremulação caso a imagem não seja primeiro desenhada para um TBitmap e depois passada para a tela. Neste caso não há necessidade de utilizar o evento OnPaint do controle.
178184
179185Aqui está um controle personalizado como exemplo:
180186
181-`type`\
182-`  TMyDrawingControl = class(TCustomControl)`\
183-`  public`\
184-`    procedure Paint; override;`\
185-`  end;`\
187+``` pascal
188+type
189+ TMyDrawingControl = class(TCustomControl)
190+ public
191+ procedure Paint; override;
192+ end;
186193
187-`implementation`\
194+implementation
188195
189-`procedure TMyDrawingControl.Paint;`\
190-`var`\
191-`  x, y: Integer;`\
192-`  Bitmap: TBitmap;`\
193-`begin`\
194-`  Bitmap := TBitmap.Create;`\
195-`  try`\
196-`    // Initializes the Bitmap Size`\
197-`    Bitmap.Height := Height;`\
198-`    Bitmap.Width := Width;`\
199-` `\
200-`    // Draws the background`\
201-`    Bitmap.Canvas.Pen.Color := clWhite;`\
202-`    Bitmap.Canvas.Rectangle(0, 0, Width, Height);`\
196+procedure TMyDrawingControl.Paint;
197+var
198+ x, y: Integer;
199+ Bitmap: TBitmap;
200+begin
201+ Bitmap := TBitmap.Create;
202+ try
203+ // Initializes the Bitmap Size
204+ Bitmap.Height := Height;
205+ Bitmap.Width := Width;
206+
207+ // Draws the background
208+ Bitmap.Canvas.Pen.Color := clWhite;
209+ Bitmap.Canvas.Rectangle(0, 0, Width, Height);
203210
204-`    // Draws squares`\
205-`    Bitmap.Canvas.Pen.Color := clBlack;`\
206-`    for x := 1 to 8 do`\
207-`     for y := 1 to 8 do`\
208-`      Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),`\
209-`       Round(x * Width / 8), Round(y * Height / 8));`\
210-`      `\
211-`    Canvas.Draw(0, 0, Bitmap);`\
212-`  finally`\
213-`    Bitmap.Free;`\
214-`  end;`\
211+ // Draws squares
212+ Bitmap.Canvas.Pen.Color := clBlack;
213+ for x := 1 to 8 do
214+ for y := 1 to 8 do
215+ Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
216+ Round(x * Width / 8), Round(y * Height / 8));
217+
218+ Canvas.Draw(0, 0, Bitmap);
219+ finally
220+ Bitmap.Free;
221+ end;
215222
216-`  inherited Paint;`\
217-`end;`
223+ inherited Paint;
224+end;
225+```
218226
219227e como o criamos numa janela:
220228
221-`procedure TMyForm.FormCreate(Sender: TObject);`\
222-`begin`\
223-`  MyDrawingControl:= TMyDrawingControl.Create(Self);`\
224-`  MyDrawingControl.Height := 400;`\
225-`  MyDrawingControl.Width := 500;`\
226-`  MyDrawingControl.Top := 0;`\
227-`  MyDrawingControl.Left := 0;`\
228-`  MyDrawingControl.Parent := Self;`\
229-`  MyDrawingControl.DoubleBuffered := True;`\
230-`end;`
229+``` pascal
230+procedure TMyForm.FormCreate(Sender: TObject);
231+begin
232+ MyDrawingControl:= TMyDrawingControl.Create(Self);
233+ MyDrawingControl.Height := 400;
234+ MyDrawingControl.Width := 500;
235+ MyDrawingControl.Top := 0;
236+ MyDrawingControl.Left := 0;
237+ MyDrawingControl.Parent := Self;
238+ MyDrawingControl.DoubleBuffered := True;
239+end;
240+```
231241
232242apenas não esqueça de destrui-lo:
233243
234-`procedure TMyForm.FormDestroy(Sender: TObject);`\
235-`begin`\
236-`  MyDrawingControl.Free;`\
237-`end;`
244+``` pascal
245+procedure TMyForm.FormDestroy(Sender: TObject);
246+begin
247+ MyDrawingControl.Free;
248+end;
249+```
238250
239251Atribuir zero às propriedades Top e Left não é necessário, logo que essa é a posição padrão, mas isso é feito para reforçar onde o controle será posto.
240252
241253"MyDrawingControl.Parent := Self;" é muito importante e você não verá seu controle se não o fizer.
242254
243255"MyDrawingControl.DoubleBuffered := True;" é necessário para evitar flutuação/tremulação no Windows. Mas isto não faz efeito com GTK.
244256
245257### Utilizando o A.J. Venter's gamepack
246258
247259The 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:\
248-`svn co `[`svn://silentcoder.co.za/lazarus/gamepack`](svn://silentcoder.co.za/lazarus/gamepack)
260+
261+```text
262+svn co svn://silentcoder.co.za/lazarus/gamepack
263+```
249264
250265---
251266*From the Free Pascal wiki, page "Developing with Graphics/pt", as it stood on 2010-09-15 after a change by Sfalcao, 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