Einfache Direct2D-Anwendung erstellen

Dieses Thema führt Sie durch den Prozess der Erstellung der DemoApp-Klasse , die ein Fenster erstellt und Direct2D zum Zeichnen von Inhalten verwendet. In diesem Tutorial erfahren Sie, wie Sie Direct2D-Ressourcen erstellen und grundlegende Shapes zeichnen. Außerdem erfahren Sie, wie Sie Ihre Anwendung strukturieren, um die Leistung zu verbessern, indem Sie die Ressourcenerstellung minimieren.

Um das Tutorial zu befolgen, können Sie microsoft Visual Studio verwenden, um ein Win32-Projekt zu erstellen und dann den Code in der Standard-Anwendungsheader und .cpp -Datei durch den in diesem Tutorial beschriebenen Code zu ersetzen.

Weitere Informationen finden Sie auch in der Einfachen Direct2D-Anwendungsbeispiel-App auf GitHub.

Hinweis

Wenn Sie eine Universelle Windows-Plattform-App (UWP) erstellen möchten, die Direct2D verwendet, lesen Sie den Direct2D-Schnellstart für Windows 8 Thema.

Eine Übersicht über die Schnittstellen, die Sie zum Erstellen von Direct2D-Inhalten verwenden können, finden Sie in der Übersicht über die Direct2D-API.

Nach Abschluss des Tutorials erzeugt die DemoApp-Klasse die in der folgenden Abbildung gezeigte Ausgabe.

Abbildung von zwei Rechtecke auf einem Rasterhintergrund

Teil 1: Erstellen des DemoApp-Headers

In diesem Schritt richten Sie Ihre Anwendung für die Verwendung von Direct2D ein, indem Sie die erforderlichen Header und Makros hinzufügen. Außerdem deklarieren Sie die Methoden und Datenmember, die Sie in späteren Teilen dieses Tutorials verwenden werden.

  1. Fügen Sie die folgenden häufig verwendeten Header in ihre Anwendungsheaderdatei ein.

    // 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. Deklarieren Sie zusätzliche Funktionen zum Freigeben von Schnittstellen und Makros für die Fehlerbehandlung und das Abrufen der Basisadresse des Moduls.

    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. Deklarieren Sie Methoden zum Initialisieren der Klasse, zum Erstellen und Verwerfen von Ressourcen, zum Behandeln der Nachrichtenschleife, zum Rendern von Inhalten und zur Windows-Prozedur.

    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. Deklarieren Sie als Klassenmember Zeiger für ein ID2D1Factory-Objekt , ein ID2D1HwndRenderTarget-Objekt und zwei ID2D1SolidColorBrush-Objekte .

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

Teil 2: Implementieren der Klasseninfrastruktur

In diesem Teil implementieren Sie den DemoApp-Konstruktor und -Destruktor, die zugehörigen Initialisierungs- und Nachrichtenschleifenmethoden sowie die WinMain-Funktion . Die meisten dieser Methoden sehen genauso aus wie in jeder anderen Win32-Anwendung. Die einzige Ausnahme ist die Initialize-Methode , die die CreateDeviceIndependentResources-Methode aufruft (die Sie im nächsten Teil definieren), die mehrere Direct2D-Ressourcen erstellt.

  1. Implementieren Sie in der Klassenimplementierungsdatei den Klassenkonstruktor und -destruktor. Der Konstruktor sollte seine Member in NULLinitialisieren. Der Destruktor sollte alle Schnittstellen freigeben, die als Klassenmember gespeichert sind.

    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. Implementieren Sie die DemoApp::RunMessageLoop-Methode , die Nachrichten übersetzt und versendet.

    void DemoApp::RunMessageLoop()
    {
        MSG msg;
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    
  3. Implementieren Sie die Initialize-Methode , die das Fenster erstellt, es anzeigt, und ruft die DemoApp::CreateDeviceIndependentResources-Methode auf . Im nächsten Abschnitt implementieren Sie die CreateDeviceIndependentResources-Methode .

    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. Implementieren Sie die WinMain-Methode , die als Einstiegspunkt für die Anwendung dient. Initialisieren Sie eine instance der DemoApp-Klasse, und beginnen Sie die Nachrichtenschleife.

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

Teil 3: Erstellen von Direct2D-Ressourcen

In diesem Teil erstellen Sie die Direct2D-Ressourcen, die Sie zum Zeichnen verwenden. Direct2D stellt zwei Arten von Ressourcen bereit: geräteunabhängige Ressourcen, die für die Dauer der Anwendung verwendet werden können, und geräteabhängige Ressourcen. Geräteabhängige Ressourcen sind einem bestimmten Renderinggerät zugeordnet und funktionieren nicht mehr, wenn dieses Gerät entfernt wird.

  1. Implementieren Sie die DemoApp::CreateDeviceIndependentResources-Methode . Erstellen Sie in der -Methode eine ID2D1Factory, bei der es sich um eine geräteunabhängige Ressource zum Erstellen anderer Direct2D-Ressourcen handelt. Verwenden Sie den m_pDirect2DdFactory Klassenmember, um die Factory zu speichern.

    HRESULT DemoApp::CreateDeviceIndependentResources()
    {
        HRESULT hr = S_OK;
    
        // Create a Direct2D factory.
        hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
    
        return hr;
    }
    
  2. Implementieren Sie die DemoApp::CreateDeviceResources-Methode . Diese Methode erstellt die geräteabhängigen Ressourcen des Fensters, ein Renderziel und zwei Pinsel. Rufen Sie die Größe des Clientbereichs ab, und erstellen Sie ein ID2D1HwndRenderTarget der gleichen Größe, die im HWND des Fensters gerendert wird. Speichern Sie das Renderziel im m_pRenderTarget Klassenmember.

    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. Verwenden Sie das Renderziel, um einen grauen ID2D1SolidColorBrush und einen blauen Cornflower ID2D1SolidColorBrush zu erstellen.

    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. Da diese Methode wiederholt aufgerufen wird, fügen Sie eine if -Anweisung hinzu, um zu überprüfen, ob das Renderziel (m_pRenderTarget) bereits vorhanden ist. Der folgende Code zeigt die vollständige CreateDeviceResources-Methode .

    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. Implementieren Sie die DemoApp::D iscardDeviceResources-Methode . Geben Sie in dieser Methode das Renderziel und die beiden Pinsel frei, die Sie in der DemoApp::CreateDeviceResources-Methode erstellt haben.

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

Teil 4: Rendern von Direct2D-Inhalten

In diesem Teil implementieren Sie die Windows-Prozedur, die OnRender-Methode (die Inhalte zeichnet) und die OnResize-Methode (die die Größe des Renderziels anpasst, wenn die Größe des Fensters geändert wird).

  1. Implementieren Sie die DemoApp::WndProc-Methode , um Fenstermeldungen zu verarbeiten. Rufen Sie für die WM_SIZE Nachricht die DemoApp::OnResize-Methode auf, und übergeben Sie die neue Breite und Höhe. Rufen Sie für die WM_PAINT - und WM_DISPLAYCHANGE-Meldungen die DemoApp::OnRender-Methode auf, um das Fenster zu zeichnen. In den folgenden Schritten implementieren Sie die Methoden OnRender und OnResize .

    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. Implementieren Sie die DemoApp::OnRender-Methode . Definieren Sie zunächst ein HRESULT. Rufen Sie dann die CreateDeviceResource-Methode auf . Diese Methode wird jedes Mal aufgerufen, wenn das Fenster gezeichnet wird. Erinnern Sie sich daran, dass Sie in Schritt 4 von Teil 3 eine if -Anweisung hinzugefügt haben, um zu verhindern, dass die Methode Arbeit ausführt, wenn das Renderziel bereits vorhanden ist.

    HRESULT DemoApp::OnRender()
    {
        HRESULT hr = S_OK;
    
        hr = CreateDeviceResources();
    
  3. Überprüfen Sie, ob die CreateDeviceResource-Methode erfolgreich war. Wenn dies nicht der Fall ist, führen Sie keine Zeichnung aus.

    if (SUCCEEDED(hr))
    {
    
  4. Initiieren Sie in der if soeben hinzugefügten Anweisung das Zeichnen, indem Sie die BeginDraw-Methode des Renderziels aufrufen. Legen Sie die Transformation des Renderziels auf die Identitätsmatrix fest, und löschen Sie das Fenster.

    m_pRenderTarget->BeginDraw();
    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
  5. Ruft die Größe des Zeichenbereichs ab.

    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    
  6. Zeichnen Sie einen Rasterhintergrund mithilfe einer for Schleife und der DrawLine-Methode des Renderziels, um eine Reihe von Linien zu zeichnen.

    // 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. Erstellen Sie zwei Rechteckgrundtypen, die auf dem Bildschirm zentriert sind.

    // 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. Verwenden Sie die FillRectangle-Methode des Renderziels, um das Innere des ersten Rechtecks mit dem grauen Pinsel zu zeichnen.

    // Draw a filled rectangle.
    m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
    
  9. Verwenden Sie die DrawRectangle-Methode des Renderziels, um die Kontur des zweiten Rechtecks mit dem blauen Pinsel cornflower zu zeichnen.

    // Draw the outline of a rectangle.
    m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
    
  10. Rufen Sie die EndDraw-Methode des Renderziels auf. Die EndDraw-Methode gibt ein HRESULT zurück , um anzugeben, ob die Zeichnungsvorgänge erfolgreich waren. Schließen Sie den Bereich der Anweisung, die if Sie in Schritt 3 begonnen haben.

        hr = m_pRenderTarget->EndDraw();
    }
    
  11. Überprüfen Sie das von EndDraw zurückgegebene HRESULT. Wenn angegeben ist, dass das Renderziel neu erstellt werden muss, rufen Sie die DemoApp::D iscardDeviceResources-Methode auf, um es freizugeben. Sie wird neu erstellt, wenn das Fenster das nächste Mal eine WM_PAINT- oder WM_DISPLAYCHANGE-Nachricht empfängt.

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    
  12. Gibt das HRESULT zurück, und schließen Sie den Bereich der Methode.

        return hr;
    }
    
  13. Implementieren Sie die DemoApp::OnResize-Methode , damit die Größe des Renderziels auf die neue Größe des Fensters geändert wird.

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

Sie haben das Tutorial abgeschlossen.

Hinweis

Um Direct2D zu verwenden, stellen Sie sicher, dass Ihre Anwendung die d2d1.h Headerdatei enthält und für die d2d1.lib Bibliothek kompiliert wird. Sie finden d2d1.h und d2d1.lib im Windows SDK.

Zusammenfassung

In diesem Tutorial haben Sie gelernt, wie Sie Direct2D-Ressourcen erstellen und grundlegende Shapes zeichnen. Außerdem haben Sie gelernt, wie Sie Ihre Anwendung strukturieren, um die Leistung zu verbessern, indem Sie die Ressourcenerstellung minimieren.