Visual Studio.NET

Usando o GDI+

Por Mauro Sant'Anna ( mas_mauro@hotmail.com ). Mauro é um "MSDN Regional Director", consultor e instrutor da MAS Informática (www.mas.com.br), tendo ministrado treinamentos na arquitetura .NET desde outubro de 2000 para diversas empresas, dentre as quais a própria Microsoft.

GDI+

Para desenhar com a API do Windows devemos usar um "HDC - device context handle" em conjunto com diversas funções que aceitam um HDC como argumento como Rectangle ou Draw. Estas funções são chamadas coletivamente de "funções GDI". Embora poderosas, usá-las é extremamente complicado. Normalmente usamos classes ou componentes que as encapsulam de forma a facilitar seu uso.

Na nova arquitetura .NET, quem encapsula as funções de GDI é a classe Graphics. Esta classe é associada a uma superfície de desenho. Para desenhar usamos seus métodos e propriedades. Ela não apenas contém todos os recursos da API do Windows, como vai além do Win32 tradicional, principalmente nos seguintes recursos:

  • Preenchimento em degradé, chamado de "gradient fill";

  • Preenchimento a partir de imagens e texturas;

  • Suporte a diversos tipos de imagem além do BMP e WMF, incluindo JPEG, PNG, GIF, TIFF, EXIF, ICON.

  • Desenho "transparente";

  • Anti-alising;

  • Sistema de coordenadas baseadas em números de ponto flutuante;

  • Recursos normalmente associados a programas de desenho, como conversão para preto e branco, modificações de brilho e contraste e rotação;

  • Funções avançadas de tipografia como quebra de linhas baseadas em dicionário, suporte a Unicode e escrita sobre um caminho.

Outra novidade é que a classe Graphics não guarda estado, como um HDC. Não precisamos "selecionar objetos" como fontes ou canetas; as chamadas passam todos os parâmetros necessários.

Obtendo um objeto da classe Graphics

A classe Graphics pode ser obtida basicamente de duas formas:

  • Com o método GetGraphics dos componentes visuais. Não podemos esquecer de chamar depois o método Dispose dentro de um bloco try/finally para os liberar recursos alocados.

  • Processando o evento OnPaint dos componentes visuais que já fornece um objeto da classe Graphics pronto para uso.

Veja o código que obtêm, usa e libera um objeto da classe Graphics:

Graphics Tela = CreateGraphics(); 
try { 
// Desenha 
Desenha(Tela); 
} 
finally { 
Tela.Dispose(); 
} 

Para desenhar, usamos vários métodos. Os iniciados em Draw desenham um contorno, os iniciados em Fill preenchem o desenho. Veja alguns exemplos:

Método para desenhar contorno

Método para desenhar imagem preenchida

Funcionalidade

DrawArc

 

Desenha um arco.

DrawBezier

 

Desenha uma curva "Bezier".

DrawBeziers

 

Desenha uma série de curvas "Bezier".

DrawClosedCurve

FillClosedCurve

Desenha uma curva fechada dada uma série de pontos.

DrawCurve

 

Desenha uma curva dada uma série de pontos.

DrawEllipse

FillEllipse

Desenha uma elipse.

DrawIcon

 

Desenha um ícone com possibilidade de esticá-lo.

DrawIconUnstretched

 

Desenha um ícone sem possibilidade de esticá-lo.

DrawImage

 

Desenha uma imagem (objeto da classe Image) com possibilidade de esticá-la.

DrawImageUnscaled

 

Desenha uma imagem (objeto da classe Image) sem possibilidade de esticá-la.

DrawLine

 

Desenha uma linha.

DrawLines

 

Desenha uma série de linhas.

DrawPath

FillPath

Desenha um objeto da classe GraphicsPath.

DrawPie

FillPie

Desenha um setor de uma elipse.

DrawPolygon

FillPolygon

Desenha um polígono.

DrawRectangle

FillRectangle

Desenha um retângulo.

DrawRectangles

FillRectangles

Desenha uma série de retângulos.

FillRegion

 

Preenche o interior de um objeto da classe Region.

DrawString

 

 

