oicìliS © - Assembly para Iniciantes
(ver 1.0 de 26.01.02)

PINTANDO TEXTO

 

Neste tutorial vamos lidar com texto. Você deve estar pensando - "Escrever um texto ? Grande coisa, e daí ?". É que no Windows não se "escreve", se "pinta" o texto. Vamos usar a área cliente de uma janela e um contexto modelo (device context). Leia o tutorial e fique por dentro...

 

PROJETO PINTANDO TEXTO
  1. Texto como objeto GUI
  2. A área cliente
  3. O contexto modelo
  4. A mensagem WM_PAINT
  5. O Retângulo Inválido
  6. A reposta à WM_PAINT
  7. Criar uma janela com a frase "Assembly NumaBoa".
  8. Instanciar as variáveis locais
  9. Iniciar o processo de pintura com BeginPaint
  10. Obter a área cliente da janela
  11. Desenhar o Texto
  12. Desligar o processo de pintura com EndPaint
  13. O resultado
  14. Atélogo - resumo e download
1. Texto como objeto GUI

 

Se você ainda se lembra, GUI significa Interface Gráfica do Usuário. A novidade é que o Windows trata texto como um gráfico, portanto, texto é um objeto gráfico ou objeto GUI. Cada caracter é um conjunto de pontos (pixels) dispostos de maneira que adquiram sua aparência peculiar. É por isso que "pintamos" o texto ao invés de escrevê-lo. Observe abaixo o caracter "a" dentro do círculo vermelho: não se vê pontinho nenhum. Logo acima está o mesmo "a" aumentado algumas vezes: os pixels começam a se delinear. Agora observe a ampliação maior: numa matriz de 10 x 10 pontos, alguns estão em azul formando a letra "a". Esta matriz servirá para quaisquer caracteres gráficos que quisermos montar para esta fonte. Aliás, a fonte usada no exemplo é a Courier New.

 

 

Podemos imaginar cada caracter da fonte como uma matriz de 10 x 10 pixels, com alguns "pintados" e outros "vazios". Para "pintar" uma frase, basta fazer uma sequência de matrizes, usando uma para cada caracter, com os respectivos pixels "cheios e vazios".

 

2. A área cliente de uma janela

 

A tela do seu monitor pode apresentar vários programas simultaneamente. É claro que precisam existir regras para que um programa não "pinte" coisas na tela do outro. O Windows garante que um programa não invada a janela do outro limitando a área de pintura de cada janela à sua própria área (chamada de área cliente). A área cliente não tem o tamanho da janela. As bordas, por exemplo, não estão incluídas. Sabemos que a área cliente de uma janela não é constante: basta o usuário mudar suas dimensões que ela se modifica. É por isso que é preciso determinar a área cliente dinamicamente.

 

O sistema não permite a ação de "pixadores" - todas as pinturas são rigorosamente controladas. Primeiro é preciso obter uma autorização do Windows para pintar. O Windows então determina o tamanho da área cliente, a fonte, as cores e outros atributos e devolve um manipulador do modelo autorizado. Este modelo é chamado de contexto modelo.

 

3. O contexto modelo (device context)

 

O contexto modelo é um estrutura de dados (veja mais em "Trabalhando com Estruturas") mantida internamente pelo sistema. Um contexto modelo geralmente está associado a um dispositivo, por exemplo, uma impressora ou uma tela de monitor. No caso do monitor, geralmente também está associado a uma janela em particular.

 

Alguns dos valores do contexto modelo são atributos gráficos, como cores e fontes. Quando solicitado, o sistema cria um contexto modelo com valores default. Estes valores podem ser mudados de acordo com as necessidades do programa e é para isso que o Windows devolve um manipulador.

 

Existem três formas de solicitar um manipulador de contexto modelo:

call BeginPaint como resposta de uma mensagem WM_PAINT,

call GetDC como resposta de outras mensagens e

