Interopérabilité avec Direct2D

Win2D est implémenté en tant que couche sur Direct2D et prend en charge l’interopérabilité dans les deux sens. Si vous avez un objet Win2D, vous pouvez accéder à l’objet Direct2D natif utilisé pour l’implémenter. Si vous avez un objet Direct2D, vous pouvez rechercher l’objet Win2D qui l’encapsule, ou créer un wrapper s’il n’en existe pas déjà un.

L’interopérabilité vous permet de combiner et de mettre en correspondance Win2D avec des API DirectX natives. Vous pouvez écrire une application qui utilise principalement Win2D, tout en pouvant revenir à DirectX natif à tout moment, par exemple pour faire appel à une autre API ou à un composant tiers qui nécessite des interfaces natives. Ou votre application peut être essentiellement une application DirectX native, tout en vous permettant de passer à Win2D à certains endroits où vous souhaitez bénéficier de sa plus grande simplicité d’utilisation ou de la prise en charge de C#.

API d’interopérabilité

Les API d’interopérabilité C++/CX sont définies dans l’en-tête Microsoft. Graphics.Canvas.native.h :

#include <Microsoft.Graphics.Canvas.native.h>

using namespace Microsoft::Graphics::Canvas;

Pour obtenir l’objet Direct2D natif encapsulé par un objet Win2D :

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);

Pour la plupart des types GetWrappedResource , il est possible d’appeler uniquement un objet wrapper Win2D en tant que paramètre. Pour quelques types (voir le tableau ci-dessous), il faut également fournir un appareil et/ou une valeur DPI. Il n'est pas considéré comme une erreur de transmettre un appareil ou un DPI lors de l'utilisation de GetWrappedResource avec des types qui n'en ont pas besoin.

Pour obtenir un objet Win2D encapsulant un objet Direct2D natif :

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 retourne une instance de wrapper existante si une instance existe déjà, ou crée un wrapper si ce n’est pas le cas. L’appel répété sur le même objet natif retourne le même wrapper chaque fois, tant que cette instance de wrapper continue d’exister. Si toutes les références à l’encapsuleur sont libérées au point que son nombre de références tombe à zéro et qu’il soit détruit, tout appel ultérieur à GetOrCreate devra créer un nouvel encapsuleur.

Pour certains types, il est possible d’appeler GetOrCreate avec comme seul paramètre un objet de ressource Direct2D, tandis que pour d’autres types (voir le tableau ci-dessous), il faut également lui passer un périphérique et une valeur de PPP (DPI). Il n'est pas considéré comme une erreur de transmettre un appareil ou un DPI lors de l'utilisation de GetOrCreate avec des types qui n'en ont pas besoin. Si un wrapper Win2D existe déjà, il est possible d’omettre le périphérique et le DPI, même pour les types qui en auraient normalement besoin : ces paramètres ne sont utilisés que lors de la création de nouvelles instances de wrapper.

GetOrCreate comprend les hiérarchies d’héritage et crée toujours le type de wrapper le plus dérivé approprié. Par exemple, si vous appelez GetOrCreate<CanvasBitmap>(ID2D1Bitmap1*) avec un ID2D1Bitmap1 portant l’indicateur D2D1_BITMAP_OPTIONS_TARGET, l’instance wrapper retournée sera en fait de type CanvasRenderTarget (qui dérive de CanvasBitmap). À l'inverse, si vous appelez GetOrCreate<CanvasRenderTarget>(ID2D1Bitmap1*) avec un ID2D1Bitmap1 qui ne possède pas D2D1_BITMAP_OPTIONS_TARGET, une exception de transtypage non valide est levée.

En prenant cela à l’extrême, il est valide d’appeler GetOrCreate<Object>(IUnknown*), et aussi GetWrappedResource<IUnknown>(Object^).

Types qui prennent en charge l’interopérabilité

