Współdziałanie z Direct2D

Win2D został zaimplementowany jako warstwa na bazie Direct2D i umożliwia interoperacyjność w obu kierunkach. Jeśli masz obiekt Win2D, możesz uzyskać dostęp do natywnego obiektu Direct2D, który jest używany do jego implementacji. Jeśli masz obiekt Direct2D, możesz odnaleźć obiekt Win2D, który stanowi jego opakowanie, lub utworzyć nowe opakowanie, jeśli taki obiekt jeszcze nie istnieje.

Interop umożliwia łączenie i dopasowywanie biblioteki Win2D z natywnymi interfejsami API DirectX. Możesz napisać aplikację, która w większości korzysta z Win2D, ale w dowolnym momencie w razie potrzeby przejść bezpośrednio do natywnego DirectX — na przykład aby wywołać inne API lub komponent innej firmy, który wymaga natywnych interfejsów. Lub twoja aplikacja może być w większości natywna DirectX, ale możesz przełączyć się na Win2D w określonych miejscach, w których potrzebujesz dodatkowej wygody lub obsługi języka C#.

Interfejsy API międzyoperacyjności

Interfejsy API interoperacyjne C++/CX są zdefiniowane w pliku nagłówkowym Microsoft.Graphics.Canvas.native.h:

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

using namespace Microsoft::Graphics::Canvas;

Aby uzyskać natywny obiekt Direct2D, który jest opakowany przez obiekt 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);

W przypadku większości typów element GetWrappedResource można wywołać, przekazując jako parametr tylko obiekt opakowujący Win2D. W przypadku kilku typów (zobacz poniższą tabelę) należy również przekazać wartość urządzenia i/lub DPI. Przekazanie urządzenia lub DPI podczas używania GetWrappedResource z typami, które ich nie wymagają, nie jest błędem.

Aby uzyskać obiekt Win2D opakowujące natywny obiekt 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 zwraca istniejącą instancję opakowania, jeśli taka już istnieje, lub tworzy nowe opakowanie, jeśli nie istnieje. Wielokrotne wywoływanie tej funkcji na tym samym obiekcie natywnym będzie przy każdym wywołaniu zwracać to samo opakowanie, dopóki ta instancja opakowania nadal istnieje. Jeśli wszystkie odwołania do obiektu opakowującego zostaną zwolnione, tak że liczba jego odwołań spadnie do zera i obiekt ten zostanie zniszczony, każde późniejsze wywołanie GetOrCreate będzie musiało utworzyć nowy obiekt opakowujący.

W przypadku niektórych typów GetOrCreate można wywołać tylko obiekt zasobu Direct2D jako parametr, podczas gdy w przypadku innych typów (patrz poniższa tabela) należy również przekazać urządzenie i wartość DPI. Przekazanie urządzenia lub DPI podczas używania GetOrCreate z typami, które ich nie wymagają, nie jest błędem. Jeśli opakowanie Win2D już istnieje, można pominąć parametry urządzenia i DPI nawet w przypadku typów, które zwykle ich wymagają: te parametry są używane tylko podczas tworzenia nowych instancji opakowań.

GetOrCreate rozumie hierarchie dziedziczenia i zawsze tworzy odpowiedni najbardziej wyspecjalizowany typ opakowujący. Jeśli na przykład wywołasz GetOrCreate<CanvasBitmap>(ID2D1Bitmap1*) z obiektem ID2D1Bitmap1, który ma flagę D2D1_BITMAP_OPTIONS_TARGET, zwrócona instancja opakowująca będzie w rzeczywistości obiektem CanvasRenderTarget (który dziedziczy po CanvasBitmap). Odwrotnie, jeśli wywołasz GetOrCreate<CanvasRenderTarget>(ID2D1Bitmap1*) z obiektem ID2D1Bitmap1, który nie ma D2D1_BITMAP_OPTIONS_TARGET, zostanie zgłoszony wyjątek nieprawidłowego rzutowania.

Doprowadzając to do skrajności, można poprawnie wywołać GetOrCreate<Object>(IUnknown*), a także GetWrappedResource<IUnknown>(Object^).

Typy obsługujące interoperacyjność

