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 methodSTDMETHOD(OnManagerStatusChanged)(UI_ANIMATION_MANAGER_STATUS newStatus,UI_ANIMATION_MANAGER_STATUS previousStatus){if (m_NotifyWindow)m_NotifyWindow.Invalidate();return S_OK;}//IAnimationNotifyWindow methodSTDMETHOD(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 interfacesCComPtr<ID2D1Factory> m_spD2dFactory;CComPtr<ID2D1HwndRenderTarget> m_spHwndRT;CComPtr<ID2D1SolidColorBrush> m_spBkgndBrush;CComPtr<ID2D1LinearGradientBrush> m_spTextBrush;//DirectWrite interfacesCComPtr<IDWriteFactory> m_spDWriteFactory;CComPtr<IDWriteTextFormat> m_spTextFormat;//UIAnimation interfacesCComPtr<IUIAnimationManager> m_spIAniManager;CComPtr<IUIAnimationTimer> m_spIAniTimer;CComPtr<IUIAnimationTransitionLibrary> m_spIAniTransLib;CComPtr<IUIAnimationVariable> m_spIAniVarRed; //redCComPtr<IUIAnimationVariable> m_spIAniVarGreen; //greemCComPtr<IUIAnimationVariable> m_spIAniVarBlue; //bluepublic: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 backgroundreturn TRUE;}void OnLButtonDown(UINT nFlags, CPoint point){ChangeColor();}private:void CreateDeviceIndependentResource(){//Direct2DIFR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_spD2dFactory));//DirectWriteIFR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,__uuidof(IDWriteFactory),reinterpret_cast<IUnknown**>(&m_spDWriteFactory)));//UIAnimationIFR(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(){//Direct2DCRect 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));//DirectWriteIFR(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 colorD2D1_RECT_F rect = RectF(0,0,size.width,size.height);m_spHwndRT->FillRectangle(rect,m_spBkgndBrush);//Rotate the render target and draw textD2D1_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));
实现效果:
当用鼠标点击窗口时,窗口会平滑的变化背景色,而窗口中的文字则会以“动画”的形式旋转。
浙公网安备 33010602011771号