Win2D 实现为构建在 Direct2D 之上的一层,并支持双向互操作性。 如果你有 Win2D 对象,则可以访问用于实现它的本机 Direct2D 对象。 如果你有 Direct2D 对象,则可以查找包装它的 Win2D 对象,或者创建一个新的包装器(如果尚不存在)。
互操作功能使你能够将 Win2D 与原生 DirectX API 混合使用。 可以编写一个主要使用 Win2D 的应用,但随时下拉到本机 DirectX - 也许要调用需要本机接口的其他 API 或第三方组件。 或者,你的应用可以主要采用原生 DirectX,但在某些需要利用 Win2D 额外便利性或 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 并不是错误。
要获取包装本机 Direct2D 对象的 Win2D 对象:
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 并不是错误。 如果 Win2D 包装器已存在,则即使对于通常需要它们的类型,也可以省略设备和 DPI:这些参数仅在创建新包装器实例时使用。
GetOrCreate 了解继承层次结构,并将始终创建适当的最派生包装器类型。 例如,如果你使用带有 GetOrCreate<CanvasBitmap>(ID2D1Bitmap1*) 标志的 ID2D1Bitmap1 调用 D2D1_BITMAP_OPTIONS_TARGET,那么返回的包装实例实际上将是一个 CanvasRenderTarget(它派生自 CanvasBitmap)。 反之,如果你使用没有 D2D1_BITMAP_OPTIONS_TARGET 的 ID2D1Bitmap1 调用 GetOrCreate<CanvasRenderTarget>(ID2D1Bitmap1*),将抛出无效的强制转换异常。
更进一步说,调用 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、、ID2D1RoundedRectangleGeometry、ID2D1EllipseGeometry或 ID2D1TransformedGeometryID2D1GeometryGroup |
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 |
注释
^1:可选 DPI 表示在不指定 DPI 值的情况下调用 GetWrappedResource 此类型是有效的,但如果指定 DPI,则 Win2D 可以通过退出冗余 DPI 补偿节点来更有效地配置效果图。 这适用于在效果上调用 GetWrappedResource,或在以效果作为其源图像的 CanvasImageBrush 上调用。
注释
^2:使用本机 C++ 互操作从 CanvasSvgDocument 生成 ID2D1SvgDocument 时,将忽略 ID2D1SvgDocument 的视区大小。
使用 C++/CX 进行互操作
下面是 Win2D 如何使用 C++ 与 Direct2D 进行互操作的示例:
#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());
注释
还可以通过 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 及更高版本的要求)。