WTL-Direct2D,DirectWrite,Windows Animation

Windows 7引入了不少有趣的新技术:Direct2D,DirectWrite,Windows Animation,还有Windows Media Foundation等等,在加上之前Windows Vista引入的Windows Image Component等技术,基本上把整个UI,多媒体框架都进行了翻新。

这几天放假无事,在看ATL/WTL,也顺带关注下Windows 7引入的这些新API。看了下SDK Sample,在介绍Windows Animation时有个有趣的小例子叫AppDrive,当用鼠标点击窗口时,窗口的背景色会非常平滑的变色。代码逻辑本身比较简单,就是纯Win32的代码看得比较费劲。于是无聊将其用WTL改写了下,代码精简了不少。

在写代码的过程中,越来越体会到ATL/WTL的强大,好用,可大大简化COM组件的编写和调用,非常适合进行Win32 API编程。

下面就把实现代码直接贴出来啦。

使用Windows Animation需要实现一个接口IUIAnimationManagerEventHandler,以下是这个接口的实现(与SDK Sample的实现方式不同):

#pragma once

#include "stdafx.h"

#include <UIAnimation.h>

[uuid("388E57E1-F20E-4E79-A732-35397AB8CC7C")]

__interface IAnimationNotifyWindow : public IUnknown

{

    HRESULT __stdcall SetNotifyWindow(HWND hNotifyWindow);

};

class CAnimationEventHandler :

    public CComObjectRootEx<CComSingleThreadModel>,

    public CComCoClass<CAnimationEventHandler>,

    public IUIAnimationManagerEventHandler,

    public IAnimationNotifyWindow

{

public:

    BEGIN_COM_MAP(CAnimationEventHandler)

        COM_INTERFACE_ENTRY(IUIAnimationManagerEventHandler)

        COM_INTERFACE_ENTRY(IAnimationNotifyWindow)

    END_COM_MAP()

    //IUIAnimationManagerEventHandler method

    STDMETHOD(OnManagerStatusChanged)(

        UI_ANIMATION_MANAGER_STATUS newStatus,

        UI_ANIMATION_MANAGER_STATUS previousStatus)

    {

        if (m_NotifyWindow)

            m_NotifyWindow.Invalidate();

        

        return S_OK;

    }

    //IAnimationNotifyWindow method

    STDMETHOD(SetNotifyWindow)(HWND hNotifyWindow)

    {

        ATLASSERT(::IsWindow(hNotifyWindow));

        m_NotifyWindow = hNotifyWindow;

        return S_OK;

    }

private:

    CWindow m_NotifyWindow;

};

这个CAnimationEventHandler类不仅实现了IUIAnimationManagerEventHandler,还实现了一个自定义接口IAnimationNotifyWindow,主窗口代码通过这个接口将其窗口句柄传递给CAnimationEventHandler类,以实现窗口通知(通知主窗口重绘)。

 

主窗口实现代码:

#pragma once

#include "stdafx.h"

#include <cstdlib>

#include <d2d1.h>

#include <d2d1helper.h>

#include <DWrite.h>

#include <wincodec.h>

#include <UIAnimation.h>

#include "AnimationEventHandler.h"

#pragma comment(lib,"d2d1.lib")

#pragma comment(lib,"dwrite.lib")

using D2D1::RenderTargetProperties;

using D2D1::HwndRenderTargetProperties;

using D2D1::LinearGradientBrushProperties;

using D2D1::SizeU;

using D2D1::SizeF;

using D2D1::ColorF;

using D2D1::Point2F;

using D2D1::Point2U;

using D2D1::RectF;

using D2D1::Matrix3x2F;

#pragma warning(push)

#pragma warning(disable:4244)

typedef CWinTraits<WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN> CMainWinTraits;

class CMainWindow :

    public CWindowImpl<CMainWindow,CWindow,CMainWinTraits>