Muitos métodos exigem alguns dos seguintes objetos para desenhar:

  • Pen, correspondente à borda do desenho. Podemos especificar atributos como cor, largura, estilo e transparência.

  • Brush, correspondente ao preenchimento do desenho. Pode ser uma cor lisa, baseada em uma imagem com ou sem recorte, degradé e transparente.

  • Font, correspondente a uma fonte de texto.

  • Region e GraphicsPath, correspondem a áreas baseadas em outras primitivas como pontos, linhas, arcos e retângulos.

  • ColorMatrix, corresponde a uma matriz de 5X5 usada para transformações de cores.

Quando você precisar usar alguns dos objetos acima, é possível tanto criar um específico a partir do zero como usar algum objeto "de estoque". Por exemplo, muitas cores de preenchimento já estão disponíveis como propriedades estáticas da classe Brush.

Desenhos Simples

Vejamos como desenhar o formulário abaixo:

vstudio_gdi-1.gif

Podemos desenhar tanto a partir do evento OnClick do botão como processando o evento OnPaint. O processamento do evento OnPaint é praticamente obrigatório, pois o Windows não armazena o conteúdo das janelas; se você colocar o código apenas no evento Click do botão, o desenho será perdido quando a janela for redesenhada pelo Windows. Veja um fragmento de código do programa acima:

const int X0 = 10; 

const int Y0 = 50; 


const int W = 200; 


const int H = 120; 


const int R = H / 3; 


const string Msg = "O C# é ótimo"; 


void Desenha(Graphics Tela){ 


// Preenche retângulo 


Tela.FillRectangle(Brushes.Green, X0, Y0, W, H); 


// Desenha borda 


Tela.DrawRectangle(Pens.Black, X0, Y0, W, H); 


// Define o polígono 


PointF [] Losango = new PointF[4]; 


Losango[0] = new PointF(X0 + 0, Y0 + H / 2); 

Losango[1] = new PointF(X0 + W / 2, Y0 + 0); 

Losango[2] = new PointF(X0 + W, Y0 + H / 2); >

Losango[3] = new PointF(X0 + W / 2, Y0 + H); 

// Preenche o polígono

Tela.FillPolygon(Brushes.Yellow, Losango); 

// Desenha círculo 

Tela.FillEllipse(Brushes.Blue, X0 + W / 2 - R, Y0 + H / 2 - R, R * 2, R * 2); 

// Cria fonte 

Font F = new Font("Arial", 20); 

// Pega o tamanho da string usando a fonte escolhida >

SizeF Tamanho = Tela.MeasureString(Msg, F); 


// Desenha texto 


Tela.DrawString(Msg, F, Brushes.DarkRed, X0 + (W - Tamanho.Width) / 2, Y0 + H); 


} 


// Clique do botão 
private void button1_Click(object sender, System.EventArgs e) { 

Graphics Tela = CreateGraphics(); 

try { 

Desenha(Tela); 
} 

finally { 

Tela.Dispose(); 

} 

} 

// Evento gerado pelo Windows 

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { 

if (checkBox1.Checked) 

Desenha(e.Graphics); 

} 

// Mudança do checkbox 

private void checkBox1_CheckedChanged(object sender, System.EventArgs e) { 

Invalidate(); 

} 

Novidades no Desenho

Vamos agora usar algumas das novidades como degradé, transparência, recortes e conversões de cores. Vamos desenhar o formulário abaixo:

Cc580564.vstudio_gdi-2(pt-br,MSDN.10).jpg

Note o seguinte:

  • As extremidades da seta foram criadas pela própria biblioteca.

  • A imagem no formato JPEG foi carregada diretamente.

  • A seta é branca e transparente e exibe o fundo.

  • A imagem em preto e branco foi gerada a partir da imagem colorida.

  • Existe um desenho baseado em uma imagem recortada.

  • O fundo é um preenchimento em degradé.

  • A cor do texto é degradé.

Veja um fragmento do código:

using System.Drawing.Drawing2D; 
using System.Drawing.Imaging; 

