다음을 통해 공유


Direct2D 및 GDI 상호 운용성 개요

이 항목에서는 Direct2D와 GDI 를 함께 사용하는 방법을 설명합니다. Direct2D를 GDI와 결합하는 방법에는 두 가지가 있습니다. Direct2D GDI 호환 렌더링 대상에 GDI 콘텐츠를 쓰거나 Direct2D 콘텐츠를 GDI DC(디바이스 컨텍스트)에 쓸 수 있습니다.

이 항목에는 다음과 같은 섹션이 포함되어 있습니다.

사전 요구 사항

이 개요에서는 기본 Direct2D 그리기 작업에 익숙하다고 가정합니다. 자습서는 간단한 Direct2D 애플리케이션 만들기를 참조하세요. 또한 GDI 그리기 작업에 익숙하다고 가정합니다.

GDI 디바이스 컨텍스트에 Direct2D 콘텐츠 그리기

Direct2D 콘텐츠를 GDI DC에 그리려면 ID2D1DCRenderTarget을 사용합니다. DC 렌더링 대상을 만들려면 ID2D1Factory::CreateDCRenderTarget 메서드를 사용합니다. 이 메서드는 두 개의 매개 변수를 사용합니다.

첫 번째 매개 변수인 D2D1_RENDER_TARGET_PROPERTIES 구조체는 렌더링, 원격, DPI, 픽셀 형식 및 사용량 정보를 지정합니다. DC 렌더링 대상이 GDI에서 작동하도록 설정하려면 DXGI 형식을 DXGI_FORMAT_B8G8R8A8_UNORM , 알파 모드를 D2D1_ALPHA_MODE_PREMULTIPLIED 또는 D2D1_ALPHA_MODE_IGNORE 설정합니다.

두 번째 매개 변수는 DC 렌더링 대상 참조를 수신하는 포인터의 주소입니다.

다음 코드는 DC 렌더링 대상을 만듭니다.

// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
    D2D1_RENDER_TARGET_TYPE_DEFAULT,
    D2D1::PixelFormat(
        DXGI_FORMAT_B8G8R8A8_UNORM,
        D2D1_ALPHA_MODE_IGNORE),
    0,
    0,
    D2D1_RENDER_TARGET_USAGE_NONE,
    D2D1_FEATURE_LEVEL_DEFAULT
    );

hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);

앞의 코드에서 m_pD2DFactoryID2D1Factory에 대한 포인터이고 m_pDCRTID2D1DCRenderTarget에 대한 포인터입니다.

DC 렌더링 대상을 사용하여 렌더링하려면 먼저 해당 BindDC 메서드를 사용하여 GDI DC와 연결해야 합니다. 다른 DC 또는 변경 내용에 그릴 영역의 크기를 사용할 때마다 이 작업을 수행합니다.

BindDC 메서드는 hDCpSubRect의 두 매개 변수를 사용합니다. hDC 매개 변수는 렌더링 대상의 출력을 수신하는 디바이스 컨텍스트에 대한 핸들을 제공합니다. pSubRect 매개 변수는 콘텐츠가 렌더링되는 디바이스 컨텍스트의 부분을 설명하는 사각형입니다. DC 렌더링 대상은 크기가 변경되면 pSubRect에서 설명하는 디바이스 컨텍스트 영역과 일치하도록 크기를 업데이트합니다.

다음 코드는 DC 렌더링 대상에 DC를 바인딩합니다.

HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{


// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
C++
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);

DC 렌더링 대상을 DC와 연결한 후에는 DC 렌더링 대상을 사용하여 그릴 수 있습니다. 다음 코드는 DC를 사용하여 Direct2D 및 GDI 콘텐츠를 그립니다.

HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{

    HRESULT hr;
    RECT rc;

    // Get the dimensions of the client drawing area.
    GetClientRect(m_hwnd, &rc);

    //
    // Draw the pie chart with Direct2D.
    //

    // Create the DC render target.
    hr = CreateDeviceResources();

    if (SUCCEEDED(hr))
    {
        // Bind the DC to the DC render target.
        hr = m_pDCRT->BindDC(ps.hdc, &rc);

        m_pDCRT->BeginDraw();

        m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());

        m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));

        m_pDCRT->DrawEllipse(
            D2D1::Ellipse(
                D2D1::Point2F(150.0f, 150.0f),
                100.0f,
                100.0f),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f + 100.0f * 0.15425f),
                (150.0f - 100.0f * 0.988f)),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f + 100.0f * 0.525f),
                (150.0f + 100.0f * 0.8509f)),
            m_pBlackBrush,
            3.0
            );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F(
                (150.0f - 100.0f * 0.988f),
                (150.0f - 100.0f * 0.15425f)),
            m_pBlackBrush,
            3.0
            );

        hr = m_pDCRT->EndDraw();
        if (SUCCEEDED(hr))
        {
            //
            // Draw the pie chart with GDI.
            //

            // Save the original object.
            HGDIOBJ original = NULL;
            original = SelectObject(
                ps.hdc,
                GetStockObject(DC_PEN)
                );

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(ps.hdc, blackPen);

            Ellipse(ps.hdc, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);


            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(ps.hdc, pntArray1, 2);
            Polyline(ps.hdc, pntArray2, 2);
            Polyline(ps.hdc, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(ps.hdc, original);
        }
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }

    return hr;
}

이 코드는 다음 그림과 같이 출력을 생성합니다(Direct2D와 GDI 렌더링의 차이점을 강조하기 위해 설명선이 추가되었습니다.)

direct2d 및 gdi로 렌더링된 두 개의 원형 차트 그림

ID2D1DCRenderTargets, GDI 변환 및 Windows의 오른쪽에서 왼쪽 언어 빌드

ID2D1DCRenderTarget을 사용하면 Direct2D 콘텐츠를 내부 비트맵으로 렌더링한 다음 GDI를 사용하여 비트맵을 DC에 렌더링합니다.

GDI가 렌더링 대상에서 사용하는 동일한 DC에 GDI 변환(SetWorldTransform 메서드를 통해) 또는 기타 효과를 적용할 수 있습니다. 이 경우 GDI는 Direct2D에서 생성된 비트맵을 변환합니다. GDI 변환을 사용하여 Direct2D 콘텐츠를 변환하면 앤티앨리어싱 및 하위 픽셀 위치 지정이 이미 계산된 비트맵을 변환하므로 출력의 시각적 품질이 저하될 수 있습니다.

예를 들어 렌더링 대상을 사용하여 앤티앨리어싱된 기하 도형 및 텍스트가 포함된 장면을 그립니다. GDI 변환을 사용하여 DC에 크기 조정 변환을 적용하고 장면의 크기를 10배 더 크게 조정하면 픽셀화 및 들쭉날쭉한 가장자리가 표시됩니다. 그러나 Direct2D를 사용하여 유사한 변환을 적용한 경우 장면의 시각적 품질이 저하되지 않습니다.

경우에 따라 GDI가 Direct2D 콘텐츠의 품질을 저하시킬 수 있는 추가 처리를 수행하고 있다는 것은 분명하지 않을 수 있습니다. 예를 들어 WINDOWS의 RTL(오른쪽에서 왼쪽) 빌드에서는 GDI가 대상에 복사할 때 ID2D1DCRenderTarget 으로 렌더링된 콘텐츠가 가로로 반전될 수 있습니다. 콘텐츠가 실제로 반전되는지 여부는 DC의 현재 설정에 따라 달라집니다.

렌더링되는 콘텐츠의 형식에 따라 반전을 방지할 수 있습니다. Direct2D 콘텐츠에 ClearType 텍스트가 포함된 경우 이 반전은 텍스트의 품질을 저하합니다.

SetLayout GDI 함수를 사용하여 RTL 렌더링 동작을 제어할 수 있습니다. 미러링을 방지하려면 다음 예제와 같이 SetLayout GDI 함수를 호출하고 LAYOUT_BITMAPORIENTATIONPRESERVED 두 번째 매개 변수의 유일한 값으로 지정합니다( LAYOUT_RTL 결합하지 않음).

SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);

Direct2D GDI-Compatible 렌더링 대상에 GDI 콘텐츠 그리기

이전 섹션에서는 GDI DC에 Direct2D 콘텐츠를 쓰는 방법을 설명합니다. Direct2D GDI 호환 렌더링 대상에 GDI 콘텐츠를 쓸 수도 있습니다. 이 방법은 주로 Direct2D로 렌더링되지만 확장성 모델 또는 GDI를 사용하여 렌더링하는 기능이 필요한 기타 레거시 콘텐츠가 있는 애플리케이션에 유용합니다.

GDI 콘텐츠를 Direct2D GDI 호환 렌더링 대상에 렌더링하려면 GDI 그리기 호출을 수락할 수 있는 디바이스 컨텍스트에 대한 액세스를 제공하는 ID2D1GdiInteropRenderTarget을 사용합니다. 다른 인터페이스와 달리 ID2D1GdiInteropRenderTarget 개체는 직접 만들어지지 않습니다. 대신 기존 렌더링 대상 instance QueryInterface 메서드를 사용합니다. 다음 코드는 이 방법을 보여 줍니다.

        D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
        rtProps.usage =  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;

        // Create a GDI compatible Hwnd render target.
        hr = m_pD2DFactory->CreateHwndRenderTarget(
            rtProps,
            D2D1::HwndRenderTargetProperties(m_hwnd, size),
            &m_pRenderTarget
            );


        if (SUCCEEDED(hr))
        {
            hr = m_pRenderTarget->QueryInterface(__uuidof(ID2D1GdiInteropRenderTarget), (void**)&m_pGDIRT); 
        }

앞의 코드에서 m_pD2DFactoryID2D1Factory에 대한 포인터이고 m_pGDIRTID2D1GdiInteropRenderTarget에 대한 포인터입니다.

Hwnd GDI 호환 렌더링 대상을 만들 때 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE 플래그가 지정됩니다. 픽셀 형식이 필요한 경우 DXGI_FORMAT_B8G8R8A8_UNORM 사용합니다. 알파 모드가 필요한 경우 D2D1_ALPHA_MODE_PREMULTIPLIED 사용하거나 D2D1_ALPHA_MODE_IGNORE.

QueryInterface 메서드는 항상 성공합니다. ID2D1GdiInteropRenderTarget 인터페이스의 메서드가 지정된 렌더링 대상에 대해 작동하는지 여부를 테스트하려면 GDI 호환성 및 적절한 픽셀 형식을 지정하는 D2D1_RENDER_TARGET_PROPERTIES 만든 다음 렌더링 대상의 IsSupported 메서드를 호출하여 렌더링 대상이 GDI와 호환되는지 확인합니다.

다음 예제에서는 원형 차트(GDI 콘텐츠)를 Hwnd GDI 호환 렌더링 대상에 그리는 방법을 보여 줍니다.

        HDC hDC = NULL;
        hr = m_pGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &hDC);

        if (SUCCEEDED(hr))
        {
            // Draw the pie chart to the GDI render target associated with the Hwnd render target.
            HGDIOBJ original = NULL;
            original = SelectObject(
                hDC,
                GetStockObject(DC_PEN)
                );

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(hDC, blackPen);

            Ellipse(hDC, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);

            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(hDC, pntArray1, 2);
            Polyline(hDC, pntArray2, 2);
            Polyline(hDC, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(hDC, original);

            m_pGDIRT->ReleaseDC(NULL);
        }

코드는 다음 그림과 같이 설명선과 함께 차트를 출력하여 렌더링 품질 차이를 강조 표시합니다. 오른쪽 원형 차트(GDI 콘텐츠)는 왼쪽 원형 차트(Direct2D 콘텐츠)보다 렌더링 품질이 낮습니다. Direct2D가 앤티앨리어싱으로 렌더링할 수 있기 때문입니다.

direct2d gdi 호환 렌더링 대상에서 렌더링된 두 개의 원형 차트 그림

ID2D1Factory::CreateDCRenderTarget

ID2D1DCRenderTarget

ID2D1GdiInteropRenderTarget

D2D1_RENDER_TARGET_PROPERTIES

GDI 디바이스 컨텍스트

GDI SDK