Introducción a las capas

En esta introducción se describen los conceptos básicos del uso de capas de Direct2D. Contiene las siguientes secciones:

¿Qué son las capas?

Las capas, representadas por objetos ID2D1Layer , permiten a una aplicación manipular un grupo de operaciones de dibujo. Para usar una capa, se "inserta" en un destino de representación. Las operaciones de dibujo posteriores por el destino de representación se dirigen a la capa. Después de terminar con la capa, se "extrae" la capa del destino de representación, que compone el contenido de la capa de vuelta al destino de representación.

Al igual que los pinceles, las capas son recursos dependientes del dispositivo creados por destinos de representación. Las capas se pueden usar en cualquier destino de representación del mismo dominio de recursos que contenga el destino de representación que lo creó. Sin embargo, un destino de representación solo puede usar un recurso de capa a la vez. Para obtener más información sobre los recursos, consulte La información general sobre los recursos.

Aunque las capas ofrecen una técnica de representación eficaz para producir efectos interesantes, un número excesivo de capas en una aplicación puede afectar negativamente a su rendimiento, debido a los diversos costos asociados a la administración de capas y recursos de capa. Por ejemplo, hay el costo de rellenar o borrar la capa y, a continuación, mezclarla de nuevo, especialmente en hardware de gama superior. A continuación, hay el costo de administrar los recursos de capa. Si se reasignan con frecuencia, los puestos resultantes en la GPU serán el problema más significativo. Al diseñar la aplicación, intente maximizar la reutilización de los recursos de capa.

Capas en Windows 8 y versiones posteriores

Windows 8 introdujo nuevas API relacionadas con la capa que simplifican, mejoran el rendimiento de y agregan características a las capas.

ID2D1DeviceContext y PushLayer

La interfaz ID2D1DeviceContext se deriva de la interfaz ID2D1RenderTarget y es clave para mostrar el contenido de Direct2D en Windows 8, para obtener más información sobre esta interfaz, vea Dispositivos y contextos de dispositivo. Con la interfaz de contexto del dispositivo, puede omitir la llamada al método CreateLayer y, a continuación, pasar NULL al método ID2D1DeviceContext::P ushLayer . Direct2D administra automáticamente el recurso de capa y puede compartir recursos entre capas y gráficos de efectos.

D2D1_LAYER_PARAMETERS1 y D2D1_LAYER_OPTIONS1

La estructura D2D1_LAYER_PARAMETERS1 es la misma que D2D1_LAYER_PARAMETERS, salvo que el miembro final de la estructura es ahora una enumeración D2D1_LAYER_OPTIONS1 .

D2D1_LAYER_OPTIONS1 no tiene ninguna opción ClearType y tiene dos opciones diferentes que puede usar para mejorar el rendimiento:

Modos de fusión

A partir de Windows 8, el contexto del dispositivo tiene un modo de combinación primitivo que determina cómo se combina cada primitivo con la superficie de destino. Este modo también se aplica a las capas cuando se llama al método PushLayer .

Por ejemplo, si usa una capa para recortar primitivos con transparencia, establezca el modo D2D1_PRIMITIVE_BLEND_COPY en el contexto del dispositivo para obtener los resultados adecuados. El modo de copia hace que el contexto del dispositivo interpole linealmente los 4 canales de color, incluido el canal alfa, de cada píxel con el contenido de la superficie de destino según la máscara geométrica de la capa.

Interoperación

A partir de Windows 8, Direct2D admite la interoperación con Direct3D y GDI mientras se inserta una capa o clip. Llama a ID2D1GdiInteropRenderTarget::GetDC mientras una capa se inserta para interoperar con GDI. Llama a ID2D1DeviceContext::Flush y, a continuación, se representa en la superficie subyacente para interoperar con Direct3D. Es tu responsabilidad representar dentro de la capa o clip con Direct3D o GDI. Si intenta representarse fuera de la capa o recortar los resultados no están definidos.

