Visão geral de pincéis

Esta visão geral descreve como criar e usar objetos ID2D1SolidColorBrush, ID2D1LinearGradientBrush, ID2D1RadialGradientBrush e ID2D1BitmapBrush para pintar áreas com cores sólidas, gradientes e bitmaps. Ele contém as seguintes seções:

Pré-requisitos

Essa visão geral pressupõe que você esteja familiarizado com a estrutura de um aplicativo de Direct2D básico, conforme descrito em Criar um aplicativo de Direct2D simples.

Tipos de pincel

Um pincel "pinta" uma área com sua saída. Pincéis diferentes têm tipos diferentes de saída. Direct2D fornece quatro tipos de pincel: ID2D1SolidColorBrush pinta uma área com uma cor sólida, ID2D1LinearGradientBrush com um gradiente linear, ID2D1RadialGradientBrush com um gradiente radial e ID2D1BitmapBrush com um bitmap.

Observação

Começando com Windows 8, você também pode usar ID2D1ImageBrush, que é semelhante a um pincel bitmap, mas também pode usar primitivos.

Todos os pincéis herdam do ID2D1Brush e compartilham um conjunto de recursos comuns (configuração e obtenção de opacidade e transformação de pincéis); eles são criados por ID2D1RenderTarget e são recursos dependentes do dispositivo: seu aplicativo deve criar pincéis depois de inicializar o destino de renderização com o qual os pincéis serão usados e recriar os pincéis sempre que o destino de renderização precisar ser recriado. (Para obter mais informações sobre recursos, consulte Visão geral de recursos.)

A ilustração a seguir mostra exemplos de cada um dos diferentes tipos de pincel.

ilustração dos efeitos visuais de pincéis de cores sólidas, pincéis de gradiente linear, pincéis de gradiente radial e pincéis de bitmap

Noções básicas sobre cores

Antes de pintar com um ID2D1SolidColorBrush ou um pincel de gradiente, você precisa escolher cores. Em Direct2D, as cores são representadas pela estrutura D2D1_COLOR_F (que na verdade é apenas um novo nome para a estrutura usada por Direct3D, D3DCOLORVALUE).

Antes de Windows 8, D2D1_COLOR_F usa a codificação sRGB. A codificação sRGB divide as cores em quatro componentes: vermelho, verde, azul e alfa. Cada componente é representado por um valor de ponto flutuante com um intervalo normal de 0.0 a 1.0. Um valor de 0.0 indica a ausência completa da cor, enquanto um valor de 1.0 indica que a cor está totalmente presente. Para o componente alfa, 0.0 representa uma cor totalmente transparente e 1.0 representa uma cor totalmente opaca.

Começando em Windows 8, D2D1_COLOR_F também aceita codificação scRGB. scRGB é um superconjunto de que permite valores de cor acima de 1,0 e abaixo de 0,0.

Para definir uma cor, você pode usar a estrutura D2D1_COLOR_F e inicializar seus campos por conta própria ou usar a classe D2D1::ColorF para ajudá-lo a criar a cor. A classe ColorF fornece vários construtores para definir cores. Se o valor alfa não for especificado nos construtores, ele usará como padrão 1.0.

  • Use o construtor ColorF(Enum, FLOAT) para especificar uma cor predefinida e um valor de canal alfa. Um valor de canal alfa varia de 0,0 a 1,0, onde 0,0 representa uma cor totalmente transparente e 1.0 representa uma cor totalmente opaca. A ilustração a seguir mostra várias cores predefinidas e seus equivalentes hexadecimal. Para obter uma lista completa de cores predefinidas, consulte a seção Constantes de cores da classe ColorF .

    ilustração de cores predefinidas

    O exemplo a seguir cria uma cor predefinida e a usa para especificar a cor de um ID2D1SolidColorBrush.

hr = m_pRenderTarget->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
    &m_pBlackBrush
    );
  • Use o construtor ColorF(FLOAT, FLOAT, FLOAT, FLOAT) para especificar uma cor na sequência de um vermelho, verde, azul e alfa, em que cada elemento tem um valor entre 0,0 e 1,0.

    O exemplo a seguir especifica os valores vermelho, verde, azul e alfa para uma cor.

    ID2D1SolidColorBrush *pGridBrush = NULL;
    hr = pCompatibleRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f)),
        &pGridBrush
        );
  • Use o construtor ColorF(UINT32, FLOAT) para especificar o valor hexadecimal de uma cor e um valor alfa, conforme mostrado no exemplo a seguir.
    hr = m_pRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),  
        &m_pYellowGreenBrush
        );

