레이어 개요

이 개요에서는 Direct2D 계층 사용의 기본 사항을 설명합니다. 이 항목에는 다음과 같은 섹션이 포함됩니다.

레이어란?

ID2D1Layer 개체로 표현되는 레이어를 사용하면 애플리케이션이 그리기 작업 그룹을 조작할 수 있습니다. 레이어를 렌더링 대상에 "푸시"하여 사용합니다. 렌더링 대상에 의한 후속 그리기 작업은 레이어로 전달됩니다. 레이어를 완료한 후에는 렌더링 대상에서 레이어를 "팝"하여 레이어의 콘텐츠를 렌더링 대상으로 다시 합성합니다.

브러시와 마찬가지로 레이어는 렌더링 대상에서 만든 디바이스 종속 리소스입니다. 레이어를 만든 렌더링 대상이 포함된 동일한 리소스 도메인의 모든 렌더링 대상에서 레이어를 사용할 수 있습니다. 그러나 레이어 리소스는 한 번에 하나의 렌더링 대상에서만 사용할 수 있습니다. 리소스에 대한 자세한 내용은 리소스 개요를 참조하세요.

레이어는 흥미로운 효과를 생성하기 위한 강력한 렌더링 기술을 제공하지만, 레이어 및 계층 리소스 관리와 관련된 다양한 비용으로 인해 애플리케이션의 과도한 레이어 수가 성능에 부정적인 영향을 줄 수 있습니다. 예를 들어 계층을 채우거나 지운 다음, 특히 고급 하드웨어에서 다시 혼합하는 데 드는 비용이 있습니다. 그런 다음 계층 리소스를 관리하는 데 드는 비용이 있습니다. 이러한 항목을 자주 재할당하는 경우 GPU에 대한 결과 중단이 가장 중요한 문제가 됩니다. 애플리케이션을 디자인할 때 계층 리소스 재사용을 최대화합니다.

Windows 8 이상의 레이어

Windows 8 계층에 기능을 간소화하고, 성능을 향상시키고, 기능을 추가하는 새로운 레이어 관련 API를 도입했습니다.

ID2D1DeviceContext 및 PushLayer

ID2D1DeviceContext 인터페이스는 ID2D1RenderTarget 인터페이스에서 파생되며 Windows 8 Direct2D 콘텐츠를 표시하는 데 핵심적인 요소입니다. 이 인터페이스에 대한 자세한 내용은 디바이스 및 디바이스 컨텍스트를 참조하세요. 디바이스 컨텍스트 인터페이스를 사용하면 CreateLayer 메서드 호출을 건너뛰고 ID2D1DeviceContext::P ushLayer 메서드에 NULL을 전달할 수 있습니다. Direct2D는 레이어 리소스를 자동으로 관리하고 레이어와 효과 그래프 간에 리소스를 공유할 수 있습니다.

D2D1_LAYER_PARAMETERS1 및 D2D1_LAYER_OPTIONS1

D2D1_LAYER_PARAMETERS1 구조체는 D2D1_LAYER_PARAMETERS 동일하며, 구조체의 최종 멤버는 이제 D2D1_LAYER_OPTIONS1 열거형입니다.

D2D1_LAYER_OPTIONS1 ClearType 옵션이 없으며 성능을 개선하는 데 사용할 수 있는 두 가지 옵션이 있습니다.

혼합 모드

Windows 8 디바이스 컨텍스트에는 각 기본 형식이 대상 표면과 혼합되는 방식을 결정하는 기본 혼합 모드가 있습니다. 이 모드는 PushLayer 메서드를 호출할 때 계층에도 적용됩니다.

예를 들어 레이어를 사용하여 투명도가 있는 기본 형식을 클리핑하는 경우 적절한 결과를 위해 디바이스 컨텍스트에서 D2D1_PRIMITIVE_BLEND_COPY 모드를 설정합니다. 복사 모드는 디바이스 컨텍스트 선형이 레이어의 기하학적 마스크에 따라 대상 표면의 내용과 함께 각 픽셀의 알파 채널을 포함한 4개의 색 채널을 모두 보간하도록 합니다.

상호 운용성

Windows 8 Direct2D는 레이어 또는 클립이 푸시되는 동안 Direct3D 및 GDI와의 상호 운용을 지원합니다. 계층이 GDI와 상호 운용되도록 푸시되는 동안 ID2D1GdiInteropRenderTarget::GetDC 를 호출합니다. ID2D1DeviceContext::Flush를 호출한 다음 기본 표면으로 렌더링하여 Direct3D와 상호 운용합니다. Direct3D 또는 GDI를 사용하여 레이어 또는 클립 내에서 렌더링하는 것은 사용자의 책임입니다. 레이어 외부로 렌더링하거나 클리핑하려고 하면 결과가 정의되지 않습니다.

레이어 만들기

레이어를 사용하려면 CreateLayer, PushLayerPopLayer 메서드와 레이어를 사용하는 방법을 정의하는 매개 변수 데이터 집합이 포함된 D2D1_LAYER_PARAMETERS 구조에 대해 잘 알고 있어야 합니다. 다음 목록에서는 메서드 및 구조에 대해 설명합니다.

  • CreateLayer 메서드를 호출하여 계층 리소스를 만듭니다.

    참고

    Windows 8 시작하여 CreateLayer 메서드 호출을 건너뛰고 ID2D1DeviceContext 인터페이스의 PushLayer 메서드에 NULL을 전달할 수 있습니다. 이는 더 간단하며 Direct2D가 레이어 리소스를 자동으로 관리하고 레이어와 효과 그래프 간에 리소스를 공유할 수 있도록 합니다.

     

  • 렌더링 대상이 그리기를 시작한 후( BeginDraw 메서드가 호출된 후) PushLayer 메서드를 사용할 수 있습니다. PushLayer 메서드는 지정된 레이어를 렌더링 대상에 추가하여 PopLayer가 호출될 때까지 대상이 모든 후속 그리기 작업을 수신하도록 합니다. 이 메서드는 CreateLayer를 호출하여 반환된 ID2D1Layer 개체와 D2D1_LAYER_PARAMETERS 구조체의 layerParameters를 사용합니다. 다음 표에서는 구조체의 필드를 설명합니다.

    필드 설명
    contentBounds 레이어의 콘텐츠 범위입니다. 콘텐츠는 이러한 범위를 벗어나 렌더링되지 않습니다. 이 매개 변수는 기본적으로 InfiniteRect로 설정됩니다. 기본값을 사용하면 콘텐츠 범위가 렌더링 대상의 범위로 효과적으로 사용됩니다.
    geometricMask (선택 사항) 계층을 잘려야 하는 ID2D1Geometry로 정의된 영역입니다. 계층을 기하 도형으로 잘리지 않아야 하는 경우 NULL 로 설정합니다.
    maskAntialiasMode geometricMask 필드에 지정된 기하학적 마스크의 앤티앨리어싱 모드를 지정하는 값입니다.
    maskTransform 레이어를 작성할 때 기하학적 마스크에 적용되는 변환을 지정하는 값입니다. 이것은 세계 변환에 상대적입니다.
    불투명도 레이어의 불투명도 값입니다. 계층에 있는 각 리소스의 불투명도는 대상에 구성할 때 이 값과 곱합니다.
    opacityBrush (선택 사항) 레이어의 불투명도를 수정하는 데 사용되는 브러시입니다. 브러시가 레이어에 매핑되고 매핑된 각 브러시 픽셀의 알파 채널이 해당 레이어 픽셀에 곱합니다. 레이어에 불투명 마스크가 없어야 하는 경우 NULL 로 설정합니다.
    layerOptions 레이어가 ClearType 앤티앨리어싱을 사용하여 텍스트를 렌더링할지 여부를 지정하는 값입니다. 이 매개 변수는 기본적으로 해제로 설정됩니다. 이 기능을 켜면 ClearType이 올바르게 작동할 수 있지만 렌더링 속도가 약간 느려집니다.

     

    참고

    Windows 8 계층에서 ClearType을 사용하여 렌더링할 수 없으므로 layerOptions 매개 변수는 항상 D2D1_LAYER_OPTIONS_NONE

     

    편의를 위해 Direct2D는 D2D1_LAYER_PARAMETERS 구조를 만드는 데 도움이 되는 D2D1::LayerParameters 메서드를 제공합니다.

  • 레이어의 콘텐츠를 렌더링 대상에 합성하려면 PopLayer 메서드를 호출합니다. EndDraw 메서드를 호출하기 전에 PopLayer 메서드를 호출해야 합니다.

다음 예제에서는 CreateLayer, PushLayerPopLayer를 사용하는 방법을 보여줍니다. D2D1_LAYER_PARAMETERS 구조체의 모든 필드는 ID2D1RadialGradientBrush로 설정된 opacityBrush를 제외하고 기본값으로 설정됩니다.

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

이 예제에서는 코드를 생략합니다.

PushLayer 및 PopLayer를 호출할 때 각 PushLayer에 일치하는 PopLayer 호출이 있는지 확인합니다. PushLayer 호출보다 PopLayer 호출이 더 많은 경우 렌더링 대상이 오류 상태로 배치됩니다. 모든 미해결 계층이 표시되기 전에 Flush 가 호출되면 렌더링 대상이 오류 상태로 전환되고 오류가 반환됩니다. 오류 상태를 지우려면 EndDraw를 사용합니다.

콘텐츠 범위

contentBounds는 레이어에 그릴 내용의 제한을 설정합니다. 콘텐츠 범위 내의 항목만 렌더링 대상에 다시 합성됩니다.

다음 예제에서는 원래 이미지가 (10, 108) 및 오른쪽 아래 모서리(121, 177)에 있는 콘텐츠 범위로 잘리도록 contentBounds 를 지정하는 방법을 보여 줍니다. 다음 그림에서는 원본 이미지와 이미지를 콘텐츠 범위로 클리핑한 결과를 보여 줍니다.

원본 그림의 콘텐츠 경계와 잘린 그림

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

이 예제에서는 코드를 생략합니다.

참고

geometricMask를 지정하면 결과 잘린 이미지가 추가로 영향을 받습니다. 자세한 내용은 기하학적 마스크 섹션을 참조하세요.

 

기하학적 마스크

기하학적 마스크는 렌더링 대상에 의해 그려질 때 레이어를 마스킹하는 ID2D1Geometry 개체로 정의된 클립 또는 컷아웃입니다. D2D1_LAYER_PARAMETERS 구조체의 geometricMask 필드를 사용하여 결과를 기하 도형으로 마스킹할 수 있습니다. 예를 들어 블록 문자 "A"로 마스킹된 이미지를 표시하려면 먼저 블록 문자 "A"를 나타내는 기하 도형을 만들고 해당 기하 도형을 레이어의 기하학적 마스크로 사용할 수 있습니다. 그런 다음 레이어를 푸시한 후 이미지를 그릴 수 있습니다. 레이어를 팝하면 이미지가 블록 문자 "A" 셰이프에 잘립니다.

다음 예제에서는 산의 모양을 포함하는 ID2D1PathGeometry 를 만든 다음 경로 기하 도형을 PushLayer에 전달하는 방법을 보여 줍니다. 그런 다음 비트맵과 사각형을 그립니다. 렌더링할 계층에 비트맵만 있는 경우 효율성을 위해 고정된 비트맵 브러시와 함께 FillGeometry 를 사용합니다. 다음 그림에서는 예제의 출력을 보여 줍니다.

산의 기하학적 마스크가 적용된 후의 잎 그림 및 결과 그림 그림 그림

첫 번째 예제에서는 마스크로 사용할 기하 도형을 정의합니다.

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

다음 예제에서는 기하 도형을 레이어의 마스크로 사용합니다.

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

이 예제에서는 코드를 생략합니다.

참고

일반적으로 geometricMask를 지정하는 경우 contentBounds에 대해 기본값 InfiniteRect를 사용할 수 있습니다.

contentBounds가 NULL이고 geometricMask가 NULL이 아닌 경우 콘텐츠 범위는 마스크 변환이 적용된 후 기하학적 마스크의 경계가 됩니다.

contentBounds가 NULL이 아니고 geometricMask가 NULL이 아닌 경우 변환된 기하학적 마스크는 콘텐츠 경계에 대해 효과적으로 잘리며 콘텐츠 범위는 무한으로 간주됩니다.

 

불투명 마스크

불투명 마스크는 브러시 또는 비트맵으로 설명하는 마스크로, 해당 개체를 부분적으로 또는 완전히 투명하게 만들기 위해 다른 개체에 적용됩니다. 브러시의 알파 채널을 콘텐츠 마스크로 사용할 수 있습니다. 예를 들어 불투명에서 투명까지 다양한 방사형 그라데이션 브러시를 정의하여 비네팅 효과를 만들 수 있습니다.

다음 예제에서는 ID2D1RadialGradientBrush (m_pRadialGradientBrush)를 불투명 마스크로 사용합니다. 그런 다음 비트맵과 사각형을 그립니다. 렌더링할 계층에 비트맵만 있는 경우 효율성을 위해 고정된 비트맵 브러시와 함께 FillGeometry 를 사용합니다. 다음 그림에서는이 예제에서 출력을 보여줍니다.

불투명 마스크가 적용된 후의 트리 그림 및 결과 그림

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

이 예제에서는 코드를 생략합니다.

참고

이 예제에서는 레이어를 사용하여 단일 개체에 불투명도 마스크를 적용하여 예제를 가능한 한 간단하게 유지합니다. 불투명 마스크를 단일 개체에 적용할 때 레이어가 아닌 FillOpacityMask 또는 FillGeometry 메서드를 사용하는 것이 더 효율적입니다.

 

레이어를 사용하지 않고 불투명 마스크를 적용하는 방법에 대한 지침은 불투명 마스크 개요를 참조하세요.

레이어에 대한 대안

앞에서 설명한 것처럼 과도한 수의 레이어가 애플리케이션의 성능에 부정적인 영향을 줄 수 있습니다. 성능을 향상시키려면 가능하면 레이어를 사용하지 마세요. 대신 대안을 사용합니다. 다음 코드 예제에서는 콘텐츠 범위가 있는 레이어를 사용하는 대신 PushAxisAlignedClipPopAxisAlignedClip 을 사용하여 영역을 클리핑하는 방법을 보여 줍니다.

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

마찬가지로 다음 예제와 같이 렌더링할 레이어에 콘텐츠가 하나만 있는 경우 불투명 마스크가 있는 레이어를 사용하는 대신 고정된 비트맵 브러시와 함께 FillGeometry 를 사용합니다.

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

기하학적 마스크와 함께 레이어를 사용하는 대신 다음 예제와 같이 비트맵 마스크를 사용하여 영역을 클리핑하는 것이 좋습니다.

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

마지막으로, 단일 기본 형식에 불투명도를 적용하려면 불투명도를 브러시 색으로 곱한 다음 기본 형식을 렌더링해야 합니다. 레이어 또는 불투명 마스크 비트맵이 필요하지 않습니다.

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

임의의 셰이프 클리핑

다음은 이미지에 클립을 적용한 결과를 보여 주는 그림입니다.

클립 앞과 뒤의 이미지 예제를 보여 주는 이미지입니다.

기하 도형 마스크가 있는 레이어 또는 불투명 브러시가 있는 FillGeometry 메서드를 사용하여 이 결과를 얻을 수 있습니다.

레이어를 사용하는 예제는 다음과 같습니다.

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

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

이 코드 예제에서는 PushLayer 메서드를 호출할 때 앱에서 만든 계층을 전달하지 않습니다. Direct2D는 사용자를 위한 레이어를 만듭니다. Direct2D는 앱의 개입 없이 이 리소스의 할당 및 소멸을 관리할 수 있습니다. 이를 통해 Direct2D는 내부적으로 계층을 재사용하고 리소스 관리 최적화를 적용할 수 있습니다.

참고

Windows 8 레이어 사용에 대한 많은 최적화가 이루어졌으며 가능하면 FillGeometry 대신 레이어 API를 사용하는 것이 좋습니다.

 

축 맞춤 클립

클리핑할 영역이 임의가 아닌 그리기 표면의 축에 정렬된 경우 이 경우 레이어 대신 클립 사각형을 사용하는 데 적합합니다. 성능 향상은 앤티앨리어스된 기하 도형보다 별칭이 지정된 기하 도형에 더 적합합니다. 축 맞춤 클립에 대한 자세한 내용은 PushAxisAlignedClip 항목을 참조하세요.

Direct2D 참조