Interoperabilidad con Direct2D

Win2D se implementa como una capa sobre Direct2D y admite la interoperabilidad en ambas direcciones. Si tienes un objeto Win2D, puedes acceder al objeto nativo de Direct2D que se usa para implementarlo. Si tienes un objeto Direct2D, puedes buscar el objeto Win2D que lo encapsula o crear un contenedor si aún no existe uno.

La interoperabilidad te permite combinar y hacer coincidir Win2D con las API nativas de DirectX. Puedes escribir una aplicación que use principalmente Win2D, pero recurrir a DirectX nativo en cualquier momento, por ejemplo, para llamar a alguna otra API o a un componente de terceros que requiera interfaces nativas. O bien, la aplicación puede ser principalmente nativa de DirectX, pero puedes cambiar a Win2D en lugares específicos donde quieras su comodidad adicional o compatibilidad con C#.

API de interoperabilidad

Las API de interoperabilidad de C++/CX se definen en el encabezado Microsoft. Graphics.Canvas.native.h:

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

using namespace Microsoft::Graphics::Canvas;

Para obtener el objeto nativo de Direct2D encapsulado por un objeto 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);

Para la mayoría de los tipos, se puede llamar a GetWrappedResource únicamente con un objeto contenedor de Win2D como parámetro. Para algunos tipos (consulte la tabla siguiente), también debe pasarse un valor de dispositivo o PPP. No es un error pasar un dispositivo o un valor de DPI al utilizar GetWrappedResource con tipos que no los requieren.

Para obtener un objeto Win2D encapsulando un objeto nativo de 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 devuelve una instancia de contenedor existente si ya existe una o crea un nuevo contenedor si no existe. Llamarlo repetidamente sobre el mismo objeto nativo devolverá el mismo wrapper cada vez, siempre y cuando esa instancia de wrapper siga existiendo. Si todas las referencias al envoltorio se liberan de forma que su recuento de referencias llegue a cero y se destruya, cualquier llamada posterior a GetOrCreate tendrá que crear un nuevo envoltorio.

Para algunos tipos, GetOrCreate se puede invocar solo con un objeto de recurso Direct2D como parámetro, mientras que para otros tipos (véase la tabla siguiente) también se debe pasar un dispositivo y un valor de DPI. No es un error pasar un dispositivo o un valor de DPI al utilizar GetOrCreate con tipos que no los requieren. Si ya existe un envoltorio Win2D, se puede omitir el dispositivo y el DPI incluso para los tipos que normalmente los necesitarían: estos parámetros solo se utilizan al crear nuevas instancias de envoltorio.

GetOrCreate entiende las jerarquías de herencia y siempre creará el tipo de envoltorio más derivado adecuado. Por ejemplo, si se invoca GetOrCreate<CanvasBitmap>(ID2D1Bitmap1*) con un ID2D1Bitmap1 que tiene el indicador D2D1_BITMAP_OPTIONS_TARGET, la instancia de envoltorio devuelta será, de hecho, un CanvasRenderTarget (que deriva de CanvasBitmap). Por el contrario, si se invoca GetOrCreate<CanvasRenderTarget>(ID2D1Bitmap1*) con un ID2D1Bitmap1 que no tiene D2D1_BITMAP_OPTIONS_TARGET, se lanzará una excepción de conversión no válida.

Llevando esto al extremo, es válido invocar GetOrCreate<Object>(IUnknown*) y también GetWrappedResource<IUnknown>(Object^).

Tipos que admiten la interoperabilidad