Type Win2D Type Direct2D Paramètres GetOrCreate Paramètres GetWrappedResource
CanvasBitmap ID2D1Bitmap1 sans D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasCachedGeometry ID2D1GeometryRealization Device -
CanvasCommandList ID2D1CommandList Device -
CanvasDevice ID2D1Device1 - -
CanvasDrawingSession ID2D1DeviceContext1 - -
CanvasFontFace IDWriteFontFaceReference - -
CanvasFontSet IDWriteFontSet - -
CanvasGeometry ID2D1Geometryou l’une de ses interfaces ID2D1PathGeometrydérivées, ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry, ID2D1EllipseGeometry, ID2D1TransformedGeometryou ID2D1GeometryGroup Device -
CanvasGradientMesh ID2D1GradientMesh Device -
CanvasImageBrush ID2D1BitmapBrush1 (si l’image est un CanvasBitmap et SourceRectangle est null) ou ID2D1ImageBrush (s’il s’agit de tout autre type de ICanvasImage, ou si SourceRectangle is set) Device DPI facultatif^1
CanvasLinearGradientBrush ID2D1LinearGradientBrush Device -
CanvasNumberSubstitution IDWriteNumberSubstitution - -
CanvasRadialGradientBrush ID2D1RadialGradientBrush Device -
CanvasRenderTarget ID2D1Bitmap1 avec D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasSolidColorBrush ID2D1SolidColorBrush Device -
CanvasStrokeStyle ID2D1StrokeStyle1 - Device
CanvasSvgDocument ID2D1SvgDocument^2 Device -
CanvasSwapChain IDXGISwapChain1 Appareil, PPP -
CanvasTextFormat IDWriteTextFormat1 - -
CanvasTextLayout IDWriteTextLayout3 Device -
CanvasTextRenderingParameters IDWriteRenderingParams3 - -
CanvasTypography IDWriteTypography - -
CanvasVirtualBitmap ID2D1ImageSource ou ID2D1TransformedImageSource Device -
ColorManagementProfile ID2D1ColorContext Device Device
EffectTransferTable3D ID2D1LookupTable3D Device -
Microsoft.Graphics.Canvas.Effects.* (plusieurs classes Win2D correspondent au même type D2D) ID2D1Effect avec le D2D1_PROPERTY_TYPE_CLSID approprié Device Appareil, DPI facultatif^1

Note

^1 : DPI facultatif signifie qu’il est possible d’appeler GetWrappedResource pour ce type sans spécifier de valeur DPI, mais si vous indiquez une valeur DPI, Win2D pourra peut-être configurer plus efficacement les graphes d’effets en omettant les nœuds redondants de compensation DPI. Cela s'applique lorsque vous appelez GetWrappedResource sur un effet, ou sur un CanvasImageBrush dont l'image source est un effet.

Note

^2 : lorsqu’un CanvasSvgDocument est produit à partir d’un ID2D1SvgDocument à l’aide de l’interopérabilité C++ native, la taille de la fenêtre d’affichage du ID2D1SvgDocument est ignorée.

Interopérabilité à l’aide de C++/CX

Voici un exemple de la façon dont Win2D peut interagir avec Direct2D à l’aide de 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

L'interopérabilité est également possible via C#, par différents moyens (par exemple en utilisant l'interopérabilité COM/WinRT intégrée, les API de CsWinRT ou des liaisons blittables). Pour plus d’informations sur l’interopérabilité manuelle en C#, reportez-vous à la documentation sur CsWinRT.

Interopérabilité à l’aide de C++/WinRT

Vous pouvez également réaliser l’interopérabilité à l’aide de C++/WinRT, moyennant quelques modifications de ce qui précède. Notez que les en-têtes C++/WinRT pour les composants Win2D Windows Runtime doivent être générés automatiquement lorsque vous ajoutez le package NuGet Win2D à votre projet C++/WinRT. Toutefois, pour l’interopérabilité, vous devez toujours utiliser le fichier d’en-tête Microsoft. Graphics.Canvas.native.h qui contient l’interface ICanvasFactoryNative ABI de bas niveau dans l’espace de noms ABI::Microsoft::Graphics::Canvas. L’interface a les fonctions suivantes que vous pouvez utiliser pour effectuer l’interopérabilité.

HRESULT GetOrCreate(ICanvasDevice* device, IUnknown* resource, float dpi, IInspectable** wrapper);
HRESULT GetNativeResource(ICanvasDevice* device, float dpi, REFIID iid, void** resource);

Voici un exemple montrant comment créer un CanvasVirtualBitmap à partir d’un IWICBitmapSource, en commençant par le IWICBitmapSource et le CanvasDevice partagé.

#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;
  }
}

N’oubliez pas d’inclure l’en-tête <unknwn.h> dans pch.h avant tous les en-têtes WinRT (requis dans le SDK 17763 et versions ultérieures).