Delen via


Een eenvoudige Direct2D-toepassing maken

In dit onderwerp wordt u begeleid bij het maken van de DemoApp-klasse, waarmee een venster wordt gemaakt en Direct2D wordt gebruikt om inhoud te tekenen. In deze zelfstudie leert u hoe u Direct2D-resources maakt en basisshapes tekent. U leert ook hoe u uw toepassing kunt structureren om de prestaties te verbeteren door het maken van resources te minimaliseren.

Als u de zelfstudie wilt volgen, kunt u Microsoft Visual Studio gebruiken om een Win32-project te maken en vervolgens de code in de header van de hoofdtoepassing en het .cpp-bestand vervangen door de code die in deze zelfstudie wordt beschreven.

Zie ook de voorbeeld-app van Simple Direct2D-toepassing op GitHub.

Notitie

Als u een UWP-app (Universal Windows Platform) wilt maken die Gebruikmaakt van Direct2D, raadpleegt u de snelstartgids Direct2D voor Windows 8 onderwerp.

Zie het overzicht van de Direct2D-API-overzichtvoor een overzicht van de interfaces die u kunt gebruiken om Direct2D-inhoud te maken.

Na voltooiing van de zelfstudie produceert de DemoApp klasse de uitvoer die wordt weergegeven in de volgende afbeelding.

afbeelding van twee rechthoeken op een rasterachtergrond

Deel 1: De DemoApp-header maken

In deze stap stelt u uw toepassing in voor het gebruik van Direct2D door de benodigde headers en macro's toe te voegen. U declareert ook de methoden en gegevensleden die u in latere delen van deze zelfstudie gaat gebruiken.

  1. Neem in het headerbestand van uw toepassing de volgende veelgebruikte headers op.

    // 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. Declareer extra functies voor het vrijgeven van interfaces en macro's voor foutafhandeling en het ophalen van het basisadres van de module.

    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. Declareer methoden voor het initialiseren van de klasse, het maken en verwijderen van resources, het verwerken van de berichtenlus, het weergeven van inhoud en de Windows-procedure.

    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. Declareer als klasseleden aanwijzers voor een ID2D1Factory--object, een ID2D1HwndRenderTarget-object en twee ID2D1SolidColorBrush objecten.

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

Deel 2: De klasse-infrastructuur implementeren

In dit deel implementeert u de constructor en destructor van DemoApp, evenals de methoden voor initialisatie en berichtlus en de functie WinMain. De meeste van deze methoden zien er hetzelfde uit als die in een andere Win32-toepassing. De enige uitzondering hierop is de methode initialiseren, waarmee de methode CreateDeviceIndependentResources wordt aangeroepen (die u in het volgende deel definieert), waarmee verschillende Direct2D-resources worden gemaakt.

  1. Implementeer in het klasse-implementatiebestand de klasseconstructor en destructor. De constructor moet zijn leden initialiseren tot NULL. De destructor moet alle interfaces vrijgeven die zijn opgeslagen als klasseleden.

    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. Implementeer de methode DemoApp::RunMessageLoop, waarmee berichten worden vertaald en verzonden.

    void DemoApp::RunMessageLoop()
    {
        MSG msg;
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    
  3. Implementeer de methode Initialize, waarmee het venster wordt gemaakt en weergegeven; en de methode DemoApp::CreateDeviceIndependentResources wordt aangeroepen. In de volgende sectie implementeert u de methode CreateDeviceIndependentResources.

    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. Implementeer de methode WinMain, die fungeert als het toegangspunt van de toepassing. Initialiseer een exemplaar van de DemoApp, klasse en begin de berichtenlus.

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

Deel 3: Direct2D-resources maken

In dit deel maakt u de Direct2D-resources die u gebruikt om te tekenen. Direct2D biedt twee typen resources: apparaatonafhankelijke resources die kunnen duren voor de duur van de toepassing en apparaatafhankelijke resources. Apparaatafhankelijke resources zijn gekoppeld aan een bepaald renderingapparaat en werken niet meer als dat apparaat wordt verwijderd.

  1. Implementeer de methode DemoApp::CreateDeviceIndependentResources. Maak in de methode een ID2D1Factory-. Dit is een apparaatonafhankelijke resource voor het maken van andere Direct2D-resources. Gebruik het m_pDirect2DdFactory klasselid om de fabriek op te slaan.

    HRESULT DemoApp::CreateDeviceIndependentResources()
    {
        HRESULT hr = S_OK;
    
        // Create a Direct2D factory.
        hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
    
        return hr;
    }
    
  2. Implementeer de methode DemoApp::CreateDeviceResources. Met deze methode maakt u de apparaatafhankelijke resources van het venster, een renderdoel en twee kwasten. Haal de grootte van het clientgebied op en maak een ID2D1HwndRenderTarget van dezelfde grootte die wordt weergegeven op de HWND-van het venster. Sla het renderdoel op in het m_pRenderTarget class member.

    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. Gebruik het renderdoel om een grijze ID2D1SolidColorBrush en een maïsbloemblauwe ID2D1SolidColorBrushte maken.

    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. Omdat deze methode herhaaldelijk wordt aangeroepen, voegt u een if instructie toe om te controleren of het renderdoel (m_pRenderTarget) al bestaat. De volgende code toont de volledige methode CreateDeviceResources.

    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. Implementeren de methode DemoApp::DiscardDeviceResources. Laat in deze methode de rendertarget en de twee kwasten die u hebt gemaakt in de methode DemoApp::CreateDeviceResources los.

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

Deel 4: Direct2D-inhoud weergeven

In dit deel implementeert u de vensterprocedure, de OnRender-methode (die inhoud schildert) en de methode OnResize (waarmee de grootte van het renderdoel wordt aangepast wanneer het venster wordt gewijzigd).

  1. Implementeer de methode DemoApp::WndProc om vensterberichten te verwerken. Roep de methode DemoApp::OnResize aan voor het WM_SIZE bericht, en geef de nieuwe breedte en hoogte door. Voor de WM_PAINT- en WM_DISPLAYCHANGE-berichten roept u de methode DemoApp::OnRender aan om het venster te schilderen. U implementeert de OnRender- en OnResize methoden in de volgende stappen.

    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. Implementeer de methode DemoApp::OnRender. Definieer eerst een HRESULT. Roep vervolgens de methode CreateDeviceResource aan. Deze methode wordt aangeroepen telkens wanneer het venster wordt geschilderd. Zoals u weet, hebt u in stap 4 van deel 3 een if instructie toegevoegd om te voorkomen dat de methode werkt als het renderdoel al bestaat.

    HRESULT DemoApp::OnRender()
    {
        HRESULT hr = S_OK;
    
        hr = CreateDeviceResources();
    
  3. Controleer of de methode CreateDeviceResource is geslaagd. Als dat niet het geval is, voert u geen tekenwerk uit.

    if (SUCCEEDED(hr))
    {
    
  4. In de if-instructie die u zojuist hebt toegevoegd, start u de tekening door de methode BeginDraw van het renderdoel aan te roepen. Stel de transformatie van het renderdoel in op de identiteitsmatrix en wis het venster.

    m_pRenderTarget->BeginDraw();
    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
  5. De grootte van het tekengebied ophalen.

    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    
  6. Teken een rasterachtergrond door een for-lus te gebruiken en de DrawLine-methode van het renderdoelwit om een reeks lijnen te tekenen.

    // 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. Maak twee rechthoekige primitieven die zijn gecentreerd op het scherm.

    // 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. Gebruik de methode FillRectangle van het renderdoel om het interieur van de eerste rechthoek te schilderen met de grijze kwast.

    // Draw a filled rectangle.
    m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
    
  9. Gebruik de DrawRectangle methode van het renderdoel om de omtrek van de tweede rechthoek te tekenen met de korenbloemblauwe penseel.

    // Draw the outline of a rectangle.
    m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
    
  10. Roep de EndDraw-methode van het renderdoel aan. De methode EndDraw retourneert een HRESULT- om aan te geven of de tekenbewerkingen zijn geslaagd. Sluit het bereik van de if-verklaring die u in stap 3 hebt gestart.

        hr = m_pRenderTarget->EndDraw();
    }
    
  11. Controleer de HRESULT geretourneerd door EndDraw. Als wordt aangegeven dat het renderdoel opnieuw moet worden gemaakt, roept u de methode DemoApp::DiscardDeviceResources aan om het vrij te geven; het wordt opnieuw gemaakt de volgende keer dat het venster een WM_PAINT of WM_DISPLAYCHANGE bericht ontvangt.

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    
  12. Retourneer de HRESULT-en sluit het bereik van de methode.

        return hr;
    }
    
  13. Implementeer de methode DemoApp::OnResize zodat het formaat van het renderdoel wordt aangepast aan de nieuwe grootte van het venster.

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

U hebt nu de zelfstudie voltooid.

Notitie

Als u Direct2D wilt gebruiken, moet u ervoor zorgen dat uw toepassing het d2d1.h headerbestand bevat en compileert op basis van de d2d1.lib-bibliotheek. U vindt d2d1.h en d2d1.lib in de Windows SDK-.

Samenvatting

In deze zelfstudie hebt u geleerd hoe u Direct2D-resources maakt en basisvormen tekent. U hebt ook geleerd hoe u uw toepassing kunt structureren om de prestaties te verbeteren door het maken van resources te minimaliseren.