Creación de capas

Trabajar con capas requiere estar familiarizado con los métodos CreateLayer, PushLayer y PopLayer , y la estructura D2D1_LAYER_PARAMETERS , que contiene un conjunto de datos paramétricos que define cómo se puede usar la capa. En la lista siguiente se describen los métodos y la estructura.

  • Llame al método CreateLayer para crear un recurso de capa.

    Nota

    A partir de Windows 8, puede omitir la llamada al método CreateLayer y, a continuación, pasar NULL al método PushLayer en la interfaz ID2D1DeviceContext. Esto es más sencillo y permite a Direct2D administrar automáticamente el recurso de capa y compartir recursos entre capas y gráficos de efectos.

     

  • Después de que el destino de representación haya comenzado a dibujar (después de llamar a su método BeginDraw ), puede usar el método PushLayer . El método PushLayer agrega la capa especificada al destino de representación, de modo que el destino reciba todas las operaciones de dibujo posteriores hasta que se llame a PopLayer . Este método toma un objeto ID2D1Layer devuelto mediante una llamada a CreateLayer y un layerParameters en la estructura D2D1_LAYER_PARAMETERS . En la tabla siguiente se describen los campos de la estructura.

    Campo Descripción
    contentBounds Límites de contenido de la capa. El contenido no se representará fuera de estos límites. Este parámetro tiene como valor predeterminado InfiniteRect. Cuando se usa el valor predeterminado, los límites de contenido se toman efectivamente para que sean los límites del destino de representación.
    geometrMask (Opcional) El área, definida por un ID2D1Geometry, a la que se debe recortar la capa. Se establece en NULL si la capa no se debe recortar en una geometría.
    maskAntialiasMode Valor que especifica el modo de suavizado de contorno para la máscara geométrica especificada por el campo geometricMask .
    maskTransform Valor que especifica la transformación que se aplica a la máscara geométrica al componer la capa. Esto es relativo a la transformación del mundo.
    Opacidad Valor de opacidad de la capa. La opacidad de cada recurso de la capa se multiplica por este valor al componer en el destino.
    opacidadBrush (Opcional) Pincel que se usa para modificar la opacidad de la capa. El pincel se asigna a la capa y el canal alfa de cada píxel de pincel asignado se multiplica por el píxel de capa correspondiente. Se establece en NULL si la capa no debe tener una máscara de opacidad.
    layerOptions Valor que especifica si la capa pretende representar texto con suavizado de contorno ClearType. Este parámetro se desactiva de forma predeterminada. Activarla permite que ClearType funcione correctamente, pero da como resultado una velocidad de representación ligeramente más lenta.

     

    Nota

    A partir de Windows 8, no se puede representar con ClearType en una capa, por lo que el parámetro layerOptions siempre debe establecerse en D2D1_LAYER_OPTIONS_NONE

     

    Para mayor comodidad, Direct2D proporciona el método D2D1::LayerParameters para ayudarle a crear estructuras D2D1_LAYER_PARAMETERS .

  • Para componer el contenido de la capa en el destino de representación, llame al método PopLayer . Debe llamar al método PopLayer antes de llamar al método EndDraw .

En el ejemplo siguiente se muestra cómo usar CreateLayer, PushLayer y PopLayer. Todos los campos de la estructura D2D1_LAYER_PARAMETERS se establecen en sus valores predeterminados, excepto opacityBrush, que se establece en un ID2D1RadialGradientBrush.

// Create a layer.
ID2D1Layer *pLayer = NULL;
hr = pRT->CreateLayer(NULL, &pLayer);

if (SUCCEEDED(hr))
{
    pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

    // Push the layer with the content bounds.
    pRT->PushLayer(
        D2D1::LayerParameters(
            D2D1::InfiniteRect(),
            NULL,
            D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
            D2D1::IdentityMatrix(),
            1.0,
            m_pRadialGradientBrush,
            D2D1_LAYER_OPTIONS_NONE),
        pLayer
        );

    pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

    pRT->FillRectangle(
        D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
        m_pSolidColorBrush
        );
    pRT->FillRectangle(
        D2D1::RectF(50.f, 50.f, 75.f, 75.f),
        m_pSolidColorBrush
        ); 
    pRT->FillRectangle(
        D2D1::RectF(75.f, 75.f, 100.f, 100.f),
        m_pSolidColorBrush
        );    
 
    pRT->PopLayer();
}
SafeRelease(&pLayer);

El código se ha omitido en este ejemplo.

Tenga en cuenta que, al llamar a PushLayer y PopLayer, asegúrese de que cada PushLayer tenga una llamada PopLayer coincidente. Si hay más llamadas PopLayer que las llamadas pushLayer , el destino de representación se coloca en un estado de error. Si se llama a Flush antes de que se extraan todas las capas pendientes, el destino de representación se coloca en un estado de error y devuelve un error. Para borrar el estado del error, use EndDraw.

Límites de contenido

ContentBounds establece el límite de lo que se va a dibujar en la capa. Solo esos elementos dentro de los límites de contenido se vuelven a componer en el destino de representación.

En el ejemplo siguiente se muestra cómo especificar contentBounds para que la imagen original se recorte en los límites de contenido con la esquina superior izquierda en (10, 108) y la esquina inferior derecha en (121, 177). En la ilustración siguiente se muestra la imagen original y el resultado de recortar la imagen en los límites de contenido.

illustration of content bounds on an original picture and the resulting clipped picture

HRESULT DemoApp::RenderWithLayerWithContentBounds(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 0));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::RectF(10, 108, 121, 177)),
            pLayer
            );

        pRT->DrawBitmap(m_pWaterBitmap, D2D1::RectF(0, 0, 128, 192));
        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

El código se ha omitido en este ejemplo.

Nota

La imagen recortada resultante se ve afectada aún más si se especifica una máscara geométrica. Consulte la sección Máscaras geométricas para obtener más información.

 

Máscaras geométricas

Una máscara geométrica es un clip o un recorte, definido por un objeto ID2D1Geometry , que enmascara una capa cuando se dibuja mediante un destino de representación. Puede usar el campo geométricoMask de la estructura D2D1_LAYER_PARAMETERS para enmascarar los resultados en una geometría. Por ejemplo, si desea mostrar una imagen enmascarada por una letra de bloque "A", primero puede crear una geometría que represente la letra de bloque "A" y usarla como máscara geométrica para una capa. A continuación, después de insertar la capa, puede dibujar la imagen. Al extraer la capa, la imagen se recorta en la forma "A" de la letra de bloque.

En el ejemplo siguiente se muestra cómo crear un ID2D1PathGeometry que contiene una forma de una montaña y, a continuación, pasar la geometría de la ruta de acceso a PushLayer. A continuación, dibuja un mapa de bits y cuadrados. Si solo hay un mapa de bits en la capa que se va a representar, use FillGeometry con un pincel de mapa de bits fijado para mejorar la eficacia. En la siguiente ilustración se muestra el resultado del ejemplo.

illustration of a picture of a leaf and the resulting picture after a geometric mask of a mountain is applied

En el primer ejemplo se define la geometría que se va a usar como máscara.

hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometry);
    
if(SUCCEEDED(hr))
{
    ID2D1GeometrySink *pSink = NULL;
    // Write to the path geometry using the geometry sink.
    hr = m_pPathGeometry->Open(&pSink);

    if (SUCCEEDED(hr))
    {
        pSink->SetFillMode(D2D1_FILL_MODE_WINDING);
        pSink->BeginFigure(
            D2D1::Point2F(0, 90),
            D2D1_FIGURE_BEGIN_FILLED
            );

        D2D1_POINT_2F points[7] = {
           D2D1::Point2F(35, 30),
           D2D1::Point2F(50, 50),
           D2D1::Point2F(70, 45),
           D2D1::Point2F(105, 90),
           D2D1::Point2F(130, 90),
           D2D1::Point2F(150, 60),
           D2D1::Point2F(170, 90)
           };

        pSink->AddLines(points, 7);
        pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
        hr = pSink->Close();
    }
    SafeRelease(&pSink);
       }

En el ejemplo siguiente se usa la geometría como máscara para la capa.

HRESULT DemoApp::RenderWithLayerWithGeometricMask(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 450));

        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::InfiniteRect(), m_pPathGeometry),
            pLayer
            );

        pRT->DrawBitmap(m_pLeafBitmap, D2D1::RectF(0, 0, 198, 132));

        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f), 
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );        

        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

El código se ha omitido en este ejemplo.

Nota

En general, si especificas un geometrMask, puedes usar el valor predeterminado, InfiniteRect, para contentBounds.

Si contentBounds es NULL y geometrMask es distinto de NULL, los límites de contenido son efectivamente los límites de la máscara geométrica después de aplicar la transformación de máscara.

Si contentBounds no es NULL y geometrMask es distinto de NULL, la máscara geométrica transformada se recorta eficazmente en los límites de contenido y se supone que los límites de contenido son infinitos.

 

Máscaras de opacidad

Una máscara de opacidad es una máscara, descrita por un pincel o mapa de bits, que se aplica a otro objeto para que ese objeto sea parcialmente o completamente transparente. Permite usar el canal alfa de un pincel como máscara de contenido. Por ejemplo, puede definir un pincel de degradado radial que varía de opaco a transparente para crear un efecto de viñeta.

En el ejemplo siguiente se usa id2D1RadialGradientBrush (m_pRadialGradientBrush) como máscara de opacidad. A continuación, dibuja un mapa de bits y cuadrados. Si solo hay un mapa de bits en la capa que se va a representar, use FillGeometry con un pincel de mapa de bits fijado para mejorar la eficacia. En la ilustración siguiente se muestra la salida de este ejemplo.

illustration of a picture of trees and the resulting picture after an opacity mask is applied

HRESULT DemoApp::RenderWithLayerWithOpacityMask(ID2D1RenderTarget *pRT)
{   

    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(
                D2D1::InfiniteRect(),
                NULL,
                D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                D2D1::IdentityMatrix(),
                1.0,
                m_pRadialGradientBrush,
                D2D1_LAYER_OPTIONS_NONE),
            pLayer
            );

        pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

        pRT->FillRectangle(
            D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
            m_pSolidColorBrush
            );
        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f),
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );    
 
        pRT->PopLayer();
    }
    SafeRelease(&pLayer);
   
    return hr;
    
}

El código se ha omitido en este ejemplo.

Nota

En este ejemplo se usa una capa para aplicar una máscara de opacidad a un solo objeto para que el ejemplo sea lo más sencillo posible. Al aplicar una máscara de opacidad a un solo objeto, es más eficaz usar los métodos FillOpacityMask o FillGeometry , en lugar de una capa.

 

Para obtener instrucciones sobre cómo aplicar una máscara de opacidad sin usar una capa, consulte información general sobre máscaras de opacidad.

Alternativas a las capas

Como se mencionó anteriormente, el número excesivo de capas puede afectar negativamente al rendimiento de la aplicación. Para mejorar el rendimiento, evite el uso de capas siempre que sea posible; en su lugar, use sus alternativas. En el ejemplo de código siguiente se muestra cómo usar PushAxisAlignedClip y PopAxisAlignedClip para recortar una región, como alternativa al uso de una capa con límites de contenido.

pRT->PushAxisAlignedClip(
    D2D1::RectF(20, 20, 100, 100),
    D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
    );

pRT->FillRectangle(D2D1::RectF(0, 0, 200, 133), m_pOriginalBitmapBrush);
pRT->PopAxisAlignedClip();

De forma similar, use FillGeometry con un pincel de mapa de bits fijado como alternativa al uso de una capa con una máscara de opacidad cuando solo hay un contenido en la capa que se va a representar, como se muestra en el ejemplo siguiente.

        m_pRenderTarget->FillGeometry(
            m_pRectGeo, 
            m_pLinearFadeFlowersBitmapBrush, 
            m_pLinearGradientBrush
            );

Como alternativa al uso de una capa con una máscara geométrica, considere la posibilidad de usar una máscara de mapa de bits para recortar una región, como se muestra en el ejemplo siguiente.

// D2D1_ANTIALIAS_MODE_ALIASED must be set for FillOpacityMask
// to function properly.
m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

m_pRenderTarget->FillOpacityMask(
    m_pBitmapMask,
    m_pOriginalBitmapBrush,
    D2D1_OPACITY_MASK_CONTENT_GRAPHICS,
    &rcBrushRect,
    NULL
    );

m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);

Por último, si desea aplicar opacidad a un único primitivo, debe multiplicar la opacidad en en el color del pincel y, a continuación, representar el primitivo. No necesita una capa ni un mapa de bits de máscara de opacidad.

float opacity = 0.9f;

ID2D1SolidColorBrush *pBrush = NULL;
hr = pCompatibleRenderTarget->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f * opacity)),
    &pBrush
    );

m_pRenderTarget->FillRectangle(
    D2D1::RectF(50.0f, 50.0f, 75.0f, 75.0f), 
    pBrush
    ); 

Recorte de una forma arbitraria

En la ilustración siguiente se muestra el resultado de aplicar un clip a una imagen.

an image that shows an example of an image before and after a clip.

Puede obtener este resultado mediante capas con una máscara de geometría o el método FillGeometry con un pincel de opacidad.

Este es un ejemplo que usa una capa:

// Call PushLayer() and pass in the clipping geometry.
m_d2dContext->PushLayer(
    D2D1::LayerParameters(
        boundsRect,
        geometricMask));

Este es un ejemplo que usa el método FillGeometry :

// Create an opacity bitmap and render content.
m_d2dContext->CreateBitmap(size, nullptr, 0,
    D2D1::BitmapProperties(
        D2D1_BITMAP_OPTIONS_TARGET,
        D2D1::PixelFormat(
            DXGI_FORMAT_A8_UNORM,
            D2D1_ALPHA_MODE_PREMULTIPLIED),
        dpiX, dpiY),
    &opacityBitmap);

m_d2dContext->SetTarget(opacityBitmap.Get());
m_d2dContext->BeginDraw();
…
m_d2dContext->EndDraw();

// Create an opacity brush from the opacity bitmap.
m_d2dContext->CreateBitmapBrush(opacityBitmap.Get(),
    D2D1::BitmapBrushProperties(),
    D2D1::BrushProperties(),
    &bitmapBrush);

// Call the FillGeometry method and pass in the clip geometry and the opacity brush
m_d2dContext->FillGeometry( 
    clipGeometry.Get(),
    brush.Get(),
    opacityBrush.Get()); 

En este ejemplo de código, al llamar al método PushLayer, no se pasa una capa creada por la aplicación. Direct2D crea una capa automáticamente. Direct2D puede administrar la asignación y destrucción de este recurso sin intervención de la aplicación. Esto permite a Direct2D reutilizar las capas internamente y aplicar optimizaciones de administración de recursos.

Nota

En Windows 8 se han realizado muchas optimizaciones para el uso de capas y se recomienda probar a usar las API de capa en lugar de FillGeometry siempre que sea posible.

 

Clips alineados con ejes

Si la región que se va a recortar se alinea con el eje de la superficie de dibujo, en lugar de arbitraria. Este caso es adecuado para usar un rectángulo de recorte en lugar de una capa. La ganancia de rendimiento es mayor para la geometría con alias que la geometría suavizada. Para obtener más información sobre los clips alineados con ejes, consulta el tema PushAxisAlignedClip .

Referencia de Direct2D