Você está na página 1de 56

No Captulo 6, j apresentamos o objeto Canvas , o processo de pintura do Windows e o evento On-

Pai nt . Neste captulo, comearemos a partir desse ponto e continuaremos a abordar os recursos gr-
ficos, seguindo vrias direes diferentes. O cdigo adicional discutido aqui est includo no CD do
Dominando o Delphi 6, nas subpastas da pasta bnus. Alguns exemplos citados, j vistos em outros ca-
ptulos do livro, esto nas pastas dos respectivos captulos.
Comearemos com o desenvolvimento de um programa complexo para demonstrar como o mo-
delo de pintura do Windows funciona. Em seguida, focalizaremos alguns componentes grficos, como
os botes grficos e as grades. Ainda nesta parte do captulo, tambm incluiremos alguma animao
nos controles.
Por fim, discutiremos o uso de bitmaps, abordando alguns recursos avanados para rpida repre-
sentao grfica, meta-arquivos, o componente TeeChart (incluindo seu uso na Web) e mais alguns
tpicos relacionados ao problema geral dos recursos grficos.
Desenhando em um Formulrio
No Captulo 6, vimos que possvel pintar diretamente na superfcie de um formulrio, em resposta
a um evento de mouse. Para ver esse comportamento, basta criar um novo formulrio com o seguinte
manipulador do evento OnMouseDow:
procedure TFor m1. For mMouseDown(Sender : TObj ect ;
But t on: TmouseBut t on; Shi f t : TShi f t St at e; X, Y: I nt eger );
begi n
Canvas. El l i pse (X- 10, Y- 10, X+10, Y+10);
end;
O programa parecefuncionar muito bem, mas no funciona. Todo clique de mouse produz um
novo crculo, mas, se voc minimizar o formulrio, todos eles desaparecero. At mesmo se voc cu-
brir apenas uma parte de seu formulrio com outra janela, as figuras por trs desse outro formulrio
desaparecero, e voc poder acabar com os crculos parcialmente pintados.
Conforme detalhamos no Captulo 6, esse desenho direto no suportado automaticamente pelo
Windows. A estratgia padro armazenar o pedido de pintura no evento OnMous eDown e depois re-
produzir a sada no evento OnPai nt . Esse evento, na verdade, chamado pelo sistema sempre que o
formulrio exige uma repintura. Entretanto, voc precisar forar essa ativao chamando os mtodos
Recursos Grficos
do Delphi
1
I nval i dat e ou Repai nt no manipulador do evento do mouse. Em outras palavras, o Windows sabe
quando o formulrio precisa ser repintado devido a uma operao de sistema (como a colocao de
outra janela na frente de seu formulrio), mas seu programa deve notificar o sistema do momento em
que a pintura exigida por causa da entrada de usurio ou outras operaes de programa.
As Ferramentas de Desenho
Todas as operaes de sada no Windows ocorrem atravs do uso de objetos da classe TCanvas. As
operaes de sada normalmente no especificam cores e elementos semelhantes, mas usam as ferra-
mentas de desenho atuais do canvas. Aqui est uma lista dessas ferramentas de desenho (ou objetosGDI
Graphics Device I nterface , uma das bibliotecas de sistema do Windows):
I A propriedade Br ush determina a cor das superfcies englobadas. O pincel usado para preen-
cher figuras fechadas, como crculos ou retngulos. As propriedades de um pincel so Col or ,
St yl e e, opcionalmente, Bi t map.
I A propriedade Pen determina a cor e o tamanho das linhas e das bordas das figuras. As pro-
priedades de uma pena so Col or , Wi dt h e St yl e, o que inclui vrias linhas pontilhadas e tra-
cejadas (disponveis apenas se a propriedade Wi dt h for igual a 1 pixel). Outra subpropriedade
relevante de Pen Mode, que indica como a cor da pena modifica a cor da superfcie de dese-
nho. O padro simplesmente usar a cor da pena (com o estilo pmCopy), mas tambm pos-
svel mesclar as duas cores de muitas maneiras diferentes e reverter a cor atual da superfcie
de desenho.
I A propriedade Font determina a fonte usada para escrever texto no formulrio, usando o m-
todo Text Out do canvas. Uma fonte tem propriedades Name, Si ze, St yl e, Col or etc.
DICA Os p r o gr am ad or es exp er i ent es no Wi nd o w s devem n ot ar q ue u m canvas d o Del p hi t ecni cam en t e
r ep r esen t a u m con t ext o d e d i sp o si t i vo d o Wi nd ow s. Os m t o d os d a cl asse TCanvas so sem el h ant es
s f u n es de GDI d a API d o Wi nd ow s. Vo c po d e ch am ar m t od os GDI ext r as usand o a p r op r i e-
d ad e Handl e d o can vas, q ue um hand l e d e t i p o HDC.
Cores
Pincis, penas e fontes (assim como formulrios e a maioria dos outros componentes) tm uma pro-
priedade Col or . Entretanto, para mudar a cor de um elemento corretamente, usando cores no-padro
(como as constantes de cor no Delphi), voc deve saber como o Windows trata cores. Teoricamente,
o Windows usa cores RGB de 24 bits. I sso significa que voc pode usar 256 valores diferentes para cada
uma das trs cores bsicas (vermelho, verde e azul), obtendo 16 milhes de tonalidades diferentes.
No entanto, voc ou seus usurios podem ter uma placa de vdeo que no consegue apresentar
tal variedade de cores, embora isso seja cada vez mais raro. Nesse caso, o Windows usa uma tcnica
chamada dithering (pontilhamento), que consiste basicamente no uso de um nmero de pixels das cores
disponveis para simular a solicitada; ou ela aproxima a cor, usando a combinao mais prxima dis-
ponvel. Para a cor de um pincel (e para a cor de fundo de um formulrio, que na verdade baseada
em um pincel), o Windows usa a tcnica de pontilhamento; para a cor de uma pena ou fonte, ele usa
a cor mais prxima disponvel.
Em termos de penas, voc pode ler (mas no alterar) a posio atual com a propriedade PenPos
do canvas. A posio da pena determina o ponto inicial da prxima linha que o programa desenhar,
usando o mtodo Li neTo. Para alter-la, voc pode usar o mtodo MoveTo do canvas. Outras proprie-
dades do canvas afetam linhas e cores tambm. Exemplos interessantes so CopyMode e Scal eMode. Ou-
2 Dominando o Delphi 6 A Bibla
tras propriedades possveis de manipular diretamente para alterar a sada o array Pi xel s , que voc
pode usar para acessar (ler) ou alterar (gravar) a cor de qualquer ponto especfico na superfcie do
formulrio. Conforme veremos no exemplo BmpDraw, as operaes por pixel so muito lentas na
GDI , comparadas com o acesso linha disponvel atravs da propriedade ScanLi nes.
Finalmente, lembre-se de que os valores de TCol or do Delphi nem sempre correspondem aos va-
lores RGB puros da representao nativa do Windows (COLORREF), devido s constantes de cor do
Delphi. Voc sempre pode converter uma cor do Delphi para o valor RGB, usando a funo Col or -
ToRGB. Voc pode encontrar os detalhes da representao do Delphi na entrada TColor typeda ajuda.
Desenhando Figuras Geomtricas
Agora, queremos estender o exemplo MouseOne construdo no Captulo 9 e transform-lo no aplica-
tivo Shapes. Nesse novo programa, queremos usar a estratgia de armazenar edesenhar com vrias fi-
guras, manipular atributos de cor e pena e fornecer uma base para extenses futuras.
Como voc precisa se lembrar da posio e dos atributos de cada figura, pode criar um objeto
para cada figura que precisa armazenar e pode manter os objetos em uma lista (para sermos mais pre-
cisos, a lista armazenar referncias aos objetos, que so alocados em reas de memria separadas).
Definimos uma classe-base para as figuras e duas classes herdadas que contm o cdigo de pintura para
os dois tipos de figuras que queremos manipular retngulos e elipses.
A classe-base tem algumas propriedades, que simplesmente lem os campos e gravam os valores
correspondentes com mtodos simples. Note que as coordenadas podem ser lidas usando-se a proprie-
dade Rect , mas devem ser modificadas usando-se as quatro propriedades posicionais. O motivo que,
se voc incluir uma parte wr i t e na propriedade Rect , poder acessar o retngulo como um todo, mas
no suas subpropriedades especficas. Aqui esto as declaraes das trs classes:
type
TBaseShape = cl ass
pri vate
FBr ushCol or : TCol or ;
FPenCol or : TCol or ;
FPenSi ze: I nt eger ;
procedure Set Br ushCol or (const Val ue: TCol or );
procedure Set PenCol or (const Val ue: TCol or );
procedure Set PenSi ze(const Val ue: I nt eger );
procedure Set Bot t om(const Val ue: I nt eger );
procedure Set Lef t (const Val ue: I nt eger );
procedure Set Ri ght (const Val ue: I nt eger );
procedure Set Top(const Val ue: I nt eger );
protected
FRect : TRect ;
publ i c
procedure Pai nt (Canvas: TCanvas); vi rtual ;
publ i shed
property PenSi ze: I nt eger read FPenSi ze wri te Set PenSi ze;
property PenCol or : TCol or read FPenCol or wri te Set PenCol or ;
property Br ushCol or : TCol or read FBr ushCol or wri te Set Br ushCol or ;
property Lef t : I nt eger wri te Set Lef t ;
property Ri ght : I nt eger wri te Set Ri ght ;
property Top: I nt eger wri te Set Top;
property Bot t om: I nt eger wri te Set Bot t om;
property Rect : TRect read FRect ;
end;
3 Recursos grficos do Delphi
type
TEl l Shape = cl ass (TBaseShape)
procedure Pai nt (Canvas: TCanvas); overri de;
end;
TRect Shape = cl ass (TBaseShape)
procedure Pai nt (Canvas: TCanvas); overri de;
end;
A maior parte do cdigo dos mtodos muito simples. O nico cdigo relevante est nos trs
procedimentos Pai nt :
procedure TBaseShape. Pai nt (Canvas: TCanvas);
begi n
/ / conf i gura os atri butos
Canvas. Pen. Col or : = f PenCol or ;
Canvas. Pen. Wi dth : = f PenSi ze;
Canvas. Br ush. Col or : = f Br ushCol or ;
end;
procedure TEl l Shape. Pai nt (Canvas: TCanvas);
begi n
i nheri ted Pai nt (Canvas);
Canvas. El l i pse (f Rect . Lef t , f Rect . Top,
f Rect . Ri ght, f Rect . Bot tom)
end;
procedure TRect Shape. Pai nt (Canvas: TCanvas);
begi n
i nheri ted Pai nt (Canvas);
Canvas. Rectangl e (f Rect. Lef t , f Rect. Top,
f Rect . Ri ght, f Rect . Bot tom)
end;
Todo esse cdigo armazenado na unidade ShapesH (Shapes Hierarchy). Para armazenar uma
lista de figuras, o formulrio tem um membro de dados de objeto TLi st , chamado ShapesLi s t , que
inicializado no manipulador do evento OnCr eat e e destrudo no final; o destrutor tambm libera to-
dos os objetos da lista (em ordem inversa, para evitar a atualizao dos dados da lista internos com
muita freqncia):
procedure TShapesFor m. For mCr eat e(Sender : TObj ect );
begi n
ShapesLi st : = TLi st. Cr eat e;
end;
procedure TShapesFor m. For mDest r oy(Sender : TObj ect );
var
I : I nt eger ;
begi n
/ / excl ui cada obj eto
for I : = ShapesLi st. Count 1 downto 0 do
TBaseShape (ShapesLi st [ I ] ). Fr ee;
ShapesLi st . Fr ee;
end;
4 Dominando o Delphi 6 A Bibla
O programa inclui um novo objeto na lista sempre que o usurio inicia a operao de desenho.
Como o objeto no est completamente definido, o formulrio mantm uma referncia a ele no campo
Cur r Shape. Note que o tipo de objeto criado depende do status das teclas do mouse:
procedure TShapesFor m. For mMouseDown(Sender : TObj ect ;
Butt on: TMouseBut t on; Shi f t: TShi f tSt ate; X, Y: I nteger );
begi n
i f But ton = mbLef t then
begi n
/ / ati va o arrasto
f Dr aggi ng : = Tr ue;
Set Capt ur e (Handl e);

/ / cri a o obj eto correto
i f ssShi f t i n Shi f t then
Cur r Shape : = TEl l Shape. Cr eat e
el se
Cur r Shape : = TRect Shape. Cr eat e;

/ / conf i gura o esti l o e as cores
Cur r Shape. PenSi ze : = Canvas. Pen. Wi dth;
Cur r Shape. PenCol or : = Canvas. Pen. Col or ;
Cur r Shape. Br ushCol or : = Canvas. Br ush. Col or ;
/ / conf i gura a posi o i ni ci al
Cur r Shape. Lef t : = X;
Cur r Shape. Top : = Y;
Cur r Shape. Ri ght : = X;
Cur r Shape. Bot t om: = Y;
Canvas. Dr awFocusRect (Cur r Shape. Rect);

/ / i ncl ui na l i sta
ShapesLi st . Add (Cur r Shape);
end;
end;
Durante a operao de arrasto, desenhamos a linha correspondente figura, como fizemos no
exemplo MouseOne:
procedure TShapesFor m. For mMouseMove(Sender : TObj ect ; Shi f t :
TShi f t St at e;
X, Y: I nt eger );
var
ARect : TRect ;
begi n
/ / copi a as coordenadas do mouse no t tul o
Capt i on : = For mat ( Shapes ( x=%d, y=%d) , [ X, Y] );
/ / cdi go de arrasto
i f f Dr aggi ng then
begi n
/ / remove e redesenha o retngul o de arrasto
ARect : = Nor mal i zeRect (Cur r Shape. Rect );
Canvas. Dr awFocusRect (ARect );
Cur r Shape. Ri ght : = X;
Cur r Shape. Bot t om: = Y;
5 Recursos grficos do Delphi
ARect : = Nor mal i zeRect (Cur r Shape. Rect );
Canvas. Dr awFocusRect (ARect );
end;
end;
Desta vez, entretanto, tambm inclumos uma correo no programa. No exemplo MouseOne,
se voc movesse o mouse em direo ao canto superior esquerdo do formulrio, enquanto arrastava,
a chamada a Dr awFocusRect no produzia nenhum efeito. I sso porque o retngulo passado como pa-
rmetro para Dr awFocus Rect deve ter um valor de Top menor do que o valor de Bot t om, e o mesmo
verdade para os valores de Lef t e Ri ght . Em outras palavras, um retngulo que se estende para o
lado negativo no funciona de forma correta. Entretanto, no final ele pintado corretamente, pois a
funo de desenho Rect angl e no tem esse problema.
Para corrigir esse problema, escrevemos uma funo simples que inverte as coordenadas de um
retngulo para faz-las refletir os pedidos da chamada a Dr awFocusRect :
functi on Nor mal i zeRect (ARect : TRect ): TRect ;
var
t mp: I nt eger ;
begi n
i f ARect . Bot t om< ARect. Top then
begi n
tmp : = ARect . Bot tom;
ARect. Bott om: = ARect. Top;
ARect. Top : = t mp;
end;
i f ARect . Ri ght < ARect. Lef t then
begi n
t mp : = ARect. Ri ght ;
ARect . Ri ght : = ARect. Lef t ;
ARect . Lef t : = t mp;
end;
Resul t : = ARect ;
end;
Finalmente, o manipulador do evento OnMous eUp configura o tamanho definitivo da imagem e
atualiza a pintura do formulrio. Em vez de chamar o mtodo I nval i dat e, o que faria todas as ima-
gens serem repintadas com muita cintilao, o programa usa a funo da API I nval i dat eRect :
procedure I nval i dat eRect (Wnd: HWnd;
Rect : PRect; Er ase: Bool );
Os trs parmetros representam o handle da janela (isto , a propriedade Handl e do formulrio),
o retngulo que voc deseja repintar e um flag indicando se voc deseja ou no apagar a rea antes
de repintar. Essa funo exige, mais uma vez, um retngulo normalizado. (Voc pode tentar substituir
essa chamada por uma chamada a I nval i dat e, para ver a diferena, que mais evidente quando voc
cria muitos formulrios.) Aqui est o cdigo completo do manipulador do evento OnMouseUp:
procedure TShapesFor m. For mMouseUp(Sender : TObj ect ; But t on: TMouseBut t on;
Shi f t : TShi f t Stat e; X, Y: I nt eger );
var
ARect : TRect ;
begi n
i f f Dr aggi ng then
begi n
/ / f i nal i za o arrasto
6 Dominando o Delphi 6 A Bibla
Rel easeCapt ur e;
f Dr aggi ng : = Fal se;

/ / conf i gura o tamanho f i nal
ARect : = Nor mal i zeRect (Cur r Shape. Rect );
Canvas. Dr awFocusRect (ARect );
Cur r Shape. Ri ght : = X;
Cur r Shape. Bott om: = Y;

/ / cdi go de i nval i dao oti mi zado
ARect : = Nor mal i zeRect (Cur r Shape. Rect );
I nval i dateRect (Handl e, @ARect , Fal se);
end;
end;
NOTA Qu and o voc sel eci o na um a p en a d e d esenh o g r an d e (ver em o s o c di go p ar a i sso em b r eve), a
bo r d a do qu ad r o pi nt ada p ar ci al m en t e den t r o e par ci al m ent e f or a d el e, par a acom od ar a p ena
gr and e. Par a p ossi b i l i t ar i sso, d evem os i n val i dar u m r et ng ul o de m ol du r a qu e d i l at ad o p el a m e-
t ad e d o t am an ho da p ena at ual . Voc p od e f azer i sso ch am an d o a f u no I nf l at eRect . Co m o
al t er n at i va, n o m t o do For mCr eat e, con f i gu r am o s a pr o p r i edade St yl e d e Pen d o f o r m ul r i o Can-
vas co m o ps I nsi deFr ame. Isso f az com q u e a f un o de d esen ho pi nt e a p en a co m p l et am en t e
den t r o d o qu ad r o d a f i g u r a.
No mtodo correspondente ao evento OnPai nt , todas as figuras atualmente armazenadas na lista
so pintadas, conforme voc pode ver na Figura 1. Como o cdigo de pintura afeta as propriedades
de Canvas , precisamos armazenar os valores atuais e reinicializ-los no final. O motivo que, confor-
me mostraremos posteriormente neste captulo, as propriedades do canvas do formul rio so usadas
para controlar os atributos selecionados pelo usurio, que poderia t-los mudado desde que a ltima
figura foi criada. Aqui est o cdigo:
procedure TShapesFor m. For mPai nt (Sender : TObj ect );
var
I , Ol dPenW: I nteger ;
AShape: TBaseShape;
Ol dPenCol , Ol dBr ushCol : TCol or ;
begi n
// armazena os atri butos de Canvas atual
Ol dPenCol : = Canvas. Pen. Col or ;
Ol dPenW: = Canvas. Pen. Wi dt h;
Ol dBr ushCol : = Canvas. Br ush. Col or ;

