From f88532d1957d47b4c9c773ad485b7bca847ad8ff Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Tue, 14 Nov 2023 05:33:19 -0500 Subject: [PATCH] [Windows] Port to Direct2D to remove win2d dependency (#2052) This change removes the win2d (Direct2D wrapper) dependency by using D2D directly. This removes the manual step of adding the win2d to any new react-native-windows projects that want to use react-native-svg. It is also a stepping stone to an easier Fabric implementation for windows. --- Example/windows/ExperimentalFeatures.props | 2 + windows/RNSVG/BrushView.cpp | 7 +- windows/RNSVG/BrushView.h | 9 +- windows/RNSVG/CircleView.cpp | 21 +- windows/RNSVG/CircleView.h | 2 +- windows/RNSVG/ClipPathView.cpp | 1 - windows/RNSVG/ClipPathView.h | 4 +- windows/RNSVG/D2DBrush.cpp | 5 + windows/RNSVG/D2DBrush.h | 19 ++ windows/RNSVG/D2DDevice.cpp | 5 + windows/RNSVG/D2DDevice.h | 19 ++ windows/RNSVG/D2DDeviceContext.cpp | 5 + windows/RNSVG/D2DDeviceContext.h | 19 ++ windows/RNSVG/D2DGeometry.cpp | 7 + windows/RNSVG/D2DGeometry.h | 19 ++ windows/RNSVG/D2DHelpers.h | 225 +++++++++++++++++++++ windows/RNSVG/DefsView.cpp | 3 +- windows/RNSVG/DefsView.h | 4 +- windows/RNSVG/EllipseView.cpp | 23 ++- windows/RNSVG/EllipseView.h | 2 +- windows/RNSVG/GroupView.cpp | 93 ++++++--- windows/RNSVG/GroupView.h | 14 +- windows/RNSVG/GroupViewManager.cpp | 8 +- windows/RNSVG/ImageView.cpp | 186 +++++++++++------ windows/RNSVG/ImageView.h | 22 +- windows/RNSVG/LineView.cpp | 34 ++-- windows/RNSVG/LineView.h | 2 +- windows/RNSVG/LinearGradientView.cpp | 52 +++-- windows/RNSVG/LinearGradientView.h | 6 +- windows/RNSVG/MarkerView.cpp | 8 + windows/RNSVG/MarkerView.h | 17 ++ windows/RNSVG/MarkerViewManager.cpp | 24 +++ windows/RNSVG/MarkerViewManager.h | 16 ++ windows/RNSVG/PathView.cpp | 35 +++- windows/RNSVG/PathView.h | 46 ++--- windows/RNSVG/PatternView.cpp | 107 ++++++---- windows/RNSVG/PatternView.h | 9 +- windows/RNSVG/RNSVG.vcxproj | 19 +- windows/RNSVG/RNSVG.vcxproj.filters | 42 ++++ windows/RNSVG/RadialGradientView.cpp | 56 ++--- windows/RNSVG/RadialGradientView.h | 6 +- windows/RNSVG/ReactPackageProvider.cpp | 2 + windows/RNSVG/RectView.cpp | 32 +-- windows/RNSVG/RectView.h | 2 +- windows/RNSVG/RenderableView.cpp | 171 +++++++++------- windows/RNSVG/RenderableView.h | 38 ++-- windows/RNSVG/RenderableViewManager.cpp | 2 + windows/RNSVG/SVGLength.cpp | 2 +- windows/RNSVG/SVGLength.h | 2 +- windows/RNSVG/SvgView.cpp | 183 ++++++++++++----- windows/RNSVG/SvgView.h | 48 ++--- windows/RNSVG/SvgViewManager.cpp | 1 - windows/RNSVG/SymbolView.cpp | 1 - windows/RNSVG/SymbolView.h | 4 +- windows/RNSVG/TSpanView.cpp | 52 +++-- windows/RNSVG/TSpanView.h | 5 +- windows/RNSVG/TextView.cpp | 16 +- windows/RNSVG/TextView.h | 4 +- windows/RNSVG/Types.idl | 43 +++- windows/RNSVG/UseView.cpp | 71 ++++--- windows/RNSVG/UseView.h | 4 +- windows/RNSVG/Utils.h | 178 +++++++--------- windows/RNSVG/ViewManagers.idl | 6 + windows/RNSVG/Views.idl | 38 ++-- windows/RNSVG/packages.config | 5 +- windows/RNSVG/pch.h | 18 +- 66 files changed, 1420 insertions(+), 711 deletions(-) create mode 100644 windows/RNSVG/D2DBrush.cpp create mode 100644 windows/RNSVG/D2DBrush.h create mode 100644 windows/RNSVG/D2DDevice.cpp create mode 100644 windows/RNSVG/D2DDevice.h create mode 100644 windows/RNSVG/D2DDeviceContext.cpp create mode 100644 windows/RNSVG/D2DDeviceContext.h create mode 100644 windows/RNSVG/D2DGeometry.cpp create mode 100644 windows/RNSVG/D2DGeometry.h create mode 100644 windows/RNSVG/D2DHelpers.h create mode 100644 windows/RNSVG/MarkerView.cpp create mode 100644 windows/RNSVG/MarkerView.h create mode 100644 windows/RNSVG/MarkerViewManager.cpp create mode 100644 windows/RNSVG/MarkerViewManager.h diff --git a/Example/windows/ExperimentalFeatures.props b/Example/windows/ExperimentalFeatures.props index 3cfbdc5a..11310c41 100644 --- a/Example/windows/ExperimentalFeatures.props +++ b/Example/windows/ExperimentalFeatures.props @@ -32,6 +32,8 @@ See https://microsoft.github.io/react-native-windows/docs/nuget --> false + + true diff --git a/windows/RNSVG/BrushView.cpp b/windows/RNSVG/BrushView.cpp index 7f0fcd25..fbe68f8d 100644 --- a/windows/RNSVG/BrushView.cpp +++ b/windows/RNSVG/BrushView.cpp @@ -2,6 +2,8 @@ #include "BrushView.h" #include "BrushView.g.cpp" +#include "D2DHelpers.h" + namespace winrt::RNSVG::implementation { void BrushView::SaveDefinition() { if (auto const &root{SvgRoot()}) { @@ -10,14 +12,13 @@ void BrushView::SaveDefinition() { } } -void BrushView::SetBounds(Windows::Foundation::Rect const &rect) { - m_bounds = rect; +void BrushView::SetBounds(Rect const &rect) { + m_bounds = D2DHelpers::AsD2DRect(rect); UpdateBounds(); } void BrushView::Unload() { if (m_brush) { - m_brush.Close(); m_brush = nullptr; } diff --git a/windows/RNSVG/BrushView.h b/windows/RNSVG/BrushView.h index a64b356a..547f9462 100644 --- a/windows/RNSVG/BrushView.h +++ b/windows/RNSVG/BrushView.h @@ -1,6 +1,7 @@ #pragma once #include "BrushView.g.h" #include "GroupView.h" +#include "D2DBrush.h" namespace winrt::RNSVG::implementation { struct BrushView : BrushViewT { @@ -9,15 +10,15 @@ struct BrushView : BrushViewT { void SaveDefinition(); - Microsoft::Graphics::Canvas::Brushes::ICanvasBrush Brush() { return m_brush; } + RNSVG::D2DBrush Brush() { return m_brush; } virtual void CreateBrush() {} virtual void Unload(); void SetBounds(Windows::Foundation::Rect const &rect); - protected: - Microsoft::Graphics::Canvas::Brushes::ICanvasBrush m_brush{nullptr}; - Windows::Foundation::Rect m_bounds{}; + RNSVG::D2DBrush m_brush; + D2D1_RECT_F m_bounds; + D2D1::Matrix3x2F m_transform{D2D1::Matrix3x2F::Identity()}; virtual void UpdateBounds() {} }; diff --git a/windows/RNSVG/CircleView.cpp b/windows/RNSVG/CircleView.cpp index 4ebe8b2a..c79c164f 100644 --- a/windows/RNSVG/CircleView.cpp +++ b/windows/RNSVG/CircleView.cpp @@ -6,7 +6,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -29,13 +28,21 @@ void CircleView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate __super::UpdateProperties(reader, forceUpdate, invalidate); } -void CircleView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; +void CircleView::CreateGeometry() { + auto const root{SvgRoot()}; + + float cx{Utils::GetAbsoluteLength(m_cx, root.ActualWidth())}; + float cy{Utils::GetAbsoluteLength(m_cy, root.ActualHeight())}; + float r{Utils::GetAbsoluteLength(m_r, Utils::GetCanvasDiagonal(root.ActualSize()))}; - float cx{Utils::GetAbsoluteLength(m_cx, canvas.Size().Width)}; - float cy{Utils::GetAbsoluteLength(m_cy, canvas.Size().Height)}; - float r{Utils::GetAbsoluteLength(m_r, Utils::GetCanvasDiagonal(canvas.Size()))}; + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; - Geometry(Geometry::CanvasGeometry::CreateCircle(resourceCreator, cx, cy, r)); + com_ptr factory; + deviceContext->GetFactory(factory.put()); + + com_ptr geometry; + check_hresult(factory->CreateEllipseGeometry(D2D1::Ellipse({cx, cy}, r, r), geometry.put())); + + Geometry(make(geometry.as())); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/CircleView.h b/windows/RNSVG/CircleView.h index 6b1a278a..32d7693d 100644 --- a/windows/RNSVG/CircleView.h +++ b/windows/RNSVG/CircleView.h @@ -7,7 +7,7 @@ struct CircleView : CircleViewT { + public: + D2DBrush() = default; + D2DBrush(com_ptr const &brush) { m_d2d = brush; } + + com_ptr Get() { return m_d2d; } + + private: + com_ptr m_d2d; +}; + +} // namespace winrt::RNSVG::implementation +namespace winrt::RNSVG::factory_implementation { +struct D2DBrush : D2DBrushT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/D2DDevice.cpp b/windows/RNSVG/D2DDevice.cpp new file mode 100644 index 00000000..a8e2b75f --- /dev/null +++ b/windows/RNSVG/D2DDevice.cpp @@ -0,0 +1,5 @@ +#include "pch.h" +#include "D2DDevice.h" +#include "D2DDevice.g.cpp" + +namespace winrt::RNSVG::implementation {} diff --git a/windows/RNSVG/D2DDevice.h b/windows/RNSVG/D2DDevice.h new file mode 100644 index 00000000..08a2a3a9 --- /dev/null +++ b/windows/RNSVG/D2DDevice.h @@ -0,0 +1,19 @@ +#pragma once +#include "D2DDevice.g.h" + +namespace winrt::RNSVG::implementation { +struct D2DDevice : D2DDeviceT { + public: + D2DDevice() = default; + D2DDevice(com_ptr const &device) { m_d2d = device; } + + com_ptr Get() { return m_d2d; } + + private: + com_ptr m_d2d; +}; + +} // namespace winrt::RNSVG::implementation +namespace winrt::RNSVG::factory_implementation { +struct D2DDevice : D2DDeviceT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/D2DDeviceContext.cpp b/windows/RNSVG/D2DDeviceContext.cpp new file mode 100644 index 00000000..46a88371 --- /dev/null +++ b/windows/RNSVG/D2DDeviceContext.cpp @@ -0,0 +1,5 @@ +#include "pch.h" +#include "D2DDeviceContext.h" +#include "D2DDeviceContext.g.cpp" + +namespace winrt::RNSVG::implementation {} diff --git a/windows/RNSVG/D2DDeviceContext.h b/windows/RNSVG/D2DDeviceContext.h new file mode 100644 index 00000000..7a57d300 --- /dev/null +++ b/windows/RNSVG/D2DDeviceContext.h @@ -0,0 +1,19 @@ +#pragma once +#include "D2DDeviceContext.g.h" + +namespace winrt::RNSVG::implementation { +struct D2DDeviceContext : D2DDeviceContextT { + public: + D2DDeviceContext() = default; + D2DDeviceContext(com_ptr const & deviceContext) { m_d2d = deviceContext;} + + com_ptr Get() { return m_d2d; } + + private: + com_ptr m_d2d; +}; + +} // namespace winrt::RNSVG::implementation +namespace winrt::RNSVG::factory_implementation { +struct D2DDeviceContext : D2DDeviceContextT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/D2DGeometry.cpp b/windows/RNSVG/D2DGeometry.cpp new file mode 100644 index 00000000..f380d255 --- /dev/null +++ b/windows/RNSVG/D2DGeometry.cpp @@ -0,0 +1,7 @@ +#include "pch.h" +#include "D2DGeometry.h" +#include "D2DGeometry.g.cpp" + +namespace winrt::RNSVG::implementation +{ +} diff --git a/windows/RNSVG/D2DGeometry.h b/windows/RNSVG/D2DGeometry.h new file mode 100644 index 00000000..a662f2d1 --- /dev/null +++ b/windows/RNSVG/D2DGeometry.h @@ -0,0 +1,19 @@ +#pragma once +#include "D2DGeometry.g.h" + +namespace winrt::RNSVG::implementation { +struct D2DGeometry : D2DGeometryT { + public: + D2DGeometry() = default; + D2DGeometry(com_ptr const &geometry) {m_d2d = geometry;} + + com_ptr Get() { return m_d2d; } + + private: + com_ptr m_d2d; +}; + +} // namespace winrt::RNSVG::implementation +namespace winrt::RNSVG::factory_implementation { +struct D2DGeometry : D2DGeometryT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/D2DHelpers.h b/windows/RNSVG/D2DHelpers.h new file mode 100644 index 00000000..992414fa --- /dev/null +++ b/windows/RNSVG/D2DHelpers.h @@ -0,0 +1,225 @@ +#pragma once + +#include "pch.h" + +#include "dwrite.h" + +namespace winrt::RNSVG { +struct D2DHelpers { + public: + + static void PushOpacityLayer( + ID2D1DeviceContext *deviceContext, + ID2D1Geometry *clipPathGeometry, + float opacity) { + com_ptr opacityLayer; + check_hresult(deviceContext->CreateLayer(nullptr, opacityLayer.put())); + + D2D1_LAYER_PARAMETERS layerParams{D2D1::LayerParameters()}; + layerParams.opacity = opacity; + + if (clipPathGeometry) { + layerParams.geometricMask = clipPathGeometry; + } + + deviceContext->PushLayer(layerParams, opacityLayer.get()); + } + + static void PushOpacityLayer( + ID2D1DeviceContext *deviceContext, + ID2D1Geometry *clipPathGeometry, + float opacity, + D2D1_MATRIX_3X2_F transform) { + com_ptr opacityLayer; + check_hresult(deviceContext->CreateLayer(nullptr, opacityLayer.put())); + + D2D1_LAYER_PARAMETERS layerParams{D2D1::LayerParameters()}; + layerParams.opacity = opacity; + layerParams.maskTransform = transform; + + if (clipPathGeometry) { + layerParams.geometricMask = clipPathGeometry; + } + + deviceContext->PushLayer(layerParams, opacityLayer.get()); + } + + static D2D1_CAP_STYLE GetLineCap(RNSVG::LineCap const &lineCap) { + switch (lineCap) { + case RNSVG::LineCap::Square: + return D2D1_CAP_STYLE_SQUARE; + case RNSVG::LineCap::Round: + return D2D1_CAP_STYLE_ROUND; + case RNSVG::LineCap::Butt: + default: + return D2D1_CAP_STYLE_FLAT; + } + } + + static D2D1_LINE_JOIN GetLineJoin(RNSVG::LineJoin const &lineJoin) { + switch (lineJoin) { + case RNSVG::LineJoin::Bevel: + return D2D1_LINE_JOIN_BEVEL; + case RNSVG::LineJoin::Round: + return D2D1_LINE_JOIN_ROUND; + case RNSVG::LineJoin::Miter: + default: + return D2D1_LINE_JOIN_MITER; + } + } + + static D2D1_FILL_MODE GetFillRule(RNSVG::FillRule const &fillRule) { + switch (fillRule) { + case RNSVG::FillRule::EvenOdd: + return D2D1_FILL_MODE_ALTERNATE; + case RNSVG::FillRule::NonZero: + default: + return D2D1_FILL_MODE_WINDING; + } + } + + static D2D1::ColorF AsD2DColor(ui::Color const &color) { + return { + color.R / 255.0f, + color.G / 255.0f, + color.B / 255.0f, + color.A / 255.0f}; + } + + static ui::Color FromD2DColor(D2D1::ColorF const color) { + return ui::ColorHelper::FromArgb( + static_cast(color.a), + static_cast(color.r), + static_cast(color.g), + static_cast(color.b)); + } + + static D2D1_RECT_F AsD2DRect(Rect const &rect) { + return {rect.X, rect.Y, rect.Width + rect.X, rect.Height + rect.Y}; + } + + static Rect FromD2DRect(D2D1_RECT_F const rect) { + return {rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top}; + } + + static Size SizeFromD2DRect(D2D1_RECT_F const rect) { + return {rect.right - rect.left, rect.bottom - rect.top}; + } + + static float WidthFromD2DRect(D2D1_RECT_F const rect) { + return rect.right - rect.left; + } + + static float HeightFromD2DRect(D2D1_RECT_F const rect) { + return rect.bottom - rect.top; + } + + static Numerics::float3x2 FromD2DTransform(D2D1_MATRIX_3X2_F const transform) { + return Numerics::float3x2( + transform.m11, transform.m12, transform.m21, transform.m22, transform._31, transform._32); + } + + static D2D1::Matrix3x2F AsD2DTransform(Numerics::float3x2 const &transform) { + return D2D1::Matrix3x2F(transform.m11, transform.m12, transform.m21, transform.m22, transform.m31, transform.m32); + } + + static D2D1_MATRIX_3X2_F GetTransform(ID2D1DeviceContext* deviceContext) { + D2D1_MATRIX_3X2_F transform; + deviceContext->GetTransform(&transform); + + return transform; + } + + static DWRITE_FONT_WEIGHT FontWeightFrom(hstring const &weight, xaml::FrameworkElement const &parent) { + if (weight == L"normal") { + return DWRITE_FONT_WEIGHT_NORMAL; + } else if (weight == L"bold") { + return DWRITE_FONT_WEIGHT_BOLD; + } + + auto const &groupView{parent.try_as()}; + DWRITE_FONT_WEIGHT parentWeight{ + groupView ? D2DHelpers::FontWeightFrom(groupView.FontWeight(), groupView.SvgParent()) : DWRITE_FONT_WEIGHT_NORMAL}; + + if (weight == L"bolder") { + return D2DHelpers::Bolder(parentWeight); + } else if (weight == L"lighter") { + return D2DHelpers::Lighter(parentWeight); + } else if (weight == L"auto") { + return parentWeight; + } + + return D2DHelpers::GetClosestFontWeight(std::stof(weight.c_str(), nullptr)); + } + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#meaning_of_relative_weights + static DWRITE_FONT_WEIGHT Bolder(DWRITE_FONT_WEIGHT weight) { + switch (weight) { + case DWRITE_FONT_WEIGHT_THIN: + case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: + case DWRITE_FONT_WEIGHT_LIGHT: + case DWRITE_FONT_WEIGHT_SEMI_LIGHT: + return DWRITE_FONT_WEIGHT_NORMAL; + case DWRITE_FONT_WEIGHT_NORMAL: + case DWRITE_FONT_WEIGHT_MEDIUM: + case DWRITE_FONT_WEIGHT_SEMI_BOLD: + return DWRITE_FONT_WEIGHT_BOLD; + case DWRITE_FONT_WEIGHT_BOLD: + case DWRITE_FONT_WEIGHT_EXTRA_BOLD: + return DWRITE_FONT_WEIGHT_BLACK; + case DWRITE_FONT_WEIGHT_BLACK: + default: + return DWRITE_FONT_WEIGHT_EXTRA_BLACK; + } + } + + static DWRITE_FONT_WEIGHT Lighter(DWRITE_FONT_WEIGHT weight) { + switch (weight) { + case DWRITE_FONT_WEIGHT_THIN: + case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: + case DWRITE_FONT_WEIGHT_LIGHT: + case DWRITE_FONT_WEIGHT_SEMI_LIGHT: + case DWRITE_FONT_WEIGHT_NORMAL: + case DWRITE_FONT_WEIGHT_MEDIUM: + return DWRITE_FONT_WEIGHT_THIN; + case DWRITE_FONT_WEIGHT_SEMI_BOLD: + case DWRITE_FONT_WEIGHT_BOLD: + return DWRITE_FONT_WEIGHT_NORMAL; + case DWRITE_FONT_WEIGHT_EXTRA_BOLD: + case DWRITE_FONT_WEIGHT_BLACK: + default: + return DWRITE_FONT_WEIGHT_BOLD; + } + } + + static DWRITE_FONT_WEIGHT GetClosestFontWeight(float weight) { + if (weight > 325 && weight < 375) { + return DWRITE_FONT_WEIGHT_SEMI_LIGHT; + } else if (weight > 925) { + return DWRITE_FONT_WEIGHT_EXTRA_BLACK; + } else { + switch (static_cast(std::round(weight / 100.0f))) { + case 1: + return DWRITE_FONT_WEIGHT_THIN; + case 2: + return DWRITE_FONT_WEIGHT_EXTRA_LIGHT; + case 3: + return DWRITE_FONT_WEIGHT_LIGHT; + case 4: + return DWRITE_FONT_WEIGHT_NORMAL; + case 5: + return DWRITE_FONT_WEIGHT_MEDIUM; + case 6: + return DWRITE_FONT_WEIGHT_SEMI_BOLD; + case 7: + return DWRITE_FONT_WEIGHT_BOLD; + case 8: + return DWRITE_FONT_WEIGHT_EXTRA_BOLD; + case 9: + default: + return DWRITE_FONT_WEIGHT_BLACK; + } + } + } +}; +} // namespace winrt::RNSVG diff --git a/windows/RNSVG/DefsView.cpp b/windows/RNSVG/DefsView.cpp index 018f6f29..0a8db1cc 100644 --- a/windows/RNSVG/DefsView.cpp +++ b/windows/RNSVG/DefsView.cpp @@ -3,8 +3,7 @@ #include "DefsView.g.cpp" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; namespace winrt::RNSVG::implementation { -void DefsView::Render(UI::Xaml::CanvasControl const &/*canvas*/, CanvasDrawingSession const &/*session*/) {} +void DefsView::Draw(RNSVG::D2DDeviceContext const& /*deviceContext*/, Size const & /*size*/) {} } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/DefsView.h b/windows/RNSVG/DefsView.h index 6f2c2f01..55df097f 100644 --- a/windows/RNSVG/DefsView.h +++ b/windows/RNSVG/DefsView.h @@ -6,9 +6,7 @@ namespace winrt::RNSVG::implementation { struct DefsView : DefsViewT { DefsView() = default; - void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/EllipseView.cpp b/windows/RNSVG/EllipseView.cpp index 54693d4f..2a0bb333 100644 --- a/windows/RNSVG/EllipseView.cpp +++ b/windows/RNSVG/EllipseView.cpp @@ -6,7 +6,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -31,14 +30,22 @@ void EllipseView::UpdateProperties(IJSValueReader const &reader, bool forceUpdat __super::UpdateProperties(reader, forceUpdate, invalidate); } -void EllipseView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; +void EllipseView::CreateGeometry() { + auto const root{SvgRoot()}; - float cx{Utils::GetAbsoluteLength(m_cx, canvas.Size().Width)}; - float cy{Utils::GetAbsoluteLength(m_cy, canvas.Size().Height)}; - float rx{Utils::GetAbsoluteLength(m_rx, canvas.Size().Width)}; - float ry{Utils::GetAbsoluteLength(m_ry, canvas.Size().Height)}; + float cx{Utils::GetAbsoluteLength(m_cx, root.ActualWidth())}; + float cy{Utils::GetAbsoluteLength(m_cy, root.ActualHeight())}; + float rx{Utils::GetAbsoluteLength(m_rx, root.ActualWidth())}; + float ry{Utils::GetAbsoluteLength(m_ry, root.ActualHeight())}; - Geometry(Geometry::CanvasGeometry::CreateEllipse(resourceCreator, cx, cy, rx, ry)); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; + + com_ptr factory; + deviceContext->GetFactory(factory.put()); + + com_ptr geometry; + check_hresult(factory->CreateEllipseGeometry(D2D1::Ellipse({cx, cy}, rx, ry), geometry.put())); + + Geometry(make(geometry.as())); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/EllipseView.h b/windows/RNSVG/EllipseView.h index 7a58ac4f..2e483713 100644 --- a/windows/RNSVG/EllipseView.h +++ b/windows/RNSVG/EllipseView.h @@ -7,7 +7,7 @@ struct EllipseView : EllipseViewT()}; - std::vector geometries; - for (auto const &child : Children()) { +void GroupView::CreateGeometry() { + std::vector geometries; + for (auto const child : Children()) { if (!child.Geometry()) { - child.CreateGeometry(canvas); + child.CreateGeometry(); + } + + if (child.Geometry()) { + com_ptr geometry{get_self(child.Geometry())->Get()}; + + // This takes advantage of the fact that geometry elements are alive for + // the duration of this method and so are their D2D resources. + geometries.emplace_back(geometry.get()); } - geometries.push_back(child.Geometry()); } - Geometry(Geometry::CanvasGeometry::CreateGroup(resourceCreator, geometries, FillRule())); + if (!geometries.empty()) { + com_ptr deviceContext{get_self(SvgRoot().DeviceContext())->Get()}; + + com_ptr factory; + deviceContext->GetFactory(factory.put()); + + com_ptr group; + check_hresult(factory->CreateGeometryGroup( + D2DHelpers::GetFillRule(FillRule()), &geometries[0], static_cast(geometries.size()), group.put())); + + Geometry(make(group.as())); + } } void GroupView::SaveDefinition() { @@ -130,36 +146,43 @@ void GroupView::MergeProperties(RNSVG::RenderableView const &other) { } } -void GroupView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { - auto const &transform{session.Transform()}; +void GroupView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { + com_ptr deviceContext{get_self(context)->Get()}; + + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; if (m_propSetMap[RNSVG::BaseProp::Matrix]) { - session.Transform(transform * SvgTransform()); + deviceContext->SetTransform(D2DHelpers::AsD2DTransform(SvgTransform()) * transform); } - auto const &clipPathGeometry{ClipPathGeometry()}; + com_ptr clipPathGeometry; - if (auto const &opacityLayer{clipPathGeometry ? session.CreateLayer(m_opacity, clipPathGeometry) : session.CreateLayer(m_opacity)}) { - if (Children().Size() == 0) { - __super::Render(canvas, session); - } else { - RenderGroup(canvas, session); - } - - opacityLayer.Close(); + if (ClipPathGeometry()) { + clipPathGeometry = get_self(ClipPathGeometry())->Get(); } - session.Transform(transform); + + D2DHelpers::PushOpacityLayer(deviceContext.get(), clipPathGeometry.get(), m_opacity); + + if (Children().Size() == 0) { + __super::Draw(context, size); + } else { + DrawGroup(context, size); + } + + deviceContext->PopLayer(); + + deviceContext->SetTransform(transform); } -void GroupView::RenderGroup(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { +void GroupView::DrawGroup(RNSVG::D2DDeviceContext const &context, Size const &size) { for (auto const &child : Children()) { - child.Render(canvas, session); + child.Draw(context, size); } } -void GroupView::CreateResources(ICanvasResourceCreator const &resourceCreator, UI::CanvasCreateResourcesEventArgs const &args) { +void GroupView::CreateResources() { for (auto const &child : Children()) { - child.CreateResources(resourceCreator, args); + child.CreateResources(); } } @@ -175,7 +198,7 @@ void GroupView::Unload() { __super::Unload(); } -winrt::RNSVG::IRenderable GroupView::HitTest(Windows::Foundation::Point const &point) { +winrt::RNSVG::IRenderable GroupView::HitTest(Point const &point) { RNSVG::IRenderable renderable{nullptr}; if (IsResponsible()) { for (auto const &child : Children()) { @@ -185,15 +208,19 @@ winrt::RNSVG::IRenderable GroupView::HitTest(Windows::Foundation::Point const &p } if (renderable && !renderable.IsResponsible()) { return *this; - } else if (!renderable){ + } else if (!renderable) { if (!Geometry()) { - if (auto const &svgRoot{SvgRoot()}) { - CreateGeometry(svgRoot.Canvas()); - } + CreateGeometry(); } + if (Geometry()) { - auto const &bounds{Geometry().ComputeBounds()}; - if (Windows::UI::Xaml::RectHelper::Contains(bounds, point)) { + com_ptr geometry{get_self(Geometry())->Get()}; + + D2D1_RECT_F bounds; + check_hresult(geometry->GetBounds(nullptr, &bounds)); + + + if (xaml::RectHelper::Contains(D2DHelpers::FromD2DRect(bounds), point)) { return *this; } } diff --git a/windows/RNSVG/GroupView.h b/windows/RNSVG/GroupView.h index 8b2c916e..d9b41973 100644 --- a/windows/RNSVG/GroupView.h +++ b/windows/RNSVG/GroupView.h @@ -20,23 +20,17 @@ struct GroupView : GroupViewT void FontWeight(hstring const &value) { m_fontWeight = value; } virtual void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - virtual void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); + virtual void CreateGeometry(); virtual void SaveDefinition(); virtual void MergeProperties(RNSVG::RenderableView const &other); - virtual void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); - virtual void RenderGroup( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + virtual void DrawGroup(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); - virtual void CreateResources( - Microsoft::Graphics::Canvas::ICanvasResourceCreator const &resourceCreator, - Microsoft::Graphics::Canvas::UI::CanvasCreateResourcesEventArgs const &args); + virtual void CreateResources(); virtual void Unload(); diff --git a/windows/RNSVG/GroupViewManager.cpp b/windows/RNSVG/GroupViewManager.cpp index 5a39f849..742e4d0d 100644 --- a/windows/RNSVG/GroupViewManager.cpp +++ b/windows/RNSVG/GroupViewManager.cpp @@ -48,7 +48,7 @@ void GroupViewManager::AddView(FrameworkElement const &parent, UIElement const & } if (auto const &root{groupView.SvgRoot()}) { - root.InvalidateCanvas(); + root.Invalidate(); } } } @@ -64,7 +64,7 @@ void GroupViewManager::RemoveAllChildren(FrameworkElement const &parent) { groupView.Children().Clear(); if (auto const &root{groupView.SvgRoot()}) { - root.InvalidateCanvas(); + root.Invalidate(); } } } @@ -78,7 +78,7 @@ void GroupViewManager::RemoveChildAt(FrameworkElement const &parent, int64_t ind groupView.Children().RemoveAt(static_cast(index)); if (auto const &root{groupView.SvgRoot()}) { - root.InvalidateCanvas(); + root.Invalidate(); } } } @@ -103,7 +103,7 @@ void GroupViewManager::ReplaceChild( newChildView.MergeProperties(groupView); if (auto const &root{groupView.SvgRoot()}) { - root.InvalidateCanvas(); + root.Invalidate(); } } } diff --git a/windows/RNSVG/ImageView.cpp b/windows/RNSVG/ImageView.cpp index 6e72517a..9bca0475 100644 --- a/windows/RNSVG/ImageView.cpp +++ b/windows/RNSVG/ImageView.cpp @@ -2,7 +2,6 @@ #include "ImageView.h" #include "ImageView.g.cpp" -#include #include #include #include @@ -10,12 +9,14 @@ #include "Utils.h" -using namespace winrt; -using namespace Microsoft::Graphics::Canvas; -using namespace Microsoft::ReactNative; -using namespace Windows::Security::Cryptography; -using namespace Windows::Storage::Streams; -using namespace Windows::Web::Http; +#include +#include +#include + +using namespace winrt::Microsoft::ReactNative; +using namespace winrt::Windows::Security::Cryptography; +using namespace winrt::Windows::Storage::Streams; +using namespace winrt::Windows::Web::Http; namespace winrt::RNSVG::implementation { void ImageView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, bool invalidate) { @@ -40,7 +41,7 @@ void ImageView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, m_source.height = 0; if (SvgParent()) { - LoadImageSourceAsync(SvgRoot().Canvas(), true); + LoadImageSourceAsync(true); } } else if (key == "width") { m_source.width = Utils::JSValueAsFloat(value); @@ -77,16 +78,31 @@ void ImageView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void ImageView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { - if (m_source.width == 0 || m_source.height == 0) { - m_source.width = canvas.Size().Width; - m_source.height = canvas.Size().Height; +void ImageView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { + if (!m_wicbitmap) { + return; } - float x{Utils::GetAbsoluteLength(m_x, canvas.Size().Width)}; - float y{Utils::GetAbsoluteLength(m_y, canvas.Size().Height)}; - float width{Utils::GetAbsoluteLength(m_width, canvas.Size().Width)}; - float height{Utils::GetAbsoluteLength(m_height, canvas.Size().Height)}; + com_ptr deviceContext{get_self(context)->Get()}; + + uint32_t imgWidth, imgHeight; + check_hresult(m_wicbitmap->GetSize(&imgWidth, &imgHeight)); + + m_source.width = static_cast(imgWidth); + m_source.height = static_cast(imgHeight); + + com_ptr bitmap; + check_hresult(deviceContext->CreateBitmapFromWicBitmap(m_wicbitmap.get(), nullptr, bitmap.put())); + + if (m_source.width == 0 || m_source.height == 0) { + m_source.width = size.Width; + m_source.height = size.Height; + } + + float x{Utils::GetAbsoluteLength(m_x, size.Width)}; + float y{Utils::GetAbsoluteLength(m_y, size.Height)}; + float width{Utils::GetAbsoluteLength(m_width, size.Width)}; + float height{Utils::GetAbsoluteLength(m_height, size.Height)}; if (width == 0) { width = m_source.width * m_source.scale; @@ -96,52 +112,61 @@ void ImageView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSessi height = m_source.height * m_source.scale; } - Effects::Transform2DEffect transformEffect{nullptr}; - if (m_align != "") { - Rect elRect{x, y, width, height}; - Rect vbRect{0, 0, m_source.width, m_source.height}; - transformEffect = Effects::Transform2DEffect{}; - transformEffect.TransformMatrix(Utils::GetViewBoxTransform(vbRect, elRect, m_align, m_meetOrSlice)); + com_ptr clipPathGeometry; + if (ClipPathGeometry()) { + clipPathGeometry = get_self(ClipPathGeometry())->Get(); } - auto const &clipPathGeometry{ClipPathGeometry()}; + D2DHelpers::PushOpacityLayer(deviceContext.get(), clipPathGeometry.get(), m_opacity); - if (auto const &opacityLayer{clipPathGeometry ? session.CreateLayer(m_opacity, clipPathGeometry) : session.CreateLayer(m_opacity)}) { - if (m_source.format == ImageSourceFormat::Bitmap && m_bitmap) { - auto const &transform{session.Transform()}; - if (m_propSetMap[RNSVG::BaseProp::Matrix]) { - session.Transform(SvgTransform()); - } + if (m_source.format == ImageSourceFormat::Bitmap) { + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; - if (m_align != "" && transformEffect) { - transformEffect.Source(m_bitmap); - Effects::CropEffect cropEffect{}; - cropEffect.SourceRectangle({x, y, width, height}); - cropEffect.Source(transformEffect); - session.DrawImage(cropEffect); - } else { - session.DrawImage(m_bitmap, {x, y, width, height}); - } - - session.Transform(transform); + if (m_propSetMap[RNSVG::BaseProp::Matrix]) { + deviceContext->SetTransform(transform * D2DHelpers::AsD2DTransform(SvgTransform())); } - opacityLayer.Close(); + if (m_align != "") { + com_ptr bitmapEffects; + check_hresult(deviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); + check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbitmap.get())); + + com_ptr transformEffect; + Rect elRect{x, y, width, height}; + Rect vbRect{0, 0, m_source.width, m_source.height}; + deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, transformEffect.put()); + transformEffect->SetValue( + D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, + Utils::GetViewBoxTransform(vbRect, elRect, m_align, m_meetOrSlice)); + transformEffect->SetInputEffect(0, bitmapEffects.get()); + + com_ptr cropEffect; + deviceContext->CreateEffect(CLSID_D2D1Crop, cropEffect.put()); + cropEffect->SetValue(D2D1_CROP_PROP_RECT, D2D1::RectF(x, y, width, height)); + cropEffect->SetInputEffect(0, transformEffect.get()); + + deviceContext->DrawImage(cropEffect.get()); + } else { + deviceContext->DrawBitmap(bitmap.get()); + } + + deviceContext->SetTransform(transform); } + + deviceContext->PopLayer(); } -void ImageView::CreateResources(ICanvasResourceCreator const &resourceCreator, UI::CanvasCreateResourcesEventArgs const &args) { - args.TrackAsyncAction(LoadImageSourceAsync(resourceCreator, false)); +void ImageView::CreateResources() { + LoadImageSourceAsync(true); } void ImageView::Unload() { - if (m_bitmap) { - m_bitmap.Close(); - m_bitmap = nullptr; + if (m_wicbitmap) { + m_wicbitmap = nullptr; } } -IAsyncAction ImageView::LoadImageSourceAsync(ICanvasResourceCreator resourceCreator, bool invalidate) { +IAsyncAction ImageView::LoadImageSourceAsync(bool invalidate) { Uri uri{m_source.uri}; hstring scheme{uri ? uri.SchemeName() : L""}; hstring ext{uri ? uri.Extension() : L""}; @@ -179,22 +204,18 @@ IAsyncAction ImageView::LoadImageSourceAsync(ICanvasResourceCreator resourceCrea } if (stream) { - m_bitmap = co_await CanvasBitmap::LoadAsync(resourceCreator, stream); - } else { - m_bitmap = co_await CanvasBitmap::LoadAsync(resourceCreator, uri); + generateBitmap(stream); } - m_source.width = m_bitmap.Size().Width; - m_source.height = m_bitmap.Size().Height; - if (invalidate) { if (auto strong_this{weak_this.get()}) { - strong_this->SvgRoot().InvalidateCanvas(); + strong_this->SvgRoot().Invalidate(); } } } -IAsyncOperation ImageView::GetImageMemoryStreamAsync(ImageSource source) { +IAsyncOperation ImageView::GetImageMemoryStreamAsync( + ImageSource source) { switch (source.type) { case ImageSourceType::Download: co_return co_await GetImageStreamAsync(source); @@ -205,7 +226,8 @@ IAsyncOperation ImageView::GetImageMemoryStreamAsync } } -IAsyncOperation ImageView::GetImageStreamAsync(ImageSource source) { +IAsyncOperation ImageView::GetImageStreamAsync( + ImageSource source) { try { co_await resume_background(); @@ -241,7 +263,8 @@ IAsyncOperation ImageView::GetImageStreamAsync(Image co_return nullptr; } -IAsyncOperation ImageView::GetImageInlineDataAsync(ImageSource source) { +IAsyncOperation ImageView::GetImageInlineDataAsync( + ImageSource source) { std::string uri{to_string(source.uri)}; size_t start = uri.find(','); @@ -260,10 +283,57 @@ IAsyncOperation ImageView::GetImageInlineDataAsync(I memoryStream.Seek(0); co_return memoryStream; - } catch (winrt::hresult_error const &) { + } catch (hresult_error const &) { // Base64 decode failed } co_return nullptr; } + +com_ptr ImageView::wicBitmapSourceFromStream(InMemoryRandomAccessStream const &results) { + if (!results) { + return nullptr; + } + + com_ptr imagingFactory; + check_hresult(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(imagingFactory.put()))); + + com_ptr istream; + check_hresult(CreateStreamOverRandomAccessStream(results.as<::IUnknown>().get(), __uuidof(IStream), istream.put_void())); + + com_ptr bitmapDecoder; + if (imagingFactory->CreateDecoderFromStream( + istream.get(), nullptr, WICDecodeMetadataCacheOnDemand, bitmapDecoder.put()) < 0) { + return nullptr; + } + + com_ptr decodedFrame; + check_hresult(bitmapDecoder->GetFrame(0, decodedFrame.put())); + return decodedFrame; +} + +void ImageView::generateBitmap(InMemoryRandomAccessStream const &results) { + com_ptr decodedFrame = wicBitmapSourceFromStream(results); + + if (!decodedFrame) { + return; + } + + com_ptr imagingFactory; + check_hresult( + CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(imagingFactory.put()))); + com_ptr converter; + check_hresult(imagingFactory->CreateFormatConverter(converter.put())); + + check_hresult(converter->Initialize( + decodedFrame.get(), + GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, + nullptr, + 0.0f, + WICBitmapPaletteTypeMedianCut)); + + check_hresult( + imagingFactory->CreateBitmapFromSource(converter.get(), WICBitmapCacheOnLoad, m_wicbitmap.put())); +} } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/ImageView.h b/windows/RNSVG/ImageView.h index 4b9cf947..084c4deb 100644 --- a/windows/RNSVG/ImageView.h +++ b/windows/RNSVG/ImageView.h @@ -22,12 +22,8 @@ struct ImageView : ImageViewT ImageView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); - void CreateResources( - Microsoft::Graphics::Canvas::ICanvasResourceCreator const &resourceCreator, - Microsoft::Graphics::Canvas::UI::CanvasCreateResourcesEventArgs const &args); + void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); + void CreateResources(); void Unload(); private: @@ -41,15 +37,19 @@ struct ImageView : ImageViewT RNSVG::MeetOrSlice m_meetOrSlice{RNSVG::MeetOrSlice::Meet}; ImageSource m_source{}; - Microsoft::Graphics::Canvas::CanvasBitmap m_bitmap{nullptr}; + com_ptr m_wicbitmap; - Windows::Foundation::IAsyncAction LoadImageSourceAsync(Microsoft::Graphics::Canvas::ICanvasResourceCreator resourceCreator, bool invalidate); - winrt::Windows::Foundation::IAsyncOperation + Windows::Foundation::IAsyncAction LoadImageSourceAsync(bool invalidate); + Windows::Foundation::IAsyncOperation GetImageMemoryStreamAsync(ImageSource source); - Windows::Foundation::IAsyncOperation + Windows::Foundation::IAsyncOperation GetImageStreamAsync(ImageSource source); - Windows::Foundation::IAsyncOperation + Windows::Foundation::IAsyncOperation GetImageInlineDataAsync(ImageSource source); + com_ptr ImageView::wicBitmapSourceFromStream( + Windows::Storage::Streams::InMemoryRandomAccessStream const &stream); + void generateBitmap( + Windows::Storage::Streams::InMemoryRandomAccessStream const &results); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/LineView.cpp b/windows/RNSVG/LineView.cpp index d1c5fd94..3d41ce65 100644 --- a/windows/RNSVG/LineView.cpp +++ b/windows/RNSVG/LineView.cpp @@ -6,7 +6,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -30,18 +29,29 @@ void LineView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void LineView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; +void LineView::CreateGeometry() { + auto const &root{SvgRoot()}; - float x1{Utils::GetAbsoluteLength(m_x1, canvas.Size().Width)}; - float y1{Utils::GetAbsoluteLength(m_y1, canvas.Size().Height)}; - float x2{Utils::GetAbsoluteLength(m_x2, canvas.Size().Width)}; - float y2{Utils::GetAbsoluteLength(m_y2, canvas.Size().Height)}; + float x1{Utils::GetAbsoluteLength(m_x1, root.ActualWidth())}; + float y1{Utils::GetAbsoluteLength(m_y1, root.ActualHeight())}; + float x2{Utils::GetAbsoluteLength(m_x2, root.ActualWidth())}; + float y2{Utils::GetAbsoluteLength(m_y2, root.ActualHeight())}; - auto const &pathBuilder{Geometry::CanvasPathBuilder(resourceCreator)}; - pathBuilder.BeginFigure(x1, y1); - pathBuilder.AddLine (x2, y2); - pathBuilder.EndFigure(Geometry::CanvasFigureLoop::Open); - Geometry(Geometry::CanvasGeometry::CreatePath(pathBuilder)); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; + + com_ptr factory; + deviceContext->GetFactory(factory.put()); + + com_ptr geometry; + check_hresult(factory->CreatePathGeometry(geometry.put())); + + com_ptr sink; + check_hresult(geometry->Open(sink.put())); + + sink->BeginFigure({x1, y1}, D2D1_FIGURE_BEGIN_FILLED); + sink->AddLine({x2, y2}); + sink->EndFigure(D2D1_FIGURE_END_OPEN); + + Geometry(make(geometry.as())); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/LineView.h b/windows/RNSVG/LineView.h index 5c086460..fd806090 100644 --- a/windows/RNSVG/LineView.h +++ b/windows/RNSVG/LineView.h @@ -7,7 +7,7 @@ struct LineView : LineViewT { public: LineView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); + void CreateGeometry(); private: RNSVG::SVGLength m_x1{}; diff --git a/windows/RNSVG/LinearGradientView.cpp b/windows/RNSVG/LinearGradientView.cpp index ec038221..15f5e816 100644 --- a/windows/RNSVG/LinearGradientView.cpp +++ b/windows/RNSVG/LinearGradientView.cpp @@ -5,7 +5,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -29,11 +28,10 @@ void LinearGradientView::UpdateProperties(IJSValueReader const &reader, bool for } else if (propertyName == "gradientUnits") { m_gradientUnits = Utils::JSValueAsBrushUnits(propertyValue); } else if (propertyName == "gradientTransform") { - m_transformSet = true; - m_transform = Utils::JSValueAsTransform(propertyValue); + m_transform = Utils::JSValueAsD2DTransform(propertyValue); if (propertyValue.IsNull()) { - m_transformSet = false; + m_transform = D2D1::Matrix3x2F::Identity(); } } } @@ -49,33 +47,47 @@ void LinearGradientView::Unload() { } void LinearGradientView::CreateBrush() { - auto const &canvas{SvgRoot().Canvas()}; - auto const &resourceCreator{canvas.try_as()}; - Brushes::CanvasLinearGradientBrush brush{resourceCreator, m_stops}; + auto const root{SvgRoot()}; - SetPoints(brush, {0, 0, canvas.Size().Width, canvas.Size().Height}); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; - if (m_transformSet) { - brush.Transform(m_transform); - } + winrt::com_ptr stopCollection; + winrt::check_hresult(deviceContext->CreateGradientStopCollection(&m_stops[0], static_cast(m_stops.size()), stopCollection.put())); - m_brush = brush; + Size size{static_cast(root.ActualWidth()), static_cast(root.ActualHeight())}; + + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES brushProperties; + brushProperties.startPoint = {0, 0}; + brushProperties.endPoint = {size.Width, size.Height}; + + winrt::com_ptr linearBrush; + winrt::check_hresult(deviceContext->CreateLinearGradientBrush(brushProperties, stopCollection.get(), linearBrush.put())); + + SetPoints(linearBrush.get(), {0, 0, size.Width, size.Height}); + + linearBrush->SetTransform(m_transform); + + m_brush = make(linearBrush.as()); } void LinearGradientView::UpdateBounds() { if (m_gradientUnits == "objectBoundingBox") { - SetPoints(m_brush.as(), m_bounds); + com_ptr brush{get_self(m_brush)->Get().as()}; + SetPoints(brush.get(), m_bounds); } } -void LinearGradientView::SetPoints(Brushes::CanvasLinearGradientBrush brush, Windows::Foundation::Rect const &bounds) { - float x1{Utils::GetAbsoluteLength(m_x1, bounds.Width) + bounds.X}; - float y1{Utils::GetAbsoluteLength(m_y1, bounds.Height) + bounds.Y}; - float x2{Utils::GetAbsoluteLength(m_x2, bounds.Width) + bounds.X}; - float y2{Utils::GetAbsoluteLength(m_y2, bounds.Height) + bounds.Y}; +void LinearGradientView::SetPoints(ID2D1LinearGradientBrush * brush, D2D1_RECT_F bounds) { + float width{D2DHelpers::WidthFromD2DRect(bounds)}; + float height{D2DHelpers::HeightFromD2DRect(bounds)}; - brush.StartPoint({x1, y1}); - brush.EndPoint({x2, y2}); + float x1{Utils::GetAbsoluteLength(m_x1, width) + bounds.left}; + float y1{Utils::GetAbsoluteLength(m_y1, height) + bounds.top}; + float x2{Utils::GetAbsoluteLength(m_x2, width) + bounds.left}; + float y2{Utils::GetAbsoluteLength(m_y2, height) + bounds.top}; + + brush->SetStartPoint({x1, y1}); + brush->SetEndPoint({x2, y2}); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/LinearGradientView.h b/windows/RNSVG/LinearGradientView.h index 071edad2..d1ce41ee 100644 --- a/windows/RNSVG/LinearGradientView.h +++ b/windows/RNSVG/LinearGradientView.h @@ -16,14 +16,12 @@ struct LinearGradientView : LinearGradientViewT m_stops{}; + std::vector m_stops{}; std::string m_gradientUnits{"objectBoundingBox"}; - bool m_transformSet{false}; - Numerics::float3x2 m_transform{Numerics::make_float3x2_scale(1)}; void CreateBrush(); void UpdateBounds(); - void SetPoints(Microsoft::Graphics::Canvas::Brushes::CanvasLinearGradientBrush brush, Windows::Foundation::Rect const &bounds); + void SetPoints(ID2D1LinearGradientBrush *brush, D2D1_RECT_F bounds); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/MarkerView.cpp b/windows/RNSVG/MarkerView.cpp new file mode 100644 index 00000000..e598c599 --- /dev/null +++ b/windows/RNSVG/MarkerView.cpp @@ -0,0 +1,8 @@ +#include "pch.h" +#include "MarkerView.h" +#include "MarkerView.g.cpp" + +using namespace winrt; +using namespace Microsoft::ReactNative; + +namespace winrt::RNSVG::implementation {} // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/MarkerView.h b/windows/RNSVG/MarkerView.h new file mode 100644 index 00000000..1d67712f --- /dev/null +++ b/windows/RNSVG/MarkerView.h @@ -0,0 +1,17 @@ +#pragma once +#include "MarkerView.g.h" +#include "GroupView.h" + +namespace winrt::RNSVG::implementation { +struct MarkerView : MarkerViewT { + public: + MarkerView() = default; + + // RenderableView + void Draw(RNSVG::D2DDeviceContext const & /*deviceContext*/, Windows::Foundation::Size const & /*size*/){}; +}; +} // namespace winrt::RNSVG::implementation + +namespace winrt::RNSVG::factory_implementation { +struct MarkerView : MarkerViewT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/MarkerViewManager.cpp b/windows/RNSVG/MarkerViewManager.cpp new file mode 100644 index 00000000..38980b7c --- /dev/null +++ b/windows/RNSVG/MarkerViewManager.cpp @@ -0,0 +1,24 @@ +#include "pch.h" +#include "MarkerViewManager.h" +#include "MarkerViewManager.g.cpp" + +using namespace winrt; +using namespace Microsoft::ReactNative; + +namespace winrt::RNSVG::implementation { +MarkerViewManager::MarkerViewManager() { + m_class = RNSVG::SVGClass::RNSVGMarker; + m_name = L"RNSVGMarker"; +} + +IMapView MarkerViewManager::NativeProps() { + auto const &parentProps{__super::NativeProps()}; + auto const &nativeProps{winrt::single_threaded_map()}; + + for (auto const &prop : parentProps) { + nativeProps.Insert(prop.Key(), prop.Value()); + } + + return nativeProps.GetView(); +} +} // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/MarkerViewManager.h b/windows/RNSVG/MarkerViewManager.h new file mode 100644 index 00000000..93139722 --- /dev/null +++ b/windows/RNSVG/MarkerViewManager.h @@ -0,0 +1,16 @@ +#pragma once +#include "MarkerViewManager.g.h" +#include "GroupViewManager.h" + +namespace winrt::RNSVG::implementation { +struct MarkerViewManager : MarkerViewManagerT { + MarkerViewManager(); + + // IViewManagerWithNativeProperties + Windows::Foundation::Collections::IMapView NativeProps(); +}; +} // namespace winrt::RNSVG::implementation + +namespace winrt::RNSVG::factory_implementation { +struct MarkerViewManager : MarkerViewManagerT {}; +} // namespace winrt::RNSVG::factory_implementation diff --git a/windows/RNSVG/PathView.cpp b/windows/RNSVG/PathView.cpp index c18d7aac..4af5a097 100644 --- a/windows/RNSVG/PathView.cpp +++ b/windows/RNSVG/PathView.cpp @@ -4,14 +4,13 @@ #include "PathView.g.cpp" #endif -#include +#include "d2d1svg.h" #include #include "JSValueXaml.h" #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -29,6 +28,7 @@ void PathView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, if (propertyValue.IsNull()) { m_d.clear(); } else { + m_d = propertyValue.AsString(); ParsePath(); } @@ -38,11 +38,32 @@ void PathView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void PathView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; - Svg::CanvasSvgDocument doc{resourceCreator}; - auto const &path{doc.CreatePathAttribute(m_segmentData, m_commands)}; - Geometry(path.CreatePathGeometry(FillRule())); +void PathView::CreateGeometry() { + auto const &root{SvgRoot()}; + + com_ptr doc; + com_ptr deviceContext{get_self(root.DeviceContext())->Get().as()}; + + check_hresult(deviceContext->CreateSvgDocument( + nullptr, + D2D1::SizeF(static_cast(root.ActualWidth()), static_cast(root.ActualHeight())), + doc.put())); + + m_segmentData.resize(m_segmentData.size()); + m_commands.resize(m_commands.size()); + + com_ptr path; + check_hresult(doc->CreatePathData( + &m_segmentData[0], + static_cast(m_segmentData.size()), + &m_commands[0], + static_cast(m_commands.size()), + path.put())); + + com_ptr geometry; + check_hresult(path->CreatePathGeometry(D2DHelpers::GetFillRule(FillRule()), geometry.put())); + + Geometry(make(geometry.as())); } void PathView::ParsePath() { diff --git a/windows/RNSVG/PathView.h b/windows/RNSVG/PathView.h index 3458f0db..160c1336 100644 --- a/windows/RNSVG/PathView.h +++ b/windows/RNSVG/PathView.h @@ -9,34 +9,34 @@ struct PathView : PathViewT { PathView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); + void CreateGeometry(); private: std::string m_d; std::vector m_segmentData; - std::vector m_commands; + std::vector m_commands; - std::map m_cmds{ - {'M', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::MoveAbsolute}, - {'m', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::MoveRelative}, - {'Z', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::ClosePath}, - {'z', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::ClosePath}, - {'L', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::LineAbsolute}, - {'l', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::LineRelative}, - {'H', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::HorizontalAbsolute}, - {'h', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::HorizontalRelative}, - {'V', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::VerticalAbsolute}, - {'v', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::VerticalRelative}, - {'C', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::CubicAbsolute}, - {'c', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::CubicRelative}, - {'S', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::CubicSmoothAbsolute}, - {'s', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::CubicSmoothRelative}, - {'Q', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::QuadraticAbsolute}, - {'q', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::QuadraticRelative}, - {'T', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::QuadraticSmoothAbsolute}, - {'t', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::QuadraticSmoothRelative}, - {'A', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::ArcAbsolute}, - {'a', Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand::ArcRelative}, + std::unordered_map m_cmds{ + {'M', D2D1_SVG_PATH_COMMAND_MOVE_ABSOLUTE}, + {'m', D2D1_SVG_PATH_COMMAND_MOVE_RELATIVE}, + {'Z', D2D1_SVG_PATH_COMMAND_CLOSE_PATH}, + {'z', D2D1_SVG_PATH_COMMAND_CLOSE_PATH}, + {'L', D2D1_SVG_PATH_COMMAND_LINE_ABSOLUTE}, + {'l', D2D1_SVG_PATH_COMMAND_LINE_RELATIVE}, + {'H', D2D1_SVG_PATH_COMMAND_HORIZONTAL_ABSOLUTE}, + {'h', D2D1_SVG_PATH_COMMAND_HORIZONTAL_RELATIVE}, + {'V', D2D1_SVG_PATH_COMMAND_VERTICAL_ABSOLUTE}, + {'v', D2D1_SVG_PATH_COMMAND_VERTICAL_RELATIVE}, + {'C', D2D1_SVG_PATH_COMMAND_CUBIC_ABSOLUTE}, + {'c', D2D1_SVG_PATH_COMMAND_CUBIC_RELATIVE}, + {'S', D2D1_SVG_PATH_COMMAND_CUBIC_SMOOTH_ABSOLUTE}, + {'s', D2D1_SVG_PATH_COMMAND_CUBIC_SMOOTH_RELATIVE}, + {'Q', D2D1_SVG_PATH_COMMAND_QUADRADIC_ABSOLUTE}, + {'q', D2D1_SVG_PATH_COMMAND_QUADRADIC_RELATIVE}, + {'T', D2D1_SVG_PATH_COMMAND_QUADRADIC_SMOOTH_ABSOLUTE}, + {'t', D2D1_SVG_PATH_COMMAND_QUADRADIC_SMOOTH_RELATIVE}, + {'A', D2D1_SVG_PATH_COMMAND_ARC_ABSOLUTE}, + {'a', D2D1_SVG_PATH_COMMAND_ARC_RELATIVE}, }; void ParsePath(); diff --git a/windows/RNSVG/PatternView.cpp b/windows/RNSVG/PatternView.cpp index eb0ae1c3..8b1c9e88 100644 --- a/windows/RNSVG/PatternView.cpp +++ b/windows/RNSVG/PatternView.cpp @@ -3,9 +3,9 @@ #include "PatternView.g.cpp" #include "Utils.h" +#include "D2DDevice.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -29,11 +29,10 @@ void PatternView::UpdateProperties(IJSValueReader const &reader, bool forceUpdat } else if (propertyName == "patternContentUnits") { m_patternContentUnits = Utils::JSValueAsBrushUnits(propertyValue, "userSpaceOnUse"); } else if (propertyName == "patternTransform") { - m_transformSet = true; - m_transform = Utils::JSValueAsTransform(propertyValue); + m_transform = Utils::JSValueAsD2DTransform(propertyValue); if (propertyValue.IsNull()) { - m_transformSet = false; + m_transform = D2D1::Matrix3x2F::Identity(); } } else if (propertyName == "vbWidth") { m_vbWidth = Utils::JSValueAsFloat(propertyValue); @@ -55,77 +54,101 @@ void PatternView::UpdateProperties(IJSValueReader const &reader, bool forceUpdat SaveDefinition(); if (auto const &root{SvgRoot()}) { - root.InvalidateCanvas(); + root.Invalidate(); } } void PatternView::UpdateBounds() { if (m_patternUnits == "objectBoundingBox") { - Rect rect{GetAdjustedRect(m_bounds)}; + com_ptr brush{get_self(m_brush)->Get().as()}; + + D2D1_RECT_F rect{GetAdjustedRect(m_bounds)}; CreateBrush(rect); } } void PatternView::CreateBrush() { - auto const &canvas{SvgRoot().Canvas()}; + auto const root{SvgRoot()}; - Rect elRect{GetAdjustedRect({0, 0, canvas.Size().Width, canvas.Size().Height})}; + D2D1_RECT_F elRect{GetAdjustedRect({0, 0, static_cast(root.ActualWidth()), static_cast(root.ActualHeight())})}; CreateBrush(elRect); } -void PatternView::CreateBrush(Windows::Foundation::Rect const &rect) { - auto const &canvas{SvgRoot().Canvas()}; - auto const &resourceCreator{canvas.try_as()}; +void PatternView::CreateBrush(D2D1_RECT_F rect) { + auto const &root{SvgRoot()}; - if (auto const &cmdList{GetCommandList(rect)}) { - Brushes::CanvasImageBrush brush{resourceCreator, cmdList}; + com_ptr device{get_self(root.Device())->Get()}; - brush.SourceRectangle(rect); - brush.ExtendX(Microsoft::Graphics::Canvas::CanvasEdgeBehavior::Wrap); - brush.ExtendY(Microsoft::Graphics::Canvas::CanvasEdgeBehavior::Wrap); + if (auto const &cmdList{GetCommandList(device.get(), rect)}) { + D2D1_IMAGE_BRUSH_PROPERTIES brushProperties{D2D1::ImageBrushProperties(rect)}; + brushProperties.extendModeX = D2D1_EXTEND_MODE_WRAP; + brushProperties.extendModeY = D2D1_EXTEND_MODE_WRAP; - cmdList.Close(); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; - m_brush = brush; + com_ptr imageBrush; + check_hresult(deviceContext->CreateImageBrush(cmdList.get(), brushProperties, imageBrush.put())); + + auto transform{D2D1::Matrix3x2F::Translation({rect.left, rect.top}) * m_transform}; + imageBrush->SetTransform(transform); + + m_brush = make(imageBrush.as()); } } -Windows::Foundation::Rect PatternView::GetAdjustedRect(Windows::Foundation::Rect const &bounds) { - float x{Utils::GetAbsoluteLength(m_x, bounds.Width) + bounds.X}; - float y{Utils::GetAbsoluteLength(m_y, bounds.Height) + bounds.Y}; - float width{Utils::GetAbsoluteLength(m_width, bounds.Width)}; - float height{Utils::GetAbsoluteLength(m_height, bounds.Height)}; +D2D1_RECT_F PatternView::GetAdjustedRect(D2D1_RECT_F bounds) { + float width{D2DHelpers::WidthFromD2DRect(bounds)}; + float height{D2DHelpers::HeightFromD2DRect(bounds)}; - return {x, y, width, height}; + float x{Utils::GetAbsoluteLength(m_x, width) + bounds.left}; + float y{Utils::GetAbsoluteLength(m_y, height) + bounds.top}; + float adjWidth{Utils::GetAbsoluteLength(m_width, width)}; + float adjHeight{Utils::GetAbsoluteLength(m_height, height)}; + + return {x, y, adjWidth + x, adjHeight + y}; } -Microsoft::Graphics::Canvas::CanvasCommandList PatternView::GetCommandList(Windows::Foundation::Rect const &rect) { - auto const &root{SvgRoot()}; - auto const &canvas{root.Canvas()}; - auto const &cmdList{CanvasCommandList(canvas)}; - auto const &session{cmdList.CreateDrawingSession()}; - +com_ptr PatternView::GetCommandList(ID2D1Device* device, D2D1_RECT_F rect) { + com_ptr deviceContext; + check_hresult(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, deviceContext.put())); + + com_ptr cmdList; + check_hresult(deviceContext->CreateCommandList(cmdList.put())); + + deviceContext->SetTarget(cmdList.get()); + + deviceContext->BeginDraw(); + deviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); + + auto transform{D2D1::Matrix3x2F::Identity()}; + if (m_align != "") { Rect vbRect{ - m_minX * root.SvgScale(), - m_minY * root.SvgScale(), - (m_vbWidth + m_minX) * root.SvgScale(), - (m_vbHeight + m_minY) * root.SvgScale()}; + m_minX, + m_minY, + (m_vbWidth + m_minX), + (m_vbHeight + m_minY)}; - auto transform{Utils::GetViewBoxTransform(vbRect, rect, m_align, m_meetOrSlice)}; + auto viewboxTransform{Utils::GetViewBoxTransform( + vbRect, + D2DHelpers::FromD2DRect(rect), + m_align, + m_meetOrSlice)}; - if (m_transformSet) { - transform = transform * m_transform; - } - - session.Transform(transform); + transform = D2DHelpers::AsD2DTransform(viewboxTransform) * transform; } + deviceContext->SetTransform(transform); + + auto context = make(deviceContext); for (auto const &child : Children()) { - child.Render(canvas, session); + child.Draw(context, D2DHelpers::SizeFromD2DRect(rect)); } - session.Close(); + cmdList->Close(); + + deviceContext->EndDraw(); + return cmdList; } diff --git a/windows/RNSVG/PatternView.h b/windows/RNSVG/PatternView.h index 4f04eb03..66d49071 100644 --- a/windows/RNSVG/PatternView.h +++ b/windows/RNSVG/PatternView.h @@ -9,6 +9,7 @@ struct PatternView : PatternViewT // RenderableView void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); + void Draw(RNSVG::D2DDeviceContext const & /*deviceContext*/, Windows::Foundation::Size const & /*size*/){}; private: RNSVG::SVGLength m_x{}; @@ -17,8 +18,6 @@ struct PatternView : PatternViewT RNSVG::SVGLength m_height{}; std::string m_patternUnits{"objectBoundingBox"}; std::string m_patternContentUnits{"userSpaceOnUse"}; - bool m_transformSet{false}; - Numerics::float3x2 m_transform{Numerics::make_float3x2_scale(1)}; // ViewBox float m_minX{0.0f}; @@ -33,9 +32,9 @@ struct PatternView : PatternViewT void UpdateBounds(); // Helpers - void CreateBrush(Windows::Foundation::Rect const &rect); - Windows::Foundation::Rect GetAdjustedRect(Windows::Foundation::Rect const &bounds); - Microsoft::Graphics::Canvas::CanvasCommandList GetCommandList(Windows::Foundation::Rect const &elRect); + void CreateBrush(D2D1_RECT_F rect); + D2D1_RECT_F GetAdjustedRect(D2D1_RECT_F bounds); + winrt::com_ptr GetCommandList(ID2D1Device* device, D2D1_RECT_F elRect); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/RNSVG.vcxproj b/windows/RNSVG/RNSVG.vcxproj index dbf027d4..11ad6f57 100644 --- a/windows/RNSVG/RNSVG.vcxproj +++ b/windows/RNSVG/RNSVG.vcxproj @@ -112,6 +112,9 @@ _DEBUG;%(PreprocessorDefinitions) + + dxguid.lib;WindowsApp.lib;%(AdditionalDependencies) + @@ -123,6 +126,11 @@ + + + + + @@ -136,6 +144,8 @@ + + @@ -170,6 +180,10 @@ + + + + @@ -182,6 +196,8 @@ + + @@ -231,7 +247,6 @@ - @@ -243,7 +258,6 @@ - @@ -254,7 +268,6 @@ - diff --git a/windows/RNSVG/RNSVG.vcxproj.filters b/windows/RNSVG/RNSVG.vcxproj.filters index f41a60d3..00e96eb2 100644 --- a/windows/RNSVG/RNSVG.vcxproj.filters +++ b/windows/RNSVG/RNSVG.vcxproj.filters @@ -130,6 +130,24 @@ Views\GroupViews + + D2DWrappers + + + ViewManagers\GroupViewManagers + + + Views\GroupViews + + + D2DWrappers + + + D2DWrappers + + + D2DWrappers + @@ -252,6 +270,27 @@ Views\GroupViews + + Utils + + + D2DWrappers + + + ViewManagers\GroupViewManagers + + + Views\GroupViews + + + D2DWrappers + + + D2DWrappers + + + D2DWrappers + @@ -276,5 +315,8 @@ {50f81ae3-d543-477a-a60c-a0f310289f29} + + {d4e16389-e870-4046-b1fe-61bc171edad9} + \ No newline at end of file diff --git a/windows/RNSVG/RadialGradientView.cpp b/windows/RNSVG/RadialGradientView.cpp index ce3dbb5d..309e31f1 100644 --- a/windows/RNSVG/RadialGradientView.cpp +++ b/windows/RNSVG/RadialGradientView.cpp @@ -5,7 +5,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -33,11 +32,10 @@ void RadialGradientView::UpdateProperties(IJSValueReader const &reader, bool for } else if (propertyName == "gradientUnits") { m_gradientUnits = Utils::JSValueAsBrushUnits(propertyValue); } else if (propertyName == "gradientTransform") { - m_transformSet = true; - m_transform = Utils::JSValueAsTransform(propertyValue); + m_transform = Utils::JSValueAsD2DTransform(propertyValue); if (propertyValue.IsNull()) { - m_transformSet = false; + m_transform = D2D1::Matrix3x2F::Identity(); } } } @@ -53,40 +51,52 @@ void RadialGradientView::Unload() { } void RadialGradientView::CreateBrush() { - auto const &canvas{SvgRoot().Canvas()}; - auto const &resourceCreator{canvas.try_as()}; - Brushes::CanvasRadialGradientBrush brush{resourceCreator, m_stops}; + auto const root{SvgRoot()}; - SetPoints(brush, {0, 0, canvas.Size().Width, canvas.Size().Height}); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; - if (m_transformSet) { - brush.Transform(m_transform); + winrt::com_ptr stopCollection; + winrt::check_hresult(deviceContext->CreateGradientStopCollection(&m_stops[0], static_cast(m_stops.size()), stopCollection.put())); + + D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES brushProperties{}; + winrt::com_ptr radialBrush; + winrt::check_hresult( + deviceContext->CreateRadialGradientBrush(brushProperties, stopCollection.get(), radialBrush.put())); + + SetPoints(radialBrush.get(), {0, 0, static_cast(root.ActualWidth()), static_cast(root.ActualHeight())}); + + if (!m_transform.IsIdentity()) { + radialBrush->SetTransform(m_transform); } - m_brush = brush; + m_brush = make(radialBrush.as()); } void RadialGradientView::UpdateBounds() { if (m_gradientUnits == "objectBoundingBox") { - SetPoints(m_brush.as(), m_bounds); + com_ptr brush{get_self(m_brush)->Get().as()}; + SetPoints(brush.get(), m_bounds); } } -void RadialGradientView::SetPoints(Brushes::CanvasRadialGradientBrush brush, Windows::Foundation::Rect const &bounds) { - float rx{Utils::GetAbsoluteLength(m_rx, bounds.Width)}; - float ry{Utils::GetAbsoluteLength(m_ry, bounds.Height)}; +void RadialGradientView::SetPoints(ID2D1RadialGradientBrush *brush, D2D1_RECT_F bounds) { + float width{D2DHelpers::WidthFromD2DRect(bounds)}; + float height{D2DHelpers::HeightFromD2DRect(bounds)}; - float fx{Utils::GetAbsoluteLength(m_fx, bounds.Width) + bounds.X}; - float fy{Utils::GetAbsoluteLength(m_fy, bounds.Height) + bounds.Y}; + float rx{Utils::GetAbsoluteLength(m_rx, width)}; + float ry{Utils::GetAbsoluteLength(m_ry, height)}; - float cx{Utils::GetAbsoluteLength(m_cx, bounds.Width) + bounds.X}; - float cy{Utils::GetAbsoluteLength(m_cy, bounds.Height) + bounds.Y}; + float fx{Utils::GetAbsoluteLength(m_fx, width) + bounds.left}; + float fy{Utils::GetAbsoluteLength(m_fy, height) + bounds.top}; - brush.RadiusX(rx); - brush.RadiusY(ry); + float cx{Utils::GetAbsoluteLength(m_cx, width) + bounds.left}; + float cy{Utils::GetAbsoluteLength(m_cy, height) + bounds.top}; - brush.Center({cx, cy}); - brush.OriginOffset({(fx - cx), (fy - cy)}); + brush->SetRadiusX(rx); + brush->SetRadiusY(ry); + + brush->SetCenter({cx, cy}); + brush->SetGradientOriginOffset({(fx - cx), (fy - cy)}); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/RadialGradientView.h b/windows/RNSVG/RadialGradientView.h index 975356e5..6ddd7c7a 100644 --- a/windows/RNSVG/RadialGradientView.h +++ b/windows/RNSVG/RadialGradientView.h @@ -18,14 +18,12 @@ struct RadialGradientView : RadialGradientViewT m_stops{}; + std::vector m_stops{}; std::string m_gradientUnits{"objectBoundingBox"}; - bool m_transformSet{false}; - Numerics::float3x2 m_transform{Numerics::make_float3x2_scale(1)}; void CreateBrush(); void UpdateBounds(); - void SetPoints(Microsoft::Graphics::Canvas::Brushes::CanvasRadialGradientBrush brush, Windows::Foundation::Rect const &bounds); + void SetPoints(ID2D1RadialGradientBrush *brush, D2D1_RECT_F bounds); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/ReactPackageProvider.cpp b/windows/RNSVG/ReactPackageProvider.cpp index 333f803c..405ea6c4 100644 --- a/windows/RNSVG/ReactPackageProvider.cpp +++ b/windows/RNSVG/ReactPackageProvider.cpp @@ -22,6 +22,7 @@ #include "RadialGradientViewManager.h" #include "PatternViewManager.h" #include "ClipPathViewManager.h" +#include "MarkerViewManager.h" using namespace winrt::Microsoft::ReactNative; @@ -47,6 +48,7 @@ namespace winrt::RNSVG::implementation packageBuilder.AddViewManager(L"RadialGradientViewManager", []() { return winrt::make(); }); packageBuilder.AddViewManager(L"PatternViewManager", []() { return winrt::make(); }); packageBuilder.AddViewManager(L"ClipPathViewManager", []() { return winrt::make(); }); + packageBuilder.AddViewManager(L"MarkerViewManager", []() { return winrt::make(); }); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/RectView.cpp b/windows/RNSVG/RectView.cpp index d4f701f9..40a3c936 100644 --- a/windows/RNSVG/RectView.cpp +++ b/windows/RNSVG/RectView.cpp @@ -8,7 +8,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -37,19 +36,28 @@ void RectView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void RectView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; +void RectView::CreateGeometry() { + auto const &root{SvgRoot()}; - float x{Utils::GetAbsoluteLength(m_x, canvas.Size().Width)}; - float y{Utils::GetAbsoluteLength(m_y, canvas.Size().Height)}; - float width{Utils::GetAbsoluteLength(m_width, canvas.Size().Width)}; - float height{Utils::GetAbsoluteLength(m_height, canvas.Size().Height)}; + float x{Utils::GetAbsoluteLength(m_x, root.ActualWidth())}; + float y{Utils::GetAbsoluteLength(m_y, root.ActualHeight())}; + float width{Utils::GetAbsoluteLength(m_width, root.ActualWidth())}; + float height{Utils::GetAbsoluteLength(m_height, root.ActualHeight())}; - auto const &rxLength{m_rx.Unit() == RNSVG::LengthType::Unknown ? m_ry : m_rx}; - auto const &ryLength{m_ry.Unit() == RNSVG::LengthType::Unknown ? m_rx : m_ry}; - float rx{Utils::GetAbsoluteLength(rxLength, canvas.Size().Width)}; - float ry{Utils::GetAbsoluteLength(ryLength, canvas.Size().Height)}; + auto const rxLength{m_rx.Unit() == RNSVG::LengthType::Unknown ? m_ry : m_rx}; + auto const ryLength{m_ry.Unit() == RNSVG::LengthType::Unknown ? m_rx : m_ry}; + float rx{Utils::GetAbsoluteLength(rxLength, root.ActualWidth())}; + float ry{Utils::GetAbsoluteLength(ryLength, root.ActualHeight())}; - Geometry(Geometry::CanvasGeometry::CreateRoundedRectangle(resourceCreator, x, y, width, height, rx, ry)); + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; + + com_ptr factory; + deviceContext->GetFactory(factory.put()); + + com_ptr geometry; + check_hresult(factory->CreateRoundedRectangleGeometry( + D2D1::RoundedRect(D2D1::RectF(x, y, width + x, height + y), rx, ry), geometry.put())); + + Geometry(make(geometry.as())); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/RectView.h b/windows/RNSVG/RectView.h index bc8c98e4..e08aa62f 100644 --- a/windows/RNSVG/RectView.h +++ b/windows/RNSVG/RectView.h @@ -9,7 +9,7 @@ struct RectView : RectViewT { RectView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); + void CreateGeometry(); private: RNSVG::SVGLength m_width{}; diff --git a/windows/RNSVG/RenderableView.cpp b/windows/RNSVG/RenderableView.cpp index 75e10acc..4033d9a8 100644 --- a/windows/RNSVG/RenderableView.cpp +++ b/windows/RNSVG/RenderableView.cpp @@ -9,7 +9,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -96,40 +95,16 @@ void RenderableView::UpdateProperties(IJSValueReader const &reader, bool forceUp if (propertyValue.IsNull()) { m_strokeLineCap = parent.StrokeLineCap(); } else { - auto const &strokeLineCap{propertyValue.AsInt32()}; - switch (strokeLineCap) { - case 2: - m_strokeLineCap = Geometry::CanvasCapStyle::Square; - break; - case 1: - m_strokeLineCap = Geometry::CanvasCapStyle::Round; - break; - case 0: - default: - m_strokeLineCap = Geometry::CanvasCapStyle::Flat; - break; - } + m_strokeLineCap = static_cast(propertyValue.AsInt32()); } } } else if (propertyName == "strokeLinejoin") { prop = RNSVG::BaseProp::StrokeLineJoin; if (forceUpdate || !m_propSetMap[prop]) { if (propertyValue.IsNull()) { - m_strokeLineCap = parent.StrokeLineCap(); + m_strokeLineJoin = parent.StrokeLineJoin(); } else { - auto const &strokeLineJoin{propertyValue.AsInt32()}; - switch (strokeLineJoin) { - case 2: - m_strokeLineJoin = Geometry::CanvasLineJoin::Bevel; - break; - case 1: - m_strokeLineJoin = Geometry::CanvasLineJoin::Round; - break; - case 0: - default: - m_strokeLineJoin = Geometry::CanvasLineJoin::Miter; - break; - } + m_strokeLineJoin = static_cast(propertyValue.AsInt32()); } } } else if (propertyName == "fillRule") { @@ -138,16 +113,7 @@ void RenderableView::UpdateProperties(IJSValueReader const &reader, bool forceUp if (propertyValue.IsNull()) { m_fillRule = parent.FillRule(); } else { - auto const &fillRule{propertyValue.AsInt32()}; - switch (fillRule) { - case 0: - m_fillRule = Geometry::CanvasFilledRegionDetermination::Alternate; - break; - case 1: - default: - m_fillRule = Geometry::CanvasFilledRegionDetermination::Winding; - break; - } + m_fillRule = static_cast(propertyValue.AsInt32()); } } } else if (propertyName == "strokeDashoffset") { @@ -213,7 +179,7 @@ void RenderableView::UpdateProperties(IJSValueReader const &reader, bool forceUp m_recreateResources = true; if (invalidate && SvgParent()) { - SvgRoot().InvalidateCanvas(); + SvgRoot().Invalidate(); } } @@ -223,47 +189,86 @@ void RenderableView::SaveDefinition() { } } -void RenderableView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { - auto const &resourceCreator{canvas.try_as()}; +void RenderableView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { if (m_recreateResources) { - CreateGeometry(canvas); + CreateGeometry(); } - auto geometry{Geometry()}; + if (!Geometry()) { + return; + } + + com_ptr geometry{get_self(m_geometry)->Get()}; + com_ptr deviceContext{get_self(context)->Get()}; + + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; + if (m_propSetMap[RNSVG::BaseProp::Matrix]) { - geometry = geometry.Transform(SvgTransform()); + deviceContext->SetTransform(D2DHelpers::AsD2DTransform(SvgTransform()) * transform); } - auto const &clipPathGeometry{ClipPathGeometry()}; + com_ptr factory; + deviceContext->GetFactory(factory.put()); - geometry = Geometry::CanvasGeometry::CreateGroup(resourceCreator, {geometry}, FillRule()); + com_ptr geometryGroup; + ID2D1Geometry *geometries[] = {geometry.get()}; + check_hresult(factory->CreateGeometryGroup(D2DHelpers::GetFillRule(FillRule()), geometries, 1, geometryGroup.put())); - if (auto const &opacityLayer{clipPathGeometry ? session.CreateLayer(m_opacity, clipPathGeometry) : session.CreateLayer(m_opacity)}) { - if (auto const &fillLayer{session.CreateLayer(FillOpacity())}) { - auto const &fill{Utils::GetCanvasBrush(FillBrushId(), Fill(), SvgRoot(), geometry, resourceCreator)}; - session.FillGeometry(geometry, fill); - fillLayer.Close(); - } + geometry = geometryGroup; - if (auto const &strokeLayer{session.CreateLayer(StrokeOpacity())}) { - Geometry::CanvasStrokeStyle strokeStyle{}; - strokeStyle.StartCap(StrokeLineCap()); - strokeStyle.EndCap(StrokeLineCap()); - strokeStyle.LineJoin(StrokeLineJoin()); - strokeStyle.DashOffset(StrokeDashOffset()); - strokeStyle.MiterLimit(StrokeMiterLimit()); - - float canvasDiagonal{Utils::GetCanvasDiagonal(canvas.Size())}; - float strokeWidth{Utils::GetAbsoluteLength(StrokeWidth(), canvasDiagonal)}; - strokeStyle.CustomDashStyle(Utils::GetAdjustedStrokeArray(StrokeDashArray(), strokeWidth, canvasDiagonal)); - - auto const &stroke{Utils::GetCanvasBrush(StrokeBrushId(), Stroke(), SvgRoot(), geometry, resourceCreator)}; - session.DrawGeometry(geometry, stroke, strokeWidth, strokeStyle); - strokeLayer.Close(); - } - - opacityLayer.Close(); + com_ptr clipPathGeometry; + if (ClipPathGeometry()) { + clipPathGeometry = get_self(ClipPathGeometry())->Get(); } + + D2DHelpers::PushOpacityLayer(deviceContext.get(), clipPathGeometry.get(), m_opacity); + + if (FillOpacity()) { + D2DHelpers::PushOpacityLayer(deviceContext.get(), clipPathGeometry.get(), FillOpacity()); + + auto fill{Utils::GetCanvasBrush(FillBrushId(), Fill(), SvgRoot(), geometry)}; + deviceContext->FillGeometry(geometry.get(), fill.get()); + + deviceContext->PopLayer(); + } + + if (StrokeOpacity()) { + D2DHelpers::PushOpacityLayer(deviceContext.get(), clipPathGeometry.get(), StrokeOpacity()); + + D2D1_CAP_STYLE capStyle{D2DHelpers::GetLineCap(m_strokeLineCap)}; + D2D1_LINE_JOIN lineJoin{D2DHelpers::GetLineJoin(m_strokeLineJoin)}; + + D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties; + strokeStyleProperties.startCap = capStyle; + strokeStyleProperties.endCap = capStyle; + strokeStyleProperties.dashCap = capStyle; + strokeStyleProperties.lineJoin = lineJoin; + strokeStyleProperties.dashOffset = StrokeDashOffset(); + strokeStyleProperties.miterLimit = StrokeMiterLimit(); + strokeStyleProperties.dashStyle = D2D1_DASH_STYLE_SOLID; + + float canvasDiagonal{Utils::GetCanvasDiagonal(size)}; + float strokeWidth{Utils::GetAbsoluteLength(StrokeWidth(), canvasDiagonal)}; + + float *dashArray = nullptr; + if (StrokeDashArray().Size() > 0) { + strokeStyleProperties.dashStyle = D2D1_DASH_STYLE_CUSTOM; + m_adjustedStrokeDashArray = Utils::GetAdjustedStrokeArray(StrokeDashArray(), strokeWidth, canvasDiagonal); + dashArray = &m_adjustedStrokeDashArray[0]; + } + + com_ptr strokeStyle; + check_hresult( + factory->CreateStrokeStyle(strokeStyleProperties, dashArray, m_strokeDashArray.Size(), strokeStyle.put())); + + auto const stroke{Utils::GetCanvasBrush(StrokeBrushId(), Stroke(), SvgRoot(), geometry)}; + deviceContext->DrawGeometry(geometry.get(), stroke.get(), strokeWidth, strokeStyle.get()); + deviceContext->PopLayer(); + } + + deviceContext->PopLayer(); + + deviceContext->SetTransform(transform); } void RenderableView::MergeProperties(RNSVG::RenderableView const &other) { @@ -331,11 +336,11 @@ RNSVG::SvgView RenderableView::SvgRoot() { return nullptr; } -Geometry::CanvasGeometry RenderableView::ClipPathGeometry() { +RNSVG::D2DGeometry RenderableView::ClipPathGeometry() { if (!m_clipPathId.empty()) { if (auto const &clipPath{SvgRoot().Templates().TryLookup(m_clipPathId)}) { if (!clipPath.Geometry()) { - clipPath.CreateGeometry(SvgRoot().Canvas()); + clipPath.CreateGeometry(); } return clipPath.Geometry(); } @@ -345,7 +350,6 @@ Geometry::CanvasGeometry RenderableView::ClipPathGeometry() { void RenderableView::Unload() { if (m_geometry) { - m_geometry.Close(); m_geometry = nullptr; } @@ -358,26 +362,37 @@ void RenderableView::Unload() { RNSVG::IRenderable RenderableView::HitTest(Point const &point) { if (m_geometry) { - bool strokeContainsPoint{false}; + BOOL strokeContainsPoint = FALSE; + D2D1_POINT_2F pointD2D{point.X, point.Y}; + + com_ptr geometry{get_self(m_geometry)->Get()}; + if (auto const &svgRoot{SvgRoot()}) { - float canvasDiagonal{Utils::GetCanvasDiagonal(svgRoot.Canvas().Size())}; + float canvasDiagonal{Utils::GetCanvasDiagonal(svgRoot.ActualSize())}; float strokeWidth{Utils::GetAbsoluteLength(StrokeWidth(), canvasDiagonal)}; - strokeContainsPoint = m_geometry.StrokeContainsPoint(point, strokeWidth); + + check_hresult(geometry->StrokeContainsPoint(pointD2D, strokeWidth, nullptr, nullptr, &strokeContainsPoint)); } - if (m_geometry.FillContainsPoint(point) || strokeContainsPoint) { + + BOOL fillContainsPoint = FALSE; + check_hresult(geometry->FillContainsPoint(pointD2D, nullptr, &fillContainsPoint)); + + if (fillContainsPoint || strokeContainsPoint) { return *this; } } return nullptr; } -void RenderableView::SetColor(const JSValueObject& propValue, Windows::UI::Color fallbackColor, std::string propName) { +void RenderableView::SetColor(const JSValueObject &propValue, ui::Color const &fallbackColor, std::string propName) { switch (propValue["type"].AsInt64()) { + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractBrush.ts#L29 case 1: { auto const &brushId{to_hstring(Utils::JSValueAsString(propValue["brushRef"]))}; propName == "fill" ? m_fillBrushId = brushId : m_strokeBrushId = brushId; break; } + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractBrush.ts#L6-L8 case 2: // currentColor case 3: // context-fill case 4: // context-stroke diff --git a/windows/RNSVG/RenderableView.h b/windows/RNSVG/RenderableView.h index a2fcefbb..9e0b9469 100644 --- a/windows/RNSVG/RenderableView.h +++ b/windows/RNSVG/RenderableView.h @@ -2,6 +2,8 @@ #include "RenderableView.g.h" #include "SVGLength.h" +#include "D2DDeviceContext.h" +#include "D2DGeometry.h" namespace winrt::RNSVG::implementation { struct RenderableView : RenderableViewT { @@ -14,8 +16,8 @@ struct RenderableView : RenderableViewT { Windows::UI::Xaml::FrameworkElement SvgParent() { return m_parent; } void SvgParent(Windows::UI::Xaml::FrameworkElement const &value) { m_parent = value; } - Microsoft::Graphics::Canvas::Geometry::CanvasGeometry Geometry() { return m_geometry; } - void Geometry(Microsoft::Graphics::Canvas::Geometry::CanvasGeometry value) { m_geometry = value; } + RNSVG::D2DGeometry Geometry() { return m_geometry; } + void Geometry(RNSVG::D2DGeometry const &value) { m_geometry = value; } hstring Id() { return m_id; } Numerics::float3x2 SvgTransform() { return m_transformMatrix; } @@ -33,22 +35,18 @@ struct RenderableView : RenderableViewT { float StrokeDashOffset() { return m_strokeDashOffset; } RNSVG::SVGLength StrokeWidth() { return m_strokeWidth; } Windows::Foundation::Collections::IVector StrokeDashArray() { return m_strokeDashArray; } - Microsoft::Graphics::Canvas::Geometry::CanvasCapStyle StrokeLineCap() { return m_strokeLineCap; } - Microsoft::Graphics::Canvas::Geometry::CanvasLineJoin StrokeLineJoin() { return m_strokeLineJoin; } - Microsoft::Graphics::Canvas::Geometry::CanvasFilledRegionDetermination FillRule() { return m_fillRule; } - Microsoft::Graphics::Canvas::Geometry::CanvasGeometry ClipPathGeometry(); + RNSVG::LineCap StrokeLineCap() { return m_strokeLineCap; } + RNSVG::LineJoin StrokeLineJoin() { return m_strokeLineJoin; } + RNSVG::FillRule FillRule() { return m_fillRule; } + RNSVG::D2DGeometry ClipPathGeometry(); virtual void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate = true, bool invalidate = true); - virtual void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const & /*canvas*/) {} + virtual void CreateGeometry() {} virtual void MergeProperties(RNSVG::RenderableView const &other); virtual void SaveDefinition(); virtual void Unload(); - virtual void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); - virtual void CreateResources( - Microsoft::Graphics::Canvas::ICanvasResourceCreator const & /*resourceCreator*/, - Microsoft::Graphics::Canvas::UI::CanvasCreateResourcesEventArgs const & /*args*/) { } + virtual void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); + virtual void CreateResources() {} virtual RNSVG::IRenderable HitTest(Windows::Foundation::Point const &point); protected: @@ -72,7 +70,7 @@ struct RenderableView : RenderableViewT { private: Microsoft::ReactNative::IReactContext m_reactContext{nullptr}; Windows::UI::Xaml::FrameworkElement m_parent{nullptr}; - Microsoft::Graphics::Canvas::Geometry::CanvasGeometry m_geometry{nullptr}; + RNSVG::D2DGeometry m_geometry{nullptr}; bool m_recreateResources{true}; bool m_isResponsible{false}; @@ -87,17 +85,15 @@ struct RenderableView : RenderableViewT { float m_strokeOpacity{1.0f}; float m_strokeMiterLimit{0.0f}; float m_strokeDashOffset{0.0f}; + std::vector m_adjustedStrokeDashArray; RNSVG::SVGLength m_strokeWidth{1.0f, RNSVG::LengthType::Pixel}; Windows::Foundation::Collections::IVector m_strokeDashArray{ winrt::single_threaded_vector()}; - Microsoft::Graphics::Canvas::Geometry::CanvasCapStyle m_strokeLineCap{ - Microsoft::Graphics::Canvas::Geometry::CanvasCapStyle::Flat}; - Microsoft::Graphics::Canvas::Geometry::CanvasLineJoin m_strokeLineJoin{ - Microsoft::Graphics::Canvas::Geometry::CanvasLineJoin::Miter}; - Microsoft::Graphics::Canvas::Geometry::CanvasFilledRegionDetermination m_fillRule{ - Microsoft::Graphics::Canvas::Geometry::CanvasFilledRegionDetermination::Winding}; + RNSVG::LineCap m_strokeLineCap{RNSVG::LineCap::Butt}; + RNSVG::LineJoin m_strokeLineJoin{RNSVG::LineJoin::Miter}; + RNSVG::FillRule m_fillRule{RNSVG::FillRule::NonZero}; - void SetColor(const Microsoft::ReactNative::JSValueObject &propValue, Windows::UI::Color fallbackColor, std::string propName); + void SetColor(const Microsoft::ReactNative::JSValueObject &propValue, Windows::UI::Color const &fallbackColor, std::string propName); }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/RenderableViewManager.cpp b/windows/RNSVG/RenderableViewManager.cpp index 5bad8960..01b28bf9 100644 --- a/windows/RNSVG/RenderableViewManager.cpp +++ b/windows/RNSVG/RenderableViewManager.cpp @@ -42,6 +42,8 @@ Windows::UI::Xaml::FrameworkElement RenderableViewManager::CreateView() { return winrt::RNSVG::PatternView(); case RNSVG::SVGClass::RNSVGClipPath: return winrt::RNSVG::ClipPathView(); + case RNSVG::SVGClass::RNSVGMarker: + return winrt::RNSVG::MarkerView(); } throw hresult_not_implemented(); diff --git a/windows/RNSVG/SVGLength.cpp b/windows/RNSVG/SVGLength.cpp index 0a63f4e8..4e3d9438 100644 --- a/windows/RNSVG/SVGLength.cpp +++ b/windows/RNSVG/SVGLength.cpp @@ -7,7 +7,7 @@ namespace winrt::RNSVG::implementation { SVGLength::SVGLength(float value) : m_value(value), m_unit(RNSVG::LengthType::Number) {} -SVGLength::SVGLength(float value, RNSVG::LengthType type) : m_value(value), m_unit(type) {} +SVGLength::SVGLength(float value, RNSVG::LengthType const &type) : m_value(value), m_unit(type) {} RNSVG::SVGLength SVGLength::From(std::string value) { auto strLength{value.size()}; diff --git a/windows/RNSVG/SVGLength.h b/windows/RNSVG/SVGLength.h index b4916eef..86729b60 100644 --- a/windows/RNSVG/SVGLength.h +++ b/windows/RNSVG/SVGLength.h @@ -9,7 +9,7 @@ struct SVGLength : SVGLengthT { public: SVGLength() = default; SVGLength(float value); - SVGLength(float value, RNSVG::LengthType type); + SVGLength(float value, RNSVG::LengthType const &type); float Value() { return m_value; } RNSVG::LengthType Unit() { return m_unit; } diff --git a/windows/RNSVG/SvgView.cpp b/windows/RNSVG/SvgView.cpp index 2d7d9757..90445486 100644 --- a/windows/RNSVG/SvgView.cpp +++ b/windows/RNSVG/SvgView.cpp @@ -5,36 +5,66 @@ #include "SvgView.g.cpp" #endif +#include +#include #include +#include "D2DDevice.h" +#include "D2DDeviceContext.h" #include "GroupView.h" #include "Utils.h" +#include + using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; -using namespace Windows::Graphics::Display; namespace winrt::RNSVG::implementation { SvgView::SvgView(IReactContext const &context) : m_reactContext(context) { - m_scale = static_cast(DisplayInformation::GetForCurrentView().ResolutionScale()) / 100; + uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - m_canvasDrawRevoker = m_canvas.Draw(winrt::auto_revoke, {get_weak(), &SvgView::Canvas_Draw}); - m_canvasCreateResourcesRevoker = m_canvas.CreateResources(winrt::auto_revoke, {get_weak(), &SvgView::Canvas_CreateResources}); - m_canvasSizeChangedRevoker = m_canvas.SizeChanged(winrt::auto_revoke, {get_weak(), &SvgView::Canvas_SizeChanged}); + D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + + // Create the Direct3D device. + com_ptr d3dDevice; + D3D_FEATURE_LEVEL supportedFeatureLevel; + check_hresult(D3D11CreateDevice( + nullptr, // default adapter + D3D_DRIVER_TYPE_HARDWARE, + 0, + creationFlags, + featureLevels, + ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, + d3dDevice.put(), + &supportedFeatureLevel, + nullptr)); + + com_ptr dxgiDevice{d3dDevice.as()}; + + // Create the Direct2D device and a corresponding context. + com_ptr device; + check_hresult(D2D1CreateDevice(dxgiDevice.get(), nullptr, device.put())); + m_device = make(device); + + com_ptr deviceContext; + check_hresult(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, deviceContext.put())); + m_deviceContext = make(deviceContext); + + m_panelLoadedRevoker = Loaded(winrt::auto_revoke, {get_weak(), &SvgView::Panel_Loaded}); m_panelUnloadedRevoker = Unloaded(winrt::auto_revoke, {get_weak(), &SvgView::Panel_Unloaded}); - - Children().Append(m_canvas); } -void SvgView::SvgParent(Windows::UI::Xaml::FrameworkElement const &value) { +void SvgView::SvgParent(xaml::FrameworkElement const &value) { if (value) { - m_canvasDrawRevoker.revoke(); - m_canvasCreateResourcesRevoker.revoke(); - m_canvasSizeChangedRevoker.revoke(); m_panelUnloadedRevoker.revoke(); - m_canvas.RemoveFromVisualTree(); - m_canvas = nullptr; m_parent = value; } } @@ -88,7 +118,7 @@ void SvgView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, b } } - InvalidateCanvas(); + Invalidate(); } } @@ -105,77 +135,79 @@ void SvgView::MergeProperties(RNSVG::RenderableView const &other) { } } -Size SvgView::MeasureOverride(Size availableSize) { +Size SvgView::MeasureOverride(Size const &availableSize) { for (auto const &child : Children()) { child.Measure(availableSize); } return availableSize; } -Size SvgView::ArrangeOverride(Size finalSize) { +Size SvgView::ArrangeOverride(Size const &finalSize) { for (auto const &child : Children()) { child.Arrange({0, 0, finalSize.Width, finalSize.Height}); } return finalSize; } -void SvgView::Render(UI::Xaml::CanvasControl const & canvas, CanvasDrawingSession const & session) { - if (m_align != "") { - Rect vbRect{m_minX * m_scale, m_minY * m_scale, m_vbWidth * m_scale, m_vbHeight * m_scale}; - float width{static_cast(canvas.ActualWidth())}; - float height{static_cast(canvas.ActualHeight())}; - bool nested{m_parent}; +void SvgView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { + com_ptr deviceContext{get_self(context)->Get()}; - if (nested) { + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; + + if (m_align != "") { + Rect vbRect{m_minX, m_minY, m_vbWidth, m_vbHeight}; + float width{size.Width}; + float height{size.Height}; + + if (m_parent) { width = Utils::GetAbsoluteLength(m_bbWidth, width); height = Utils::GetAbsoluteLength(m_bbHeight, height); } Rect elRect{0, 0, width, height}; - - session.Transform(Utils::GetViewBoxTransform(vbRect, elRect, m_align, m_meetOrSlice)); + deviceContext->SetTransform(Utils::GetViewBoxTransformD2D(vbRect, elRect, m_align, m_meetOrSlice) * transform); } if (m_group) { m_group.SaveDefinition(); - m_group.Render(canvas, session); + m_group.Draw(context, size); } -} -void SvgView::Canvas_Draw(UI::Xaml::CanvasControl const &sender, UI::Xaml::CanvasDrawEventArgs const &args) { if (!m_hasRendered) { m_hasRendered = true; } - m_brushes.Clear(); - m_templates.Clear(); - - Render(sender, args.DrawingSession()); + deviceContext->SetTransform(transform); } -void SvgView::CreateResources(ICanvasResourceCreator const &resourceCreator, UI::CanvasCreateResourcesEventArgs const &args) { +void SvgView::CreateGeometry() { if (m_group) { - m_group.CreateResources(resourceCreator, args); + m_group.CreateGeometry(); } } -void SvgView::CreateGeometry(UI::Xaml::CanvasControl const& canvas) { +void SvgView::CreateResources() { if (m_group) { - m_group.CreateGeometry(canvas); + m_group.CreateResources(); + } + + Invalidate(); + + m_image.Width(ActualWidth()); + m_image.Height(ActualHeight()); + m_image.Stretch(xaml::Media::Stretch::UniformToFill); + + Children().Append(m_image); +} + +void SvgView::Panel_Loaded(IInspectable const &sender, xaml::RoutedEventArgs const & /*args*/) { + if (auto const &svgView{sender.try_as()}) { + m_loaded = true; + svgView.CreateResources(); } } -void SvgView::Canvas_CreateResources(UI::Xaml::CanvasControl const &sender, UI::CanvasCreateResourcesEventArgs const &args) { - CreateResources(sender, args); -} - -void SvgView::Canvas_SizeChanged( - IInspectable const & /*sender*/, - Windows::UI::Xaml::SizeChangedEventArgs const & /*args*/) { - // sender.Invalidate(); -} - -void SvgView::Panel_Unloaded(IInspectable const &sender, Windows::UI::Xaml::RoutedEventArgs const & /*args*/) { +void SvgView::Panel_Unloaded(IInspectable const &sender, xaml::RoutedEventArgs const & /*args*/) { if (auto const &svgView{sender.try_as()}) { svgView.Unload(); } @@ -189,16 +221,55 @@ void SvgView::Unload() { if (m_group) { m_group.Unload(); } - - if (m_canvas) { - m_canvas.RemoveFromVisualTree(); - m_canvas = nullptr; - } } -void SvgView::InvalidateCanvas() { - if (m_hasRendered) { - m_canvas.Invalidate(); +void SvgView::Invalidate() { + if (!m_loaded) { + return; } + + m_brushes.Clear(); + m_templates.Clear(); + + com_ptr deviceContext{get_self(DeviceContext())->Get()}; + + Size size{static_cast(ActualWidth()), static_cast(ActualHeight())}; + + xaml::Media::Imaging::SurfaceImageSource surfaceImageSource( + static_cast(size.Width), static_cast(size.Height)); + com_ptr sisNativeWithD2D{surfaceImageSource.as()}; + + // Associate the Direct2D device with the SurfaceImageSource. + com_ptr device{get_self(Device())->Get()}; + sisNativeWithD2D->SetDevice(device.get()); + + com_ptr dxgiSurface; + + // RECT is a LTRB rect, but since we're using 0 for LT, we are using width/height for RB. + RECT updateRect{0, 0, static_cast(size.Width), static_cast(size.Height)}; + POINT offset{0, 0}; + check_hresult(sisNativeWithD2D->BeginDraw(updateRect, __uuidof(IDXGISurface), dxgiSurface.put_void(), &offset)); + + // Create render target. + com_ptr bitmap; + check_hresult(deviceContext->CreateBitmapFromDxgiSurface(dxgiSurface.get(), nullptr, bitmap.put())); + + // Set context's render target. + deviceContext->SetTarget(bitmap.get()); + + // Draw using Direct2D context + deviceContext->BeginDraw(); + + auto transform = Numerics::make_float3x2_translation({static_cast(offset.x), static_cast(offset.y)}); + deviceContext->SetTransform(D2DHelpers::AsD2DTransform(transform)); + + deviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); + + Draw(DeviceContext(), size); + + deviceContext->EndDraw(); + sisNativeWithD2D->EndDraw(); + + m_image.Source(surfaceImageSource); } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/SvgView.h b/windows/RNSVG/SvgView.h index d99c5ebd..584e9f2d 100644 --- a/windows/RNSVG/SvgView.h +++ b/windows/RNSVG/SvgView.h @@ -9,18 +9,18 @@ struct SvgView : SvgViewT { SvgView(Microsoft::ReactNative::IReactContext const &context); - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl Canvas() { return m_canvas; } - Windows::UI::Xaml::FrameworkElement SvgParent() { return m_parent; } void SvgParent(Windows::UI::Xaml::FrameworkElement const &value); RNSVG::GroupView Group() { return m_group; } void Group(RNSVG::GroupView const &value) { m_group = value; } - Microsoft::Graphics::Canvas::Geometry::CanvasGeometry Geometry() { return m_group ? m_group.Geometry() : nullptr; } - void Geometry(Microsoft::Graphics::Canvas::Geometry::CanvasGeometry /*value*/) { } + RNSVG::D2DDevice Device() { return m_device; } - float SvgScale() { return m_scale; } + RNSVG::D2DDeviceContext DeviceContext() { return m_deviceContext; } + + RNSVG::D2DGeometry Geometry() { return m_group ? m_group.Geometry() : nullptr; } + void Geometry(RNSVG::D2DGeometry const & /*value*/) {} Windows::UI::Color CurrentColor() { return m_currentColor; } @@ -39,43 +39,31 @@ struct SvgView : SvgViewT { void MergeProperties(RNSVG::RenderableView const &other); void SaveDefinition(); void Unload(); - void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); - void CreateResources( - Microsoft::Graphics::Canvas::ICanvasResourceCreator const &resourceCreator, - Microsoft::Graphics::Canvas::UI::CanvasCreateResourcesEventArgs const &args); - void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); + void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); + void CreateResources(); + void CreateGeometry(); RNSVG::IRenderable HitTest(Windows::Foundation::Point const & /*point*/) { return nullptr; } // Overrides - Windows::Foundation::Size MeasureOverride(Windows::Foundation::Size availableSize); - Windows::Foundation::Size ArrangeOverride(Windows::Foundation::Size finalSize); - - // CanvasControl - void Canvas_Draw( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &sender, - Microsoft::Graphics::Canvas::UI::Xaml::CanvasDrawEventArgs const &args); - void Canvas_CreateResources( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &sender, - Microsoft::Graphics::Canvas::UI::CanvasCreateResourcesEventArgs const &args); - void Canvas_SizeChanged( - Windows::Foundation::IInspectable const &sender, - Windows::UI::Xaml::SizeChangedEventArgs const &args); + Windows::Foundation::Size MeasureOverride(Windows::Foundation::Size const &availableSize); + Windows::Foundation::Size ArrangeOverride(Windows::Foundation::Size const &finalSize); + void Panel_Loaded(Windows::Foundation::IInspectable const &sender, Windows::UI::Xaml::RoutedEventArgs const &args); void Panel_Unloaded(Windows::Foundation::IInspectable const &sender, Windows::UI::Xaml::RoutedEventArgs const &args); - void InvalidateCanvas(); + void Invalidate(); private: + bool m_loaded{false}; bool m_hasRendered{false}; bool m_isResponsible{false}; Microsoft::ReactNative::IReactContext m_reactContext{nullptr}; - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl m_canvas{}; Windows::UI::Xaml::FrameworkElement m_parent{nullptr}; + RNSVG::D2DDevice m_device; + RNSVG::D2DDeviceContext m_deviceContext; + Windows::UI::Xaml::Controls::Image m_image; RNSVG::GroupView m_group{nullptr}; hstring m_id{L""}; - float m_scale{0.0f}; float m_minX{0.0f}; float m_minY{0.0f}; float m_vbWidth{0.0f}; @@ -92,9 +80,7 @@ struct SvgView : SvgViewT { winrt::single_threaded_map()}; Windows::Foundation::Collections::IMap m_brushes{ winrt::single_threaded_map()}; - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl::Draw_revoker m_canvasDrawRevoker{}; - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl::CreateResources_revoker m_canvasCreateResourcesRevoker{}; - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl::SizeChanged_revoker m_canvasSizeChangedRevoker{}; + Windows::UI::Xaml::FrameworkElement::Loaded_revoker m_panelLoadedRevoker{}; Windows::UI::Xaml::FrameworkElement::Unloaded_revoker m_panelUnloadedRevoker{}; }; } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/SvgViewManager.cpp b/windows/RNSVG/SvgViewManager.cpp index 64bc4816..f8175059 100644 --- a/windows/RNSVG/SvgViewManager.cpp +++ b/windows/RNSVG/SvgViewManager.cpp @@ -15,7 +15,6 @@ namespace winrt { using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; -using namespace Microsoft::Graphics::Canvas::UI::Xaml; using namespace Microsoft::ReactNative; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; diff --git a/windows/RNSVG/SymbolView.cpp b/windows/RNSVG/SymbolView.cpp index 6631a739..5e56c290 100644 --- a/windows/RNSVG/SymbolView.cpp +++ b/windows/RNSVG/SymbolView.cpp @@ -5,7 +5,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { diff --git a/windows/RNSVG/SymbolView.h b/windows/RNSVG/SymbolView.h index 9a37905d..35f5c8e3 100644 --- a/windows/RNSVG/SymbolView.h +++ b/windows/RNSVG/SymbolView.h @@ -16,9 +16,7 @@ struct SymbolView : SymbolViewT { // RenderableView void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &/*canvas*/, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &/*session*/){}; + void Draw(RNSVG::D2DDeviceContext const & /*deviceContext*/, Windows::Foundation::Size const & /*size*/){}; private: float m_minX{0.0f}; diff --git a/windows/RNSVG/TSpanView.cpp b/windows/RNSVG/TSpanView.cpp index 5414a0fc..34dbb97b 100644 --- a/windows/RNSVG/TSpanView.cpp +++ b/windows/RNSVG/TSpanView.cpp @@ -2,11 +2,11 @@ #include "TSpanView.h" #include "TSpanView.g.cpp" +#include + #include "Utils.h" -#include using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -25,28 +25,50 @@ void TSpanView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void TSpanView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) { - auto const &resourceCreator{canvas.try_as()}; - Microsoft::Graphics::Canvas::Text::CanvasTextFormat const& textFormat{}; - textFormat.FontSize(FontSize()); - textFormat.FontFamily(FontFamily()); - textFormat.FontWeight(Utils::FontWeightFrom(FontWeight(), SvgParent())); +void TSpanView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { + com_ptr deviceContext{get_self(context)->Get()}; - Geometry(Geometry::CanvasGeometry::CreateText({resourceCreator, to_hstring(m_content), textFormat, canvas.Size().Width, canvas.Size().Height})); -} + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; -void TSpanView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { - auto const &transform{session.Transform()}; bool translateXY{X().Size() > 0 || Y().Size() > 0}; if (translateXY) { float x{X().Size() > 0 ? X().GetAt(0).Value() : 0}; float y{Y().Size() > 0 ? Y().GetAt(0).Value() : 0}; - session.Transform(transform * Numerics::make_float3x2_translation(x, y)); + deviceContext->SetTransform(D2D1::Matrix3x2F::Translation({x, y}) * transform); } - __super::Render(canvas, session); + + com_ptr d2dFactory; + deviceContext->GetFactory(d2dFactory.put()); + + com_ptr dwriteFactory; + check_hresult(DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<::IUnknown **>(dwriteFactory.put_void()))); + + com_ptr textFormat; + check_hresult(dwriteFactory->CreateTextFormat( + FontFamily().c_str(), + nullptr, // Font collection (nullptr sets it to use the system font collection). + D2DHelpers::FontWeightFrom(FontWeight(), SvgParent()), + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + FontSize(), + L"", + textFormat.put())); + + auto const fill{Utils::GetCanvasBrush(FillBrushId(), Fill(), SvgRoot(), nullptr)}; + + deviceContext->DrawText( + to_hstring(m_content).c_str(), + static_cast(m_content.size()), + textFormat.get(), + D2D1::RectF(0, 0, size.Width, size.Height), + fill.get()); + if (translateXY) { - session.Transform(transform); + deviceContext->SetTransform(transform); } } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/TSpanView.h b/windows/RNSVG/TSpanView.h index a2ef4689..cbed7ad9 100644 --- a/windows/RNSVG/TSpanView.h +++ b/windows/RNSVG/TSpanView.h @@ -8,10 +8,7 @@ public: TSpanView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void CreateGeometry(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas); - virtual void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + virtual void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); private: std::string m_content; diff --git a/windows/RNSVG/TextView.cpp b/windows/RNSVG/TextView.cpp index 40ceaa26..18d46f30 100644 --- a/windows/RNSVG/TextView.cpp +++ b/windows/RNSVG/TextView.cpp @@ -2,8 +2,9 @@ #include "TextView.h" #include "TextView.g.cpp" +#include "D2DHelpers.h" + using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -45,17 +46,20 @@ void TextView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, __super::UpdateProperties(reader, forceUpdate, invalidate); } -void TextView::RenderGroup(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { - auto const &transform{session.Transform()}; +void TextView::DrawGroup(RNSVG::D2DDeviceContext const &context, Size const &size) { + com_ptr deviceContext{get_self(context)->Get()}; + + D2D1_MATRIX_3X2_F transform{D2DHelpers::GetTransform(deviceContext.get())}; + bool translateXY{X().Size() > 0 || Y().Size() > 0}; if (translateXY) { float x{X().Size() > 0 ? X().GetAt(0).Value() : 0}; float y{Y().Size() > 0 ? Y().GetAt(0).Value() : 0}; - session.Transform(transform * Numerics::make_float3x2_translation(x, y)); + deviceContext->SetTransform(D2D1::Matrix3x2F::Translation({x,y}) * transform); } - __super::RenderGroup(canvas, session); + __super::DrawGroup(context, size); if (translateXY) { - session.Transform(transform); + deviceContext->SetTransform(transform); } } } // namespace winrt::RNSVG::implementation diff --git a/windows/RNSVG/TextView.h b/windows/RNSVG/TextView.h index 2ca5753c..b1c62e02 100644 --- a/windows/RNSVG/TextView.h +++ b/windows/RNSVG/TextView.h @@ -14,9 +14,7 @@ struct TextView : TextViewT { Windows::Foundation::Collections::IVector Rotate() { return m_rotate; } virtual void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - virtual void RenderGroup( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + virtual void DrawGroup(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); private: Windows::Foundation::Collections::IVector m_x{winrt::single_threaded_vector()}; diff --git a/windows/RNSVG/Types.idl b/windows/RNSVG/Types.idl index 4d3f1b87..7a3310da 100644 --- a/windows/RNSVG/Types.idl +++ b/windows/RNSVG/Types.idl @@ -23,6 +23,7 @@ namespace RNSVG { Unknown, }; + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractViewBox.ts#L3-L10 enum MeetOrSlice { Meet, Slice, @@ -67,6 +68,26 @@ namespace RNSVG { Pica, }; + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractStroke.ts#L6-L10 + enum LineCap { + Butt, + Round, + Square, + }; + + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractStroke.ts#L12-L16 + enum LineJoin { + Miter, + Round, + Bevel, + }; + + // https://github.com/software-mansion/react-native-svg/blob/main/src/lib/extract/extractFill.ts#L6-L9 + enum FillRule { + EvenOdd, + NonZero, + }; + [default_interface] runtimeclass SVGLength { SVGLength(); @@ -76,4 +97,24 @@ namespace RNSVG { Single Value{ get; }; LengthType Unit{ get; }; }; -} + + [default_interface] + runtimeclass D2DDevice { + D2DDevice(); + }; + + [default_interface] + runtimeclass D2DDeviceContext { + D2DDeviceContext(); + }; + + [default_interface] + runtimeclass D2DGeometry { + D2DGeometry(); + }; + + [default_interface] + runtimeclass D2DBrush { + D2DBrush(); + }; + } diff --git a/windows/RNSVG/UseView.cpp b/windows/RNSVG/UseView.cpp index c7ce866d..2cc62f58 100644 --- a/windows/RNSVG/UseView.cpp +++ b/windows/RNSVG/UseView.cpp @@ -5,7 +5,6 @@ #include "Utils.h" using namespace winrt; -using namespace Microsoft::Graphics::Canvas; using namespace Microsoft::ReactNative; namespace winrt::RNSVG::implementation { @@ -32,66 +31,64 @@ void UseView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, b __super::UpdateProperties(reader, forceUpdate, invalidate); } -void UseView::Render(UI::Xaml::CanvasControl const &canvas, CanvasDrawingSession const &session) { +void UseView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { if (auto const &view{GetRenderableTemplate()}) { - auto const &originalTransform{session.Transform()}; - auto transform{Numerics::make_float3x2_scale(1)}; + com_ptr deviceContext{get_self(context)->Get()}; + + D2D1_MATRIX_3X2_F originalTransform{D2DHelpers::GetTransform(deviceContext.get())}; + auto transform{originalTransform}; + + float x{Utils::GetAbsoluteLength(m_x, size.Width)}; + float y{Utils::GetAbsoluteLength(m_y, size.Height)}; + + transform = D2D1::Matrix3x2F::Translation({x, y}) * originalTransform; + + // Combine new transform with existing one if it's set + if (m_propSetMap[RNSVG::BaseProp::Matrix]) { + transform = transform * D2DHelpers::AsD2DTransform(SvgTransform()); + } // Figure out any necessary transforms if (auto const &symbol{view.try_as()}) { if (symbol.Align() != L"") { - if (auto const &root{SvgRoot()}) { - Rect vbRect{ - symbol.MinX() * root.SvgScale(), - symbol.MinY() * root.SvgScale(), - (symbol.MinX() + symbol.VbWidth()) * root.SvgScale(), - (symbol.MinY() + symbol.VbHeight()) * root.SvgScale()}; + Rect vbRect{ + symbol.MinX(), + symbol.MinY(), + (symbol.MinX() + symbol.VbWidth()), + (symbol.MinY() + symbol.VbHeight())}; - float elX{Utils::GetAbsoluteLength(m_x, canvas.Size().Width)}; - float elY{Utils::GetAbsoluteLength(m_y, canvas.Size().Height)}; - float elWidth{Utils::GetAbsoluteLength(m_width, canvas.Size().Width)}; - float elHeight{Utils::GetAbsoluteLength(m_height, canvas.Size().Height)}; - Rect elRect{elX, elY, elWidth, elHeight}; + float elWidth{Utils::GetAbsoluteLength(m_width, size.Width)}; + float elHeight{Utils::GetAbsoluteLength(m_height, size.Height)}; + Rect elRect{0.0f, 0.0f, elWidth, elHeight}; - transform = Utils::GetViewBoxTransform(vbRect, elRect, to_string(symbol.Align()), symbol.MeetOrSlice()); - } + auto vbTransform{Utils::GetViewBoxTransformD2D(vbRect, elRect, to_string(symbol.Align()), symbol.MeetOrSlice())}; + transform = vbTransform * transform; } - } else { - float x{Utils::GetAbsoluteLength(m_x, canvas.Size().Width)}; - float y{Utils::GetAbsoluteLength(m_y, canvas.Size().Height)}; - transform = Numerics::make_float3x2_translation({x, y}); } - // Combine new transform with existing one if it's set - if (m_propSetMap[RNSVG::BaseProp::Matrix]) { - transform = transform * SvgTransform(); - } - - session.Transform(transform); + deviceContext->SetTransform(transform); // Propagate props to template view.MergeProperties(*this); // Set opacity and render - if (auto const &opacityLayer{session.CreateLayer(m_opacity)}) { - if (auto const &symbol{view.try_as()}) { - symbol.RenderGroup(canvas, session); - } else { - view.Render(canvas, session); - } - opacityLayer.Close(); + D2DHelpers::PushOpacityLayer(deviceContext.get(), nullptr, m_opacity); + if (auto const &symbol{view.try_as()}) { + symbol.DrawGroup(context, size); + } else { + view.Draw(context, size); } + deviceContext->PopLayer(); + // Restore original template props if (auto const &parent{view.SvgParent().try_as()}) { view.MergeProperties(parent); } // Restore session transform - session.Transform(originalTransform); + deviceContext->SetTransform(originalTransform); - } else { - throw hresult_not_implemented(L"'Use' element expected a pre-defined svg template as 'href' prop. Template named: " + m_href + L" is not defined"); } } diff --git a/windows/RNSVG/UseView.h b/windows/RNSVG/UseView.h index 052c3a4d..ab38cc1e 100644 --- a/windows/RNSVG/UseView.h +++ b/windows/RNSVG/UseView.h @@ -8,9 +8,7 @@ struct UseView : UseViewT { UseView() = default; void UpdateProperties(Microsoft::ReactNative::IJSValueReader const &reader, bool forceUpdate, bool invalidate); - void Render( - Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const &canvas, - Microsoft::Graphics::Canvas::CanvasDrawingSession const &session); + void Draw(RNSVG::D2DDeviceContext const &deviceContext, Windows::Foundation::Size const &size); private: hstring m_href{L""}; diff --git a/windows/RNSVG/Utils.h b/windows/RNSVG/Utils.h index 2b48b340..03c5ffa9 100644 --- a/windows/RNSVG/Utils.h +++ b/windows/RNSVG/Utils.h @@ -2,19 +2,16 @@ #include "pch.h" -#include #include #include #include "JSValueReader.h" +#include "D2DHelpers.h" +#include "D2DBrush.h" #define _USE_MATH_DEFINES #include -using namespace winrt; -using namespace Microsoft::Graphics::Canvas; -using namespace Microsoft::ReactNative; -using namespace Windows::UI; -using namespace Windows::UI::Text; +using namespace winrt::Microsoft::ReactNative; namespace winrt::RNSVG { struct Utils { @@ -22,7 +19,7 @@ struct Utils { static std::vector GetAdjustedStrokeArray(IVector const &value, float strokeWidth, float canvasDiagonal) { std::vector result; - for (auto const &item : value) { + for (auto const item : value) { float absValue{GetAbsoluteLength(item, canvasDiagonal)}; // Win2D sets the length of each dash as the product of the element value in array and stroke width, @@ -31,16 +28,20 @@ struct Utils { result.push_back(absValue / (strokeWidth == 0.0f ? 1.0f : strokeWidth)); } - return std::move(result); + return result; } - static float GetCanvasDiagonal(Windows::Foundation::Size const &size) { + static float GetCanvasDiagonal(Size const &size) { float powX{std::powf(size.Width, 2)}; float powY{std::powf(size.Height, 2)}; return std::sqrtf(powX + powY) * static_cast(M_SQRT1_2); } + static float GetAbsoluteLength(SVGLength const& length, double relativeTo) { + return GetAbsoluteLength(length, static_cast(relativeTo)); + } + static float GetAbsoluteLength(SVGLength const &length, float relativeTo) { auto value{length.Value()}; @@ -78,81 +79,7 @@ struct Utils { return Numerics::make_float3x2_rotation(radians); } - static FontWeight FontWeightFrom(hstring const& weight, Xaml::FrameworkElement const& parent) { - if (weight == L"normal") { - return FontWeights::Normal(); - } else if (weight == L"bold") { - return FontWeights::Bold(); - } else if (weight == L"bolder" || weight == L"lighter" || weight == L"auto") { - auto const &groupView{parent.try_as()}; - FontWeight parentWeight{ - groupView ? FontWeightFrom(groupView.FontWeight(), groupView.SvgParent()) : FontWeights::Normal()}; - - if (weight == L"bolder") { - return Bolder(parentWeight.Weight); - } else if (weight == L"lighter") { - return Lighter(parentWeight.Weight); - } else if (weight == L"auto") { - return parentWeight; - } - } - - return GetClosestFontWeight(std::stof(weight.c_str(), nullptr)); - } - - static FontWeight GetClosestFontWeight(float weight) { - if (weight > 325 && weight < 375) { - return FontWeights::SemiLight(); - } else if (weight > 925) { - return FontWeights::ExtraBlack(); - } else { - switch (static_cast(std::round(weight / 100.0f))) { - case 1: - return FontWeights::Thin(); - case 2: - return FontWeights::ExtraLight(); - case 3: - return FontWeights::Light(); - case 4: - return FontWeights::Normal(); - case 5: - return FontWeights::Medium(); - case 6: - return FontWeights::SemiBold(); - case 7: - return FontWeights::Bold(); - case 8: - return FontWeights::ExtraBold(); - case 9: - default: - return FontWeights::ExtraBlack(); - } - } - } - - static FontWeight Bolder(uint16_t weight) { - if (weight < 350) { - return FontWeights::Normal(); - } else if (weight < 550) { - return FontWeights::Bold(); - } else if (weight < 900) { - return FontWeights::Black(); - } else { - return FontWeights::ExtraBlack(); - } - } - - static FontWeight Lighter(uint16_t weight) { - if (weight < 550) { - return FontWeights::Thin(); - } else if (weight < 750) { - return FontWeights::Normal(); - } else { - return FontWeights::Bold(); - } - } - - static Numerics::float3x2 GetViewBoxTransform(Rect vbRect, Rect elRect, std::string align, RNSVG::MeetOrSlice meetOrSlice) { + static Numerics::float3x2 GetViewBoxTransform(Rect const &vbRect, Rect const &elRect, std::string align, RNSVG::MeetOrSlice const &meetOrSlice) { // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute @@ -217,6 +144,10 @@ struct Utils { return scale * translate; } + static D2D1_MATRIX_3X2_F GetViewBoxTransformD2D(Rect const &vbRect, Rect const &elRect, std::string align, RNSVG::MeetOrSlice const &meetOrSlice) { + return D2DHelpers::AsD2DTransform(GetViewBoxTransform(vbRect, elRect, align, meetOrSlice)); + } + static RNSVG::MeetOrSlice GetMeetOrSlice(JSValue const &value) { if (value.IsNull()) { return RNSVG::MeetOrSlice::Meet; @@ -263,11 +194,11 @@ struct Utils { } } - static Color JSValueAsColor(JSValue const &value, Color defaultValue = Colors::Transparent()) { + static ui::Color JSValueAsColor(JSValue const &value, ui::Color const &defaultValue = Colors::Transparent()) { if (value.IsNull()) { return defaultValue; - } else if (auto const &brush{value.To()}) { - if (auto const &scb{brush.try_as()}) { + } else if (auto const &brush{value.To()}) { + if (auto const &scb{brush.try_as()}) { return scb.Color(); } } @@ -283,7 +214,7 @@ struct Utils { } } - static Numerics::float3x2 JSValueAsTransform(JSValue const& value, Numerics::float3x2 defaultValue = {}) { + static Numerics::float3x2 JSValueAsTransform(JSValue const &value, Numerics::float3x2 const &defaultValue = {}) { if (value.IsNull()) { return defaultValue; } else { @@ -299,45 +230,84 @@ struct Utils { } } - static std::vector JSValueAsStops(JSValue const& value) { + static D2D1::Matrix3x2F JSValueAsD2DTransform(JSValue const& value, D2D1::Matrix3x2F const defaultValue = {}) { + if (value.IsNull()) { + return defaultValue; + } else { + auto const &matrix{value.AsArray()}; + + return D2D1::Matrix3x2F( + matrix.at(0).AsSingle(), + matrix.at(1).AsSingle(), + matrix.at(2).AsSingle(), + matrix.at(3).AsSingle(), + matrix.at(4).AsSingle(), + matrix.at(5).AsSingle()); + } + } + + static std::vector JSValueAsStops(JSValue const &value) { if (value.IsNull()) { return {}; } auto const &stops{value.AsArray()}; - std::vector canvasStops{}; + std::vector gradientStops; for (size_t i = 0; i < stops.size(); ++i) { - Brushes::CanvasGradientStop stop{}; - stop.Position = Utils::JSValueAsFloat(stops.at(i)); - stop.Color = Utils::JSValueAsColor(stops.at(++i)); - canvasStops.push_back(stop); + D2D1_GRADIENT_STOP stop{}; + stop.position = Utils::JSValueAsFloat(stops.at(i)); + stop.color = D2DHelpers::AsD2DColor(Utils::JSValueAsColor(stops.at(++i))); + gradientStops.emplace_back(stop); } - return canvasStops; + return gradientStops; } - static Brushes::ICanvasBrush GetCanvasBrush( + static com_ptr GetCanvasBrush( hstring const &brushId, - Color color, + ui::Color const &color, RNSVG::SvgView const &root, - Geometry::CanvasGeometry const &geometry, - ICanvasResourceCreator const &resourceCreator) { - Brushes::ICanvasBrush brush{nullptr}; + com_ptr const &geometry) { + com_ptr brush; + com_ptr deviceContext{get_self(root.DeviceContext())->Get()}; + if (root && brushId != L"") { if (brushId == L"currentColor") { - brush = Brushes::CanvasSolidColorBrush(resourceCreator, root.CurrentColor()); + com_ptr scb; + deviceContext->CreateSolidColorBrush(D2DHelpers::AsD2DColor(root.CurrentColor()), scb.put()); + brush = scb.as(); } else if (auto const &brushView{root.Brushes().TryLookup(brushId)}) { - brushView.SetBounds(geometry.ComputeBounds()); - brush = brushView.Brush(); + brushView.CreateBrush(); + + if (geometry) { + D2D1_RECT_F bounds; + geometry->GetBounds(nullptr, &bounds); + brushView.SetBounds(D2DHelpers::FromD2DRect(bounds)); + } + + brush = get_self(brushView.Brush())->Get(); } } if (!brush) { - brush = Brushes::CanvasSolidColorBrush(resourceCreator, color); + com_ptr scb; + deviceContext->CreateSolidColorBrush(D2DHelpers::AsD2DColor(color), scb.put()); + brush = scb.as(); } return brush; } + + static D2D1_VECTOR_2F GetScale(D2D1_MATRIX_3X2_F const matrix) { + auto scaleX = std::sqrt(matrix.m11 * matrix.m11 + matrix.m12 * matrix.m12); + auto scaleY = std::sqrt(matrix.m21 * matrix.m21 + matrix.m22 * matrix.m22); + + return {scaleX, scaleY}; + } + + static D2D1_VECTOR_2F GetScale(Numerics::float3x2 const &matrix) { + return GetScale(D2DHelpers::AsD2DTransform(matrix)); + } }; } // namespace winrt::RNSVG diff --git a/windows/RNSVG/ViewManagers.idl b/windows/RNSVG/ViewManagers.idl index 437fc1a0..aede3457 100644 --- a/windows/RNSVG/ViewManagers.idl +++ b/windows/RNSVG/ViewManagers.idl @@ -108,6 +108,12 @@ namespace RNSVG ClipPathViewManager(); }; + [default_interface] + runtimeclass MarkerViewManager : GroupViewManager + { + MarkerViewManager(); + }; + [default_interface] unsealed runtimeclass TextViewManager : GroupViewManager { diff --git a/windows/RNSVG/Views.idl b/windows/RNSVG/Views.idl index 700f1ec1..83c8e57a 100644 --- a/windows/RNSVG/Views.idl +++ b/windows/RNSVG/Views.idl @@ -5,20 +5,16 @@ namespace RNSVG interface IRenderable { Windows.UI.Xaml.FrameworkElement SvgParent; - Microsoft.Graphics.Canvas.Geometry.CanvasGeometry Geometry; + D2DGeometry Geometry; Boolean IsResponsible; - void CreateResources( - Microsoft.Graphics.Canvas.ICanvasResourceCreator resourceCreator, - Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args); - void Render( - Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl canvas, - Microsoft.Graphics.Canvas.CanvasDrawingSession session); + void CreateResources(); + void Draw(D2DDeviceContext deviceContext, Windows.Foundation.Size size); void UpdateProperties(Microsoft.ReactNative.IJSValueReader reader, Boolean forceUpdate, Boolean invalidate); void MergeProperties(RenderableView other); void SaveDefinition(); void Unload(); - void CreateGeometry(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl canvas); + void CreateGeometry(); IRenderable HitTest(Windows.Foundation.Point point); }; @@ -27,14 +23,14 @@ namespace RNSVG { SvgView(Microsoft.ReactNative.IReactContext context); - Single SvgScale{ get; }; GroupView Group; Windows.UI.Color CurrentColor{ get; }; - Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl Canvas{ get; }; + D2DDevice Device{ get; }; + D2DDeviceContext DeviceContext{ get; }; Windows.Foundation.Collections.IMap Templates{ get; }; Windows.Foundation.Collections.IMap Brushes{ get; }; - void InvalidateCanvas(); + void Invalidate(); }; [default_interface] @@ -55,10 +51,10 @@ namespace RNSVG Single StrokeMiterLimit{ get; }; Single StrokeDashOffset{ get; }; Windows.Foundation.Collections.IVector StrokeDashArray{ get; }; - Microsoft.Graphics.Canvas.Geometry.CanvasCapStyle StrokeLineCap{ get; }; - Microsoft.Graphics.Canvas.Geometry.CanvasLineJoin StrokeLineJoin{ get; }; - Microsoft.Graphics.Canvas.Geometry.CanvasFilledRegionDetermination FillRule{ get; }; - Microsoft.Graphics.Canvas.Geometry.CanvasGeometry ClipPathGeometry { get; }; + LineCap StrokeLineCap{ get; }; + LineJoin StrokeLineJoin{ get; }; + FillRule FillRule{ get; }; + D2DGeometry ClipPathGeometry{ get; }; }; [default_interface] @@ -113,9 +109,7 @@ namespace RNSVG String FontFamily; String FontWeight; - void RenderGroup( - Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl canvas, - Microsoft.Graphics.Canvas.CanvasDrawingSession session); + void DrawGroup(D2DDeviceContext deviceContext, Windows.Foundation.Size size); }; [default_interface] @@ -160,12 +154,18 @@ namespace RNSVG ClipPathView(); }; + [default_interface] + runtimeclass MarkerView : GroupView + { + MarkerView(); + }; + [default_interface] unsealed runtimeclass BrushView : GroupView { BrushView(); - Microsoft.Graphics.Canvas.Brushes.ICanvasBrush Brush{ get; }; + D2DBrush Brush{ get; }; void CreateBrush(); void SetBounds(Windows.Foundation.Rect rect); }; diff --git a/windows/RNSVG/packages.config b/windows/RNSVG/packages.config index fb604318..caf3ef5f 100644 --- a/windows/RNSVG/packages.config +++ b/windows/RNSVG/packages.config @@ -1,6 +1,5 @@ - + - - \ No newline at end of file + diff --git a/windows/RNSVG/pch.h b/windows/RNSVG/pch.h index 6c6e58df..6e63782d 100644 --- a/windows/RNSVG/pch.h +++ b/windows/RNSVG/pch.h @@ -2,30 +2,14 @@ #define NOMINMAX +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include - #include - -#include -#include -#include -#include -#include -using namespace winrt::Windows::Foundation;