Win2D 實作為 Direct2D 之上的一層,並支援雙向互通。 如果你有 Win2D 物件,可以存取用來實作它的原生 Direct2D 物件。 如果你有一個 Direct2D 物件,可以查找包裝該物件的 Win2D 物件;如果原本不存在,則可以建立新的包裝物件。
Interop 允許你將 Win2D 與原生 DirectX API 混搭使用。 你可以寫一個主要使用 Win2D 的應用程式,但隨時切換到原生 DirectX——可能是為了呼叫其他需要原生介面的 API 或第三方元件。 或者你的應用程式大部分是原生 DirectX,但在特定需要額外便利性或 C# 支援的地方,可以切換到 Win2D。
互通性 API
C++/CX 互操作 API 定義於標頭 Microsoft。Graphics.Canvas.native.h:
#include <Microsoft.Graphics.Canvas.native.h>
using namespace Microsoft::Graphics::Canvas;
若要取得由 Win2D 物件封裝的原生 Direct2D 物件:
template<typename T, typename U>
Microsoft::WRL::ComPtr<T> GetWrappedResource(U^ wrapper);
template<typename T, typename U>
Microsoft::WRL::ComPtr<T> GetWrappedResource(CanvasDevice^ device, U^ wrapper);
template<typename T, typename U>
Microsoft::WRL::ComPtr<T> GetWrappedResource(CanvasDevice^ device, U^ wrapper, float dpi);
大多數類型 GetWrappedResource 可以只用 Win2D 包裝物件作為參數來呼叫。 對於某些類型(見下表),還必須傳遞裝置和/或 DPI 值。 將 GetWrappedResource 與不需要裝置或 DPI 的類型一起使用時,傳遞裝置或 DPI 並不是錯誤。
要讓 Win2D 物件包裝原生 Direct2D 物件:
template<typename WRAPPER>
WRAPPER^ GetOrCreate(IUnknown* resource);
template<typename WRAPPER>
WRAPPER^ GetOrCreate(CanvasDevice^ device, IUnknown* resource);
template<typename WRAPPER>
WRAPPER^ GetOrCreate(ID2D1Device1* device, IUnknown* resource);
template<typename WRAPPER>
WRAPPER^ GetOrCreate(CanvasDevice^ device, IUnknown* resource, float dpi);
template<typename WRAPPER>
WRAPPER^ GetOrCreate(ID2D1Device1* device, IUnknown* resource, float dpi);
GetOrCreate 如果已有包裝器實例,則回傳;若沒有,則回傳新的包裝器。 只要該包裝物件實例仍然存在,對同一個原生物件反覆呼叫此方法,每次都會回傳相同的包裝物件。 如果所有對包裝器的引用都被釋放,導致其引用計數降為零並遭到銷毀,之後任何對 GetOrCreate 的呼叫都必須建立新的包裝器。
某些類型 GetOrCreate 只需以 Direct2D 資源物件作為參數呼叫,而其他類型(見下表)則必須傳遞裝置與 DPI 值。 將 GetOrCreate 與不需要裝置或 DPI 的類型一起使用時,傳遞裝置或 DPI 並不是錯誤。 如果已經有 Win2D 包裝器,即使是通常需要的類型,也可以省略裝置和 DPI:這些參數只在建立新包裝實例時使用。
GetOrCreate 理解繼承階層,並且一律建立適當的最衍生包裝型別。 例如,如果你以帶有 GetOrCreate<CanvasBitmap>(ID2D1Bitmap1*) 旗標的 ID2D1Bitmap1 呼叫 D2D1_BITMAP_OPTIONS_TARGET,則回傳的包裝器執行個體實際上會是 CanvasRenderTarget(其衍生自 CanvasBitmap)。 反過來說,如果你以不具有 GetOrCreate<CanvasRenderTarget>(ID2D1Bitmap1*) 的 ID2D1Bitmap1 呼叫 D2D1_BITMAP_OPTIONS_TARGET,就會擲回無效轉型例外。
極端一點來說,稱其為 GetOrCreate<Object>(IUnknown*),或甚至稱其為 GetWrappedResource<IUnknown>(Object^),都是成立的。
支援互通操作的類型
| Win2D 類型 | Direct2D 類型 |
GetOrCreate 參數 |
GetWrappedResource 參數 |
|---|---|---|---|
CanvasBitmap |
ID2D1Bitmap1 不含 D2D1_BITMAP_OPTIONS_TARGET |
Device | - |
CanvasCachedGeometry |
ID2D1GeometryRealization |
Device | - |
CanvasCommandList |
ID2D1CommandList |
Device | - |
CanvasDevice |
ID2D1Device1 |
- | - |
CanvasDrawingSession |
ID2D1DeviceContext1 |
- | - |
CanvasFontFace |
IDWriteFontFaceReference |
- | - |
CanvasFontSet |
IDWriteFontSet |
- | - |
CanvasGeometry |
ID2D1Geometry,或其衍生介面ID2D1PathGeometry之一,、ID2D1RectangleGeometry、ID2D1RoundedRectangleGeometryID2D1EllipseGeometryID2D1TransformedGeometry或ID2D1GeometryGroup |
Device | - |
CanvasGradientMesh |
ID2D1GradientMesh |
Device | - |
CanvasImageBrush |
ID2D1BitmapBrush1(若影像是 CanvasBitmap 且 SourceRectangle 為 null),或 ID2D1ImageBrush(若其為其他任何類型的 ICanvasImage,或若 SourceRectangle is set) |
Device | 可選 DPI^1 |
CanvasLinearGradientBrush |
ID2D1LinearGradientBrush |
Device | - |
CanvasNumberSubstitution |
IDWriteNumberSubstitution |
- | - |
CanvasRadialGradientBrush |
ID2D1RadialGradientBrush |
Device | - |
CanvasRenderTarget |
ID2D1Bitmap1 和 D2D1_BITMAP_OPTIONS_TARGET |
Device | - |
CanvasSolidColorBrush |
ID2D1SolidColorBrush |
Device | - |
CanvasStrokeStyle |
ID2D1StrokeStyle1 |
- | Device |
CanvasSvgDocument |
ID2D1SvgDocument^2 |
Device | - |
CanvasSwapChain |
IDXGISwapChain1 |
裝置、DPI | - |
CanvasTextFormat |
IDWriteTextFormat1 |
- | - |
CanvasTextLayout |
IDWriteTextLayout3 |
Device | - |
CanvasTextRenderingParameters |
IDWriteRenderingParams3 |
- | - |
CanvasTypography |
IDWriteTypography |
- | - |
CanvasVirtualBitmap |
ID2D1ImageSource 或 ID2D1TransformedImageSource |
Device | - |
ColorManagementProfile |
ID2D1ColorContext |
Device | Device |
EffectTransferTable3D |
ID2D1LookupTable3D |
Device | - |
Microsoft.Graphics.Canvas.Effects.* (多個 Win2D 類別對應到相同的 D2D 類型) |
ID2D1Effect 搭配適當的 D2D1_PROPERTY_TYPE_CLSID |
Device | 裝置,選配 DPI^1 |
Note
^1:選擇性 DPI 表示,對此類型呼叫 GetWrappedResource 時不指定 DPI 值也是有效的;但如果指定了 DPI,Win2D 或許可以藉由省略多餘的 DPI 補償節點,更有效率地設定效果圖。 這適用於呼叫 GetWrappedResource 一個效果,或是對具有效果的 a CanvasImageBrush 作為其來源影像。
Note
^2:使用原生 C++ 互通操作從 CanvasSvgDocument 產生 ID2D1SvgDocument 時,會忽略 ID2D1SvgDocument 的檢視區大小。
使用 C++/CX 的互通操作
以下是一個 Win2D 如何與 Direct2D 互通的範例,使用 C++:
#include <Microsoft.Graphics.Canvas.native.h>
#include <d2d1_2.h>
using namespace Microsoft::Graphics::Canvas;
using namespace Microsoft::WRL;
// Interop Win2D -> Direct2D.
CanvasDevice^ canvasDevice = ...;
CanvasBitmap^ canvasBitmap = ...;
ComPtr<ID2D1Device> nativeDevice = GetWrappedResource<ID2D1Device>(canvasDevice);
ComPtr<ID2D1Bitmap1> nativeBitmap = GetWrappedResource<ID2D1Bitmap1>(canvasBitmap);
// Interop Direct2D -> Win2D.
canvasDevice = GetOrCreate<CanvasDevice>(nativeDevice.Get());
bitmap = GetOrCreate<CanvasBitmap>(canvasDevice, nativeBitmap.Get());
Note
也可以透過 C# 以各種方式實現互通(例如使用內建的 COM/WinRT 互通、透過 CsWinRT 的 API,或使用 blittable 繫結)。 想了解更多關於 C# 手動互通的資訊,請參考 CsWinRT 的文件。
使用 C++/WinRT 進行互通
你也可以用 C++/WinRT 進行互操作,並稍作修改上述設定。 請注意,當你將 Win2D NuGet 套件加入 C++/WinRT 專案時,Win2D Windows 執行階段 元件的 C++/WinRT 標頭應該會自動產生。 不過,要互通,你仍然需要使用 Microsoft 的標頭檔。Graphics.Canvas.native.h 包含命名空間ICanvasFactoryNative中低階的 ABI 介面ABI::Microsoft::Graphics::Canvas。 介面有以下功能,你可以用來進行互通操作。
HRESULT GetOrCreate(ICanvasDevice* device, IUnknown* resource, float dpi, IInspectable** wrapper);
HRESULT GetNativeResource(ICanvasDevice* device, float dpi, REFIID iid, void** resource);
以下是一個範例,示範如何從 CanvasVirtualBitmap 建立 IWICBitmapSource,並從 IWICBitmapSource 和共用的 CanvasDevice 開始。
#include "pch.h"
#include <wincodec.h>
#include <wincodecsdk.h>
#include <winrt/Microsoft.Graphics.Canvas.h> //This defines the C++/WinRT interfaces for the Win2D Windows Runtime Components
#include <Microsoft.Graphics.Canvas.h> //This defines the low-level ABI interfaces for the Win2D Windows Runtime Components
#include <Microsoft.Graphics.Canvas.native.h> //This is for interop
#include <d2d1_3.h>
using namespace winrt::Microsoft::Graphics::Canvas;
namespace abi {
using namespace ABI::Microsoft::Graphics::Canvas;
}
namespace winrt::Win2DInteropTest::implementation {
CanvasVirtualBitmap CreateVirtualBitmapFromBitmapSource(com_ptr<IWICBitmapSource> const& pBitmapSource){
CanvasDevice sharedDevice = CanvasDevice::GetSharedDevice();
//First we need to get an ID2D1Device1 pointer from the shared CanvasDevice
com_ptr<abi::ICanvasResourceWrapperNative> nativeDeviceWrapper = sharedDevice.as<abi::ICanvasResourceWrapperNative>();
com_ptr<ID2D1Device1> pDevice{ nullptr };
check_hresult(nativeDeviceWrapper->GetNativeResource(nullptr, 0.0f, guid_of<ID2D1Device1>(), pDevice.put_void()));
//Next we need to call some Direct2D functions to create the ID2D1ImageSourceFromWic object
com_ptr<ID2D1DeviceContext1> pContext{ nullptr };
check_hresult(pDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, pContext.put()));
com_ptr<ID2D1DeviceContext2> pContext2 = pContext.as<ID2D1DeviceContext2>();
com_ptr<ID2D1ImageSourceFromWic> pImage{ nullptr };
check_hresult(pContext2->CreateImageSourceFromWic(pBitmapSource.get(), D2D1_IMAGE_SOURCE_LOADING_OPTIONS_RELEASE_SOURCE, pImage.put()));
//Finally we need to wrap the ID2D1ImageSourceFromWic object inside
com_ptr<::IInspectable> pInspectable{ nullptr };
auto factory = winrt::get_activation_factory<CanvasDevice, abi::ICanvasFactoryNative>(); //abi::ICanvasFactoryNative is the activation factory for the CanvasDevice class
check_hresult(factory->GetOrCreate(sharedDevice.as<abi::ICanvasDevice>().get(), pImage.as<::IUnknown>().get(), 0.0f, pInspectable.put())); //Note abi::ICanvasDevice is defined in the header Microsoft.Graphics.Canvas.h
CanvasVirtualBitmap cvb = pInspectable.as<CanvasVirtualBitmap>();
return cvb;
}
}
請記得在任何 WinRT 標頭之前,先在 pch.h 中包含標頭 <unknwn.h>(這是 SDK 17763 及更新版本中的必要要求)。