Direct2D Brush操作

为方便,将D2D的一个基本类DesktopWindow写成如下所示:

View Code
#include "Precompiled.h"
#include <d2d1.h>
#include <wrl.h>

#pragma comment(lib, "d2d1")

using namespace D2D1;
using namespace Microsoft::WRL;

template<typename T>
struct DesktopWindow
    : CWindowImpl<T, CWindow, CWinTraits<WS_OVERLAPPEDWINDOW | WS_VISIBLE>>
{
    ComPtr<ID2D1Factory> m_factory;
    ComPtr<ID2D1HwndRenderTarget> m_target;

    DECLARE_WND_CLASS_EX(L"My D2D1 Window", CS_HREDRAW | CS_VREDRAW, -1);

    BEGIN_MSG_MAP(DesktopWindow)
        MESSAGE_HANDLER(WM_PAINT, PaintHandler)
        MESSAGE_HANDLER(WM_DESTROY, DestroyHandler)
        MESSAGE_HANDLER(WM_SIZE, SizeHandler)
        MESSAGE_HANDLER(WM_DISPLAYCHANGE, DisplayChangeHandler)
    END_MSG_MAP()

    LRESULT DisplayChangeHandler(UINT, WPARAM, LPARAM, BOOL&)
    {
        Invalidate();
        return 0;
    }

    LRESULT SizeHandler(UINT, WPARAM, LPARAM lparam, BOOL&)
    {
        if (m_target)
        {
            if (m_target->Resize(SizeU(LOWORD(lparam), HIWORD(lparam))) != S_OK)
            {
                m_target.Reset();
            }
        }
        return 0;
    }

    LRESULT PaintHandler(UINT, WPARAM, LPARAM, BOOL&)
    {
        PAINTSTRUCT ps;
        VERIFY(BeginPaint(&ps));
        Render();
        EndPaint(&ps);
        return 0;
    }

    LRESULT DestroyHandler(UINT, WPARAM, LPARAM, BOOL&)
    {
        PostQuitMessage(0);
        return 0;
    }

    void Invalidate()
    {
        VERIFY(InvalidateRect(nullptr, false))
    }

    void Render()
    {
        if (!m_target)
        {
            RECT rect;
            VERIFY(GetClientRect(&rect));

            auto size = SizeU(rect.right, rect.bottom);

            m_factory->CreateHwndRenderTarget(RenderTargetProperties(), 
                                              HwndRenderTargetProperties(m_hWnd, size), 
                                              m_target.GetAddressOf());
            static_cast<T *>(this)->CreateDeviceResources();
        }

        if (!(m_target->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
        {
            m_target->BeginDraw();

            static_cast<T *>(this)->Draw();

            if (m_target->EndDraw() == D2DERR_RECREATE_TARGET)
            {
                m_target.Reset();
            }
        }
    }

    int Run()
    {
        D2D1_FACTORY_OPTIONS fo = {};
#ifdef DEBUG
        fo.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif

        D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 
                          fo, 
                          m_factory.GetAddressOf());
        CreateDeviceIndependentResources();
        VERIFY(__super::Create(nullptr, 0, L"title"));

        MSG msg;
        BOOL result;
        while (result = GetMessage(&msg, 0, 0, 0))
        {
            if (result != -1)
            {
                DispatchMessage(&msg);
            }
        }

        return msg.wParam;
    }

    virtual void CreateDeviceIndependentResources() {}
};

所有的画笔(Brush)都有SetOpacity和SetTransform两个方法,所有的单色画笔(SolidColorBrush)都有SetColor方法,可以设置颜色。
画笔是可修改的(mutable),因此,可以只声明一个画笔,然后在使用的时候,不断地更换颜色。

最简单的单色画笔(SolidColorBrush)示例如下所示:

View Code
#include "Precompiled.h"
#include "DesktopWindow.h"

D2D1_COLOR_F const COLOR_BLUE    = { 0.26f, 0.56f, 0.87f, 1.0f };
D2D1_COLOR_F const COLOR_YELLOW = { 0.99f, 0.85f, 0.0f,  1.0f };
D2D1_COLOR_F const COLOR_BLACK  = { 0.0f,  0.0f,  0.0f,  1.0f };
D2D1_COLOR_F const COLOR_WHITE  = { 1.0f,  1.0f,  1.0f,  1.0f };

struct SampleWindow : DesktopWindow
{
    ComPtr<ID2D1SolidColorBrush> m_brush;

    void CreateDeviceResources()
    {
        m_target->CreateSolidColorBrush(COLOR_BLUE, 
            m_brush.ReleaseAndGetAddressOf());
    }

    void Draw()
    {
        m_target->Clear(COLOR_BLUE);

        auto size = m_target->GetSize();

        m_brush->SetOpacity(1.0f);
        
        m_brush->SetColor(COLOR_BLACK);
        auto br = RectF(100.0f, 100.0f, size.width - 100.0f, 200.0f);
        m_target->FillRectangle(br, m_brush.Get());

        m_brush->SetColor(COLOR_WHITE);
        auto bw = RectF(100.0f, 300.0f, size.width - 100.0f, 400.0f);
        m_target->FillRectangle(bw, m_brush.Get());

        m_brush->SetOpacity(.5f);

        m_brush->SetColor(COLOR_YELLOW);
        auto ry = RectF(150.0f, 150.0f, size.width - 150.0f, 350.0f);
        m_target->FillRectangle(ry, m_brush.Get());
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    SampleWindow window;
    
    window.Run();
}

 线性渐变画笔(LinearGradientBrush)稍微麻烦,需要先定义关键点(D2D1_GRADIENT_STOP),然后将关键点加入渐变集合(ID2D1GradientStopCollection),最后将渐变集合定义为渐变画笔,其示例如下所示: 

View Code
#include "Precompiled.h"
#include "DesktopWindow.h"

D2D_COLOR_F const COLOR_BLUE    = { 0.26f, 0.56f, 0.87f, 1.0f };
D2D_COLOR_F const COLOR_WHITE    = { 1.0f,  1.0f,  1.0f,  1.0f };

struct SampleWindow : DesktopWindow<SampleWindow>
{
    ComPtr<ID2D1LinearGradientBrush> m_brush;

    void CreateDeviceResources()
    {
        D2D1_GRADIENT_STOP stops[] = 
        {
            { 0.0f, COLOR_WHITE }, 
            { 1.0f, COLOR_BLUE }
        };

        ComPtr<ID2D1GradientStopCollection> collection;

        m_target->CreateGradientStopCollection(stops, _countof(stops), 
            collection.GetAddressOf());

        D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = {};

        m_target->CreateLinearGradientBrush(props, collection.Get(), 
            m_brush.ReleaseAndGetAddressOf());
    }

    void Draw()
    {
        auto size = m_target->GetSize();

        m_brush->SetEndPoint(Point2F(size.width, size.height));
        
        auto r = RectF(0.0f, 0.0f, size.width, size.height);

        m_target->FillRectangle(r, m_brush.Get());
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    SampleWindow window;
    
    window.Run();
}

 可以使用环形渐变画笔(RadialGradientBrush),其定义方法和线性渐变画笔类似,也是定义关键点、渐变集合(COM),然后再定义渐变画笔,其救命如下所示:

View Code
#include "Precompiled.h"
#include "DesktopWindow.h"

D2D1_COLOR_F const COLOR_BLUE    = { 0.26f, 0.56f, 0.87f, 1.0f };
D2D_COLOR_F const COLOR_WHITE     = { 1.0f,  1.0f,  1.0f,  1.0f };

struct SampleWindow : DesktopWindow<SampleWindow>
{
    ComPtr<ID2D1RadialGradientBrush> m_brush;

    void CreateDeviceResources()
    {
        D2D1_GRADIENT_STOP stops[] = 
        {
            { 0.0f, COLOR_WHITE }, 
            { 1.0f, COLOR_BLUE }
        };

        ComPtr<ID2D1GradientStopCollection> collection;
        m_target->CreateGradientStopCollection(stops, _countof(stops),
            collection.GetAddressOf());
        D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = {};

        m_target->CreateRadialGradientBrush(props, collection.Get(), 
            m_brush.ReleaseAndGetAddressOf());
    }

    void Draw()
    {
        auto size = m_target->GetSize();

        m_brush->SetCenter(Point2F(size.width / 2.0f, size.height / 2.0f));
        m_brush->SetRadiusX(size.width / 2.0f);
        m_brush->SetRadiusY(size.height / 2.0f);

        auto r = RectF(0.0f, 0.0f, size.width, size.height);
        m_target->FillRectangle(r, m_brush.Get());
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    SampleWindow window;
    
    window.Run();
}

 在创建渐变集合时(CreateGradientStopCollection),可以设置D2D1_GAMMA_1_0或者D2D1_GAMMA_2_2,默认是D2D1_GAMMA_2_2,具体区别如下图所示(上图为2.2),直观上的区别就是D2D1_GAMMA_2_2的渐变性更好。

最后,还可以通过设置边框样式(ID2D1StrokeStyle)来显示各种边框,注意边框样式是独立于设备的资源,因此,只需将边框样式的定义放在CreateDeviceIndependentResources方法中即可:

View Code
#include "Precompiled.h"
#include "DesktopWindow.h"

D2D1_COLOR_F const COLOR_BLUE    = { 0.26f, 0.56f, 0.87f, 1.0f };
D2D_COLOR_F const COLOR_WHITE     = { 1.0f,  1.0f,  1.0f,  1.0f };

struct SampleWindow : DesktopWindow<SampleWindow>
{
    ComPtr<ID2D1SolidColorBrush> m_brush;
    ComPtr<ID2D1StrokeStyle> m_style;

    void CreateDeviceIndependentResources()
    {
        D2D1_STROKE_STYLE_PROPERTIES props = {};
        props.lineJoin = D2D1_LINE_JOIN_ROUND;
        props.dashStyle = D2D1_DASH_STYLE_DASH_DOT;
        props.dashCap = D2D1_CAP_STYLE_ROUND;

        m_factory->CreateStrokeStyle(props, nullptr, 0, m_style.GetAddressOf());
    }

    void CreateDeviceResources()
    {
        m_target->CreateSolidColorBrush(COLOR_WHITE, 
            m_brush.ReleaseAndGetAddressOf());
    }

    void Draw()
    {
        m_target->Clear(COLOR_BLUE);
        auto size = m_target->GetSize();
        
        auto r = RectF(100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f);
        m_target->DrawRectangle(r, m_brush.Get(), 20.0f, m_style.Get());
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    SampleWindow window;
    
    window.Run();
}


 

posted @ 2013-04-18 13:21  .NET骚操作  阅读(576)  评论(0编辑  收藏  举报