call CreateDC para criar um contexto modelo próprio.

 

Observação: após utilizar o contexto modelo é preciso liberá-lo. SEMPRE libere o contexto modelo na MESMA resposta de mensagem que você utilizou para obtê-lo.

 

4. A mensagem WM_PAINT

 

O Windows envia uma mensagem WM_PAINT para uma janela para notificá-la de que é preciso repintar sua área cliente. Quando uma janela que estava coberta (ou semi-coberta) por outra é novamente mostrada integralmente, o Windows põe uma mensagem WM_PAINT na lista de mensagens da janela em questão. A janela, ao receber esta mensagem, repinta sua área cliente. Fica claro que, quando nós quisermos pintar algo na área cliente de uma janela, precisamos interceptar a mensagem WM_PAINT para efetuar o trabalho de pintura.

 

5. O Retângulo Inválido

 

O Windows sempre define a menor área de uma janela que esteja precisando de uma repintura. É a menor área retangular que precisa ser atualizada, o chamado retângulo inválido. Repintando apenas o retângulo inválido, o sistema deixa de fazer muito trabalho inútil.

Quando o Windows detecta um retângulo inválido na área cliente de uma janela, ele envia uma mensagem WM_PAINT para esta janela. Como resposta a esta mensagem, a janela pode obter uma estrutura paintstruct, a qual contém, entre outras informações, as coordenadas do retângulo inválido.

Se formos processar uma mensagem WM_PAINT, no mínimo precisamos chamar o procedimento padrão do Windows (com DefWindowProc) ou validar o retângulo inválido com ValidateRect, caso contrário o Windows ficará enviando continuamente mensagens WM_PAINT.

 

6. A reposta à mensagem WM_PAINT

 

A seguir encontram-se as etapas de uma resposta à mensagem WM_PAINT:

  1. Obter um manipulador de contexto modelo através da função BeginPaint
  2. Pintar a área cliente
  3. Liberar o manipulador com a função EndPaint

Não é preciso validar explicitamente o retângulo inválido - a chamada a BeginPaint faz isso por nós. Entre o par BeginPaint / EndPaint podemos fazer chamadas a funções para tarefas de pintura.

 

7. A janela com a frase "Assembly NumaBoa"

 

O modelo do código fonte é o nosso velho conhecido, o da "Janelinha NumaBoa" que você encontra na caixa de ferramentas. Apenas o texto adicional será destacado e é claro que ele se encontra na nossa função gerenteMensagem:

 

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

.IF uMsg == WM_CREATE

 

.ELSEIF uMsg == WM_SIZE

 

.ELSEIF uMsg == WM_PAINT

 

.ELSEIF uMsg == WM_COMMAND

 

.ELSEIF uMsg == WM_CLOSE

 

.ELSEIF uMsg==WM_DESTROY

invoke PostQuitMessage, NULL

xor eax,eax

ret

.ENDIF

invoke DefWindowProc, hWnd, uMsg, wParam, lParam

ret

gerenteMensagem endp

 

8. Instanciando as variáveis locais

 

Já vimos que vamos precisar de um manipulador do contexto modelo, uma estrutura PAINTSTRUCT e de uma estrutura RECT.

O contexto modelo é gerenciado pelo sistema e precisamos apenas do seu manipulador: vamos chamá-lo de mCM.

A estrutura PAINTSTRUCT contém 5 membros: 3 reservados apenas para uso interno do Windows, o que contém o manipulador do contexto modelo e o que contém a informação se o fundo deve ser repintado ou não. Não precisamos nos preocupar com esta estrutura porque, neste caso, apenas o Windows fará uso da mesma. Precisamos apenas instanciá-la que o Windows se encarrega de inicializar seus valores. Vamos chamá-la de ps.

A estrutura RECT receberá o nome de eRet. Este tipo de estrutura define as coordenadas do canto superior esquerdo e do inferior direito de um retângulo:

