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 a GDI (Interface de Dispositivo gráfico do Windows).

Contextos do dispositivo, identificadores e objetos gráficos

Se você tiver escrito programas usando GDI (a interface de dispositivo gráfico incluída em versões anteriores do Windows), você está familiarizado com a ideia de um DC (contexto de dispositivo). 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 tela. 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.

Ao usar GDI+, você não precisa estar tão preocupado com identificadores e contextos de dispositivo como quando usa gdi. Você simplesmente cria um objeto Graphics e, em seguida, invoca seus métodos no estilo familiar orientado a objetos — myGraphicsObject.DrawLine(parameters). O objeto Graphics está no núcleo do GDI+ assim como o contexto do dispositivo está no núcleo da GDI. O contexto do dispositivo e o objeto Graphics desempenham funções semelhantes , mas há algumas diferenças fundamentais entre o modelo de programação baseado em identificador usado com contextos de dispositivo (GDI) e o modelo orientado a objeto usado com objetos Graphics (GDI+).

O objeto Graphics , como o contexto do dispositivo, está 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 Graphics não está vinculado a uma caneta, pincel, caminho, imagem ou fonte como um contexto de dispositivo. Por exemplo, na 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 é chamado de seleção da 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 Pen como um argumento para o método DrawLine da classe Graphics . Você pode usar um objeto Pen diferente em cada uma de uma série de chamadas DrawLine sem precisar associar um determinado objeto Pen a um objeto Graphics .

Duas maneiras de desenhar uma linha

Os dois exemplos a seguir desenham uma linha vermelha de largura 3 do local (20, 10) para o local (200.100). O primeiro exemplo chama GDI e o segundo chama GDI+ por meio da interface de classe C++.

Desenhando 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 de caneta como (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);

Desenhando 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 Graphics e um objeto Pen . Observe que você não solicita ao Windows identificadores para esses objetos. Em vez disso, você usa construtores para criar uma instância da classe Graphics (um objeto Graphics ) e uma instância da classe Pen (um objeto Pen ). Desenhar uma linha envolve chamar o método Graphics::D rawLine da classe Graphics . O primeiro parâmetro do método Graphics::D rawLine é um ponteiro para o objeto Pen . Esse é um esquema mais simples e flexível do que selecionar uma caneta em um contexto de dispositivo, conforme mostrado no exemplo de 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 Pen podem ser criados e mantidos separadamente do objeto Graphics , que fornece os métodos de desenho. Os objetos Brush, GraphicsPath, Image e Font também podem ser criados e mantidos separadamente do objeto Graphics . Muitos dos métodos de desenho fornecidos pela classe Graphics recebem um objeto Brush, GraphicsPath, Image ou Font como um argumento. Por exemplo, o endereço de um objeto Brush é 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::D rawPath . Da mesma forma, os endereços dos objetos Image e Font são passados para os métodos DrawImage e DrawString . Isso contrasta com a 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.

Sobrecarga de 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 Graphics 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 drawline acima recebem um ponteiro para um objeto Pen , as coordenadas do ponto de partida 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 inteiros. As primeira e terceira variações 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 Point (ou PointF).

Não há mais posição atual

Observe que, nos métodos DrawLine mostrados anteriormente, tanto o ponto inicial quanto o ponto final da linha são recebidos como argumentos. Essa é uma saída do esquema GDI em que você chama MoveToEx para definir a posição da caneta atual seguida por LineTo para desenhar uma linha começando em (x1, y1) e terminando em (x2, y2). O 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 os interiores de formas como retângulos. A GDI tem uma função Rectangle que desenha a estrutura de tópicos e preenche o interior de um retângulo em uma única etapa. A estrutura de tópicos é desenhada 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);

O GDI+ tem métodos separados para desenhar a estrutura de tópicos e preencher o interior de um retângulo. O método DrawRectangle da classe Graphics tem o endereço de um objeto Pen como um de seus parâmetros, e o método FillRectangle tem o endereço de um objeto Brush como um de 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 no GDI+ recebem argumentos que especificam a borda esquerda, a parte superior, a largura e a altura do retângulo. Isso contrasta com a funçãoRetângulo GDI, que usa argumentos que especificam a borda esquerda, a borda direita, a parte superior e inferior do retângulo. Observe também que o construtor da classe Color no GDI+ tem quatro parâmetros. Os últimos três parâmetros são os valores vermelhos, verdes e azuis usuais; o primeiro parâmetro é o valor alfa, que especifica até que ponto a cor que está sendo desenhada é combinada com a cor da tela de fundo.

Construindo regiões

A GDI fornece várias funções para criar regiões: CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn e CreatePolyPolygonRgn. Você pode esperar que a classe Region no GDI+ tenha construtores análogos que tomam retângulos, reticências, retângulos arredondados e polígonos como argumentos, mas esse não é o caso. A classe Region no 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 um retângulo arredondado, reticência arredondado ou reticências, 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 Region .

O GDI+ facilita a formação de regiões complexas combinando formas e caminhos. A classe Region tem métodos Union e Intersect que você pode usar para aumentar uma região existente com um caminho ou outra região. Um bom recurso do esquema GDI+ é que um objeto GraphicsPath não é destruído quando é passado como um argumento para um construtor Region . 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, para que você possa 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);