Bagikan melalui


Membuat aplikasi Direct2D sederhana

Topik ini memancing Anda melalui proses pembuatan kelas DemoApp , yang membuat jendela, dan menggunakan Direct2D untuk menggambar konten. Dalam tutorial ini, Anda mempelajari cara membuat sumber daya Direct2D, dan menggambar bentuk dasar. Anda juga mempelajari cara menyusun aplikasi untuk meningkatkan performa dengan meminimalkan pembuatan sumber daya.

Untuk mengikuti tutorial, Anda dapat menggunakan Microsoft Visual Studio untuk membuat proyek Win32, lalu mengganti kode di header dan .cpp file aplikasi utama dengan kode yang dijelaskan dalam tutorial ini.

Lihat juga aplikasi sampel Simple Direct2D di GitHub.

Catatan

Jika Anda ingin membuat aplikasi Platform Windows Universal (UWP) yang menggunakan Direct2D, lihat mulai cepat Direct2D untuk topik Windows 8.

Untuk gambaran umum antarmuka yang dapat Anda gunakan untuk membuat konten Direct2D, lihat gambaran umum API Direct2D.

Setelah menyelesaikan tutorial, kelas DemoApp menghasilkan output yang ditunjukkan dalam ilustrasi berikut.

ilustrasi dua persegi panjang pada latar belakang kisi

Bagian 1: Buat Header DemoApp

Dalam langkah ini, Anda menyiapkan aplikasi untuk menggunakan Direct2D dengan menambahkan header dan makro yang diperlukan. Anda juga mendeklarasikan metode dan anggota data yang akan Anda gunakan di bagian selanjutnya dari tutorial ini.

  1. Dalam file header aplikasi Anda, sertakan header yang sering digunakan berikut ini.

    // 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. Deklarasikan fungsi tambahan untuk merilis antarmuka, dan makro untuk penanganan kesalahan dan mengambil alamat dasar modul.

    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. Nyatakan metode untuk menginisialisasi kelas, membuat dan membuang sumber daya, menangani perulangan pesan, merender konten, dan prosedur windows.

    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. Sebagai anggota kelas, deklarasikan pointer untuk objek ID2D1Factory , objek ID2D1HwndRenderTarget , dan dua objek ID2D1SolidColorBrush .

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

Bagian 2: Menerapkan infrastruktur kelas

