diff --git a/vnext/ReactUWP/Base/UwpReactInstance.cpp b/vnext/ReactUWP/Base/UwpReactInstance.cpp index c141f793535..9b925f011b0 100644 --- a/vnext/ReactUWP/Base/UwpReactInstance.cpp +++ b/vnext/ReactUWP/Base/UwpReactInstance.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include // Polyester View Managers // TODO: Move Polyester implementations out of this library and depot @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/vnext/ReactUWP/Modules/ImageViewManagerModule.cpp b/vnext/ReactUWP/Modules/ImageViewManagerModule.cpp new file mode 100644 index 00000000000..ac6c6ad7524 --- /dev/null +++ b/vnext/ReactUWP/Modules/ImageViewManagerModule.cpp @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// NYI: +// implement image cache +// implement prefetch functionality +// implement multi source (parse out most suitable image source from array of sources) +#include "pch.h" + +#include "ImageViewManagerModule.h" + +#include + +#include + +#include + +#if _MSC_VER <= 1913 +// VC 19 (2015-2017.6) cannot optimize co_await/cppwinrt usage +#pragma optimize( "", off ) +#endif + +namespace winrt { + using namespace Windows::Storage::Streams; + using namespace Windows::UI::Xaml::Media::Imaging; +} + +namespace react { namespace uwp { + // + // ImageViewManagerModule::ImageViewManagerModuleImpl + // + class ImageViewManagerModule::ImageViewManagerModuleImpl + { + public: + ImageViewManagerModuleImpl(ImageViewManagerModule* parent, const std::shared_ptr& defaultQueueThread) + : m_parent(parent) + , m_queueThread(defaultQueueThread) + { } + + void Disconnect() + { + m_parent = nullptr; + } + + void getSize(std::string uri, Callback successCallback, Callback errorCallback); + void prefetchImage(std::string uri, Callback successCallback, Callback errorCallback); + void queryCache(const folly::dynamic& requests, Callback successCallback, Callback errorCallback); + + private: + ImageViewManagerModule* m_parent; + std::shared_ptr m_queueThread; + }; + + winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::module::CxxModule::Callback successCallback, facebook::xplat::module::CxxModule::Callback errorCallback) + { + bool succeeded{ false }; + + try + { + ImageSource source; + source.uri = uri; + + winrt::InMemoryRandomAccessStream memoryStream{ co_await react::uwp::GetImageStreamAsync(source) }; + + winrt::BitmapImage bitmap; + if (memoryStream) + { + co_await bitmap.SetSourceAsync(memoryStream); + } + + if (bitmap) + { + successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); + succeeded = true; + } + } + catch (winrt::hresult_error const&) + { + } + + if (!succeeded) + errorCallback({}); + + co_return; + } + + void ImageViewManagerModule::ImageViewManagerModuleImpl::getSize(std::string uri, Callback successCallback, Callback errorCallback) + { + GetImageSizeAsync(uri, successCallback, errorCallback); + } + + void ImageViewManagerModule::ImageViewManagerModuleImpl::prefetchImage(std::string /*uri*/, Callback successCallback, Callback /*errorCallback*/) + { + // NotYetImplemented + successCallback({}); + } + + void ImageViewManagerModule::ImageViewManagerModuleImpl::queryCache(const folly::dynamic& requests, Callback successCallback, Callback /*errorCallback*/) + { + // NotYetImplemented + successCallback({ folly::dynamic::object() }); + } + + // + // ImageViewManagerModule + // + const char* ImageViewManagerModule::name = "ImageViewManager"; + + ImageViewManagerModule::ImageViewManagerModule(const std::shared_ptr& defaultQueueThread) + : m_imageViewManagerModule(std::make_shared(this, defaultQueueThread)) + { + } + + ImageViewManagerModule::~ImageViewManagerModule() + { + } + + std::string ImageViewManagerModule::getName() + { + return name; + } + + std::map ImageViewManagerModule::getConstants() + { + return { {} }; + } + + auto ImageViewManagerModule::getMethods() -> std::vector + { + std::shared_ptr imageViewManager(m_imageViewManagerModule); + return { + Method("getSize", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) + { + imageViewManager->getSize(facebook::xplat::jsArgAsString(args, 0), successCallback, errorCallback); + }, AsyncTag), + Method("prefetchImage", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) + { + imageViewManager->prefetchImage(facebook::xplat::jsArgAsString(args, 0), successCallback, errorCallback); + }), + Method("queryCache", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) + { + imageViewManager->queryCache(args[0], successCallback, errorCallback); + }), + }; + } + +} } + diff --git a/vnext/ReactUWP/Modules/ImageViewManagerModule.h b/vnext/ReactUWP/Modules/ImageViewManagerModule.h new file mode 100644 index 00000000000..19cdb0070ab --- /dev/null +++ b/vnext/ReactUWP/Modules/ImageViewManagerModule.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#include + +namespace facebook { + namespace react { + class MessageQueueThread; + } +} + +namespace react { namespace uwp { + +class ImageViewManagerModule : public facebook::xplat::module::CxxModule +{ + public: + ImageViewManagerModule(const std::shared_ptr & defaultQueueThread); + virtual ~ImageViewManagerModule(); + + // CxxModule + std::string getName() override; + std::map getConstants() override; + auto getMethods()->std::vector override; + + static const char* name; + + private: + class ImageViewManagerModuleImpl; + std::shared_ptr m_imageViewManagerModule; +}; + +} } // namespace react::uwp diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index 26e87c8a4f5..eb6496576ec 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -90,6 +90,7 @@ true DebugFull -minpdbpathlen:256 + dxguid.lib;%(AdditionalDependencies) $(ProjectDir)ABI\idl @@ -167,6 +168,7 @@ + @@ -189,15 +191,24 @@ + + + + + - + + + + + @@ -231,7 +242,7 @@ - + @@ -244,6 +255,7 @@ + @@ -273,7 +285,9 @@ - + + + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index b30ae3eb7fa..74bdcec1dce 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -94,9 +94,6 @@ Views - - Views - Views @@ -219,9 +216,21 @@ Views + + Views\Image + + + Views\Image + + + Views\Image + Views + + Modules + @@ -311,9 +320,6 @@ ABI - - Views - Views @@ -416,18 +422,6 @@ Views\cppwinrt - - Views\cppwinrt - - - Views\cppwinrt - - - Views\cppwinrt - - - Views\cppwinrt - Views @@ -441,9 +435,54 @@ Views + + Views\Image + + + Views\cppwinrt\winrt + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt + + + Views\cppwinrt + + + Views\Image + + + Views\Image + Views + + Modules + + + Views\Image + + + Views\Image + @@ -572,6 +611,15 @@ {101c2d4a-d343-4771-969f-29af0c9f46b2} + + {6d877886-1c5c-45b8-8eae-2e1354cf673e} + + + {e5234a36-e9ee-4ba3-9b11-02e7bd2ca6f0} + + + {24ea7245-f460-4176-82b1-3cac4cd54621} + diff --git a/vnext/ReactUWP/Views/Image/BorderEffect.h b/vnext/ReactUWP/Views/Image/BorderEffect.h new file mode 100644 index 00000000000..e580f958b8b --- /dev/null +++ b/vnext/ReactUWP/Views/Image/BorderEffect.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "Microsoft.UI.Composition.Effects_Impl.h" + +namespace winrt::Microsoft::UI::Composition::Effects::implementation +{ + class BorderEffect : + public BorderEffectT + { + public: + DECLARE_D2D_GUID(CLSID_D2D1Border); + DECLARE_SINGLE_SOURCE(Source); + DECLARE_POD_PROPERTY(ExtendX, winrt::CanvasEdgeBehavior, winrt::CanvasEdgeBehavior::Clamp, true); + DECLARE_POD_PROPERTY(ExtendY, winrt::CanvasEdgeBehavior, winrt::CanvasEdgeBehavior::Clamp, true); + DECLARE_NAMED_PROPERTY_MAPPING( + { L"ExtendX", D2D1_BORDER_PROP_EDGE_MODE_X, PropertyMapping::GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT }, + { L"ExtendY", D2D1_BORDER_PROP_EDGE_MODE_Y, PropertyMapping::GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT }); + + public: + IFACEMETHODIMP GetPropertyCount(_Out_ UINT* count) override { *count = 2; return S_OK; } + + IFACEMETHODIMP GetProperty(UINT index, _Outptr_ abi::IPropertyValue** value) override + { + return UsePropertyFactory(value, [=]() + { + switch (index) + { + case D2D1_BORDER_PROP_EDGE_MODE_X: return winrt::PropertyValue::CreateUInt32((UINT32)m_ExtendX); + case D2D1_BORDER_PROP_EDGE_MODE_Y: return winrt::PropertyValue::CreateUInt32((UINT32)m_ExtendY); + default: throw winrt::hresult_invalid_argument(); + } + }); + } + }; +} diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp new file mode 100644 index 00000000000..9942a8e8e0d --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// NYI: +// implement multi source (parse out most suitable image source from array of sources) +#include "pch.h" + +#include "ImageViewManager.h" + +#include +#include + +#include +#include +#include +#include "ReactImage.h" + +namespace winrt { + using namespace Windows::Foundation; + using namespace Windows::UI::Xaml::Controls; +} + +// Such code is better to move to a seperate parser layer +template<> +struct json_type_traits +{ + static react::uwp::ImageSource parseJson(const folly::dynamic& json) + { + react::uwp::ImageSource source; + for (auto& item : json.items()) + { + if (item.first == "uri") + source.uri = item.second.asString(); + else if (item.first == "method") + source.method = item.second.asString(); + else if (item.first == "headers") + source.headers = item.second; + else if (item.first == "width") + source.width = item.second.asDouble(); + else if (item.first == "height") + source.height = item.second.asDouble(); + else if (item.first == "scale") + source.scale = item.second.asDouble(); + else if (item.first == "__packager_asset") + source.packagerAsset = item.second.asBool(); + } + return source; + } +}; + +template<> +struct json_type_traits +{ + static react::uwp::ResizeMode parseJson(const folly::dynamic& json) + { + auto resizeMode{ react::uwp::ResizeMode::Contain }; + + if (json == "cover") + { + resizeMode = react::uwp::ResizeMode::Cover; + } + else if (json == "contain") + { + resizeMode = react::uwp::ResizeMode::Contain; + } + else if (json == "stretch") + { + resizeMode = react::uwp::ResizeMode::Stretch; + } + else if (json == "center") + { + resizeMode = react::uwp::ResizeMode::Center; + } + else if (json == "repeat") + { + resizeMode = react::uwp::ResizeMode::Repeat; + } + + return resizeMode; + } +}; + +namespace react { namespace uwp { + + ImageViewManager::ImageViewManager(const std::shared_ptr& reactInstance) + : Super(reactInstance) + { + } + + const char* ImageViewManager::GetName() const + { + return "RCTImageView"; + } + + XamlView ImageViewManager::CreateViewCore(int64_t tag) + { + auto reactImage{ ReactImage::Create() }; + + reactImage->OnLoadEnd([this, reactImage](const auto&, const bool& succeeded) + { + ImageSource source{ reactImage->Source() }; + + EmitImageEvent(m_wkReactInstance.lock(), reactImage.as(), succeeded ? "topLoad" : "topError", source); + EmitImageEvent(m_wkReactInstance.lock(), reactImage.as(), "topLoadEnd", source); + }); + + return reactImage.as(); + } + + void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) + { + auto canvas{ nodeToUpdate->GetView().as() }; + + if (canvas == nullptr) + return; + + for (const auto& pair : reactDiffMap.items()) + { + const std::string& propertyName{ pair.first.getString() }; + const folly::dynamic& propertyValue{ pair.second }; + + if (propertyName == "source") + { + setSource(canvas, propertyValue); + } + else if (propertyName == "resizeMode") + { + auto resizeMode{ json_type_traits::parseJson(propertyValue) }; + auto reactImage{ canvas.as() }; + reactImage->ResizeMode(resizeMode); + } + + // TODO: overflow + } + + Super::UpdateProperties(nodeToUpdate, reactDiffMap); + } + + void EmitImageEvent(const std::shared_ptr& reactInstance, winrt::Canvas canvas, const char* eventName, ImageSource& source) + { + if (reactInstance == nullptr) + return; + + int64_t tag = canvas.Tag().as().GetInt64(); + folly::dynamic imageSource = folly::dynamic::object() + ("url", source.uri) + ("width", source.width) + ("height", source.height); + + folly::dynamic eventData = folly::dynamic::object() + ("target", tag) + ("source", imageSource); + reactInstance->DispatchEvent(tag, eventName, std::move(eventData)); + } + + void ImageViewManager::setSource(winrt::Canvas canvas, const folly::dynamic& data) + { + auto instance{ m_wkReactInstance.lock() }; + if (instance == nullptr) + return; + + auto sources{ json_type_traits>::parseJson(data) }; + auto reactImage{ canvas.as() }; + + EmitImageEvent(instance, canvas, "topLoadStart", sources[0]); + reactImage->Source(sources[0]); + } + + folly::dynamic ImageViewManager::GetExportedCustomDirectEventTypeConstants() const + { + auto directEvents = Super::GetExportedCustomDirectEventTypeConstants(); + directEvents["topLoadStart"] = folly::dynamic::object("registrationName", "onLoadStart"); + directEvents["topLoad"] = folly::dynamic::object("registrationName", "onLoad"); + directEvents["topLoadEnd"] = folly::dynamic::object("registrationName", "onLoadEnd"); + directEvents["topError"] = folly::dynamic::object("registrationName", "onError"); + + return directEvents; + } + + folly::dynamic ImageViewManager::GetNativeProps() const { + auto props = Super::GetNativeProps(); + // TODO: implement native props propagation from property map + props.update(folly::dynamic::object + ("source", "Map") + ("resizeMode", "string") + ("accessibilityLabel", "string") + ); + + return props; + } +} } diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h new file mode 100644 index 00000000000..488528de5f5 --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +namespace react { namespace uwp { + + class ImageViewManager : public FrameworkElementViewManager + { + using Super = FrameworkElementViewManager; + public: + ImageViewManager(const std::shared_ptr& reactInstance); + + const char* GetName() const override; + void UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) override; + + folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; + folly::dynamic GetNativeProps() const override; + + protected: + XamlView CreateViewCore(int64_t tag) override; + + private: + void setSource(winrt::Windows::UI::Xaml::Controls::Canvas canvas, const folly::dynamic& sources); + }; +} } diff --git a/vnext/ReactUWP/Views/Image/Microsoft.UI.Composition.Effects_Impl.h b/vnext/ReactUWP/Views/Image/Microsoft.UI.Composition.Effects_Impl.h new file mode 100644 index 00000000000..5a13a8b7c28 --- /dev/null +++ b/vnext/ReactUWP/Views/Image/Microsoft.UI.Composition.Effects_Impl.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#define CATCH_RETURN \ + return S_OK; \ + } catch (...) { \ + auto hr = winrt::to_hresult(); \ + __analysis_assume(FAILED(hr)); \ + return hr; + +namespace abi +{ + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::Graphics::Effects; +} + +namespace winrt +{ + using namespace winrt::Microsoft::UI::Composition::Effects; + using namespace winrt::Windows::Foundation; + using namespace winrt::Windows::Graphics::Effects; + using namespace winrt::Windows::UI; +} + +inline winrt::IGraphicsEffectSource& to_winrt(abi::IGraphicsEffectSource*& instance) +{ + return reinterpret_cast(instance); +} + +inline winrt::IPropertyValue& to_winrt(abi::IPropertyValue*& instance) +{ + return reinterpret_cast(instance); +} + +namespace winrt::Microsoft::UI::Composition::Effects::implementation +{ + // Base class for Win2D-like effect descriptions + class EffectBase : + public winrt::implements< + EffectBase, + abi::IGraphicsEffectD2D1Interop> + { + protected: + // This is a header file so we can't use "using namespace", but we can do this: + typedef winrt::Color UIColor; // Renamed because we use "Color" as a field name + typedef winrt::PropertyValue PropertyValue; + typedef abi::GRAPHICS_EFFECT_PROPERTY_MAPPING PropertyMapping; + + public: + // IGraphicsEffect + winrt::hstring Name() { return m_Name; } + void Name(winrt::hstring const& value) { m_Name = value; } + + // IGraphicsEffectD2D1Interop + IFACEMETHODIMP GetSourceCount(_Out_ UINT* count) override { *count = 0; return S_OK; } + IFACEMETHODIMP GetPropertyCount(_Out_ UINT* count) override { *count = 0; return S_OK; } + + IFACEMETHODIMP GetSource(UINT, _Outptr_ abi::IGraphicsEffectSource**) override + { + return E_INVALIDARG; + } + + IFACEMETHODIMP GetProperty(UINT, _Outptr_ abi::IPropertyValue**) override + { + return E_INVALIDARG; + } + + IFACEMETHODIMP GetNamedPropertyMapping(LPCWSTR, _Out_ UINT*, + _Out_ abi::GRAPHICS_EFFECT_PROPERTY_MAPPING*) override + { + return E_INVALIDARG; + } + + protected: + // Invokes a functor with the pointer to the property factory + static HRESULT UsePropertyFactory(_Outptr_ abi::IPropertyValue** value, std::function const& func) try + { + auto ret = func(); + auto propertyValue = ret.as(); + to_winrt(*value) = propertyValue; + CATCH_RETURN; + } + + template + static winrt::IInspectable CreateColor(UIColor color) + { + static_assert(ComponentCount == 3 || ComponentCount == 4, "Unexpected color component count."); + float values[] = { color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f }; + return winrt::PropertyValue::CreateSingleArray(reinterpret_cast&>(values)); + } + + // Helpers to implement GetNamedPropertyMapping more succintly + struct NamedProperty + { + const wchar_t* Name; // Compile-time constant + UINT Index; // Property index + abi::GRAPHICS_EFFECT_PROPERTY_MAPPING Mapping; + }; + + HRESULT GetNamedPropertyMappingImpl( + _In_count_(mappingCount) const NamedProperty* namedProperties, + UINT namedPropertyCount, + LPCWSTR name, + _Out_ UINT* index, + _Out_ abi::GRAPHICS_EFFECT_PROPERTY_MAPPING* mapping) + { + for (UINT i = 0; i < namedPropertyCount; ++i) + { + const auto& prop = namedProperties[i]; + if (_wcsicmp(name, prop.Name) == 0) + { + *index = prop.Index; + *mapping = prop.Mapping; + return S_OK; + } + } + return E_INVALIDARG; + } + + // M_PI requires us to be the first to include math.h, not worth it + static constexpr float k_PI = 3.14159265358979f; + static constexpr float k_DegreesPerRadian = 180.0f / k_PI; + + public: + winrt::hstring m_Name; + }; + + //----------------------------------------------------------------------------------------------------------------- + // Helper macros to make implementation more succint + //----------------------------------------------------------------------------------------------------------------- + +#pragma push_macro("DECLARE_D2D_GUID") +#undef DECLARE_D2D_GUID +#define DECLARE_D2D_GUID(Guid) \ + IFACEMETHODIMP GetEffectId(_Out_ GUID * id) override { *id = Guid; return S_OK; } + +#pragma push_macro("DECLARE_SOURCE") +#undef DECLARE_SOURCE +#define DECLARE_SOURCE(Name) \ + winrt::IGraphicsEffectSource m_##Name; \ + winrt::IGraphicsEffectSource Name() { return m_##Name; } \ + void Name(winrt::IGraphicsEffectSource const& value) { m_##Name = value; } + +#pragma push_macro("DECLARE_SINGLE_SOURCE") +#undef DECLARE_SINGLE_SOURCE +#define DECLARE_SINGLE_SOURCE(Name) \ + DECLARE_SOURCE(Name) \ + IFACEMETHODIMP GetSourceCount(_Out_ UINT * count) override { *count = 1; return S_OK; } \ + IFACEMETHODIMP GetSource(UINT index, _Outptr_ abi::IGraphicsEffectSource ** source) override try \ + { \ + if (index == 0) to_winrt(*source) = m_##Name; \ + else throw winrt::hresult_invalid_argument(); \ + CATCH_RETURN; \ + } + +#pragma push_macro("DECLARE_POD_PROPERTY") +#undef DECLARE_POD_PROPERTY +#define DECLARE_POD_PROPERTY(Name, Type, InitialValue, Condition) \ + private: \ + Type m_##Name = InitialValue; \ + public: \ + Type Name() { return m_##Name; } \ + void Name(Type const& value) \ + { \ + if (!(0, Condition)) { throw winrt::hresult_invalid_argument(); } \ + m_##Name = value; \ + } + +#pragma push_macro("DECLARE_NAMED_PROPERTY_MAPPING") +#undef DECLARE_NAMED_PROPERTY_MAPPING +#define DECLARE_NAMED_PROPERTY_MAPPING(...) \ + IFACEMETHODIMP GetNamedPropertyMapping(LPCWSTR name, _Out_ UINT * index, \ + _Out_ abi::GRAPHICS_EFFECT_PROPERTY_MAPPING * mapping) override \ + { \ + static const NamedProperty s_Properties[] = { __VA_ARGS__ }; \ + return GetNamedPropertyMappingImpl(s_Properties, _countof(s_Properties), name, index, mapping); \ + } +} diff --git a/vnext/ReactUWP/Views/Image/ReactImage.cpp b/vnext/ReactUWP/Views/Image/ReactImage.cpp new file mode 100644 index 00000000000..8bb2dab283a --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImage.cpp @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" + +#include "ReactImage.h" + +#include + +#include "unicode.h" + +namespace winrt { + using namespace Windows::Foundation; + using namespace Windows::Storage::Streams; + using namespace Windows::UI; + using namespace Windows::UI::Xaml::Media; + using namespace Windows::Web::Http; +} + +namespace facebook { + using namespace facebook::react::unicode; +} + +#if _MSC_VER <= 1913 +// VC 19 (2015-2017.6) cannot optimize co_await/cppwinrt usage +#pragma optimize( "", off ) +#endif + +namespace react { + namespace uwp { + + ReactImage::ReactImage() + { + m_brush = ReactImageBrush::Create(); + this->Background(m_brush.as()); + } + + /*static*/ winrt::com_ptr ReactImage::Create() + { + return winrt::make_self(); + } + + winrt::Size ReactImage::ArrangeOverride(winrt::Size finalSize) + { + auto brush{ Background().as() }; + brush->AvailableSize(finalSize); + + return finalSize; + } + + winrt::event_token ReactImage::OnLoadEnd(winrt::EventHandler const& handler) + { + return m_onLoadEndEvent.add(handler); + } + + void ReactImage::OnLoadEnd(winrt::event_token const& token) noexcept + { + m_onLoadEndEvent.remove(token); + } + + winrt::fire_and_forget ReactImage::Source(ImageSource source) + { + std::string uriString{ source.uri }; + if (uriString.length() == 0) + { + m_onLoadEndEvent(*this, false); + return; + } + + if (source.packagerAsset && uriString.find("file://") == 0) + { + uriString.replace(0, 7, "ms-appx:///Bundle/"); + } + + winrt::Uri uri{ facebook::utf8ToUtf16(uriString) }; + winrt::hstring scheme{ uri.SchemeName() }; + bool needsDownload = (scheme == L"http") || (scheme == L"https"); + + try + { + m_imageSource = source; + + winrt::InMemoryRandomAccessStream memoryStream; + if (needsDownload) + { + memoryStream = co_await GetImageStreamAsync(source); + + if (!memoryStream) + { + m_onLoadEndEvent(*this, false); + } + } + + if (!needsDownload || memoryStream) + { + auto surface{ needsDownload ? + winrt::LoadedImageSurface::StartLoadFromStream(memoryStream) : + winrt::LoadedImageSurface::StartLoadFromUri(uri) }; + + surface.LoadCompleted({ this, &ReactImage::LoadedImageSurfaceHandler }); + + m_brush->Source(surface); + } + } + catch (winrt::hresult_error const&) + { + m_onLoadEndEvent(*this, false); + } + } + + void ReactImage::LoadedImageSurfaceHandler(winrt::LoadedImageSurface const& sender, winrt::LoadedImageSourceLoadCompletedEventArgs const& args) + { + bool succeeded{ false }; + if (args.Status() == winrt::LoadedImageSourceLoadStatus::Success) + { + m_brush->Source(sender.as()); + succeeded = true; + } + + m_onLoadEndEvent(*this, succeeded); + } + + winrt::IAsyncOperation GetImageStreamAsync(ImageSource source) + { + try + { + auto httpMethod{ source.method.empty() ? + winrt::HttpMethod::Get() : + winrt::HttpMethod{facebook::utf8ToUtf16(source.method)} + }; + + winrt::Uri uri{ facebook::utf8ToUtf16(source.uri) }; + winrt::HttpRequestMessage request{ httpMethod, uri }; + + if (!source.headers.empty()) + { + for (auto& header : source.headers.items()) + { + const std::string& name{ header.first.getString() }; + const std::string& value{ header.second.getString() }; + + if (_stricmp(name.c_str(), "authorization") == 0) + { + request.Headers().TryAppendWithoutValidation(facebook::utf8ToUtf16(name), facebook::utf8ToUtf16(value)); + } + else + { + request.Headers().Append(facebook::utf8ToUtf16(name), facebook::utf8ToUtf16(value)); + } + } + } + + winrt::HttpClient httpClient; + winrt::HttpResponseMessage response{ co_await httpClient.SendRequestAsync(request) }; + + if (response.StatusCode() == winrt::HttpStatusCode::Ok) + { + winrt::IInputStream inputStream{ co_await response.Content().ReadAsInputStreamAsync() }; + winrt::InMemoryRandomAccessStream memoryStream; + co_await winrt::RandomAccessStream::CopyAsync(inputStream, memoryStream); + memoryStream.Seek(0); + + return memoryStream; + } + } + catch (winrt::hresult_error const&) + { + } + + return nullptr; + } + } +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/Image/ReactImage.h b/vnext/ReactUWP/Views/Image/ReactImage.h new file mode 100644 index 00000000000..9b6a08f215d --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImage.h @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "ReactImageBrush.h" + +#include +#include +#include +#include +#include + +#include + +namespace react { + namespace uwp { + + struct ImageSource + { + std::string uri; + std::string method; + folly::dynamic headers; + double width = 0; + double height = 0; + double scale = 1.0; + bool packagerAsset = false; + }; + + struct ReactImage : winrt::Windows::UI::Xaml::Controls::CanvasT + { + using Super = winrt::Windows::UI::Xaml::Controls::CanvasT; + private: + // Constructors + ReactImage(); + + public: + static winrt::com_ptr Create(); + template friend auto winrt::make_self(Args&& ... args); + + // Overrides + virtual winrt::Windows::Foundation::Size ArrangeOverride( + winrt::Windows::Foundation::Size finalSize); + + // Events + winrt::event_token OnLoadEnd(winrt::Windows::Foundation::EventHandler const& handler); + void OnLoadEnd(winrt::event_token const& token) noexcept; + + // Public Properties + ImageSource Source() { return m_imageSource; } + winrt::fire_and_forget Source(ImageSource source); + + react::uwp::ResizeMode ResizeMode() { return m_brush->ResizeMode(); } + void ResizeMode(react::uwp::ResizeMode value) { m_brush->ResizeMode(value); } + + private: + void LoadedImageSurfaceHandler( + winrt::Windows::UI::Xaml::Media::LoadedImageSurface const& sender, + winrt::Windows::UI::Xaml::Media::LoadedImageSourceLoadCompletedEventArgs const& args); + + ImageSource m_imageSource; + winrt::com_ptr m_brush; + winrt::event> m_onLoadEndEvent; + }; + + // Helper functions + winrt::Windows::Foundation::IAsyncOperation GetImageStreamAsync(ImageSource source); + } +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp new file mode 100644 index 00000000000..6666816671d --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" + +#include "ReactImageBrush.h" + +#include + +#include + +#include "BorderEffect.h" + +namespace winrt { + using namespace winrt::Windows::Storage::Streams; + using namespace winrt::Windows::UI::Composition; + using namespace winrt::Windows::UI::Xaml; + using namespace winrt::Windows::UI::Xaml::Media; +} + +namespace react { + namespace uwp { + /*static*/ winrt::com_ptr ReactImageBrush::Create() + { + return winrt::make_self(); + } + + void ReactImageBrush::OnConnected() + { + UpdateCompositionBrush(); + } + + void ReactImageBrush::OnDisconnected() + { + // Dispose of composition resources when no longer in use. + if (CompositionBrush()) + { + CompositionBrush(nullptr); + } + } + + void ReactImageBrush::ResizeMode(react::uwp::ResizeMode value) + { + if (m_resizeMode != value) + { + m_resizeMode = value; + UpdateCompositionBrush(); + } + } + + void ReactImageBrush::AvailableSize(winrt::Size const& value) + { + if (m_availableSize != value) + { + m_availableSize = value; + UpdateCompositionBrush(); + } + } + + void ReactImageBrush::Source(winrt::LoadedImageSurface const& value) + { + if (m_loadedImageSurface != value) + { + m_loadedImageSurface = value; + UpdateCompositionBrush(); + } + } + + void ReactImageBrush::UpdateCompositionBrush() + { + if (m_loadedImageSurface) + { + winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; + surfaceBrush.Stretch(ResizeModeToStretch()); + + auto compositionBrush{ surfaceBrush.as() }; + if (ResizeMode() == ResizeMode::Repeat) + { + // If ResizeMode is set to Repeat, then we need to use a CompositionEffectBrush. + // The CompositionSurfaceBrush holding the image is used as its source. + compositionBrush = GetOrCreateEffectBrush(surfaceBrush); + } + + // The CompositionBrush is only set after the image is first loaded, + // and anytime we switch between Surface and Effect brushes (to/from ResizeMode::Repeat) + if (CompositionBrush() != compositionBrush) + { + CompositionBrush(compositionBrush); + } + } + } + + bool ReactImageBrush::IsImageLargerThanView() + { + if (m_loadedImageSurface) + { + auto surface{ GetOrCreateSurfaceBrush().Surface().as() }; + winrt::Size dipsSize{ surface.DecodedSize() }; + + return (dipsSize.Height > AvailableSize().Height) || (dipsSize.Width > AvailableSize().Width); + } + + return false; + } + + bool ReactImageBrush::UsingSurfaceBrush() + { + return CompositionBrush().try_as() != nullptr; + } + + winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() + { + auto stretch{ winrt::CompositionStretch::None }; + + switch (ResizeMode()) + { + case ResizeMode::Center: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; + + case ResizeMode::Contain: + stretch = winrt::CompositionStretch::Uniform; + break; + + case ResizeMode::Cover: + stretch = winrt::CompositionStretch::UniformToFill; + break; + + case ResizeMode::Stretch: + stretch = winrt::CompositionStretch::Fill; + break; + + case ResizeMode::Repeat: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; + } + + return stretch; + } + + winrt::CompositionSurfaceBrush ReactImageBrush::GetOrCreateSurfaceBrush() + { + // If it doesn't exist, create it + if (!CompositionBrush()) + { + winrt::CompositionSurfaceBrush surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; + surfaceBrush.Surface(m_loadedImageSurface); + + return surfaceBrush; + } + + auto surfaceBrush{ CompositionBrush().try_as() }; + + // If ResizeMode is set to Repeat, then we're using a CompositionEffectBrush. + // Get the CompositionSurfaceBrush from its source. + if (!surfaceBrush) + { + auto effectBrush{ CompositionBrush().as() }; + assert(effectBrush); + + surfaceBrush = effectBrush.GetSourceParameter(L"source").as(); + } + + return surfaceBrush; + } + + winrt::CompositionEffectBrush ReactImageBrush::GetOrCreateEffectBrush(winrt::CompositionSurfaceBrush const& surfaceBrush) + { + if (!m_effectBrush) + { + auto borderEffect{ winrt::make() }; + borderEffect.ExtendX(winrt::CanvasEdgeBehavior::Wrap); + borderEffect.ExtendY(winrt::CanvasEdgeBehavior::Wrap); + + winrt::CompositionEffectSourceParameter borderEffectSourceParameter{ L"source" }; + borderEffect.Source(borderEffectSourceParameter); + + winrt::CompositionEffectFactory effectFactory{ winrt::Window::Current().Compositor().CreateEffectFactory(borderEffect) }; + m_effectBrush = effectFactory.CreateBrush(); + + surfaceBrush.HorizontalAlignmentRatio(0.0f); + surfaceBrush.VerticalAlignmentRatio(0.0f); + + m_effectBrush.SetSourceParameter(L"source", surfaceBrush); + } + + return m_effectBrush; + } + + } +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h new file mode 100644 index 00000000000..d2472f84e4f --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include + +namespace react { + namespace uwp { + + enum class ResizeMode + { + Cover = 0, + Contain = 1, + Stretch = 2, + Repeat = 3, + Center = 4 + }; + + struct ReactImageBrush : winrt::Windows::UI::Xaml::Media::XamlCompositionBrushBaseT + { + using Super = winrt::Windows::UI::Xaml::Media::XamlCompositionBrushBaseT; + private: + // Constructors + ReactImageBrush() = default; + + public: + static winrt::com_ptr Create(); + template friend auto winrt::make_self(Args&& ... args); + + // XamlCompositionBaseBrush Overrides + void OnConnected(); + void OnDisconnected(); + + // Public Properties + react::uwp::ResizeMode ResizeMode() { return m_resizeMode; } + void ResizeMode(react::uwp::ResizeMode value); + + winrt::Windows::Foundation::Size AvailableSize() { return m_availableSize; } + void AvailableSize(winrt::Windows::Foundation::Size const& value); + + void Source(winrt::Windows::UI::Xaml::Media::LoadedImageSurface const& value); + + private: + void UpdateCompositionBrush(); + bool IsImageLargerThanView(); + bool UsingSurfaceBrush(); + winrt::Windows::UI::Composition::CompositionStretch ResizeModeToStretch(); + winrt::Windows::UI::Composition::CompositionSurfaceBrush GetOrCreateSurfaceBrush(); + winrt::Windows::UI::Composition::CompositionEffectBrush GetOrCreateEffectBrush(winrt::Windows::UI::Composition::CompositionSurfaceBrush const& surfaceBrush); + + react::uwp::ResizeMode m_resizeMode{ ResizeMode::Contain }; + winrt::Windows::Foundation::Size m_availableSize{ }; + winrt::Windows::UI::Xaml::Media::LoadedImageSurface m_loadedImageSurface{ nullptr }; + winrt::Windows::UI::Composition::CompositionEffectBrush m_effectBrush{ nullptr }; + }; + } +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/ImageViewManager.cpp b/vnext/ReactUWP/Views/ImageViewManager.cpp deleted file mode 100644 index 28509fbc908..00000000000 --- a/vnext/ReactUWP/Views/ImageViewManager.cpp +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// NYI: -// implement image cache -// implement prefetch functionality -// implement multi source (parse out most suitable image source from array of sources) - -#include "pch.h" - -#include "ImageViewManager.h" - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "unicode.h" - -#include -#include -#include -#include - -#include "Utils/PropertyHandlerUtils.h" - -#include - -#if _MSC_VER <= 1913 -// VC 19 (2015-2017.6) cannot optimize co_await/cppwinrt usage -#pragma optimize( "", off ) -#endif - -namespace winrt { -using namespace Windows::Foundation; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::UI::Xaml::Media; -using namespace Windows::UI::Xaml::Media::Imaging; -} - -namespace react { - namespace uwp { - struct ImageSource - { - std::string uri; - std::string method; - folly::dynamic headers; - double width = 0; - double height = 0; - double scale = 1.0; - bool packagerAsset = false; - }; -} } - -// Such code is better to move to a seperate parser layer -template<> -struct json_type_traits -{ - static react::uwp::ImageSource parseJson(const folly::dynamic& json) - { - react::uwp::ImageSource source; - for (auto& item : json.items()) - { - if (item.first == "uri") - source.uri = item.second.asString(); - else if (item.first == "method") - source.method = item.second.asString(); - else if (item.first == "headers") - source.headers = item.second; - else if (item.first == "width") - source.width = item.second.asDouble(); - else if (item.first == "height") - source.height = item.second.asDouble(); - else if (item.first == "scale") - source.scale = item.second.asDouble(); - else if (item.first == "__packager_asset") - source.packagerAsset = item.second.asBool(); - } - return source; - } -}; - - -template<> -struct json_type_traits -{ - static winrt::Windows::UI::Xaml::Media::Stretch parseJson(const folly::dynamic& json) - { - winrt::Windows::UI::Xaml::Media::Stretch stretch; - if (json == "cover") - { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill; - } - else if (json == "contain") - { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::Uniform; - } - else - { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::Fill; - } - - return stretch; - } -}; - - -namespace react { namespace uwp { - - ImageViewManager::ImageViewManager(const std::shared_ptr& reactInstance) - : Super(reactInstance) - { - } - - const char* ImageViewManager::GetName() const - { - return "RCTImageView"; - } - - XamlView ImageViewManager::CreateViewCore(int64_t tag) - { - winrt::Image image; - image.Stretch(winrt::Stretch::UniformToFill); - return image; - } - - void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) - { - auto image = nodeToUpdate->GetView().as(); - if (image == nullptr) - return; - - for (const auto& pair : reactDiffMap.items()) - { - const std::string& propertyName = pair.first.getString(); - const folly::dynamic& propertyValue = pair.second; - - if (propertyName == "source") - { - setSource(image, propertyValue); - } - else if (propertyName == "resizeMode") - { - auto stretch = json_type_traits::parseJson(propertyValue); - image.Stretch(stretch); - } - - // TODO: overflow - } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); - } - - void EmitImageEvent(const std::shared_ptr &reactInstance, winrt::Image image, const char* eventName, ImageSource& source) - { - if (reactInstance == nullptr) - return; - - int64_t tag = image.Tag().as().GetInt64(); - folly::dynamic imageSource = folly::dynamic::object() - ("url", source.uri) - ("width", source.width) - ("height", source.height); - - folly::dynamic eventData = folly::dynamic::object() - ("target", tag) - ("source", imageSource); - reactInstance->DispatchEvent(tag, eventName, std::move(eventData)); - } - - std::future DownloadImageAsync(winrt::Image image, ImageSource source, std::weak_ptr instanceWeak) - { - // Since this is a fire and forget function (not waited upon when called), we need to wrap in a - // try/catch or uncaught exceptions will take down the process - try - { - winrt::Windows::Web::Http::HttpClient httpClient; - winrt::Windows::Web::Http::HttpMethod httpMethod(nullptr); - if (source.method.empty()) - httpMethod = winrt::Windows::Web::Http::HttpMethod::Get(); - else - httpMethod = winrt::Windows::Web::Http::HttpMethod(facebook::react::unicode::utf8ToUtf16(source.method)); - winrt::Windows::Foundation::Uri uri(facebook::react::unicode::utf8ToUtf16(source.uri)); - - winrt::Windows::Web::Http::HttpRequestMessage request(httpMethod, uri); - - if (!source.headers.empty()) - { - for (auto& header : source.headers.items()) - { - auto& name = header.first.getString(); - auto& value = header.second.getString(); - if (_stricmp(name.c_str(), "authorization") == 0) - request.Headers().TryAppendWithoutValidation(facebook::react::unicode::utf8ToUtf16(name), facebook::react::unicode::utf8ToUtf16(value)); - else - request.Headers().Append(facebook::react::unicode::utf8ToUtf16(name), facebook::react::unicode::utf8ToUtf16(value)); - } - } - - winrt::Windows::Web::Http::HttpResponseMessage response = co_await httpClient.SendRequestAsync(request); - - auto instance = instanceWeak.lock(); - if (response.StatusCode() == winrt::Windows::Web::Http::HttpStatusCode::Ok) - { - winrt::Windows::Storage::Streams::IInputStream inputStream = co_await response.Content().ReadAsInputStreamAsync(); - winrt::Windows::Storage::Streams::InMemoryRandomAccessStream memoryStream; - co_await winrt::Windows::Storage::Streams::RandomAccessStream::CopyAsync(inputStream, memoryStream); - memoryStream.Seek(0); - - winrt::BitmapImage bitmap; - co_await bitmap.SetSourceAsync(memoryStream); - image.Source(bitmap); - - EmitImageEvent(instance, image, "topLoad", source); - } - else - { - EmitImageEvent(instance, image, "topError", source); - } - } - catch (winrt::hresult_error const &) - { - EmitImageEvent(instanceWeak.lock(), image, "topError", source); - } - - EmitImageEvent(instanceWeak.lock(), image, "topLoadEnd", source); - } - - void ImageViewManager::setSource(winrt::Image image, const folly::dynamic& data) - { - auto instance = m_wkReactInstance.lock(); - if (instance == nullptr) - return; - - auto sources = json_type_traits>::parseJson(data); - auto uriString = sources[0].uri; - if (uriString.length() == 0) - { - EmitImageEvent(instance, image, "topError", sources[0]); - return; - } - - if (sources[0].packagerAsset && uriString.find("file://") == 0) - uriString.replace(0, 7, "ms-appx:///Bundle/"); - - bool needsDownload = false; - - try - { - auto uri = winrt::Uri(winrt::hstring(asWStr(uriString).c_str())); - auto scheme = uri.SchemeName(); - needsDownload = scheme == L"http" || scheme == L"https"; - - EmitImageEvent(instance, image, "topLoadStart", sources[0]); - if (needsDownload) - { - // FUTURE: This should get a weak_ptr from instance - // fix when the win32 instance merge happens - DownloadImageAsync(image, sources[0], instance); - } - else - { - winrt::BitmapImage bitmap; - bitmap.UriSource(uri); - image.Source(bitmap); - - EmitImageEvent(instance, image, "topLoad", sources[0]); - } - } - catch (...) - { - OutputDebugString(L"caught exception setting up image source"); - EmitImageEvent(instance, image, "topError", sources[0]); - } - - if (!needsDownload) - EmitImageEvent(instance, image, "topLoadEnd", sources[0]); - } - - folly::dynamic ImageViewManager::GetExportedCustomDirectEventTypeConstants() const - { - auto directEvents = Super::GetExportedCustomDirectEventTypeConstants(); - directEvents["topLoadStart"] = folly::dynamic::object("registrationName", "onLoadStart"); - directEvents["topLoad"] = folly::dynamic::object("registrationName", "onLoad"); - directEvents["topLoadEnd"] = folly::dynamic::object("registrationName", "onLoadEnd"); - directEvents["topError"] = folly::dynamic::object("registrationName", "onError"); - - return directEvents; - } - - folly::dynamic ImageViewManager::GetNativeProps() const { - auto props = Super::GetNativeProps(); - // TODO: implement native props propagation from property map - props.update(folly::dynamic::object - ("source", "Map") - ("resizeMode", "string") - ("accessibilityLabel", "string") - ); - - return props; - } - -// -// ImageViewManagerModule::ImageViewManagerModuleImpl -// -class ImageViewManagerModule::ImageViewManagerModuleImpl -{ -public: - ImageViewManagerModuleImpl(ImageViewManagerModule* parent, const std::shared_ptr& defaultQueueThread) - : m_parent(parent) - , m_queueThread(defaultQueueThread) - { } - - void Disconnect() - { - m_parent = nullptr; - } - - void getSize(std::string uri, Callback successCallback, Callback errorCallback); - void prefetchImage(std::string uri, Callback successCallback, Callback errorCallback); - void queryCache(const folly::dynamic& requests, Callback successCallback, Callback errorCallback); - -private: - ImageViewManagerModule *m_parent; - std::shared_ptr m_queueThread; -}; - -winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::module::CxxModule::Callback successCallback, facebook::xplat::module::CxxModule::Callback errorCallback) -{ - winrt::Image image; - ImageSource source; - source.uri = uri; - - winrt::apartment_context ui_thread; - - bool succeeded = false; - try - { - co_await DownloadImageAsync(image, source, std::weak_ptr()); - - // Get back to the UI-thread to read properties on Image - co_await ui_thread; - auto bitmap = image.Source().try_as(); - if (bitmap) - { - successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); - succeeded = true; - } - } - catch (winrt::hresult_error const &) - { - } - - if (!succeeded) - errorCallback({}); - - co_return; -} - - -void ImageViewManagerModule::ImageViewManagerModuleImpl::getSize(std::string uri, Callback successCallback, Callback errorCallback) -{ - GetImageSizeAsync(uri, successCallback, errorCallback); -} - -void ImageViewManagerModule::ImageViewManagerModuleImpl::prefetchImage(std::string /*uri*/, Callback successCallback, Callback /*errorCallback*/) -{ - // NotYetImplemented - successCallback({}); -} - -void ImageViewManagerModule::ImageViewManagerModuleImpl::queryCache(const folly::dynamic& requests, Callback successCallback, Callback /*errorCallback*/) -{ - // NotYetImplemented - successCallback({ folly::dynamic::object() }); -} - - -// -// ImageViewManagerModule -// -const char* ImageViewManagerModule::name = "ImageViewManager"; - -ImageViewManagerModule::ImageViewManagerModule(const std::shared_ptr& defaultQueueThread) - : m_imageViewManagerModule(std::make_shared(this, defaultQueueThread)) -{ -} - -ImageViewManagerModule::~ImageViewManagerModule() -{ -} - -std::string ImageViewManagerModule::getName() -{ - return name; -} - -std::map ImageViewManagerModule::getConstants() -{ - return { {} }; -} - -auto ImageViewManagerModule::getMethods() -> std::vector -{ - std::shared_ptr imageViewManager(m_imageViewManagerModule); - return { - Method("getSize", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) - { - imageViewManager->getSize(facebook::xplat::jsArgAsString(args, 0), successCallback, errorCallback); - }, AsyncTag), - Method("prefetchImage", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) - { - imageViewManager->prefetchImage(facebook::xplat::jsArgAsString(args, 0), successCallback, errorCallback); - }), - Method("queryCache", [imageViewManager](folly::dynamic args, Callback successCallback, Callback errorCallback) - { - imageViewManager->queryCache(args[0], successCallback, errorCallback); - }), - }; -} - - -} } diff --git a/vnext/ReactUWP/Views/ImageViewManager.h b/vnext/ReactUWP/Views/ImageViewManager.h deleted file mode 100644 index 2fc3369f024..00000000000 --- a/vnext/ReactUWP/Views/ImageViewManager.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include - -namespace winrt::Windows::UI::Xaml::Media { enum class Stretch; } - -namespace facebook { namespace react { - class MessageQueueThread; -} } - -namespace react { namespace uwp { - - class ImageViewManager : public FrameworkElementViewManager - { - using Super = FrameworkElementViewManager; - public: - ImageViewManager(const std::shared_ptr& reactInstance); - - const char* GetName() const override; - void UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) override; - - folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; - folly::dynamic GetNativeProps() const override; - - protected: - XamlView CreateViewCore(int64_t tag) override; - - private: - void setSource(winrt::Windows::UI::Xaml::Controls::Image image, const folly::dynamic& sources); - }; - - class ImageViewManagerModule : public facebook::xplat::module::CxxModule - { - public: - ImageViewManagerModule(const std::shared_ptr& defaultQueueThread); - virtual ~ImageViewManagerModule(); - - // CxxModule - std::string getName() override; - std::map getConstants() override; - auto getMethods() -> std::vector override; - - static const char* name; - - private: - class ImageViewManagerModuleImpl; - std::shared_ptr m_imageViewManagerModule; - }; - -} } diff --git a/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.BorderEffect.g.h b/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.BorderEffect.g.h new file mode 100644 index 00000000000..b2f6cc4eae1 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.BorderEffect.g.h @@ -0,0 +1,53 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +#include "winrt/Windows.Graphics.Effects.h" +#include "winrt/Microsoft.UI.Composition.Effects.h" + +namespace winrt::Microsoft::UI::Composition::Effects::implementation { + +template +struct WINRT_EBO BorderEffect_base : implements +{ + using base_type = BorderEffect_base; + using class_type = Microsoft::UI::Composition::Effects::BorderEffect; + using implements_type = typename BorderEffect_base::implements_type; + using implements_type::implements_type; + +#if _MSC_VER < 1914 + operator class_type() const noexcept + { + static_assert(std::is_same_v::type, default_interface>); + class_type result{ nullptr }; + attach_abi(result, detach_abi(static_cast>(*this))); + return result; + } +#else + operator impl::producer_ref const() const noexcept + { + return { to_abi>(this) }; + } +#endif + + hstring GetRuntimeClassName() const + { + return L"Microsoft.UI.Composition.Effects.BorderEffect"; + } +}; + +} + +#if defined(WINRT_FORCE_INCLUDE_BORDEREFFECT_XAML_G_H) || __has_include("Microsoft.UI.Composition.Effects.BorderEffect.xaml.g.h") + +#include "Microsoft.UI.Composition.Effects.BorderEffect.xaml.g.h" + +#else + +namespace winrt::Microsoft::UI::Composition::Effects::implementation +{ + template + using BorderEffectT = BorderEffect_base; +} + +#endif diff --git a/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.idl b/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.idl new file mode 100644 index 00000000000..d0513a1cfbe --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.idl @@ -0,0 +1,31 @@ +namespace Microsoft.UI.Composition.Effects +{ + [version(1)] + typedef enum CanvasEdgeBehavior + { + Clamp = (int)0, + Wrap = (int)1, + Mirror = (int)2 + } CanvasEdgeBehavior; + + runtimeclass BorderEffect; + + [version(1)] + [uuid(31602441-15db-5b4a-98dd-ba4247548b40), exclusiveto(BorderEffect)] + interface IBorderEffect : IInspectable + { + [propget] HRESULT ExtendX([out, retval] Microsoft.UI.Composition.Effects.CanvasEdgeBehavior* value); + [propput] HRESULT ExtendX([in] Microsoft.UI.Composition.Effects.CanvasEdgeBehavior value); + + [propget] HRESULT ExtendY([out, retval] Microsoft.UI.Composition.Effects.CanvasEdgeBehavior* value); + [propput] HRESULT ExtendY([in] Microsoft.UI.Composition.Effects.CanvasEdgeBehavior value); + + [propget] HRESULT Source([out, retval] Windows.Graphics.Effects.IGraphicsEffectSource** source); + [propput] HRESULT Source([in] Windows.Graphics.Effects.IGraphicsEffectSource* source); + }; + + runtimeclass BorderEffect + : [default] IBorderEffect, Windows.Graphics.Effects.IGraphicsEffect + { + } +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h new file mode 100644 index 00000000000..74807d7bbb1 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h @@ -0,0 +1,352 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +#include "winrt/base.h" + +#ifndef OLD_CPPWINRT +static_assert(winrt::check_version(CPPWINRT_VERSION, "1.0.180821.2"), "Mismatched component and base headers."); +#endif +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/impl/Windows.Graphics.Effects.2.h" +#include "winrt/impl/Microsoft.UI.Composition.Effects.2.h" + +namespace winrt::impl { + +template Microsoft::UI::Composition::Effects::CanvasEdgeBehavior consume_Microsoft_UI_Composition_Effects_IBorderEffect::ExtendX() const +{ + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value{}; + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->get_ExtendX(put_abi(value))); + return value; +} + +template void consume_Microsoft_UI_Composition_Effects_IBorderEffect::ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const& value) const +{ + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->put_ExtendX(get_abi(value))); +} + +template Microsoft::UI::Composition::Effects::CanvasEdgeBehavior consume_Microsoft_UI_Composition_Effects_IBorderEffect::ExtendY() const +{ + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value{}; + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->get_ExtendY(put_abi(value))); + return value; +} + +template void consume_Microsoft_UI_Composition_Effects_IBorderEffect::ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const& value) const +{ + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->put_ExtendY(get_abi(value))); +} + +template Windows::Graphics::Effects::IGraphicsEffectSource consume_Microsoft_UI_Composition_Effects_IBorderEffect::Source() const +{ + Windows::Graphics::Effects::IGraphicsEffectSource source{ nullptr }; + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->get_Source(put_abi(source))); + return source; +} + +template void consume_Microsoft_UI_Composition_Effects_IBorderEffect::Source(Windows::Graphics::Effects::IGraphicsEffectSource const& source) const +{ + check_hresult(WINRT_SHIM(Microsoft::UI::Composition::Effects::IBorderEffect)->put_Source(get_abi(source))); +} + +template +struct produce : produce_base +{ + int32_t WINRT_CALL get_ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior* value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ExtendX, WINRT_WRAP(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior)); + *value = detach_from(this->shim().ExtendX()); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL put_ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ExtendX, WINRT_WRAP(void), Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const&); + this->shim().ExtendX(*reinterpret_cast(&value)); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL get_ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior* value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ExtendY, WINRT_WRAP(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior)); + *value = detach_from(this->shim().ExtendY()); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL put_ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ExtendY, WINRT_WRAP(void), Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const&); + this->shim().ExtendY(*reinterpret_cast(&value)); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL get_Source(void** source) noexcept final + { + try + { + *source = nullptr; + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(Source, WINRT_WRAP(Windows::Graphics::Effects::IGraphicsEffectSource)); + *source = detach_from(this->shim().Source()); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL put_Source(void* source) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(Source, WINRT_WRAP(void), Windows::Graphics::Effects::IGraphicsEffectSource const&); + this->shim().Source(*reinterpret_cast(&source)); + return 0; + } + catch (...) { return to_hresult(); } + } +}; + +} + +WINRT_EXPORT namespace winrt::Microsoft::UI::Composition::Effects { + +} + +namespace winrt::impl { + +struct property_Microsoft_UI_Composition_Effects_IBorderEffect +{ struct named { + struct ExtendX + { + struct name { static constexpr std::wstring_view value{ L"ExtendX"sv }; }; + using property_type = winrt::Microsoft::UI::Composition::Effects::CanvasEdgeBehavior; + using target_type = winrt::Microsoft::UI::Composition::Effects::IBorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.ExtendX(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ExtendX(std::forward(value)); + } + }; + }; + struct ExtendY + { + struct name { static constexpr std::wstring_view value{ L"ExtendY"sv }; }; + using property_type = winrt::Microsoft::UI::Composition::Effects::CanvasEdgeBehavior; + using target_type = winrt::Microsoft::UI::Composition::Effects::IBorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.ExtendY(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ExtendY(std::forward(value)); + } + }; + }; + struct Source + { + struct name { static constexpr std::wstring_view value{ L"Source"sv }; }; + using property_type = winrt::Windows::Graphics::Effects::IGraphicsEffectSource; + using target_type = winrt::Microsoft::UI::Composition::Effects::IBorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.Source(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.Source(std::forward(value)); + } + }; + };}; + struct list { using type = impl::typelist; }; +}; + +struct property_Microsoft_UI_Composition_Effects_BorderEffect +{ struct named { + struct Source + { + struct name { static constexpr std::wstring_view value{ L"Source"sv }; }; + using property_type = winrt::Windows::Graphics::Effects::IGraphicsEffectSource; + using target_type = winrt::Microsoft::UI::Composition::Effects::BorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.Source(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.Source(std::forward(value)); + } + }; + }; + struct ExtendY + { + struct name { static constexpr std::wstring_view value{ L"ExtendY"sv }; }; + using property_type = winrt::Microsoft::UI::Composition::Effects::CanvasEdgeBehavior; + using target_type = winrt::Microsoft::UI::Composition::Effects::BorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.ExtendY(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ExtendY(std::forward(value)); + } + }; + }; + struct ExtendX + { + struct name { static constexpr std::wstring_view value{ L"ExtendX"sv }; }; + using property_type = winrt::Microsoft::UI::Composition::Effects::CanvasEdgeBehavior; + using target_type = winrt::Microsoft::UI::Composition::Effects::BorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.ExtendX(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ExtendX(std::forward(value)); + } + }; + }; + struct Name + { + struct name { static constexpr std::wstring_view value{ L"Name"sv }; }; + using property_type = winrt::hstring; + using target_type = winrt::Microsoft::UI::Composition::Effects::BorderEffect; + + using is_readable = std::true_type; + using is_writable = std::true_type; + using is_static = std::false_type; + struct getter + { + auto operator()(target_type const& target) const + { + return target.Name(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.Name(std::forward(value)); + } + }; + };}; + struct list { using type = impl::typelist; }; +}; + +} + +WINRT_EXPORT namespace winrt::experimental::reflect { +template <> struct named_property : impl::property_Microsoft_UI_Composition_Effects_IBorderEffect::named {}; +template <> struct properties : impl::property_Microsoft_UI_Composition_Effects_IBorderEffect::list {}; +template <> struct named_property : impl::property_Microsoft_UI_Composition_Effects_BorderEffect::named {}; +template <> struct properties : impl::property_Microsoft_UI_Composition_Effects_BorderEffect::list {}; +template <> struct get_enumerator_names +{ + static constexpr std::array value{{ + {L"Clamp", 5}, + {L"Wrap", 4}, + {L"Mirror", 6}, }}; +}; +template <> struct get_enumerator_values +{ + static constexpr std::array value{{ + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior::Clamp, + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior::Wrap, + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior::Mirror, }}; +}; + +} + +WINRT_EXPORT namespace std { + +template<> struct hash : winrt::impl::hash_base {}; +template<> struct hash : winrt::impl::hash_base {}; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.0.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.0.h new file mode 100644 index 00000000000..e9170387437 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.0.h @@ -0,0 +1,58 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +WINRT_EXPORT namespace winrt::Windows::Graphics::Effects { + +struct IGraphicsEffectSource; + +} + +WINRT_EXPORT namespace winrt::Microsoft::UI::Composition::Effects { + +enum class CanvasEdgeBehavior : int32_t +{ + Clamp = 0, + Wrap = 1, + Mirror = 2, +}; + +struct IBorderEffect; +struct BorderEffect; + +} + +namespace winrt::impl { + +template <> struct category{ using type = interface_category; }; +template <> struct category{ using type = class_category; }; +template <> struct category{ using type = enum_category; }; +template <> struct name{ static constexpr auto & value{ L"Microsoft.UI.Composition.Effects.IBorderEffect" }; }; +template <> struct name{ static constexpr auto & value{ L"Microsoft.UI.Composition.Effects.BorderEffect" }; }; +template <> struct name{ static constexpr auto & value{ L"Microsoft.UI.Composition.Effects.CanvasEdgeBehavior" }; }; +template <> struct guid_storage{ static constexpr guid value{ 0x31602441,0x15DB,0x5B4A,{ 0x98,0xDD,0xBA,0x42,0x47,0x54,0x8B,0x40 } }; }; +template <> struct default_interface{ using type = Microsoft::UI::Composition::Effects::IBorderEffect; }; + +template <> struct abi{ struct type : IInspectable +{ + virtual int32_t WINRT_CALL get_ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior* value) noexcept = 0; + virtual int32_t WINRT_CALL put_ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value) noexcept = 0; + virtual int32_t WINRT_CALL get_ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior* value) noexcept = 0; + virtual int32_t WINRT_CALL put_ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior value) noexcept = 0; + virtual int32_t WINRT_CALL get_Source(void** source) noexcept = 0; + virtual int32_t WINRT_CALL put_Source(void* source) noexcept = 0; +};}; + +template +struct consume_Microsoft_UI_Composition_Effects_IBorderEffect +{ + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior ExtendX() const; + void ExtendX(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const& value) const; + Microsoft::UI::Composition::Effects::CanvasEdgeBehavior ExtendY() const; + void ExtendY(Microsoft::UI::Composition::Effects::CanvasEdgeBehavior const& value) const; + Windows::Graphics::Effects::IGraphicsEffectSource Source() const; + void Source(Windows::Graphics::Effects::IGraphicsEffectSource const& source) const; +}; +template <> struct consume { template using type = consume_Microsoft_UI_Composition_Effects_IBorderEffect; }; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.1.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.1.h new file mode 100644 index 00000000000..e1937a49e98 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.1.h @@ -0,0 +1,16 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once +#include "winrt/impl/Windows.Graphics.Effects.0.h" +#include "winrt/impl/Microsoft.UI.Composition.Effects.0.h" + +WINRT_EXPORT namespace winrt::Microsoft::UI::Composition::Effects { + +struct WINRT_EBO IBorderEffect : + Windows::Foundation::IInspectable, + impl::consume_t +{ + IBorderEffect(std::nullptr_t = nullptr) noexcept {} +}; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.2.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.2.h new file mode 100644 index 00000000000..402bcd08ece --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.2.h @@ -0,0 +1,24 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once +#include "winrt/impl/Windows.Graphics.Effects.1.h" +#include "winrt/impl/Microsoft.UI.Composition.Effects.1.h" + +WINRT_EXPORT namespace winrt::Microsoft::UI::Composition::Effects { + +} + +namespace winrt::impl { + +} + +WINRT_EXPORT namespace winrt::Microsoft::UI::Composition::Effects { + +struct WINRT_EBO BorderEffect : + Microsoft::UI::Composition::Effects::IBorderEffect, + impl::require +{ + BorderEffect(std::nullptr_t) noexcept {} +}; + +}