From bdf4bcf9d710711f9f01721b99b158de231322af Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Mon, 3 Jun 2019 19:38:24 -0700 Subject: [PATCH 01/10] Move ImageViewManager to its own folder --- vnext/ReactUWP/Base/UwpReactInstance.cpp | 2 +- vnext/ReactUWP/ReactUWP.vcxproj | 4 ++-- vnext/ReactUWP/ReactUWP.vcxproj.filters | 15 +++++++++------ .../Views/{ => Image}/ImageViewManager.cpp | 0 .../ReactUWP/Views/{ => Image}/ImageViewManager.h | 0 5 files changed, 12 insertions(+), 9 deletions(-) rename vnext/ReactUWP/Views/{ => Image}/ImageViewManager.cpp (100%) rename vnext/ReactUWP/Views/{ => Image}/ImageViewManager.h (100%) diff --git a/vnext/ReactUWP/Base/UwpReactInstance.cpp b/vnext/ReactUWP/Base/UwpReactInstance.cpp index 88c1a7cf030..b191ba778c8 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 diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index 05ff6ac374a..48b82112dd6 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -194,7 +194,7 @@ - + @@ -268,7 +268,7 @@ - + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index 9e4eaba7e32..b096155f4eb 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -91,9 +91,6 @@ Views - - Views - Views @@ -216,6 +213,9 @@ Views + + Views\Image + @@ -305,9 +305,6 @@ ABI - - Views - Views @@ -432,6 +429,9 @@ Views + + Views\Image + @@ -560,6 +560,9 @@ {101c2d4a-d343-4771-969f-29af0c9f46b2} + + {6d877886-1c5c-45b8-8eae-2e1354cf673e} + diff --git a/vnext/ReactUWP/Views/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp similarity index 100% rename from vnext/ReactUWP/Views/ImageViewManager.cpp rename to vnext/ReactUWP/Views/Image/ImageViewManager.cpp diff --git a/vnext/ReactUWP/Views/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h similarity index 100% rename from vnext/ReactUWP/Views/ImageViewManager.h rename to vnext/ReactUWP/Views/Image/ImageViewManager.h From 55f101d44a0ff1587da247086768e246e7f7a786 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Mon, 3 Jun 2019 20:56:38 -0700 Subject: [PATCH 02/10] Add BorderEffect impl --- vnext/ReactUWP/ReactUWP.vcxproj | 7 + vnext/ReactUWP/ReactUWP.vcxproj.filters | 48 ++- vnext/ReactUWP/Views/Image/BorderEffect.h | 218 +++++++++++ .../ReactUWP/Views/Image/ImageViewManager.cpp | 11 +- ...ft.UI.Composition.Effects.BorderEffect.g.h | 53 +++ .../winrt/Microsoft.UI.Composition.Effects.h | 351 ++++++++++++++++++ .../impl/Microsoft.UI.Composition.Effects.0.h | 58 +++ .../impl/Microsoft.UI.Composition.Effects.1.h | 16 + .../impl/Microsoft.UI.Composition.Effects.2.h | 24 ++ 9 files changed, 770 insertions(+), 16 deletions(-) create mode 100644 vnext/ReactUWP/Views/Image/BorderEffect.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.BorderEffect.g.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.0.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.1.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/Microsoft.UI.Composition.Effects.2.h diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index 48b82112dd6..ee9edc3aefd 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -89,6 +89,7 @@ true DebugFull -minpdbpathlen:256 + dxguid.lib;%(AdditionalDependencies) $(ProjectDir)ABI\idl @@ -187,13 +188,19 @@ + + + + + + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index b096155f4eb..42d86fd2277 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -404,18 +404,6 @@ Views\cppwinrt - - Views\cppwinrt - - - Views\cppwinrt - - - Views\cppwinrt - - - Views\cppwinrt - Views @@ -432,6 +420,36 @@ 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 + @@ -563,6 +581,12 @@ {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..d3050872c0c --- /dev/null +++ b/vnext/ReactUWP/Views/Image/BorderEffect.h @@ -0,0 +1,218 @@ +#pragma once +#include + +#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); \ + } + + //----------------------------------------------------------------------------------------------------------------- + + 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 index 28509fbc908..916a3d157bc 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -36,16 +36,19 @@ #include +#include "BorderEffect.h" + #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; + using namespace Microsoft::UI::Composition::Effects; + 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 { 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/winrt/Microsoft.UI.Composition.Effects.h b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h new file mode 100644 index 00000000000..c06e5a8c4e6 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h @@ -0,0 +1,351 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +#include "winrt/base.h" + + +static_assert(winrt::check_version(CPPWINRT_VERSION, "1.0.180821.2"), "Mismatched component and base headers."); +#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 {} +}; + +} From 7869db208daf2943b2c60de76e243c37a5fb1dfc Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Tue, 4 Jun 2019 01:02:36 -0700 Subject: [PATCH 03/10] Switch from Image to Canvas --- .../ReactUWP/Views/Image/ImageViewManager.cpp | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index 916a3d157bc..94cb5441406 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -46,9 +46,12 @@ namespace winrt { using namespace Microsoft::UI::Composition::Effects; using namespace Windows::Foundation; + using namespace Windows::Storage::Streams; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media::Imaging; + using namespace Windows::Web::Http; + } namespace react { @@ -95,22 +98,22 @@ struct json_type_traits template<> -struct json_type_traits +struct json_type_traits { - static winrt::Windows::UI::Xaml::Media::Stretch parseJson(const folly::dynamic& json) + static winrt::Stretch parseJson(const folly::dynamic& json) { - winrt::Windows::UI::Xaml::Media::Stretch stretch; + winrt::Stretch stretch; if (json == "cover") { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill; + stretch = winrt::Stretch::UniformToFill; } else if (json == "contain") { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::Uniform; + stretch = winrt::Stretch::Uniform; } else { - stretch = winrt::Windows::UI::Xaml::Media::Stretch::Fill; + stretch = winrt::Stretch::Fill; } return stretch; @@ -132,15 +135,14 @@ namespace react { namespace uwp { XamlView ImageViewManager::CreateViewCore(int64_t tag) { - winrt::Image image; - image.Stretch(winrt::Stretch::UniformToFill); - return image; + winrt::Canvas canvas; + return canvas; } void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) { - auto image = nodeToUpdate->GetView().as(); - if (image == nullptr) + auto canvas = nodeToUpdate->GetView().as(); + if (canvas == nullptr) return; for (const auto& pair : reactDiffMap.items()) @@ -150,12 +152,12 @@ namespace react { namespace uwp { if (propertyName == "source") { - setSource(image, propertyValue); + setSource(canvas, propertyValue); } else if (propertyName == "resizeMode") { - auto stretch = json_type_traits::parseJson(propertyValue); - image.Stretch(stretch); + auto stretch = json_type_traits::parseJson(propertyValue); + // image.Stretch(stretch); } // TODO: overflow @@ -164,12 +166,12 @@ namespace react { namespace uwp { Super::UpdateProperties(nodeToUpdate, reactDiffMap); } - void EmitImageEvent(const std::shared_ptr &reactInstance, winrt::Image image, const char* eventName, ImageSource& source) + void EmitImageEvent(const std::shared_ptr &reactInstance, winrt::Canvas canvas, const char* eventName, ImageSource& source) { if (reactInstance == nullptr) return; - int64_t tag = image.Tag().as().GetInt64(); + int64_t tag = canvas.Tag().as().GetInt64(); folly::dynamic imageSource = folly::dynamic::object() ("url", source.uri) ("width", source.width) @@ -181,21 +183,23 @@ namespace react { namespace uwp { reactInstance->DispatchEvent(tag, eventName, std::move(eventData)); } - std::future DownloadImageAsync(winrt::Image image, ImageSource source, std::weak_ptr instanceWeak) + std::future DownloadImageAsync(winrt::Canvas canvas, 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); + winrt::HttpClient httpClient; + winrt::HttpMethod httpMethod(nullptr); + if (source.method.empty()) - httpMethod = winrt::Windows::Web::Http::HttpMethod::Get(); + httpMethod = winrt::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)); + httpMethod = winrt::HttpMethod(facebook::react::unicode::utf8ToUtf16(source.method)); + + winrt::Uri uri(facebook::react::unicode::utf8ToUtf16(source.uri)); - winrt::Windows::Web::Http::HttpRequestMessage request(httpMethod, uri); + winrt::HttpRequestMessage request(httpMethod, uri); if (!source.headers.empty()) { @@ -203,6 +207,7 @@ namespace react { namespace uwp { { 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 @@ -210,36 +215,42 @@ namespace react { namespace uwp { } } - winrt::Windows::Web::Http::HttpResponseMessage response = co_await httpClient.SendRequestAsync(request); + winrt::HttpResponseMessage response = co_await httpClient.SendRequestAsync(request); auto instance = instanceWeak.lock(); - if (response.StatusCode() == winrt::Windows::Web::Http::HttpStatusCode::Ok) + if (response.StatusCode() == winrt::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); + winrt::IInputStream inputStream = co_await response.Content().ReadAsInputStreamAsync(); + winrt::InMemoryRandomAccessStream memoryStream; + co_await winrt::RandomAccessStream::CopyAsync(inputStream, memoryStream); memoryStream.Seek(0); + // winrt::LoadedImageSurface().StartLoadFromStream(memoryStream); + winrt::BitmapImage bitmap; co_await bitmap.SetSourceAsync(memoryStream); - image.Source(bitmap); - EmitImageEvent(instance, image, "topLoad", source); + winrt::ImageBrush brush; + brush.ImageSource(bitmap); + + canvas.Background(brush); + + EmitImageEvent(instance, canvas, "topLoad", source); } else { - EmitImageEvent(instance, image, "topError", source); + EmitImageEvent(instance, canvas, "topError", source); } } catch (winrt::hresult_error const &) { - EmitImageEvent(instanceWeak.lock(), image, "topError", source); + EmitImageEvent(instanceWeak.lock(), canvas, "topError", source); } - EmitImageEvent(instanceWeak.lock(), image, "topLoadEnd", source); + EmitImageEvent(instanceWeak.lock(), canvas, "topLoadEnd", source); } - void ImageViewManager::setSource(winrt::Image image, const folly::dynamic& data) + void ImageViewManager::setSource(winrt::Canvas canvas, const folly::dynamic& data) { auto instance = m_wkReactInstance.lock(); if (instance == nullptr) @@ -249,7 +260,7 @@ namespace react { namespace uwp { auto uriString = sources[0].uri; if (uriString.length() == 0) { - EmitImageEvent(instance, image, "topError", sources[0]); + EmitImageEvent(instance, canvas, "topError", sources[0]); return; } @@ -264,30 +275,34 @@ namespace react { namespace uwp { auto scheme = uri.SchemeName(); needsDownload = scheme == L"http" || scheme == L"https"; - EmitImageEvent(instance, image, "topLoadStart", sources[0]); + EmitImageEvent(instance, canvas, "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); + DownloadImageAsync(canvas, sources[0], instance); } else { winrt::BitmapImage bitmap; bitmap.UriSource(uri); - image.Source(bitmap); - EmitImageEvent(instance, image, "topLoad", sources[0]); + winrt::ImageBrush brush; + brush.ImageSource(bitmap); + + canvas.Background(brush); + + EmitImageEvent(instance, canvas, "topLoad", sources[0]); } } catch (...) { OutputDebugString(L"caught exception setting up image source"); - EmitImageEvent(instance, image, "topError", sources[0]); + EmitImageEvent(instance, canvas, "topError", sources[0]); } if (!needsDownload) - EmitImageEvent(instance, image, "topLoadEnd", sources[0]); + EmitImageEvent(instance, canvas, "topLoadEnd", sources[0]); } folly::dynamic ImageViewManager::GetExportedCustomDirectEventTypeConstants() const @@ -340,7 +355,7 @@ class ImageViewManagerModule::ImageViewManagerModuleImpl winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::module::CxxModule::Callback successCallback, facebook::xplat::module::CxxModule::Callback errorCallback) { - winrt::Image image; + winrt::Canvas canvas; ImageSource source; source.uri = uri; @@ -349,15 +364,17 @@ winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::modul bool succeeded = false; try { - co_await DownloadImageAsync(image, source, std::weak_ptr()); + co_await DownloadImageAsync(canvas, 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) + if (auto brush = canvas.Background().try_as()) { - successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); - succeeded = true; + if (auto bitmap = brush.ImageSource().try_as()) + { + successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); + succeeded = true; + } } } catch (winrt::hresult_error const &) From ccf5a68b67c713c43fa2b578da729835c7ac42a8 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Tue, 4 Jun 2019 02:10:07 -0700 Subject: [PATCH 04/10] Add ReactImageBrush (name TBD) --- vnext/ReactUWP/ReactUWP.vcxproj | 7 + vnext/ReactUWP/ReactUWP.vcxproj.filters | 21 ++ .../ReactUWP/Views/Image/ImageViewManager.cpp | 48 ++-- vnext/ReactUWP/Views/Image/ImageViewManager.h | 2 +- .../ReactUWP/Views/Image/ReactImageBrush.cpp | 152 +++++++++++ vnext/ReactUWP/Views/Image/ReactImageBrush.h | 41 +++ .../react.uwp.image.ReactImageBrush.g.h | 83 ++++++ .../cppwinrt/winrt/impl/react.uwp.image.0.h | 56 ++++ .../cppwinrt/winrt/impl/react.uwp.image.1.h | 19 ++ .../cppwinrt/winrt/impl/react.uwp.image.2.h | 29 ++ .../Views/cppwinrt/winrt/react.uwp.image.h | 254 ++++++++++++++++++ 11 files changed, 693 insertions(+), 19 deletions(-) create mode 100644 vnext/ReactUWP/Views/Image/ReactImageBrush.cpp create mode 100644 vnext/ReactUWP/Views/Image/ReactImageBrush.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h create mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index ee9edc3aefd..d80a3c2136e 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -190,18 +190,24 @@ + + + + + + @@ -276,6 +282,7 @@ + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index 42d86fd2277..bca349268dd 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -216,6 +216,9 @@ Views\Image + + Views\Image + @@ -450,6 +453,24 @@ Views\Image + + Views\cppwinrt + + + Views\cppwinrt\winrt + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\cppwinrt\winrt\impl + + + Views\Image + diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index 94cb5441406..1bb4af22dda 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -36,7 +36,7 @@ #include -#include "BorderEffect.h" +#include "ReactImageBrush.h" #if _MSC_VER <= 1913 // VC 19 (2015-2017.6) cannot optimize co_await/cppwinrt usage @@ -44,14 +44,13 @@ #endif namespace winrt { - using namespace Microsoft::UI::Composition::Effects; + using namespace winrt::react::uwp::image; using namespace Windows::Foundation; using namespace Windows::Storage::Streams; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::Web::Http; - } namespace react { @@ -98,25 +97,33 @@ struct json_type_traits template<> -struct json_type_traits +struct json_type_traits { - static winrt::Stretch parseJson(const folly::dynamic& json) + static winrt::ResizeMode parseJson(const folly::dynamic& json) { - winrt::Stretch stretch; + winrt::ResizeMode resizeMode; if (json == "cover") { - stretch = winrt::Stretch::UniformToFill; + resizeMode = winrt::ResizeMode::Cover; } else if (json == "contain") { - stretch = winrt::Stretch::Uniform; + resizeMode = winrt::ResizeMode::Contain; + } + else if (json == "stretch") + { + resizeMode = winrt::ResizeMode::Stretch; } - else + else if (json == "center") { - stretch = winrt::Stretch::Fill; + resizeMode = winrt::ResizeMode::Center; + } + else if (json == "repeat") + { + resizeMode = winrt::ResizeMode::Repeat; } - return stretch; + return resizeMode; } }; @@ -156,8 +163,9 @@ namespace react { namespace uwp { } else if (propertyName == "resizeMode") { - auto stretch = json_type_traits::parseJson(propertyValue); - // image.Stretch(stretch); + auto resizeMode = json_type_traits::parseJson(propertyValue); + auto brush = canvas.Background().as(); + brush.ResizeMode(resizeMode); } // TODO: overflow @@ -284,13 +292,17 @@ namespace react { namespace uwp { } else { - winrt::BitmapImage bitmap; - bitmap.UriSource(uri); + auto brush = winrt::make(); + brush.SourceUri(uri); + canvas.Background(brush); - winrt::ImageBrush brush; - brush.ImageSource(bitmap); + //winrt::BitmapImage bitmap; + //bitmap.UriSource(uri); - canvas.Background(brush); + //winrt::ImageBrush brush; + //brush.ImageSource(bitmap); + + //canvas.Background(brush); EmitImageEvent(instance, canvas, "topLoad", sources[0]); } diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h index 2fc3369f024..df38dd063c9 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.h +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.h @@ -29,7 +29,7 @@ namespace react { namespace uwp { XamlView CreateViewCore(int64_t tag) override; private: - void setSource(winrt::Windows::UI::Xaml::Controls::Image image, const folly::dynamic& sources); + void setSource(winrt::Windows::UI::Xaml::Controls::Canvas canvas, const folly::dynamic& sources); }; class ImageViewManagerModule : public facebook::xplat::module::CxxModule diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp new file mode 100644 index 00000000000..8632fb647a2 --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -0,0 +1,152 @@ +#include "pch.h" + +#include "BorderEffect.h" +#include "ReactImageBrush.h" +#include + +#include + +namespace winrt { + using namespace winrt::Windows::UI::Composition; + using namespace winrt::Windows::UI::Xaml; + using namespace winrt::Windows::UI::Xaml::Media; +} + +using namespace winrt::react::uwp; + +namespace winrt::react::uwp::image::implementation +{ + void ReactImageBrush::ResizeMode(image::ResizeMode value) + { + if (m_resizeMode != value) + { + m_resizeMode = value; + UpdateCompositionBrush(); + } + } + + void ReactImageBrush::OnConnected() + { + if (SourceUri()) { + UpdateCompositionBrush(); + } + } + + void ReactImageBrush::OnDisconnected() + { + // Dispose of composition resources when no longer in use. + if (CompositionBrush()) + { + CompositionBrush(nullptr); + } + } + + void ReactImageBrush::UpdateCompositionBrush() + { + winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; + surfaceBrush.Stretch(ResizeModeToStretch()); + + auto compositionBrush{ surfaceBrush.as() }; + if (ResizeMode() == image::ResizeMode::Repeat) + { + compositionBrush = GetOrCreateEffectBrush(surfaceBrush); + } + + // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat + if (ShouldSwitchCompositionBrush()) + { + CompositionBrush(compositionBrush); + } + } + + 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); + + auto effectFactory{ winrt::Window::Current().Compositor().CreateEffectFactory(borderEffect) }; + m_effectBrush = effectFactory.CreateBrush(); + m_effectBrush.SetSourceParameter(L"source", surfaceBrush); + } + + return m_effectBrush; + } + + winrt::CompositionSurfaceBrush ReactImageBrush::GetOrCreateSurfaceBrush() + { + // If it doesn't exist, create it + if (!CompositionBrush()) + { + auto surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; + auto loadedSurface{ winrt::LoadedImageSurface::StartLoadFromUri(SourceUri()) }; + + surfaceBrush.Surface(loadedSurface); + + return surfaceBrush; + } + + auto brush{ CompositionBrush().try_as() }; + + // If ResizeMode is set to Repeat, then we're using a CompositionEffectBrush. + // Get the CompositionSurfaceBrush from its source. + if (!brush) + { + auto effectBrush{ CompositionBrush().as() }; + brush = effectBrush.GetSourceParameter(L"source").as(); + } + + return brush; + } + + bool ReactImageBrush::ShouldSwitchCompositionBrush() + { + bool effectToSurfaceBrushSwitch = + ResizeMode() != image::ResizeMode::Repeat && + !CompositionBrush().try_as(); + + bool surfaceToEffectBrushSwitch = ResizeMode() == image::ResizeMode::Repeat; + + return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; + } + + bool ReactImageBrush::IsImageLargerThanView() + { + return true; + } + + winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() + { + auto stretch{ winrt::CompositionStretch::None }; + + switch (ResizeMode()) + { + case image::ResizeMode::Center: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; + + case image::ResizeMode::Contain: + stretch = winrt::CompositionStretch::Uniform; + break; + + case image::ResizeMode::Cover: + stretch = winrt::CompositionStretch::UniformToFill; + break; + + case image::ResizeMode::Stretch: + stretch = winrt::CompositionStretch::Fill; + break; + + case image::ResizeMode::Repeat: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; + } + + return stretch; + } +} diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h new file mode 100644 index 00000000000..5d0896e2d51 --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -0,0 +1,41 @@ +#pragma once +#include + +namespace winrt::react::uwp::image::implementation +{ + struct ReactImageBrush : ReactImageBrushT + { + ReactImageBrush() = default; + + react::uwp::image::ResizeMode ResizeMode() { return m_resizeMode; } + void ResizeMode(react::uwp::image::ResizeMode value); + + Windows::Foundation::Uri SourceUri() { return m_sourceUri; } + void SourceUri(Windows::Foundation::Uri const& value) { m_sourceUri = value; } + + // XamlCompositionBaseBrush + void OnConnected(); + void OnDisconnected(); + + private: + bool m_usingEffectBrush{ false }; + Windows::Foundation::Uri m_sourceUri{ nullptr }; + react::uwp::image::ResizeMode m_resizeMode{ react::uwp::image::ResizeMode::Contain }; + Windows::UI::Composition::CompositionEffectBrush m_effectBrush{ nullptr }; + + bool IsImageLargerThanView(); + bool ShouldSwitchCompositionBrush(); + void UpdateCompositionBrush(); + Windows::UI::Composition::CompositionStretch ResizeModeToStretch(); + Windows::UI::Composition::CompositionSurfaceBrush GetOrCreateSurfaceBrush(); + Windows::UI::Composition::CompositionEffectBrush GetOrCreateEffectBrush(Windows::UI::Composition::CompositionSurfaceBrush const& surfaceBrush); + + }; +} + +namespace winrt::react::uwp::image::factory_implementation +{ + struct ReactImageBrush : ReactImageBrushT + { + }; +} diff --git a/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h b/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h new file mode 100644 index 00000000000..8e2b6b2c624 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h @@ -0,0 +1,83 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.UI.Composition.h" +#include "winrt/Windows.UI.Xaml.h" +#include "winrt/Windows.UI.Xaml.Media.h" +#include "winrt/react.uwp.image.h" + +namespace winrt::react::uwp::image::implementation { + +template +struct WINRT_EBO ReactImageBrush_base : implements, + impl::require, + impl::base, + Windows::UI::Xaml::Media::IBrushOverrides2T, Windows::UI::Xaml::Media::IXamlCompositionBrushBaseOverridesT +{ + using base_type = ReactImageBrush_base; + using class_type = react::uwp::image::ReactImageBrush; + using implements_type = typename ReactImageBrush_base::implements_type; + using implements_type::implements_type; + using composable_base = Windows::UI::Xaml::Media::XamlCompositionBrushBase; +#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"react.uwp.image.ReactImageBrush"; + } + ReactImageBrush_base() + { + impl::call_factory([&](auto&& f) { f.CreateInstance(*this, this->m_inner); }); + } +}; + +} + +namespace winrt::react::uwp::image::factory_implementation { + +template +struct WINRT_EBO ReactImageBrushT : implements +{ + using instance_type = react::uwp::image::ReactImageBrush; + + hstring GetRuntimeClassName() const + { + return L"react.uwp.image.ReactImageBrush"; + } + + Windows::Foundation::IInspectable ActivateInstance() const + { + return make(); + } +}; + +} + +#if defined(WINRT_FORCE_INCLUDE_REACTIMAGEBRUSH_XAML_G_H) || __has_include("react.uwp.image.ReactImageBrush.xaml.g.h") + +#include "react.uwp.image.ReactImageBrush.xaml.g.h" + +#else + +namespace winrt::react::uwp::image::implementation +{ + template + using ReactImageBrushT = ReactImageBrush_base; +} + +#endif diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h new file mode 100644 index 00000000000..3995c35c0d8 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h @@ -0,0 +1,56 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +WINRT_EXPORT namespace winrt::Windows::Foundation { + +struct Uri; + +} + +WINRT_EXPORT namespace winrt::react::uwp::image { + +enum class ResizeMode : int32_t +{ + Cover = 0, + Contain = 1, + Stretch = 2, + Repeat = 3, + Center = 4, +}; + +struct IReactImageBrush; +struct ReactImageBrush; + +} + +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"react.uwp.image.IReactImageBrush" }; }; +template <> struct name{ static constexpr auto & value{ L"react.uwp.image.ReactImageBrush" }; }; +template <> struct name{ static constexpr auto & value{ L"react.uwp.image.ResizeMode" }; }; +template <> struct guid_storage{ static constexpr guid value{ 0x9672DBF3,0xAC79,0x5057,{ 0xBE,0x51,0x03,0xB3,0x79,0x9D,0x89,0xEC } }; }; +template <> struct default_interface{ using type = react::uwp::image::IReactImageBrush; }; + +template <> struct abi{ struct type : IInspectable +{ + virtual int32_t WINRT_CALL get_ResizeMode(react::uwp::image::ResizeMode* value) noexcept = 0; + virtual int32_t WINRT_CALL put_ResizeMode(react::uwp::image::ResizeMode value) noexcept = 0; + virtual int32_t WINRT_CALL get_SourceUri(void** value) noexcept = 0; + virtual int32_t WINRT_CALL put_SourceUri(void* value) noexcept = 0; +};}; + +template +struct consume_react_uwp_image_IReactImageBrush +{ + react::uwp::image::ResizeMode ResizeMode() const; + void ResizeMode(react::uwp::image::ResizeMode const& value) const; + Windows::Foundation::Uri SourceUri() const; + void SourceUri(Windows::Foundation::Uri const& value) const; +}; +template <> struct consume { template using type = consume_react_uwp_image_IReactImageBrush; }; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h new file mode 100644 index 00000000000..ce6d30efbd9 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h @@ -0,0 +1,19 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once +#include "winrt/impl/Windows.Foundation.0.h" +#include "winrt/impl/Windows.UI.Composition.0.h" +#include "winrt/impl/Windows.UI.Xaml.0.h" +#include "winrt/impl/Windows.UI.Xaml.Media.0.h" +#include "winrt/impl/react.uwp.image.0.h" + +WINRT_EXPORT namespace winrt::react::uwp::image { + +struct WINRT_EBO IReactImageBrush : + Windows::Foundation::IInspectable, + impl::consume_t +{ + IReactImageBrush(std::nullptr_t = nullptr) noexcept {} +}; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h new file mode 100644 index 00000000000..04a46a43ac1 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h @@ -0,0 +1,29 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once +#include "winrt/impl/Windows.Foundation.1.h" +#include "winrt/impl/Windows.UI.Composition.1.h" +#include "winrt/impl/Windows.UI.Xaml.1.h" +#include "winrt/impl/Windows.UI.Xaml.Media.1.h" +#include "winrt/impl/react.uwp.image.1.h" + +WINRT_EXPORT namespace winrt::react::uwp::image { + +} + +namespace winrt::impl { + +} + +WINRT_EXPORT namespace winrt::react::uwp::image { + +struct WINRT_EBO ReactImageBrush : + react::uwp::image::IReactImageBrush, + impl::base, + impl::require +{ + ReactImageBrush(std::nullptr_t) noexcept {} + ReactImageBrush(); +}; + +} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h b/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h new file mode 100644 index 00000000000..dd126f84f27 --- /dev/null +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h @@ -0,0 +1,254 @@ +// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 + +#pragma once + +#include "winrt/base.h" + + +static_assert(winrt::check_version(CPPWINRT_VERSION, "1.0.180821.2"), "Mismatched component and base headers."); +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/impl/Windows.Foundation.2.h" +#include "winrt/impl/Windows.UI.Composition.2.h" +#include "winrt/impl/Windows.UI.Xaml.2.h" +#include "winrt/impl/Windows.UI.Xaml.Media.2.h" +#include "winrt/impl/react.uwp.image.2.h" +#include "winrt/react.uwp.h" + +namespace winrt::impl { + +template react::uwp::image::ResizeMode consume_react_uwp_image_IReactImageBrush::ResizeMode() const +{ + react::uwp::image::ResizeMode value{}; + check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->get_ResizeMode(put_abi(value))); + return value; +} + +template void consume_react_uwp_image_IReactImageBrush::ResizeMode(react::uwp::image::ResizeMode const& value) const +{ + check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->put_ResizeMode(get_abi(value))); +} + +template Windows::Foundation::Uri consume_react_uwp_image_IReactImageBrush::SourceUri() const +{ + Windows::Foundation::Uri value{ nullptr }; + check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->get_SourceUri(put_abi(value))); + return value; +} + +template void consume_react_uwp_image_IReactImageBrush::SourceUri(Windows::Foundation::Uri const& value) const +{ + check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->put_SourceUri(get_abi(value))); +} + +template +struct produce : produce_base +{ + int32_t WINRT_CALL get_ResizeMode(react::uwp::image::ResizeMode* value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ResizeMode, WINRT_WRAP(react::uwp::image::ResizeMode)); + *value = detach_from(this->shim().ResizeMode()); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL put_ResizeMode(react::uwp::image::ResizeMode value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(ResizeMode, WINRT_WRAP(void), react::uwp::image::ResizeMode const&); + this->shim().ResizeMode(*reinterpret_cast(&value)); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL get_SourceUri(void** value) noexcept final + { + try + { + *value = nullptr; + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(SourceUri, WINRT_WRAP(Windows::Foundation::Uri)); + *value = detach_from(this->shim().SourceUri()); + return 0; + } + catch (...) { return to_hresult(); } + } + + int32_t WINRT_CALL put_SourceUri(void* value) noexcept final + { + try + { + typename D::abi_guard guard(this->shim()); + WINRT_ASSERT_DECLARATION(SourceUri, WINRT_WRAP(void), Windows::Foundation::Uri const&); + this->shim().SourceUri(*reinterpret_cast(&value)); + return 0; + } + catch (...) { return to_hresult(); } + } +}; + +} + +WINRT_EXPORT namespace winrt::react::uwp::image { + +inline ReactImageBrush::ReactImageBrush() : + ReactImageBrush(impl::call_factory([](auto&& f) { return f.template ActivateInstance(); })) +{} + +} + +namespace winrt::impl { + +struct property_react_uwp_image_IReactImageBrush +{ struct named { + struct ResizeMode + { + struct name { static constexpr std::wstring_view value{ L"ResizeMode"sv }; }; + using property_type = winrt::react::uwp::image::ResizeMode; + using target_type = winrt::react::uwp::image::IReactImageBrush; + + 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.ResizeMode(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ResizeMode(std::forward(value)); + } + }; + }; + struct SourceUri + { + struct name { static constexpr std::wstring_view value{ L"SourceUri"sv }; }; + using property_type = winrt::Windows::Foundation::Uri; + using target_type = winrt::react::uwp::image::IReactImageBrush; + + 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.SourceUri(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.SourceUri(std::forward(value)); + } + }; + };}; + struct list { using type = impl::typelist; }; +}; + +struct property_react_uwp_image_ReactImageBrush +{ struct named { + struct SourceUri + { + struct name { static constexpr std::wstring_view value{ L"SourceUri"sv }; }; + using property_type = winrt::Windows::Foundation::Uri; + using target_type = winrt::react::uwp::image::ReactImageBrush; + + 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.SourceUri(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.SourceUri(std::forward(value)); + } + }; + }; + struct ResizeMode + { + struct name { static constexpr std::wstring_view value{ L"ResizeMode"sv }; }; + using property_type = winrt::react::uwp::image::ResizeMode; + using target_type = winrt::react::uwp::image::ReactImageBrush; + + 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.ResizeMode(); + } + }; + struct setter + { + template + void operator()(target_type const& target, Value&& value) const + { + target.ResizeMode(std::forward(value)); + } + }; + };}; + struct list { using type = impl::typelist; }; +}; + +} + +WINRT_EXPORT namespace winrt::experimental::reflect { +template <> struct named_property : impl::property_react_uwp_image_IReactImageBrush::named {}; +template <> struct properties : impl::property_react_uwp_image_IReactImageBrush::list {}; +template <> struct named_property : impl::property_react_uwp_image_ReactImageBrush::named {}; +template <> struct properties : impl::property_react_uwp_image_ReactImageBrush::list {}; + +template <> +struct base_type { using type = Windows::UI::Xaml::Media::XamlCompositionBrushBase; };template <> struct get_enumerator_names +{ + static constexpr std::array value{{ + {L"Cover", 5}, + {L"Contain", 7}, + {L"Stretch", 7}, + {L"Repeat", 6}, + {L"Center", 6}, }}; +}; +template <> struct get_enumerator_values +{ + static constexpr std::array value{{ + react::uwp::image::ResizeMode::Cover, + react::uwp::image::ResizeMode::Contain, + react::uwp::image::ResizeMode::Stretch, + react::uwp::image::ResizeMode::Repeat, + react::uwp::image::ResizeMode::Center, }}; +}; + +} + +WINRT_EXPORT namespace std { + +template<> struct hash : winrt::impl::hash_base {}; +template<> struct hash : winrt::impl::hash_base {}; + +} From af2e5052fdbd69366b394e441c38631db4dbc3ba Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Tue, 4 Jun 2019 18:43:19 -0700 Subject: [PATCH 05/10] wip: couple of fixes --- .../ReactUWP/Views/Image/ImageViewManager.cpp | 23 ++++++-- .../ReactUWP/Views/Image/ReactImageBrush.cpp | 54 ++++++++++++------- vnext/ReactUWP/Views/Image/ReactImageBrush.h | 2 +- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index 1bb4af22dda..e8ed2833ffe 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -101,7 +101,8 @@ struct json_type_traits { static winrt::ResizeMode parseJson(const folly::dynamic& json) { - winrt::ResizeMode resizeMode; + auto resizeMode = winrt::ResizeMode::Contain; + if (json == "cover") { resizeMode = winrt::ResizeMode::Cover; @@ -142,16 +143,27 @@ namespace react { namespace uwp { XamlView ImageViewManager::CreateViewCore(int64_t tag) { + auto brush{ winrt::make() }; + winrt::Canvas canvas; + canvas.Background(brush); + return canvas; } void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) { auto canvas = nodeToUpdate->GetView().as(); + if (canvas == nullptr) return; + auto width{ canvas.Width() }; + auto heigth{ canvas.Height() }; + + auto actualWidth{ canvas.ActualWidth() }; + auto actualHeight{ canvas.ActualHeight() }; + for (const auto& pair : reactDiffMap.items()) { const std::string& propertyName = pair.first.getString(); @@ -286,13 +298,18 @@ namespace react { namespace uwp { EmitImageEvent(instance, canvas, "topLoadStart", sources[0]); if (needsDownload) { + auto brush{ canvas.Background().as() }; + brush.SourceUri(uri); + canvas.Background(brush); + // FUTURE: This should get a weak_ptr from instance // fix when the win32 instance merge happens - DownloadImageAsync(canvas, sources[0], instance); + // DownloadImageAsync(canvas, sources[0], instance); } else { - auto brush = winrt::make(); + // auto brush = winrt::make(); + auto brush{ canvas.Background().as() }; brush.SourceUri(uri); canvas.Background(brush); diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp index 8632fb647a2..07f65ce3c1d 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -16,6 +16,15 @@ using namespace winrt::react::uwp; namespace winrt::react::uwp::image::implementation { + void ReactImageBrush::SourceUri(Windows::Foundation::Uri const& value) + { + if (m_sourceUri != value) + { + m_sourceUri = value; + UpdateCompositionBrush(); + } + } + void ReactImageBrush::ResizeMode(image::ResizeMode value) { if (m_resizeMode != value) @@ -27,9 +36,7 @@ namespace winrt::react::uwp::image::implementation void ReactImageBrush::OnConnected() { - if (SourceUri()) { - UpdateCompositionBrush(); - } + UpdateCompositionBrush(); } void ReactImageBrush::OnDisconnected() @@ -43,19 +50,22 @@ namespace winrt::react::uwp::image::implementation void ReactImageBrush::UpdateCompositionBrush() { - winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; - surfaceBrush.Stretch(ResizeModeToStretch()); - - auto compositionBrush{ surfaceBrush.as() }; - if (ResizeMode() == image::ResizeMode::Repeat) + if (SourceUri()) { - compositionBrush = GetOrCreateEffectBrush(surfaceBrush); - } - - // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat - if (ShouldSwitchCompositionBrush()) - { - CompositionBrush(compositionBrush); + winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; + surfaceBrush.Stretch(ResizeModeToStretch()); + + auto compositionBrush{ surfaceBrush.as() }; + if (ResizeMode() == image::ResizeMode::Repeat) + { + compositionBrush = GetOrCreateEffectBrush(surfaceBrush); + } + + // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat + if (ShouldSwitchCompositionBrush()) + { + CompositionBrush(compositionBrush); + } } } @@ -72,6 +82,10 @@ namespace winrt::react::uwp::image::implementation auto 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); } @@ -84,6 +98,7 @@ namespace winrt::react::uwp::image::implementation if (!CompositionBrush()) { auto surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; + auto uri{ SourceUri() }; auto loadedSurface{ winrt::LoadedImageSurface::StartLoadFromUri(SourceUri()) }; surfaceBrush.Surface(loadedSurface); @@ -106,18 +121,19 @@ namespace winrt::react::uwp::image::implementation bool ReactImageBrush::ShouldSwitchCompositionBrush() { - bool effectToSurfaceBrushSwitch = + bool effectToSurfaceBrushSwitch{ ResizeMode() != image::ResizeMode::Repeat && - !CompositionBrush().try_as(); + !CompositionBrush().try_as() }; - bool surfaceToEffectBrushSwitch = ResizeMode() == image::ResizeMode::Repeat; + bool surfaceToEffectBrushSwitch{ ResizeMode() == image::ResizeMode::Repeat }; return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; } bool ReactImageBrush::IsImageLargerThanView() { - return true; + bool value = false; + return value; } winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h index 5d0896e2d51..d2776fc2511 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.h +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -11,7 +11,7 @@ namespace winrt::react::uwp::image::implementation void ResizeMode(react::uwp::image::ResizeMode value); Windows::Foundation::Uri SourceUri() { return m_sourceUri; } - void SourceUri(Windows::Foundation::Uri const& value) { m_sourceUri = value; } + void SourceUri(Windows::Foundation::Uri const& value); // XamlCompositionBaseBrush void OnConnected(); From 634087eeae212bbb6ed24af9d2c057d638e1a838 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Thu, 6 Jun 2019 12:21:17 -0700 Subject: [PATCH 06/10] Add ReactImage canvas wrapper --- vnext/ReactUWP/ReactUWP.vcxproj | 7 +- vnext/ReactUWP/ReactUWP.vcxproj.filters | 21 +- .../ReactUWP/Views/Image/ImageViewManager.cpp | 62 ++--- vnext/ReactUWP/Views/Image/ReactImage.cpp | 38 +++ vnext/ReactUWP/Views/Image/ReactImage.h | 41 +++ .../ReactUWP/Views/Image/ReactImageBrush.cpp | 250 ++++++++++-------- vnext/ReactUWP/Views/Image/ReactImageBrush.h | 101 ++++--- 7 files changed, 309 insertions(+), 211 deletions(-) create mode 100644 vnext/ReactUWP/Views/Image/ReactImage.cpp create mode 100644 vnext/ReactUWP/Views/Image/ReactImage.h diff --git a/vnext/ReactUWP/ReactUWP.vcxproj b/vnext/ReactUWP/ReactUWP.vcxproj index d80a3c2136e..7974fd50f28 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -190,23 +190,19 @@ - - - - - + @@ -282,6 +278,7 @@ + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index bca349268dd..9f2c748f830 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -219,6 +219,9 @@ Views\Image + + Views\Image + @@ -453,24 +456,12 @@ Views\Image - - Views\cppwinrt - - - Views\cppwinrt\winrt - - - Views\cppwinrt\winrt\impl - - - Views\cppwinrt\winrt\impl - - - Views\cppwinrt\winrt\impl - Views\Image + + Views\Image + diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index e8ed2833ffe..675ae071403 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -36,7 +36,7 @@ #include -#include "ReactImageBrush.h" +#include "ReactImage.h" #if _MSC_VER <= 1913 // VC 19 (2015-2017.6) cannot optimize co_await/cppwinrt usage @@ -44,7 +44,6 @@ #endif namespace winrt { - using namespace winrt::react::uwp::image; using namespace Windows::Foundation; using namespace Windows::Storage::Streams; using namespace Windows::UI::Xaml::Controls; @@ -97,38 +96,37 @@ struct json_type_traits template<> -struct json_type_traits +struct json_type_traits { - static winrt::ResizeMode parseJson(const folly::dynamic& json) + static react::uwp::ResizeMode parseJson(const folly::dynamic& json) { - auto resizeMode = winrt::ResizeMode::Contain; + auto resizeMode = react::uwp::ResizeMode::Contain; if (json == "cover") { - resizeMode = winrt::ResizeMode::Cover; + resizeMode = react::uwp::ResizeMode::Cover; } else if (json == "contain") { - resizeMode = winrt::ResizeMode::Contain; + resizeMode = react::uwp::ResizeMode::Contain; } else if (json == "stretch") { - resizeMode = winrt::ResizeMode::Stretch; + resizeMode = react::uwp::ResizeMode::Stretch; } else if (json == "center") { - resizeMode = winrt::ResizeMode::Center; + resizeMode = react::uwp::ResizeMode::Center; } else if (json == "repeat") { - resizeMode = winrt::ResizeMode::Repeat; + resizeMode = react::uwp::ResizeMode::Repeat; } return resizeMode; } }; - namespace react { namespace uwp { ImageViewManager::ImageViewManager(const std::shared_ptr& reactInstance) @@ -143,12 +141,7 @@ namespace react { namespace uwp { XamlView ImageViewManager::CreateViewCore(int64_t tag) { - auto brush{ winrt::make() }; - - winrt::Canvas canvas; - canvas.Background(brush); - - return canvas; + return ReactImage::Create().as(); } void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) @@ -158,12 +151,6 @@ namespace react { namespace uwp { if (canvas == nullptr) return; - auto width{ canvas.Width() }; - auto heigth{ canvas.Height() }; - - auto actualWidth{ canvas.ActualWidth() }; - auto actualHeight{ canvas.ActualHeight() }; - for (const auto& pair : reactDiffMap.items()) { const std::string& propertyName = pair.first.getString(); @@ -175,9 +162,9 @@ namespace react { namespace uwp { } else if (propertyName == "resizeMode") { - auto resizeMode = json_type_traits::parseJson(propertyValue); - auto brush = canvas.Background().as(); - brush.ResizeMode(resizeMode); + auto resizeMode = json_type_traits::parseJson(propertyValue); + auto reactImage{ canvas.as() }; + reactImage->ResizeMode(resizeMode); } // TODO: overflow @@ -298,28 +285,15 @@ namespace react { namespace uwp { EmitImageEvent(instance, canvas, "topLoadStart", sources[0]); if (needsDownload) { - auto brush{ canvas.Background().as() }; - brush.SourceUri(uri); - canvas.Background(brush); + auto reactImage{ canvas.as() }; + reactImage->SourceUri(uri); - // FUTURE: This should get a weak_ptr from instance - // fix when the win32 instance merge happens - // DownloadImageAsync(canvas, sources[0], instance); + // TODO: Bubble up LoadedImageSourceLoadStatus } else { - // auto brush = winrt::make(); - auto brush{ canvas.Background().as() }; - brush.SourceUri(uri); - canvas.Background(brush); - - //winrt::BitmapImage bitmap; - //bitmap.UriSource(uri); - - //winrt::ImageBrush brush; - //brush.ImageSource(bitmap); - - //canvas.Background(brush); + auto reactImage{ canvas.as() }; + reactImage->SourceUri(uri); EmitImageEvent(instance, canvas, "topLoad", sources[0]); } diff --git a/vnext/ReactUWP/Views/Image/ReactImage.cpp b/vnext/ReactUWP/Views/Image/ReactImage.cpp new file mode 100644 index 00000000000..1cdef1a1e52 --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImage.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" + +#include "ReactImage.h" + +namespace winrt { + using namespace Windows::UI; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Controls; + using namespace Windows::UI::Xaml::Media; + using namespace Windows::Foundation; +} + +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; + } + } +} // 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..6620d250c1d --- /dev/null +++ b/vnext/ReactUWP/Views/Image/ReactImage.h @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "ReactImageBrush.h" + +#include +#include +#include + +namespace react { + namespace uwp { + + 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); + + // Public Properties + winrt::Windows::Foundation::Uri SourceUri() { return m_brush->SourceUri(); } + void SourceUri(winrt::Windows::Foundation::Uri const& value) { m_brush->SourceUri(value); } + + react::uwp::ResizeMode ResizeMode() { return m_brush->ResizeMode(); } + void ResizeMode(react::uwp::ResizeMode value) { m_brush->ResizeMode(value); } + + private: + winrt::com_ptr m_brush; + }; + + } +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp index 07f65ce3c1d..885dbd80340 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + #include "pch.h" #include "BorderEffect.h" @@ -12,157 +15,190 @@ namespace winrt { using namespace winrt::Windows::UI::Xaml::Media; } -using namespace winrt::react::uwp; +namespace react { + namespace uwp { -namespace winrt::react::uwp::image::implementation -{ - void ReactImageBrush::SourceUri(Windows::Foundation::Uri const& value) - { - if (m_sourceUri != value) + ReactImageBrush::ReactImageBrush() { - m_sourceUri = value; - UpdateCompositionBrush(); } - } - void ReactImageBrush::ResizeMode(image::ResizeMode value) - { - if (m_resizeMode != value) + /*static*/ winrt::com_ptr ReactImageBrush::Create() { - m_resizeMode = value; - UpdateCompositionBrush(); + return winrt::make_self(); } - } - void ReactImageBrush::OnConnected() - { - UpdateCompositionBrush(); - } + void ReactImageBrush::SourceUri(winrt::Uri const& value) + { + if (m_sourceUri != value) + { + m_sourceUri = value; + UpdateCompositionBrush(); + } + } - void ReactImageBrush::OnDisconnected() - { - // Dispose of composition resources when no longer in use. - if (CompositionBrush()) + void ReactImageBrush::ResizeMode(react::uwp::ResizeMode value) { - CompositionBrush(nullptr); + if (m_resizeMode != value) + { + m_resizeMode = value; + UpdateCompositionBrush(); + } } - } - void ReactImageBrush::UpdateCompositionBrush() - { - if (SourceUri()) + void ReactImageBrush::AvailableSize(winrt::Size const& value) { - winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; - surfaceBrush.Stretch(ResizeModeToStretch()); + if (m_availableSize != value) + { + m_availableSize = value; + UpdateCompositionBrush(); + } + } - auto compositionBrush{ surfaceBrush.as() }; - if (ResizeMode() == image::ResizeMode::Repeat) + void ReactImageBrush::OnConnected() + { + UpdateCompositionBrush(); + } + + void ReactImageBrush::OnDisconnected() + { + // Dispose of composition resources when no longer in use. + if (CompositionBrush()) { - compositionBrush = GetOrCreateEffectBrush(surfaceBrush); + CompositionBrush(nullptr); } + } - // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat - if (ShouldSwitchCompositionBrush()) + void ReactImageBrush::UpdateCompositionBrush() + { + if (SourceUri()) { - CompositionBrush(compositionBrush); + winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; + surfaceBrush.Stretch(ResizeModeToStretch()); + + auto compositionBrush{ surfaceBrush.as() }; + if (ResizeMode() == ResizeMode::Repeat) + { + compositionBrush = GetOrCreateEffectBrush(surfaceBrush); + } + + // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat + if (ShouldSwitchCompositionBrush()) + { + CompositionBrush(compositionBrush); + } } } - } - winrt::CompositionEffectBrush ReactImageBrush::GetOrCreateEffectBrush(winrt::CompositionSurfaceBrush const& surfaceBrush) - { - if (!m_effectBrush) + winrt::CompositionEffectBrush ReactImageBrush::GetOrCreateEffectBrush(winrt::CompositionSurfaceBrush const& surfaceBrush) { - auto borderEffect{ winrt::make() }; - borderEffect.ExtendX(winrt::CanvasEdgeBehavior::Wrap); - borderEffect.ExtendY(winrt::CanvasEdgeBehavior::Wrap); + 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::CompositionEffectSourceParameter borderEffectSourceParameter{ L"source" }; + borderEffect.Source(borderEffectSourceParameter); - auto effectFactory{ winrt::Window::Current().Compositor().CreateEffectFactory(borderEffect) }; - m_effectBrush = effectFactory.CreateBrush(); + auto effectFactory{ winrt::Window::Current().Compositor().CreateEffectFactory(borderEffect) }; + m_effectBrush = effectFactory.CreateBrush(); - surfaceBrush.HorizontalAlignmentRatio(0.0f); - surfaceBrush.VerticalAlignmentRatio(0.0f); + surfaceBrush.HorizontalAlignmentRatio(0.0f); + surfaceBrush.VerticalAlignmentRatio(0.0f); - m_effectBrush.SetSourceParameter(L"source", surfaceBrush); - } + m_effectBrush.SetSourceParameter(L"source", surfaceBrush); + } - return m_effectBrush; - } + return m_effectBrush; + } - winrt::CompositionSurfaceBrush ReactImageBrush::GetOrCreateSurfaceBrush() - { - // If it doesn't exist, create it - if (!CompositionBrush()) + winrt::CompositionSurfaceBrush ReactImageBrush::GetOrCreateSurfaceBrush() { - auto surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; - auto uri{ SourceUri() }; - auto loadedSurface{ winrt::LoadedImageSurface::StartLoadFromUri(SourceUri()) }; + // If it doesn't exist, create it + if (!CompositionBrush()) + { + auto loadedSurface{ winrt::LoadedImageSurface::StartLoadFromUri(SourceUri()) }; - surfaceBrush.Surface(loadedSurface); + loadedSurface.LoadCompleted([this](IInspectable const&, winrt::LoadedImageSourceLoadCompletedEventArgs const& args) + { + if (args.Status() == winrt::LoadedImageSourceLoadStatus::Success) + { + m_imageLoaded = true; + UpdateCompositionBrush(); + } + }); - return surfaceBrush; - } + auto surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; + surfaceBrush.Surface(loadedSurface); - auto brush{ CompositionBrush().try_as() }; + return surfaceBrush; + } - // If ResizeMode is set to Repeat, then we're using a CompositionEffectBrush. - // Get the CompositionSurfaceBrush from its source. - if (!brush) - { - auto effectBrush{ CompositionBrush().as() }; - brush = effectBrush.GetSourceParameter(L"source").as(); + 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() }; + surfaceBrush = effectBrush.GetSourceParameter(L"source").as(); + } + + return surfaceBrush; } - return brush; - } + bool ReactImageBrush::ShouldSwitchCompositionBrush() + { + bool effectToSurfaceBrushSwitch{ + ResizeMode() != ResizeMode::Repeat && + !CompositionBrush().try_as() }; - bool ReactImageBrush::ShouldSwitchCompositionBrush() - { - bool effectToSurfaceBrushSwitch{ - ResizeMode() != image::ResizeMode::Repeat && - !CompositionBrush().try_as() }; + bool surfaceToEffectBrushSwitch{ ResizeMode() == ResizeMode::Repeat }; - bool surfaceToEffectBrushSwitch{ ResizeMode() == image::ResizeMode::Repeat }; + return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; + } - return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; - } + bool ReactImageBrush::IsImageLargerThanView() + { + if (m_imageLoaded) + { + auto surface{ GetOrCreateSurfaceBrush().Surface().as() }; + auto dipsSize{ surface.DecodedSize() }; - bool ReactImageBrush::IsImageLargerThanView() - { - bool value = false; - return value; - } + return (dipsSize.Height > AvailableSize().Height) || (dipsSize.Width > AvailableSize().Width); + } - winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() - { - auto stretch{ winrt::CompositionStretch::None }; + return false; + } - switch (ResizeMode()) + winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() { - case image::ResizeMode::Center: - stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; - break; + auto stretch{ winrt::CompositionStretch::None }; - case image::ResizeMode::Contain: - stretch = winrt::CompositionStretch::Uniform; - break; + switch (ResizeMode()) + { + case ResizeMode::Center: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; - case image::ResizeMode::Cover: - stretch = winrt::CompositionStretch::UniformToFill; - break; + case ResizeMode::Contain: + stretch = winrt::CompositionStretch::Uniform; + break; - case image::ResizeMode::Stretch: - stretch = winrt::CompositionStretch::Fill; - break; + case ResizeMode::Cover: + stretch = winrt::CompositionStretch::UniformToFill; + break; - case image::ResizeMode::Repeat: - stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; - break; - } + case ResizeMode::Stretch: + stretch = winrt::CompositionStretch::Fill; + break; + + case ResizeMode::Repeat: + stretch = IsImageLargerThanView() ? winrt::CompositionStretch::Uniform : winrt::CompositionStretch::None; + break; + } - return stretch; + return stretch; + } } -} +} // namespace react::uwp diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h index d2776fc2511..68d6690f2de 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.h +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -1,41 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + #pragma once -#include - -namespace winrt::react::uwp::image::implementation -{ - struct ReactImageBrush : ReactImageBrushT - { - ReactImageBrush() = default; - - react::uwp::image::ResizeMode ResizeMode() { return m_resizeMode; } - void ResizeMode(react::uwp::image::ResizeMode value); - - Windows::Foundation::Uri SourceUri() { return m_sourceUri; } - void SourceUri(Windows::Foundation::Uri const& value); - - // XamlCompositionBaseBrush - void OnConnected(); - void OnDisconnected(); - - private: - bool m_usingEffectBrush{ false }; - Windows::Foundation::Uri m_sourceUri{ nullptr }; - react::uwp::image::ResizeMode m_resizeMode{ react::uwp::image::ResizeMode::Contain }; - Windows::UI::Composition::CompositionEffectBrush m_effectBrush{ nullptr }; - - bool IsImageLargerThanView(); - bool ShouldSwitchCompositionBrush(); - void UpdateCompositionBrush(); - Windows::UI::Composition::CompositionStretch ResizeModeToStretch(); - Windows::UI::Composition::CompositionSurfaceBrush GetOrCreateSurfaceBrush(); - Windows::UI::Composition::CompositionEffectBrush GetOrCreateEffectBrush(Windows::UI::Composition::CompositionSurfaceBrush const& surfaceBrush); - - }; -} - -namespace winrt::react::uwp::image::factory_implementation -{ - struct ReactImageBrush : ReactImageBrushT - { - }; -} + +#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(); + + public: + static winrt::com_ptr Create(); + template friend auto winrt::make_self(Args&& ... args); + + // Public Properties + react::uwp::ResizeMode ResizeMode() { return m_resizeMode; } + void ResizeMode(react::uwp::ResizeMode value); + + winrt::Windows::Foundation::Uri SourceUri() { return m_sourceUri; } + void SourceUri(winrt::Windows::Foundation::Uri const& value); + + winrt::Windows::Foundation::Size AvailableSize() { return m_availableSize; } + void AvailableSize(winrt::Windows::Foundation::Size const& value); + + // XamlCompositionBaseBrush Methods + void OnConnected(); + void OnDisconnected(); + + private: + bool m_imageLoaded{ false }; + winrt::Windows::Foundation::Uri m_sourceUri{ nullptr }; + winrt::Windows::Foundation::Size m_availableSize{ }; + react::uwp::ResizeMode m_resizeMode{ ResizeMode::Contain }; + winrt::Windows::UI::Composition::CompositionEffectBrush m_effectBrush{ nullptr }; + + bool IsImageLargerThanView(); + bool ShouldSwitchCompositionBrush(); + void UpdateCompositionBrush(); + 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); + }; + } +} // namespace react::uwp From 071564d42346703fe3dda1d2559a30df68b49143 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Thu, 13 Jun 2019 19:23:56 -0700 Subject: [PATCH 07/10] PR feeedback + add OnLoadEnd event to ReactImage --- .../ReactUWP/Views/Image/ImageViewManager.cpp | 194 ++++-------------- vnext/ReactUWP/Views/Image/ImageViewManager.h | 2 - vnext/ReactUWP/Views/Image/ReactImage.cpp | 135 +++++++++++- vnext/ReactUWP/Views/Image/ReactImage.h | 34 ++- .../ReactUWP/Views/Image/ReactImageBrush.cpp | 156 +++++++------- vnext/ReactUWP/Views/Image/ReactImageBrush.h | 26 ++- .../Microsoft.UI.Composition.Effects.idl | 31 +++ 7 files changed, 314 insertions(+), 264 deletions(-) create mode 100644 vnext/ReactUWP/Views/cppwinrt/Microsoft.UI.Composition.Effects.idl diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index 675ae071403..c15666e04bd 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -10,32 +10,15 @@ #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 +#include +#include +#include #include "ReactImage.h" #if _MSC_VER <= 1913 @@ -47,25 +30,9 @@ namespace winrt { using namespace Windows::Foundation; using namespace Windows::Storage::Streams; using namespace Windows::UI::Xaml::Controls; - using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media::Imaging; - using namespace Windows::Web::Http; } -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 @@ -94,7 +61,6 @@ struct json_type_traits } }; - template<> struct json_type_traits { @@ -141,7 +107,17 @@ namespace react { namespace uwp { XamlView ImageViewManager::CreateViewCore(int64_t tag) { - return ReactImage::Create().as(); + auto reactImage{ ReactImage::Create() }; + + reactImage->OnLoadEnd([=](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) @@ -173,7 +149,7 @@ namespace react { namespace uwp { Super::UpdateProperties(nodeToUpdate, reactDiffMap); } - void EmitImageEvent(const std::shared_ptr &reactInstance, winrt::Canvas canvas, const char* eventName, ImageSource& source) + void EmitImageEvent(const std::shared_ptr& reactInstance, winrt::Canvas canvas, const char* eventName, ImageSource& source) { if (reactInstance == nullptr) return; @@ -190,73 +166,6 @@ namespace react { namespace uwp { reactInstance->DispatchEvent(tag, eventName, std::move(eventData)); } - std::future DownloadImageAsync(winrt::Canvas canvas, 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::HttpClient httpClient; - winrt::HttpMethod httpMethod(nullptr); - - if (source.method.empty()) - httpMethod = winrt::HttpMethod::Get(); - else - httpMethod = winrt::HttpMethod(facebook::react::unicode::utf8ToUtf16(source.method)); - - winrt::Uri uri(facebook::react::unicode::utf8ToUtf16(source.uri)); - - winrt::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::HttpResponseMessage response = co_await httpClient.SendRequestAsync(request); - - auto instance = instanceWeak.lock(); - 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); - - // winrt::LoadedImageSurface().StartLoadFromStream(memoryStream); - - winrt::BitmapImage bitmap; - co_await bitmap.SetSourceAsync(memoryStream); - - winrt::ImageBrush brush; - brush.ImageSource(bitmap); - - canvas.Background(brush); - - EmitImageEvent(instance, canvas, "topLoad", source); - } - else - { - EmitImageEvent(instance, canvas, "topError", source); - } - } - catch (winrt::hresult_error const &) - { - EmitImageEvent(instanceWeak.lock(), canvas, "topError", source); - } - - EmitImageEvent(instanceWeak.lock(), canvas, "topLoadEnd", source); - } - void ImageViewManager::setSource(winrt::Canvas canvas, const folly::dynamic& data) { auto instance = m_wkReactInstance.lock(); @@ -264,6 +173,7 @@ namespace react { namespace uwp { return; auto sources = json_type_traits>::parseJson(data); + auto uriString = sources[0].uri; if (uriString.length() == 0) { @@ -271,41 +181,9 @@ namespace react { namespace uwp { 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, canvas, "topLoadStart", sources[0]); - if (needsDownload) - { - auto reactImage{ canvas.as() }; - reactImage->SourceUri(uri); - - // TODO: Bubble up LoadedImageSourceLoadStatus - } - else - { - auto reactImage{ canvas.as() }; - reactImage->SourceUri(uri); - - EmitImageEvent(instance, canvas, "topLoad", sources[0]); - } - } - catch (...) - { - OutputDebugString(L"caught exception setting up image source"); - EmitImageEvent(instance, canvas, "topError", sources[0]); - } - - if (!needsDownload) - EmitImageEvent(instance, canvas, "topLoadEnd", sources[0]); + auto reactImage{ canvas.as() }; + EmitImageEvent(instance, canvas, "topLoadStart", sources[0]); + reactImage->Source(sources[0]); } folly::dynamic ImageViewManager::GetExportedCustomDirectEventTypeConstants() const @@ -331,6 +209,7 @@ namespace react { namespace uwp { return props; } + // // ImageViewManagerModule::ImageViewManagerModuleImpl // @@ -358,26 +237,27 @@ class ImageViewManagerModule::ImageViewManagerModuleImpl winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::module::CxxModule::Callback successCallback, facebook::xplat::module::CxxModule::Callback errorCallback) { - winrt::Canvas canvas; - ImageSource source; - source.uri = uri; - - winrt::apartment_context ui_thread; + bool succeeded{ false }; - bool succeeded = false; try { - co_await DownloadImageAsync(canvas, source, std::weak_ptr()); + ImageSource source; + source.uri = uri; - // Get back to the UI-thread to read properties on Image - co_await ui_thread; - if (auto brush = canvas.Background().try_as()) + winrt::InMemoryRandomAccessStream memoryStream{ co_await react::uwp::GetImageStreamAsync(source) }; + + if (!memoryStream) { - if (auto bitmap = brush.ImageSource().try_as()) - { - successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); - succeeded = true; - } + EmitImageEvent(std::weak_ptr().lock(), {}, "topError", source); + } + + winrt::BitmapImage bitmap; + co_await bitmap.SetSourceAsync(memoryStream); + + if (bitmap) + { + successCallback({ bitmap.PixelWidth(), bitmap.PixelHeight() }); + succeeded = true; } } catch (winrt::hresult_error const &) @@ -390,7 +270,6 @@ winrt::fire_and_forget GetImageSizeAsync(std::string uri, facebook::xplat::modul co_return; } - void ImageViewManagerModule::ImageViewManagerModuleImpl::getSize(std::string uri, Callback successCallback, Callback errorCallback) { GetImageSizeAsync(uri, successCallback, errorCallback); @@ -452,5 +331,4 @@ auto ImageViewManagerModule::getMethods() -> std::vector }; } - } } diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h index df38dd063c9..b828b557df5 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.h +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.h @@ -5,8 +5,6 @@ #include #include -namespace winrt::Windows::UI::Xaml::Media { enum class Stretch; } - namespace facebook { namespace react { class MessageQueueThread; } } diff --git a/vnext/ReactUWP/Views/Image/ReactImage.cpp b/vnext/ReactUWP/Views/Image/ReactImage.cpp index 1cdef1a1e52..fcf140fce2f 100644 --- a/vnext/ReactUWP/Views/Image/ReactImage.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImage.cpp @@ -5,14 +5,27 @@ #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; - using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Media; - using namespace Windows::Foundation; + 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 { @@ -34,5 +47,121 @@ namespace react { 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 (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&) + { + } + } + + 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 index 6620d250c1d..9b6a08f215d 100644 --- a/vnext/ReactUWP/Views/Image/ReactImage.h +++ b/vnext/ReactUWP/Views/Image/ReactImage.h @@ -6,12 +6,27 @@ #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; @@ -24,18 +39,31 @@ namespace react { template friend auto winrt::make_self(Args&& ... args); // Overrides - virtual winrt::Windows::Foundation::Size ArrangeOverride(winrt::Windows::Foundation::Size finalSize); + 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 - winrt::Windows::Foundation::Uri SourceUri() { return m_brush->SourceUri(); } - void SourceUri(winrt::Windows::Foundation::Uri const& value) { m_brush->SourceUri(value); } + 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 index 885dbd80340..338bfecbf40 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -3,13 +3,16 @@ #include "pch.h" -#include "BorderEffect.h" #include "ReactImageBrush.h" -#include #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; @@ -17,22 +20,22 @@ namespace winrt { namespace react { namespace uwp { - - ReactImageBrush::ReactImageBrush() + /*static*/ winrt::com_ptr ReactImageBrush::Create() { + return winrt::make_self(); } - /*static*/ winrt::com_ptr ReactImageBrush::Create() + void ReactImageBrush::OnConnected() { - return winrt::make_self(); + UpdateCompositionBrush(); } - void ReactImageBrush::SourceUri(winrt::Uri const& value) + void ReactImageBrush::OnDisconnected() { - if (m_sourceUri != value) + // Dispose of composition resources when no longer in use. + if (CompositionBrush()) { - m_sourceUri = value; - UpdateCompositionBrush(); + CompositionBrush(nullptr); } } @@ -54,23 +57,18 @@ namespace react { } } - void ReactImageBrush::OnConnected() - { - UpdateCompositionBrush(); - } - - void ReactImageBrush::OnDisconnected() + void ReactImageBrush::Source(winrt::LoadedImageSurface const& value) { - // Dispose of composition resources when no longer in use. - if (CompositionBrush()) + if (m_loadedImageSurface != value) { - CompositionBrush(nullptr); + m_loadedImageSurface = value; + UpdateCompositionBrush(); } } void ReactImageBrush::UpdateCompositionBrush() { - if (SourceUri()) + if (m_loadedImageSurface) { winrt::CompositionSurfaceBrush surfaceBrush{ GetOrCreateSurfaceBrush() }; surfaceBrush.Stretch(ResizeModeToStretch()); @@ -89,62 +87,17 @@ namespace react { } } - 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); - - auto 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; - } - - winrt::CompositionSurfaceBrush ReactImageBrush::GetOrCreateSurfaceBrush() + bool ReactImageBrush::IsImageLargerThanView() { - // If it doesn't exist, create it - if (!CompositionBrush()) + if (m_loadedImageSurface) { - auto loadedSurface{ winrt::LoadedImageSurface::StartLoadFromUri(SourceUri()) }; - - loadedSurface.LoadCompleted([this](IInspectable const&, winrt::LoadedImageSourceLoadCompletedEventArgs const& args) - { - if (args.Status() == winrt::LoadedImageSourceLoadStatus::Success) - { - m_imageLoaded = true; - UpdateCompositionBrush(); - } - }); - - auto surfaceBrush{ winrt::Window::Current().Compositor().CreateSurfaceBrush() }; - surfaceBrush.Surface(loadedSurface); - - return surfaceBrush; - } - - auto surfaceBrush{ CompositionBrush().try_as() }; + auto surface{ GetOrCreateSurfaceBrush().Surface().as() }; + winrt::Size dipsSize{ surface.DecodedSize() }; - // If ResizeMode is set to Repeat, then we're using a CompositionEffectBrush. - // Get the CompositionSurfaceBrush from its source. - if (!surfaceBrush) - { - auto effectBrush{ CompositionBrush().as() }; - surfaceBrush = effectBrush.GetSourceParameter(L"source").as(); + return (dipsSize.Height > AvailableSize().Height) || (dipsSize.Width > AvailableSize().Width); } - return surfaceBrush; + return false; } bool ReactImageBrush::ShouldSwitchCompositionBrush() @@ -158,19 +111,6 @@ namespace react { return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; } - bool ReactImageBrush::IsImageLargerThanView() - { - if (m_imageLoaded) - { - auto surface{ GetOrCreateSurfaceBrush().Surface().as() }; - auto dipsSize{ surface.DecodedSize() }; - - return (dipsSize.Height > AvailableSize().Height) || (dipsSize.Width > AvailableSize().Width); - } - - return false; - } - winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() { auto stretch{ winrt::CompositionStretch::None }; @@ -200,5 +140,53 @@ namespace react { 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() }; + 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 index 68d6690f2de..1250d4badbc 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.h +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -24,39 +24,37 @@ namespace react { using Super = winrt::Windows::UI::Xaml::Media::XamlCompositionBrushBaseT; private: // Constructors - ReactImageBrush(); + 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::Uri SourceUri() { return m_sourceUri; } - void SourceUri(winrt::Windows::Foundation::Uri const& value); - winrt::Windows::Foundation::Size AvailableSize() { return m_availableSize; } void AvailableSize(winrt::Windows::Foundation::Size const& value); - // XamlCompositionBaseBrush Methods - void OnConnected(); - void OnDisconnected(); + void Source(winrt::Windows::UI::Xaml::Media::LoadedImageSurface const& value); private: - bool m_imageLoaded{ false }; - winrt::Windows::Foundation::Uri m_sourceUri{ nullptr }; - winrt::Windows::Foundation::Size m_availableSize{ }; - react::uwp::ResizeMode m_resizeMode{ ResizeMode::Contain }; - winrt::Windows::UI::Composition::CompositionEffectBrush m_effectBrush{ nullptr }; - + void UpdateCompositionBrush(); bool IsImageLargerThanView(); bool ShouldSwitchCompositionBrush(); - void UpdateCompositionBrush(); 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/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 + { + } +} From 23e7aba626803b5a3e4b5742eebed4cb90cd4606 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Thu, 13 Jun 2019 20:16:02 -0700 Subject: [PATCH 08/10] Removing accidentally added files --- .../ReactUWP/Views/Image/ReactImageBrush.cpp | 10 +- vnext/ReactUWP/Views/Image/ReactImageBrush.h | 3 +- .../react.uwp.image.ReactImageBrush.g.h | 83 ------ .../cppwinrt/winrt/impl/react.uwp.image.0.h | 56 ---- .../cppwinrt/winrt/impl/react.uwp.image.1.h | 19 -- .../cppwinrt/winrt/impl/react.uwp.image.2.h | 29 -- .../Views/cppwinrt/winrt/react.uwp.image.h | 254 ------------------ 7 files changed, 11 insertions(+), 443 deletions(-) delete mode 100644 vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h delete mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h delete mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h delete mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h delete mode 100644 vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp index 338bfecbf40..5ea8428480d 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -104,13 +104,19 @@ namespace react { { bool effectToSurfaceBrushSwitch{ ResizeMode() != ResizeMode::Repeat && - !CompositionBrush().try_as() }; + !UsingSurfaceBrush() + }; bool surfaceToEffectBrushSwitch{ ResizeMode() == ResizeMode::Repeat }; return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; } + bool ReactImageBrush::UsingSurfaceBrush() + { + return CompositionBrush().try_as() != nullptr; + } + winrt::CompositionStretch ReactImageBrush::ResizeModeToStretch() { auto stretch{ winrt::CompositionStretch::None }; @@ -159,6 +165,8 @@ namespace react { if (!surfaceBrush) { auto effectBrush{ CompositionBrush().as() }; + assert(effectBrush); + surfaceBrush = effectBrush.GetSourceParameter(L"source").as(); } diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h index 1250d4badbc..3bbcf9927ab 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.h +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -16,7 +16,7 @@ namespace react { Contain = 1, Stretch = 2, Repeat = 3, - Center = 4, + Center = 4 }; struct ReactImageBrush : winrt::Windows::UI::Xaml::Media::XamlCompositionBrushBaseT @@ -47,6 +47,7 @@ namespace react { void UpdateCompositionBrush(); bool IsImageLargerThanView(); bool ShouldSwitchCompositionBrush(); + 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); diff --git a/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h b/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h deleted file mode 100644 index 8e2b6b2c624..00000000000 --- a/vnext/ReactUWP/Views/cppwinrt/react.uwp.image.ReactImageBrush.g.h +++ /dev/null @@ -1,83 +0,0 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 - -#pragma once - -#include "winrt/Windows.Foundation.h" -#include "winrt/Windows.UI.Composition.h" -#include "winrt/Windows.UI.Xaml.h" -#include "winrt/Windows.UI.Xaml.Media.h" -#include "winrt/react.uwp.image.h" - -namespace winrt::react::uwp::image::implementation { - -template -struct WINRT_EBO ReactImageBrush_base : implements, - impl::require, - impl::base, - Windows::UI::Xaml::Media::IBrushOverrides2T, Windows::UI::Xaml::Media::IXamlCompositionBrushBaseOverridesT -{ - using base_type = ReactImageBrush_base; - using class_type = react::uwp::image::ReactImageBrush; - using implements_type = typename ReactImageBrush_base::implements_type; - using implements_type::implements_type; - using composable_base = Windows::UI::Xaml::Media::XamlCompositionBrushBase; -#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"react.uwp.image.ReactImageBrush"; - } - ReactImageBrush_base() - { - impl::call_factory([&](auto&& f) { f.CreateInstance(*this, this->m_inner); }); - } -}; - -} - -namespace winrt::react::uwp::image::factory_implementation { - -template -struct WINRT_EBO ReactImageBrushT : implements -{ - using instance_type = react::uwp::image::ReactImageBrush; - - hstring GetRuntimeClassName() const - { - return L"react.uwp.image.ReactImageBrush"; - } - - Windows::Foundation::IInspectable ActivateInstance() const - { - return make(); - } -}; - -} - -#if defined(WINRT_FORCE_INCLUDE_REACTIMAGEBRUSH_XAML_G_H) || __has_include("react.uwp.image.ReactImageBrush.xaml.g.h") - -#include "react.uwp.image.ReactImageBrush.xaml.g.h" - -#else - -namespace winrt::react::uwp::image::implementation -{ - template - using ReactImageBrushT = ReactImageBrush_base; -} - -#endif diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h deleted file mode 100644 index 3995c35c0d8..00000000000 --- a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.0.h +++ /dev/null @@ -1,56 +0,0 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 - -#pragma once - -WINRT_EXPORT namespace winrt::Windows::Foundation { - -struct Uri; - -} - -WINRT_EXPORT namespace winrt::react::uwp::image { - -enum class ResizeMode : int32_t -{ - Cover = 0, - Contain = 1, - Stretch = 2, - Repeat = 3, - Center = 4, -}; - -struct IReactImageBrush; -struct ReactImageBrush; - -} - -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"react.uwp.image.IReactImageBrush" }; }; -template <> struct name{ static constexpr auto & value{ L"react.uwp.image.ReactImageBrush" }; }; -template <> struct name{ static constexpr auto & value{ L"react.uwp.image.ResizeMode" }; }; -template <> struct guid_storage{ static constexpr guid value{ 0x9672DBF3,0xAC79,0x5057,{ 0xBE,0x51,0x03,0xB3,0x79,0x9D,0x89,0xEC } }; }; -template <> struct default_interface{ using type = react::uwp::image::IReactImageBrush; }; - -template <> struct abi{ struct type : IInspectable -{ - virtual int32_t WINRT_CALL get_ResizeMode(react::uwp::image::ResizeMode* value) noexcept = 0; - virtual int32_t WINRT_CALL put_ResizeMode(react::uwp::image::ResizeMode value) noexcept = 0; - virtual int32_t WINRT_CALL get_SourceUri(void** value) noexcept = 0; - virtual int32_t WINRT_CALL put_SourceUri(void* value) noexcept = 0; -};}; - -template -struct consume_react_uwp_image_IReactImageBrush -{ - react::uwp::image::ResizeMode ResizeMode() const; - void ResizeMode(react::uwp::image::ResizeMode const& value) const; - Windows::Foundation::Uri SourceUri() const; - void SourceUri(Windows::Foundation::Uri const& value) const; -}; -template <> struct consume { template using type = consume_react_uwp_image_IReactImageBrush; }; - -} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h deleted file mode 100644 index ce6d30efbd9..00000000000 --- a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.1.h +++ /dev/null @@ -1,19 +0,0 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 - -#pragma once -#include "winrt/impl/Windows.Foundation.0.h" -#include "winrt/impl/Windows.UI.Composition.0.h" -#include "winrt/impl/Windows.UI.Xaml.0.h" -#include "winrt/impl/Windows.UI.Xaml.Media.0.h" -#include "winrt/impl/react.uwp.image.0.h" - -WINRT_EXPORT namespace winrt::react::uwp::image { - -struct WINRT_EBO IReactImageBrush : - Windows::Foundation::IInspectable, - impl::consume_t -{ - IReactImageBrush(std::nullptr_t = nullptr) noexcept {} -}; - -} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h b/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h deleted file mode 100644 index 04a46a43ac1..00000000000 --- a/vnext/ReactUWP/Views/cppwinrt/winrt/impl/react.uwp.image.2.h +++ /dev/null @@ -1,29 +0,0 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 - -#pragma once -#include "winrt/impl/Windows.Foundation.1.h" -#include "winrt/impl/Windows.UI.Composition.1.h" -#include "winrt/impl/Windows.UI.Xaml.1.h" -#include "winrt/impl/Windows.UI.Xaml.Media.1.h" -#include "winrt/impl/react.uwp.image.1.h" - -WINRT_EXPORT namespace winrt::react::uwp::image { - -} - -namespace winrt::impl { - -} - -WINRT_EXPORT namespace winrt::react::uwp::image { - -struct WINRT_EBO ReactImageBrush : - react::uwp::image::IReactImageBrush, - impl::base, - impl::require -{ - ReactImageBrush(std::nullptr_t) noexcept {} - ReactImageBrush(); -}; - -} diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h b/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h deleted file mode 100644 index dd126f84f27..00000000000 --- a/vnext/ReactUWP/Views/cppwinrt/winrt/react.uwp.image.h +++ /dev/null @@ -1,254 +0,0 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 - -#pragma once - -#include "winrt/base.h" - - -static_assert(winrt::check_version(CPPWINRT_VERSION, "1.0.180821.2"), "Mismatched component and base headers."); -#include "winrt/Windows.Foundation.h" -#include "winrt/Windows.Foundation.Collections.h" -#include "winrt/impl/Windows.Foundation.2.h" -#include "winrt/impl/Windows.UI.Composition.2.h" -#include "winrt/impl/Windows.UI.Xaml.2.h" -#include "winrt/impl/Windows.UI.Xaml.Media.2.h" -#include "winrt/impl/react.uwp.image.2.h" -#include "winrt/react.uwp.h" - -namespace winrt::impl { - -template react::uwp::image::ResizeMode consume_react_uwp_image_IReactImageBrush::ResizeMode() const -{ - react::uwp::image::ResizeMode value{}; - check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->get_ResizeMode(put_abi(value))); - return value; -} - -template void consume_react_uwp_image_IReactImageBrush::ResizeMode(react::uwp::image::ResizeMode const& value) const -{ - check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->put_ResizeMode(get_abi(value))); -} - -template Windows::Foundation::Uri consume_react_uwp_image_IReactImageBrush::SourceUri() const -{ - Windows::Foundation::Uri value{ nullptr }; - check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->get_SourceUri(put_abi(value))); - return value; -} - -template void consume_react_uwp_image_IReactImageBrush::SourceUri(Windows::Foundation::Uri const& value) const -{ - check_hresult(WINRT_SHIM(react::uwp::image::IReactImageBrush)->put_SourceUri(get_abi(value))); -} - -template -struct produce : produce_base -{ - int32_t WINRT_CALL get_ResizeMode(react::uwp::image::ResizeMode* value) noexcept final - { - try - { - typename D::abi_guard guard(this->shim()); - WINRT_ASSERT_DECLARATION(ResizeMode, WINRT_WRAP(react::uwp::image::ResizeMode)); - *value = detach_from(this->shim().ResizeMode()); - return 0; - } - catch (...) { return to_hresult(); } - } - - int32_t WINRT_CALL put_ResizeMode(react::uwp::image::ResizeMode value) noexcept final - { - try - { - typename D::abi_guard guard(this->shim()); - WINRT_ASSERT_DECLARATION(ResizeMode, WINRT_WRAP(void), react::uwp::image::ResizeMode const&); - this->shim().ResizeMode(*reinterpret_cast(&value)); - return 0; - } - catch (...) { return to_hresult(); } - } - - int32_t WINRT_CALL get_SourceUri(void** value) noexcept final - { - try - { - *value = nullptr; - typename D::abi_guard guard(this->shim()); - WINRT_ASSERT_DECLARATION(SourceUri, WINRT_WRAP(Windows::Foundation::Uri)); - *value = detach_from(this->shim().SourceUri()); - return 0; - } - catch (...) { return to_hresult(); } - } - - int32_t WINRT_CALL put_SourceUri(void* value) noexcept final - { - try - { - typename D::abi_guard guard(this->shim()); - WINRT_ASSERT_DECLARATION(SourceUri, WINRT_WRAP(void), Windows::Foundation::Uri const&); - this->shim().SourceUri(*reinterpret_cast(&value)); - return 0; - } - catch (...) { return to_hresult(); } - } -}; - -} - -WINRT_EXPORT namespace winrt::react::uwp::image { - -inline ReactImageBrush::ReactImageBrush() : - ReactImageBrush(impl::call_factory([](auto&& f) { return f.template ActivateInstance(); })) -{} - -} - -namespace winrt::impl { - -struct property_react_uwp_image_IReactImageBrush -{ struct named { - struct ResizeMode - { - struct name { static constexpr std::wstring_view value{ L"ResizeMode"sv }; }; - using property_type = winrt::react::uwp::image::ResizeMode; - using target_type = winrt::react::uwp::image::IReactImageBrush; - - 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.ResizeMode(); - } - }; - struct setter - { - template - void operator()(target_type const& target, Value&& value) const - { - target.ResizeMode(std::forward(value)); - } - }; - }; - struct SourceUri - { - struct name { static constexpr std::wstring_view value{ L"SourceUri"sv }; }; - using property_type = winrt::Windows::Foundation::Uri; - using target_type = winrt::react::uwp::image::IReactImageBrush; - - 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.SourceUri(); - } - }; - struct setter - { - template - void operator()(target_type const& target, Value&& value) const - { - target.SourceUri(std::forward(value)); - } - }; - };}; - struct list { using type = impl::typelist; }; -}; - -struct property_react_uwp_image_ReactImageBrush -{ struct named { - struct SourceUri - { - struct name { static constexpr std::wstring_view value{ L"SourceUri"sv }; }; - using property_type = winrt::Windows::Foundation::Uri; - using target_type = winrt::react::uwp::image::ReactImageBrush; - - 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.SourceUri(); - } - }; - struct setter - { - template - void operator()(target_type const& target, Value&& value) const - { - target.SourceUri(std::forward(value)); - } - }; - }; - struct ResizeMode - { - struct name { static constexpr std::wstring_view value{ L"ResizeMode"sv }; }; - using property_type = winrt::react::uwp::image::ResizeMode; - using target_type = winrt::react::uwp::image::ReactImageBrush; - - 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.ResizeMode(); - } - }; - struct setter - { - template - void operator()(target_type const& target, Value&& value) const - { - target.ResizeMode(std::forward(value)); - } - }; - };}; - struct list { using type = impl::typelist; }; -}; - -} - -WINRT_EXPORT namespace winrt::experimental::reflect { -template <> struct named_property : impl::property_react_uwp_image_IReactImageBrush::named {}; -template <> struct properties : impl::property_react_uwp_image_IReactImageBrush::list {}; -template <> struct named_property : impl::property_react_uwp_image_ReactImageBrush::named {}; -template <> struct properties : impl::property_react_uwp_image_ReactImageBrush::list {}; - -template <> -struct base_type { using type = Windows::UI::Xaml::Media::XamlCompositionBrushBase; };template <> struct get_enumerator_names -{ - static constexpr std::array value{{ - {L"Cover", 5}, - {L"Contain", 7}, - {L"Stretch", 7}, - {L"Repeat", 6}, - {L"Center", 6}, }}; -}; -template <> struct get_enumerator_values -{ - static constexpr std::array value{{ - react::uwp::image::ResizeMode::Cover, - react::uwp::image::ResizeMode::Contain, - react::uwp::image::ResizeMode::Stretch, - react::uwp::image::ResizeMode::Repeat, - react::uwp::image::ResizeMode::Center, }}; -}; - -} - -WINRT_EXPORT namespace std { - -template<> struct hash : winrt::impl::hash_base {}; -template<> struct hash : winrt::impl::hash_base {}; - -} From 67272696436be5c6a0462fb063e8aca739ff0c43 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Thu, 13 Jun 2019 22:00:22 -0700 Subject: [PATCH 09/10] Fix break after merge --- .../Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h index c06e5a8c4e6..74807d7bbb1 100644 --- a/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h +++ b/vnext/ReactUWP/Views/cppwinrt/winrt/Microsoft.UI.Composition.Effects.h @@ -1,11 +1,12 @@ -// WARNING: Please don't edit this file. It was generated by C++/WinRT v1.0.180821.2 +// 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" From ce9e01485b545629b0d10fc085c14b7b5b01c305 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Mon, 17 Jun 2019 20:16:05 -0700 Subject: [PATCH 10/10] PR feedback + Moved Image module to separate file --- vnext/ReactUWP/Base/UwpReactInstance.cpp | 1 + .../Modules/ImageViewManagerModule.cpp | 148 +++++++++++ .../ReactUWP/Modules/ImageViewManagerModule.h | 31 +++ vnext/ReactUWP/ReactUWP.vcxproj | 5 +- vnext/ReactUWP/ReactUWP.vcxproj.filters | 15 +- vnext/ReactUWP/Views/Image/BorderEffect.h | 232 ++---------------- .../ReactUWP/Views/Image/ImageViewManager.cpp | 161 +----------- vnext/ReactUWP/Views/Image/ImageViewManager.h | 25 -- .../Microsoft.UI.Composition.Effects_Impl.h | 186 ++++++++++++++ vnext/ReactUWP/Views/Image/ReactImage.cpp | 6 + .../ReactUWP/Views/Image/ReactImageBrush.cpp | 19 +- vnext/ReactUWP/Views/Image/ReactImageBrush.h | 1 - 12 files changed, 427 insertions(+), 403 deletions(-) create mode 100644 vnext/ReactUWP/Modules/ImageViewManagerModule.cpp create mode 100644 vnext/ReactUWP/Modules/ImageViewManagerModule.h create mode 100644 vnext/ReactUWP/Views/Image/Microsoft.UI.Composition.Effects_Impl.h diff --git a/vnext/ReactUWP/Base/UwpReactInstance.cpp b/vnext/ReactUWP/Base/UwpReactInstance.cpp index 140322604ef..9b925f011b0 100644 --- a/vnext/ReactUWP/Base/UwpReactInstance.cpp +++ b/vnext/ReactUWP/Base/UwpReactInstance.cpp @@ -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 d5767443b69..eb6496576ec 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj +++ b/vnext/ReactUWP/ReactUWP.vcxproj @@ -168,6 +168,7 @@ + @@ -204,6 +205,7 @@ + @@ -240,7 +242,7 @@ - + @@ -253,6 +255,7 @@ + diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index bcba303454d..74bdcec1dce 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -228,6 +228,9 @@ Views + + Modules + @@ -462,9 +465,6 @@ Views\cppwinrt - - Views\Image - Views\Image @@ -474,6 +474,15 @@ Views + + Modules + + + Views\Image + + + Views\Image + diff --git a/vnext/ReactUWP/Views/Image/BorderEffect.h b/vnext/ReactUWP/Views/Image/BorderEffect.h index d3050872c0c..e580f958b8b 100644 --- a/vnext/ReactUWP/Views/Image/BorderEffect.h +++ b/vnext/ReactUWP/Views/Image/BorderEffect.h @@ -1,218 +1,36 @@ #pragma once -#include - -#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); -} +#include +#include "Microsoft.UI.Composition.Effects_Impl.h" 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); \ - } - - //----------------------------------------------------------------------------------------------------------------- - - 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( + 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 }); + { 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; } + public: + IFACEMETHODIMP GetPropertyCount(_Out_ UINT* count) override { *count = 2; return S_OK; } - IFACEMETHODIMP GetProperty(UINT index, _Outptr_ abi::IPropertyValue** value) override + IFACEMETHODIMP GetProperty(UINT index, _Outptr_ abi::IPropertyValue** value) override + { + return UsePropertyFactory(value, [=]() + { + switch (index) { - 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(); - } - }); + 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 index c15666e04bd..9942a8e8e0d 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -2,35 +2,22 @@ // 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 "ReactImage.h" -#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::Storage::Streams; using namespace Windows::UI::Xaml::Controls; - using namespace Windows::UI::Xaml::Media::Imaging; } // Such code is better to move to a seperate parser layer @@ -66,7 +53,7 @@ struct json_type_traits { static react::uwp::ResizeMode parseJson(const folly::dynamic& json) { - auto resizeMode = react::uwp::ResizeMode::Contain; + auto resizeMode{ react::uwp::ResizeMode::Contain }; if (json == "cover") { @@ -109,7 +96,7 @@ namespace react { namespace uwp { { auto reactImage{ ReactImage::Create() }; - reactImage->OnLoadEnd([=](const auto&, const bool& succeeded) + reactImage->OnLoadEnd([this, reactImage](const auto&, const bool& succeeded) { ImageSource source{ reactImage->Source() }; @@ -122,15 +109,15 @@ namespace react { namespace uwp { void ImageViewManager::UpdateProperties(ShadowNodeBase* nodeToUpdate, const folly::dynamic& reactDiffMap) { - auto canvas = nodeToUpdate->GetView().as(); + 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; + const std::string& propertyName{ pair.first.getString() }; + const folly::dynamic& propertyValue{ pair.second }; if (propertyName == "source") { @@ -138,7 +125,7 @@ namespace react { namespace uwp { } else if (propertyName == "resizeMode") { - auto resizeMode = json_type_traits::parseJson(propertyValue); + auto resizeMode{ json_type_traits::parseJson(propertyValue) }; auto reactImage{ canvas.as() }; reactImage->ResizeMode(resizeMode); } @@ -168,20 +155,13 @@ namespace react { namespace uwp { void ImageViewManager::setSource(winrt::Canvas canvas, const folly::dynamic& data) { - auto instance = m_wkReactInstance.lock(); + 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, canvas, "topError", sources[0]); - return; - } - + auto sources{ json_type_traits>::parseJson(data) }; auto reactImage{ canvas.as() }; + EmitImageEvent(instance, canvas, "topLoadStart", sources[0]); reactImage->Source(sources[0]); } @@ -208,127 +188,4 @@ namespace react { namespace uwp { 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) -{ - bool succeeded{ false }; - - try - { - ImageSource source; - source.uri = uri; - - winrt::InMemoryRandomAccessStream memoryStream{ co_await react::uwp::GetImageStreamAsync(source) }; - - if (!memoryStream) - { - EmitImageEvent(std::weak_ptr().lock(), {}, "topError", source); - } - - winrt::BitmapImage bitmap; - 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/Views/Image/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h index b828b557df5..488528de5f5 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.h +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.h @@ -1,13 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include #include -#include - -namespace facebook { namespace react { - class MessageQueueThread; -} } namespace react { namespace uwp { @@ -29,23 +23,4 @@ namespace react { namespace uwp { private: void setSource(winrt::Windows::UI::Xaml::Controls::Canvas canvas, 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/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 index fcf140fce2f..8bb2dab283a 100644 --- a/vnext/ReactUWP/Views/Image/ReactImage.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImage.cpp @@ -61,6 +61,11 @@ namespace react { 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) { @@ -99,6 +104,7 @@ namespace react { } catch (winrt::hresult_error const&) { + m_onLoadEndEvent(*this, false); } } diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp index 5ea8428480d..6666816671d 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.cpp @@ -76,11 +76,14 @@ namespace react { 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); } - // Only switch CompositionBrush if we are switching to/from ResizeMode::Repeat - if (ShouldSwitchCompositionBrush()) + // 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); } @@ -100,18 +103,6 @@ namespace react { return false; } - bool ReactImageBrush::ShouldSwitchCompositionBrush() - { - bool effectToSurfaceBrushSwitch{ - ResizeMode() != ResizeMode::Repeat && - !UsingSurfaceBrush() - }; - - bool surfaceToEffectBrushSwitch{ ResizeMode() == ResizeMode::Repeat }; - - return effectToSurfaceBrushSwitch || surfaceToEffectBrushSwitch; - } - bool ReactImageBrush::UsingSurfaceBrush() { return CompositionBrush().try_as() != nullptr; diff --git a/vnext/ReactUWP/Views/Image/ReactImageBrush.h b/vnext/ReactUWP/Views/Image/ReactImageBrush.h index 3bbcf9927ab..d2472f84e4f 100644 --- a/vnext/ReactUWP/Views/Image/ReactImageBrush.h +++ b/vnext/ReactUWP/Views/Image/ReactImageBrush.h @@ -46,7 +46,6 @@ namespace react { private: void UpdateCompositionBrush(); bool IsImageLargerThanView(); - bool ShouldSwitchCompositionBrush(); bool UsingSurfaceBrush(); winrt::Windows::UI::Composition::CompositionStretch ResizeModeToStretch(); winrt::Windows::UI::Composition::CompositionSurfaceBrush GetOrCreateSurfaceBrush();