// repi nta cada f i gura da l i sta
for I : = 0 to ShapeLi st. Count - 1 do
begi n
AShape : = ShapeLi st . I t ems [ I ] ;
AShape. Pai nt (Canvas);
end;

// rei ni ci al i za os atri butos de Canvas atual
Canvas. Pen. Col or : = Ol dPenCol ;
Canvas. Pen. Wi dth : = Ol dPenW;
Canvas. Br ush. Col or : = Ol dBr ushCol ;
end;
7 Recursos grficos do Delphi
Os outros mtodos do formulrio so simples. Trs dos comandos de menu nos permitem usar
as cores do fundo, as bordas da figura (a pena) e a rea interna (o pincel). Esses mtodos usam o
componente ColorDialog e armazenam o resultado nas propriedades do canvas do formulrio. Aqui
est um exemplo:
procedure TShapesFor m. PenCol or 1Cl i ck(Sender : TObj ect );
begi n
/ / sel eci ona uma nova cor para a pena
Col or Di al og1. Col or : = Canvas. Pen. Col or ;
i f Col or Di al og1. Execut e then
Canvas. Pen. Col or : = Col or Di al og1. Col or ;
end;
As novas cores afetaro as figuras criadas no futuro, mas no as j existentes. A mesma estratgia
usada para a largura das linhas (a pena), embora desta vez o programa tambm verifique se o valor
se tornou pequeno demais, desativando o item de menu, se isso acontecer:
procedure TShapesFor m. Decr easePenSi ze1Cl i ck(Sender : TObj ect );
begi n
Canvas. Pen. Wi dt h : = Canvas. Pen. Wi dt h - 2;
i f Canvas. Pen. Wi dt h < 3 then
Decr easePenSi ze1. Enabl ed : = Fal se;
end;
Para alterar as cores da borda (a pena) ou da superfcie (o pincel) da figura, usamos a caixa de
dilogo Cor padro. Aqui est um dos dois mtodos:
procedure TShapesFor m. PenCol or 1Cl i ck(Sender : TObj ect );
begi n
Col or Di al og1. Col or : = Canvas. Pen. Col or ;
i f Col or Di al og1. Execut e then
Canvas. Pen. Col or : = Col or Di al og1. Col or ;
end;
Na Figura 2, voc pode ver outro exemplo da sada do programa Shapes, desta vez usando outras
cores para as figuras e seus fundos. O programa pede para o usurio confirmar algumas operaes,
como a sada do programa ou a remoo de todas as figuras da lista (com o comando File | New):
procedure TShapesFor m. New1Cl i ck(Sender : TObj ect );
begi n
FIGURA 1
O exemplo Shapes pode ser usado
para desenhar vrias figuras, que ele
armazena em uma lista.
8 Dominando o Delphi 6 A Bibla
i f (ShapesLi st . Count > 0) and (MessageDl g (
Are you sure you want to del ete al l the shapes? ,
mt Conf i r mat i on, [ mbYes, mbNo] , 0) = i dYes) then
begi n
/ / excl ui cada obj eto
for I : = ShapesLi st . Count - 1 downto 0 do
TBaseShape (ShapesLi st [ I ] ). Fr ee;
ShapesLi st . Cl ear ;
Ref r esh;
end;
end;
Imprimindo Figuras
Alm de pintar as figuras em um canvas de formulrio, podemos pint-las em um canvas de impres-
sora, efetivamente imprimindo-as! Pelo fato de ser possvel executar os mesmos mtodos em um can-
vas de impressora como em qualquer outro canvas, voc poderia ficar tentado a incluir no programa
um novo mtodo para imprimir as figuras. Com certeza isso fcil, mas uma opo ainda melhor
escrever um nico mtodo de sada para usar para a tela e para a impressora.
Como exemplo dessa estratgia, construmos uma nova verso do programa, chamada ShapesPr.
O ponto interessante que movemos o cdigo do exemplo For mPai nt para outro mtodo que defi-
nimos, chamado CommonPai nt . Esse novo mtodo possui dois parmetros, o canvas e um fator de es-
cala (cujo padro 1):
procedure CommonPai nt (Canvas: TCanvas; Scal e: I nt eger = 1);
O mtodo CommonPai nt produz como sada a lista de figuras do canvas, passadas como parme-
tros, usando o fator de escala correto:
procedure TShapesFor m. CommonPai nt (
Canvas: TCanvas; Scal e: I nteger );
var
I , Ol dPenW: I nteger ;
AShape: TBaseShape;
Ol dPenCol , Ol dBr ushCol : TCol or ;
begi n
/ / armazena os atri butos do Canvas atual
Ol dPenCol : = Canvas. Pen. Col or ;
FIGURA 2
Alterar as cores e o tamanho
da linha das figuras permite
que voc use o exemplo Shapes
para produzir qualquer tipo de
resultado.
9 Recursos grficos do Delphi
Ol dPenW: = Canvas. Pen. Wi dt h;
Ol dBr ushCol : = Canvas. Br ush. Col or ;

