如何建置簡單的視覺化樹狀結構
注意
針對Windows 10上的應用程式,我們建議使用 Windows.UI.Composition API,而不是 DirectComposition。 如需詳細資訊,請參閱 使用視覺層將傳統型應用程式現代化。
本主題示範如何建置簡單的 Microsoft DirectComposition 視覺化樹狀結構。 本主題中的範例會建置並組成包含根視覺效果和三個子視覺效果的視覺化樹狀結構。 根視覺效果的內容是淺藍色點陣圖,可作為子視覺效果的背景。 此圖顯示本主題中範例程式碼所建立的組合。
您所需了解的事情
技術
必要條件
瞭解:
- C/C++
- Microsoft Win32
- 元件物件模型 (COM)
指示
步驟 1:初始化裝置和組合目標物件
如需詳細資訊,請參閱 如何初始化 DirectComposition。
步驟 2:建立視覺物件並設定點陣圖內容
使用 IDCompositionDevice::CreateVisual 方法來建立視覺效果,以及 IDCompositionVisual::SetContent 方法可設定視覺效果的點陣圖內容。
注意
在下列範例中,陣列的第一個專案 m_hBitmaps
包含根視覺效果的點陣圖,而其餘元素則包含子視覺效果的點陣圖。
#define NUM_VISUALS 4 // number of visuals in the composition
HBITMAP m_hBitmaps[NUM_VISUALS];
HRESULT hr = S_OK;
IDCompositionVisual *pVisuals[NUM_VISUALS]{ };
IDCompositionSurface *pSurface{ nullptr };
// Create a visual objects and set their content.
for (int i = 0; i < NUM_VISUALS; i++)
{
hr = m_pDevice->CreateVisual(&pVisuals[i]);
if (SUCCEEDED(hr))
{
// This application-defined function creates a DirectComposition
// surface and renders a GDI bitmap onto the surface.
hr = MyCreateGDIRenderedDCompSurface(m_hBitmaps[i], &pSurface);
if (SUCCEEDED(hr))
{
// Set the bitmap content.
hr = pVisuals[i]->SetContent(pSurface);
}
SafeRelease(&pSurface);
}
if (FAILED(hr))
{
goto Cleanup;
}
}
下列應用程式定義函數示範如何建立 Microsoft DirectComposition 介面,並將 Windows 圖形裝置介面 (GDI) 點陣圖呈現至介面。
// MyCreateGDIRenderedDCompSurface - Creates a DirectComposition surface and
// copies the bitmap to the surface.
//
// Parameters:
// hBitmap - a GDI bitmap.
// ppSurface - the composition surface object.
//
HRESULT DemoApp::MyCreateGDIRenderedDCompSurface(HBITMAP hBitmap, IDCompositionSurface **ppSurface)
{
HRESULT hr = S_OK;
int bitmapWidth = 0;
int bitmapHeight = 0;
int bmpSize = 0;
BITMAP bmp = { };
HBITMAP hBitmapOld = NULL;
HDC hSurfaceDC = NULL;
HDC hBitmapDC = NULL;
IDXGISurface1 *pDXGISurface = nullptr;
POINT pointOffset = { };
hr = hBitmap ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Get information about the bitmap.
bmpSize = GetObject(hBitmap, sizeof(BITMAP), &bmp);
}
hr = bmpSize ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Save the bitmap dimensions.
bitmapWidth = bmp.bmWidth;
bitmapHeight = bmp.bmHeight;
// Create a DirectComposition-compatible surface that is the same size
// as the bitmap.
hr = m_pDevice->CreateSurface(bitmapWidth, bitmapHeight,
DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_IGNORE, ppSurface);
}
hr = ppSurface ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
hr = (*ppSurface)->BeginDraw(NULL, __uuidof(IDXGISurface1),
reinterpret_cast<void**>(&pDXGISurface), &pointOffset);
}
if (SUCCEEDED(hr))
{
pDXGISurface->GetDC(FALSE, &hSurfaceDC);
}
hr = hSurfaceDC ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
hBitmapDC = CreateCompatibleDC(hSurfaceDC);
if (hBitmapDC != NULL)
{
hBitmapOld = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
BitBlt(hSurfaceDC, pointOffset.x, pointOffset.y,
bitmapWidth, bitmapHeight, hBitmapDC, 0, 0, SRCCOPY);
if (hBitmapOld)
{
SelectObject(hBitmapDC, hBitmapOld);
}
DeleteDC(hBitmapDC);
}
pDXGISurface->ReleaseDC(NULL);
}
(*ppSurface)->EndDraw();
SafeRelease(&pDXGISurface);
return hr;
}
步驟 3:設定根視覺效果
呼叫 IDCompositionTarget::SetRoot 方法,設定根視覺效果的水準和垂直位移,然後將它新增至視覺化樹狀結構。
float xPosRoot = 50.0;
float yPosRoot = 50.0;
// Set the horizontal and vertical position of the root visual.
pVisuals[0]->SetOffsetX(xPosRoot);
pVisuals[0]->SetOffsetY(yPosRoot);
// Set the root visual of the visual tree.
hr = m_pCompTarget->SetRoot(pVisuals[0]);
步驟 4:新增子視覺效果,並認可組合
使用每個子視覺效果的 IDCompositionVisual 介面公開的方法來設定點陣圖內容和其他屬性,然後使用根視覺效果的 IDCompositionVisual::AddVisual 方法,將子視覺效果新增至視覺化樹狀結構的根目錄。 呼叫 IDCompositionDevice::Commit ,將命令批次認可至 DirectComposition 進行處理。 產生的組合會出現在目標視窗中。
float xPosChild = 20.0f;
float yPosChild = 20.0f;
if (SUCCEEDED(hr))
{
// Set the positions of the child visuals and add them to the visual tree.
for (int i = 1; i < NUM_VISUALS; i++)
{
pVisuals[i]->SetOffsetX(xPosChild);
pVisuals[i]->SetOffsetY(
static_cast<float>((yPosChild * i) + (CHILD_BITMAP_HEIGHT * (i - 1))));
// Add the child visuals as children of the root visual.
pVisuals[0]->AddVisual(pVisuals[i], TRUE, nullptr);
}
}
// Commit the visual to be composed and displayed.
hr = m_pDevice->Commit();
步驟 5:釋放 DirectComposition 物件
一旦您不再需要視覺效果物件,即可釋出視覺效果物件。 下一位程式碼會呼叫應用程式定義的 SafeRelease 宏,以釋放視覺物件。
Cleanup:
// Free the visuals.
for (int i = 0; i < NUM_VISUALS; i++)
{
SafeRelease(&pVisuals[i]);
}
此外,請記得在應用程式結束之前釋放裝置和組合目標物件。
SafeRelease(&m_pD3D11Device);
SafeRelease(&m_pDevice);
SafeRelease(&m_pCompTarget);
完整範例
// 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>
// C RunTime Header Files
#include <math.h>
#include <dcomp.h>
#include <d3d11.h>
/******************************************************************
* *
* Macros *
* *
******************************************************************/
template<class Interface>
inline void
SafeRelease(
Interface **ppInterfaceToRelease
)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
#ifndef Assert
#if defined( DEBUG ) || defined( _DEBUG )
#define Assert(b) if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}
#else
#define Assert(b)
#endif //DEBUG || _DEBUG
#endif
#ifndef HINST_THISCOMPONENT
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
#endif
// Application-defined constants
#define NUM_VISUALS 4 // number of visuals in the composition
#define CHILD_BITMAP_HEIGHT 96 // height, in pixels, of a child visual
#define NUM_BITMAPS 4 // number of bitmaps in the composition
/******************************************************************
* *
* DemoApp *
* *
******************************************************************/
class DemoApp
{
public:
DemoApp();
~DemoApp();
HRESULT Initialize();
void RunMessageLoop();
private:
HRESULT InitializeDirectCompositionDevice();
HRESULT CreateResources();
void DiscardResources();
HRESULT OnClientClick();
HRESULT DemoApp::LoadResourceGDIBitmap(
PCWSTR resourceName,
HBITMAP &hbmp
);
HRESULT DemoApp::MyCreateGDIRenderedDCompSurface(HBITMAP hBitmap, IDCompositionSurface **ppSurface);
static LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
private:
HWND m_hwnd;
HBITMAP m_hBitmaps[NUM_VISUALS];
ID3D11Device *m_pD3D11Device;
IDCompositionDevice *m_pDevice;
IDCompositionTarget *m_pCompTarget;
};
// 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: Right-click in the client area to cause DirectCompostion
// to create a simple composition consisting a root visual and three child
// visuals. The root visual provides the background bitmap for the
// composition, and the three child visuals provide bitmaps that are
// composed on top of the root. All bitmaps are GDI.
#include "SimpleVisualTree.h"
/******************************************************************
* *
* 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.Initialize()))
{
app.RunMessageLoop();
}
}
CoUninitialize();
}
return 0;
}
/******************************************************************
* *
* DemoApp::DemoApp constructor *
* *
* Initialize member data. *
* *
******************************************************************/
DemoApp::DemoApp() :
m_hwnd(NULL),
m_hBitmaps(),
m_pD3D11Device(nullptr),
m_pDevice(nullptr),
m_pCompTarget(nullptr)
{
}
/******************************************************************
* *
* Release resources. *
* *
******************************************************************/
DemoApp::~DemoApp()
{
SafeRelease(&m_pD3D11Device);
SafeRelease(&m_pDevice);
SafeRelease(&m_pCompTarget);
}
/*******************************************************************
* *
* Create the application window. *
* *
*******************************************************************/
HRESULT DemoApp::Initialize()
{
HRESULT hr;
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc;
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);
// Create the application window.
//
// Because the CreateWindow function takes its size in pixels, we
// obtain the system DPI and use it to scale the window size.
int dpiX = 0;
int dpiY = 0;
HDC hdc = GetDC(NULL);
if (hdc)
{
dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
m_hwnd = CreateWindow(
L"DirectCompDemoApp",
L"DirectComposition Demo Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
UpdateWindow(m_hwnd);
// Initialize DirectComposition resources, such as the
// device object and composition target object.
hr = InitializeDirectCompositionDevice();
}
if (SUCCEEDED(hr))
{
hr = CreateResources();
}
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::InitializeDirectCompositionDevice()
{
HRESULT hr = S_OK;
D3D_FEATURE_LEVEL featureLevelSupported;
IDXGIDevice *pDXGIDevice = nullptr;
// Create the D3D device object.
D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL,
0,
D3D11_SDK_VERSION,
&m_pD3D11Device,
&featureLevelSupported,
NULL);
hr = (m_pD3D11Device == nullptr) ? E_UNEXPECTED : S_OK;
if (SUCCEEDED(hr))
{
// Create the DXGI device used to create bitmap surfaces.
hr = m_pD3D11Device->QueryInterface(&pDXGIDevice);
}
if (SUCCEEDED(hr))
{
// Create the DirectComposition device object.
hr = DCompositionCreateDevice(pDXGIDevice, __uuidof(IDCompositionDevice),
reinterpret_cast<void **>(&m_pDevice));
}
if (SUCCEEDED(hr))
{
// Create the composition target object.
hr = m_pDevice->CreateTargetForHwnd(m_hwnd, TRUE, &m_pCompTarget);
}
SafeRelease(&pDXGIDevice);
return hr;
}
/******************************************************************
* *
* This method creates the GDI bitmaps to be associated with *
* DirectComposition visual objects. *
* *
******************************************************************/
HRESULT DemoApp::CreateResources()
{
HRESULT hr = S_OK;
hr = LoadResourceGDIBitmap(L"Background", m_hBitmaps[0]);
hr = LoadResourceGDIBitmap(L"BlkKnight", m_hBitmaps[1]);
hr = LoadResourceGDIBitmap(L"BlkQueen", m_hBitmaps[2]);
hr = LoadResourceGDIBitmap(L"BlkRook", m_hBitmaps[3]);
return hr;
}
/******************************************************************
* *
* Discard device-specific resources. *
* *
******************************************************************/
void DemoApp::DiscardResources()
{
int i = 0;
while (i < NUM_VISUALS)
{
DeleteObject(m_hBitmaps[i++]);
}
}
/******************************************************************
* *
* The main window's message loop. *
* *
******************************************************************/
void DemoApp::RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/******************************************************************
* *
* Called whenever the user clicks the client area of the main *
* window. This method builds a simple visual tree consisting of *
* a root visual and three child visuals. *
******************************************************************/
HRESULT DemoApp::OnClientClick()
{
HRESULT hr = S_OK;
IDCompositionVisual *pVisuals[NUM_VISUALS] = { };
IDCompositionSurface *pSurface = nullptr;
// Create a visual objects and set their content.
for (int i = 0; i < NUM_VISUALS; i++)
{
hr = m_pDevice->CreateVisual(&pVisuals[i]);
if (SUCCEEDED(hr))
{
// This application-defined function creates a DirectComposition
// surface and renders a GDI bitmap onto the surface.
hr = MyCreateGDIRenderedDCompSurface(m_hBitmaps[i], &pSurface);
if (SUCCEEDED(hr))
{
// Set the bitmap content.
hr = pVisuals[i]->SetContent(pSurface);
}
SafeRelease(&pSurface);
}
if (FAILED(hr))
{
goto Cleanup;
}
}
float xPosRoot = 50.0;
float yPosRoot = 50.0;
// Set the horizontal and vertical position of the root visual.
pVisuals[0]->SetOffsetX(xPosRoot);
pVisuals[0]->SetOffsetY(yPosRoot);
// Set the root visual of the visual tree.
hr = m_pCompTarget->SetRoot(pVisuals[0]);
float xPosChild = 20.0f;
float yPosChild = 20.0f;
if (SUCCEEDED(hr))
{
// Set the positions of the child visuals and add them to the visual tree.
for (int i = 1; i < NUM_VISUALS; i++)
{
pVisuals[i]->SetOffsetX(xPosChild);
pVisuals[i]->SetOffsetY(
static_cast<float>((yPosChild * i) + (CHILD_BITMAP_HEIGHT * (i - 1))));
// Add the child visuals as children of the root visual.
pVisuals[0]->AddVisual(pVisuals[i], TRUE, nullptr);
}
}
// Commit the visual to be composed and displayed.
hr = m_pDevice->Commit();
Cleanup:
// Free the visuals.
for (int i = 0; i < NUM_VISUALS; i++)
{
SafeRelease(&pVisuals[i]);
}
return hr;
}
/******************************************************************
* *
* The window's message handler. *
* *
******************************************************************/
LRESULT CALLBACK DemoApp::WndProc(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_LBUTTONDOWN:
{
pDemoApp->OnClientClick();
}
case WM_DISPLAYCHANGE:
{
InvalidateRect(hwnd, NULL, FALSE);
}
wasHandled = true;
result = 0;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
pDemoApp->DiscardResources();
}
wasHandled = true;
result = 1;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
/******************************************************************
* *
* This method loads the specified GDI bitmap from the *
* application resources, creates a new bitmap that is in a *
* format that DirectComposition can use, and copies the contents *
* of the original bitmap to the new bitmap. *
* *
******************************************************************/
HRESULT DemoApp::LoadResourceGDIBitmap(PCWSTR resourceName, HBITMAP &hbmp)
{
// Load the bitmap from the application resources.
hbmp = static_cast<HBITMAP>(LoadImageW(HINST_THISCOMPONENT, resourceName,
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR));
return hbmp ? S_OK : E_FAIL;
}
// MyCreateGDIRenderedDCompSurface - Creates a DirectComposition surface and
// copies the bitmap to the surface.
//
// Parameters:
// hBitmap - a GDI bitmap.
// ppSurface - the composition surface object.
//
HRESULT DemoApp::MyCreateGDIRenderedDCompSurface(HBITMAP hBitmap, IDCompositionSurface **ppSurface)
{
HRESULT hr = S_OK;
int bitmapWidth = 0;
int bitmapHeight = 0;
int bmpSize = 0;
BITMAP bmp = { };
HBITMAP hBitmapOld = NULL;
HDC hSurfaceDC = NULL;
HDC hBitmapDC = NULL;
IDXGISurface1 *pDXGISurface = nullptr;
POINT pointOffset = { };
hr = hBitmap ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Get information about the bitmap.
bmpSize = GetObject(hBitmap, sizeof(BITMAP), &bmp);
}
hr = bmpSize ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Save the bitmap dimensions.
bitmapWidth = bmp.bmWidth;
bitmapHeight = bmp.bmHeight;
// Create a DirectComposition-compatible surface that is the same size
// as the bitmap.
hr = m_pDevice->CreateSurface(bitmapWidth, bitmapHeight,
DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_IGNORE, ppSurface);
}
hr = ppSurface ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
hr = (*ppSurface)->BeginDraw(NULL, __uuidof(IDXGISurface1),
reinterpret_cast<void**>(&pDXGISurface), &pointOffset);
}
if (SUCCEEDED(hr))
{
pDXGISurface->GetDC(FALSE, &hSurfaceDC);
}
hr = hSurfaceDC ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
hBitmapDC = CreateCompatibleDC(hSurfaceDC);
if (hBitmapDC != NULL)
{
hBitmapOld = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
BitBlt(hSurfaceDC, pointOffset.x, pointOffset.y,
bitmapWidth, bitmapHeight, hBitmapDC, 0, 0, SRCCOPY);
if (hBitmapOld)
{
SelectObject(hBitmapDC, hBitmapOld);
}
DeleteDC(hBitmapDC);
}
pDXGISurface->ReleaseDC(NULL);
}
(*ppSurface)->EndDraw();
SafeRelease(&pDXGISurface);
return hr;
}