1133 lines
29 KiB
C++
1133 lines
29 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
/*------------------------------------------------------------------------
|
|
Sample name: WAMManagingDComp
|
|
|
|
Description:
|
|
This sample demonstrates how to use the curve generation feature of
|
|
Windows Animation Manager (WAM) with DirectComposition (DComp).
|
|
Specifically, the application shows how to:
|
|
|
|
- create DirectComposition device and visuals
|
|
- set DirectComposition transforms
|
|
- create WAM storyboard, variable, and transitions
|
|
- generate animation curves and propagate the curves to DComp
|
|
- synchronize WAM and DirectComposition time
|
|
- Schedule animations
|
|
|
|
Usage:
|
|
Left mousekey press - slides the tiles forward
|
|
Right mousekey press - slides the tiles backward
|
|
|
|
--------------------------------------------------------------------------*/
|
|
|
|
#include <dwmapi.h>
|
|
#include <math.h>
|
|
#include <wincodec.h>
|
|
#include <tchar.h>
|
|
#include <strsafe.h>
|
|
|
|
#include "Resource.h"
|
|
#include "DirectComposition_WAM.h"
|
|
|
|
CApplication *CApplication::_application = nullptr;
|
|
|
|
#define WINDOW_SIZE 500
|
|
#define TILE_SPACING 170.0f
|
|
|
|
//------------------------------------------------------
|
|
// Processing Windows Commands
|
|
//------------------------------------------------------
|
|
LRESULT CALLBACK CApplication::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT result = 0;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_CREATE:
|
|
_application->OnCreate();
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
result = _application->OnPaint(hwnd);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
result = _application->Move(forward);
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
result = _application->Move(backward);
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
result = _application->Move(stopForward);
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
result = _application->Move(stopBackward);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
result = _application->OnClose();
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
result = _application->OnDestroy();
|
|
break;
|
|
|
|
default:
|
|
result = DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CApplication::CApplication(HINSTANCE instance) :
|
|
_hinstance(instance),
|
|
_hwnd(NULL),
|
|
_hbrush(NULL)
|
|
{
|
|
_application = this;
|
|
}
|
|
|
|
CApplication::~CApplication()
|
|
{
|
|
_application = nullptr;
|
|
}
|
|
|
|
int CApplication::Run()
|
|
{
|
|
int result = 0;
|
|
|
|
if (SUCCEEDED(BeforeEnteringMessageLoop()))
|
|
{
|
|
result = EnterMessageLoop();
|
|
}
|
|
else
|
|
{
|
|
MessageBoxW(NULL, L"An error occuring when running the sample", NULL, MB_OK);
|
|
}
|
|
|
|
AfterLeavingMessageLoop();
|
|
|
|
return result;
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Creates and initializes all the objects we need for the application
|
|
//--------------------------------------------------------
|
|
|
|
HRESULT CApplication::BeforeEnteringMessageLoop()
|
|
{
|
|
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateApplicationWindow();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateD3D11Device();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateD2D1Factory();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateD2D1Device();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateWICFactory();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateAnimationManager();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateAnimationTransitionLibrary();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateAnimationVariables();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateDCompositionDevice();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateDCompositionRenderTarget();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateDCompositionVisualTree();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = AttachDCompositionVisualTreeToRenderTarget();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->Commit();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
//----------------------------------------------------------
|
|
// Create an instance of WAM Manager Object which manages storyboards,
|
|
// transitions, and variables
|
|
//----------------------------------------------------------
|
|
HRESULT CApplication::CreateAnimationManager()
|
|
{
|
|
return ::CoCreateInstance(
|
|
CLSID_UIAnimationManager2,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IUIAnimationManager2,
|
|
reinterpret_cast<LPVOID *>(&_manager));
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// Creates an WAM transition library which enables us to schedule
|
|
// transitions
|
|
//-----------------------------------------------------------
|
|
HRESULT CApplication::CreateAnimationTransitionLibrary()
|
|
{
|
|
return ::CoCreateInstance(
|
|
CLSID_UIAnimationTransitionLibrary2,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IUIAnimationTransitionLibrary2,
|
|
reinterpret_cast<LPVOID *>(&_transitionLibrary));
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// Creates an WAM animation variable which we will use to animate
|
|
// the tiles
|
|
//-----------------------------------------------------------
|
|
HRESULT CApplication::CreateAnimationVariables()
|
|
{
|
|
HRESULT hr = (_manager == nullptr) ? E_UNEXPECTED : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _manager->CreateAnimationVariable(0.0, &_animationVariable);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::CreateD3D11Device()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
D3D_DRIVER_TYPE driverTypes[] =
|
|
{
|
|
D3D_DRIVER_TYPE_HARDWARE,
|
|
D3D_DRIVER_TYPE_WARP,
|
|
};
|
|
|
|
D3D_FEATURE_LEVEL featureLevelSupported;
|
|
|
|
for (int i = 0; i < sizeof(driverTypes) / sizeof(driverTypes[0]); ++i)
|
|
{
|
|
CComPtr<ID3D11Device> d3d11Device;
|
|
CComPtr<ID3D11DeviceContext> d3d11DeviceContext;
|
|
|
|
hr = D3D11CreateDevice(
|
|
nullptr,
|
|
driverTypes[i],
|
|
NULL,
|
|
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
|
NULL,
|
|
0,
|
|
D3D11_SDK_VERSION,
|
|
&d3d11Device,
|
|
&featureLevelSupported,
|
|
&d3d11DeviceContext);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_d3d11Device = d3d11Device.Detach();
|
|
_d3d11DeviceContext = d3d11DeviceContext.Detach();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::CreateD2D1Factory()
|
|
{
|
|
return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &_d2d1Factory);
|
|
}
|
|
|
|
HRESULT CApplication::CreateD2D1Device()
|
|
{
|
|
HRESULT hr = ((_d3d11Device == nullptr) || (_d2d1Factory == nullptr)) ? E_UNEXPECTED : S_OK;
|
|
|
|
CComPtr<IDXGIDevice> dxgiDevice;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _d3d11Device->QueryInterface(&dxgiDevice);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _d2d1Factory->CreateDevice(dxgiDevice, &_d2d1Device);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &_d2d1DeviceContext);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::CreateWICFactory()
|
|
{
|
|
return CoCreateInstance(
|
|
CLSID_WICImagingFactory,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&_wicFactory));
|
|
}
|
|
|
|
int CApplication::EnterMessageLoop()
|
|
{
|
|
int result = 0;
|
|
|
|
if (ShowApplicationWindow())
|
|
{
|
|
MSG msg = { 0 };
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
result = static_cast<int>(msg.wParam);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CApplication::DestroyAnimationVariables()
|
|
{
|
|
_animationVariable = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyAnimationTransitionLibrary()
|
|
{
|
|
_transitionLibrary = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyAnimationManager()
|
|
{
|
|
_manager = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyWICFactory()
|
|
{
|
|
_wicFactory = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyD2D1Device()
|
|
{
|
|
_d2d1DeviceContext = nullptr;
|
|
_d2d1Device = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyD2D1Factory()
|
|
{
|
|
_d2d1Factory = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyD3D11Device()
|
|
{
|
|
_d3d11DeviceContext = nullptr;
|
|
_d3d11Device = nullptr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// Cleanup when we close the application
|
|
//-----------------------------------------------------------
|
|
void CApplication::AfterLeavingMessageLoop()
|
|
{
|
|
DestroyDCompositionVisualTree();
|
|
DestroyDCompositionRenderTarget();
|
|
DestroyDCompositionDevice();
|
|
DestroyApplicationWindow();
|
|
DestroyAnimationTransitionLibrary();
|
|
DestroyAnimationVariables();
|
|
DestroyAnimationManager();
|
|
DestroyApplicationWindow();
|
|
DestroyWICFactory();
|
|
DestroyD2D1Device();
|
|
DestroyD2D1Factory();
|
|
DestroyD3D11Device();
|
|
CoUninitialize();
|
|
}
|
|
|
|
HRESULT CApplication::CreateApplicationWindow()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WNDCLASSEX wcex = { 0 };
|
|
|
|
wcex.cbSize = sizeof (wcex);
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = CApplication::WindowProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = _hinstance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
|
|
wcex.lpszMenuName = nullptr;
|
|
wcex.lpszClassName = "MainWindowClass";
|
|
wcex.hIconSm = NULL;
|
|
|
|
hr = !RegisterClassEx(&wcex) ? E_FAIL : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RECT rect = { 0, 0, WINDOW_SIZE, WINDOW_SIZE};
|
|
|
|
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
|
|
_hwnd = CreateWindowExW(
|
|
0,
|
|
L"MainWindowClass",
|
|
L"Windows Animation Manager (WAM) Sample",
|
|
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
NULL,
|
|
NULL,
|
|
_hinstance,
|
|
nullptr);
|
|
|
|
if (_hwnd == NULL)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR fontTypeface[32] = { 0 };
|
|
|
|
hr = !LoadStringW(_hinstance, IDS_FONT_TYPEFACE, fontTypeface, ARRAYSIZE(fontTypeface)) ? E_FAIL : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCopyW(_fontTypeface, ARRAYSIZE(_fontTypeface), fontTypeface);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR fontHeightLogo[32] = { 0 };
|
|
|
|
hr = !LoadStringW(_hinstance, IDS_FONT_HEIGHT_LOGO, fontHeightLogo, ARRAYSIZE(fontHeightLogo)) ? E_FAIL : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_fontHeightLogo = _wtoi(fontHeightLogo);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR fontHeightTitle[32] = { 0 };
|
|
|
|
hr = !LoadStringW(_hinstance, IDS_FONT_HEIGHT_TITLE, fontHeightTitle, ARRAYSIZE(fontHeightTitle)) ? E_FAIL : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_fontHeightTitle = _wtoi(fontHeightTitle);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR fontHeightDescription[32] = { 0 };
|
|
|
|
hr = !LoadStringW(_hinstance, IDS_FONT_HEIGHT_DESCRIPTION, fontHeightDescription, ARRAYSIZE(fontHeightDescription)) ? E_FAIL : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_fontHeightDescription = _wtoi(fontHeightDescription);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool CApplication::ShowApplicationWindow()
|
|
{
|
|
if (_hwnd != NULL)
|
|
{
|
|
ShowWindow(_hwnd, SW_SHOW);
|
|
UpdateWindow(_hwnd);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CApplication::DestroyApplicationWindow()
|
|
{
|
|
if (_hwnd != NULL)
|
|
{
|
|
DestroyWindow(_hwnd);
|
|
_hwnd = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// Creates a DirectComposition device
|
|
//-----------------------------------------------------------
|
|
HRESULT CApplication::CreateDCompositionDevice()
|
|
{
|
|
HRESULT hr = (_d3d11Device == nullptr) ? E_UNEXPECTED : S_OK;
|
|
|
|
CComPtr<IDXGIDevice> dxgiDevice;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _d3d11Device->QueryInterface(&dxgiDevice);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = DCompositionCreateDevice(dxgiDevice, __uuidof(IDCompositionDevice), reinterpret_cast<void **>(&_device));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// Creates an render target for DirectComposition which
|
|
// is an hwnd in this case
|
|
//--------------------------------------------------------
|
|
HRESULT CApplication::CreateDCompositionRenderTarget()
|
|
{
|
|
HRESULT hr = ((_device == nullptr) || (_hwnd == NULL)) ? E_UNEXPECTED : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->CreateTargetForHwnd(_hwnd, TRUE, &_target);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Creates a DirectComposition visual tree and places each visual
|
|
// inside the application window
|
|
//---------------------------------------------------------------------
|
|
HRESULT CApplication::CreateDCompositionVisualTree()
|
|
{
|
|
static const WCHAR *filename = L"220Strawberry.png";
|
|
static const float tileSize = 0.3f * WINDOW_SIZE;
|
|
static const int visualChildCount = sizeof(_visualChild) / sizeof(_visualChild[0]);
|
|
static const float d = 2.0f * WINDOW_SIZE;
|
|
|
|
HRESULT hr = ((_device == nullptr) || (_hwnd == NULL)) ? E_UNEXPECTED : S_OK;
|
|
|
|
int bitmapWidth = 0;
|
|
int bitmapHeight = 0;
|
|
|
|
CComPtr<IDCompositionSurface> surface;
|
|
|
|
// Create DirectComposition surface from the bitmap file
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateSurfaceFromFile(filename, &bitmapWidth, &bitmapHeight, &surface);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_bitmapWidth = bitmapWidth;
|
|
_bitmapHeight = bitmapHeight;
|
|
|
|
hr = _device->CreateVisual(&_visual);
|
|
}
|
|
|
|
// Set the content of each visual to be the surface that was created from the bitmap
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (int i = 0; SUCCEEDED(hr) && i < visualChildCount; ++i)
|
|
{
|
|
hr = _device->CreateVisual(&_visualChild[i]);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _visual->AddVisual(_visualChild[i], FALSE, nullptr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _visualChild[i]->SetContent(surface);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Using DirectComposition transforms to scale and place each visual such that the tiles
|
|
// are side by side within the application window
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (int i = 0; SUCCEEDED(hr) && i < visualChildCount; ++i)
|
|
{
|
|
//setting up scale transform on each visual
|
|
CComPtr<IDCompositionScaleTransform> scaleTransform;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->CreateScaleTransform(&scaleTransform);
|
|
}
|
|
|
|
float sx = tileSize / bitmapWidth;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = scaleTransform->SetScaleX(sx);
|
|
}
|
|
|
|
float sy = tileSize / bitmapHeight;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = scaleTransform->SetScaleY(sy);
|
|
}
|
|
|
|
//Setting up a translate transform on each visual
|
|
CComPtr<IDCompositionTranslateTransform> translateTransform;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->CreateTranslateTransform(&translateTransform);
|
|
}
|
|
|
|
float x = (visualChildCount - 1 - i) * TILE_SPACING;
|
|
float y = TILE_SPACING + 30;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = translateTransform->SetOffsetX(x);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = translateTransform->SetOffsetY(y);
|
|
}
|
|
|
|
// Creating a transform group to group the two transforms together such that
|
|
// they can be applied at once.
|
|
IDCompositionTransform *transforms[] =
|
|
{
|
|
scaleTransform,
|
|
translateTransform,
|
|
};
|
|
|
|
CComPtr<IDCompositionTransform> transformGroup;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_device->CreateTransformGroup(transforms, sizeof(transforms)/sizeof(transforms[0]), &transformGroup);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_visualChild[i]->SetTransform(transformGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------
|
|
// Use WAM to generate and propagate the appropriate animation curves to DirectComposition when
|
|
// keypress is detected
|
|
//-------------------------------------------------------------------------------
|
|
HRESULT CApplication::CreateSlideAnimation(DIRECTION dir, IDCompositionAnimation **slideAnimation)
|
|
{
|
|
HRESULT hr = (slideAnimation == nullptr) ? E_POINTER : S_OK;
|
|
|
|
float rightMargin = 27 * TILE_SPACING * -1; //where the tiles end. Note forward direction is represented by a negative value.
|
|
float leftMargin = 0; // where the tiles begin
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*slideAnimation = nullptr;
|
|
hr = ((_device == nullptr) || (_animationVariable == nullptr)) ? E_UNEXPECTED : S_OK;
|
|
}
|
|
|
|
//WAM propagates curves to DirectComposition using the IDCompositionAnimation object
|
|
CComPtr<IDCompositionAnimation> animation;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->CreateAnimation(&animation);
|
|
}
|
|
|
|
//Create a storyboard for the slide animation
|
|
CComPtr<IUIAnimationStoryboard2> storyboard;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _manager->CreateStoryboard(&storyboard);
|
|
}
|
|
|
|
// Synchronizing WAM and DirectComposition time such that when WAM Update is called,
|
|
// the value reflects the DirectComposition value at the given time.
|
|
DCOMPOSITION_FRAME_STATISTICS frameStatistics = { 0 };
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->GetFrameStatistics(&frameStatistics);
|
|
}
|
|
|
|
UI_ANIMATION_SECONDS nextEstimatedFrameTime = 0.0;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
nextEstimatedFrameTime = static_cast<double>(frameStatistics.nextEstimatedFrameTime.QuadPart) / static_cast<double>(frameStatistics.timeFrequency.QuadPart);
|
|
}
|
|
|
|
//Upating the WAM time
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _manager->Update(nextEstimatedFrameTime);
|
|
}
|
|
|
|
CComPtr<IUIAnimationTransition2> transition;
|
|
double curValue = 0; //current value of the animation variable
|
|
int velocity = 500; //arbitrary fix velocity for the slide animation
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _animationVariable->GetValue(&curValue);
|
|
|
|
switch (dir)
|
|
{
|
|
case stopForward:
|
|
case stopBackward:
|
|
// Stopping the animation smoothly when key is let go
|
|
if (curValue != leftMargin && curValue != rightMargin)
|
|
hr = _transitionLibrary->CreateSmoothStopTransition(0.5, curValue + dir * 50, &transition);
|
|
break;
|
|
case forward:
|
|
// slide the tiles forward using a linear curve upon left button press
|
|
hr = _transitionLibrary->CreateLinearTransition(-1 * (rightMargin - curValue)/velocity, rightMargin, &transition);
|
|
break;
|
|
case backward:
|
|
// slide the tiles backward using a linear cruve upon right button press
|
|
hr = _transitionLibrary->CreateLinearTransition(-1 * curValue/velocity, leftMargin, &transition);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Add above transition to storyboard
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = storyboard->AddTransition(_animationVariable, transition);
|
|
}
|
|
|
|
//schedule the storyboard for play at the next estimate vblank
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = storyboard->Schedule(nextEstimatedFrameTime);
|
|
}
|
|
|
|
//Giving WAM varialbe the IDCompositionAnimation object to recieve the animation curves
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _animationVariable->GetCurve(animation);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*slideAnimation = animation.Detach();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::AttachDCompositionVisualTreeToRenderTarget()
|
|
{
|
|
HRESULT hr = ((_target == nullptr) || (_visual == nullptr)) ? E_UNEXPECTED : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _target->SetRoot(_visual);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::DetachDCompositionVisualTreeToRenderTarget()
|
|
{
|
|
HRESULT hr = (_target == nullptr) ? E_UNEXPECTED : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _target->SetRoot(nullptr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CApplication::DestroyDCompositionVisualTree()
|
|
{
|
|
for (int i = 0; i < sizeof(_visualChild) / sizeof(_visualChild[0]); ++i)
|
|
{
|
|
_visualChild[i] = nullptr;
|
|
}
|
|
|
|
_visual = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyDCompositionRenderTarget()
|
|
{
|
|
_target = nullptr;
|
|
}
|
|
|
|
void CApplication::DestroyDCompositionDevice()
|
|
{
|
|
_device = nullptr;
|
|
}
|
|
|
|
void CApplication::OnCreate()
|
|
{
|
|
_hbrush = CreateSolidBrush(RGB(255, 255, 255));
|
|
}
|
|
|
|
LRESULT CApplication::OnPaint(HWND hwnd)
|
|
{
|
|
RECT rcClient;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hwnd, &ps);
|
|
FillRect(hdc, &ps.rcPaint, _hbrush);
|
|
|
|
// get the dimensions of the main window.
|
|
GetClientRect(_hwnd, &rcClient);
|
|
|
|
// Logo
|
|
HFONT hlogo = CreateFontW(_fontHeightLogo, 0, 0, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, _fontTypeface); // Logo Font and Size
|
|
if (hlogo != NULL)
|
|
{
|
|
HFONT hOldFont = static_cast<HFONT>(SelectObject(hdc, hlogo));
|
|
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
|
|
rcClient.top = 10;
|
|
rcClient.left = 30;
|
|
|
|
DrawTextW(hdc, L"Windows samples", -1, &rcClient, DT_WORDBREAK);
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
DeleteObject(hlogo);
|
|
}
|
|
|
|
// Title
|
|
HFONT htitle = CreateFontW(_fontHeightTitle, 0, 0, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, _fontTypeface); // Title Font and Size
|
|
if (htitle != NULL)
|
|
{
|
|
HFONT hOldFont = static_cast<HFONT>(SelectObject(hdc, htitle));
|
|
|
|
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
|
|
|
|
rcClient.top = 25;
|
|
rcClient.left = 30;
|
|
|
|
DrawTextW(hdc, L"WAM Sample", -1, &rcClient, DT_WORDBREAK);
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
DeleteObject(htitle);
|
|
}
|
|
|
|
// Description
|
|
HFONT hdescription = CreateFontW(_fontHeightDescription, 0, 0, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, _fontTypeface); // Description Font and Size
|
|
if (hdescription != NULL)
|
|
{
|
|
HFONT hOldFont = static_cast<HFONT>(SelectObject(hdc, hdescription));
|
|
|
|
rcClient.top = 90;
|
|
rcClient.left = 30;
|
|
|
|
DrawTextW(hdc, L"This sample shows how DirectComposition and Windows Animation Manager (WAM) can be used together as an independent animation platform.", -1, &rcClient, DT_WORDBREAK);
|
|
|
|
rcClient.top = 400;
|
|
rcClient.left = 220;
|
|
|
|
DrawTextW(hdc, L"Left/Right click to control the animation.", -1, &rcClient, DT_WORDBREAK);
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
DeleteObject(hdescription);
|
|
}
|
|
|
|
EndPaint(hwnd, &ps);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CApplication::OnClose()
|
|
{
|
|
if (_hwnd != NULL)
|
|
{
|
|
DestroyWindow(_hwnd);
|
|
_hwnd = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CApplication::OnDestroy()
|
|
{
|
|
if (_hbrush != NULL)
|
|
{
|
|
DeleteObject(_hbrush);
|
|
_hbrush = NULL;
|
|
}
|
|
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CApplication::CreateSurfaceFromFile(const WCHAR *filename, int *bitmapWidth, int *bitmapHeight, IDCompositionSurface **surface)
|
|
{
|
|
HRESULT hr = ((bitmapWidth == nullptr) || (bitmapHeight == nullptr) || (surface == nullptr)) ? E_POINTER : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*bitmapWidth = 0;
|
|
*bitmapHeight = 0;
|
|
*surface = NULL;
|
|
|
|
hr = (filename == nullptr) ? E_INVALIDARG : S_OK;
|
|
}
|
|
|
|
CComPtr<ID2D1Bitmap> d2d1Bitmap;
|
|
D2D1_SIZE_F bitmapSize = { 0 };
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateD2D1BitmapFromFile(filename, &d2d1Bitmap);
|
|
}
|
|
|
|
CComPtr<IDCompositionSurface> surfaceTile;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bitmapSize = d2d1Bitmap->GetSize();
|
|
|
|
hr = _device->CreateSurface(
|
|
static_cast<UINT>(bitmapSize.width),
|
|
static_cast<UINT>(bitmapSize.height),
|
|
DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
DXGI_ALPHA_MODE_IGNORE,
|
|
&surfaceTile);
|
|
}
|
|
|
|
CComPtr<IDXGISurface> dxgiSurface;
|
|
POINT offset;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RECT rect = { 0, 0, static_cast<LONG>(bitmapSize.width), static_cast<LONG>(bitmapSize.height) };
|
|
|
|
hr = surfaceTile->BeginDraw(&rect, __uuidof(IDXGISurface), reinterpret_cast<void **>(&dxgiSurface), &offset);
|
|
}
|
|
|
|
CComPtr<ID2D1Bitmap1> d2d1Target;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
FLOAT dpiX = 0.0f;
|
|
FLOAT dpiY = 0.0f;
|
|
|
|
_d2d1Factory->GetDesktopDpi(&dpiX, &dpiY);
|
|
|
|
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
|
|
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
|
|
D2D1::PixelFormat(DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
|
|
dpiX,
|
|
dpiY);
|
|
|
|
hr = _d2d1DeviceContext->CreateBitmapFromDxgiSurface(dxgiSurface, &bitmapProperties, &d2d1Target);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_d2d1DeviceContext->SetTarget(d2d1Target);
|
|
|
|
_d2d1DeviceContext->BeginDraw();
|
|
|
|
_d2d1DeviceContext->DrawBitmap(
|
|
d2d1Bitmap,
|
|
D2D1::RectF(
|
|
offset.x + 0.0f,
|
|
offset.y + 0.0f,
|
|
offset.x + bitmapSize.width,
|
|
offset.y + bitmapSize.height));
|
|
|
|
hr = _d2d1DeviceContext->EndDraw();
|
|
}
|
|
|
|
surfaceTile->EndDraw();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*bitmapWidth = static_cast<int>(bitmapSize.width);
|
|
*bitmapHeight = static_cast<int>(bitmapSize.height);
|
|
*surface = surfaceTile.Detach();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CApplication::CreateD2D1BitmapFromFile(LPCWSTR filename, ID2D1Bitmap **bitmap)
|
|
{
|
|
HRESULT hr = (bitmap == nullptr) ? E_POINTER : S_OK;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*bitmap = nullptr;
|
|
|
|
hr = (_wicFactory == nullptr) ? E_UNEXPECTED : S_OK;
|
|
}
|
|
|
|
CComPtr<IWICBitmapDecoder> wicBitmapDecoder;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _wicFactory->CreateDecoderFromFilename(
|
|
filename,
|
|
nullptr,
|
|
GENERIC_READ,
|
|
WICDecodeMetadataCacheOnLoad,
|
|
&wicBitmapDecoder);
|
|
}
|
|
|
|
CComPtr<IWICBitmapFrameDecode> wicBitmapFrame;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = wicBitmapDecoder->GetFrame(0, &wicBitmapFrame);
|
|
}
|
|
|
|
CComPtr<IWICFormatConverter> wicFormatConverter;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _wicFactory->CreateFormatConverter(&wicFormatConverter);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = wicFormatConverter->Initialize(
|
|
wicBitmapFrame,
|
|
GUID_WICPixelFormat32bppPBGRA,
|
|
WICBitmapDitherTypeNone,
|
|
nullptr,
|
|
0.0f,
|
|
WICBitmapPaletteTypeMedianCut);
|
|
}
|
|
|
|
CComPtr<IWICBitmap> wicBitmap;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _wicFactory->CreateBitmapFromSource(wicFormatConverter, WICBitmapCacheOnLoad, &wicBitmap);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _d2d1DeviceContext->CreateBitmapFromWicBitmap(wicBitmap, bitmap);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Slides the tiles in the direction of the button press. WAM is
|
|
// used to generate the animation curves while DirectComposition translates the visuals
|
|
// using those curves
|
|
//--------------------------------------------------------------------
|
|
HRESULT CApplication::Move(DIRECTION dir)
|
|
{
|
|
HRESULT hr = ((_device == nullptr) || (_hwnd == NULL)) ? E_UNEXPECTED : S_OK;
|
|
|
|
CComPtr<IDCompositionAnimation> spAnimation;
|
|
CComPtr<IDCompositionAnimation> slideAnimation;
|
|
CComPtr<IDCompositionTranslateTransform> translateTransform;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create the animation curves using WAM
|
|
hr = CreateSlideAnimation(dir, &slideAnimation);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _device->CreateTranslateTransform(&translateTransform);
|
|
}
|
|
|
|
//Set DirectComposition translation animation using the curves propagated by WAM
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = translateTransform->SetOffsetX(slideAnimation);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_visual->SetTransform(translateTransform);
|
|
}
|
|
|
|
// Committing all changes to DirectComposition visuals in order for them to take effect visually
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_device->Commit();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR pszCmdLine, _In_ int iCmdShow)
|
|
{
|
|
CApplication application(hInstance);
|
|
return application.Run();
|
|
} |