/ / repi nta cada f i gura da l i sta
for I : = 0 t o ShapesLi st . Count - 1 do
begi n
AShape : = ShapesLi st. I t ems [ I ] ;
AShape. Pai nt (Canvas, Scal e);
end;
/ / rei ni ci al i za os atri butos do Canvas atual
Canvas. Pen. Col or : = Ol dPenCol ;
Canvas. Pen. Wi dt h : = Ol dPenW;
Canvas. Br ush. Col or : = Ol dBr ushCol ;
end;
Uma vez escrito esse cdigo, os mtodos For mPai nt e Pr i nt 1Cl i ck so simples de implementar.
Para pintar a imagem na tela, voc pode chamar CommonPai nt sem um fator de escala (para que o valor
padro de 1 seja usado):
procedure TShapesFor m. For mPai nt (Sender : TObj ect );
begi n
CommonPai nt (Canvas);
end;
Para pintar o contedo do formulrio na impressora, em vez do formulrio, voc pode reprodu-
zir a sada no canvas da impressora, usando um fator de escala correto. Em vez de escolher uma escala,
decidimos calcul-la automaticamente. A idia imprimir as figuras no formulrio com o maior ta-
manho possvel, dimensionando a rea cliente do formulrio de modo que ela ocupe a pgina inteira.
O cdigo provavelmente mais simples do que a descrio:
procedure TShapesFor m. Pr i nt 1Cl i ck(Sender : TObj ect );
var
Scal e, Scal e1: I nt eger ;
begi n
Scal e : = Pr i nt er . PageWi dt h di v Cl i ent Wi dt h;
Scal e1 : = Pr i nter . PageHei ght di v Cl i entHei ght ;
i f Scal e1 < Scal e then
Scal e : = Scal e1;
Pr i nt er . Begi nDoc;
try
CommonPai nt (Pr i nt er . Canvas, Scal e);
Pr i nter . EndDoc;
except
Pr i nter . Abor t;
rai se;
end;
end;
claro que voc precisa se lembrar de chamar os comandos especficos para comear a imprimir (Be-
gi nDoc) e efetivar a sada (EndDoc), antes e depois de chamar o mtodo CommonPai nt . Se uma exceo for
lanada, o programa chama Abor t para terminar o processo de impresso de qualquer maneira.
10 Dominando o Delphi 6 A Bibla
Componentes Grficos do Delphi
O exemplo Shapes usa quase nenhum componente, fora uma caixa de dilogo de seleo de cor pa-
dro. Como alternativa, poderamos ter usado alguns componentes do Delphi que suportam especifi-
camente figuras:
I Voc usa o componente PaintBox quando precisa pintar em uma certa rea de um formulrio
que pode se mover no formulrio. Por exemplo, PaintBox til para pintar em uma caixa de
dilogo sem o risco de misturar a rea da sada com a rea dos controles. O componente
PaintBox poderia se encaixar dentro de outros controles de um formulrio, como uma barra
de ferramentas ou uma barra de status, e evitar qualquer confuso ou sobreposio da sada.
No exemplo Shapes, usar esse componente no fazia sentido, pois sempre trabalhamos na su-
perfcie inteira do formulrio.
I Voc usa o componente Shape para pintar figuras na tela, exatamente como fizemos at agora. Voc
poderia usar o componente Shape em lugar da sada manual, mas queremos mostrar como se reali-
zam algumas operaes de sada diretas. Esta estratgia no era muito mais complexa do que a que
o Delphi sugere. Usar o componente Shape teria sido til para estender o exemplo, permitindo ao
usurio arrastar figuras na tela, remov-las e trabalhar com elas de vrias outras maneiras.
I Voc pode usar o componente I mage para apresentar um bitmap existente, possivelmente car-
regando-o de um arquivo, ou mesmo para pintar sobre um bitmap, conforme demonstrare-
mos nos prximos dois exemplos e que discutiremos na prxima seo.
I Se ele estiver includo em sua verso de Delphi, voc pode usar o controle TeeChart para gerar
grficos comerciais, conforme veremos no final deste captulo.
I Voc pode usar o suporte grfico fornecido pelos controles botes de bitmap e speed buton,
entre outros. Posteriormente neste captulo, veremos como estender os recursos grficos des-
ses controles.
I Voc pode usar o componente Animate para tornar as figuras animadas. Alm de usar esse
componente, voc pode criar animaes manualmente, apresentando bitmaps em seqncia ou
rolando-os, conforme veremos em outros exemplos.
Como voc pode ver, temos um longo caminho pela frente para abordar o suporte grfico do
Delphi de todos os seus ngulos.
Desenhando em um Bitmap
J mencionamos que, usando um componente I mage, voc pode desenhar imagens diretamente em
um bitmap. Em vez de desenhar na superfcie de uma janela, voc desenha em um bitmap na memria
e depois copia o bitmap na superfcie da janela. A vantagem que, em vez de ter de repintar a imagem
cada vez que um evento OnPai nt ocorrer, o componente copia o bitmap para o vdeo.
Tecnicamente, um objeto TBi t map tem seu prprio canvas. Desenhando nesse canvas, voc pode
mudar o contedo do bitmap. Como alternativa, voc pode trabalhar no canvas de um bitmap I mage
conectado ao bitmap que deseja alterar. Voc poderia considerar a escolha dessa estratgia em lugar
da estratgia de pintura tpica se qualquer uma das seguintes condies fosse verdadeira:
I O programa precisa oferecer suporte a desenho mo livre ou a imagens grficas muito com-
plexas (como imagens fractais).
I O programa deve ser muito rpido no desenho de vrias imagens.
I O consumo de memria RAM no problema.
I Voc um programador preguioso.
11 Recursos grficos do Delphi
O ltimo ponto interessante, pois a pintura geralmente exige mais cdigo do que o desenho,
embora ela permita mais flexibilidade. Em um programa grfico, por exemplo, se voc usar pintura,
ter de armazenar a localizao e as cores de cada figura. Por outro lado, voc pode mudar facilmente
a cor de uma figura existente ou mov-la. Essas operaes so muito difceis com a estratgia de pin-
tura, e podem fazer com que a rea por trs de uma imagem seja perdida. Se voc estiver trabalhando
em um aplicativo grfico complexo, provavelmente dever escolher uma combinao das duas estra-
tgias. Para os programadores grficos casuais, a escolha entre as duas estratgias envolve uma deciso
tpica entre velocidade e memria: a pintura exige menos memria; o armazenamento do bitmap
mais rpido.
Desenhando Figuras
Agora, vamos ver um exemplo com o componente I mage que pintar em um bitmap. A idia sim-
ples. Basicamente, escrevemos uma verso simplificada do exemplo Shape, colocando um componente
I mage em seu formulrio e redirecionando toda as operaes de sada para o canvas desse componente.
Neste exemplo, ShapeBmp, tambm inclumos alguns itens de menu novos, para salvar a imagem
em um arquivo e para carregar um bitmap existente. Para fazer isso, inclumos no formulrio dois
componentes de dilogo padro, OpenDialog e SaveDialog. Uma das propriedades que tivemos de
mudar foi a cor de fundo do formulrio. Na verdade, quando voc executa a primeira operao grfica
na imagem, ela cria um bitmap que possui um fundo branco como padro. Se o formulrio tem um
fundo cinza, sempre que a janela repintada, alguma cintilao ocorre. Por esse motivo, escolhemos
um fundo branco tambm para o formulrio.
O cdigo deste exemplo ainda muito simples, considerando o nmero de operaes e coman-
dos de menu. A parte relativa ao desenho linear e muito parecida com o exemplo MouseOne, exceto
que os eventos de mouse agora esto relacionados imagem; em vez do formulrio, usamos a funo
Nor mal i zeRect durante o arrasto, e o programa usa o canvas da imagem. Aqui est o manipulador
do evento OnMouseMove, que reintroduz o desenho de pontos ao mover o mouse com a tecla Shift
pressionada:
procedure TShapesFor m. I mage1MouseMove(Sender : TObj ect ;
Shi f t : TShi f t Stat e; X, Y: I nt eger );
var
ARect : TRect ;
begi n
/ / apresenta a posi o do mouse no t tul o
Capt i on : = For mat ( ShapeBmp ( x=%d, y=%d) , [ X, Y] );
i f f Dr aggi ng then
begi n
/ / remove e redesenha o retngul o de arrasto
ARect : = Nor mal i zeRect (f Rect);
Canvas. Dr awFocusRect (ARect );
f Rect. Ri ght : = X;
f Rect. Bott om: = Y;
ARect : = Nor mal i zeRect (f Rect);
Canvas. Dr awFocusRect (ARect );
end
el se
i f ssShi f t i n Shi f t then
/ / marca o ponto emvermel ho
I mage1. Canvas. Pi xel s [ X, Y] : = cl Red;
end;
12 Dominando o Delphi 6 A Bibla
Note que o retngulo de foco temporrio pintado diretamente no formulrio, sobre a imagem
(e, assim, no armazenado no bitmap). A diferena que no final da operao de desenho, o pro-
grama pinta o retngulo na imagem, armazenando-o no bitmap. Desta vez o programa no chama I n-
val i dat e e no tem manipulador do evento OnPaint:
procedure TShapesFor m. I mage1MouseUp(Sender : TObj ect ;
But t on: TMouseBut ton; Shi f t : TShi f t St at e; X, Y: I nt eger );
begi n
i f f Dr aggi ng then
begi n
Rel easeCapt ur e;
f Dr aggi ng : = Fal se;
I mage1. Canvas. Rect angl e (f Rect . Lef t , f Rect . Top,
f Rect . Ri ght, f Rect . Bot tom);
end;
end;
Para evitar um suporte a arquivos demasiadamente complexo, decidimos implementar os coman-
dos File | Load e File | Save As e no manipular o comando Save, que em geral mais complexo.
Simplesmente inclumos um campo f Changed no formulrio, para saber quando uma imagem mudou,
e inclumos cdigo que verifica esse valor vrias vezes (antes de pedir confirmao para o usurio).
O manipulador do evento OnCl i ck do item de menu File | New chama o mtodo Fi l l Ar ea para
pintar um grande retngulo branco sobre o bitmap inteiro. Neste cdigo, voc tambm pode ver
como o campo Changed usado:
procedure TShapesFor m. New1Cl i ck(Sender : TObj ect );
var
Ar ea: TRect;
Ol dCol or : TCol or ;
begi n
i f not f Changed or (MessageDl g (
Are you sure you want to del ete the current i mage? ,
mtConf i r mat i on, [ mbYes, mbNo] , 0) = i dYes) then
begi n
{repi nta a superf ci e, cobri ndo a rea i ntei ra,
e rei ni ci al i zando o pi ncel anti go}
Ar ea : = Rect (0, 0, I mage1. Pi ct ur e. Wi dt h,
I mage1. Pi ctur e. Hei ght );
Ol dCol or : = I mage1. Canvas. Br ush. Col or ;
I mage1. Canvas. Br ush. Col or : = cl Whi t e;
I mage1. Canvas. Fi l l Rect (Ar ea);
I mage1. Canvas. Br ush. Col or : = Ol dCol or ;
f Changed : = Fal se;
end;
end;
claro que o cdigo tem de salvar a cor original e restaur-la posteriormente. Um realinhamen-
to das cores tambm exigido pelo mtodo de resposta do comando File | Load. Quando voc car-
rega um novo bitmap, na verdade, o componente I mage cria um novo canvas com os atributos
padro. Por esse motivo, o programa salva as cores e o tamanho da pena e os copia posteriormente
no novo canvas:
procedure TShapesFor m. Load1Cl i ck(Sender : TObj ect );
var
PenCol , Br ushCol : TCol or ;
13 Recursos grficos do Delphi
PenSi ze: I nt eger ;
begi n
i f not f Changed or (MessageDl g (
Are you sure you want to del ete the current i mage? ,
mt Conf i r mat i on, [ mbYes, mbNo] , 0) = i dYes) then
i f OpenDi al og1. Execut e then
begi n
PenCol : = I mage1. Canvas. Pen. Col or ;
Br ushCol : = I mage1. Canvas. Br ush. Col or ;
PenSi ze : = I mage1. Canvas. Pen. Wi dt h;
I mage1. Pi ct ur e. LoadFr omFi l e (OpenDi al og1. Fi l ename);
I mage1. Canvas. Pen. Col or : = PenCol ;
I mage1. Canvas. Br ush. Col or : = Br ushCol ;
I mage1. Canvas. Pen. Wi dt h : = PenSi ze;
f Changed : = Fal se;
end;
end;
Salvar a imagem atual muito mais simples:
procedure TShapesFor m. Saveas1Cl i ck(Sender : TObj ect );
begi n
i f SaveDi al og1. Execut e then
begi n
I mage1. Pi ct ur e. SaveToFi l e (
SaveDi al og1. Fi l ename);
f Changed : = Fal se;
end;
end;
Finalmente, aqui est o cdigo do evento OnCl os eQuer y do formulrio, que usa o campo Chan-
ged:
procedure TShapesFor m. For mCl oseQuer y(Sender : TObj ect ;
var CanCl ose: Bool ean);
begi n
i f not f Changed or (MessageDl g (
Are you sure you want to del ete the current i mage? ,
mtConf i r mat i on, [ mbYes, mbNo] , 0) = i dYes) then
CanCl ose : = Tr ue
el se
CanCl ose : = Fal se;
end;
ShapeBmp um programa interessante (veja a Figura 3), com suporte a arquivos limitado, mas
funcional. O problema em si que o componente I mage cria um bitmap de seu prprio tamanho.
Quando voc aumenta o tamanho da janela, o componente I mage redimensionado, mas no o bi-
tmap presente na memria. Portanto, voc no pode desenhar nas reas direita e inferior da janela.
Existem muitas solues possveis: usar a propriedade Cons t r ai nt s para configurar o tamanho mxi-
mo do formulrio, usar uma borda fixa, marcar visualmente a rea dedesenho na tela etc. Entretanto,
decidimos deixar o programa como est, porque ele realiza suficientemente bem o trabalho de de-
monstrar como se desenha em um bitmap.
14 Dominando o Delphi 6 A Bibla
Um Visualizador de Imagens
O programa ShadeBmp pode ser usado como um visualizador de imagens, pois voc pode carregar
qualquer bitmap nele. Em geral, no controle I mage, voc pode carregar qualquer tipo de arquivo gr-
fico que tenha sido registrado com a classe Tpi ct ur e da VCL. Os formatos de arquivo padro so
arquivos de bitmap (BMP), arquivos de cone (I CO) ou met a- ar qui vos do Windows (WMF). Os ar-
quivos de bitmap e cone so formatos bem conhecidos. Os meta-arquivos do Windows, entretanto,
no so to comuns. Eles so um conjunto de comandos grficos, semelhantes a uma lista de chamadas
de funo de GDI que precisam ser executadas para reconstruir uma imagem. Os meta-arquivos nor-
malmente so referidos como imagens grficas vetoriais e so parecidos com os formatos de arquivo
grfico usados para bibliotecas de clip-art. O Delphi tambm vem com suporte de JPG para TI mage,
e outros fornecedores possuem GI F e outros formatos de arquivo tratados.
NOTA Par a p r od uzi r um met a- ar qui vo d o Wi nd o w s, u m p r o gr am a deve cham ar f u n es d a GDI, r ed i -
r eci on an do su a sada par a o ar qu i vo . No Del p hi , voc p od e usar um m t od o TMet af i l eCanvas e
os m t od o s TCanvas d e al t o n vel . Post er i or m en t e, esse met a- ar qui vo p od e ser r ep r o du zi do ou
execut ad o p ar a ch am ar as f u n es co r r esp on den t es, p r o du zi nd o assi m um a f i g ur a. Os met a- ar -
qui vos t m d u as van t ag ens p r i n ci p ai s: a qu an t i d ad e d e ar m azenam ent o l i m i t ad a q ue el es exi g em
em com par ao a o ut r o s f or m at o s g r f i co s e a i n dep end nci a d e d i spo si t i vo d e sua sad a. Vam os
ab or d ar o sup o r t e a met a- ar qui vo d o Del p h i p o st er i o r m ent e nest e cap t u l o .
Para construir um programa visualizador de imagens completo, I mageV, em torno do compo-
nente I mage, precisamos apenas criar um formulrio com uma imagem que preencha a rea cliente
inteira, um menu simples e um componente OpenDialog:
obj ect Vi ewer For m: TVi ewer For m
Capt i on = I mage Vi ewer
Menu = Mai nMenu1
obj ect I mage1: TI mage
Al i gn = al Cl i ent
end
obj ect Mai nMenu1: TMai nMenu
obj ect Fi l e1: TMenuI t em. . .
obj ect Open1: TMenuI tem. . .
FIGURA 3
O exemplo ShapeBMP tem
suporte a arquivos limitado, mas
funcional: voc pode carregar um
bitmap existente, desenhar figuras
sobre ele e salv-lo no disco.
15 Recursos grficos do Delphi
obj ect Exi t 1: TMenuI tem. . .
obj ect Opt i ons1: TMenuI t em
obj ect Str et ch1: TMenuI t em
obj ect Cent er 1: TMenuI t em
obj ect Hel p1: TMenuI t em
obj ect About I mageVi ewer 1: TMenuI tem
end
obj ect OpenDi al og1: TOpenDi al og
Fi l eEdi t St yl e = f sEdi t
Fi l t er = Bi tmap ( *. bmp) | *. bmp|
I con ( *. i co) | *. i co| Metaf i l e (*. mf ) | *. mf
Opti ons = [ of Hi deReadOnl y, of Pat hMust Exi st ,
of Fi l eMust Exi st ]
end
end
De forma surpreendente, esse aplicativo exige muito pouco cdigo, pelo menos em sua primeira
verso bsica. Os comandos File | Exit e Help | About so simples, e o comando File | Open tem
o seguinte cdigo:
procedure TVi ewer For m. Open1Cl i ck(Sender : TObj ect );
begi n
i f OpenDi al og1. Execut e then
begi n
I mage1. Pi ctur e. LoadFr omFi l e (OpenDi al og1. Fi l eName);
Capt i on : = I mage Vi ewer - + OpenDi al og1. Fi l eName;
end;
end;
O quarto e o quinto comandos de menu, Options | Stretch e Options | Center, simplesmente
ativam/desativam a propriedade St r et ch (veja o resultado na Figura 4) e a propriedade Cent er do
componente e incluem uma marca de seleo em si mesmos. Aqui est o manipulador do evento On-
Cl i ck do item de menu St r et ch1:
procedure TVi ewer For m. St r et ch1Cl i ck(Sender : TObj ect );
begi n
I mage1. St r et ch : = not I mage1. St r etch;
Str et ch1. Checked : = I mage1. St r et ch;
end;
Lembre-se de que, ao alongar uma imagem, voc pode mudar sua relao largura-altura, possi-
velmente distorcendo a figura, e que nem todas as imagens podem ser alongadas de forma correta. O
alongamento de bitmaps em preto-e-branco e de 256 cores nem sempre funciona corretamente.
Alm desse problema, o aplicativo tem alguns outros inconvenientes. Se voc selecionar um ar-
quivo sem uma das extenses padro, o componente I mage lanar uma exceo. O manipulador de
excees fornecido pelo sistema se comporta como esperado; o arquivo de imagem errado no car-
regado, e o programa pode continuar com segurana. Outro problema que, se voc carregar uma
imagem grande, o visualizador no possuir barras de rolagem. Voc pode maximizar a janela do vi-
sualizador, mas isso poder no ser suficiente. Os componentes I mage no manipulam barras de ro-
lagem automaticamente, mas o formulrio pode fazer isso. A seguir, vamos estender ainda mais esse
exemplo, para incluir barras de rolagem.
16 Dominando o Delphi 6 A Bibla
Rolando uma Imagem
Uma vantagem do modo como o rolamento automtico funciona no Delphi que, se o tamanho de
um componente grande contido em um formulrio muda, barras de rolagem so includas ou remo-
vidas automaticamente. Um bom exemplo o uso do componente I mage. Se a propriedade Aut oSi ze
desse componente for configurada para Tr ue e voc carregar uma nova figura nele, o componente se
dimensionar automaticamente, e o formulrio incluir ou remover as barras de rolagem, conforme
for necessrio.
Se voc carregar um bitmap grande no exemplo I mageV, notar que parte do bitmap permanece
oculta. Para corrigir isso, voc pode configurar a propriedade Aut oSi ze do componente I mage para
Tr ue e desativar seu alinhamento com a rea cliente. Voc tambm deve configurar um tamanho ini-
cial pequeno para a imagem. Voc no precisa fazer quaisquer ajustes quando carrega um novo bi-
tmap, pois o tamanho do componente I mage configurado automaticamente pelo sistema. Na Figura
5 voc pode ver que barras de rolagem so realmente includas no formulrio. A figura mostra duas
execues diferentes do programa. A diferena entre a execuo do programa esquerda e a que est
direita que a primeira tem uma imagem menor do que sua rea cliente; portanto, nenhuma barra
de rolagem inserida. Quando voc carregar uma imagem maior no programa, duas barras de rolagem
aparecero automaticamente, como no exemplo da direita.
FIGURA 4
Dois exemplos do programa I mageV,
que apresentam as verses normal e
alongada do mesmo bitmap.
17 Recursos grficos do Delphi
Mais algum cdigo exigido para desativar as barras de rolagem e mudar o alinhamento da ima-
gem quando o comando de menu Stretch selecionado e para restaur-las quando esse recurso for
desativado. De novo, no atuamos diretamente sobre as barras de rolagem em si, mas simplesmente
mudamos o alinhamento do painel, usando sua propriedade St r et ch, e calculamos manualmente o
novo tamanho, usando o tamanho da figura atualmente carregada. (Este cdigo imita o efeito da pro-
priedade Aut oSi ze, que funciona apenas quando um novo arquivo carregado.)
procedure TVi ewer For m. St r et ch1Cl i ck(Sender : TObj ect );
begi n
I mage1. St r et ch : = not I mage1. St r etch;
Str et ch1. Checked : = I mage1. St r et ch;
i f I mage1. St r et ch then
I mage1. Al i gn : = al Cl i ent
el se
begi n
I mage1. Al i gn : = al None;
I mage1. Hei ght : = I mage1. Pi ct ur e. Hei ght ;
I mage1. Wi dth : = I mage1. Pi ctur e. Wi dth;
end;
end;
Bitmaps ao Mximo
Quando o controle I mage est conectado a um bitmap, existem algumas operaes adicionais que voc
deve realizar, mas, antes de examin-las, temos que apresentar os formatos de bitmap. Existem dife-
rentes tipos de bitmaps no Windows. Os bitmaps podem ser independentes dedispositivo ou no, um
termo usado para indicar se o bitmap tem informaes de gerenciamento de paleta extras. Os arquivos
BMP normalmente so bitmaps independentes de dispositivo.
Outra diferena se relaciona com a profundidade de cores isto , o nmero de diferentes cores
que o bitmap pode usar ou, em outras palavras, o nmero de bits exigidos para armazenar cada pixel.
Em um bitmap de 1 bit, cada ponto pode ser branco ou preto (para sermos mais precisos, os bitmaps
de 1 bit podem ter uma paleta de cores, permitindo que ele represente quaisquer duas cores e no
apenas preto e branco). Um bitmap de 8 bits normalmente possui uma paleta acompanhante para in-
dicar como as 256 diferentes cores so mapeadas nas cores de sistema reais, um bitmap de 24 bits
indica a cor de sistema diretamente. Para tornar as coisas mais complexas, quando o sistema desenha
um bitmap em um computador com uma capacidade de cores diferente, ele precisa realizar alguma
converso.
I nternamente, o formato do bitmap muito simples, qualquer que seja a profundidade de cores.
Todos os valores que constituem uma linha so armazenados em um bloco de memria. I sso efi-
FIGURA 5
No exemplo I mageV2,
barras de rolagem so includas
automaticamente no formulrio,
quando o bitmap inteiro no cabe
na rea cliente do formulrio
apresentado.
18 Dominando o Delphi 6 A Bibla
ciente para mover os dados da memria para a tela, mas no um modo to eficiente para armazenar
informaes; os arqui vos BMP geralmente so muito grandes e eles no tm compactao.
NOTA Na ver d ad e, o f o r m at o BM P t em u m a f o r m a m ui t o l i m i t ad a d e co m p act ao , con heci d a com o Ru n-
Len gt h En co di ng (RLE), em q u e os p i xel s sub seq ent es co m a m esm a co r so sub st i t udo s p el o n -
m er o d e t ai s p i xel s seg u i d os da co r . Isso p o de r ed uzi r o t am anh o d a i m ag em , m as, em al gu ns
caso s, a f ar cr escer . Par a i m ag ens co m p act ad as no Del p h i , voc p o de u sar a cl asse TJpegI mage e
o sup or t e ao f o r m at o JPEG of er eci d o pel a cl asse TPi ct ur e. Na ver dade, t ud o q ue TPi ct ur e f az
ger enci ar um a l i st a r egi st r ad a d e cl asses gr f i cas.
O exemplo BmpDraw usa essa informao sobre a estrutura interna de um bitmap e alguns ou-
tros recursos tcnicos para levar a manipulao direta de bitmaps para um novo nvel. Primeiro, ele
estende o exemplo I mageV atravs da incluso de um item de menu que voc pode usar para apre-
sentar a profundidade de cores do bitmap atual, usando a propriedade Pi xel For mat correspondente:
procedure TBi t mapFor m. Col or Dept h1Cl i ck(Sender : TObj ect );
var
st r Depth : St r i ng;
begi n
case I mage1. Pi ct ur e. Bi tmap. Pi xel For mat of
pf Devi ce: st r Depth : = Devi ce ;
pf 1bi t : st r Dept h : = 1- bi t ;
pf 4bi t : st r Dept h : = 4- bi t ;
pf 8bi t : st r Dept h : = 8- bi t ;
pf 15bi t : st r Dept h : = 15- bi t ;
pf 16bi t : st r Dept h : = 16- bi t ;
pf 24bi t : st r Dept h : = 24- bi t ;
pf 32bi t : st r Dept h : = 32- bi t ;
pf Cust om: st r Depth : = Custom ;
end;
MessageDl g ( Bi tmap col or depth: + st r Dept h,
mt I nf or mat i on, [ mbOK] , 0);
end;
Voc pode tentar carregar bitmaps diferentes e ver o efeito desse mtodo, como ilustra a Figura 6.
O mais interessante estudar como se acessa a imagem de memria mantida pelo objeto bitmap.
Uma soluo simples usar a propriedade Pi xel s, conforme fizemos no exemplo ShapeBmp, para de-
senhar os pixels vermelhos durante a operao de arrasto. Nesse programa, inclumos um item de menu
para criar um bitmap inteiramente novo, pixel por pixel, usando um clculo matemtico simples para de-
terminar a cor. (A mesma estratgia pode ser usada, por exemplo, para construir imagens fractais.)
Aqui est o cdigo do mtodo, que simplesmente varre o bitmap nas duas direes e define a
cor de cada pixel. Como estamos realizando muitas operaes sobre o bitmap, podemos armazenar
uma referncia a ele na varivel local Bmp, por simplicidade:
procedure TBi t mapFor m. Gener at eSl ow1Cl i ck(Sender : TObj ect );
var
Bmp: TBi t map;
I , J, T: I nt eger ;
begi n
/ / obtma i mageme a modi f i ca
Bmp : = I mage1. Pi ctur e. Bi t map;
Bmp. Pi xel For mat : = pf 24bi t;
Bmp. Wi dt h : = 256;
19 Recursos grficos do Delphi
Bmp. Hei ght : = 256;

T : = Get Ti ckCount ;
/ / al tera cada pi xel
for I : = 0 to Bmp. Hei ght - 1 do
for J : = 0 to Bmp. Wi dth - 1 do
Bmp. Canvas. Pi xel s [ I , J] : = RGB (I *J mod 255, I , J);
Capt i on : = I mage Vi ewer - Memory I mage ( MSecs: +
I ntToStr (Get Ti ckCount - T) + ) ;
end;
Note que o programa controla o tempo exigido por essa operao, que, em meu computador,
levou cerca de seis segundos. Conforme se v a partir do nome da funo, essa uma verso lenta do
cdigo.
Podemos aceler-la consideravelmente acessando o bitmap uma linha inteira por vez. Esse recur-
so pouco conhecido est disponvel atravs da propriedade ScanLi ne do bitmap, que retorna um pon-
teiro para a rea de memria da linha de bitmap. Pegando esse ponteiro e acessando a memria
diretamente, tornamos o programa muito mais rpido. O nico problema que precisamos conhecer
a representao interna do bitmap. No caso de um bitmap de 24 bits, todo ponto representado por
trs bytes que definem a intensidade de azul, verde e vermelho (o inverso da seqncia RGB). Aqui
est o cdigo alternativo, com uma sada ligeiramente diferente (pois modificamos deliberadamente o
clculo da cor):
procedure TBi t mapFor m. Gener at eFast 1Cl i ck(Sender : TObj ect );
var
Bmp: TBi t map;
I , J, T: I nt eger ;
Li ne: PByteAr r ay;
begi n
/ / obtma i mageme a modi f i ca
Bmp : = I mage1. Pi ctur e. Bi t map;
Bmp. Pi xel For mat : = pf 24bi t;
Bmp. Wi dt h : = 256;
Bmp. Hei ght : = 256;
FIGURA 6
A profundidade de cores de um
bitmap padro do Windows,
conforme apresentada pelo
exemplo BmpDRAW.
20 Dominando o Delphi 6 A Bibla
T : = Get Ti ckCount ;
/ / muda cada pi xel , l i nha por l i nha
for I : = 0 to Bmp. Hei ght - 1 do
begi n
Li ne : = PByt eAr r ay (Bmp. ScanLi ne [ I ] );
for J : = 0 to Bmp. Wi dth - 1 do
begi n
Li ne [ J*3] : = J;
Li ne [ J*3+1] : = I *J mod 255;
Li ne [ J*3+2] : = I ;
end;
end;
/ / atual i za o v deo
I mage1. I nval i dat e;
Capt i on : = I mage Vi ewer - Memory I mage ( MSecs: +
I ntToStr (Get Ti ckCount - T) + ) ;
end;
Apenas mover uma linha na memria no faz uma tela ser atualizada; portanto, o programa cha-
ma I nval i dat e no final. A sada produzida por este segundo mtodo (veja a Figura 7) muito pa-
recida, mas o tempo que ela levou em meu computador foi de cerca de 60 milissegundos. I sso quase
um centsimo do tempo da outra estratgia! Essa tcnica to rpida que podemos us-la para rolar
as linhas do bitmap e ainda produzir um efeito rpido e suave. A operao de rolagem tem algumas
opes; portanto, quando voc seleciona os itens de menu correspondentes, o programa simplesmente
mostra um painel dentro do formulrio. Esse painel tem uma barra de controle que voc pode usar
para ajustar a velocidade da operao de rolagem (reduzindo sua suavidade medida que a velocidade
aumenta). A posio da barra de controle salva em um campo local do formulrio:
procedure TBi t mapFor m. Tr ackBar 1Change(Sender : TObj ect );
begi n
nLi nes : = Tr ackBar 1. Posi t i on;
Tr ackBar 1. Hi nt : = I nt ToSt r (Tr ackBar 1. Posi t i on);
end;
No painel existem tambm dois botes, usados para iniciar e interromper a operao de rolagem.
O cdigo do boto Go tem dois laos f or . O lao externo usado para repetir a operao de rolagem,
FIGURA 7
O desenho que voc v na tela
gerado pelo exemplo BmpDraw em
uma frao de segundo (conforme
informado em seu ttulo).
21 Recursos grficos do Delphi
tantas vezes quantas forem as linhas no bitmap. O lao interno realiza a operao de rolagem copiando
cada linha do bitmap na anterior. A primeira linha armazenada temporariamente em um bloco de
memria e depois copiada na ltima linha no final. Esse bloco de memria temporrio mantido em
uma rea de memria alocada dinamicamente (Al l ocMem), grande o suficiente para conter uma linha.
Essa informao obtida pelo clculo da diferena nos endereos de memria de duas linhas conse-
cutivas.
O centro da operao de movimentao efetuado atravs do uso da funo Move do Delphi.
Seu parmetro a varivel a ser movida, e no os endereos de memria. Por isso, voc tem de retirar
a referncia aos ponteiros. (Bem, esse mtodo realmente um bom exerccio sobre ponteiros!) Final-
mente, note que, desta vez, no podemos invalidar a imagem inteira aps cada operao de rolagem,
pois isso produz muito tremido na sada. A soluo oposta invalidar cada linha depois de ela ter sido
movida, mas isso torna o programa lento demais. Como uma soluo intermediria, decidimos inva-
lidar um bloco de linhas por vez, conforme determinado pela expresso J mod nLi nes = 0. Quando
determinado nmero de linhas tiver sido movido, o programa atualiza essas linhas:
Rect (0, Panel Scr ol l . Hei ght + H - nLi nes,
W, Panel Scr ol l . Hei ght + H);
Conforme voc pode ver, o nmero de linhas determinado pela posio do controle TrackBar.
Um usurio pode at mudar a velocidade movendo o controle speed durante a operao de ro-
lagem. Tambm permitimos que o usurio pressione o boto Cancel durante a operao. I sso se tor-
nou possvel pela chamada a Appl i cat i on. Pr oces sMes sages no lao f or interno. O boto Cancel
altera o flag f Cancel , que testado em cada iterao do lao f or externo:
procedure TBi t mapFor m. Bt nCancel Cl i ck(Sender : TObj ect );
begi n
f Cancel : = Tr ue;
end;
Assim, aps toda essa descrio, aqui est o cdigo completo do manipulador do evento OnCl i ck
do boto Go:
procedure TBi t mapFor m. Bt nGoCl i ck(Sender : TObj ect );
var
W, H, I , J, Li neByt es: I nteger ;
Li ne: PByteAr r ay;
Bmp: TBi t map;
R: TRect ;
begi n
/ / conf i gura a i nterf ace como usuri o
f Cancel : = Fal se;
BtnGo. Enabl ed : = Fal se;
BtnCancel . Enabl ed : = Tr ue;