typedef struct _RECT { // rc

LONG left; // esquerda

LONG top; // topo

LONG right; // direita

LONG bottom; // base

} RECT;

 

Vamos mudar um pouco a forma do procedimento da função gerenteMensagem apenas para não ficar na mesmice:

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL mCM:HDC

LOCAL ps:PAINTSTRUCT

LOCAL eRet:RECT

 

.IF uMsg == WM_DESTROY

invoke PostQuitMessage, NULL

xor eax,eax

ret

.ELSEIF uMsg == WM_PAINT

 

.ELSE

invoke DefWindowProc, hWnd, uMsg, wParam, lParam

ret

.ENDIF

xor eax,eax

ret

gerenteMensagem endp

 

9. Iniciar o processo de pintura com BeginPaint

 

Quando o programa é iniciado, uma das primeiras coisas que faz é "produzir" a janela principal. É claro que esta janela será pintada na tela, ou seja, receberá uma mensagem WM_PAINT. A partir daí, sempre que houver a necessidade de repintá-la, lhe será enviada a mesma mensagem. É aí que pegamos o gancho e solicitamos uma licença ao sistema para pintarmos nosso texto. Neste exemplo, a licença será solicitada através da chamada à função BeginPaint. Esta função, da user32.dll, prepara a janela especificada para pintura e inicializa a estrutura PAINTSTRUCT enviada com as informações necessárias:

HDC BeginPaint(

HWND hwnd, // manipulador da janela

LPPAINTSTRUCT lpPaint // ponteiro para a estrutura PAINTSTRUCT

);

 

Se tudo correr bem, o sistema nos devolve o manipulador do modelo do contexto. Passamos então o manipulador para a nossa variável local mCM:

 

.ELSEIF uMsg == WM_PAINT

invoke BeginPaint, hWnd, ADDR ps

mov mCM, eax

 

10. Obtendo a área cliente da janela

 

Com a função GetClientRect obtemos as coordenadas da área cliente de uma janela. As coordenadas cliente especificam os cantos superior esquerdo e inferior direito da área cliente. Como as coordenadas cliente são relativas ao canto superior esquerdo de uma área cliente, as coordenadas do canto superior esquerdo são (0,0).

BOOL GetClientRect(

HWND hWnd, // manipulador da janela

LPRECT lpRect // endereço da estrutura para as coordenadas cliente

);

 

.ELSEIF uMsg == WM_PAINT

invoke BeginPaint, hWnd, ADDR ps

mov mCM, eax

invoke GetClientRect, hWnd, ADDR eRet

 

11. Desenhar o Texto

 

Finalmente chegamos no texto... qual era mesmo? Nós sabemos que queremos "Assembly NumaBoa" mas nosso programa ainda não sabe. Precisamos criar uma variável que contenha a string e, aproveitando o embalo, vamos chamar a classe de "Janela" e personalizar o nome da nova janela:

.DATA

NomeClasse db "Janela",0

TituloJanela db "Janela Pintada",0

NossoTexto db "Assembly NumaBoa",0

 

Agora é só chamar a função DrawText, da user32.dll. DrawText é uma função API de alto nível para saída de texto (a prima pobre desta função é a TextOut). Esta função desenha um texto formatado dentro do retângulo especificado. Ela formata o texto de acordo com o método especificado (alinhando o texto, com quebra de linha, etc):

int DrawText(

HDC hDC, // manipulador do contexto modelo

LPCTSTR lpString, // ponteiro da string do texto

int nCount, // comprimento da string, em caracteres

LPRECT lpRect, // ponteiro para a estrutura com as dimensões de formatação

UINT uFormat // flags de formatação do texto

);

 

O parâmetro nCount especifica o número de caracteres da string. Se nCount for -1, então o parâmetro lpString é considerado como um ponteiro para uma string terminada em zero e DrawText calcula o número de caracteres automaticamente.

