[PL] Windows 7 – nowe API – Direct2D/DirectWrite

Jakiś czas temu wspomniałem o DirectX 11. W raz z nim pojawia się nowe API o staro brzmiącej nazwie – Direct 2D – oraz Direct Write. 

Po pobraniu najnowszego SDK do DirectX oraz Windows SDK miałem okazję zapoznać się z podstawowymi elementami tych API.

Aby z niego skorzystać warto zapamiętać poniższe pliki nagłówkowe (Native C++):

#include <d2d1.h>
//Definiuje główne elementy Direct2D API
#include <d2d1helper.h>
//Definiuje pomocnicze funkcje, klasy oraz struktury
#include <d2dbasetypes.h>
//Definiuje primitywy wykorzystywane przez Direct 2D, jak punkt, prostokąt, itd. Ten nagłówek jest dołączony w d2d1.h
#include <d2derr.h>
//Definiuje kody błędów w Direct 2D. Ten nagłówek także jest dołączony w d2d1.h
#include <dwrite.h>
//Definiuje główne elementy Direct Write API

Direct 2D także opiera się na COM’owych interfejsach. Wprowadzone przez niego nowe interfejsy to: ID2D1Factory oraz ID2D1Resource.

Pierwszy tworzy dla nas obiekty zasobów oraz służy za punkt startowy dla naszej aplikacji Direct 2D. Wszystkie zasoby Direct 2D dziedziczą po drugim interfejsie.

Przykłady poniżej:

    ID2D1FactoryPtr m_spD2DFactory;
//Startowy, główny intefejs Direct 2D
    IWICImagingFactoryPtr m_spWICFactory;
//Windows Imaging Component (osobne API)
    IDWriteFactoryPtr m_spDWriteFactory;
//Startowy, główny intefejs DirectWrite
    ID2D1HwndRenderTargetPtr m_spRT;
//Tzw. Rendertarget, bufor docelowy gdzie odbywa się rendering
    IDWriteTextFormatPtr m_spTextFormat;
//Obiekt zawierający metody odpowiedzialne za format renderowanego tekstu
    ID2D1PathGeometryPtr m_spPathGeometry;
// Przykład zasobu, zawiera informacje o nieregularnej geometrii zawierającej, np. łuki, linie, krzywe, itd.
    ID2D1LinearGradientBrushPtr m_spLGBrush;
// Przykład zasobu, pędzel z wypełnieniem gradientowym
    ID2D1SolidColorBrushPtr m_spBlackBrush;
// Przykład zasobu, pędzel o jednolitym kolorze
    ID2D1BitmapPtr m_spBitmap;
// Przykład zasobu, zawiera informacje o wyświetlanej bitmapie

Powyższe definicje wskaźników powstały dzięki zastosowaniu makro:

_COM_SMARTPTR_TYPEDEF(ID2D1Factory, __uuidof(ID2D1Factory));

Analogiczne makro zostało zastosowane do pozostałych typów.
Inicjalizacja naszych fabryk to poniższy kod:

    IFR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &m_spD2DFactory
        ));

    IFR(CoCreateInstance(
        CLSID_WICImagingFactory,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWICImagingFactory,
        reinterpret_cast<void **>(&m_spWICFactory)
        ));

    IFR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(IDWriteFactory),
        reinterpret_cast<IUnknown **>(&m_spDWriteFactory)
        ));

Warto się przyjrzeć wszystkim kreatorom, jakie ukryte są pod metodami, jest ich naprawdę sporo:

image 

Skupię się na jednym istotnym. Podobnie jak w Direct 3D tworzymy docelowy bufor gdzie będziemy renderować naszą grafikę:

        RECT rc;
        GetClientRect(m_hwnd,&rc);

       D2D1_SIZE_U size = D2D1::SizeU(
            rc.right - rc.left,
     rc.bottom - rc.top);

        IFR(m_spD2DFactory->CreateHwndRenderTarget(
            D2D1::RenderTargetProperties(),
            D2D1::HwndRenderTargetProperties(m_hwnd,size),
            &m_spRT));

Mając wszystkie potrzebne zasoby odpowiednio zdefiniowane i zainicjalizowane możemy się skupić na metodzie renderingu, poniżej przykład jak się go realizuje:

HRESULT Render(const RECT &rcPaint)
{
    HRESULT hr;

    IFR(CreateDeviceResources());

    if (!(m_spRT->CheckWindowState()&D2D1_WINDOW_STATE_OCCLUDED))
    {
m_spRT->BeginDraw();
        m_spRT->SetTransform(D2D1::Matrix3x2F::Identity());

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

        D2D1_SIZE_F size = m_spBitmap->GetSize(); 
        m_spRT->DrawBitmap(m_spBitmap,
            D2D1::Rect<float>(0.0f, 0.0f, size.width,size.height)
            );

        m_spRT->SetTransform(
            D2D1::Matrix3x2F::Rotation(
                45,
                D2D1::Point2F(rtSize.width/2, rtSize.height / 2)
            ));

        m_spRT->DrawText(
            sc_helloWorld,
            ARRAY_SIZE(sc_helloWorld),
            m_spTextFormat,
            D2D1::RectF(
                0, 0, rtSize.width, rtSize.height
                ),
            m_spBlackBrush,
            D2D1_DRAW_TEXT_OPTIONS_NO_CLIP);

        hr = m_spRT->EndDraw();

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

    return S_OK;
}

Powyższa konstrukcja tego API jak widać nie powinna być znaczącą przeszkodą w nauce dla programisty, który do tej pory dotknął jakiegokolwiek API związanego z DirectX.
Są nowe intefejsy, nowe metody w obiektach do nauki, natomiast filozofia jest podobna.

Zasoby na których operujemy sugerują, że to API bardziej do grafiki użytkowej, podobnie jak GDI/GDI+ wcześniej. Mamy bogaty zestaw funkcjonalności do pracy na prymitywnych typach graficznych jak punkty, linie, krzywe, grafika rastrowa, fonty i praca na tekście.

Jeżeli chodzi o zaawansowaną pracę programistyczną w ramach grafiki 2D od razu narzuciły mi się dwa pytania. Jak wygląda bezpośredni dostęp do poszczególnych pixeli oraz ich post processingiem. Obie kwestie powinny uzyskać swoje odpowiedzi po zapoznaniu się z architekturą Direct2D i sprawdzeniem czy pipeline uwzględnia dostęp do chociażby shaderów czy bezpośredniego wskaźnika do bufora pamięci (na GPU) czy ekranu czy obrabianych bitmap.

Tutaj niestety nie jest tak prosto. Oczywiście można tak zrobić przez współprace pomiędzy Direct2D a Direct3D (D2D – D3D interop), wtedy można zawartość Direct 2D renderować do powierzchni Direct3D i podpiąć się pod pipeline D3D, natomiast chyba nie o to chodzi.

Skoro tego nie ma to co tak naprawdę uzyskaliśmy? Moim zdaniem pewnego rodzaju namiastkę WPF w Native/C++  tylko bez Xaml’a. Znając ograniczenia tego API, zapewne będzie można bardzo sprawnie stworzyć aplikacje zawierającą grafikę użytkową (raporty, intefaktywne raporty) czy multimedialne z grafiką raczej bliżej Media Center.

Nowy Direct Draw napewno to jednak nie jest.

Technorati Tagi: Polish posts,coding,DirectX,Windows 7