/ / obtmo bi tmap da i mageme o redi mensi ona
Bmp : = I mage1. Pi ctur e. Bi t map;
W: = Bmp. Wi dt h;
H : = Bmp. Hei ght ;

/ / al oca memri a suf i ci ente para uma l i nha
Li neByt es : = Abs (I nt eger (Bmp. ScanLi ne [ 1] ) -
I nt eger (Bmp. ScanLi ne [ 0] ));
Li ne : = Al l ocMem(Li neByt es);

22 Dominando o Delphi 6 A Bibla
/ / rol a tantos i tens quantas f oremas l i nhas
for I : = 0 to H - 1 do
begi n
/ / sai do l ao f or se o boto Cancel f oi pressi onado
i f f Cancel then
Br eak;

/ / copi a a pri mei ra l i nha
Move ((Bmp. ScanLi ne [ 0] )^, Li ne^, Li neByt es);
/ / para toda l i nha
for J : = 1 to H - 1 do
begi n
/ / move a l i nha para a anteri or
Move ((Bmp. ScanLi ne [ J] )^, (Bmp. ScanLi ne [ J- 1] )^, Li neByt es);
/ / cada nLi nes atual i za a sa da
i f (J mod nLi nes = 0) then
begi n
R : = Rect (0, Panel Scr ol l . Hei ght + J- nLi nes,
W, Panel Scr ol l . Hei ght + J);
I nval i dateRect (Handl e, @R, Fal se);
UpdateWi ndow (Handl e);
end;
end;

/ / move a pri mei ra l i nha para o f i nal
Move (Li ne^, (Bmp. ScanLi ne [ Bmp. Hei ght - 1] ) ^, Li neBytes) ;
/ / atual i za a parte f i nal do bi tmap
R : =Rect ( 0, Panel Scrol l . Hei ght +H - nLi nes,
W, Panel Scrol l . Hei ght +H) ;
I nval i dateRect ( Handl e, @R, Fal se);
UpdateWi ndow(Handl e) ;

/ / permi te que o programa mani pul e outras mensagens
Appl i cati on. ProcessMessages;
end;