Typ Win2D Typ Direct2D GetOrCreate Parametry GetWrappedResource Parametry
CanvasBitmap ID2D1Bitmap1 bez D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasCachedGeometry ID2D1GeometryRealization Device -
CanvasCommandList ID2D1CommandList Device -
CanvasDevice ID2D1Device1 - -
CanvasDrawingSession ID2D1DeviceContext1 - -
CanvasFontFace IDWriteFontFaceReference - -
CanvasFontSet IDWriteFontSet - -
CanvasGeometry ID2D1Geometrylub jeden z jego interfejsów pochodnych ID2D1PathGeometry, , ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry, ID2D1EllipseGeometry, ID2D1TransformedGeometrylub ID2D1GeometryGroup Device -
CanvasGradientMesh ID2D1GradientMesh Device -
CanvasImageBrush ID2D1BitmapBrush1 (jeśli obraz jest CanvasBitmap, a SourceRectangle ma wartość null) lub ID2D1ImageBrush (jeśli jest to dowolny inny typ ICanvasImage lub jeśli SourceRectangle is set) Device Opcjonalne DPI^1
CanvasLinearGradientBrush ID2D1LinearGradientBrush Device -
CanvasNumberSubstitution IDWriteNumberSubstitution - -
CanvasRadialGradientBrush ID2D1RadialGradientBrush Device -
CanvasRenderTarget ID2D1Bitmap1 z D2D1_BITMAP_OPTIONS_TARGET Device -
CanvasSolidColorBrush ID2D1SolidColorBrush Device -
CanvasStrokeStyle ID2D1StrokeStyle1 - Device
CanvasSvgDocument ID2D1SvgDocument^2 Device -
CanvasSwapChain IDXGISwapChain1 Urządzenie, DPI -
CanvasTextFormat IDWriteTextFormat1 - -
CanvasTextLayout IDWriteTextLayout3 Device -
CanvasTextRenderingParameters IDWriteRenderingParams3 - -
CanvasTypography IDWriteTypography - -
CanvasVirtualBitmap ID2D1ImageSource lub ID2D1TransformedImageSource Device -
ColorManagementProfile ID2D1ColorContext Device Device
EffectTransferTable3D ID2D1LookupTable3D Device -
Microsoft.Graphics.Canvas.Effects.* (wiele klas Win2D jest mapowanych na ten sam typ D2D) ID2D1Effect z odpowiednimi D2D1_PROPERTY_TYPE_CLSID Device Urządzenie, DPI (opcjonalnie)^1

Note

^1: opcjonalna wartość DPI oznacza, że można wywołać GetWrappedResource dla tego typu bez określania wartości DPI, ale jeśli określisz DPI, Win2D może wydajniej konfigurować grafy efektów, pomijając nadmiarowe węzły kompensacji DPI. Ma to zastosowanie podczas wywoływania GetWrappedResource dla efektu lub dla CanvasImageBrush, który ma efekt jako obraz źródłowy.

Note

^2: gdy element CanvasSvgDocument jest tworzony z elementu ID2D1SvgDocument przy użyciu natywnej interoperacyjności języka C++, rozmiar widoku elementu ID2D1SvgDocument jest ignorowany.

Współdziałanie przy użyciu języka C++/CX

Oto przykład sposobu współdziałania win2D z direct2D przy użyciu języka 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

Interoperacyjność jest również możliwa w C#, na różne sposoby (np. przy użyciu wbudowanej interoperacyjności COM/WinRT, za pośrednictwem interfejsów API CsWinRT lub wiązań blittable). Aby uzyskać więcej informacji na temat ręcznego współdziałania w języku C#, zapoznaj się z dokumentacją dotyczącą csWinRT.

Współdziałanie przy użyciu języka C++/WinRT

Można również wykonywać międzyoperacyjnie przy użyciu języka C++/WinRT z pewnymi modyfikacjami powyższych elementów. Pamiętaj, że nagłówki C++/WinRT dla składników środowisko wykonawcze systemu Windows Win2D powinny być generowane automatycznie po dodaniu pakietu NuGet Win2D do projektu C++/WinRT. Jednak w przypadku interoperacyjności nadal należy użyć pliku nagłówka Microsoft.Graphics.Canvas.native.h, który zawiera interfejs ABI niskiego poziomu ICanvasFactoryNative w przestrzeni nazw ABI::Microsoft::Graphics::Canvas. Interfejs ma następujące funkcje, których można użyć do zapewnienia interoperacyjności.

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

Oto przykład pokazujący, jak utworzyć element CanvasVirtualBitmap z elementu IWICBitmapSource, zaczynając od elementu IWICBitmapSource i współdzielonego elementu 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;
  }
}

Pamiętaj, aby uwzględnić nagłówek <unknwn.h> w pliku pch.h przed wszystkimi nagłówkami WinRT (wymaganymi w zestawie SDK 17763 lub nowszym).