Modos alfa

Independentemente do modo alfa do destino de renderização com o qual você usa um pincel, D2D1_COLOR_F valores são sempre interpretados como alfa reto.

Usando pincéis de cores sólidas

Para criar um pincel de cor sólida, chame o método ID2D1RenderTarget::CreateSolidColorBrush , que retorna um OBJETO HRESULT e um objeto ID2D1SolidColorBrush . A ilustração a seguir mostra um quadrado que é acariciou com um pincel de cor preta e pintado com um pincel de cor sólida que tem o valor de cor de 0x9ACD32.

ilustração de um quadrado pintado com um pincel de cor sólida

O código a seguir mostra como criar e usar um pincel de cor preta e um pincel com um valor de cor de 0x9ACD32 para preencher e desenhar esse quadrado.

    ID2D1SolidColorBrush *m_pBlackBrush;
    ID2D1SolidColorBrush *m_pYellowGreenBrush;
if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
        &m_pBlackBrush
        );
}

// Create a solid color brush with its rgb value 0x9ACD32.
if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),  
        &m_pYellowGreenBrush
        );
}
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pYellowGreenBrush);
m_pRenderTarget->DrawRectangle(&rcBrushRect, m_pBlackBrush, 1, NULL);

Ao contrário de outros pincéis, criar um ID2D1SolidColorBrush é uma operação relativamente barata. Você pode criar objetos ID2D1SolidColorBrush sempre que renderizar com pouco ou nenhum impacto no desempenho. Essa abordagem não é recomendada para pincéis de gradiente ou bitmap.

Usando pincéis de gradiente linear

Um ID2D1LinearGradientBrush pinta uma área com um gradiente linear definido ao longo de uma linha, o eixo do gradiente. Especifique as cores do gradiente e sua localização ao longo do eixo gradiente usando objetos ID2D1GradientStop . Você também pode modificar o eixo do gradiente, que permite criar gradiente horizontal e vertical e reverter a direção do gradiente. Para criar um pincel de gradiente linear, chame o método ID2D1RenderTarget::CreateLinearGradientBrush .

A ilustração a seguir mostra um quadrado pintado com um ID2D1LinearGradientBrush que tem duas cores predefinidas, "Yellow" e "ForestGreen".

ilustração de um quadrado pintado com um pincel gradiente linear de amarelo e verde floresta

Para criar o gradiente mostrado na ilustração anterior, conclua estas etapas:

  1. Declare dois objetos D2D1_GRADIENT_STOP . Cada parada de gradiente especifica uma cor e uma posição. Uma posição de 0,0 indica o início do gradiente, enquanto uma posição de 1,0 indica o final do gradiente.

    O código a seguir cria uma matriz de dois objetos D2D1_GRADIENT_STOP . A primeira parada especifica a cor "Amarelo" em uma posição 0 e a segunda parada especifica a cor "ForestGreen" na posição 1.

    // Create an array of gradient stops to put in the gradient stop
    // collection that will be used in the gradient brush.
    ID2D1GradientStopCollection *pGradientStops = NULL;

    D2D1_GRADIENT_STOP gradientStops[2];
    gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
    gradientStops[0].position = 0.0f;
    gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
    gradientStops[1].position = 1.0f;
  1. Crie um ID2D1GradientStopCollection. O exemplo a seguir chama CreateGradientStopCollection, passando a matriz de objetos D2D1_GRADIENT_STOP , o número de paradas de gradiente (2), D2D1_GAMMA_2_2 para interpolação e D2D1_EXTEND_MODE_CLAMP para o modo de extensão.
    // Create the ID2D1GradientStopCollection from a previously
    // declared array of D2D1_GRADIENT_STOP structs.
    hr = m_pRenderTarget->CreateGradientStopCollection(
        gradientStops,
        2,
        D2D1_GAMMA_2_2,
        D2D1_EXTEND_MODE_CLAMP,
        &pGradientStops
        );
  1. Crie o ID2D1LinearGradientBrush. O exemplo a seguir chama o método CreateLinearGradientBrush e passa as propriedades de pincel de gradiente linear que contêm o ponto inicial em (0, 0) e o ponto final em (150, 150) e o gradiente é interrompido na etapa anterior.
    // The line that determines the direction of the gradient starts at
    // the upper-left corner of the square and ends at the lower-right corner.

    if (SUCCEEDED(hr))
    {
        hr = m_pRenderTarget->CreateLinearGradientBrush(
            D2D1::LinearGradientBrushProperties(
                D2D1::Point2F(0, 0),
                D2D1::Point2F(150, 150)),
            pGradientStops,
            &m_pLinearGradientBrush
            );
    }
  1. Use o ID2D1LinearGradientBrush. O próximo exemplo de código usa o pincel para preencher um retângulo.
    m_pRenderTarget->FillRectangle(&rcBrushRect, m_pLinearGradientBrush);

Mais sobre paradas de gradiente

O D2D1_GRADIENT_STOP é o bloco de construção básico de um pincel de gradiente. Uma parada de gradiente especifica a cor e a posição ao longo do eixo do gradiente. O valor da posição do gradiente varia entre 0,0 e 1,0. Quanto mais perto estiver de 0,0, mais próxima a cor fica do início do gradiente; quanto mais perto estiver de 1,0, mais próxima a cor fica do final do gradiente.

A ilustração a seguir realça as paradas de gradiente. O círculo marca a posição das paradas de gradiente e uma linha tracejada mostra o eixo do gradiente.

ilustração de um pincel de gradiente linear com quatro paradas ao longo do eixo

A primeira parada de gradiente especifica a cor amarela em uma posição de 0,0. A segunda parada de gradiente especifica a cor vermelha em uma posição de 0,25. Da esquerda para a direita ao longo do eixo do gradiente, as cores entre essas duas paradas mudam gradualmente de amarelo para vermelho. A terceira parada de gradiente especifica a cor azul em uma posição de 0,75. As cores entre o segundo e o terceiro gradiente para gradualmente mudam de vermelho para azul. A quarta parada de gradiente especifica verde limão em uma posição de 1,0. As cores entre o terceiro e o quarto gradiente para gradualmente mudam de azul para verde limão.

O eixo do gradiente

Conforme mencionado anteriormente, as paradas de gradiente de um pincel de gradiente linear são posicionadas ao longo de uma linha, o eixo do gradiente. Você pode especificar a orientação e o tamanho da linha usando os campos startPoint e endPoint da estrutura D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES ao criar um pincel de gradiente linear. Depois de criar um pincel, você pode ajustar o eixo do gradiente chamando os métodos SetStartPoint e SetEndPoint do pincel. Manipulando o ponto inicial e o ponto de extremidade do pincel, você pode criar gradientes horizontais e verticais, inverter a direção do gradiente e muito mais.

Por exemplo, na ilustração a seguir, o ponto inicial é definido como (0,0) e o ponto final como (150, 50); isso cria um gradiente diagonal que começa no canto superior esquerdo e se estende até o canto inferior direito da área que está sendo pintada. Quando você define o ponto inicial como (0, 25) e o ponto final como (150, 25), um gradiente horizontal é criado. Da mesma forma, definir o ponto inicial como (75, 0) e o ponto final como (75, 50) cria um gradiente vertical. Definir o ponto inicial como (0, 50) e o ponto final como (150, 0) cria um gradiente diagonal que começa no canto inferior esquerdo e se estende até o canto superior direito da área que está sendo pintada.

ilustração de quatro eixos de gradiente diferentes no mesmo retângulo

Usando pincéis de gradiente radial

Ao contrário de um ID2D1LinearGradientBrush, que mescla duas ou mais cores ao longo de um eixo gradiente, um ID2D1RadialGradientBrush pinta uma área com um gradiente radial que mescla duas ou mais cores em uma elipse. Enquanto um ID2D1LinearGradientBrush define seu eixo gradiente com um ponto inicial e um ponto de extremidade, um ID2D1RadialGradientBrush define sua elipse gradiente especificando um raio central, horizontal e vertical e um deslocamento de origem de gradiente.

Como um ID2D1LinearGradientBrush, um ID2D1RadialGradientBrush usa um ID2D1GradientStopCollection para especificar as cores e posições no gradiente.

A ilustração a seguir mostra um círculo pintado com um ID2D1RadialGradientBrush. O círculo tem duas paradas de gradiente: a primeira especifica uma cor predefinida "Amarelo" em uma posição de 0,0 e a segunda especifica uma cor predefinida "ForestGreen" em uma posição de 1,0. O gradiente tem um centro de (75, 75), um deslocamento de origem gradiente de (0, 0) e um raio x e y de 75.

ilustração de um círculo pintado com um pincel de gradiente radial

Os exemplos de código a seguir mostram como pintar esse círculo com um ID2D1RadialGradientBrush que tem duas paradas de cor: "Amarelo" em uma posição de 0,0 e "ForestGreen" em uma posição de 1,0. Semelhante à criação de um ID2D1LinearGradientBrush, o exemplo chama CreateGradientStopCollection para criar um ID2D1GradientStopCollection de uma matriz de paradas de gradiente.

// Create an array of gradient stops to put in the gradient stop
// collection that will be used in the gradient brush.
ID2D1GradientStopCollection *pGradientStops = NULL;

D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
// Create the ID2D1GradientStopCollection from a previously
// declared array of D2D1_GRADIENT_STOP structs.
hr = m_pRenderTarget->CreateGradientStopCollection(
    gradientStops,
    2,
    D2D1_GAMMA_2_2,
    D2D1_EXTEND_MODE_CLAMP,
    &pGradientStops
    );

Para criar um ID2D1RadialGradientBrush, use o método ID2D1RenderTarget::CreateRadialGradientBrush . O CreateRadialGradientBrush usa três parâmetros. O primeiro parâmetro, um D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES especifica o deslocamento de origem central, gradiente e o raio horizontal e vertical do gradiente. O segundo parâmetro é um ID2D1GradientStopCollection que descreve as cores e suas posições no gradiente e o terceiro parâmetro é o endereço do ponteiro que recebe a nova referência ID2D1RadialGradientBrush . Algumas sobrecargas recebem um parâmetro adicional, uma estrutura D2D1_BRUSH_PROPERTIES que especifica um valor de opacidade e uma transformação a ser aplicada ao novo pincel.

O exemplo a seguir chama CreateRadialGradientBrush, passando a matriz de paradas de gradiente e as propriedades do pincel de gradiente radial que têm o valor central definido como (75, 75), o gradientOriginOffset definido como (0, 0) e radiusX e radiusY definidos como 75.

// The center of the gradient is in the center of the box.
// The gradient origin offset was set to zero(0, 0) or center in this case.
if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateRadialGradientBrush(
        D2D1::RadialGradientBrushProperties(
            D2D1::Point2F(75, 75),
            D2D1::Point2F(0, 0),
            75,
            75),
        pGradientStops,
        &m_pRadialGradientBrush
        );
}

O exemplo final usa o pincel para preencher uma elipse.

m_pRenderTarget->FillEllipse(ellipse, m_pRadialGradientBrush);
m_pRenderTarget->DrawEllipse(ellipse, m_pBlackBrush, 1, NULL);

Configurando um gradiente radial

Valores diferentes para center, gradientOriginOffset, radiusX e/ou radiusY produzem gradientes diferentes. A ilustração a seguir mostra vários gradientes radiais que têm diferentes deslocamentos de origem de gradiente, criando a aparência da luz iluminando os círculos de diferentes ângulos.

ilustração do mesmo círculo pintado com pincéis de gradiente radial com deslocamentos de origem diferentes

Usando pincéis de bitmap

Um ID2D1BitmapBrush pinta uma área com um bitmap (representado por um objeto ID2D1Bitmap ).

A ilustração a seguir mostra um quadrado pintado com um bitmap de uma planta.

ilustração de um quadrado pintado com bitmap de planta

Os exemplos a seguir mostram como pintar esse quadrado com um ID2D1BitmapBrush.

O primeiro exemplo inicializa um ID2D1Bitmap para uso com o pincel. O ID2D1Bitmap é fornecido por um método auxiliar, LoadResourceBitmap, definido em outro lugar no exemplo.

// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
    hr = LoadResourceBitmap(
        m_pRenderTarget,
        m_pWICFactory,
        L"FERN",
        L"Image",
        &m_pBitmap
        );
}

Para criar o pincel de bitmap, chame o método ID2D1RenderTarget::CreateBitmapBrush e especifique o ID2D1Bitmap com o qual pintar. O método retorna um OBJETO HRESULT e um objeto ID2D1BitmapBrush . Algumas sobrecargas de CreateBitmapBrush permitem que você especifique opções adicionais aceitando um D2D1_BRUSH_PROPERTIES e uma estrutura D2D1_BITMAP_BRUSH_PROPERTIES .

if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateBitmapBrush(
        m_pBitmap,
        &m_pBitmapBrush
        );
}

O exemplo a seguir usa o pincel para preencher um retângulo.

m_pRenderTarget->FillRectangle(&rcBrushRect, m_pBitmapBrush);

Configurando modos de extensão

Às vezes, o gradiente de um pincel de gradiente ou o bitmap para um pincel de bitmap não preenche completamente a área que está sendo pintada.

A ilustração a seguir mostra os resultados de todas as combinações possíveis dos modos de extensão para um ID2D1BitmapBrush: D2D1_EXTEND_MODE_CLAMP (CLAMP), D2D1_EXTEND_MODE_WRAP (WRAP) e D2D1_EXTEND_MIRROR (MIRROR).

ilustração de uma imagem original e as imagens resultantes de vários modos de extensão

O exemplo a seguir mostra como definir os modos x e y-extend do pincel de bitmap como D2D1_EXTEND_MIRROR. Em seguida, ele pinta o retângulo com o ID2D1BitmapBrush.

m_pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_MIRROR);
m_pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_MIRROR);

m_pRenderTarget->FillRectangle(exampleRectangle, m_pBitmapBrush);

Ele produz a saída, conforme mostrado na ilustração a seguir.

ilustração de uma imagem original e a imagem resultante depois de espelhar a direção x e a direção y

Transformando pincéis

Quando você pinta com um pincel, ele pinta no espaço de coordenadas do destino de renderização. Pincéis não se posicionam automaticamente para se alinharem ao objeto que está sendo pintado; por padrão, eles começam a pintar na origem (0, 0) do destino de renderização.

Você pode "mover" o gradiente definido por um ID2D1LinearGradientBrush para uma área de destino definindo seu ponto inicial e ponto final. Da mesma forma, você pode mover o gradiente definido por um ID2D1RadialGradientBrush alterando seu centro e raios.

Para alinhar o conteúdo de um ID2D1BitmapBrush à área que está sendo pintada, você pode usar o método SetTransform para traduzir o bitmap para o local desejado. Essa transformação afeta apenas o pincel; não afeta nenhum outro conteúdo desenhado pelo destino de renderização.

As ilustrações a seguir mostram o efeito de usar um ID2D1BitmapBrush para preencher um retângulo localizado em (100, 100). A ilustração à esquerda mostra o resultado do preenchimento do retângulo sem transformar o pincel: o bitmap é desenhado na origem do destino de renderização. Como resultado, apenas uma parte do bitmap aparece no retângulo. A ilustração à direita mostra o resultado da transformação do ID2D1BitmapBrush para que seu conteúdo seja deslocado 50 pixels para a direita e 50 pixels para baixo. O bitmap agora preenche o retângulo.

ilustração de um quadrado pintado com um pincel de bitmap sem transformar o pincel e transformando o pincel

O código a seguir mostra como fazer isso. Primeiro, aplique uma tradução ao ID2D1BitmapBrush, movendo o pincel 50 pixels para a direita ao longo do eixo x e 50 pixels para baixo ao longo do eixo y. Em seguida, use o ID2D1BitmapBrush para preencher o retângulo que tem o canto superior esquerdo em (100, 100) e o canto inferior direito em (200, 200).

// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
    hr = LoadResourceBitmap(
        m_pRenderTarget,
        m_pWICFactory,
        L"FERN",
        L"Image",
        &m_pBitmap
        );
   
}

if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateBitmapBrush(
        m_pBitmap,
        &m_pBitmapBrush
        );
}

D2D1_RECT_F rcTransformedBrushRect = D2D1::RectF(100, 100, 200, 200);

// Demonstrate the effect of transforming a bitmap brush.
m_pBitmapBrush->SetTransform(
     D2D1::Matrix3x2F::Translation(D2D1::SizeF(50,50))
     );

// To see the content of the rcTransformedBrushRect, comment
// out this statement.
m_pRenderTarget->FillRectangle(
     &rcTransformedBrushRect, 
     m_pBitmapBrush
     );

m_pRenderTarget->DrawRectangle(rcTransformedBrushRect, m_pBlackBrush, 1, NULL);