/ / rei ni ci al i za a UI
BtnGo. Enabl ed : = Tr ue;
BtnCancel . Enabl ed : = Fal se;
end;
Voc pode ver um bitmap durante a operao de rolagem na Figura 8. Note que a rolagem pode
ocorrer em qualquer tipo de bitmap, e no apenas nos bitmaps de 24 bits gerados por este programa.
Na verdade, voc pode carregar outro bitmap no programa e depois rol-lo, como fizemos para criar
a ilustrao.
Um Bitmap Animado em um Boto
Os botes de bitmap so fceis de usar e podem produzir aplicativos de melhor aparncia do que os
botes padro (o componente Button). Para aprimorar ainda mais o efeito visual de um boto, tam-
bm podemos considerar a animao do boto. Existem basicamente dois tipos de botes animados
botes que mudam sua imagem ligeiramente quando so pressionados e botes que possuem uma
23 Recursos grficos do Delphi
imagem que se move, independentemente da operao. Vamos mostrar um exemplo simples de cada
tipo, Fire e World. Para cada um dos exemplos, exploraremos duas verses ligeiramente diferentes.
Um Boto de Dois Estados
O primeiro exemplo, o programa Fire, tem um formulrio simples, contendo apenas um boto de bi-
tmap. Esse boto est conectado a um elemento Gl yph que representa um canho. I magine tal boto
como parte de um programa de jogo. Quando o boto pressionado, a figura muda para mostrar um
canho atirando. Assim que o boto solto, a imagem padro novamente carregada. Nesse nterim,
o programa apresenta uma mensagem, caso o usurio tenha realmente dado um clique no boto.
Para escrever este programa, precisamos manipular trs eventos do boto: OnMous eDown, OnMou-
seUp e OnCl i ck. O cdigo dos trs mtodos extremamente simples:
procedure TFor m1. Bi t Bt nFi r eMouseDown(Sender : TObj ect ;
But t on: TMouseBut ton; Shi f t : TShi f t St at e; X, Y: I nt eger );
begi n
/ / carrega o bi tmap do canho ati rando
i f Butt on = mbLef t then
Bi t Bt nFi r e. Gl yph. LoadFr omFi l e ( f i re2. bmp );
end;
procedure TFor m1. Bi t Bt nFi r eMouseUp(Sender : TObj ect ;
But t on: TMouseBut ton; Shi f t : TShi f t St at e; X, Y: I nt eger );
begi n
/ / carrega o bi tmap do canho padro
i f Butt on = mbLef t then
Bi t BtnFi r e. Gl yph. LoadFr omFi l e ( f i re. bmp );
end;
procedure TFor m1. Bi t Bt nFi r eCl i ck(Sender : TObj ect );
begi n
Pl aySound ( Boom. wav , 0, snd_Async);
MessageDl g ( Boom! , mt War ni ng, [ mbOK] , 0);
end;
FIGURA 8
O exemplo BmpDraw permite a
rolagem rpida de um bitmap.
24 Dominando o Delphi 6 A Bibla
I nclumos alguns recursos sonoros, reproduzindo um arquivo WAV quando o boto pressio-
nado, com uma chamada para a funo Pl aySound da unidade MmSystem. Quando voc mantm o
boto esquerdo do mouse pressionado sobre o boto de bitmap, o boto pressionado. Se, ento,
voc move o cursor do mouse para fora do boto, enquanto mantm o boto do mouse pressionado,
o boto de bitmap liberado, mas ele no recebe um evento OnMouseUp; portanto, o canho atirando
permanece l. Se, posteriormente, voc soltar o boto esquerdo do mouse fora da superfcie do boto
de bitmap, ele receber o evento OnMous eUp. O motivo que todos os botes no Windows capturam
a entrada de mouse quando so pressionados.
Muitas Imagens em um Bitmap
O exemplo Fire usou uma estratgia manual. Carregamos dois bitmaps e alteramos o valor da pro-
priedade Gl yph quando queramos mudar a imagem. O componente BitBtn, entretanto, tambm pode
manipular vrios bitmaps automaticamente. Voc pode preparar um nico bitmap que contenha vrias
imagens (ou grifos) e configurar esse nmero como o valor da propriedade NumGl yphs. Todos esses
sub-bitmaps devem ter o mesmo tamanho, pois o bitmap global dividido em partes iguais.
Se voc fornecer mais de uma imagem no bitmap, elas sero usadas de acordo com as regras a
seguir:
I O primeiro bitmap usado para o boto solto, a posio padro.
I O segundo bitmap usado para o boto desativado.
I O terceiro bitmap usado quando o boto recebe um clique de mouse.
I O quarto bitmap usado quando o boto permanece pressionado, como nos botes que se
comportam como caixas de seleo.
Normalmente, voc fornece uma nica imagem, e as outras so automaticamente calculadas a
partir dela, com alteraes grficas simples. Entretanto, fcil fornecer uma segunda, uma terceira e
uma quarta figura personalizada. Se voc no fornecer todos os quatro bitmaps, os ausentes sero cal-
culados automaticamente a partir do primeiro.
Em nosso exemplo, a nova verso de Fire (chamada Fire2), precisamos apenas da primeira e da
terceira imagens do bitmap, mas somos obrigados a incluir o segundo bitmap. Para ver como essa
imagem (a segunda do bitmap) pode ser usada, inclumos uma caixa de seleo para desativar o boto
de bitmap. Para construir a nova verso do programa, preparamos um bitmap de 32x96 pixels (veja
a Figura 9) e o usamos para a propriedade Gl yph do bitmap. O Delphi configura automaticamente a
propriedade NumGl yphs em 3, pois o bitmap trs vezes mais largo do que sua altura.
FIGURA 9
O bitmap com trs imagens do
exemplo Fire2, conforme visto no
Delphi I mage Editor.
25 Recursos grficos do Delphi
A caixa de seleo, usada para ativar e desativar o boto (para que possamos ver a imagem cor-
respondente ao status desativado), tem o seguinte evento OnCl i ck:
procedure TFor m1. CheckBox1Cl i ck(Sender : TObj ect );
begi n
Bi t Bt nFi r e. Enabl ed : = CheckBox1. Checked;
end;
Quando voc executa o programa, existem duas maneiras de mudar o bitmap no boto. Voc
pode desativar o boto de bitmap usando a caixa de seleo (veja a Figura 10) ou pode pressionar o
boto para ver o canho atirar. Na primeira verso (o exemplo Fire), a imagem com o canho atirando
permanecia no boto at que a caixa de mensagem fosse fechada. Agora (no exemplo Fire2), a imagem
mostrada apenas enquanto o boto est pressionado. Assim que voc sai da superfcie do boto, a
primeira imagem apresentada.
O Mundo Girando
O segundo exemplo de animao, World, tem um boto que apresenta a Terra, que gira lentamente,
mostrando os vrios continentes. Voc pode ver alguns exemplos na Figura 11, mas, claro, deve exe-
cutar o programa para ver sua sada. No exemplo anterior, a imagem mudava quando o boto era
pressionado. Agora, a imagem muda sozinha, automaticamente. I sso ocorre graas presena de um
componente Timer, que recebe uma mensagem em intervalos fixos de tempo.
Aqui est um resumo das propriedades do componente:
obj ect Wor l dFor m: TWor l dFor m
Capt i on = Worl d
OnCr eat e = For mCr eat e
obj ect Label 1: TLabel . . .
obj ect Wor l dBut t on: TBi t Btn
Capt i on = &Start
OnCl i ck = Wor l dBut tonCl i ck
Gl yph. Dat a = {W1. bmp}
Spaci ng = 15
end
obj ect Ti mer 1: TTi mer
Enabl ed = Fal se
I nter val = 500
OnTi mer = Ti mer 1Ti mer
end
end
FIGURA 10
Os botes de bitmap
ativado e desativado do exemplo
Fire2, em duas execues
diferentes do aplicativo.
26 Dominando o Delphi 6 A Bibla
O componente temporizador iniciado e parado (ativado e desativado) quando o usurio pres-
siona o boto de bitmap que possui a imagem do mundo:
procedure TWor l dFor m. Wor l dBut t onCl i ck(Sender : TObj ect );
begi n
i f Ti mer 1. Enabl ed then
begi n
Ti mer 1. Enabl ed : = Fal se;
Wor l dBut t on. Capt i on : = &Start ;
end
el se
begi n
Ti mer 1. Enabl ed : = Tr ue;
Wor l dBut t on. Capt i on : = &Stop ;
end;
end;
Conforme voc pode ver na Figura 11, um rtulo sobre o boto indica qual das imagens est sendo
apresentada. Sempre que a mensagem do temporizador recebida, a imagem e o rtulo mudam:
procedure TWor l dFor m. Ti mer 1Ti mer (Sender : TObj ect );
begi n
Count : = (Count mod 16) + 1;
Label 1. Capt i on : = Di spl ayi ng i mage +
I nt ToSt r (Count );
Wor l dBut t on. Gl yph. LoadFr omFi l e (
w + I nt ToSt r (Count ) + . bmp );
end;
Nesse cdigo, Count um campo do formulrio que inicializado como 1 no mtodo For mCr ea-
t e. A cada intervalo do temporizador, Count aumentado como mdulo 16 e depois convertido para
uma string (precedida pela letra w). A razo para esse limite simples tnhamos 16 bitmaps da Ter-
ra para apresentar. Chamar os arquivos de bitmap de W1. BMP, W2. BMP etc. facilita para o programa
acess-los, construindo as strings com o nome em tempo de execuo.
NOTA A o per ao d e m d ul o r et or n a o r est o da d i vi so ent r e i nt ei r os. Isso si g ni f i ca qu e Count mod 16
r et or n a i nvar i avel m en t e u m val o r n o i n t er val o d e 0 a 1 5 . Som and o um a esse val or d e r et or n o,
ob t em o s o n m er o d o b i t m ap , q u e est no i nt er val o de 1 a 16 .
FIGURA 11
Alguns exemplos do programa
World em funcionamento.
27 Recursos grficos do Delphi
Uma Lista de Bitmaps, o Uso de Recursos e um
ControlCanvas
O programa World funciona, mas muito lento por dois motivos. Primeiramente, a cada intervalo
do temporizador, ele precisa ler um arquivo do disco e, embora uma cache de disco possa tornar isso
mais rpido, certamente essa no a soluo mais eficiente. Alm de ler o arquivo do disco, o pro-
grama tem de criar e destruir objetos de bitmap do Windows e isso requer algum tempo. O segundo
problema depende de como a imagem atualizada: quando voc muda o bitmap do boto, o com-
ponente completamente apagado e repintado. I sso causa algum tremido, conforme voc pode ver
executando o programa.
Para resolver o primeiro problema (e para mostrar a voc uma estratgia diferente para manipu-
lar bitmaps), criamos uma segunda verso do exemplo, World2. Aqui, inclumos um continer TOb-
j ect Li st do Delphi 5, armazenando uma lista de bitmaps, no formulrio do programa. O formulrio
tambm tem mais alguns campos:
type
TWor l dFor m= cl ass(TFor m)
. . .
pri vate
Count , YPos, XPos: I nt eger ;
Bi t mapsLi st : TObj ectLi st;
Contr ol Canvas: TContr ol Canvas;
end;
Todos os bitmaps so carregados quando o programa comea e destrudos quando ele termina.
A cada intervalo do temporizador, o programa mostra um dos bitmaps da lista no boto de bitmap.
Usando uma lista, evitamos o carregamento de um arquivo cada vez que precisarmos apresentar um
bitmap, mas ainda precisamos ter todos os arquivos com as imagens no diretrio que possui o arquivo
executvel. Uma soluo para esse problema mover os bitmaps dos arquivos independentes para o
arquivo de recursos do aplicativo. I sso mais fcil fazer do que explicar.
Para usar os recursos em lugar dos arquivos de bitmap, precisamos primeiro criar esse arquivo.
A melhor estratgia escrever um script de recursos (um arquivo RC), listando os nomes dos arquivos
de bitmap e dos recursos correspondentes. Abra um novo arquivo de texto (em qualquer editor) e es-
creva o seguinte cdigo:
W1 BI TMAP "W1. BMP"
W2 BI TMAP "W2. BMP"
W3 BI TMAP "W3. BMP"
/ / . . . etc.
Uma vez que voc tenha preparado esse arquivo RC (o chamamos de Wor l dBmp. RC), possvel
compil-lo em um arquivo RES, usando o compilador de recursos includo e o aplicativo de linha de
comando BRCC32, que pode ser encontrado no diretrio BI N do Delphi, e depois inclu-lo no pro-
jeto atravs da insero da diretiva {$R WORLDBMP. RES} no cdigo-fonte do projeto ou em uma das
unidades.
Entretanto, no Delphi 5, voc pode usar uma estratgia mais simples. Voc pode pegar o arquivo
RC e simplesmente inclu-lo no projeto, usando o comando Add to Project do menu Project ou sim-
plesmente arrastando o arquivo para o projeto. O Delphi 5 ativar automaticamente o compilador de
recursos e depois ligar o arquivo de recursos ao arquivo executvel. Essas operaes so controladas
por uma diretiva de incluso de recurso estendida, includa no cdigo-fonte do projeto:
{$R WORLDBMP. res WORLDBMP. RC }
28 Dominando o Delphi 6 A Bibla
Uma vez que tenhamos definido corretamente os recursos do aplicativo, precisamos carregar os
bitmaps dos recursos. Para um objeto TBi t map, podemos usar o mtodo LoadFr omResour ceName, caso
o recurso tenha um identificador de string, ou o mtodo LoadFr omRes our ceI D, caso ele tenha um
identificador numrico. O primeiro parmetro dos dois mtodos um handle para o aplicativo, co-
nhecido como HI nst ance, disponvel no Delphi como uma varivel global.
DICA O Del p hi d ef i ne u m a seg un d a var i vel gl ob al , Mai nI nst ance, q ue se r ef er e a HI nst ance d o ar qu i vo
execu t vel p r i n ci p al . A no ser q ue voc est ej a d en t r o de u m a DLL, p od e usar u m a o u ou t r a i n d i s-
t i nt am ent e.
Este o cdigo do mtodo For mCr eat e:
procedure TWor l dFor m. For mCr eat e(Sender : TObj ect );
var
I : I nteger ;
Bmp: TBi t map;
begi n
Count : = 1;
/ / carrega os bi tmaps e os i ncl ui na l i sta
Bi t mapsLi st : = TLi st . Cr eate;
for I : = 1 to 16 do
begi n
Bmp : = TBi tmap. Cr eat e;
Bmp. LoadFr omResour ceName (HI nstance,
W + I nt ToSt r (I ));
Bi tmapsLi st. Add (Bmp);
end;
end;
NOTA Co m o al t er nat i va, p od er am os t er u sad o o co m p on ent e Im ag eLi st , m as, p ar a est e exem p l o, d eci -
di m os u sar um a est r at gi a d e b ai xo n vel p ar a m o st r ar a voc t o do s os d et al h es envo l vi d o s.
Permanece um problema para ser resolvido: obter uma transio suave de uma imagem do mun-
do para a seguinte. O programa deve pintar os bitmaps em um canvas usando o mtodo Draw. I nfe-
lizmente, o canvas do boto de bitmap no est diretamente disponvel (e sem evento protegido);
portanto, decidimos usar um componente TCont r ol Canvas (em geral o canvas interno de um controle,
mas um que voc tambm possa associar externamente). Para us-lo para pintar sobre um boto, po-
demos atribuir o boto ao controle canvas no mtodo For mCr eat e:
Cont r ol Canvas : = TCont r ol Canvas. Cr eat e;
Cont r ol Canvas. Cont r ol : = Wor l dBut t on;
YPos : = (Wor l dBut t on. Hei ght Bmp. Hei ght ) di v 2;
XPos : = Wor l dBut t on. Mar gi n;
A posio horizontal do boto onde a imagem est localizada (e onde devemos pintar) depende
do componente Mar gi n do cone do boto de bitmap e da altura do bitmap. Uma vez que o canvas
do controle esteja corretamente definido, o mtodo Ti mer 1Ti mer simplesmente pinta sobre ele e
sobre o boto:
procedure TWor l dFor m. Ti mer 1Ti mer (Sender : TObj ect );
begi n
Count : = (Count mod 16) + 1;
Label 1. Capt i on : = For mat ( Di spl ayi ng i mage %d , [ Count ] );
29 Recursos grficos do Delphi
/ / desenha o bi tmap atual no canvas do control e
Cont r ol Canvas. Dr aw (XPos, YPos,
Bi t mapsLi st . I tems[ Count - 1] as TBi tmap);
end;
O ltimo problema mudar a posio da imagem quando o boto esquerdo do mouse for pres-
sionado ou solto sobre ele (isto , nos eventos OnMouseDown e OnMous eUp do boto). Alm de mover
a imagem por alguns pixels, devemos atualizar a imagem do bitmap, pois o Delphi a apresentar au-
tomaticamente enquanto redesenha o boto. Caso contrrio, um usurio veria a imagem inicial at que
o intervalo do temporizador tenha decorrido e o componente ativado o evento OnTi mer . (I sso poderia
levar algum tempo, caso voc o tivesse parado!) Aqui est o cdigo do primeiro dos dois mtodos:
procedure TWor l dFor m. Wor l dBut t onMouseDown(Sender : TObj ect ;
But t on: TMouseBut ton; Shi f t : TShi f t St at e; X, Y: I nt eger );
begi n
i f Butt on = mbLef t then
begi n
/ / pi nta a i magematual sobre o boto
Wor l dBut t on. Gl yph. Assi gn (
Bi tmapsLi st. I t ems[ Count- 1] as TBi t map);
I nc (YPos, 2);
I nc (XPos, 2);
end;
end;
O Controle Animado
H uma maneira melhor de obter animao do que apresentar uma srie de bitmaps em seqncia.
Use o controle comum Animate do Win32. O controle Animate baseado no uso de arquivos AVI
(Audio Video I nterleaved), uma srie de bitmaps semelhante a um filme.
NOTA Na ver d ad e, o co nt r ol e An i m at e po d e ap r esen t ar ap en as os ar q ui vo s AVI qu e t enh am um n i co
f l uxo d e vd eo, sej am d escom pact ad o s ou com p act ad os com co m p act ao RLE8 e no t en ham m u-
danas de p al et a; e se el es t i ver em som , el e ser i g no r ad o. Na p r t i ca, os ar q ui vos co r r espo nd ent es
a esse r eq ui si t o so aqu el es co nst i t u d os d e um a sr i e d e b i t m aps d e com pu t ad o r e no aq u el es
basead os em u m f i l m e r eal .
O controle Animate pode ter duas fontes possveis para sua animao:
I Ele pode ser baseado em qualquer arquivo AVI que atenda aos requisitos indicados na nota
anterior; para usar esse tipo de fonte, configure um valor correto para a propriedade Fi l eName.
I Ele pode usar uma animao interna especial do Windows, parte da biblioteca de controle co-
mum; para usar esse tipo de fonte, escolha um dos valores possveis da propriedade CommonAVI
(que baseada em uma enumerao).
Se voc simplesmente colocar um controle Animate em um formulrio, escolha uma animao
usando um dos mtodos que acabamos de descrever e, finalmente, configure sua propriedade Act i ve
para Tr ue. Voc comear a ver a animao at em tempo de projeto. Por definio, a animao ocor-
re continuamente, reiniciando assim que acaba. Entretanto, voc pode regular esse efeito usando a
propriedade Repet i t i ons . O valor padro -1 causa uma repetio infinita; use qualquer outro valor
para especificar um nmero de repeties.
30 Dominando o Delphi 6 A Bibla
Voc tambm pode especificar o quadro inicial e final da seqncia, com as propriedades St ar t -
Fr ame e St opFr ame. Essas trs propriedades (posio inicial, posio final e nmero de repeties) cor-
respondem aos trs parmetros do mtodo Pl ay, que voc usar freqentemente com um controle
Animate. Como alternativa, voc pode configurar as propriedades e depois chamar o mtodo St ar t .
Em tempo de execuo, voc tambm pode acessar o nmero total de quadros usando a propriedade
Fr ameCount . Voc pode usar isso para executar a animao do incio ao fim. Finalmente, para obter
um controle mais apurado, voc pode usar o mtodo Seek, que apresenta um quadro especfico.
Usamos todos esses mtodos em um programa demonstrativo simples (AnimCtrl), que pode
usar os dois arquivos e as animaes padro do Windows. O programa permite que voc escolha um
arquivo ou uma das animaes usando um componente ListBox. I nclumos nesse componente ListBox
um item para cada elemento da enumerao TCommonAVI e usamos a mesma ordem:
obj ect Li st Box1: TLi st Box
I tems. St r i ngs = (
[ Use an AVI f i l e]
Fi nd Fol der
Fi nd Fi l e
Fi nd Computer
Copy Fi l es
Copy Fi l e
Recycl e Fi l e
Empty Recycl e
Del ete Fi l e )
OnCl i ck = Li stBox1Cl i ck
end
Graas a essa estrutura, quando o usurio d um clique no componente ListBox, apenas conver-
ter o nmero dos itens selecionados para o tipo de dados enumerado fornecer o valor correto para
a propriedade CommonAVI .
procedure TFor m1. Li st Box1Cl i ck(Sender : TObj ect );
begi n
Ani mate1. CommonAVI : = TCommonAVI (Li stBox1. I t emI ndex);
i f (Li st Box1. I t emI ndex = 0) and
OpenDi al og1. Execut e then
Ani mate1. Fi l eName : = OpenDi al og1. Fi l eName
end;
Conforme voc pode ver, quando o primeiro item selecionado (o valor caNone), o programa
carrega automaticamente um arquivo AVI , usando um componente OpenDialog. O componente mais
importante do formulrio o controle Animate. Aqui est sua descrio textual:
obj ect Ani mat e1: TAni mat e
Aut oSi ze = Fal se
Al i gn = al Cl i ent
CommonAVI = avi Fi ndFol der
OnOpen = Ani mat e1Open
end
Ele alinhado com a rea cliente, de modo que um usurio pode redimension-lo facilmente,
dependendo do tamanho real dos quadros da animao. Conforme voc pode ver, tambm definimos
um manipulador para um evento desse componente, OnOpen:
procedure TFor m1. Ani mat e1Open(Sender : TObj ect );
begi n
31 Recursos grficos do Delphi
Lbl Fr ames. Capti on : = Frames +
I ntToStr (Ani mat e1. Fr ameCount );
end;
Quando um novo arquivo (ou animao comum) aberto, o programa simplesmente produz
como sada o nmero de seus quadros em um rtulo. Esse rtulo fica junto a vrios botes e alguns
controles SpinEdit em um painel grande, atuando como uma barra de ferramentas. Voc pode v-los
no formulrio em tempo de projeto da Figura 12.
Os botes Start e Stop so totalmente triviais, mas o boto Play Once tem algum cdigo:
procedure TFor m1. Bt nOnceCl i ck(Sender : TObj ect );
begi n
Ani mate1. Pl ay (0, Ani mat e1. Fr ameCount , 1);
end;
As coisas comeam a ficar mais interessantes com o cdigo usado para reproduzir a animao
trs vezes ou para reproduzir apenas um trecho dela. Esses dois mtodos tm por base o mtodo Pl ay:
procedure TFor m1. Bt nTr i ceCl i ck(Sender : TObj ect );
begi n
Ani mate1. Pl ay (0, Ani mat e1. Fr ameCount , 3);
end;
procedure TFor m1. Bt nFr agment Cl i ck(Sender : TObj ect );
begi n
Ani mate1. Pl ay (Spi nEdi t 1. Val ue, Spi nEdi t 2. Val ue, - 1);
end;
Os dois ltimos manipuladores de evento de boto so baseados no mtodo Seek. O boto Goto
simplesmente vai para o quadro indicado pelo componente SpinEdit correspondente, enquanto os bo-
tes Reverse vo para cada quadro por sua vez, partindo do ltimo e fazendo uma pausa entre cada
um deles:
procedure TFor m1. Bt nGot oCl i ck(Sender : TObj ect );
begi n
Ani mate1. Seek (Spi nEdi t 3. Val ue);
end;
FIGURA 12
O formulrio do exemplo
AnimCtrl em tempo de projeto.
32 Dominando o Delphi 6 A Bibla
procedure TFor m1. Bt nRever seCl i ck(Sender : TObj ect );
var
I ni t : TDateTi me;
I : I nteger ;
begi n
for I : = Ani mat e1. Fr ameCount downto 1 do
begi n
Ani mat e1. Seek (I );
/ / espera 50 mi l i ssegundos
I ni t : = Now;
whi l e Now < I ni t + EncodeTi me (0, 0, 0, 50) do
Appl i cati on. Pr ocessMessages;
end;
end;
O Controle Animate em um Boto
Agora que voc sabe como o controle Animate funciona, podemos us-lo para construir outro boto
animado. Basta colocar um controle Animate e um boto grande (possivelmente com uma fonte gran-
de tambm) em um formulrio. Em seguida, escreva o seguinte cdigo para transformar o boto na
janela progenitora do controle Animate em tempo de execuo e posicion-lo corretamente:
procedure TFor m1. For mCr eat e(Sender : TObj ect );
var
Di f f : I nt eger ;
begi n
Ani mate1. Par ent : = Butt on1;
Di f f : = But t on1. Hei ght - Ani mat e1. Hei ght ;
Ani mate1. Set Bounds ( hDi f f di v 2, hDi f f di v 2,
Ani mat e1. Wi dt h, Ani mat e1. Hei ght );
Ani mate1. Act i ve : = Tr ue;
end;
Voc pode ver um exemplo desse efeito na Figura 13 (o projeto tem o nome AnimBtn). Essa
mesmo a estratgia mais simples para produzir um boto animado, mas ela tambm permite o mnimo
de controle.
Grades Grficas
As grades representam outro grupo interessante de componentes grficos do Delphi. O sistema ofe-
rece diferentes componentes grficos: uma grade de strings, uma de imagens, grades relacionadas a
FIGURA 13
O efeito do controle Animate dentro
de um boto, como ilustrado pelo
programa AnimBtn.
33 Recursos grficos do Delphi
banco de dados e uma grade de amostra de cores. Os dois primeiros tipos de grades so particular-
mente teis, pois eles permitem que voc represente muitas informaes e tambm que o usurio na-
vegue nelas. claro que as grades so extremamente importantes na programao de banco de dados,
e elas podem ser personalizadas com figuras, conforme vimos no Captulo 13 do DominandooDelphi 6.
Os componentes DrawGrid e StringGrid so intimamente relacionados. Na verdade, a classe
TSt r i ngGr i d uma subclasse de TDr awGr i d. Para que usar essas grades? Basicamente, voc pode ar-
mazenar alguns valores, ou nas strings relacionadas ao componente StringGrid ou em outras estruturas
de dados, e depois apresentar valores selecionados, usando critrios especficos. Embora as grades de
strings possam ser usadas quase como esto (pois elas j fornecem recursos de edio), as grades de ob-
jetos genricos normalmente exigem mais codificao.
Na verdade, as grades definem o modo como as informaes so organizadas para exibio e no
da maneira como so armazenadas. A nica grade que armazena os dados que apresenta StringGrid.
Todas as outras grades (incluindo os componentes DrawGrid e DBGrid) so apenas visualizadores de
dados, e no contineres de dados. O componente DBGrid no possui os dados que apresenta; ele
busca os dados a partir da fonte de dados conectada. s vezes isso causa confuso.
A estrutura bsica de uma grade inclui vrias colunas e linhas fixas, que indicam a regio no
rolante da grade (conforme voc pode ver na Figura 14). As grades esto entre os componentes mais
complexos disponveis no Delphi, conforme indicado pelo alto nmero de propriedades e mtodos
que elas contm. Existem muitas opes e propriedades excelentes para as grades, controlando sua
aparncia e seu comportamento.
Em sua aparncia, a grade pode ter linhas de diferentes tamanhos ou pode no ter linhas. Voc
pode configurar o tamanho de cada coluna ou linha independentemente das outras, pois as proprie-
dades RowSi ze, Col Wi dt h e RowHei ght so arrays. Para o comportamento da grade, voc pode deixar
que o usurio redimensione as colunas e as linhas (goCol Si zi ng e goRowSi zi ng), arraste colunas e li-
nhas inteiras para uma nova posio (goRowMovi ng e goCol umnMovi ng), selecione a edio automtica
e permita selees de intervalos. Como vrias opes permitem que os usurios executem diversas ope-
raes nas grades, tambm existem vrios elementos relacionados s grades, como OnCol umnMove, On-
Dr awCel l ou OnSet Edi t Text .
O evento mais importante provavelmente OnDr awCel l . Em resposta a esse evento, um progra-
ma deve pintar determinada clula da grade. Apenas as grades de strings podem apresentar automati-
camente seu contedo. Na verdade, o componente DrawGrid no tem suporte ao armazenamento de
dados. Ele simplesmente uma ferramenta para organizar uma parte da tela, para apresentar informa-
es em um formato regular. Trata-se de uma ferramenta simples, mas tambm poderosa. Mtodos
como Cel l Rect , que retorna o retngulo correspondente rea de uma clula, ou Mous eToCel l , que
retorna a clula em uma posio especfica, so uma satisfao de usar. Manipulando linhas e colunas
FIGURA 14
Quando voc coloca um novo
componente grade em um formulrio,
ele contm uma linha fixa e uma
coluna fixa como padro.
34 Dominando o Delphi 6 A Bibla
redimensionveis e grades rolantes, eles simplificam tarefas complexas e liberam o programador de cl-
culos maantes.
Para que voc pode usar uma grade? Construir uma planilha eletrnica provavelmente a pri-
meira idia que vem mente, mas isso talvez seja um pouco complexo demais para um exemplo. De-
cidimos usar o controle StringGrid em um programa que mostra as fontes instaladas no sistema, e o
controle DrawGrid em um programa que simula o jogo MineSweeper (Campo minado).
Uma Grade de Fontes
Se voc colocar um componente StringGrid em um formulrio e configurar suas opes corretamente,
ter um editor funcional completo de strings organizadas em uma grade, sem realizar qualquer pro-
gramao. Para tornar o exemplo mais interessante, decidimos desenhar cada clula da grade com uma
fonte diferente, variando seu tamanho e seu tipo. Voc pode ver o resultado do programa FontGrid
na Fi gura 15.
O formulrio desse programa muito simples. Voc precisa apenas colocar um componente gra-
de em um formulrio, alinh-lo com a rea cliente, configurar algumas propriedades e opes, e deixar
o programa fazer o resto. Na verdade, o nmero de colunas e linhas e seu tamanho so calculados em
tempo de execuo. As propriedades importantes que voc precisa configurar so Def aul t Dr awi ng,
que deve ser Fal se para nos permitir pintar a grade como quisermos, e Opt i ons :
obj ect For m1: TFor m1
Capt i on = Font Gri d
OnCr eat e = For mCr eat e
obj ect St r i ngGr i d1: TSt r i ngGr i d
Al i gn = al Cl i ent
Def aul tCol Wi dt h = 200
Def aul tDr awi ng = Fal se
Opti ons = [ goFi xedVer t Li ne, goFi xedHor zLi ne,
goVer t Li ne, goHor zLi ne, goDr awFocusSel ect ed,
goCol Si zi ng, goCol Movi ng, goEdi ti ng]
OnDr awCel l = Str i ngGr i d1Dr awCel l
end
end
Como normalmente acontece no Delphi, quanto mais simples for o formulrio, mais complexo
ser o cdigo. Este exemplo segue essa regra, embora ele tenha apenas dois mtodos, um para inicia-
FIGURA 15
Um exemplo da sada do aplicativo
FontGrid. A largura de cada coluna
pode ser alterada em tempo de
execuo.
35 Recursos grficos do Delphi
lizar a grade na inicializao e outro para desenhar os itens. A edio, na verdade, no foi personali-
zada e ocorre usando a fonte de sistema. O primeiro dos dois mtodos For mCr eat e. No incio, esse
mtodo usa o objeto global Scr een para acessar as fontes instaladas no sistema.
A grade tem uma coluna para cada fonte, assim como uma coluna fixa com nmeros represen-
tando tamanhos de fonte. O nome de cada coluna copiado do objeto Scr een para a primeira clula
de cada coluna (que possui um ndice zero):
procedure TFor m1. For mCr eat e(Sender : TObj ect );
var
I , J: I nt eger ;
begi n
{o nmero de col unas i gual ao nmero de f ontes mai s
1 para a pri mei ra col una f i xa, que temumtamanho i gual a 20}
Str i ngGr i d1. Col Count : = Scr een. Font s. Count + 1;
Str i ngGr i d1. Col Wi dt hs [ 0] : = 50;