Tipo Win2D Tipo Direct2D Parámetros GetOrCreate Parámetros GetWrappedResource
CanvasBitmap ID2D1Bitmap1 sin D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasCachedGeometry ID2D1GeometryRealization Device -
CanvasCommandList ID2D1CommandList Device -
CanvasDevice ID2D1Device1 - -
CanvasDrawingSession ID2D1DeviceContext1 - -
CanvasFontFace IDWriteFontFaceReference - -
CanvasFontSet IDWriteFontSet - -
CanvasGeometry ID2D1Geometry, o una de sus interfaces derivadas ID2D1PathGeometry, , ID2D1RectangleGeometryID2D1RoundedRectangleGeometry, ID2D1EllipseGeometry, ID2D1TransformedGeometryoID2D1GeometryGroup Device -
CanvasGradientMesh ID2D1GradientMesh Device -
CanvasImageBrush ID2D1BitmapBrush1 (si la imagen es una CanvasBitmap y SourceRectangle es null) o ID2D1ImageBrush (si es cualquier otro tipo de ICanvasImage, o si SourceRectangle is set) Device DPI opcional^1
CanvasLinearGradientBrush ID2D1LinearGradientBrush Device -
CanvasNumberSubstitution IDWriteNumberSubstitution - -
CanvasRadialGradientBrush ID2D1RadialGradientBrush Device -
CanvasRenderTarget ID2D1Bitmap1 con D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasSolidColorBrush ID2D1SolidColorBrush Device -
CanvasStrokeStyle ID2D1StrokeStyle1 - Device
CanvasSvgDocument ID2D1SvgDocument^2 Device -
CanvasSwapChain IDXGISwapChain1 Dispositivo, PPP -
CanvasTextFormat IDWriteTextFormat1 - -
CanvasTextLayout IDWriteTextLayout3 Device -
CanvasTextRenderingParameters IDWriteRenderingParams3 - -
CanvasTypography IDWriteTypography - -
CanvasVirtualBitmap ID2D1ImageSource o ID2D1TransformedImageSource Device -
ColorManagementProfile ID2D1ColorContext Device Device
EffectTransferTable3D ID2D1LookupTable3D Device -
Microsoft.Graphics.Canvas.Effects.* (varias clases Win2D se asignan al mismo tipo D2D) ID2D1Effect con el D2D1_PROPERTY_TYPE_CLSID adecuado Device Dispositivo opcional, DPI^1

Note

^1: DPI opcional significa que es válido llamar a GetWrappedResource para este tipo sin especificar un valor de DPI, pero si se especifica el DPI, Win2D podría configurar los gráficos de efectos de forma más eficiente al omitir los nodos de compensación de DPI redundantes. Esto se aplica al llamar a GetWrappedResource en un efecto, o en un CanvasImageBrush que tenga un efecto como imagen de origen.

Note

^2: cuando se genera un CanvasSvgDocument a partir de un ID2D1SvgDocument mediante la interoperabilidad nativa de C++, se ignora el tamaño de la ventana gráfica del ID2D1SvgDocument.

Interoperabilidad con C++/CX

Este es un ejemplo de cómo Win2D puede interoperar con Direct2D mediante 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

La interoperabilidad también es posible mediante C#, a través de diversos medios (por ejemplo, utilizando la interoperabilidad COM/WinRT integrada, a través de las API de CsWinRT, o utilizando enlaces blittable). Para obtener más información sobre la interoperabilidad manual en C#, consulte los documentos sobre CsWinRT.

Interoperabilidad con C++/WinRT

También puedes realizar la interoperabilidad con C++/WinRT con alguna modificación de lo anterior. Tenga en cuenta que los encabezados de C++/WinRT para win2D Windows Runtime Components deben generarse automáticamente al agregar el paquete NuGet Win2D al proyecto de C++/WinRT. Sin embargo, para la interoperabilidad seguirá siendo necesario utilizar el archivo de cabecera Microsoft.Graphics.Canvas.native.h, que contiene la interfaz ABI de bajo nivel ICanvasFactoryNative en el espacio de nombres ABI::Microsoft::Graphics::Canvas. La interfaz tiene las siguientes funciones que puede usar para realizar la interoperabilidad.

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

Aquí se muestra un ejemplo de cómo crear un CanvasVirtualBitmap a partir de un IWICBitmapSource, comenzando con el IWICBitmapSource y el CanvasDevice compartido.

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

No olvide incluir el encabezado <unknwn.h> en pch.h antes de cualquier encabezado de WinRT (necesario en el SDK 17763 y versiones posteriores).