delphi canvas
TRANSCRIPT
DELPHI 4.0 – Capítulo 5: Gráficos 56
Anotações:
5. GRÁFICOS
5.1. O CANVAS
O canvas (tela) representa uma superfície em um objeto onde se pode desenhar um bitmap. O can-vas é sempre uma propriedade de alguma coisa e nunca uma classe ou objeto por si mesmo.
Quando se deseja desenhar ou pintar sobre um objeto, deve-se desenhar ou pintar sobre o canvas a ele associado.
Operações de desenho envolvem a manipulação de pixels individuais de modo a se desenhar pon-
tos ou linhas. Por exemplo, a cor de um pixel pode ser alterada com a seguinte instrução:
Canvas.Pixels [10, 10] := clRed;
Operações de pintura envolvem a manipulação de grandes quantidades de pixels. Geralmente, pin-tura inclui desenho. Para desenhar ou pintar, o usuário provavelmente pensará em usar o mouse. Antes de implemenatr métodos de desenho, portanto, é necessário ver como o Delphi pode responder a eventos de mouse.
5.2. RESPONDENDO AO MOUSE
Existem quatro tipos de eventos de mouse que podem capturados por um aplicativo. Destes, três
são realmente causados por ações do mouse: Mouse-Down, Mouse-Up e Mouse-Move. Um querto evento de mouse seria o Click, mas este evento é um pouco diferente, correspondendo a uma sequência completa que compreende clicar e liberar o botão esquerdo do mouse. O evento Click não é capaz de diferenciar entre os botões do mouse ou detectar teclas pressionadas junto com o botão do mouse.
Os eventos OnMouseDown, OnMouseMove e OnMouseUp repassam os seguintes parâmetros:
Parâmetro Significado
Sender O objeto que causou o evento Button Indica qual o botão envolvido no evento: mbLeft,
mbMiddle, or mbRight Shift Indica o estado das teclas Alt, Ctrl e Shift keys du-
rante o evento de mouse X, Y São as coordenadas da tela onde ocorreu o evento.
DELPHI 4.0 – Capítulo 5: Gráficos 57
Anotações:
O Delphi gera automaticamente um manipulador de eventos para os eventos de Mouse, os quais têm a seguinte forma:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
end;
Por exemplo, o trecho de código a seguir mostrará a palvra “Aqui !” nas coordenadas da tela onde o
mouse for clicado. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, 'Aqui!');
end;
Podemos pensar em desenhar linhas retas com o mouse, como fazem programas de edição de figu-
ras. Clicando com o mouse, definimos o início da linha a ser desenhada. Para isto, usamos o método Mo-veTo para definir uma nova posição gráfica (a posição do último evento gráfico na tela).
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.MoveTo(X, Y);
end;
Pressionando o mouse, agora, definimos a posição da caneta (pen position). Para desenhar a linha, devemos capturar o evento OnMouseUp e definir as coordenadas da tela onde o usuário liberou o mouse.
Um evento OnMouseUp ocorre sempre que o usuário libera o botão do mouse sobre um objeto. Este evento, por si mesmo, não detecta o movimento do mouse. Podemos usar o método LineTo para desenhar uma linha reta que irá da posição da caneta (PenPos) até a coordenada onde o mouse foi liberado:
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);
end;
DELPHI 4.0 – Capítulo 5: Gráficos 58
Anotações:
Por enquanto, o usuário não pode ver a linha que está FlagDesenho. Para que isto seja possível,
devemos capturar o evento OnMouseMove e usá-lo para desenhar uma “inha elástica”. O evento OnMouseMove ocorre periodicamente, quando o usuário move o mouse sobre um objeto.O exem-plo a seguir mostra como desenhar linhas retas a partir da PenPos até a coordenada onde ocorrre o evento OnMouseMove. Entretanto, por enquanto, todos os desenhos intermediários aparecem e não são apagados.
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);
end;
Note que o evento OnMouseMove ocorre mesmo se o usuário não pressionar qualquer botão. Se você quizer detectar se um botão, por exemplo, o botão direito, foi pressionado, será necessário adicionar um campo (field) ao formulário.
Quando você adiciona um componente a um formulário, o Delphi também adiciona um campo que representa o componente para o formulário. Você pode se referir ao componente pelo nome do campo. Você também pode inserir campos, editando a declaração de tipos (type) na parte de cima da unidade do formulá-rio. Para detectar se o botão do mouse foi pressionado, basta adicionar um campo do tipo Boolean.
O seguinte trecho de código declara dois campos, um do tipo Boolean e outro do tipo Tpoint, que servirá para armazenar coordenadas:
type
TForm1 = class(TForm)
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
public
FlagDesenho: Boolean; {campo para detectar se um botão foi .
pressionado}
Origem, MovePt: TPoint; { campo para armazenar pontos}
end;
A seguir, o campo FlagDesenho será feito igual a True no início do evento OnMouseDown, e igual a false no evento OnMouseUp.
DELPHI 4.0 – Capítulo 5: Gráficos 59
Anotações:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FlagDesenho := True;
Canvas.MoveTo(X, Y);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X, Y);
FlagDesenho := False;
end;
A procedure do evento OnMouseMove deve ser modificada para desenhar linhas apenas se o botão tiver sido pressionado.
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FlagDesenho then
Canvas.LineTo(X, Y);
end;
O problema, agora, é que em vez de uma linha reta, o que obtemos é uma linha desenhada a mão li-
vre. Isto ocorre porque, cada vez que uma linha é desenhada, o mouse atualiza a PenPos do objeto Canvas. Para resolver este problema, devemos armazenar as coordenadas do evento OnMouseDown inicial e dese-nhar a linha a partir delas.
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FlagDesenho := True;
Canvas.MoveTo(X, Y);
Origem := Point(X, Y);
end;
DELPHI 4.0 – Capítulo 5: Gráficos 60
Anotações:
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Canvas.MoveTo(Origem.X, Origem.Y);
Canvas.LineTo(X, Y);
FlagDesenho := False;
end;
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FlagDesenho then
begin
Canvas.MoveTo(Origem.X, Origem.Y);
Canvas.LineTo(X, Y);
end;
end;
Note a maneira usada para nos referirmos ao campo Origem (Origem.X e Origem.Y).
O que temos por enquanto é o seguinte: o evento OnMouseDown fixa as coordenadas do evento e define a PenPos. OnMouseMove desenha uma linha reta sempre começando da Origem até as coordenadas atuais. A linha final é fixada no evento OnMouseUp. Contudo, as linhas intermediárias ainda não sãio apa-gadas. Isto pode ser corrigido apagando-se cada linha antes que a próxima seja desenhada. Um novo cam-po, denominado MovePonto, será usado para memorizar a linha anterior. Além disso, para apagar uma linha, é mais fácil desenhar outra linha sobre ela, com o modo de desenho da caneta (Pen.Mode) definido para pmNotXor.
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
. Shift: TShiftState; X, Y: Integer);
begin
FlagDesenho := True;
Canvas.MoveTo(X, Y);
Origem := Point(X, Y);
MovePonto := Point(X, Y); { memoriza as coordenadas último
movimento}
end;
procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FlagDesenho then
DELPHI 4.0 – Capítulo 5: Gráficos 61
Anotações:
begin
Canvas.Pen.Mode := pmNotXor; { usa o modo XOR para .
desenhar}
Canvas.MoveTo(Origem.X, Origem.Y); {move a caneta para a
origem}
Canvas.LineTo(MovePonto.X, MovePonto.Y); { apaga a linha .
anterior}
Canvas.MoveTo(Origem.X, Origem.Y); { volta para a origem}
Canvas.LineTo(X, Y); { desenha a nova .
linha}
end;
MovePonto := Point(X, Y); { memoriza as coordenadas .
para o . próximo movimento}
Canvas.Pen.Mode := pmCopy; {retorna ao modo Copy (default)]
end;
O resultado deve ser uma linha reta elástica, que pode ser esticada pela tela com o evento OnMou-seMove e fixa com o evento OnMouseUp. Note que o campo MovePonto, do tipo Tpoint, deve ser definido na seção Type, juntamente com Origem e FlagDesenho
5.3. DESENHANDO FIGURAS
O próximo passo do nosso programa gráfico é ser capza de desenhar figuras como retângulos e elipses. Para tanto, precisamos definir qual ferramenta de desenho será usada e a maneira mais fácil de fazer isto, em Pascal, é usar um tipo enumerado, que pode ser declarado da seguinte forma: type TFerramenta = (frLinha, frRetângulo, frElipse, frRetRedondo);
Por convenção, identificadores de tipocomeçam com a letra T. e grupos de constantes começam com
o mesmo prefixo de duas letras. A declaração do tipo Tferramenta é similar a declarar constantes separadas: const
frLinha = 0;
frRetângulo = 1;
frElipse = 2;
frRetRedondo = 3;
DELPHI 4.0 – Capítulo 5: Gráficos 62
Anotações:
Entretanto, a declaração de um tipo enumerado apresenta certas vantagens, pois não será possível atribuir à variável um valor não definido. Caso isso aconteça, um erro de compilação é gerado. O próximo passo é declarar um campo Ferramenta como pertencente ao tipo Tferramenta.
type
TFerramenta = (frLinha, frRetângulo, frElipse, frRetRedondo);
TForm1 = class(TForm)
public
FlagDesenho: Boolean;
Origem, MovePonto: TPoint;
Ferramenta: TDrawingTool;
end;
É conveniente lembrar que todos os objetos inicializam todos os seus campos em zero, o que signi-fica que o valor inicial de Ferramenta será frLinha.
Você pode, agora, posicionar quatro botões do tipo SpeedButtons no formulário e usá-los para defi-nir a ferramenta de desenho a ser usada. Por exemplo, se o nome deste botões forem spbLinha, spbRetân-gulo, spbElipse e spbRetRedondo, os manipuladores de eventos serão, respectivemente:
procedure TForm1.spbLinhaClick(Sender: TObject);
begin
Ferramenta := frLinha;
end;
procedure TForm1.spbRetânguloClick(Sender: TObject);
begin
Ferramenta := frRetângulo;
end;
procedure TForm1.spbElipseClick(Sender: TObject);
begin
Ferramenta := frElipse;
end;
DELPHI 4.0 – Capítulo 5: Gráficos 63
Anotações:
procedure TForm1.sobRetRedondoClick(Sender: TObject);
begin
Ferramenta := frRetRedondo;
end;
A escolha de quais procedimentos serão executados quando o usuário pressionar cada um dos bo-tões poderia ser feita com instruções If ..Then. Entretanto, o Object Pascal fornece o comando Case, que é muito mais eficiente neste caso. Além disso, para desenhar figuras basta executar o método apropriado:
procedure TForm1.FormMouseUp(Sender: TObject);
begin
case Ferramenta of
frLinha:
begin
Canvas.MoveTo(Origem.X, Origem.Y);
Canvas.LineTo(X, Y)
end;
frRetângulo: Canvas.Rectangle(Origem.X, Origem.Y, X, Y);
frElipse: Canvas.Ellipse(Origem.X, Origem.Y, X, Y);
frRetRedondo: Canvas.RoundRect(Origem.X, Origem.Y, X, Y, .
(Origem.X - X) div 2, (Origem.Y - Y) div 2);
end;
FlagDesenho := False;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: .
Integer);
begin
if FlagDesenho then
begin
Canvas.Pen.Mode := pmNotXor;
case Ferramenta of
frLinha :
begin
Canvas.MoveTo(Origem.X, Origem.Y);
Canvas.LineTo(MovePonto.X, MovePonto.Y);
Canvas.MoveTo(Origem.X, Origem.Y);
Canvas.LineTo(X, Y);
end;
frRetângulo :
begin
DELPHI 4.0 – Capítulo 5: Gráficos 64
Anotações:
Canvas.Rectangle(Origem.X, Origem.Y, MovePonto.X, .
MovePonto.Y);
Canvas.Rectangle(Origem.X, Origem.Y, X, Y);
end;
frElipse :
begin
Canvas.Ellipse(Origem.X, Origem.Y, X, Y);
Canvas.Ellipse(Origem.X, Origem.Y, X, Y);
end;
frRetRedondo :
begin
Canvas.RoundRect(Origem.X, Origem.Y, X, Y,
(Origem.X - X) div 2, (Origem.Y - Y) div 2);
Canvas.RoundRect(Origem.X, Origem.Y, X, Y,
(Origem.X - X) div 2, (Origem.Y - Y) div 2);
end;
end;
MovePonto := Point(X, Y);
end;
Canvas.Pen.Mode := pmCopy;
end;
Você deve ter notado que há muita repetição de código nestas duas procedures. Quando isto acon-tece, é melhor escrever uma sub-rotina separada que será acessada por partes diferentes do programa, evi-tando repetição desnecessária.
Como adicionar uma sub-rotina (método) a um formulário:
1. Adicione a declaração do método ao objeto formulário. Isto pode ser feito dentro das seções public ou private da unidade associada ao formulário:
2. Escreva a implementação do método na seção implementation. No nosso caso, o código fica da seguinte forma:
. type
. TForm1 = class(TForm)
.
.
.public
{ Public declarations }
procedure DesenhaForma(SupEsq, InfDir: TPoint; AModo: TPenMode);
end;
DELPHI 4.0 – Capítulo 5: Gráficos 65
Anotações:
Note que DesenhaForma recebe três parâmetros, correspondendo às coordenadas do canto superior esquerdo, às coordenadas do canto inferior direito e ao modo de desenho. A implementação não traz muitas novidades.
.implementation
{$R *.FRM}
.
procedure TForm1.DesenhaForma(SupEsq, InfDir: TPoint; AModo: TPenMode);
begin
with Canvas do
begin
Pen.Mode := AModo;
case Ferramenta of
frLinha:
begin
MoveTo(SupEsq.X, SupEsq.Y);
LineTo(InfDir.X, InfDir.Y);
end;
frRetângulo: Rectangle(SupEsq.X, SupEsq.Y, InfDir.X, .
InfDir.Y);
frElipse: Ellipse(SupEsq.X, SupEsq.Y, InfDir.X, .
InfDir.Y);
frRetRedondo: RoundRect(SupEsq.X, SupEsq.Y, InfDir.X, .
InfDir.Y,
(SupEsq.X - InfDir.X) div 2, (SupEsq.Y - InfDir.Y) div .
2);
end;
end;
end;
As procedures dos eventos OnMOuseUp e OnMOuseMove precisam ser modificadas:
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
. DesenhaForma(Origem, Point(X, Y), pmCopy);
. FlagDesenho := False;
. end;
procedure TForm1.FormMouseMove(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
DELPHI 4.0 – Capítulo 5: Gráficos 66
Anotações:
if FlagDesenho then
begin
DesenhaForma(Origem, MovePonto, pmNotXor);
MovePonto := Point(X, Y); { record the current point }
DesenhaForma(Origem, MovePonto, pmNotXor);
end;
end;
5.4. MUDANDO O ESTILO DE DESENHO E PINTURA
MUDANDO O ESTILO DA CANETA O estilo da caneta pode ser definido para uma das seguintes constantes:
* psSolid; * psDash; * psDot; * psDashDot; * psDashDotDot; * psClear.
Por exemplo, podemos escrever:
Canvas.Pen.Style := psDash
MUDANDO A COR DA CANETA
A cor da caneta pode ser alterada por meio da propriedade Color. Por exemplo,
Canvas.Pen.Color := RGB(100, 200, 57);
DELPHI 4.0 – Capítulo 5: Gráficos 67
Anotações:
MUDANDO A ESPESSURA DA CANETA
A espessura (width) da caneta é um número que determina a espessura, em pixels, da linha a ser desenhada. O valor default é um (1).
Canvas.Pen.Width := 5;
MUDANDO O ESTILO DO PINCEL
O estilo do pincel determina qual o padrão usado para preencher objetos fechados, podendo assumir
um dos seguintes valores:
bsSolid;
bsClear;
bsHorizontal;
bsVertical;
bsFDiagonal;
bsBDiagonal;
bsCross;
bsDiagCross.
MUDANDO A COR DO PINCEL
A cor do pincel determina qual a cor usada pelo Canvas para preencher figuras. Por exemplo,
Canvas.Brush.Color := rgb(300, 0, 0);
5.5. CARREGANDO E SALVANDO FIGURAS
Para carregar uma figura a partir de um arquivo em disco, use a procedure LoadBitMap. Para salvar
uma figura em disco, use a procedure SaveBitMap. Por exemplo, para carregar o bitmap ‘BANDEIRA.BMP’, armazenado em C:\DELPHI_CURSO, para dentro do controle Image1 ( uma caixa de imagem), fazemos:
procedure TForm1.LoadBitmapClick(Sender: TObject);
begin
. Image1.Picture.LoadFromFile(‘C:\DELPHI_CURSO\BANDEIRA.BMP’);
end;
DELPHI 4.0 – Capítulo 5: Gráficos 68
Anotações:
Note que a figura é carregada para a propriedade Picture de Image1.
5.6. COPIANDO BITMAPS
O Delphi oferece quatro diferentes maneiras de copiar imagens de um canvas para outro, depen-
dendo do efeito que se deseja obter. A tabela a seguir mostra estes quatro métodos.
Efeito desejado Método
Copiar uma figura inteira Draw Copiar e redimensionar uma figura StretchDraw Copiar parte de um canvas CopyRect Copiar um bitmap com opções de rasterização BrushCopy
DELPHI 4.0 – Capítulo 5: Gráficos 69
Anotações:
COFFEE-BREAK: O LADO CÔMICO DA INFORMÁTICA
Documento secreto: Como a Microsoft realmente produz software.
1. Os programadores produzem códigos que julgam estar livres de bugs;
2. O produto é testado: 20 bugs são encontrados;
3. Os programadores corrigem 10 dos 20 bugs e explicam que os outros 10 não são
realmente bugs;
4. O departamento de testes descobre que 5 das correções não funcionam e des-
cobrem 15 novos bugs.
5. Volta para 3.
6. Volta para 4.
7. Volta para 5.
8. Volta para 6.
9. Volta para 7.
10. Volta para 8.
11. Devido a enormes pressões de marketing e à uma data de lançamento baseada e,
projeções muito otimistas, o programa é lançado; os usuários encontram 137 no-
vos bugs;
12. Os programadores originais não são mais encontrados em lugar algum;
13. Uma nova equipe de programadores corrige quase todos os 137 bugs, mas intro-
duz outros 456;
14. Os programadores originais mandam postais das Ilhas Fiji;
15. Um novo diretor de desenvolvimento assume e contrata novos programadores
para refazer o produto a partir do início;
16. Os programadores produzem códigos que julgam estar livres de bugs;
17. Volta para 2 .