O uFormat precisa de algumas explicações. uFormat pode ser a combinação de muitos valores diferentes, dos quais os mais comumente usados são:

 

Valor Observação
DT_BOTTOM Alinha o texto na base do retângulo. Este valor precisa ser combinado com DT_SINGLELINE.
DT_CALCRECT Determina a largura e a altura do retângulo. Se houver várias linhas de texto, DrawText usa a largura do retângulo apontado pelo parâmetro lpRect e amplia a base do retângulo de modo que possa conter a última linha do texto. Se houver apenas uma linha de texto, DrawText modifica o lado direito do retângulo de modo que possa conter o último caracter da linha. Em ambos os casos, DrawText retorna a altura do texto formatado mas NÃO desenha o texto.
DT_CENTER Centra o texto horizontalmente no retângulo.
DT_EXPANDTABS Expande os caracteres tab. O número default de caracteres por tab é oito.
DT_LEFT Alinha o texto à esquerda.
DT_NOCLIP Desenha sem cortes (clipping). DrawText é um pouco mais rápida quando DT_NOCLIP é usado.
DT_RIGHT Alinha o texto à direita.
DT_SINGLELINE Apresenta o texto numa linha única. Os retornos de carro e quebra de linha não quebram a linha.
DT_TOP Alinha o texto no topo (apenas para linha única)
DT_VCENTER Centra o texto verticalmente (apenas para linha única).

 

Queremos nosso texto como linha única e centrado na horizontal e na vertical, portanto:

.ELSEIF uMsg == WM_PAINT

invoke BeginPaint, hWnd, ADDR ps

mov mCM, eax

invoke GetClientRect, hWnd, ADDR eRet

invoke DrawText, mCM, ADDR NossoTexto, -1, ADDR eRet,

DT_SINGLELINE or DT_CENTER or DT_VCENTER

 

12. Desligar o processo de pintura com EndPaint

 

Lembre-se de que precisamos liberar o manipulador do contexto modelo dentro da mesma resposta em que o criamos. Para não esquecer, eu costumo escrever o invoke BeginPaint e logo depois o invoke EndPaint. O miolo eu preencho posteriormente. Mas vamos lá. Já que a pintura está terminada, vamos liberar o manipulador do contexto modelo mCM chamando EndPaint:

BOOL EndPaint(

HWND hWnd, // manipulador da janela

CONST PAINTSTRUCT *lpPaint // ponteiro para a estrutura com os dados de pintura

);

 

.ELSEIF uMsg == WM_PAINT

invoke BeginPaint, hWnd, ADDR ps

mov mCM, eax

invoke GetClientRect, hWnd, ADDR eRet

invoke DrawText, mCM, ADDR NossoTexto, -1, ADDR eRet,

DT_SINGLELINE or DT_CENTER or DT_VCENTER

invoke EndPaint, hWnd, ADDR ps

 

13. O resultado



ATÉLOGO

 

Resumindo a história

  • Chame o par BeginPaint / EndPaint como resposta de uma mensagem WM_PAINT
  • Faça o que quiser com a área cliente entre as duas chamadas
  • Se quiser repintar sua área cliente em resposta a outras mensagens, existem duas possibilidades:
    • Use o par GetDC / ReleaseDC e faça sua pintura entre estas duas chamadas
    • Chame InvalidateRect ou UpdateWindow para invalidar a área cliente inteira e forçando o Windows a colocar uma mensagem na lista de mensagens da sua janela e faça a sua pintura na seção WM_PAINT.

É isso aí. E como não podia deixar de ser, o arquivo contendo todo o tutorial está disponível para download como tutNB05.zip (21 Kb).

 


| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > oicìliS > Assembly > Pintando texto
Créditos: vovó Vicki

webdesign sobMedida by vickiSoft - /informatica/oiciliS/assembler/tutPaint/paint.php (26.01.02) versão 1.0 de 30.01.02
Licença Creative Commons 1998-2006 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.