Developing with Graphics/pt: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## Visão Geral | |
| 2 | 2 | ||
| 3 | 3 | Esta 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. | |
| 4 | 4 | ||
| 5 | 5 | Nesta página algumas informações gerais serão fornecidas. | |
| 6 | 6 | ||
| 7 | 7 | ## Outros Tutoriais Gráficos | |
| 8 | 8 | ||
| 9 | 9 | - [[GLScene]] - Uma porta para a biblioteca de gráficos visuais OpenGL [GLScene](http://www.glscene.org) | |
| 10 | 10 | - [[TAChart/pt]] - Componente gráfico para Lazarus | |
| 11 | 11 | - [[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. | |
| 12 | 12 | - [[PlotPanel]] - Um componente para traçar e realizar gráficos animados. | |
| 13 | 13 | ||
| 14 | 14 | ## Trabalhando com TBitmap | |
| 15 | 15 | ||
| 16 | 16 | A 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 | |
| 17 | 17 | ||
| 18 | 18 | ### Um exemplo de desvanescimento (fading) | |
| 19 | 19 | ||
| 20 | - | ||
| 20 | + | Digamos que você deseja fazer uma figura desaparecer lentamente. No Delphi você pode fazer algo como: | |
| 21 | 21 | ||
| 22 | 22 | `type`\ | |
| 23 | 23 | ` PRGBTripleArray = ^TRGBTripleArray;`\ | |
| 24 | 24 | ` TRGBTripleArray = array[0..32767] of TRGBTriple;`\ | |
| 25 | - | ||
| 25 | + | ||
| 26 | 26 | `procedure TForm1.FadeIn(aBitMap: TBitMap);`\ | |
| 27 | 27 | `var`\ | |
| 28 | 28 | ` Bitmap, BaseBitmap: TBitmap;`\ | |
| 29 | 29 | ` Row, BaseRow: PRGBTripleArray;`\ | |
| 30 | 30 | ` x, y, step: integer;`\ | |
| 31 | 31 | `begin`\ | |
| 32 | 32 | ` Bitmap := TBitmap.Create;`\ | |
| 33 | 33 | ` try`\ | |
| 34 | 34 | ` Bitmap.PixelFormat := pf32bit; // ou pf24bit`\ | |
| 35 | 35 | ` Bitmap.Assign(aBitMap);`\ | |
| 36 | 36 | ` BaseBitmap := TBitmap.Create;`\ | |
| 37 | 37 | ` try`\ | |
| 38 | 38 | ` BaseBitmap.PixelFormat := pf32bit;`\ | |
| 39 | 39 | ` BaseBitmap.Assign(Bitmap);`\ | |
| 40 | 40 | ` for step := 0 to 32 do begin`\ | |
| 41 | 41 | ` for y := 0 to (Bitmap.Height - 1) do begin`\ | |
| 42 | 42 | ` BaseRow := BaseBitmap.Scanline[y];`\ | |
| 43 | 43 | ` Row := Bitmap.Scanline[y];`\ | |
| 44 | 44 | ` for x := 0 to (Bitmap.Width - 1) do begin`\ | |
| 45 | 45 | ` Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;`\ | |
| 46 | 46 | ` Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading`\ | |
| 47 | 47 | ` Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;`\ | |
| 48 | 48 | ` end;`\ | |
| 49 | 49 | ` end;`\ | |
| 50 | 50 | ` Form1.Canvas.Draw(0, 0, Bitmap);`\ | |
| 51 | 51 | ` InvalidateRect(Form1.Handle, nil, False);`\ | |
| 52 | 52 | ` RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);`\ | |
| 53 | 53 | ` end;`\ | |
| 54 | 54 | ` finally`\ | |
| 55 | 55 | ` BaseBitmap.Free;`\ | |
| 56 | 56 | ` end;`\ | |
| 57 | 57 | ` finally`\ | |
| 58 | 58 | ` Bitmap.Free;`\ | |
| 59 | 59 | ` end;`\ | |
| 60 | 60 | `end;` | |
| 61 | 61 | ||
| 62 | - | ||
| 62 | + | Esta função no Lazarus pode ser implementada como: | |
| 63 | 63 | ||
| 64 | 64 | `procedure TForm1.FadeIn(ABitMap: TBitMap);`\ | |
| 65 | 65 | `var`\ | |
| 66 | 66 | ` SrcIntfImg, TempIntfImg: TLazIntfImage;`\ | |
| 67 | 67 | ` ImgHandle,ImgMaskHandle: HBitmap;`\ | |
| 68 | 68 | ` FadeStep: Integer;`\ | |
| 69 | 69 | ` px, py: Integer;`\ | |
| 70 | 70 | ` CurColor: TFPColor;`\ | |
| 71 | 71 | ` TempBitmap: TBitmap;`\ | |
| 72 | 72 | `begin`\ | |
| 73 | 73 | ` SrcIntfImg:=TLazIntfImage.Create(0,0);`\ | |
| 74 | 74 | ` SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);`\ | |
| 75 | 75 | ` TempIntfImg:=TLazIntfImage.Create(0,0);`\ | |
| 76 | 76 | ` TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);`\ | |
| 77 | 77 | ` TempBitmap:=TBitmap.Create;`\ | |
| 78 | 78 | ` for FadeStep:=1 to 32 do begin`\ | |
| 79 | 79 | ` for py:=0 to SrcIntfImg.Height-1 do begin`\ | |
| 80 | 80 | ` for px:=0 to SrcIntfImg.Width-1 do begin`\ | |
| 81 | 81 | ` CurColor:=SrcIntfImg.Colors[px,py];`\ | |
| 82 | 82 | ` CurColor.Red:=(CurColor.Red*FadeStep) shr 5;`\ | |
| 83 | 83 | ` CurColor.Green:=(CurColor.Green*FadeStep) shr 5;`\ | |
| 84 | 84 | ` CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;`\ | |
| 85 | 85 | ` TempIntfImg.Colors[px,py]:=CurColor;`\ | |
| 86 | 86 | ` end;`\ | |
| 87 | 87 | ` end;`\ | |
| 88 | 88 | ` TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);`\ | |
| 89 | 89 | ` TempBitmap.Handle:=ImgHandle;`\ | |
| 90 | 90 | ` TempBitmap.MaskHandle:=ImgMaskHandle;`\ | |
| 91 | 91 | ` Canvas.Draw(0,0,TempBitmap);`\ | |
| 92 | 92 | ` end;`\ | |
| 93 | 93 | ` SrcIntfImg.Free;`\ | |
| 94 | 94 | ` TempIntfImg.Free;`\ | |
| 95 | 95 | ` TempBitmap.Free;`\ | |
| 96 | 96 | `end;` | |
| 97 | 97 | ||
| 98 | - | ||
| 98 | + | O 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. | |
| 99 | 99 | ||
| 100 | 100 | ### Desenhando bitmaps com cor transparente | |
| 101 | 101 | ||
| 102 | 102 | Uma 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. | |
| 103 | 103 | ||
| 104 | 104 | O 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. | |
| 105 | 105 | ||
| 106 | 106 | procedure MyForm.MyButtonOnClick(Sender: TObject); | |
| 107 | 107 | var | |
| 108 | 108 | buffer: THandle; | |
| 109 | 109 | bmp: TBitmap; | |
| 110 | 110 | memstream: TMemoryStream; | |
| 111 | 111 | begin | |
| 112 | 112 | bmp := TBitmap.Create; | |
| 113 | 113 | ||
| 114 | 114 | buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID)); | |
| 115 | 115 | ||
| 116 | 116 | if (buffer = 0) then exit; // Error loading the bitmap | |
| 117 | 117 | ||
| 118 | 118 | bmp.Handle := buffer; | |
| 119 | 119 | memstream := TMemoryStream.create; | |
| 120 | 120 | try | |
| 121 | 121 | bmp.SaveToStream(memstream); | |
| 122 | 122 | memstream.position := 0; | |
| 123 | 123 | bmp.LoadFromStream(memstream); | |
| 124 | 124 | finally | |
| 125 | 125 | memstream.free; | |
| 126 | 126 | end; | |
| 127 | 127 | ||
| 128 | 128 | bmp.Transparent := True; | |
| 129 | 129 | bmp.TransparentColor := clFuchsia; | |
| 130 | 130 | ||
| 131 | 131 | MyCanvas.Draw(0, 0, bmp); | |
| 132 | 132 | ||
| 133 | 133 | bmp.Free; // Release allocated resource | |
| 134 | 134 | end; | |
| 135 | 135 | ||
| 136 | 136 | Note 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. | |
| 137 | 137 | ||
| 138 | 138 | ## Gráficos em movimento - Como evitar a tremulação | |
| 139 | 139 | ||
| 140 | 140 | Muitos 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. | |
| 141 | 141 | ||
| 142 | 142 | Mas 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. | |
| 143 | 143 | ||
| 144 | 144 | Agora nós examinaremos as opções que temos para desenhar na tela: | |
| 145 | 145 | ||
| 146 | 146 | - Desenhando num TImage | |
| 147 | 147 | - Desenhando no evento OnPaint de uma janela, TPaintBox ou outro controle | |
| 148 | 148 | - Criando um controle personalizado que se desenha | |
| 149 | 149 | - Utilizando o A.J. Venter's gamepack | |
| 150 | 150 | ||
| 151 | 151 | ### Desenhando num TImage | |
| 152 | 152 | ||
| 153 | 153 | Nunca 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. | |
| 154 | 154 | ||
| 155 | - | ||
| 156 | - | ||
| 157 | 155 | `procedure TForm1.BitBtn1Click(Sender: TObject);`\ | |
| 158 | 156 | `var`\ | |
| 159 | 157 | ` x, y: Integer;`\ | |
| 160 | 158 | `begin`\ | |
| 161 | 159 | ` // Draws the backgroung`\ | |
| 162 | 160 | ` Image.Canvas.Pen.Color := clWhite;`\ | |
| 163 | 161 | ` Image.Canvas.Rectangle(0, 0, Image.Width, Image.Height);`\ | |
| 164 | 162 | ` `\ | |
| 165 | 163 | ` // Draws squares`\ | |
| 166 | 164 | ` Bitmap.Canvas.Pen.Color := clBlack;`\ | |
| 167 | 165 | ` for x := 1 to 8 do`\ | |
| 168 | 166 | ` for y := 1 to 8 do`\ | |
| 169 | 167 | ` Image.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),`\ | |
| 170 | 168 | ` Round(x * Image.Width / 8), Round(y * Image.Height / 8));`\ | |
| 171 | 169 | `end;` | |
| 172 | 170 | ||
| 173 | - | ||
| 174 | - | ||
| 175 | 171 | ### Desenhando no evento OnPaint | |
| 176 | 172 | ||
| 177 | 173 | Neste caso todo o desenho tem que ser feito no evento OnPaint. Não permanece no armazenamento temporário, como no evento TImage. | |
| 178 | 174 | ||
| 179 | 175 | ### Criando um controle personalizado que se desenha | |
| 180 | 176 | ||
| 181 | 177 | Utilizar 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. | |
| 182 | 178 | ||
| 183 | 179 | Aqui está um controle personalizado como exemplo: | |
| 184 | 180 | ||
| 185 | - | ||
| 186 | - | ||
| 187 | 181 | `type`\ | |
| 188 | 182 | ` TMyDrawingControl = class(TCustomControl)`\ | |
| 189 | 183 | ` public`\ | |
| 190 | 184 | ` procedure Paint; override;`\ | |
| 191 | 185 | ` end;`\ | |
| 192 | - | ||
| 186 | + | ||
| 193 | 187 | `implementation`\ | |
| 194 | - | ||
| 188 | + | ||
| 195 | 189 | `procedure TMyDrawingControl.Paint;`\ | |
| 196 | 190 | `var`\ | |
| 197 | 191 | ` x, y: Integer;`\ | |
| 198 | 192 | ` Bitmap: TBitmap;`\ | |
| 199 | 193 | `begin`\ | |
| 200 | 194 | ` Bitmap := TBitmap.Create;`\ | |
| 201 | 195 | ` try`\ | |
| 202 | 196 | ` // Initializes the Bitmap Size`\ | |
| 203 | 197 | ` Bitmap.Height := Height;`\ | |
| 204 | 198 | ` Bitmap.Width := Width;`\ | |
| 205 | 199 | ` `\ | |
| 206 | 200 | ` // Draws the background`\ | |
| 207 | 201 | ` Bitmap.Canvas.Pen.Color := clWhite;`\ | |
| 208 | 202 | ` Bitmap.Canvas.Rectangle(0, 0, Width, Height);`\ | |
| 209 | - | ||
| 203 | + | ||
| 210 | 204 | ` // Draws squares`\ | |
| 211 | 205 | ` Bitmap.Canvas.Pen.Color := clBlack;`\ | |
| 212 | 206 | ` for x := 1 to 8 do`\ | |
| 213 | 207 | ` for y := 1 to 8 do`\ | |
| 214 | 208 | ` Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),`\ | |
| 215 | 209 | ` Round(x * Width / 8), Round(y * Height / 8));`\ | |
| 216 | 210 | ` `\ | |
| 217 | 211 | ` Canvas.Draw(0, 0, Bitmap);`\ | |
| 218 | 212 | ` finally`\ | |
| 219 | 213 | ` Bitmap.Free;`\ | |
| 220 | 214 | ` end;`\ | |
| 221 | - | ||
| 215 | + | ||
| 222 | 216 | ` inherited Paint;`\ | |
| 223 | 217 | `end;` | |
| 224 | 218 | ||
| 225 | - | ||
| 219 | + | e como o criamos numa janela: | |
| 226 | 220 | ||
| 227 | 221 | `procedure TMyForm.FormCreate(Sender: TObject);`\ | |
| 228 | 222 | `begin`\ | |
| 229 | 223 | ` MyDrawingControl:= TMyDrawingControl.Create(Self);`\ | |
| 230 | 224 | ` MyDrawingControl.Height := 400;`\ | |
| 231 | 225 | ` MyDrawingControl.Width := 500;`\ | |
| 232 | 226 | ` MyDrawingControl.Top := 0;`\ | |
| 233 | 227 | ` MyDrawingControl.Left := 0;`\ | |
| 234 | 228 | ` MyDrawingControl.Parent := Self;`\ | |
| 235 | 229 | ` MyDrawingControl.DoubleBuffered := True;`\ | |
| 236 | 230 | `end;` | |
| 237 | 231 | ||
| 238 | - | ||
| 232 | + | apenas não esqueça de destrui-lo: | |
| 239 | 233 | ||
| 240 | 234 | `procedure TMyForm.FormDestroy(Sender: TObject);`\ | |
| 241 | 235 | `begin`\ | |
| 242 | 236 | ` MyDrawingControl.Free;`\ | |
| 243 | 237 | `end;` | |
| 244 | - | ||
| 245 | - | ||
| 246 | 238 | ||
| 247 | 239 | Atribuir 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. | |
| 248 | 240 | ||
| 249 | 241 | "MyDrawingControl.Parent := Self;" é muito importante e você não verá seu controle se não o fizer. | |
| 250 | 242 | ||
| 251 | 243 | "MyDrawingControl.DoubleBuffered := True;" é necessário para evitar flutuação/tremulação no Windows. Mas isto não faz efeito com GTK. | |
| 252 | 244 | ||
| 253 | 245 | ### Utilizando o A.J. Venter's gamepack | |
| 254 | 246 | ||
| 255 | 247 | 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:\ | |
| 256 | 248 | `svn co `[`svn://silentcoder.co.za/lazarus/gamepack`](svn://silentcoder.co.za/lazarus/gamepack) | |
| 257 | 249 | ||
| 258 | 250 | --- | |
| 259 | 251 | *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).* |
