Aracılığıyla paylaş


Basit bir Direct2D uygulaması oluşturma

Bu konu, bir pencere oluşturan ve içerik çizmek için Direct2D kullanan DemoApp sınıfını oluşturma işleminde size yol gösterir. Bu öğreticide Direct2D kaynakları oluşturmayı ve temel şekiller çizmeyi öğreneceksiniz. Ayrıca, kaynak oluşturmayı en aza indirerek performansı artırmak için uygulamanızı nasıl yapılandıracağınızı da öğrenirsiniz.

Öğreticiyi izlemek için Microsoft Visual Studio'yu kullanarak bir Win32 projesi oluşturabilir ve ardından ana uygulama üst bilgisindeki ve .cpp dosyasındaki kodu bu öğreticide açıklanan kodla değiştirebilirsiniz.

Ayrıca GitHub Simple Direct2D uygulama örneği uygulamasına da bakın.

Not

Direct2D kullanan bir Evrensel Windows Platformu (UWP) uygulaması oluşturmak istiyorsanız Windows 8 için Direct2D hızlı başlangıcı konusuna bakın.

Direct2D içeriği oluşturmak için kullanabileceğiniz arabirimlere genel bakış için Direct2D API'sine genel bakış bölümüne bakın.

Eğitimi tamamladıktan sonra DemoApp sınıfı aşağıdaki görselde gösterilen çıkışı oluşturur.

ızgara arka planındaki iki dikdörtgenin tasviri

Bölüm 1: DemoApp Üst Bilgisi Oluşturma

Bu adımda, gerekli üst bilgileri ve makroları ekleyerek uygulamanızı Direct2D kullanacak şekilde ayarlarsınız. Ayrıca, bu öğreticinin sonraki bölümlerinde kullanacağınız yöntemleri ve veri üyelerini de bildirirsiniz.

  1. Uygulama üst bilgi dosyanıza aşağıdaki sık kullanılan üst bilgileri ekleyin.

    // Windows Header Files:
    #include <windows.h>
    
    // C RunTime Header Files:
    #include <stdlib.h>
    #include <malloc.h>
    #include <memory.h>
    #include <wchar.h>
    #include <math.h>
    
    #include <d2d1.h>
    #include <d2d1helper.h>
    #include <dwrite.h>
    #include <wincodec.h>
    
  2. Arabirimleri serbest bırakmak için ek işlevler ve hata işleme ve modülün temel adresini almak için makrolar bildirin.

    template<class Interface>
    inline void SafeRelease(
        Interface **ppInterfaceToRelease)
    {
        if (*ppInterfaceToRelease != NULL)
        {
            (*ppInterfaceToRelease)->Release();
            (*ppInterfaceToRelease) = NULL;
        }
    }
    
    #ifndef Assert
    #if defined( DEBUG ) || defined( _DEBUG )
    #define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0)
    #else
    #define Assert(b)
    #endif //DEBUG || _DEBUG
    #endif
    
    #ifndef HINST_THISCOMPONENT
    EXTERN_C IMAGE_DOS_HEADER __ImageBase;
    #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
    #endif
    
  3. Sınıfı başlatma, kaynakları oluşturma ve atma, mesaj döngüsünü yönetme, içeriği render etme ve Windows işlemleri için yöntemleri deklar edin.

    class DemoApp
    {
    public:
        DemoApp();
        ~DemoApp();
    
        // Register the window class and call methods for instantiating drawing resources
        HRESULT Initialize();
    
        // Process and dispatch messages
        void RunMessageLoop();
    
    private:
        // Initialize device-independent resources.
        HRESULT CreateDeviceIndependentResources();
    
        // Initialize device-dependent resources.
        HRESULT CreateDeviceResources();
    
        // Release device-dependent resource.
        void DiscardDeviceResources();
    
        // Draw content.
        HRESULT OnRender();
    
        // Resize the render target.
        void OnResize(
            UINT width,
            UINT height
            );
    
        // The windows procedure.
        static LRESULT CALLBACK WndProc(
            HWND hWnd,
            UINT message,
            WPARAM wParam,
            LPARAM lParam
            );
    };
    
  4. Sınıf üyeleri olarak, bir ID2D1Factory nesnesi, bir ID2D1HwndRenderTarget nesnesi ve iki ID2D1SolidColorBrush nesnesi için işaretçiler bildirin.

    private:
    HWND m_hwnd;
    ID2D1Factory* m_pDirect2dFactory;
    ID2D1HwndRenderTarget* m_pRenderTarget;
    ID2D1SolidColorBrush* m_pLightSlateGrayBrush;
    ID2D1SolidColorBrush* m_pCornflowerBlueBrush;
    

Bölüm 2: Sınıf altyapısını uygulama

Bu bölümde, DemoApp oluşturucusunu ve yıkıcısını, başlatma ve ileti döngü yöntemlerini ve WinMain işlevini uygulayacaksınız. Bu yöntemlerin çoğu, başka bir Win32 uygulamasında bulunanlarla aynı görünür. Tek istisna, birkaç Direct2D kaynağı oluşturan CreateDeviceIndependentResources yöntemini çağıran Initialize yöntemidir (sonraki bölümde tanımlayacaksınız).

  1. Sınıf uygulama dosyasında sınıf oluşturucuyu ve yıkıcıyı uygulayın. Oluşturucu, üyelerini NULLolarak başlatmalıdır. Yıkıcı, sınıf üyesi olarak depolanan tüm arabirimleri serbest bırakmalıdır.

    DemoApp::DemoApp() :
        m_hwnd(NULL),
        m_pDirect2dFactory(NULL),
        m_pRenderTarget(NULL),
        m_pLightSlateGrayBrush(NULL),
        m_pCornflowerBlueBrush(NULL)
    {}
    
    DemoApp::~DemoApp()
    {
        SafeRelease(&m_pDirect2dFactory);
        SafeRelease(&m_pRenderTarget);
        SafeRelease(&m_pLightSlateGrayBrush);
        SafeRelease(&m_pCornflowerBlueBrush);
    }
    
  2. İletileri çevirip dağıtan DemoApp::RunMessageLoop yöntemini uygulayın.

    void DemoApp::RunMessageLoop()
    {
        MSG msg;
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    
  3. Pencereyi oluşturan, gösteren ve DemoApp::CreateDeviceIndependentResources yöntemini çağıran Initialize yöntemini uygulayın. Bir sonraki bölümde CreateDeviceIndependentResources yöntemini uygulayacaksınız.

    HRESULT DemoApp::Initialize()
    {
        HRESULT hr;
    
        // Initialize device-independent resources, such
        // as the Direct2D factory.
        hr = CreateDeviceIndependentResources();
    
        if (SUCCEEDED(hr))
        {
            // Register the window class.
            WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
            wcex.style         = CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc   = DemoApp::WndProc;
            wcex.cbClsExtra    = 0;
            wcex.cbWndExtra    = sizeof(LONG_PTR);
            wcex.hInstance     = HINST_THISCOMPONENT;
            wcex.hbrBackground = NULL;
            wcex.lpszMenuName  = NULL;
            wcex.hCursor       = LoadCursor(NULL, IDI_APPLICATION);
            wcex.lpszClassName = L"D2DDemoApp";
    
            RegisterClassEx(&wcex);
    
            // In terms of using the correct DPI, to create a window at a specific size
            // like this, the procedure is to first create the window hidden. Then we get
            // the actual DPI from the HWND (which will be assigned by whichever monitor
            // the window is created on). Then we use SetWindowPos to resize it to the
            // correct DPI-scaled size, then we use ShowWindow to show it.
    
            m_hwnd = CreateWindow(
                L"D2DDemoApp",
                L"Direct2D demo application",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                0,
                0,
                NULL,
                NULL,
                HINST_THISCOMPONENT,
                this);
    
            if (m_hwnd)
            {
                // Because the SetWindowPos function takes its size in pixels, we
                // obtain the window's DPI, and use it to scale the window size.
                float dpi = GetDpiForWindow(m_hwnd);
    
                SetWindowPos(
                    m_hwnd,
                    NULL,
                    NULL,
                    NULL,
                    static_cast<int>(ceil(640.f * dpi / 96.f)),
                    static_cast<int>(ceil(480.f * dpi / 96.f)),
                    SWP_NOMOVE);
                ShowWindow(m_hwnd, SW_SHOWNORMAL);
                UpdateWindow(m_hwnd);
            }
        }
    
        return hr;
    }
    
  4. Uygulama giriş noktası olarak hizmet veren WinMain yöntemini uygulayın. DemoAppsınıfının bir örneğini başlatın ve ileti döngüsünü başlatın.

    int WINAPI WinMain(
        HINSTANCE /* hInstance */,
        HINSTANCE /* hPrevInstance */,
        LPSTR /* lpCmdLine */,
        int /* nCmdShow */
        )
    {
        // Use HeapSetInformation to specify that the process should
        // terminate if the heap manager detects an error in any heap used
        // by the process.
        // The return value is ignored, because we want to continue running in the
        // unlikely event that HeapSetInformation fails.
        HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
    
        if (SUCCEEDED(CoInitialize(NULL)))
        {
            {
                DemoApp app;
    
                if (SUCCEEDED(app.Initialize()))
                {
                    app.RunMessageLoop();
                }
            }
            CoUninitialize();
        }
    
        return 0;
    }
    

Bölüm 3: Direct2D kaynakları oluşturma

Bu bölümde, çizmek için kullandığınız Direct2D kaynaklarını oluşturursunuz. Direct2D iki tür kaynak sağlar: uygulama süresi boyunca devam eden cihazdan bağımsız kaynaklar ve cihaza bağımlı kaynaklar. Cihaza bağımlı kaynaklar belirli bir işleme cihazıyla ilişkilendirilir ve bu cihaz kaldırılırsa çalışmaz.

  1. DemoApp::CreateDeviceIndependentResources yöntemini uygulayın. yönteminde, diğer Direct2D kaynaklarını oluşturmak için cihazdan bağımsız bir kaynak olan ID2D1Factoryoluşturun. Fabrikayı depolamak için m_pDirect2DdFactory sınıf üyesini kullanın.

    HRESULT DemoApp::CreateDeviceIndependentResources()
    {
        HRESULT hr = S_OK;
    
        // Create a Direct2D factory.
        hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
    
        return hr;
    }
    
  2. DemoApp::CreateDeviceResources yöntemini uygulayın. Bu yöntem pencerenin cihaza bağımlı kaynaklarını, bir işleme hedefini ve iki fırçayı oluşturur. İstemci alanının boyutunu alın ve pencerenin HWND'ine uygun boyutta bir ID2D1HwndRenderTarget oluşturun. İşleme hedefini m_pRenderTarget sınıf üyesinde depolayın.

    RECT rc;
    GetClientRect(m_hwnd, &rc);
    
    D2D1_SIZE_U size = D2D1::SizeU(
        rc.right - rc.left,
        rc.bottom - rc.top);
    
    // Create a Direct2D render target.
    hr = m_pDirect2dFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(m_hwnd, size),
        &m_pRenderTarget);
    
  3. ID2D1SolidColorBrushgrive ID2D1SolidColorBrush bir mısır çiçeği mavisioluşturmak için işleme hedefini kullanın.

    if (SUCCEEDED(hr))
    {
        // Create a gray brush.
        hr = m_pRenderTarget->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::LightSlateGray),
            &m_pLightSlateGrayBrush
            );
    }
    
    if (SUCCEEDED(hr))
    {
        // Create a blue brush.
        hr = m_pRenderTarget->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::CornflowerBlue),
            &m_pCornflowerBlueBrush
            );
    }
    
  4. Bu yöntem tekrar tekrar çağrılacağından, işleme hedefinin (m_pRenderTarget) zaten var olup olmadığını denetlemek için bir if deyimi ekleyin. Aşağıdaki kod, CreateDeviceResources yönteminin tamamını gösterir.

    HRESULT DemoApp::CreateDeviceResources()
    {
        HRESULT hr = S_OK;
    
        if (!m_pRenderTarget)
        {
            RECT rc;
            GetClientRect(m_hwnd, &rc);
    
            D2D1_SIZE_U size = D2D1::SizeU(
                rc.right - rc.left,
                rc.bottom - rc.top
                );
    
            // Create a Direct2D render target.
            hr = m_pDirect2dFactory->CreateHwndRenderTarget(
                D2D1::RenderTargetProperties(),
                D2D1::HwndRenderTargetProperties(m_hwnd, size),
                &m_pRenderTarget
                );
    
            if (SUCCEEDED(hr))
            {
                // Create a gray brush.
                hr = m_pRenderTarget->CreateSolidColorBrush(
                    D2D1::ColorF(D2D1::ColorF::LightSlateGray),
                    &m_pLightSlateGrayBrush
                    );
            }
            if (SUCCEEDED(hr))
            {
                // Create a blue brush.
                hr = m_pRenderTarget->CreateSolidColorBrush(
                    D2D1::ColorF(D2D1::ColorF::CornflowerBlue),
                    &m_pCornflowerBlueBrush
                    );
            }
        }
    
        return hr;
    }
    
  5. DemoApp::DiscardDeviceResources metodunu uygulayın. Bu yöntemde, DemoApp::CreateDeviceResources yönteminde oluşturduğunuz işleme hedefini ve iki fırçayı serbest bırakın.

    void DemoApp::DiscardDeviceResources()
    {
        SafeRelease(&m_pRenderTarget);
        SafeRelease(&m_pLightSlateGrayBrush);
        SafeRelease(&m_pCornflowerBlueBrush);
    }
    

Bölüm 4: Direct2D içeriğini işleme

Bu bölümde windows yordamını, OnRender yöntemini (içeriği boyayan) ve OnResize yöntemini (pencere yeniden boyutlandırıldığında işleme hedefinin boyutunu ayarlar) uygularsınız.

  1. Pencere iletilerini işlemek için DemoApp::WndProc yöntemini uygulayın. WM_SIZE iletisi için DemoApp::OnResize yöntemini çağırın ve yeni genişlik ve yükseklik değerlerini geçirin. WM_PAINT ve WM_DISPLAYCHANGE iletileri için pencereyi boyamak için DemoApp::OnRender yöntemini çağırın. Aşağıdaki adımlarda OnRender ve OnResize yöntemlerini uygulayacaksınız.

    LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        LRESULT result = 0;
    
        if (message == WM_CREATE)
        {
            LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
            DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
    
            ::SetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA,
                reinterpret_cast<LONG_PTR>(pDemoApp)
                );
    
            result = 1;
        }
        else
        {
            DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
                ::GetWindowLongPtrW(
                    hwnd,
                    GWLP_USERDATA
                    )));
    
            bool wasHandled = false;
    
            if (pDemoApp)
            {
                switch (message)
                {
                case WM_SIZE:
                    {
                        UINT width = LOWORD(lParam);
                        UINT height = HIWORD(lParam);
                        pDemoApp->OnResize(width, height);
                    }
                    result = 0;
                    wasHandled = true;
                    break;
    
                case WM_DISPLAYCHANGE:
                    {
                        InvalidateRect(hwnd, NULL, FALSE);
                    }
                    result = 0;
                    wasHandled = true;
                    break;
    
                case WM_PAINT:
                    {
                        pDemoApp->OnRender();
                        ValidateRect(hwnd, NULL);
                    }
                    result = 0;
                    wasHandled = true;
                    break;
    
                case WM_DESTROY:
                    {
                        PostQuitMessage(0);
                    }
                    result = 1;
                    wasHandled = true;
                    break;
                }
            }
    
            if (!wasHandled)
            {
                result = DefWindowProc(hwnd, message, wParam, lParam);
            }
        }
    
        return result;
    }
    
  2. DemoApp::OnRender yöntemini uygulayın. İlk olarak, bir HRESULTtanımlayın. Ardından CreateDeviceResource yöntemini çağırın. Pencere her boyandığında bu yöntem çağrılır. 3. Bölümün 4. adımında, işleme hedefi zaten varsa yöntemin herhangi bir iş yapmasını önlemek için bir if deyimi eklediğinizi unutmayın.

    HRESULT DemoApp::OnRender()
    {
        HRESULT hr = S_OK;
    
        hr = CreateDeviceResources();
    
  3. CreateDeviceResource yönteminin başarılı olduğunu doğrulayın. Eğer olmadıysa, herhangi bir çizim yapmayın.

    if (SUCCEEDED(hr))
    {
    
  4. Yeni eklediğiniz if deyiminin içinde, işleme hedefinin BeginDraw yöntemini çağırarak çizim başlatın. İşleme hedefinin dönüşümünü kimlik matrisine ayarlayın ve pencereyi temizleyin.

    m_pRenderTarget->BeginDraw();
    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
  5. Çizim alanının boyutunu alın.

    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    
  6. Bir dizi çizgi çizmek için, bir for döngüsü ve işleme hedefinin DrawLine yöntemini kullanarak bir kılavuz arka planı çizin.

    // Draw a grid background.
    int width = static_cast<int>(rtSize.width);
    int height = static_cast<int>(rtSize.height);
    
    for (int x = 0; x < width; x += 10)
    {
        m_pRenderTarget->DrawLine(
            D2D1::Point2F(static_cast<FLOAT>(x), 0.0f),
            D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height),
            m_pLightSlateGrayBrush,
            0.5f
            );
    }
    
    for (int y = 0; y < height; y += 10)
    {
        m_pRenderTarget->DrawLine(
            D2D1::Point2F(0.0f, static_cast<FLOAT>(y)),
            D2D1::Point2F(rtSize.width, static_cast<FLOAT>(y)),
            m_pLightSlateGrayBrush,
            0.5f
            );
    }
    
  7. Ekranda ortalanmış iki dikdörtgen temel öğe oluşturun.

    // Draw two rectangles.
    D2D1_RECT_F rectangle1 = D2D1::RectF(
        rtSize.width/2 - 50.0f,
        rtSize.height/2 - 50.0f,
        rtSize.width/2 + 50.0f,
        rtSize.height/2 + 50.0f
        );
    
    D2D1_RECT_F rectangle2 = D2D1::RectF(
        rtSize.width/2 - 100.0f,
        rtSize.height/2 - 100.0f,
        rtSize.width/2 + 100.0f,
        rtSize.height/2 + 100.0f
        );
    
  8. İlk dikdörtgenin içini gri fırçayla boyamak için işleme hedefinin FillRectangle yöntemini kullanın.

    // Draw a filled rectangle.
    m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
    
  9. İşleme hedefinin DrawRectangle yöntemini kullanarak ikinci dikdörtgenin ana hattını mısır çiçeği mavisi fırçayla boyayın.

    // Draw the outline of a rectangle.
    m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
    
  10. İşleme hedefinin EndDraw yöntemini çağırın. EndDraw yöntemi, çizim işlemlerinin başarılı olup olmadığını göstermek için bir HRESULT döndürür. Üçüncü adımda başladığınız if ifadesinin kapsamını kapatın.

        hr = m_pRenderTarget->EndDraw();
    }
    
  11. EndDrawtarafından döndürülen HRESULT denetleyin. İşleme hedefinin yeniden oluşturulması gerektiğini belirtiyorsa, DemoApp::DiscardDeviceResources yöntemini çağırarak serbest bırakılır; pencere bir sonraki WM_PAINT veya WM_DISPLAYCHANGE iletisini aldığında yeniden oluşturulacaktır.

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    
  12. HRESULTdöndürerek yöntemin kapsamını kapatın.

        return hr;
    }
    
  13. İşleme hedefini pencerenin yeni boyutuna yeniden boyutlandırması için DemoApp::OnResize yöntemini uygulayın.

    void DemoApp::OnResize(UINT width, UINT height)
    {
        if (m_pRenderTarget)
        {
            // Note: This method can fail, but it's okay to ignore the
            // error here, because the error will be returned again
            // the next time EndDraw is called.
            m_pRenderTarget->Resize(D2D1::SizeU(width, height));
        }
    }
    

Eğitimi tamamladınız.

Not

Direct2D'yi kullanmak için uygulamanızın d2d1.h üst bilgi dosyasını içerdiğinden ve d2d1.lib kitaplığında derlendiğinden emin olun. d2d1.h ve d2d1.lib'i Windows SDK'te bulabilirsiniz.

Özet

Bu öğreticide Direct2D kaynakları oluşturmayı ve temel şekiller çizmeyi öğrendiniz. Ayrıca kaynak oluşturmayı en aza indirerek performansı artırmak için uygulamanızı nasıl yapılandırabileceğinizi de öğrendinsiniz.