{

private:

    //Direct2D interfaces

    CComPtr<ID2D1Factory> m_spD2dFactory;

    CComPtr<ID2D1HwndRenderTarget> m_spHwndRT;

    CComPtr<ID2D1SolidColorBrush> m_spBkgndBrush;

    CComPtr<ID2D1LinearGradientBrush> m_spTextBrush;

    //DirectWrite interfaces

    CComPtr<IDWriteFactory> m_spDWriteFactory;

    CComPtr<IDWriteTextFormat> m_spTextFormat;

    //UIAnimation interfaces

    CComPtr<IUIAnimationManager> m_spIAniManager;

    CComPtr<IUIAnimationTimer> m_spIAniTimer;

    CComPtr<IUIAnimationTransitionLibrary> m_spIAniTransLib;

    CComPtr<IUIAnimationVariable> m_spIAniVarRed;   //red

    CComPtr<IUIAnimationVariable> m_spIAniVarGreen; //greem

    CComPtr<IUIAnimationVariable> m_spIAniVarBlue;  //blue

public:

    DECLARE_WND_CLASS(_T("WTL main window"))

    BEGIN_MSG_MAP(CMainWindow)

        MSG_WM_LBUTTONDOWN(OnLButtonDown)

        MSG_WM_ERASEBKGND(OnEraseBkgnd)

        MSG_WM_CREATE(OnCreate)

        MSG_WM_DESTROY(OnDestroy)

        MSG_WM_PAINT(OnPaint)

        MSG_WM_SIZE(OnSize)

    END_MSG_MAP()

    int OnCreate(LPCREATESTRUCT lpCreateStruct)

    {

        SetWindowText(_T("Direct2D & DirectWrite & Windows Animation"));

        CreateDeviceIndependentResource();

        CreateDeviceResource();

        return 0;

    }

    void OnDestroy()

    {

        PostQuitMessage(0);

    }

    

    void OnPaint(CDCHandle)

    {

        UI_ANIMATION_SECONDS timeNow;

        IFR(m_spIAniTimer->GetTime(&timeNow));

        IFR(m_spIAniManager->Update(timeNow));

        Render();

        UI_ANIMATION_MANAGER_STATUS status;

        m_spIAniManager->GetStatus(&status);

        if (status == UI_ANIMATION_MANAGER_BUSY)

        {

            Invalidate(FALSE);

        }

    }

    

    void OnSize(UINT nType, CSize size)

    {

        if (m_spHwndRT)

            m_spHwndRT->Resize(SizeU(size.cx,size.cy));

    }

    

    BOOL OnEraseBkgnd(CDCHandle dc)

    {

        //We have ereased the background

        return TRUE;

    }

    

    void OnLButtonDown(UINT nFlags, CPoint point)

    {

        ChangeColor();

    }

private:

    void CreateDeviceIndependentResource()

    {

        //Direct2D

        IFR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,

            &m_spD2dFactory));

        //DirectWrite

        IFR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,

            __uuidof(IDWriteFactory),

            reinterpret_cast<IUnknown**>(&m_spDWriteFactory)));

        //UIAnimation

        IFR(m_spIAniManager.CoCreateInstance(CLSID_UIAnimationManager));

        IFR(m_spIAniTimer.CoCreateInstance(CLSID_UIAnimationTimer));

        IFR(m_spIAniTransLib.CoCreateInstance(CLSID_UIAnimationTransitionLibrary));

        CComPtr<IUIAnimationManagerEventHandler> spIAniEventHandler;

        IFR(CAnimationEventHandler::CreateInstance(&spIAniEventHandler));

        CComPtr<IAnimationNotifyWindow> spIAniNotifyWindow;

        IFR(spIAniEventHandler->QueryInterface(&spIAniNotifyWindow));

        spIAniNotifyWindow->SetNotifyWindow(m_hWnd);

        IFR(m_spIAniManager->SetManagerEventHandler(spIAniEventHandler));

        IFR(m_spIAniManager->CreateAnimationVariable(1.0,&m_spIAniVarRed));

        IFR(m_spIAniVarRed->SetLowerBound(0));

        IFR(m_spIAniVarRed->SetUpperBound(1.0));

        IFR(m_spIAniManager->CreateAnimationVariable(1.0,&m_spIAniVarGreen));

        IFR(m_spIAniVarGreen->SetLowerBound(0));

        IFR(m_spIAniVarGreen->SetUpperBound(1.0));

        IFR(m_spIAniManager->CreateAnimationVariable(1.0,&m_spIAniVarBlue));

        IFR(m_spIAniVarBlue->SetLowerBound(0));

        IFR(m_spIAniVarBlue->SetUpperBound(1.0));

    }

    void CreateDeviceResource()

    {

        //Direct2D

        CRect rc;

        GetClientRect(&rc);

        D2D1_SIZE_U size = SizeU(rc.Width(),rc.Height());

        IFR(m_spD2dFactory->CreateHwndRenderTarget(

            RenderTargetProperties(),

            HwndRenderTargetProperties(m_hWnd,size),

            &m_spHwndRT));

        D2D1_COLOR_F color = ColorF(ColorF::Red);

        IFR(m_spHwndRT->CreateSolidColorBrush(color,&m_spBkgndBrush));

        CComPtr<ID2D1GradientStopCollection> spStopColl = NULL;

        D2D1_GRADIENT_STOP stops2[] =

        {

            {0.25f,ColorF(ColorF::Red)},

            {0.5f,ColorF(ColorF::Yellow)},

            {0.75f,ColorF(ColorF::Blue)}

        };

        IFR(m_spHwndRT->CreateGradientStopCollection(

            stops2,

            ARRAYSIZE(stops2),

            &spStopColl));

        IFR(m_spHwndRT->CreateLinearGradientBrush(

            LinearGradientBrushProperties(Point2F(0,0),Point2F(100,0)),

            spStopColl,

            &m_spTextBrush));

        //DirectWrite

        IFR(m_spDWriteFactory->CreateTextFormat(

            _T("Courier New"),

            nullptr,

            DWRITE_FONT_WEIGHT_NORMAL,

            DWRITE_FONT_STYLE_NORMAL,

            DWRITE_FONT_STRETCH_NORMAL,

            48,

            _T("zh-Hans"),

            &m_spTextFormat));

        m_spTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);

        m_spTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);

    }

    void DiscardDeviceResource()

    {

        ATLTRACE("DiscardDeviceResource\n");

        m_spTextBrush = NULL;

        m_spBkgndBrush = NULL;

        m_spHwndRT = NULL;

        m_spTextFormat = NULL;

    }

    void Render()

    {

        if (!m_spHwndRT)

            CreateDeviceResource();

        HRESULT hr = S_OK;

        m_spHwndRT->BeginDraw();

        m_spHwndRT->SetTransform(Matrix3x2F::Identity());

        //m_spHwndRT->Clear(ColorF(ColorF::White));

        DOUBLE red=0, green=0, blue=0;

        m_spIAniVarRed->GetValue(&red);

        m_spIAniVarGreen->GetValue(&green);

        m_spIAniVarBlue->GetValue(&blue);

        m_spBkgndBrush->SetColor(ColorF(red,green,blue));

        D2D1_SIZE_F size = m_spHwndRT->GetSize();

        //Fill the background with randomly generated color

        D2D1_RECT_F rect = RectF(0,0,size.width,size.height);

        m_spHwndRT->FillRectangle(rect,m_spBkgndBrush);

        

        //Rotate the render target and draw text

        D2D1_POINT_2F center = Point2F(size.width/2,size.height/2);

        FLOAT degree = red*360.0f;

        m_spHwndRT->SetTransform(Matrix3x2F::Rotation(degree,center));

        m_spTextBrush->SetEndPoint(Point2F(size.width,0));

        CString text = _T("Direct2D&DirectWrite&Windows Animation");

        m_spHwndRT->DrawText(

            text,

            text.GetLength(),

            m_spTextFormat,

            rect,

            m_spTextBrush);

        hr = m_spHwndRT->EndDraw();

        if (hr == D2DERR_RECREATE_TARGET)

            DiscardDeviceResource();

    }

    void ChangeColor()

    {

        DOUBLE red = ((DOUBLE)rand())/RAND_MAX;

        DOUBLE green = ((DOUBLE)rand())/RAND_MAX;

        DOUBLE blue = ((DOUBLE)rand())/RAND_MAX;

        const UI_ANIMATION_SECONDS DURATION = 0.5;

        const DOUBLE ACCEL_RATIO = 0.5;

        const DOUBLE DECEL_RATIO = 0.5;

        CComPtr<IUIAnimationStoryboard> spStoryBoard;

        IFR(m_spIAniManager->CreateStoryboard(&spStoryBoard));

        

        CComPtr<IUIAnimationTransition> spITransRed;

        IFR(m_spIAniTransLib->CreateAccelerateDecelerateTransition(

            DURATION,

            red,

            ACCEL_RATIO,

            DECEL_RATIO,

            &spITransRed));

        CComPtr<IUIAnimationTransition> spITransGreen;

        IFR(m_spIAniTransLib->CreateAccelerateDecelerateTransition(

            DURATION,

            green,

            ACCEL_RATIO,

            DECEL_RATIO,

            &spITransGreen));

        CComPtr<IUIAnimationTransition> spITransBlue;

        IFR(m_spIAniTransLib->CreateAccelerateDecelerateTransition(

            DURATION,

            blue,

            ACCEL_RATIO,

            DECEL_RATIO,

            &spITransBlue));

        IFR(spStoryBoard->AddTransition(m_spIAniVarRed,spITransRed));

        IFR(spStoryBoard->AddTransition(m_spIAniVarGreen,spITransGreen));

        IFR(spStoryBoard->AddTransition(m_spIAniVarBlue,spITransBlue));

        UI_ANIMATION_SECONDS timeNow;

        IFR(m_spIAniTimer->GetTime(&timeNow));

        spStoryBoard->Schedule(timeNow);

    }

};

#pragma warning(pop)

与SDK Sample不同的是对IUIAnimationManagerEventHandler接口的实现以及通知窗口的设置,感觉用ATL风格的实现更加优雅,代码也要简洁很多:

 

CComPtr<IUIAnimationManagerEventHandler> spIAniEventHandler;

IFR(CAnimationEventHandler::CreateInstance(&spIAniEventHandler));

CComPtr<IAnimationNotifyWindow> spIAniNotifyWindow;

IFR(spIAniEventHandler->QueryInterface(&spIAniNotifyWindow));

spIAniNotifyWindow->SetNotifyWindow(m_hWnd);

IFR(m_spIAniManager->SetManagerEventHandler(spIAniEventHandler));

 

实现效果:

ScreenShot00116

 

当用鼠标点击窗口时,窗口会平滑的变化背景色,而窗口中的文字则会以“动画”的形式旋转。

你可能感兴趣的:(animation)