Di bagian ini, Anda mengimplementasikan konstruktor dan destruktor DemoApp , metode perulangan inisialisasi dan pesannya, dan fungsi WinMain . Sebagian besar metode ini terlihat sama dengan yang ditemukan di aplikasi Win32 lainnya. Satu-satunya pengecualian adalah metode Inisialisasi , yang memanggil metode CreateDeviceIndependentResources (yang akan Anda tentukan di bagian berikutnya), yang membuat beberapa sumber daya Direct2D.

  1. Dalam file implementasi kelas, terapkan konstruktor kelas dan destruktor. Konstruktor harus menginisialisasi anggotanya ke NULL. Destruktor harus merilis antarmuka apa pun yang disimpan sebagai anggota kelas.

    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. Terapkan metode DemoApp::RunMessageLoop , yang menerjemahkan dan mengirimkan pesan.

    void DemoApp::RunMessageLoop()
    {
        MSG msg;
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    
  3. Terapkan metode Inisialisasi, yang membuat jendela, menunjukkannya, dan memanggil metode DemoApp::CreateDeviceIndependentResources. Anda akan menerapkan metode CreateDeviceIndependentResources di bagian berikutnya.

    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. Terapkan metode WinMain , yang berfungsi sebagai titik masuk aplikasi. Inisialisasi instans DemoApp, kelas dan mulai perulangan pesannya.

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

Bagian 3: Membuat sumber daya Direct2D

Di bagian ini, Anda membuat sumber daya Direct2D yang Anda gunakan untuk menggambar. Direct2D menyediakan dua jenis sumber daya—sumber daya independen perangkat yang dapat bertahan selama durasi aplikasi, dan sumber daya yang bergantung pada perangkat. Sumber daya yang bergantung pada perangkat dikaitkan dengan perangkat penyajian tertentu, dan akan berhenti berfungsi jika perangkat tersebut dihapus.

  1. Terapkan metode DemoApp::CreateDeviceIndependentResources . Dalam metode ini, buat ID2D1Factory, yang merupakan sumber daya independen perangkat untuk membuat sumber daya Direct2D lainnya. m_pDirect2DdFactory Gunakan anggota kelas untuk menyimpan pabrik.

    HRESULT DemoApp::CreateDeviceIndependentResources()
    {
        HRESULT hr = S_OK;
    
        // Create a Direct2D factory.
        hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
    
        return hr;
    }
    
  2. Terapkan metode DemoApp::CreateDeviceResources . Metode ini membuat sumber daya yang bergantung pada perangkat jendela, target render, dan dua kuas. Ambil ukuran area klien dan buat ID2D1HwndRenderTarget dengan ukuran yang sama yang dirender ke HWND jendela. Simpan target render di m_pRenderTarget anggota kelas.

    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. Gunakan target render untuk membuat ID2D1SolidColorBrush abu-abu dan cornflower blue ID2D1SolidColorBrush.

    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. Karena metode ini akan dipanggil berulang kali, tambahkan if pernyataan untuk memeriksa apakah target render (m_pRenderTarget) sudah ada. Kode berikut menunjukkan metode CreateDeviceResources lengkap.

    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. Terapkan metode DemoApp::D iscardDeviceResources . Dalam metode ini, lepaskan target render dan dua kuas yang Anda buat di metode DemoApp::CreateDeviceResources .

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

Bagian 4: Merender konten Direct2D

Di bagian ini, Anda menerapkan prosedur windows, metode OnRender (yang melukis konten), dan metode OnResize (yang menyesuaikan ukuran target render saat jendela diubah ukurannya).

  1. Terapkan metode DemoApp::WndProc untuk menangani pesan jendela. Untuk pesan WM_SIZE , panggil metode DemoApp::OnResize , dan berikan lebar dan tinggi baru. Untuk pesan WM_PAINT dan WM_DISPLAYCHANGE , panggil metode DemoApp::OnRender untuk melukis jendela. Anda akan menerapkan metode OnRender dan OnResize dalam langkah-langkah berikut.

    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. Terapkan metode DemoApp::OnRender . Pertama, tentukan HRESULT. Kemudian panggil metode CreateDeviceResource . Metode itu dipanggil setiap kali jendela dicat. Ingat bahwa, pada langkah 4 Bagian 3, Anda menambahkan if pernyataan untuk mencegah metode melakukan pekerjaan apa pun jika target render sudah ada.

    HRESULT DemoApp::OnRender()
    {
        HRESULT hr = S_OK;
    
        hr = CreateDeviceResources();
    
  3. Verifikasi bahwa metode CreateDeviceResource berhasil. Jika tidak, maka jangan lakukan gambar apa pun.

    if (SUCCEEDED(hr))
    {
    
  4. if Di dalam pernyataan yang baru saja Anda tambahkan, mulai menggambar dengan memanggil metode BeginDraw target render. Atur transformasi target render ke matriks identitas, dan kosongkan jendela.

    m_pRenderTarget->BeginDraw();
    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
  5. Ambil ukuran area gambar.

    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    
  6. Gambar latar belakang kisi dengan menggunakan perulangan for dan metode DrawLine target render untuk menggambar serangkaian garis.

    // 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. Buat dua primitif persegi panjang yang berpusat di layar.

    // 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. Gunakan metode FillRectangle target render untuk melukis interior persegi panjang pertama dengan kuas abu-abu.

    // Draw a filled rectangle.
    m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
    
  9. Gunakan metode DrawRectangle target render untuk melukis garis persegi panjang kedua dengan kuas biru cornflower.

    // Draw the outline of a rectangle.
    m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
    
  10. Panggil metode EndDraw target render. Metode EndDraw mengembalikan HRESULT untuk menunjukkan apakah operasi gambar berhasil. Tutup cakupan if pernyataan yang Anda mulai di Langkah 3.

        hr = m_pRenderTarget->EndDraw();
    }
    
  11. Periksa HRESULT yang dikembalikan oleh EndDraw. Jika menunjukkan bahwa target render perlu dibuat ulang, panggil metode DemoApp::D iscardDeviceResources untuk merilisnya ; ini akan dibuat ulang saat berikutnya jendela menerima pesan WM_PAINT atau WM_DISPLAYCHANGE .

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    
  12. Kembalikan HRESULT, dan tutup cakupan metode.

        return hr;
    }
    
  13. Terapkan metode DemoApp::OnResize sehingga mengubah ukuran target render ke ukuran baru jendela.

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

Anda sekarang telah menyelesaikan tutorial.

Catatan

Untuk menggunakan Direct2D, pastikan aplikasi Anda menyertakan d2d1.h file header, dan mengkompilasi terhadap d2d1.lib pustaka. Anda dapat menemukan d2d1.h dan d2d1.lib di Windows SDK.

Ringkasan

Dalam tutorial ini, Anda belajar cara membuat sumber daya Direct2D, dan menggambar bentuk dasar. Anda juga mempelajari cara menyusun aplikasi untuk meningkatkan performa dengan meminimalkan pembuatan sumber daya.