const string NomeArq = "LogoNET.jpg"; 
const string Msg = "Texto Colorido"; 
void DesenhaGradiente(Graphics Tela){ 
// Pega o retâmgulo da superfície do formulário 
Rectangle R = ClientRectangle; 
// Cria um brush "degradê" a 45 graus de inclinação, com o mesmo tamanho do formulário 
LinearGradientBrush brush = new LinearGradientBrush(R, Color.DarkBlue, Color.DarkOrange, 45); 
// Desenha 
Tela.FillRectangle(brush, R); 
} 
void DesenhaSeta(Graphics Tela){ 
// Cria uma caneta branca com 25% de transparência 
// 64 corresponde à transperência de 25 % (64/256) 
Pen pen = new Pen(Color.FromArgb(64, 255, 255, 255), 20); 
// Define uma ponta triangular 
pen.StartCap = LineCap.Triangle; 
// Define a outra ponta como seta 
pen.EndCap = LineCap.ArrowAnchor; 
// Desenha a linha 
Tela.DrawLine(pen, new Point(30,30), new Point(180,220)); 
} 
void CarrregaImagem(Graphics Tela){ 
// Carrega imagem 
Image Img = Image.FromFile(NomeArq); 
// Desenha 
Tela.DrawImage(Img, new Rectangle(200, 200, Img.Width, Img.Height)); 
} 
void CarrregaImagemRecortada(Graphics Tela){ 
// Carrega imagem 
Image Img = Image.FromFile(NomeArq); 
// Define um triângulo a ser usado para recorte 
PointF [] Vertices = new PointF[] {new PointF((int)(Img.Width * 2.5), Img.Height), 
new PointF(Img.Width * 2, Img.Height * 2), new PointF(Img.Width * 3, Img.Height * 2)}; 
// Cria e define a área de recorte 
GraphicsPath starPath = new GraphicsPath(); 
starPath.AddPolygon(Vertices); 
// Cria um brush com a imagem recortada 
TextureBrush logoBrush = new TextureBrush(Img); 
// Desenha 
Tela.FillPath(logoBrush, starPath); 
} 
void DesenhaCinza(Graphics Tela) { 
// Carrega imagem 
Image Img = Image.FromFile(NomeArq); 
// Define array que fará a transformação para preto e branco 
float[][] grayCoef = new float[5][]; 
grayCoef[0] = new float[] {.33f, .33f, .33f, 0.0f, 0.0f}; 
grayCoef[1] = new float[] {.33f, .33f, .33f, 0.0f, 0.0f}; 
grayCoef[2] = new float[] {.33f, .33f, .33f, 0.0f, 0.0f}; 
grayCoef[3] = new float[] {0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; 
grayCoef[4] = new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; 
// Cria matriz de cores 
ColorMatrix grayscale = new ColorMatrix(grayCoef); 
// Cria atributo para ser usado no desenha 
ImageAttributes attrib = new ImageAttributes(); 
attrib.SetColorMatrix(grayscale); 
// Desenha imagem 
Tela.DrawImage(Img, new Rectangle(120, 10, Img.Width, Img.Height), 
0, 0, Img.Width, Img.Height, GraphicsUnit.Pixel, attrib); 
} 
// Imgem para desenho 
void TextoGradiente(Graphics Tela) { 
// Cria fonte 
Font F = new Font("Times New Roman", 50, FontStyle.Bold); 
// Cria brush degradê 
LinearGradientBrush brush = new LinearGradientBrush(new RectangleF(0, 0, 10, F.Height), 
Color.Red, Color.Black, 90.0f); 
// Desenha string com o fundo criado acima 
Tela.DrawString(Msg, F , brush, 20, 300); 
} 
// Evento gerado pelo Windows 
private void FormGDIP_Paint(object sender, System.Windows.Forms.PaintEventArgs e) 
{ 
// Desenha tudo 
DesenhaGradiente(e.Graphics); 
DesenhaSeta(e.Graphics); 
CarrregaImagem(e.Graphics); 
CarrregaImagemRecortada(e.Graphics); 
DesenhaCinza(e.Graphics); 
TextoGradiente(e.Graphics); 
} 

Conclusão

A biblioteca GDI+ é bem mais fácil de usar que as funções do Windows e ainda traz várias novas funcionalidades que antes exigiam pacotes comerciais de desenho.

Faça o download deste documento:

Usando o GDI+

downl formato Word

    138 Kb    

Mostrar: