Información general sobre los pinceles

En esta introducción se describe cómo crear y usar objetos ID2D1SolidColorBrush, ID2D1LinearGradientBrush, ID2D1RadialGradientBrush e ID2D1BitmapBrush para pintar áreas con colores sólidos, degradados y mapas de bits. Contiene las siguientes secciones:

Requisitos previos

En esta introducción se da por supuesto que está familiarizado con la estructura de una aplicación básica de Direct2D, como se describe en Creación de una aplicación direct2D sencilla.

Tipos de pincel

Un pincel "pinta" un área con su salida. Distintos pinceles tienen tipos de salida diferentes. Direct2D proporciona cuatro tipos de pincel: ID2D1SolidColorBrush pinta un área con un color sólido, ID2D1LinearGradientBrush con un degradado lineal, ID2D1RadialGradientBrush con un degradado radial y ID2D1BitmapBrush con un mapa de bits.

Nota:

A partir de Windows 8, también puedes usar ID2D1ImageBrush, que es similar a un pincel de mapa de bits, pero también puedes usar primitivos.

Todos los pinceles heredan de ID2D1Brush y comparten un conjunto de características comunes (configuración y obtención de opacidad y transformación de pinceles); se crean mediante ID2D1RenderTarget y son recursos dependientes del dispositivo: la aplicación debe crear pinceles después de inicializar el destino de representación con el que se usarán los pinceles y volver a crear los pinceles cada vez que se necesite volver a crear el destino de representación. (Para obtener más información sobre los recursos, consulte Información general sobre recursos).

En la ilustración siguiente se muestran ejemplos de cada uno de los distintos tipos de pincel.

ilustración de los efectos visuales de pinceles de color sólido, pinceles de degradado lineal, pinceles de degradado radial y pinceles de mapa de bits

Conceptos básicos de color

Antes de pintar con un pincel ID2D1SolidColorBrush o un pincel degradado, debe elegir colores. En Direct2D, los colores se representan mediante la estructura D2D1_COLOR_F (que en realidad es solo un nuevo nombre para la estructura que usa Direct3D, D3DCOLORVALUE).

Antes de Windows 8, D2D1_COLOR_F usa la codificación sRGB. La codificación sRGB divide los colores en cuatro componentes: rojo, verde, azul y alfa. Cada componente se representa mediante un valor de punto flotante con un intervalo normal de 0,0 a 1,0. Un valor de 0,0 indica la ausencia completa de ese color, mientras que un valor de 1,0 indica que el color está completamente presente. Para el componente alfa, 0,0 representa un color completamente transparente y 1,0 representa un color totalmente opaco.

A partir de Windows 8, D2D1_COLOR_F también acepta codificación scRGB. scRGB es un superconjunto de que permite valores de color superiores a 1,0 y por debajo de 0,0.

Para definir un color, puede usar la estructura D2D1_COLOR_F e inicializar sus campos usted mismo, o bien puede usar la clase D2D1::ColorF para ayudarle a crear el color. La clase ColorF proporciona varios constructores para definir colores. Si el valor alfa no se especifica en los constructores, el valor predeterminado es 1.0.

  • Use el constructor ColorF(Enum, FLOAT) para especificar un color predefinido y un valor de canal alfa. Un valor de canal alfa oscila entre 0,0 y 1,0, donde 0,0 representa un color totalmente transparente y 1,0 representa un color totalmente opaco. En la ilustración siguiente se muestran varios colores predefinidos y sus equivalentes hexadecimales. Para obtener una lista completa de los colores predefinidos, vea la sección Constantes de color de la clase ColorF .

    ilustración de colores predefinidos

    En el ejemplo siguiente se crea un color predefinido y se usa para especificar el color de un ID2D1SolidColorBrush.

hr = m_pRenderTarget->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
    &m_pBlackBrush
    );
  • Utilice el constructor ColorF(FLOAT, FLOAT, FLOAT, FLOAT) para especificar un color en la secuencia de un color rojo, verde, azul y alfa, donde cada elemento tiene un valor entre 0,0 y 1,0.

    En el ejemplo siguiente se especifican los valores rojo, verde, azul y alfa de un color.

    ID2D1SolidColorBrush *pGridBrush = NULL;
    hr = pCompatibleRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f)),
        &pGridBrush
        );
  • Use el constructor ColorF(UINT32, FLOAT) para especificar el valor hexadecimal de un color y un valor alfa, como se muestra en el ejemplo siguiente.
    hr = m_pRenderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),  
        &m_pYellowGreenBrush
        );

Modos alfa

Independientemente del modo alfa del destino de representación con el que se usa un pincel, los valores de D2D1_COLOR_F siempre se interpretan como alfa recto.

Uso de pinceles de color sólido

Para crear un pincel de color sólido, llame al método ID2D1RenderTarget::CreateSolidColorBrush , que devuelve un HRESULT y un objeto ID2D1SolidColorBrush . En la ilustración siguiente se muestra un cuadrado que se dibuja con un pincel de color negro y pintado con un pincel de color sólido que tiene el valor de color de 0x9ACD32.

ilustración de un cuadrado pintado con un pincel de color sólido

En el código siguiente se muestra cómo crear y usar un pincel de color negro y un pincel con un valor de color de 0x9ACD32 para rellenar y dibujar este cuadrado.

    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);

A diferencia de otros pinceles, crear un ID2D1SolidColorBrush es una operación relativamente económica. Puede crear objetos ID2D1SolidColorBrush cada vez que represente con poco o ningún impacto en el rendimiento. Este enfoque no se recomienda para pinceles de degradado o mapa de bits.

Uso de pinceles de degradado lineal

Un ID2D1LinearGradientBrush pinta un área con un degradado lineal definido a lo largo de una línea, el eje de degradado. Especifique los colores del degradado y su ubicación a lo largo del eje de degradado mediante objetos ID2D1GradientStop . También puede modificar el eje de degradado, lo que le permite crear degradado horizontal y vertical y invertir la dirección del degradado. Para crear un pincel de degradado lineal, llame al método ID2D1RenderTarget::CreateLinearGradientBrush .

En la ilustración siguiente se muestra un cuadrado que se pinta con un ID2D1LinearGradientBrush que tiene dos colores predefinidos, "Amarillo" y "ForestGreen".

ilustración de un cuadrado pintado con un pincel degradado lineal de color amarillo y verde bosque

Para crear el degradado que se muestra en la ilustración anterior, complete estos pasos:

  1. Declare dos objetos D2D1_GRADIENT_STOP . Cada detención de degradado especifica un color y una posición. Una posición de 0,0 indica el principio del degradado, mientras que una posición de 1,0 indica el final del degradado.

    El código siguiente crea una matriz de dos objetos D2D1_GRADIENT_STOP . La primera parada especifica el color "Amarillo" en una posición 0 y la segunda parada especifica el color "ForestGreen" en la posición 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. Cree una colección ID2D1GradientStopCollection. En el ejemplo siguiente se llama a CreateGradientStopCollection, pasando la matriz de objetos D2D1_GRADIENT_STOP , el número de paradas de degradado (2), D2D1_GAMMA_2_2 para la interpolación y D2D1_EXTEND_MODE_CLAMP para el modo de extensión.
    // 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. Cree id2D1LinearGradientBrush. En el ejemplo siguiente se llama al método CreateLinearGradientBrush y se le pasan las propiedades de pincel de degradado lineal que contienen el punto inicial en (0, 0) y el punto final en (150, 150) y las paradas de degradado creadas en el paso 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 id2D1LinearGradientBrush. En el ejemplo de código siguiente se usa el pincel para rellenar un rectángulo.
    m_pRenderTarget->FillRectangle(&rcBrushRect, m_pLinearGradientBrush);

Más información sobre las paradas de degradado

El D2D1_GRADIENT_STOP es el bloque de creación básico de un pincel degradado. Un delimitador de degradado especifica el color y la posición a lo largo del eje de degradado. El valor de la posición de degradado oscila entre 0,0 y 1,0. Cuanto más cerca se acerque a 0,0, cuanto más cerca esté el color es al principio del degradado; cuanto más cerca esté a 1,0, más cerca estará el color hasta el final del degradado.

En la ilustración siguiente se resaltan los delimitadores de degradado. El círculo marca la posición de los delimitadores de degradado y una línea discontinua muestra el eje de degradado.

ilustración de un pincel de degradado lineal con cuatro paradas a lo largo del eje

La primera parada de degradado especifica el color amarillo en una posición de 0,0. La segunda parada de degradado especifica el color rojo en una posición de 0,25. De izquierda a derecha a lo largo del eje degradado, los colores entre estos dos se detiene gradualmente cambian de amarillo a rojo. La tercera parada de degradado especifica el color azul en una posición de 0,75. Los colores entre el segundo y el tercer degradado dejan de cambiar gradualmente de rojo a azul. La cuarta parada de degradado especifica verde lima en una posición de 1,0. Los colores entre el tercer y el cuarto degradado dejan de cambiar gradualmente de azul a verde lima.

Eje de degradado

Como se mencionó anteriormente, las paradas de degradado de un pincel de degradado lineal se colocan a lo largo de una línea, el eje de degradado. Puede especificar la orientación y el tamaño de la línea mediante los campos startPoint y endPoint de la estructura D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES al crear un pincel de degradado lineal. Después de crear un pincel, puede ajustar el eje de degradado llamando a los métodos SetStartPoint y SetEndPoint del pincel. Al manipular el punto inicial y el punto final del pincel, puede crear degradados horizontales y verticales, invertir la dirección del degradado, etc.

Por ejemplo, en la ilustración siguiente, el punto inicial se establece en (0,0) y el punto final en (150, 50); esto crea un degradado diagonal que comienza en la esquina superior izquierda y se extiende a la esquina inferior derecha del área que se está pintando. Al establecer el punto de inicio en (0, 25) y el punto final en (150, 25), se crea un degradado horizontal. Del mismo modo, al establecer el punto de inicio en (75, 0) y el punto final en (75, 50) se crea un degradado vertical. Al establecer el punto de inicio en (0, 50) y el punto final en (150, 0), se crea un degradado diagonal que comienza en la esquina inferior izquierda y se extiende a la esquina superior derecha del área que se está pintando.

ilustración de cuatro ejes de degradado diferentes en el mismo rectángulo

Uso de pinceles de degradado radial

A diferencia de un ID2D1LinearGradientBrush, que combina dos o más colores a lo largo de un eje degradado, un ID2D1RadialGradientBrush pinta un área con un degradado radial que combina dos o más colores en una elipse. Mientras que un ID2D1LinearGradientBrush define su eje de degradado con un punto de inicio y un punto final, un ID2D1RadialGradientBrush define su elipse de degradado especificando un radio central, horizontal y vertical, y un desplazamiento de origen degradado.

Al igual que un ID2D1LinearGradientBrush, un ID2D1RadialGradientBrush usa id2D1GradientStopCollection para especificar los colores y las posiciones en el degradado.

En la ilustración siguiente se muestra un círculo pintado con un ID2D1RadialGradientBrush. El círculo tiene dos paradas de degradado: la primera especifica un color predefinido "Amarillo" en una posición de 0,0 y el segundo especifica un color predefinido "ForestGreen" en una posición de 1,0. El degradado tiene un centro de (75, 75), un desplazamiento de origen de degradado de (0, 0) y un radio x e y de 75.

ilustración de un círculo pintado con un pincel degradado radial

En los ejemplos de código siguientes se muestra cómo pintar este círculo con un ID2D1RadialGradientBrush que tiene dos paradas de color: "Amarillo" en una posición de 0,0 y "ForestGreen" en una posición de 1,0. De forma similar a la creación de un ID2D1LinearGradientBrush, el ejemplo llama a CreateGradientStopCollection para crear una colección ID2D1GradientStopCollection a partir de una matriz de paradas de degradado.

// 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 crear un id2D1RadialGradientBrush, use el método ID2D1RenderTarget::CreateRadialGradientBrush . CreateRadialGradientBrush toma tres parámetros. El primer parámetro, un D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES especifica el centro, el desplazamiento de origen degradado y los radios horizontales y verticales del degradado. El segundo parámetro es un ID2D1GradientStopCollection que describe los colores y sus posiciones en el degradado, y el tercer parámetro es la dirección del puntero que recibe la nueva referencia id2D1RadialGradientBrush . Algunas sobrecargas toman un parámetro adicional, una estructura de D2D1_BRUSH_PROPERTIES que especifica un valor de opacidad y una transformación que se aplicará al nuevo pincel.

En el ejemplo siguiente se llama a CreateRadialGradientBrush, pasando la matriz de paradas de degradado y las propiedades del pincel de degradado radial que tienen el valor central establecido en (75, 75), gradientOriginOffset establecido en (0, 0) y radiusX y radiusY establecidos en 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
        );
}

En el ejemplo final se usa el pincel para rellenar una elipse.

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

Configuración de un degradado radial

Los distintos valores para center, gradientOriginOffset, radiusX o radiusY producen diferentes degradados. En la ilustración siguiente se muestran varios degradados radiales que tienen diferentes desplazamientos de origen de degradado, creando la apariencia de la luz iluminando los círculos desde distintos ángulos.

