Alterações no modelo de programação
As seções a seguir descrevem várias maneiras pelas quais a programação com o Windows GDI+ é diferente da programação com o Windows Graphics Device Interface (GDI).
- Contextos de dispositivo, identificadores e objetos gráficos
- Duas maneiras de desenhar uma linha
- Canetas, pincéis, caminhos, imagens e fontes como parâmetros
- Sobrecarregar método
- Não há mais posição atual
- Métodos separados para desenhar e preencher
- Construir regiões
Contextos de dispositivo, identificadores e objetos gráficos
Se você já escreveu programas usando GDI (a interface de dispositivo gráficos incluída nas versões anteriores do Windows), está familiarizado com a ideia de um contexto de dispositivo (DC). Um contexto de dispositivo é uma estrutura usada pelo Windows para armazenar informações sobre os recursos de um determinado dispositivo de exibição e atributos que especificam como os itens serão desenhados nesse dispositivo. Um contexto de dispositivo para uma exibição de vídeo também está associado a uma janela específica na exibição. Primeiro, você obtém um identificador para um contexto de dispositivo (HDC) e, em seguida, passa esse identificador como um argumento para funções GDI que realmente fazem o desenho. Você também passa o identificador como um argumento para funções GDI que obtêm ou definem os atributos do contexto do dispositivo.
Quando você usa o GDI+, não precisa se preocupar tanto com identificadores e contextos de dispositivos como quando usa o GDI. Basta criar um objeto Gráficos e, em seguida, invocar seus métodos no estilo familiar orientado a objetos: myGraphicsObject.DrawLine(parameters). O objeto Graphics está no centro do GDI+, assim como o contexto do dispositivo está no núcleo do GDI. O contexto do dispositivo e o objeto Gráficos desempenham funções semelhantes, mas há algumas diferenças fundamentais entre o modelo de programação baseado em identificador usado com GDI (contextos de dispositivo) e o modelo orientado a objeto usado com objetos Gráficos (GDI+).
O objeto Gráficos , como o contexto do dispositivo, é associado a uma janela específica na tela e contém atributos (por exemplo, modo de suavização e dica de renderização de texto) que especificam como os itens devem ser desenhados. No entanto, o objeto Gráficos não está vinculado a uma caneta, pincel, caminho, imagem ou fonte como um contexto de dispositivo. Por exemplo, no GDI, antes de usar um contexto de dispositivo para desenhar uma linha, você deve chamar SelectObject para associar um objeto de caneta ao contexto do dispositivo. Isso é conhecido como selecionar a caneta no contexto do dispositivo. Todas as linhas desenhadas no contexto do dispositivo usarão essa caneta até que você selecione uma caneta diferente. Com GDI+, você passa um objeto Caneta como um argumento para o método DrawLine da classe Gráficos. Você pode usar um objeto Pen em cada uma de uma série de chamadas DrawLine sem ter que associar um determinado objeto Caneta com o objeto Gráficos.
Duas maneiras de desenhar uma linha
Os dois exemplos a seguir desenham uma linha vermelha de largura 3 do local (20, 10) ao local (200.100). O primeiro exemplo chama GDI e o segundo chama GDI+ por meio da interface de classe C++.
Desenhar uma linha com GDI
Para desenhar uma linha com GDI, você precisa de dois objetos: um contexto de dispositivo e uma caneta. Você obtém um identificador para um contexto de dispositivo chamando BeginPaint e um identificador para uma caneta chamando CreatePen. Em seguida, chame SelectObject para selecionar a caneta no contexto do dispositivo. Você define a posição da caneta como (20, 10) chamando MoveToEx e, em seguida, desenha uma linha dessa posição da caneta para (200, 100) chamando LineTo. Observe que MoveToEx e LineTo recebem hdc como um argumento.
HDC hdc;
PAINTSTRUCT ps;
HPEN hPen;
HPEN hPenOld;
hdc = BeginPaint(hWnd, &ps);
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
hPenOld = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, 20, 10, NULL);
LineTo(hdc, 200, 100);
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
Desenhar uma linha com GDI+ e a interface de classe C++
Para desenhar uma linha com GDI+ e a interface de classe C++, você precisa de um objeto Gráficos e um objeto Caneta. Observe que você não solicita identificadores do Windows para esses objetos. Em vez disso, você usa construtores para criar uma instância da classe Gráficos (um objeto Gráficos ) e uma instância da classe Caneta (um objeto Caneta ). Desenhar uma linha envolve chamar o método Graphics::DrawLine da classe Gráficos. O primeiro parâmetro do método Graphics::D rawLine é um ponteiro para o objeto Caneta. Esse é um esquema mais simples e flexível do que selecionar uma caneta em um contexto de dispositivo, conforme mostrado no exemplo GDI anterior.
HDC hdc;
PAINTSTRUCT ps;
Pen* myPen;
Graphics* myGraphics;
hdc = BeginPaint(hWnd, &ps);
myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics = new Graphics(hdc);
myGraphics->DrawLine(myPen, 20, 10, 200, 100);
delete myGraphics;
delete myPen;
EndPaint(hWnd, &ps);
Canetas, pincéis, caminhos, imagens e fontes como parâmetros
Os exemplos anteriores mostram que os objetos Caneta podem ser criados e mantidos separadamente do objeto Gráficos que fornece os métodos de desenho. Os objetos Pincel, GraphicsPath, Imagem, e objetos Fonte também podem ser criados e mantidos separadamente do objeto Gráficos. Muitos dos métodos de desenho fornecidos pela classe Gráficos recebem um objeto Pincel, GraphicsPath, Imagem ou Fonte como argumento. Por exemplo, o endereço de um objeto Pincel é passado como um argumento para o método FillRectangle e o endereço de um objeto GraphicsPath é passado como um argumento para o método Graphics::DrawPath. Da mesma forma, os endereços dos objetos Imagem e Fonte são passados para os métodos DrawImage e DrawString. Isso contrasta com o GDI, em que você seleciona um pincel, caminho, imagem ou fonte no contexto do dispositivo e, em seguida, passa um identificador para o contexto do dispositivo como um argumento para uma função de desenho.
Sobrecarregar método
Muitos dos métodos GDI+ estão sobrecarregados, ou seja, vários métodos compartilham o mesmo nome, mas têm listas de parâmetros diferentes. Por exemplo, o método DrawLine da classe Gráficos vem nas seguintes formas:
Status DrawLine(IN const Pen* pen,
IN REAL x1,
IN REAL y1,
IN REAL x2,
IN REAL y2);
Status DrawLine(IN const Pen* pen,
IN const PointF& pt1,
IN const PointF& pt2);
Status DrawLine(IN const Pen* pen,
IN INT x1,
IN INT y1,
IN INT x2,
IN INT y2);
Status DrawLine(IN const Pen* pen,
IN const Point& pt1,
IN const Point& pt2);
Todas as quatro variações de DrawLine acima recebem um ponteiro para um objeto Caneta , as coordenadas do ponto inicial e as coordenadas do ponto final. As duas primeiras variações recebem as coordenadas como números de ponto flutuante e as duas últimas variações recebem as coordenadas como números inteiros. A primeira e a terceira variação recebem as coordenadas como uma lista de quatro números separados, enquanto a segunda e a quarta variações recebem as coordenadas como um par de objetos Ponto (ou objetos PointF).
Não há mais posição atual
Observe que, nos métodos DrawLine mostrados anteriormente, o ponto inicial e o ponto final da linha são recebidos como argumentos. Esse é um desvio do esquema GDI em que você chama MoveToEx para definir a posição atual da caneta seguida por LineTo para desenhar uma linha começando em (x1, y1) e terminando em (x2, y2). A GDI+ como um todo abandonou a noção de posição atual.
Métodos separados para desenhar e preencher
O GDI+ é mais flexível do que o GDI quando se trata de desenhar os contornos e preencher o interior de formas como retângulos. O GDI tem uma função Rectangle que desenha o contorno e preenche o interior de um retângulo em uma única etapa. O contorno é desenhado com a caneta selecionada no momento e o interior é preenchido com o pincel selecionado no momento.
hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);
GDI+ tem métodos separados para desenhar o contorno e preencher o interior de um retângulo. O método DrawRectangle da classe Gráficos tem o endereço do objeto Caneta como um dos seus parâmetros e o método FillRectangle tem o endereço do objeto Pincel como um dos seus parâmetros.
HatchBrush* myHatchBrush = new HatchBrush(
HatchStyleCross,
Color(255, 0, 255, 0),
Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);
Observe que os métodos FillRectangle e DrawRectangle na GDI+ recebem argumentos que especificam a borda esquerda, a parte superior, a largura e a altura do retângulo. Isso contrasta com as funções GDI Retângulo que recebe argumentos que especificam a borda esquerda, a borda direita, a parte superior e a parte inferior do retângulo. Observe também que o construtor da classe Cor em GDI+ tem quatro parâmetros. Os três últimos parâmetros são os valores usuais de vermelho, verde e azul. O primeiro parâmetro é o valor alfa, que especifica até que ponto a cor que está sendo desenhada é mesclada com a cor de fundo.
Construir regiões
GDI fornece várias funções para criar regiões: CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn e CreatePolyPolygonRgn. Você pode esperar que a classe Região na GDI+ tenha construtores análogos que usam retângulos, elipses, retângulos arredondados e polígonos como argumentos, mas esse não é o caso. A classe Região na GDI+ fornece um construtor que recebe uma referência de objeto Rect e outro construtor que recebe o endereço de um objeto GraphicsPath. Se você quiser construir uma região com base em uma elipse, retângulo arredondado ou polígono, poderá fazer isso facilmente criando um objeto GraphicsPath (que contém uma elipse, por exemplo) e, em seguida, passando o endereço desse objeto GraphicsPath para um construtor Região.
O GDI+ facilita a formação de regiões complexas combinando formas e caminhos. A classe Região tem métodos de União e Interseção que você pode usar para aumentar uma região existente com um caminho ou outra região. Um recurso interessante do esquema GDI+ é que um objeto GraphicsPath não é destruído quando é passado como um argumento para um construtor Região. No GDI, você pode converter um caminho em uma região com a função PathToRegion, mas o caminho é destruído no processo. Além disso, um objeto GraphicsPath não é destruído quando seu endereço é passado como um argumento para um método Union ou Intersect, portanto, você pode usar um determinado caminho como um bloco de construção para várias regiões separadas. Isso é mostrado no exemplo a seguir. Suponha que onePath seja um ponteiro para um objeto GraphicsPath (simples ou complexo) que já foi inicializado.
Region region1(rect1);
Region region2(rect2);
region1.Union(onePath);
region2.Intersect(onePath);