for I : = 1 to Scr een. Fonts. Count do
begi n
/ / escreve o nome da f onte na pri mei ra l i nha
Str i ngGr i d1. Cel l s [ I , 0] : =
Scr een. Font s. St r i ngs [ I - 1] ;

{cal cul a o tamanho mxi mo exi gi do para a col una, obtendo a l argura do texto como mai or tamanho
da f onte nessa col una}
Str i ngGr i d1. Canvas. Font . Name : =
Str i ngGr i d1. Cel l s [ I , 0] ;
St r i ngGr i d1. Canvas. Font . Si ze : = 32;
St r i ngGr i d1. Col Wi dt hs [ I ] : =
Str i ngGr i d1. Canvas. Text Wi dt h ( AaBbYyZz );
end;
. . .
Na ltima parte do cdigo anterior, o programa calcula a largura de cada coluna. I sso feito
atravs da avaliao do espao ocupado pela string de texto personalizada AaBbYyZz, usando-se a fon-
te da coluna (escrita na primeira clula, Cel l s [ I , 0] ) e o maior tamanho de fonte usado pelo pro-
grama (32). Para calcular o espao exigido pelo texto, voc pode aplicar os mtodos Text Wi dt h e
Text Hei ght em um canvas, com a fonte correta selecionada.
As linhas, em vez disso, tm sempre uma largura 26 e uma altura que aumenta, calculada com
a frmula aproximada: 15 + I x 2. Na verdade, calcular o texto mais alto significa verificar a altura
do texto em cada coluna, certamente uma operao complexa demais para este exemplo. A frmula
aproximada funciona suficientemente bem, conforme voc pode ver executando o programa. Na pri-
meira clula de cada linha, o programa escreve o tamanho da fonte, que corresponde ao nmero da
linha mais sete.
A ltima operao armazenar a string AaBbYyZz em cada clula no fixa da grade. Para fazer
isso, o programa usa um lao f or aninhado. Espere usar laos for aninhados freqentemente quando
trabalhar com grades. Aqui est a segunda parte do mtodo For mCr eat e:
/ / def i ne o nmero de col unas
St r i ngGr i d1. RowCount : = 26;
for I : = 1 to 25 do
begi n
/ / escreve o nmero na pri mei ra col una
Str i ngGr i d1. Cel l s [ 0, I ] : = I nt ToSt r (I +7);
/ / conf i gura uma al tura que aumenta para as l i nhas
36 Dominando o Delphi 6 A Bibla
Str i ngGr i d1. RowHei ght s [ I ] : = 15 + I *2;
/ / i nsere texto padro emcada col una
for J : = 1 to St r i ngGr i d1. Col Count do
St r i ngGr i d1. Cel l s [ J, I ] : = AaBbYyZz
end;
Str i ngGr i d1. RowHei ght s [ 0] : = 25;
end;
Agora, podemos estudar o segundo mtodo, St r i ngGr i d1Dr awCel l , que corresponde ao evento
OnDr awCel l da grade. Esse mtodo tem vrios parmetros:
I Col e Row se referem clula que estamos pintando atualmente.
I Rect a rea da clula que vamos pintar.
I St at e o estado da clula, um conjunto de trs flags que podem estar ativos ao mesmo tem-
po: gdSel ect ed (a clula est selecionada), gdFocused (a clula tem o foco de entrada) e
gdFi xed (a clula est na rea fixa, que normalmente tem uma cor de fundo diferente). im-
portante conhecer o estado da clula, pois isso normalmente afeta sua sada.
O mtodo Dr awCel l pinta o texto do elemento da grade correspondente, com a fonte usada pela
coluna e com o tamanho usado para a linha. Aqui est a listagem desse mtodo:
procedure TFor m1. St r i ngGr i d1Dr awCel l (Sender : TObj ect ;
Col , Row: I nt eger ; Rect : TRect; Stat e: TGr i dDr awStat e);
begi n
/ / sel eci ona uma f onte, dependendo da col una
i f (Col = 0) or (Row = 0) then
St r i ngGr i d1. Canvas. Font. Name : = I
el se
St r i ngGr i d1. Canvas. Font. Name : =
St r i ngGr i d1. Cel l s [ Col , 0] ;

/ / sel eci ona o tamanho da f onte, dependendo da l i nha
i f Row = 0 then
St r i ngGr i d1. Canvas. Font. Si ze : = 14
el se
St r i ngGr i d1. Canvas. Font. Si ze : = Row + 7;

/ / sel eci ona a cor de f undo
i f gdSel ect ed i n St at e then
St r i ngGr i d1. Canvas. Br ush. Col or : = cl Hi ghl i ght
el se i f gdFi xed i n St at e then
St r i ngGr i d1. Canvas. Br ush. Col or : = cl Bt nFace
el se
St r i ngGr i d1. Canvas. Br ush. Col or : = cl Wi ndow;

/ / exi be o texto
Str i ngGr i d1. Canvas. Text Rect (
Rect , Rect . Lef t, Rect . Top,
St r i ngGr i d1. Cel l s [ Col , Row] );

/ / desenha o f oco
i f gdFocused i n Stat e then
St r i ngGr i d1. Canvas. Dr awFocusRect (Rect );
end;
37 Recursos grficos do Delphi
O nome da fonte recuperado pela linha 0 da mesma coluna. O tamanho da fonte calculado
somando-se 7 ao nmero da linha. As colunas fixas usam alguns valores padro. Tendo configurado
a fonte e seu tamanho, o programa seleciona uma cor para o fundo da clula, dependendo de seus
estados possveis: selecionada, fixa ou normal (isto , nenhum estilo especial). O valor do flag gdFo-
cused do estilo usado algumas linhas depois para desenhar o retngulo de foco tpico. Quando tudo
estiver configurado, o programa poder produzir alguma sada em si, desenhando o texto e, se neces-
srio, o retngulo de foco, com as duas ltimas instrues do mtodo St r i ngGr i d1Dr awCel l anterior.
DICA Par a desen har o t ext o n a cl u l a d a gr ade, u sam o s o m t o d o Text Rect do can vas, em vez d o m -
t o d o Text Out , m ai s com u m . O m o t i vo q u e Text Rect r ecor t a a sad a no r et n g ul o d ad o , evi t and o
o d esenh o f or a d essa r ea. Isso p ar t i cul ar m ent e i m p o r t an t e n o caso d e g r ad es, p oi s a sada d e
u m a cl ul a no d eve ul t r ap assar su as b o r das. Com o est am os p i n t an d o n o canvas da gr ade i n t ei r a,
q uand o est i ver m o s desen han d o um a cl u l a, p od em o s acab ar d an i f i can d o t am b m o co nt e do d e
cl u l as vi zi n has.
Como uma observao final, lembre-se de que, quando voc decidir desenhar o contedo da c-
lula de uma grade, no deve desenhar apenas a imagem padro, mas tambm fornecer uma sada di-
ferente para o item selecionado, desenhar o foco corretamente etc.
Minas em uma Grade
O componente StringGrid usa o array Cel l s para armazenar os valores dos elementos e tambm pos-
sui uma propriedade Obj ect s para armazenar dados personalizados de cada clula. O componente
DrawGrid, por sua vez no tem um armazenamento predefinido. Por esse motivo, o exemplo a seguir
define um array bidimensional para armazenar o valor das clulas da grade isto , do campo de repro-
duo.
O exemplo Mines um clone do jogo Campo minado includo com o Windows. Se voc nunca
jogou esse jogo, sugerimos que tente fazer isso e leia suas regras no arquivo de ajuda, pois fornece-
remos apenas uma descrio bsica. Quando o programa comea, ele apresenta um campo vazio (uma
grade) em que existem algumas minas escondidas. Dando um clique com o boto esquerdo do mouse
em uma clula, voc testa se h ou no uma mina nessa posio. Se voc encontrar uma mina, ela
explodir, e o jogo termina. Voc perde.
Se no houver mina na clula, o programa indicar o nmero de minas nas oito clulas vizinhas
a ela. Conhecendo o nmero de minas prximas clula, voc tem um bom palpite para a prxima
jogada. Para ajud-lo ainda mais, quando uma clula no possui minas na rea vizinha, o nmero de
minas dessas clulas apresentado automaticamente e, se uma delas no tiver minas nas vizinhanas,
o processo ser repetido. Assim, se voc tiver sorte, com um nico clique de mouse, poder descobrir
um bom nmero de clulas vazias (veja a Figura 16).
Quando voc achar que encontrou uma mina, basta dar um clique com o boto direito do mouse
na clula; isso colocar uma bandeira l. O programa no diz se sua inferncia est correta; a bandeira
apenas um indcio para suas tentativas futuras. Se posteriormente voc mudar de idia, pode dar no-
vamente um clique com o boto direito do mouse na clula, para remover a bandeira. Quando tiver
encontrado todas as minas, voc ganhar, e o jogo termina.
Essas so as regras do jogo. Agora, precisamos implement-las usando um componente Draw-
Grid como ponto de partida. Nesse exemplo, a grade fixa e no pode ser redimensionada ou modi-
ficada de qualquer maneira, durante a execuo. Na verdade, ela possui clulas quadradas de 30x30
pixels, que sero usadas para apresentar bitmaps do mesmo tamanho.
38 Dominando o Delphi 6 A Bibla
O cdigo desse programa complexo e no fcil encontrar um ponto de partida para descre-
v-lo. Por isso, inclumos mais comentrios que o normal no cdigo-fonte que acompanha o captulo,
para que voc possa percorr-lo a fim de entender o que ele faz. Contudo, descreveremos seus ele-
mentos mais importantes. Primeiro, os dados do programa so armazenados em dois arrays (declara-
dos como campos pr i vat e do formulrio):
Di spl ay: array [ 0 . . NI t ems - 1, 0 . . NI t ems - 1] of Bool ean;
Map: array [ 0 . . NI t ems - 1, 0 . . NI t ems - 1] of Char ;
O primeiro um array de valores boleanos que indicam se um item deve ser apresentado ou per-
manecer oculto. Note que o nmero de linhas e colunas desse array NI t ems. Voc pode mudar li-
vremente essa constante, mas deve redimensionar a grade de acordo. O segundo array, Map, contm
as posies das minas e bandeiras, e os nmeros das minas vizinhas. Ele usa cdigos de caractere, em
vez de um tipo de dados de enumerao correto, para utilizar os dgitos de 0 a 8 para indicar o n-
mero de minas em torno da clula. Aqui est uma lista dos cdigos:
I M: Mina oculta indica a posio de uma mina que o usurio ainda no encontrou.
I K: Mina conhecida indica a posio de uma mina j encontrada pelo usurio e que tem uma
bandeira.
I W: Mina incorreta indica uma posio onde o usurio colocou uma bandeira, mas onde no
existe mina.
I 0 a 8: Nmerodeminasindica o nmero de minas nas clulas vizinhas.
O primeiro mtodo a explorar For mCr eat e, executado na inicializao. Esse mtodo inicializa
vrios campos da classe de formulrio, preenche os dois arrays com valores padro (usando dois laos
f or aninhados) e depois estabelece as minas na grade. Para o nmero de vezes definido em uma cons-
tante (isto , o nmero de minas), o programa insere uma mina em uma posi o aleatria. Entretanto,
se j havia uma mina, o lao deve ser executado mais uma vez, pois o nmero final de minas no array
Map deve ser igual ao nmero solicitado. Caso contrrio, o programa nunca terminar, pois ele testa
quando o nmero de minas encontradas igual ao nmero de minas includas na grade. Aqui est o
cdigo do lao; ele pode ser executado mais do que NMi nes vezes, graas ao uso da varivel inteira
Mi nesToPl ace, que aumentada quando tentamos colocar uma mina sobre uma j existente:
FIGURA 16
O programa Mines aps um
nico clique de sorte. Um grupo
de clulas sem minas
apresentado simultaneamente.
39 Recursos grficos do Delphi
Randomi ze;
/ / col oca NMi nes mi nas que no se sobrepem
Mi nesToPl ace : = NMi nes;
whi l e Mi nesToPl ace > 0 do
begi n
X : = Random(NI t ems);
Y : = Random(NI t ems);
/ / se no houver uma mi na
i f Map [ X, Y] <> M then
begi n
/ / i nsere uma mi na
Map [ X, Y] : = M ;
Dec (Mi nesToPl ace)
end;
end;
O ltimo trecho do cdigo de inicializao calcula o nmero de minas vizinhas de cada clula
que no tem mina. I sso feito chamando-se o procedimento Comput eMi nes para cada clula. O cdigo
dessa funo bastante complexo, pois ele precisa considerar os casos especiais das minas junto a uma
ou duas bordas da grade. O efeito dessa chamada armazenar no array Map o caractere que representa
o nmero de minas vizinhas a cada clula.
O prximo procedimento lgico Dr awGr i d1Mous eDown. Esse mtodo primeiramente calcula a
clula em que foi dado o clique de mouse, com uma chamada ao mtodo MouseToCel l da grade. Em
seguida, existem trs trechos alternativos de cdigo: um pequeno para quando o jogo tiver terminado
e os outros dois para os botes do mouse. Quando o boto esquerdo do mouse pressionado, o pro-
grama verifica se existe uma mina (oculta ou no) e, se houver, ele apresenta uma mensagem e termina
com uma exploso (veja a Figura 17).
Se no houver mina, o programa configura o valor Di spl ay da clula como Tr ue e, se houver
um 0, ele iniciar o procedimento Fl oodZer os . Esse mtodo apresenta os oito itens prximos a uma
clula visvel que tenha o valor 0, repetindo a operao seguidamente, caso uma das clulas vizinhas
tambm tenha o valor 0. Essa chamada recursiva complexa, pois voc tem de fornecer um modo de
termin-la. Se houver duas clulas prximas, ambas tendo o valor 0, cada uma estar na rea vizinha
outra; portanto, elas poderiam continuar para sempre a pedir para que a outra clula exiba a si pr-
pria e s suas clulas vizinhas. Novamente, o cdigo complexo, e a melhor maneira de estud-lo pode
ser percorr-lo no depurador.
FIGURA 17
Ai! Voc pisou em uma mina!
40 Dominando o Delphi 6 A Bibla
Quando o usurio pressiona o boto direito do mouse, o programa muda o status da clula. A
ao do boto direito do mouse alternar a bandeira na tela, de modo que o usurio sempre possa
remover uma bandeira existente, caso ele ache que a deciso anterior esteja errada. Por isso, o status
de uma clula que contm uma mina pode mudar de M (mina oculta) para K (mina conhecida) e vice-
versa. Quando todas as minas tiverem sido encontradas, o programa terminar com uma mensagem
de felicitaes.
Um trecho muito importante do cdigo est no final do mtodo de resposta ao evento OnMou-
seDown. Sempre que o usurio d um clique em uma clula e seu contedo muda, essa clula deve ser
repintada. Se voc repintar a grade inteira, o programa ficar mais lento. Por isso, usamos a funo
de API do Windows I nval i dat eRect :
MyRect : = Dr awGr i d1. Cel l Rect (Col , Row);
I nval i dat eRect (Dr awGr i d1. Handl e, @MyRect , Fal se);
O ltimo mtodo importante Dr awGr i d1Dr awCel l . J usamos esse procedimento de pintura no
ltimo exemplo, de modo que voc deve se lembrar de que ele chamado para cada clula que precisa
de repintura. Fundamentalmente, esse mtodo extrai o cdigo correspondente clula, o que mostra
um bitmap correspondente, carregado a partir de um arquivo. Mais uma vez, preparamos um bitmap
para cada uma das imagens em um novo arquivo de recursos, que includo no projeto graas ao Pro-
ject Manager melhorado do Delphi 5.
Lembre-se de que, ao usar recursos, o cdigo tende a ser mais rpido do que o uso de arquivos
separados e, novamente, acabamos com um nico arquivo executvel para instalar. Os bitmaps tm
nomes correspondentes ao cdigo da grade, com um caractere (M) na frente, pois o nome 0 seria
invlido. Os bitmaps podem ser carregados e desenhados na clula com o seguinte cdigo:
Bmp. LoadFr omResour ceName (HI nst ance, M + Code);
Dr awGr i d1. Canvas. Dr aw (Rect . Lef t , Rect . Top, Bmp);
claro que isso ocorrer apenas se a clula estiver visvel isto , se Di s pl ay for Tr ue. Caso
contrrio, um bitmap indefinido padro ser apresentado. (O nome do bitmap UNDEF.) Carregar
os bitmaps a partir dos recursos toda vez parece lento, de modo que o programa poderia ter armaze-
nado todos os bitmaps em uma lista na memria, como fez o exemplo World2, j visto neste captulo.
Entretanto, desta vez, decidimos usar uma estratgia diferente, embora ligeiramente menos eficiente:
uma cache. I sso faz sentido, pois j usamos recursos em lugar de arquivos para acelerar as coisas.
A cache de bitmap do programa Mines pequena, pois ela tem apenas um elemento, mas sua
presena acelera o programa consideravelmente. O programa armazena o ltimo bitmap que usou e
seu cdigo; ento, sempre que ele precisar desenhar um novo item, se o cdigo for o mesmo, usar
o bitmap da cache. Aqui est a nova verso do cdigo anterior:
i f not (Code = Last Bmp) then
begi n
Bmp. LoadFr omResour ceName (HI nst ance, M + Code);
Last Bmp : = Code;
end;
Dr awGr i d1. Canvas. Dr aw (Rect . Lef t , Rect . Top, Bmp);
Aumentar o tamanho dessa cache certamente melhorar a velocidade do programa. Voc pode
considerar uma lista de bitmaps como uma cache grande, mas isso provavelmente intil, pois alguns
bitmaps (aqueles com nmeros altos) so raramente usados. Conforme voc pode ver, algumas me-
lhorias podem ser feitas para acelerar o programa, e muito tambm pode ser feito para melhorar sua
interface com o usurio. Se voc tiver entendido esta verso do programa, achamos que poder apri-
mor-lo consideravelmente.
41 Recursos grficos do Delphi
Usando TeeChart
O TeeChart um componente baseado na VCL, criado por David Berneda e licenciado para a Bor-
land, para a incluso nas verses Developer e Enterprise do Delphi. O componente TeeChart muito
complexo: o Delphi inclui um arquivo de ajuda e outros materiais de referncia para esse componente;
portanto, no perderemos tempo listando todos os seus recursos. Construiremos apenas dois exem-
plos. O TeeChart aparece em trs verses: o componente independente (na pgina Additional da pa-
leta de componentes), a verso consciente de dados (na pgina Data Controls) e a verso relatrio
(na pgina QReport). O Delphi Enterprise tambm inclui um controle DecisionChart na pgina De-
cision Cube da paleta. A verso consciente de dados do TeeChart foi apresentada no Captulo 13 deste
livro, e a usaremos novamente em um exemplo orientado Web.
NOTA cl ar o q ue ser i a m ai s si m pl es co nst r ui r u m exem pl o usand o o TeeChar t Wi zar d , m as ver t o do s os
passo s d ar a voc um m el h or ent en di m ent o d a est r u t ur a d esse com p o nent e.
O componente TeeChart fornece a estrutura bsica para a produo de grficos, atravs de uma
base complexa de grficos e uma srie de classes e do continer visual para grficos (o controle em
si). Os grficos so objetos da classe TChar t Ser i es ou de classes derivadas. Uma vez que voc tenha
colocado o componente TeeChart em um formulrio, deve criar uma ou mais sries. Para fazer isso,
voc pode abrir o Chart Component Editor: selecione o componente, d um clique com o boto di-
reito do mouse para apresentar o menu local do projetista de formulrio e escolha o comando Edit
Chart. Agora, pressione o boto Add (na pgina Sries da guia Chat) e escolha o grfico (ou srie)
que voc deseja incluir dentre os muitos disponveis (conforme voc pode ver na Figura 18).
Assim que voc cria uma nova srie, um novo objeto de uma subclasse TChar t Ser i es includo
em seu formulrio. Esse o mesmo comportamento do componente MainMenu, que inclui objetos
da classe TMenuI t emno formulrio. Voc pode ento editar as propriedades do objeto TSer i es no
Chart Component Editor, ou pode selecionar o objeto TChar t Ser i es no Object I nspector (com a cai-
xa de combinao Object Selector) e editar suas muitas propriedades.
As diferentes subclasses TChar t Ser i es isto , os diferentes tipos de grfico tm diferentes
propriedades e mtodos (embora alguns deles sejam comuns a mais de uma subclasse). Lembre-se de
que um grfico pode ter vrias sries: se elas forem todas do mesmo tipo, provavelmente vo se in-
FIGURA 18
A TeeChart Gallery permite que voc
escolha o tipo de grfico ou uma srie.
42 Dominando o Delphi 6 A Bibla
tegrar melhor, como no caso de vrias barras. De qualquer forma, voc tambm pode ter um layout
complexo com grficos de diferentes tipos visveis simultaneamente. s vezes, essa uma opo ex-
tremamente poderosa.
Construindo um Primeiro Exemplo
Para construir este exemplo, colocamos um componente TeeChart em um formulrio e depois sim-
plesmente inclumos quatro sries 3D Bar isto , quatro objetos da classe TBar Ser i es . Em seguida,
configuramos algumas propriedades, como o ttulo do grfico etc. Aqui est um resumo dessas infor-
maes, extradas da descrio textual do formulrio:
obj ect Char t 1: TChar t
Ani matedZoom= Tr ue
Ti t l e. Text. St r i ngs = (
Si mpl e TeeChart Demo f or Masteri ng Del phi )
Bevel Out er = bvLower ed
obj ect Ser i es1: TBar Ser i es
Ser i esCol or = cl Red
Mar ks. Vi si bl e = Fal se
end
obj ect Ser i es2: TBar Ser i es
Ser i esCol or = cl Gr een
Mar ks. Vi si bl e = Fal se
end
obj ect Ser i es3: TBar Ser i es
Ser i esCol or = cl Yel l ow
Mar ks. Vi si bl e = Fal se
end
obj ect Ser i es4: TBar Ser i es
Ser i esCol or = cl Bl ue
Mar ks. Vi si bl e = Fal se
end
end
Em seguida, inclumos no formulrio uma grade de string e um boto de seleo chamado Up-
date. Esse boto usado para copiar os valores numricos da grade de string no grfico. A grade
baseada em uma matriz de 5x4, assim como uma linha e uma coluna para os ttulos. Aqui est sua
descrio textual:
obj ect St r i ngGr i d1: TSt r i ngGr i d
Col Count = 6
Def aul t Col Wi dth = 50
Opt i ons = [ goFi xedVer tLi ne, goFi xedHor zLi ne,
goVer t Li ne, goHor zLi ne, goEdi t i ng]
Scr ol l Bar s = ssNone
OnGet Edi t Mask = Str i ngGr i d1GetEdi tMask
end
O valor 5 para a propriedade RowCount o padro, e ele no aparece na descrio textual. (O
mesmo vale para o valor 1 para as propriedades Fi xedCol s e Fi xedRows.) Um elemento importante
dessa grade de strings a mscara de edio usada por todas as suas clulas. I sso ajustado usando-se
o evento OnGet Edi t Mask:
procedure TFor m1. St r i ngGr i d1Get Edi t Mask(Sender : TObj ect ;
ACol , ARow: Longi nt; var Val ue: st r i ng);
43 Recursos grficos do Delphi
begi n
/ / mscara de edi o para as cl ul as da grade
Val ue : = 09; 0 ;
end;
Na verdade, existe mais um componente importante, uma caixa de verificao usada para alternar
a visibilidade dos marcadores da srie. (Os marcadores so pequenos rtulos amarelos que descrevem
cada valor; voc precisar executar o programa para v-los.) Voc pode ver o formulrio em tempo
de projeto na Figura 19. Neste caso, as sries so populadas por valores aleatrios; esse um excelente
recurso do componente, pois ele permite que voc veja uma prvia da sada sem introduzir dados em si.
Incluindo Dados no Grfico
Agora, basta inicializarmos os dados da grade de string e copi-los nas sries do grfico. I sso ocorre
no manipulador do evento OnCr eat e do formulrio. Esse mtodo preenche os itens fixos da grade e
os nomes das sries, em seguida, preenche a parte relativa aos dados da grade de strings e, por fim
chama o manipulador do evento OnCl i ck do boto Update, para atualizar o grfico:
procedure TFor m1. For mCr eat e(Sender : TObj ect );
var
I , J: I nt eger ;
begi n
wi th St r i ngGr i d1 do
begi n
{preenche a col una e a f i l ei ra f i xas,
e os nomes da sri es do grf i co}
for I : = 1 to 5 do
Cel l s [ I , 0] : = For mat ( Group%d , [ I ] );
for J : = 1 to 4 do
begi n
Cel l s [ 0, J] : = For mat ( Seri es%d , [ J] );
Char t 1. Ser i es [ J- 1] . Ti t l e : = For mat ( Seri es%d , [ J] );
end;

FIGURA 19
O exemplo Graph1, baseado
no componente TeeChart, em
tempo de projeto.
44 Dominando o Delphi 6 A Bibla
/ / preenche a grade comval ores al eatri os
Randomi ze;
for I : = 1 to 5 do
for J : = 1 to 4 do
Cel l s [ I , J] : = I nt ToSt r (Random(100));
end; / / com

/ / atual i za o grf i co
Updat eBut tonCl i ck (Sel f);
end;
Podemos acessar as sries usando o nome do componente (como Ser i es 1) ou usando a proprie-
dade de array Ser i es do grfico, como em Char t 1. Ser i es[ J- 1] . Nessa expresso, note que os dados
em si da grade de strings comeam na linha e na coluna 1 a primeira linha e coluna, indicadas pelo
ndice zero, so usadas para os elementos fixos e o array de grficos Ser i es baseado em zero.
Outro exemplo de atualizao de cada srie est presente no manipulador do evento OnCl i ck da
caixa de seleo; esse mtodo alterna a visibilidade dos marcadores:
procedure TFor m1. ChBoxMar ksCl i ck(Sender : TObj ect );
var
I : I nteger ;
begi n
for I : = 1 to 4 do
Char t 1. Ser i es [ I - 1] . Mar ks. Vi si bl e : =
ChBoxMar ks. Checked;
end;
Mas o cdigo realmente interessante est no mtodo Updat eBut t onCl i ck, que atualiza o grfico.
Para fazer isso, o programa primeiro remove os dados existentes de cada grfico e depois inclui novos
dados (ou pontosdedados, para usar o jargo correto):
procedure TFor m1. Updat eBut t onCl i ck(Sender : TObj ect );
var
I , J: I nt eger ;
begi n
for I : = 1 to 4 do
begi n
Char t 1. Ser i es [ I - 1] . Cl ear ;
for J : = 1 to 5 do
Char t 1. Ser i es [ I - 1] . Add (
St r ToI nt (St r i ngGr i d1. Cel l s [ J, I ] ),
, Char t1. Ser i es [ I - 1] . Ser i esCol or );
end;
end;
Os parmetros do mtodo Add (usados quando voc no quer especificar um valor X, mas apenas
um valor Y) so o valor em si, o rtulo e a cor. Neste exemplo, o rtulo no usado; portanto, sim-
plesmente o omitimos. Poderamos ter usado o valor padro cl TeeCol or para obter a cor correta das
sries. Voc poderia usar cores especficas para indicar diferentes intervalos de dados.
Uma vez que voc tenha construdo o grfico, o TeeChart permite muitas opes de visualizao. Voc
pode facilmente ampliar a visualizao (basta indicar a rea com o boto esquerdo do mouse), reduzir (usan-
do o mouse do modo oposto, arrastando em direo ao canto superior esquerdo) e usar o boto direito
do mouse para mover a visualizao. Voc pode ver um exemplo de zoom na Figura 20.
45 Recursos grficos do Delphi
Criando Sries Dinamicamente
O exemplo Graph1 mostra alguns dos recursos do componente TeeChart, mas ele tem por base um
nico tipo de grfico fixo. Poderamos t-lo aprimorado, permitindo alguma personalizao da forma
das barras verticais; em vez disso, escolhemos uma estratgia mais geral, permitindo ao usurio esco-
lher diferentes tipos de sries (grficos).
I nicialmente, o componente TeeChart tem os mesmos atributos que no exemplo anterior. Mas
agora o formulrio tem quatro caixas de combinao, uma para cada linha da grade de strings. Cada
caixa de combinao tem quatro valores (Line, Bar, Area e Point), correspondentes aos quatro tipos
de sries que queremos manipular. Para manipular essas caixas de combinao de uma maneira mais
flexvel no cdigo, inclumos um array desses controles nos campos privados do formulrio:
pri vate
Combos: array [ 0. . 3] of TComboBox;
Esse array preenchido com o componente em si no mtodo For mCr eat e, que tambm seleciona
o item inicial de cada um deles. Aqui est o novo cdigo de For mCr eat e:
/ / preenche o array Combos
Combos [ 0] : = ComboBox1;
Combos [ 1] : = ComboBox2;
Combos [ 2] : = ComboBox3;
Combos [ 3] : = ComboBox4;
/ / mostra o ti po de grf i co i ni ci al
for I : = 0 to 3 do
Combos [ I ] . I t emI ndex : = 1;
DICA Esse exem p l o dem o nst r a um m od o co m u m de cr i ar u m ar r ay d e co nt r ol es n o Del p hi , al g o q ue os
p r og r am ad or es Vi sual Basi c d esej am f r eq ent em en t e. Na ver d ad e, o Del p hi t o f l exvel q ue o s ar -
r ays d e co nt r o l es no so i n cor p o r ad os; vo c p od e cr i - l os co m o q ui ser . Essa est r at gi a con t a com
o f at o d e q ue g er al m ent e p o ssvel associ ar o m esm o m an i p ul ad or de even t o a di f er ent es event os,
al go q u e o VB no p er m i t e f azer .
FIGURA 20
O formulrio do exemplo Graph1 em
tempo de execuo. Note que demos
um zoom no grfico.
46 Dominando o Delphi 6 A Bibla
Todas essas caixas de combinao compartilham o mesmo manipulador de eventos OnCl i ck, que
destri cada uma das sries atuais do grfico, cria as novas quando solicitadas e depois atualiza suas
propriedades e dados:
procedure TFor m1. ComboChange(Sender : TObj ect );
var
I : I nteger ;
Ser i esCl ass: TChar t Ser i esCl ass;
NewSer i es: TChar t Ser i es;
begi n
/ / destri as sri es exi stentes ( emordemi nversa)
for I : = 3 downt o 0 do
Char t 1. Ser i es [ I ] . Fr ee;
/ / cri a as novas sri es
for I : = 0 to 3 do
begi n
case Combos [ I ] . I t emI ndex of
0: Ser i esCl ass : = TLi neSer i es;
1: Ser i esCl ass : = TBar Ser i es;
2: Ser i esCl ass : = TAr eaSer i es;
el se / / 3: e padro
Ser i esCl ass : = TPoi nt Ser i es;
end;
NewSer i es : = Ser i esCl ass. Cr eat e (sel f);
NewSer i es. Par ent Char t : = Char t 1;
NewSer i es. Ti t l e : =
For mat ( Seri es %d , [ I + 1] );
end;
/ / atual i za os marcadores e os dados
ChBoxMar ksCl i ck (sel f);
Updat eBut tonCl i ck (sel f);
Modi f i ed : = Tr ue;
end;
A parte central desse cdigo a instruo cas e, que armazena uma nova classe na varivel de
referncia de classe Ser i es Cl as s, usada para criar os novos objetos de srie e configurar as proprie-
dades Par ent Char t e Ti t l e de cada um. Tambm poderamos ter usado uma chamada ao mtodo
AddSer i es do grfico em cada desvio da instruo case e depois configurado a propriedade Ti t l e
com outro lao f or . Na verdade, uma chamada como
Char t 1. AddSer i es (TBar Ser i es. Cr eat e (sel f));
cria os objetos da srie e ao mesmo tempo configura seu grfico progenitor.
Note que essa nova verso do programa permite que voc mude o tipo de grfico para cada srie
independentemente. Voc pode ver um exemplo do efeito resultante na Figura 21.
Por fim o exemplo Graph2 permite salvar os dados atuais que est apresentando em um arquivo
e carregar os dados de arquivos existentes. O programa tem uma varivel booleana Modi f i ed, usada
para saber se o usurio mudou qualquer um dos dados. O suporte a arquivos baseado em streams
e no particularmente complexo, pois o nmero de elementos a salvar fixo (todos os arquivos tm
o mesmo tamanho). Aqui esto os dois mtodos conectados aos itens de menu Open e Save:
procedure TFor m1. Open1Cl i ck(Sender : TObj ect );
var
LoadStr eam: TFi l eSt r eam;
I , J, Val ue: I nt eger ;
47 Recursos grficos do Delphi
begi n
i f OpenDi al og1. Execut e then
begi n
Cur r entFi l e : = OpenDi al og1. Fi l ename;
Capt i on : = Graph [ + Cur r ent Fi l e + ] ;
/ / carrega do arqui vo atual
LoadSt r eam: = TFi l eSt r eam. Cr eate (
Cur r ent Fi l e, f mOpenRead);
try
/ / l o val or de cada el emento de grade
for I : = 1 to 5 do
for J : = 1 to 4 do
begi n
LoadSt r eam. Read (Val ue, si zeof (I nt eger ));
St r i ngGr i d1. Cel l s [ I , J] : = I nt ToSt r (Val ue);
end;
/ / carrega o status da cai xa de sel eo e das cai xas de combi nao
LoadSt r eam. Read (Val ue, si zeof (I nt eger ));
ChBoxMar ks. Checked : = Bool ean(Val ue);
for I : = 0 to 3 do
begi n
LoadSt r eam. Read (Val ue, si zeof (I nt eger ));
Combos [ I ] . I temI ndex : = Val ue;
end;
fi nal l y
LoadSt r eam. Fr ee;
end;
/ / di spara atual i zar eventos
ChBoxMar ksCl i ck (Sel f);
ComboChange (Sel f);
Updat eBut t onCl i ck (Sel f);
Modi f i ed : = Fal se;
end;
end;
procedure TFor m1. Save1Cl i ck(Sender : TObj ect );
var
SaveStr eam: TFi l eSt r eam;
I , J, Val ue: I nt eger ;
begi n
i f Modi f i ed then
i f Cur r ent Fi l e = then / / chama "Sal var como"
SaveAs1Cl i ck (Sel f)
el se
begi n
/ / sal va o arqui vo atual
SaveSt r eam: = TFi l eSt r eam. Cr eat e (
Cur r ent Fi l e, f mOpenWr i t e or f mCr eat e);
try
/ / escreve o val or de cada el emento da grade
for I : = 1 to 5 do
for J : = 1 to 4 do
begi n
Val ue : = St r ToI nt Def (Tr i m(
St r i ngGr i d1. Cel l s [ I , J] ), 0);
SaveStr eam. Wr i t e (Val ue, si zeof (I nt eger ));
48 Dominando o Delphi 6 A Bibla
end;
/ / sal va a cai xa de sel eo e as cai xas de combi nao
Val ue : = I nt eger (C BoxMar ks. Checked);
SaveSt r eam. Wr i t e (Val ue, si zeof (I nt eger ));
for I : = 0 to 3 do
begi n
Val ue : = Combos [ I ] . I temI ndex;
SaveStr eam. Wr i t e (Val ue, si zeof (I nt eger ));
end;
Modi f i ed : = Fal se;
fi nal l y
SaveSt r eam. Fr ee;
end;
end;
end;
Um Grfico de Banco de Dados na Web
No Captulo 22 deste livro, vimos como criar uma figura simples e retorn-la a partir de um aplicativo
CGI . Podemos aplicar a mesma estratgia para retornar um grfico complexo e dinmico, construdo
com o componente TDBChar t . Usar esse componente na memria um pouco mais complexo do que
configurar todas as suas propriedades em tempo de projeto, pois voc ter que configurar as proprie-
dades no cdigo Pascal. (Voc no pode usar um componente visual, como um DBChart, em um m-
dulo Web ou em qualquer outro mdulo de dados.)
No aplicativo I SAPI WebChart, usamos a tabela Country.DB para produzir um grfico de pizza
com a rea e a populao dos pases da Amrica, como no exemplo ChartDb do Captulo 13 deste
livro. Os dois grficos so gerados por duas aes diferentes, indicadas pelos caminhos /popul at i on
e /ar ea. Como a maior parte do cdigo usada mais de uma vez, o reunimos nos eventos OnCr eat e
e OnAf t er Di s pat ch do mdulo da Web.
FIGURA 21
Vrios tipos de grficos ou sries de
grfico apresentados pelo exemplo
Graph2.
49 Recursos grficos do Delphi
ALERTA Da m anei r a com o est escr i t o , est e p r og r am a no of er ece sup o r t e a usur i os co ncor r ent es. Vo c
pr eci sar i n cl u i r al g um c di go d e l i nh a de execuo (t hr ead i n g ) e si n cr o ni sm o nessa DLL ISAPI,
par a f az- l o f u nci on ar co m vr i o s u sur i os si m u l t aneam en t e. Um a al t er nat i va col ocar t od o o
cd i g o no s m an i p ul ad or es d e event os Act i on , par a qu e n enh um o bj et o g l ob al sej a com par t i l h a-
do en t r e vr i os p ed i d o s. Ou , en t o , vo c p o de t r an sf or m - l o em um ap l i cat i vo CGI.
O mdulo de dados tem um objeto tabela, que corretamente inicializado em tempo de projeto,
e trs campos privados:
pri vate
Char t : TDBChar t ;
Ser i es: TPi eSer i es;
I mage: TI mage;
Os objetos correspondentes a esses campos so criados junto ao mdulo da Web (e usados por
chamadas subseqentes):
procedure TWebModul e1. WebModul e1Cr eat e(Sender : TObj ect );
begi n
/ / abre a tabel a de banco de dados
Tabl e1. Open;
/ / cri a o grf i co
Char t : = TDBChar t . Cr eat e (ni l );
Char t . Wi dth : = 600;
Char t . Hei ght : = 400;
Char t . Axi sVi si bl e : = Fal se;
Char t . Legend. Vi si bl e : = Fal se;
Char t . Bot tomAxi s. Ti t l e. Capt i on : = Name ;
/ / cri a a sri e de pi zza
Ser i es : = TPi eSer i es. Cr eate (Char t);
Ser i es. Par ent Char t : = Char t ;
Ser i es. DataSour ce : = Tabl e1;
Ser i es. XLabel sSour ce : = Name ;
Ser i es. Ot her Sl i ce. St yl e : = poBel owPer cent ;
Ser i es. Ot her Sl i ce. Text : = Others ;
Ser i es. Ot her Sl i ce. Val ue : = 2;
Char t . AddSer i es (Ser i es);
/ / cri a o bi tmap de memri a
I mage : = TI mage. Cr eat e (ni l );
I mage. Wi dth : = Char t . Wi dt h;
I mage. Hei ght : = Char t . Hei ght ;
end;
O prximo passo executar o manipulador da ao especfica, que configura a srie do grfico
de pizza para o campo de dados especfico e atualiza alguns ttulos:
procedure TWebModul e1. WebModul e1Act i onPopul at i onAct i on(
Sender : TObj ect ; Request : TWebRequest ;
Response: TWebResponse; var Handl ed: Bool ean);
begi n
/ / conf i gura os val ores espec f i cos
Char t . Ti t l e. Text . Cl ear ;
Char t . Ti t l e. Text . Add ( Popul ati on of Countri es );
Char t . Lef tAxi s. Ti tl e. Capt i on : = Popul ati on ;
Ser i es. Ti tl e : = Popul ati on ;
Ser i es. Pi eVal ues. Val ueSour ce : = Popul ati on ;
end;
50 Dominando o Delphi 6 A Bibla
I sso cria o componente DBChart correto na memria. O ltimo passo, novamente comum para
as duas aes, salvar o grfico em uma imagem de bitmap e depois format-lo como um JPEG em
um stream, para ser posteriormente retornado do aplicativo no lado do servidor. O cdigo parecido
com aquele do exemplo anterior:
procedure TWebModul e1. WebModul e1Af t er Di spat ch(
Sender : TObj ect ; Request : TWebRequest ;
Response: TWebResponse; var Handl ed: Bool ean);
var
Jpeg: TJpegI mage;
MemSt r : TMemor ySt r eam;
begi n
/ / pi nta o grf i co no bi tmap de memri a
Char t . Dr aw (I mage. Canvas, I mage. BoundsRect);
/ / cri a o j peg e copi a a i magemnel e
Jpeg : = TJpegI mage. Cr eat e;
try
Jpeg. Assi gn (I mage. Pi ctur e. Bi t map);
MemSt r : = TMemor yStr eam. Cr eat e;
/ / sal va emumstreame o retorna
Jpeg. SaveToSt r eam(MemSt r );
MemSt r . Posi t i on : = 0;
Response. Cont ent Type : = i mage/ j peg ;
Response. Cont ent St r eam: = MemStr ;
Response. SendResponse;
fi nal l y
Jpeg. Fr ee;
end;
end;
O resultado, visvel na Fi gura 22, certamente interessante. Como opo, voc pode estender
esse aplicativo vinculando-o a uma tabela HTML que mostra os dados do banco de dados. Basta es-
crever um programa com uma ao principal que retorne a tabela HTML e uma referncia figura
incorporada, que ser retornada por uma segunda ativao da DLL I SAPI com um caminho diferente.
FIGURA 22
O JPEG com o grfico de
populao gerado pelo
aplicativo WebChart.
51 Recursos grficos do Delphi
Usando Meta-Arquivos
Os formatos de bitmap abordados anteriormente neste captulo armazenavam o status de cada pixel
de um bitmap, embora eles normalmente compactassem a informao. Um tipo totalmente diferente
de formato grfico representado pelos formatos orientados a vetores. Nesse caso, o arquivo arma-
zena a informao exigida para recriar a figura, como os pontos inicial e final de cada linha ou o cl-
culo matemtico que define uma curva. Existem muitos formatos de arquivo orientados a vetores
diferentes, mas o nico suportado pelo sistema operacional Windows o WMF (Windows Metafile
Format) formato de meta-arquivos do Windows. Esse formato foi estendido no Win32 para o
EMF (Extended Metafile Format) formato de meta-arquivo estendido, que armazena informaes
extras relacionadas aos modos de mapeamento e ao sistema de coordenadas.
Um meta-arquivo do Windows basicamente uma srie de chamadas s funes primitivas da
GDI . Depois de ter armazenado a seqncia de chamadas, voc pode execut-las, reproduzindo as fi-
guras. O Delphi suporta meta-arquivos do Windows atravs das classes TMet af i l e e TMet aFi l eCan-
vas. Construir um exemplo muito simples.
A classe TMet af i l e usada para manipular o arquivo em si, com mtodos para carregar e salvar
os arquivos, e propriedades que determinam os principais recursos do arquivo. Uma delas a proprie-
dade Enhanced, que determina o tipo de formato de meta-arquivo. Note que, quando o Windows est
lendo um arquivo, a propriedade Enhanced configurada dependendo da extenso de arquivo
WMF para meta-arquivos do Windows 3.1 e EMF para os meta-arquivos estendidos do Win32.
Para gerar um meta-arquivo, voc pode usar um objeto da classe TMet af i l eCanvas , conectado
ao arquivo atravs de seus construtores, como se v no seguinte trecho de cdigo:
Wmf : = TMet af i l e. Cr eat e;
Wmf Canvas : = TMet af i l eCanvas. Cr eat eWi t hComment (
Wmf , 0, Marco , Demo metaf i l e );
Uma vez que voc tenha criado os dois objetos, pode pintar sobre o objeto canvas com chamadas
regulares e, no final, salvar o meta-arquivo conectado a um arquivo fsico.
Uma vez que voc tenha o meta-arquivo (um novo que acabou de criar ou um construdo com
outro programa), pode apresent-lo em um componente I mage ou pode simplesmente chamar os m-
todos Dr aw ou St r et chDr aw de qualquer canvas.
No exemplo WmfDemo, escrevemos algum cdigo simples, apenas para mostrar a voc os fun-
damentos dessa estratgia. O manipulador do evento OnCr eat e do formulrio cria o meta-arquivo es-
tendido, um objeto simples que usado para operaes de leitura e escrita:
procedure TFor m1. For mCr eat e(Sender : TObj ect );
begi n
Wmf : = TMet af i l e. Cr eate;
Wmf . Enhanced : = Tr ue;
Randomi ze;
end;
O formulrio do programa tem dois botes e os componentes PaintBox, mais uma caixa de se-
leo. O primeiro boto cria um meta-arquivo, gerando uma srie de linhas parcialmente aleatrias.
O resultado aparece no primeiro componente PaintBox e tambm salvo em um arquivo fixo:
procedure TFor m1. Bt nCr eat eCl i ck(Sender : TObj ect );
var
Wmf Canvas: TMet af i l eCanvas;
X, Y: I nt eger ;
begi n
52 Dominando o Delphi 6 A Bibla
/ / cri a o canvas vi rtual
Wmf Canvas : = TMet af i l eCanvas. Cr eateWi th Comment (
Wmf , 0, Marco , Demo metaf i l e );