ilustración del mismo círculo pintado con pinceles de degradado radial con diferentes desplazamientos de origen

Uso de pinceles de mapa de bits

Un ID2D1BitmapBrush pinta un área con un mapa de bits (representado por un objeto ID2D1Bitmap ).

En la ilustración siguiente se muestra un cuadrado pintado con un mapa de bits de una planta.

ilustración de un cuadrado pintado con mapa de bits de planta

Los ejemplos siguientes muestran cómo pintar este cuadrado con un ID2D1BitmapBrush.

En el primer ejemplo se inicializa un objeto ID2D1Bitmap para usarlo con el pincel. Id2D1Bitmap lo proporciona un método auxiliar, LoadResourceBitmap, definido en otro lugar del ejemplo.

// 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 crear el pincel de mapa de bits, llame al método ID2D1RenderTarget::CreateBitmapBrush y especifique el id2D1Bitmap con el que se va a pintar. El método devuelve un HRESULT y un objeto ID2D1BitmapBrush . Algunas sobrecargas de CreateBitmapBrush permiten especificar opciones adicionales aceptando un D2D1_BRUSH_PROPERTIES y una estructura de D2D1_BITMAP_BRUSH_PROPERTIES .

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

En el ejemplo siguiente se usa el pincel para rellenar un rectángulo.

m_pRenderTarget->FillRectangle(&rcBrushRect, m_pBitmapBrush);

Configuración de modos de extensión

A veces, el degradado de un pincel degradado o el mapa de bits de un pincel de mapa de bits no rellena completamente el área que se está pintando.

En la ilustración siguiente se muestran los resultados de cada combinación posible de los modos de extensión para un ID2D1BitmapBrush: D2D1_EXTEND_MODE_CLAMP (CLAMP), D2D1_EXTEND_MODE_WRAP (WRAP) y D2D1_EXTEND_MIRROR (MIRROR).

ilustración de una imagen original y las imágenes resultantes de varios modos de extensión

En el ejemplo siguiente se muestra cómo establecer los modos de extensión x e y del pincel del mapa de bits en D2D1_EXTEND_MIRROR. A continuación, pinta el rectángulo con id2D1BitmapBrush.

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

m_pRenderTarget->FillRectangle(exampleRectangle, m_pBitmapBrush);

Genera la salida como se muestra en la ilustración siguiente.

ilustración de una imagen original y la imagen resultante después de crear reflejo de la dirección x e y-direction

Transformación de pinceles

Al pintar con un pincel, pinta en el espacio de coordenadas del destino de representación. Los pinceles no se colocan automáticamente para alinearse con el objeto que se está pintando; De forma predeterminada, comienzan a pintar en el origen (0, 0) del destino de representación.

Puede "mover" el degradado definido por un ID2D1LinearGradientBrush a un área de destino estableciendo su punto inicial y punto final. Del mismo modo, puede mover el degradado definido por un ID2D1RadialGradientBrush cambiando su centro y radio.

Para alinear el contenido de un ID2D1BitmapBrush con el área que se está pintando, puede usar el método SetTransform para traducir el mapa de bits a la ubicación deseada. Esta transformación solo afecta al pincel; no afecta a ningún otro contenido dibujado por el destino de representación.

En las ilustraciones siguientes se muestra el efecto de usar un ID2D1BitmapBrush para rellenar un rectángulo situado en (100, 100). En la ilustración de la izquierda se muestra el resultado de rellenar el rectángulo sin transformar el pincel: el mapa de bits se dibuja en el origen del destino de representación. Como resultado, solo aparece una parte del mapa de bits en el rectángulo. La ilustración de la derecha muestra el resultado de transformar id2D1BitmapBrush para que su contenido se desplaze 50 píxeles a la derecha y 50 píxeles hacia abajo. El mapa de bits ahora rellena el rectángulo.

ilustración de un cuadrado pintado con un pincel de mapa de bits sin transformar el pincel y mediante la transformación del pincel

En el código siguiente se muestra cómo hacerlo. En primer lugar, aplique una traducción a ID2D1BitmapBrush, moviendo el pincel 50 píxeles a lo largo del eje X y 50 píxeles hacia abajo a lo largo del eje Y. A continuación, use id2D1BitmapBrush para rellenar el rectángulo que tiene la esquina superior izquierda en (100, 100) y la esquina inferior derecha en (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);