Анимация растрового изображения многоуровневого дочернего окна
Примечание
Для приложений на Windows 10 рекомендуется использовать API Windows.UI.Composition вместо DirectComposition. Дополнительные сведения см. в статье Модернизация классического приложения с помощью визуального слоя.
В этом разделе описывается, как создать и анимировать визуальный элемент, использующий растровое изображение многоуровневого дочернего окна в качестве содержимого визуального элемента. В примере, описанном в этом разделе, используется анимированное преобразование масштабирования для "увеличения" растрового изображения дочернего окна от размера большого пальца до полного размера. Дополнительные сведения о многоуровневых окнах см. в разделе Растровые изображения окон.
Это важно знать
Технологии
Предварительные требования
- C/C++
- Microsoft Win32
- Модель COM
Инструкции
Шаг 1. Создание многоуровневого дочернего окна
Выполните следующие действия, чтобы создать многоуровневое дочернее окно.
- Зарегистрируйте класс дочернего окна и создайте дочернее окно со стилем WS_EX_LAYERED . В следующем примере
m_dpiX
иm_dpiY
укажите разрешение экрана в пикселях на логический дюйм иm_hwndMain
является дескриптором main окна приложения.
HWND m_hwndLayeredChild;
HRESULT hr = S_OK;
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = 0;
wcex.lpfnWndProc = DemoApp::ChildWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"DCompLayeredChildWindow";
wcex.hIconSm = NULL;
if (!RegisterClassExW(&wcex))
{
return FALSE;
}
m_hwndLayeredChild = CreateWindowEx(WS_EX_LAYERED,
L"DCompLayeredChildWindow",
NULL,
WS_CHILD | WS_CLIPSIBLINGS,
0,
0,
static_cast<UINT>(ceil(640.0f * m_dpiX / 96.0f)),
static_cast<UINT>(ceil(480.0f * m_dpiY / 96.0f)),
m_hwndMain,
NULL,
HINST_THISCOMPONENT,
this);
- Вызовите функцию SetLayeredWindowAttributes , чтобы задать ключ цвета прозрачности и непрозрачность многоуровневого дочернего окна. Следующий код задает для ключа цвета прозрачности нулевое значение, а для непрозрачности — 255 (непрозрачный).
if (!SetLayeredWindowAttributes(m_hwndLayeredChild, 0, 255, LWA_ALPHA))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
- Отрисовка содержимого в дочернем окне.
Шаг 2. Инициализация объектов DirectComposition
Создайте объект устройства и целевой объект композиции. Дополнительные сведения см. в разделе Как инициализировать DirectComposition.
Шаг 3. Создание визуального объекта и установка растрового изображения многоуровневого дочернего окна в качестве свойства содержимого
Выполните следующие действия, чтобы создать визуальный элемент, задать его свойство содержимого для использования растрового изображения многоуровневого дочернего окна, а затем добавить визуальный элемент в визуальное дерево.
- Вызовите IDCompositionDevice::CreateVisual , чтобы создать визуальный объект.
- Создайте поверхность Microsoft DirectComposition для многоуровневого дочернего окна, передав дескриптор дочернего окна функции CreateSurfaceFromHwnd .
- Вызовите метод IDCompositionVisual::SetContent визуального объекта, чтобы задать новую поверхность в качестве визуального содержимого многоуровневого дочернего окна.
- Добавьте визуальный объект в визуальное дерево. Чтобы добавить визуальный элемент в корень дерева, вызовите метод IDCompositionTarget::SetRoot . Чтобы добавить визуальный элемент в качестве дочернего элемента другого визуального элемента, используйте метод IDCompositionVisual::AddVisual родительского визуального элемента.
В следующем примере создается визуальный объект, задается его свойство Content для использования растрового изображения многоуровневого дочернего окна и добавляется визуальный элемент в корень визуального дерева.
IDCompositionVisual *pVisual = nullptr;
IUnknown* pSurface = nullptr;
hr = m_pDevice->CreateVisual(&pVisual);
if (SUCCEEDED(hr))
{
hr = m_pDevice->CreateSurfaceFromHwnd(m_hwndLayeredChild, &pSurface);
}
if (SUCCEEDED(hr))
{
hr = pVisual->SetContent(pSurface);
}
if (SUCCEEDED(hr))
{
hr = m_pCompTarget->SetRoot(pVisual);
}
Шаг 4. Создание объекта анимации и объекта преобразования масштабирования
Используйте метод IDCompositionDevice::CreateAnimation для создания объекта анимации, а метод IDCompositionDevice::CreateScaleTransform — для создания объекта преобразования масштабирования.
IDCompositionAnimation *pAnimateScale = NULL;
IDCompositionScaleTransform *pScale = NULL;
if (SUCCEEDED(hr))
{
hr = m_pDevice->CreateAnimation(&pAnimateScale);
}
if (SUCCEEDED(hr))
{
// Create the scale transform object.
hr = m_pDevice->CreateScaleTransform(&pScale);
}
Шаг 5. Создание функции анимации
Используйте методы интерфейса IDCompositionAnimation объекта анимации для создания анимационной функции.
В следующем примере создается простая функция анимации, состоящая из одного кубиального полиномиального сегмента и одного конечного сегмента.
pAnimateScale->AddCubic(
0.0f, // offset from beginning of animation function, in seconds
0.0f, // constant coefficient
1.0f, // linear coefficient
0.0f, // quadratic coefficient
0.0f); // cubic coefficient
pAnimateScale->End(1.0f, 1.0f);
Шаг 6. Применение объекта анимации к свойствам объекта преобразования масштабирования
Используйте методы IDCompositionScale::SetScaleX и SetScaleY , чтобы применить объект анимации к свойствам ScaleX и ScaleY объекта преобразования масштабирования.
// Find the center of the child window.
RECT rcChild = { };
GetClientRect(m_hwndLayeredChild, &rcChild);
float centerX = rcChild.right / 2.0f;
float centerY = rcChild.bottom / 2.0f;
// Scale from the center point of the child window's bitmap.
pScale->SetCenterX(centerX);
pScale->SetCenterY(centerY);
// Use the same animation object to animate the X and Y scale
// factors.
pScale->SetScaleX(pAnimateScale);
pScale->SetScaleY(pAnimateScale);
Шаг 7. Применение объекта преобразования масштабирования к свойству transform визуального элемента
Используйте метод IDCompositionVisual::SetTransform , чтобы применить объект преобразования масштабирования к свойству Transform визуального элемента.
hr = pVisual->SetTransform(pScale);
Шаг 8. Маскировка многоуровневого дочернего окна
Перед фиксацией анимации используйте функцию DwmSetWindowAttribute с флагом DWMWA_CLOAK , чтобы "закрыть" многоуровневые дочерние окна. Маскировка удаляет многоуровневые дочерние окна из представления, а анимированная версия растрового представления окна отображается на экране.
BOOL fCloak = TRUE;
DwmSetWindowAttribute(pDemoApp->m_hwndLayeredChild,
DWMWA_CLOAK,
&fCloak,
sizeof(fCloak));
Шаг 9. Фиксация композиции
Используйте метод IDCompositionDevice::Commit для фиксации пакета команд в Microsoft DirectComposition для обработки. Анимация появится в целевом окне.
Шаг 10. Снимите многоуровневое дочернее окно
После завершения анимации используйте функцию DwmSetWindowAttribute с флагом DWMWA_CLOAK , чтобы снять многоуровневые дочерние окна.
Шаг 11. Освобождение объектов DirectComposition
Не забудьте освободить все объекты DirectComposition, если они больше не нужны. В следующем примере вызывается определенный приложением макрос SafeRelease для освобождения объектов DirectComposition.
SafeRelease(&pVisual);
SafeRelease(&pAnimateScale);
SafeRelease(&pScale);
SafeRelease(&m_pDevice);
SafeRelease(&m_pCompTarget);
Полный пример
//
// AnimateLayeredChildWindow.h
//
// 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
#pragma once
// Modify the following definitions if you need to target a platform prior to the ones specified below.
#ifndef WINVER // Allow use of features specific to Windows 7 or later.
#define WINVER 0x0700 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Allow use of features specific to Windows 7 or later.
#define _WIN32_WINNT 0x0700 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used items from Windows headers
// Windows Header Files:
#include <windows.h>
#include <wincodec.h>
// C RunTime Header Files
#include <math.h>
// DirectComposition Header File
#include <dcomp.h>
// Direct2D Header Files
#include <d2d1.h>
#include <d2d1helper.h>
// Desktop Window Manager (DWM) Header File
#include <dwmapi.h>
/******************************************************************
* *
* Macros *
* *
******************************************************************/
template<class Interface>
inline void
SafeRelease(
Interface **ppInterfaceToRelease
)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
#ifndef HINST_THISCOMPONENT
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
#endif
/******************************************************************
* *
* DemoApp *
* *
******************************************************************/
class DemoApp
{
public:
DemoApp();
~DemoApp();
HRESULT InitializeMainWindow();
HRESULT InitializeLayeredChildWindow();
void RunMessageLoop();
private:
HRESULT InitializeDirectCompositionObjects();
HRESULT CreateDeviceIndependentResources();
HRESULT CreateDeviceResources();
void DiscardDeviceResources();
HRESULT OnChildClick();
HRESULT OnChildRender();
HRESULT LoadResourceD2DBitmap(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR resourceName,
PCWSTR resourceType,
ID2D1Bitmap **ppBitmap
);
static LRESULT CALLBACK MainWndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
static LRESULT CALLBACK ChildWndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
private:
int m_dpiX;
int m_dpiY;
int m_childOffsetX;
int m_childOffsetY;
HWND m_hwndMain;
HWND m_hwndLayeredChild;
IDCompositionDevice *m_pDevice;
IDCompositionTarget *m_pCompTarget;
ID2D1HwndRenderTarget *m_pRenderTarget;
ID2D1Factory *m_pD2DFactory;
ID2D1Bitmap *m_pBitmap;
IWICImagingFactory *m_pWICFactory;
};
//
// AnimateLayeredChildWindow.cpp
//
// 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
// Instructions: Click the thumnail image to animate the transition
// of the child window from thumbsize to fullsize. Click the child
// window again to reset.
#include "AnimateLayeredChildWindow.h"
#define TIMER_ID 100
/******************************************************************
* *
* The application entry point. *
* *
******************************************************************/
int WINAPI WinMain(
HINSTANCE /* hInstance */,
HINSTANCE /* hPrevInstance */,
LPSTR /* lpCmdLine */,
int /* nCmdShow */
)
{
// Ignore the return value because we want to run the program even in the
// unlikely event that HeapSetInformation fails.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (SUCCEEDED(CoInitialize(NULL)))
{
{
DemoApp app;
if (SUCCEEDED(app.InitializeMainWindow()) && SUCCEEDED(app.InitializeLayeredChildWindow()))
{
app.RunMessageLoop();
}
}
CoUninitialize();
}
return 0;
}
/******************************************************************
* *
* DemoApp::DemoApp constructor *
* *
* Initialize member data. *
* *
******************************************************************/
DemoApp::DemoApp() :
m_dpiX(0),
m_dpiY(0),
m_childOffsetX(20),
m_childOffsetY(40),
m_hwndMain(NULL),
m_hwndLayeredChild(NULL),
m_pDevice(NULL),
m_pCompTarget(NULL),
m_pRenderTarget(NULL),
m_pBitmap(NULL),
m_pD2DFactory(NULL),
m_pWICFactory(NULL)
{
}
/******************************************************************
* *
* Release resources. *
* *
******************************************************************/
DemoApp::~DemoApp()
{
SafeRelease(&m_pDevice);
SafeRelease(&m_pCompTarget);
SafeRelease(&m_pRenderTarget);
SafeRelease(&m_pBitmap),
SafeRelease(&m_pD2DFactory);
SafeRelease(&m_pWICFactory);
}
/*******************************************************************
* *
* Create the application window. *
* *
*******************************************************************/
HRESULT DemoApp::InitializeMainWindow()
{
HRESULT hr = S_OK;
// Initialize device-independent resources, such
// as the Direct2D factory.
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
// Register the main window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = 0;
wcex.lpfnWndProc = DemoApp::MainWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = L"DirectCompDemoApp";
RegisterClassEx(&wcex);
RECT rect = { };
SetRect(&rect, 0, 0, 640, 480);
AdjustWindowRect(&rect, WS_OVERLAPPED | WS_SYSMENU, FALSE);
// Create the main application window.
//
// Because the CreateWindow function takes its size in pixels, we
// obtain the system DPI and use it to scale the window size.
HDC hdc = GetDC(NULL);
if (hdc)
{
m_dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
m_dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
float width = static_cast<float>(rect.right - rect.left);
float height = static_cast<float>(rect.bottom - rect.top);
m_hwndMain = CreateWindow(
L"DirectCompDemoApp",
L"DirectComposition Demo Application",
WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(width * m_dpiX / 96.f)),
static_cast<UINT>(ceil(height * m_dpiY / 96.f)),
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
}
hr = m_hwndMain ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Create and initialize the DirectCompositoin objects.
hr = InitializeDirectCompositionObjects();
}
if (SUCCEEDED(hr))
{
ShowWindow(m_hwndMain, SW_SHOWNORMAL);
UpdateWindow(m_hwndMain);
}
return hr;
}
/*******************************************************************
* *
* Create the layered child window. *
* *
/******************************************************************/
HRESULT DemoApp::InitializeLayeredChildWindow()
{
int thumbWidth = 48;
int thumbHeight = 32;
HRESULT hr = S_OK;
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = 0;
wcex.lpfnWndProc = DemoApp::ChildWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"DCompLayeredChildWindow";
wcex.hIconSm = NULL;
if (!RegisterClassExW(&wcex))
{
return FALSE;
}
m_hwndLayeredChild = CreateWindowEx(WS_EX_LAYERED,
L"DCompLayeredChildWindow",
NULL,
WS_CHILD | WS_CLIPSIBLINGS,
0,
0,
static_cast<UINT>(ceil(640.0f * m_dpiX / 96.0f)),
static_cast<UINT>(ceil(480.0f * m_dpiY / 96.0f)),
m_hwndMain,
NULL,
HINST_THISCOMPONENT,
this);
hr = m_hwndLayeredChild ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Set the opacity and transparency color key of the layered
// child window.
if (!SetLayeredWindowAttributes(m_hwndLayeredChild, 0, 255, LWA_ALPHA))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
if (SUCCEEDED(hr))
{
// While the child window is fullsize, load the bitmap resource and
// render it to the child window.
hr = CreateDeviceResources();
}
if (SUCCEEDED(hr))
{
// Reduce the window to thumbsize.
MoveWindow(m_hwndLayeredChild, m_childOffsetX, m_childOffsetY,
thumbWidth, thumbHeight, TRUE);
ShowWindow(m_hwndLayeredChild, SW_SHOWNORMAL);
UpdateWindow(m_hwndLayeredChild);
}
return hr;
}
/******************************************************************
* *
* This method creates the DirectComposition device object and *
* and the composition target object. These objects endure for *
* the lifetime of the application. *
* *
******************************************************************/
HRESULT DemoApp::InitializeDirectCompositionObjects()
{
HRESULT hr = S_OK;
// Create a DirectComposition device object.
hr = DCompositionCreateDevice(nullptr, __uuidof(IDCompositionDevice),
reinterpret_cast<void**>(&m_pDevice));
if (SUCCEEDED(hr))
{
// Create the composition target object.
hr = m_pDevice->CreateTargetForHwnd(m_hwndMain, TRUE, &m_pCompTarget);
}
return hr;
}
/******************************************************************
* *
* This method is used to create resources which are not bound *
* to any device. Their lifetime effectively extends for the *
* duration of the app. *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceIndependentResources()
{
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pWICFactory)
);
if (SUCCEEDED(hr))
{
// Create a Direct2D factory.
hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&m_pD2DFactory
);
}
return hr;
}
/******************************************************************
* *
* This method creates the D2D bitmap that the application gives *
* to DirectComposition to be composed. *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
RECT rc;
GetClientRect(m_hwndLayeredChild, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
// Create a Direct2D render target.
hr = m_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwndLayeredChild, size),
&m_pRenderTarget
);
if (SUCCEEDED(hr))
{
hr = LoadResourceD2DBitmap(
m_pRenderTarget,
m_pWICFactory,
L"WhiteFlowers",
L"Image",
&m_pBitmap
);
}
return hr;
}
/******************************************************************
* *
* Discard device-specific resources. *
* *
******************************************************************/
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pRenderTarget);
SafeRelease(&m_pBitmap);
}
/******************************************************************
* *
* The main window's message loop. *
* *
******************************************************************/
void DemoApp::RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/******************************************************************
* *
* The main window's message handler. *
* *
******************************************************************/
LRESULT CALLBACK DemoApp::MainWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(pDemoApp)
);
result = 1;
}
else
{
DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_DISPLAYCHANGE:
{
InvalidateRect(hwnd, NULL, FALSE);
}
wasHandled = true;
result = 0;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
pDemoApp->DiscardDeviceResources();
}
wasHandled = true;
result = 1;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
/******************************************************************
* *
* The layered child window's message handler. *
* *
******************************************************************/
LRESULT CALLBACK DemoApp::ChildWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(pDemoApp)
);
result = 1;
}
else
{
DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_PAINT:
{
pDemoApp->OnChildRender();
ValidateRect(hwnd, NULL);
}
wasHandled = true;
result = 0;
break;
case WM_LBUTTONDOWN:
{
HRESULT hr = S_OK;
static BOOL fThumbsize = TRUE;
// If the child window is already fullsize, reset
// the visual tree and return the child window
// to thumbsize and move it to its initial location.
if (!fThumbsize)
{
pDemoApp->m_pCompTarget->SetRoot(NULL);
hr = pDemoApp->m_pDevice->Commit();
MoveWindow(pDemoApp->m_hwndLayeredChild,
pDemoApp->m_childOffsetX,
pDemoApp->m_childOffsetY, 48, 32, TRUE);
pDemoApp->OnChildRender();
}
else
{
// Cloak the child window.
BOOL fCloak = TRUE;
DwmSetWindowAttribute(pDemoApp->m_hwndLayeredChild,
DWMWA_CLOAK,
&fCloak,
sizeof(fCloak));
pDemoApp->OnChildClick();
}
fThumbsize = !fThumbsize;
}
wasHandled = true;
result = 0;
break;
case WM_TIMER:
{
// Uncloak the child window.
BOOL fCloak = FALSE;
DwmSetWindowAttribute(pDemoApp->m_hwndLayeredChild,
DWMWA_CLOAK,
&fCloak,
sizeof(fCloak));
KillTimer(pDemoApp->m_hwndLayeredChild, TIMER_ID);
}
wasHandled = true;
result = 0;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
/******************************************************************
* *
* Draws a bitmap in the layered child window. *
* *
******************************************************************/
HRESULT DemoApp::OnChildRender()
{
HRESULT hr = S_OK;
// Retrieve the size of the render target.
D2D1_SIZE_F size = m_pRenderTarget->GetSize();
m_pRenderTarget->BeginDraw();
// Draw a bitmap scaled to fill the window.
m_pRenderTarget->DrawBitmap(
m_pBitmap,
D2D1::RectF(0.0f, 0.0f, size.width, size.height)
);
hr = m_pRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
/******************************************************************
* *
* Handles a mouse click in the layered child window by animating *
* the transition from a thumbsize window to a fullsize window. *
* *
******************************************************************/
HRESULT DemoApp::OnChildClick()
{
int fullWidth = 640;
int fullHeight = 480;
HRESULT hr = S_OK;
IDCompositionVisual *pVisual = nullptr;
IUnknown* pSurface = nullptr;
hr = m_pDevice->CreateVisual(&pVisual);
if (SUCCEEDED(hr))
{
hr = m_pDevice->CreateSurfaceFromHwnd(m_hwndLayeredChild, &pSurface);
}
if (SUCCEEDED(hr))
{
hr = pVisual->SetContent(pSurface);
}
if (SUCCEEDED(hr))
{
hr = m_pCompTarget->SetRoot(pVisual);
}
if (SUCCEEDED(hr))
{
// Position the visual at the same location as the
// the child window.
hr = pVisual->SetOffsetX(static_cast<float>(m_childOffsetX));
if (SUCCEEDED(hr))
{
hr = pVisual->SetOffsetY(static_cast<float>(m_childOffsetY));
}
}
IDCompositionAnimation *pAnimateX = NULL;
IDCompositionAnimation *pAnimateY = NULL;
if (SUCCEEDED(hr))
{
// Create the animation objects.
hr = m_pDevice->CreateAnimation(&pAnimateX);
if (SUCCEEDED(hr))
{
hr = m_pDevice->CreateAnimation(&pAnimateY);
}
}
IDCompositionAnimation *pAnimateScale = NULL;
IDCompositionScaleTransform *pScale = NULL;
if (SUCCEEDED(hr))
{
hr = m_pDevice->CreateAnimation(&pAnimateScale);
}
if (SUCCEEDED(hr))
{
// Create the scale transform object.
hr = m_pDevice->CreateScaleTransform(&pScale);
}
if (SUCCEEDED(hr))
{
// Calculate the X and Y offsets that will position the child window
// in the center of the main window's client area.
RECT rcParent = { };
RECT rcChild = { };
GetClientRect(m_hwndMain, &rcParent);
GetClientRect(m_hwndLayeredChild, &rcChild);
float endValX = rcParent.right / 2.0f - rcChild.right / 2.0f;
float endValY = rcParent.bottom / 2.0f - rcChild.bottom / 2.0f;
// Build the animation functions that will move the visual to the
// center of the main window's client area.
pAnimateX->AddCubic(0.0f, static_cast<float>(m_childOffsetX),
endValX - m_childOffsetX, 0.0f, 0.0f);
pAnimateX->End(0.9f, endValX);
pAnimateY->AddCubic(0.0f, static_cast<float>(m_childOffsetY),
endValY - m_childOffsetY, 0.0f, 0.0f);
pAnimateY->End(0.9f, endValY);
// Associate the animation objects with the offset properties of
// the visual.
hr = pVisual->SetOffsetX(pAnimateX);
if (SUCCEEDED(hr))
{
hr = pVisual->SetOffsetY(pAnimateY);
}
}
if (SUCCEEDED(hr))
{
// Commit the visual tree.
hr = m_pDevice->Commit();
// Give the animation a chance to run.
Sleep(900);
}
if (SUCCEEDED(hr))
{
// Align the visual with the upper-left corner of the
// parent window's client area.
pVisual->SetOffsetX(0.0);
pVisual->SetOffsetY(0.0);
// Enlarge the child window to fill the main window.
if (!MoveWindow(m_hwndLayeredChild, 0, 0, fullWidth, fullHeight, TRUE))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
if (SUCCEEDED(hr))
{
// Add animation primitives that define the animation object function
// used to scale up the child window's bitmap.
pAnimateScale->AddCubic(
0.0f, // offset from beginning of animation function, in seconds
0.0f, // constant coefficient
1.0f, // linear coefficient
0.0f, // quadratic coefficient
0.0f); // cubic coefficient
pAnimateScale->End(1.0f, 1.0f);
// Find the center of the child window.
RECT rcChild = { };
GetClientRect(m_hwndLayeredChild, &rcChild);
float centerX = rcChild.right / 2.0f;
float centerY = rcChild.bottom / 2.0f;
// Scale from the center point of the child window's bitmap.
pScale->SetCenterX(centerX);
pScale->SetCenterY(centerY);
// Use the same animation object to animate the X and Y scale
// factors.
pScale->SetScaleX(pAnimateScale);
pScale->SetScaleY(pAnimateScale);
// Set the Transform property of the visual to use the scale
// transform.
hr = pVisual->SetTransform(pScale);
}
if (SUCCEEDED(hr))
{ // Commit the visual tree.
hr = m_pDevice->Commit();
// Use a WM_TIMER message in the child window procedure
// to uncloak the child window.
SetTimer(m_hwndLayeredChild, TIMER_ID, 1000, NULL);
}
SafeRelease(&pAnimateX);
SafeRelease(&pAnimateY);
SafeRelease(&pVisual);
SafeRelease(&pAnimateScale);
SafeRelease(&pScale);
return hr;
}
/******************************************************************
* *
* This method will create a Direct2D bitmap from an application *
* resource. *
* *
******************************************************************/
HRESULT DemoApp::LoadResourceD2DBitmap(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR resourceName,
PCWSTR resourceType,
ID2D1Bitmap **ppBitmap
)
{
HRESULT hr = S_OK;
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
HRSRC imageResHandle = NULL;
HGLOBAL imageResDataHandle = NULL;
void *pImageFile = NULL;
DWORD imageFileSize = 0;
// Locate the resource.
imageResHandle = FindResourceW(HINST_THISCOMPONENT, resourceName, resourceType);
hr = imageResHandle ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Load the resource.
imageResDataHandle = LoadResource(HINST_THISCOMPONENT, imageResHandle);
hr = imageResDataHandle ? S_OK : E_FAIL;
}
if (SUCCEEDED(hr))
{
// Lock it to get a system memory pointer.
pImageFile = LockResource(imageResDataHandle);
hr = pImageFile ? S_OK : E_FAIL;
}
if (SUCCEEDED(hr))
{
// Calculate the size.
imageFileSize = SizeofResource(HINST_THISCOMPONENT, imageResHandle);
hr = imageFileSize ? S_OK : E_FAIL;
}
if (SUCCEEDED(hr))
{
// Create a WIC stream to map onto the memory.
hr = pIWICFactory->CreateStream(&pStream);
}
if (SUCCEEDED(hr))
{
// Initialize the stream with the memory pointer and size.
hr = pStream->InitializeFromMemory(
reinterpret_cast<BYTE*>(pImageFile),
imageFileSize
);
}
if (SUCCEEDED(hr))
{
// Create a decoder for the stream.
hr = pIWICFactory->CreateDecoderFromStream(
pStream,
NULL,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);
}
if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pIWICFactory->CreateFormatConverter(&pConverter);
}
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
if (SUCCEEDED(hr))
{
// Create a Direct2D bitmap from the WIC bitmap.
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
NULL,
ppBitmap
);
}
SafeRelease(&pDecoder);
SafeRelease(&pSource);
SafeRelease(&pStream);
SafeRelease(&pConverter);
return hr;
}
Связанные темы