try
/ / l i mpa o f undo
Wmf Canvas. Br ush. Col or : = cl Whi te;
Wmf Canvas. Fi l l Rect (Wmf Canvas. Cl i pRect );

/ / desenha 400 l i nhas
for X : = 1 to 20 do
for Y : = 1 to 20 do
begi n
Wmf Canvas. MoveTo (15 * (X + Random(3)), 15 * (Y + Random(3)));
Wmf Canvas. Li neTo (45 * Y, 45 * X);
end;
fi nal l y
/ / f i nal i za a operao de desenho
Wmf Canvas. Fr ee;
end;

/ / apresenta o desenho atual e o sal va
Pai nt Box1. Canvas. Dr aw (0, 0, Wmf );
Wmf . SaveToFi l e (Ext r act Fi l ePath (
Appl i cat i on. ExeName) + test. emf );
end;
ALERTA Se vo c desen har ou sal var o m et a- ar q ui vo ant es q ue o canvas d o m et a- ar q ui vo con ect ad o sej a
f ech ad o ou d est r u d o, essas op er a es n o pr o d uzi r o n en hu m ef ei t o ! Esse o m o t i vo p el o q ual
cham am o s o m t od o Fr ee ant es d e ch am ar Dr aw e SaveToFi l e.
Recarregar e repintar o meta-arquivo ainda mais simples:
procedure TFor m1. Bt nLoadCl i ck(Sender : TObj ect );
begi n
/ / carrega o meta- arqui vo
Wmf . LoadFr omFi l e (Ext r act Fi l ePat h (
Appl i cat i on. ExeName) + test. emf );

/ / desenha- o ou esti ca- o
i f cbSt r etched. Checked then
Pai nt Box2. Canvas. Str et chDr aw Pai nt Box2. Canvas. Cl i pRect , Wmf )
el se
Pai nt Box2. Canvas. Dr aw (0, 0, Wmf );
end;
Note que voc pode reproduzir exatamente o mesmo desenho, mas tambm modific-lo com a
chamada a St r et chDr aw. (O resultado dessa operao aparece na Figura 23.) Essa operao diferente
do esticamento de um bitmap, que normalmente degrada ou modifica a imagem, pois aqui estamos
mudando a escala atravs da alterao do mapeamento de coordenadas. I sso significa que, ao imprimir
um meta-arquivo, voc pode estic-lo para preencher a pgina inteira com um efeito muito bom, algo
muito difcil de fazer com um bitmap.
53 Recursos grficos do Delphi
Girando Texto
Neste captulo, abordamos muitos exemplos diferentes do uso de bitmaps e criamos figuras de muitos
tipos. Entretanto, o tipo mais importante de figura com o qual normalmente tratamos nos aplicativos
Delphi texto. Na verdade, mesmo quando apresenta um rtulo ou o texto de uma caixa de edio,
o Windows ainda o pinta da mesma maneira como qualquer outro elemento grfico. J apresentamos
um exemplo de pintura de fonte anteriormente neste captulo, no exemplo FontGrid. Agora, vamos
voltar a esse assunto com uma estratgia ligeiramente menos comum.
Quando voc pinta texto no Windows, no h meios de indicar a direo da fonte: o Windows
parece desenhar o texto apenas horizontalmente. Entretanto, para sermos precisos, o Windows dese-
nha o texto na direo aceita por sua fonte, que horizontal como padro. Por exemplo, podemos
mudar o texto apresentado pelos componentes de um formulrio, modificando a fonte do prprio for-
mulrio, como fizemos no exemplo SideText. Na verdade, voc no pode modificar uma fonte, mas
pode criar uma nova fonte, semelhante a uma j existente:
procedure TFor m1. For mCr eat e(Sender : TObj ect );
var
ALogFont : TLogFont;
hFont : THandl e;
begi n
ALogFont . l f Hei ght : = Font . Hei ght ;
ALogFont . l f Wi dt h : = 0;
ALogFont . l f Escapement : = - 450;
ALogFont . l f Or i ent at i on : = - 450;
ALogFont . l f Wei ght : = f w_Demi Bol d;
ALogFont . l f I t al i c : = 0; / / f al so
ALogFont . l f Under l i ne : = 0; / / f al so
ALogFont . l f St r i keOut : = 0; / / f al so
ALogFont . l f Char Set : = Ansi _Char Set;
ALogFont . l f OutPr eci si on : = Out_Def aul t_Pr eci s;
ALogFont . l f Cl i pPr eci si on : = Cl i p_Def aul t _Pr eci s;
ALogFont . l f Qual i t y : = Def aul t _Qual i t y;
ALogFont . l f Pi tchAndFami l y : = Def aul t _Pi t ch;
FIGURA 23
A sada do exemplo WmfDemo
com um meta-arquivo alongado.
54 Dominando o Delphi 6 A Bibla
Str Copy (ALogFont . l f FaceName, PChar (Font . Name));
Font : = Cr eat eFontI ndi r ect (ALogFont );
Font . Handl e : = Font ;
end;
Esse cdigo produziu o efeito desejado no rtulo do formulrio do exemplo, mas, se voc incluir
outros componentes nele, o texto geralmente ser impresso fora da parte visvel do componente. Em
outras palavras, voc precisar fornecer esse tipo de suporte dentro de componentes se quiser que tudo
aparea corretamente. Para os rtulos, entretanto, voc pode evitar criar um novo componente; em
vez disso, basta mudar a fonte associada ao Canvas do formulrio (e no o formulrio inteiro) e usar
os mtodos de desenho de texto padro. O exemplo SideText muda a fonte do Canvas no mtodo
OnPai nt , que semelhante a OnCr eat e:
procedure TFor m1. For mPai nt (Sender : TObj ect );
var
ALogFont : TLogFont ;
hFont : THandl e;
begi n
ALogFont . l f Hei ght : = Font . Hei ght ;
ALogFont . l f Escapement : = 900;
ALogFont . l f Or i ent ati on : = 900;
. . .
hFont : = Cr eat eFontI ndi r ect (ALogFont );
Canvas. Font. Handl e : = hFont ;
Canvas. TextOut (0, Cl i ent Hei ght, Hel l o );
end;
A orientao da fonte tambm modificada por um terceiro manipulador de eventos, associado
a um temporizador. Seu efeito girar o formulrio com o passar do tempo, e seu cdigo muito
parecido com o procedimento anterior, com exceo do cdigo para determinar o escapede fonte (o
ngulo da rotao da fonte):
ALogFont . l f Escapement : = - (Get Ti ckCount di v 10) mod 3600;
Com essas trs tcnicas diferentes de rotao de fonte (ttulo de rtulo, texto pintado e texto
girando com o passar do tempo), o formulrio do programa SideText em tempo de execuo seme-
lhante Fi gura 24.
FIGURA 24
Os efeitos do exemplo SideText,
com texto realmente girando.
55 Recursos grficos do Delphi
O Que Vem a Seguir?
Neste captulo, exploramos vrias tcnicas diferentes que voc pode usar no Delphi para produzir sada
grfica. Usamos o Canvas do formulrio, bitmaps, meta-arquivos, componentes grficos, grades e ou-
tras tcnicas. Certamente existem muito mais tcnicas relacionadas programao grfica no Delphi
e no Windows em geral, incluindo a grande rea da programao de jogos de alta velocidade.
Permitindo que voc se vincule diretamente API do Windows, o suporte do Delphi a figuras
certamente amplo. Entretanto, a maioria dos programadores Delphi nunca faz chamadas diretas ao
sistema GDI , mas, em vez disso, conta com o suporte oferecido pelos componentes existentes no
Delphi. Esse assunto j foi apresentado no Captulo 12 do livro.
Se voc j leu o livro, esperamos que tambm tenha aproveitado este captulo extra. Caso tenha
comeado por este captulo, o restante do livro tem muito a oferecer, mesmo no contexto dos recursos
grficos, mas certamente no apenas limitado a isso. Consulte os endereos www.pearsonedbra-
sil.com/delphi6_cantu para obter mais informaes sobre o livro e www. mar cocant u. com(em ingls)
para pegar o cdigo-fonte gratuito deste captulo e do livro inteiro (que tambm est no CD que
acompanha o livro).
56 Dominando o Delphi 6 A Bibla

Você também pode gostar