Informática Numaboa - Tutoriais e Programação
Pintando texto (masm)
Ter 20 Jan 2009 22:10 |
- Detalhes
- Categoria: Assembly Numaboa (antigo oiciliS)
- Atualização: Domingo, 21 Junho 2009 18:45
- Autor: vovó Vicki
- Acessos: 11964
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. Leia o tutorial e fique por dentro...
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 imagem, portanto, texto é um objeto gráfico ou objeto GUI. Cada caractere é um conjunto de pontos (pixels) dispostos de maneira que adquiram uma aparência peculiar. É por isso que "pintamos" o texto ao invés de escrevê-lo. Observe abaixo o caractere "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 caractere da fonte como uma matriz de 10 x 10 pixels, com alguns deles "pintados" e outros "vazios". Para "pintar" uma frase, basta criar uma sequência de matrizes, usando uma para cada caractere, com os respectivos pixels "cheios e vazios".
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 de 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. Depois, o Windows 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 de dispositivo.
O contexto de dispositivo
O contexto de dispositivo é um estrutura de dados (veja mais em "Trabalhando com Estruturas") mantida internamente pelo sistema. Este contexto geralmente está associado a um dispositivo específico, 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 de dispositivo são atributos gráficos, como cores e fontes. Quando solicitado, o sistema cria um contexto 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 de dispositivo:
- call BeginPaint - como resposta de uma mensagem WM_PAINT,
- call GetDC - como resposta de outras mensagens e
- call CreateDC - para criar um contexto de dispositivo próprio.
Observação: após utilizar o contexto de dispositivo é preciso liberá-lo. SEMPRE libere o contexto na MESMA resposta de mensagem que você utilizou para obtê-lo.
A mensagem WM_PAINT
O Windows envia uma mensagem WM_PAINT para uma janela para notificá-la de que é preciso refazer a pintura da 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, refaz a pintura da 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.
O retângulo inválido
O Windows sempre define a menor área para uma janela que esteja precisando de uma nova pintura. É a menor área retangular que precisa ser atualizada, o chamado retângulo inválido. Refazendo a pintura apenas no 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.
A resposta à mensagem WM_PAINT
A seguir encontram-se as etapas de uma resposta à mensagem WM_PAINT:
- Obter um manipulador de contexto de dispositivo através da função BeginPaint
- Pintar a área cliente
- 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.
Uma janela com a frase Assembly Numaboa
O modelo do código fonte é nosso velho conhecido, o mesmo mostrado no tutorial "". Neste tutorial falaremos apenas do código adicional e, é claro, ele será adicionado na função gerenteMensagem. Veja a função novamente:
Instanciando as variáveis locais
Já vimos que vamos precisar de um manipulador do contexto de dispositivo, de uma estrutura PAINTSTRUCT e de uma estrutura RECT. Se você leu o texto de apoio Trabalhando com Estruturas, sabe do que estou falando; se não, faça-o agora, pois vai precisar destas informações.
O contexto de dispositivo é gerenciado pelo sistema e precisamos apenas do seu manipulador: vamos chamá-lo de mDC.
A estrutura PAINTSTRUCT contém 5 membros: 3 reservados apenas para uso interno do Windows, o que contém o manipulador do contexto de dispositivo 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 depois 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:
Iniciando o processo de pintura
Quando o programa é iniciado, uma das primeiras coisas que faz é "produzir" a janela principal. 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 contexto de dispositivo. Passamos então o manipulador para a nossa variável local mDC:
Obtendo a área cliente da janela
Com a função GetClientRect obtemos as coordenadas da área cliente de uma janela. Ests coordenadas 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 );
Desenhando 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:
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> | Explicaçã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 altura 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 caractere 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:
Desligando o processo de pintura com EndPaint
Lembre-se de que é preciso liberar o manipulador do contexto de dispositivo 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 de dispositivo mDC chamando EndPaint:
BOOL EndPaint( HWND hWnd, // manipulador da janela CONST PAINTSTRUCT *lpPaint // ponteiro para a estrutura com os dados de pintura );
Finalmentes
O resultado deste novo programa pode ser visto ao lado: a mesma janelinha que já conhecemos, mas agora com um texto colocado bem no centro da sua área cliente.
Resumo desta 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 a á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, 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.