[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.
This commit is contained in:
Marlene Cota
2023-11-14 05:33:19 -05:00
committed by GitHub
parent 22f47333fb
commit f88532d195
66 changed files with 1420 additions and 711 deletions

View File

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

View File

@@ -1,6 +1,7 @@
#pragma once
#include "BrushView.g.h"
#include "GroupView.h"
#include "D2DBrush.h"
namespace winrt::RNSVG::implementation {
struct BrushView : BrushViewT<BrushView, RNSVG::implementation::GroupView> {
@@ -9,15 +10,15 @@ struct BrushView : BrushViewT<BrushView, RNSVG::implementation::GroupView> {
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() {}
};

View File

@@ -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<ICanvasResourceCreator>()};
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
Geometry(Geometry::CanvasGeometry::CreateCircle(resourceCreator, cx, cy, r));
com_ptr<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
com_ptr<ID2D1EllipseGeometry> geometry;
check_hresult(factory->CreateEllipseGeometry(D2D1::Ellipse({cx, cy}, r, r), geometry.put()));
Geometry(make<RNSVG::implementation::D2DGeometry>(geometry.as<ID2D1Geometry>()));
}
} // namespace winrt::RNSVG::implementation

View File

@@ -7,7 +7,7 @@ struct CircleView : CircleViewT<CircleView, RNSVG::implementation::RenderableVie
public:
CircleView() = 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_r{};

View File

@@ -3,7 +3,6 @@
#include "ClipPathView.g.cpp"
using namespace winrt;
using namespace Microsoft::Graphics::Canvas;
using namespace Microsoft::ReactNative;
namespace winrt::RNSVG::implementation {

View File

@@ -8,9 +8,7 @@ struct ClipPathView : ClipPathViewT<ClipPathView, RNSVG::implementation::GroupVi
ClipPathView() = default;
// RenderableView
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

View File

@@ -0,0 +1,5 @@
#include "pch.h"
#include "D2DBrush.h"
#include "D2DBrush.g.cpp"
namespace winrt::RNSVG::implementation {}

19
windows/RNSVG/D2DBrush.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "D2DBrush.g.h"
namespace winrt::RNSVG::implementation {
struct D2DBrush : D2DBrushT<D2DBrush> {
public:
D2DBrush() = default;
D2DBrush(com_ptr<ID2D1Brush> const &brush) { m_d2d = brush; }
com_ptr<ID2D1Brush> Get() { return m_d2d; }
private:
com_ptr<ID2D1Brush> m_d2d;
};
} // namespace winrt::RNSVG::implementation
namespace winrt::RNSVG::factory_implementation {
struct D2DBrush : D2DBrushT<D2DBrush, implementation::D2DBrush> {};
} // namespace winrt::RNSVG::factory_implementation

View File

@@ -0,0 +1,5 @@
#include "pch.h"
#include "D2DDevice.h"
#include "D2DDevice.g.cpp"
namespace winrt::RNSVG::implementation {}

19
windows/RNSVG/D2DDevice.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "D2DDevice.g.h"
namespace winrt::RNSVG::implementation {
struct D2DDevice : D2DDeviceT<D2DDevice> {
public:
D2DDevice() = default;
D2DDevice(com_ptr<ID2D1Device> const &device) { m_d2d = device; }
com_ptr<ID2D1Device> Get() { return m_d2d; }
private:
com_ptr<ID2D1Device> m_d2d;
};
} // namespace winrt::RNSVG::implementation
namespace winrt::RNSVG::factory_implementation {
struct D2DDevice : D2DDeviceT<D2DDevice, implementation::D2DDevice> {};
} // namespace winrt::RNSVG::factory_implementation

View File

@@ -0,0 +1,5 @@
#include "pch.h"
#include "D2DDeviceContext.h"
#include "D2DDeviceContext.g.cpp"
namespace winrt::RNSVG::implementation {}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "D2DDeviceContext.g.h"
namespace winrt::RNSVG::implementation {
struct D2DDeviceContext : D2DDeviceContextT<D2DDeviceContext> {
public:
D2DDeviceContext() = default;
D2DDeviceContext(com_ptr<ID2D1DeviceContext> const & deviceContext) { m_d2d = deviceContext;}
com_ptr<ID2D1DeviceContext> Get() { return m_d2d; }
private:
com_ptr<ID2D1DeviceContext> m_d2d;
};
} // namespace winrt::RNSVG::implementation
namespace winrt::RNSVG::factory_implementation {
struct D2DDeviceContext : D2DDeviceContextT<D2DDeviceContext, implementation::D2DDeviceContext> {};
} // namespace winrt::RNSVG::factory_implementation

View File

@@ -0,0 +1,7 @@
#include "pch.h"
#include "D2DGeometry.h"
#include "D2DGeometry.g.cpp"
namespace winrt::RNSVG::implementation
{
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "D2DGeometry.g.h"
namespace winrt::RNSVG::implementation {
struct D2DGeometry : D2DGeometryT<D2DGeometry> {
public:
D2DGeometry() = default;
D2DGeometry(com_ptr<ID2D1Geometry> const &geometry) {m_d2d = geometry;}
com_ptr<ID2D1Geometry> Get() { return m_d2d; }
private:
com_ptr<ID2D1Geometry> m_d2d;
};
} // namespace winrt::RNSVG::implementation
namespace winrt::RNSVG::factory_implementation {
struct D2DGeometry : D2DGeometryT<D2DGeometry, implementation::D2DGeometry> {};
} // namespace winrt::RNSVG::factory_implementation

225
windows/RNSVG/D2DHelpers.h Normal file
View File

@@ -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<ID2D1Layer> 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<ID2D1Layer> 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<uint8_t>(color.a),
static_cast<uint8_t>(color.r),
static_cast<uint8_t>(color.g),
static_cast<uint8_t>(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<RNSVG::GroupView>()};
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<uint16_t>(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

View File

@@ -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

View File

@@ -6,9 +6,7 @@ namespace winrt::RNSVG::implementation {
struct DefsView : DefsViewT<DefsView, RNSVG::implementation::GroupView> {
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

View File

@@ -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<ICanvasResourceCreator>()};
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
com_ptr<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
com_ptr<ID2D1EllipseGeometry> geometry;
check_hresult(factory->CreateEllipseGeometry(D2D1::Ellipse({cx, cy}, rx, ry), geometry.put()));
Geometry(make<RNSVG::implementation::D2DGeometry>(geometry.as<ID2D1Geometry>()));
}
} // namespace winrt::RNSVG::implementation

View File

@@ -7,7 +7,7 @@ struct EllipseView : EllipseViewT<EllipseView, RNSVG::implementation::Renderable
public:
EllipseView() = 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_cx{};

View File

@@ -11,7 +11,6 @@
#include "Utils.h"
using namespace winrt;
using namespace Microsoft::Graphics::Canvas;
using namespace Microsoft::ReactNative;
namespace winrt::RNSVG::implementation {
@@ -97,21 +96,38 @@ void GroupView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate,
}
if (invalidate && SvgParent()) {
SvgRoot().InvalidateCanvas();
SvgRoot().Invalidate();
}
}
void GroupView::CreateGeometry(UI::Xaml::CanvasControl const &canvas) {
auto const &resourceCreator{canvas.try_as<ICanvasResourceCreator>()};
std::vector<Geometry::CanvasGeometry> geometries;
for (auto const &child : Children()) {
void GroupView::CreateGeometry() {
std::vector<ID2D1Geometry*> geometries;
for (auto const child : Children()) {
if (!child.Geometry()) {
child.CreateGeometry(canvas);
child.CreateGeometry();
}
if (child.Geometry()) {
com_ptr<ID2D1Geometry> geometry{get_self<D2DGeometry>(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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(SvgRoot().DeviceContext())->Get()};
com_ptr<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
com_ptr<ID2D1GeometryGroup> group;
check_hresult(factory->CreateGeometryGroup(
D2DHelpers::GetFillRule(FillRule()), &geometries[0], static_cast<uint32_t>(geometries.size()), group.put()));
Geometry(make<RNSVG::implementation::D2DGeometry>(group.as<ID2D1Geometry>()));
}
}
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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<ID2D1Geometry> 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<D2DGeometry>(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<ID2D1Geometry> geometry{get_self<D2DGeometry>(Geometry())->Get()};
D2D1_RECT_F bounds;
check_hresult(geometry->GetBounds(nullptr, &bounds));
if (xaml::RectHelper::Contains(D2DHelpers::FromD2DRect(bounds), point)) {
return *this;
}
}

View File

@@ -20,23 +20,17 @@ struct GroupView : GroupViewT<GroupView, RNSVG::implementation::RenderableView>
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();

View File

@@ -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<uint32_t>(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();
}
}
}

View File

@@ -2,7 +2,6 @@
#include "ImageView.h"
#include "ImageView.g.cpp"
#include <winrt/Microsoft.Graphics.Canvas.Effects.h>
#include <winrt/Windows.Security.Cryptography.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Web.Http.Headers.h>
@@ -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 <d2d1effects.h>
#include <shcore.h>
#include <wincodec.h>
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(context)->Get()};
uint32_t imgWidth, imgHeight;
check_hresult(m_wicbitmap->GetSize(&imgWidth, &imgHeight));
m_source.width = static_cast<float>(imgWidth);
m_source.height = static_cast<float>(imgHeight);
com_ptr<ID2D1Bitmap1> 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<ID2D1Geometry> clipPathGeometry;
if (ClipPathGeometry()) {
clipPathGeometry = get_self<D2DGeometry>(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<ID2D1Effect> bitmapEffects;
check_hresult(deviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put()));
check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbitmap.get()));
com_ptr<ID2D1Effect> 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<ID2D1Effect> 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<InMemoryRandomAccessStream> ImageView::GetImageMemoryStreamAsync(ImageSource source) {
IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageMemoryStreamAsync(
ImageSource source) {
switch (source.type) {
case ImageSourceType::Download:
co_return co_await GetImageStreamAsync(source);
@@ -205,7 +226,8 @@ IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageMemoryStreamAsync
}
}
IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageStreamAsync(ImageSource source) {
IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageStreamAsync(
ImageSource source) {
try {
co_await resume_background();
@@ -241,7 +263,8 @@ IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageStreamAsync(Image
co_return nullptr;
}
IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageInlineDataAsync(ImageSource source) {
IAsyncOperation<InMemoryRandomAccessStream> ImageView::GetImageInlineDataAsync(
ImageSource source) {
std::string uri{to_string(source.uri)};
size_t start = uri.find(',');
@@ -260,10 +283,57 @@ IAsyncOperation<InMemoryRandomAccessStream> 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<IWICBitmapSource> ImageView::wicBitmapSourceFromStream(InMemoryRandomAccessStream const &results) {
if (!results) {
return nullptr;
}
com_ptr<IWICImagingFactory> imagingFactory;
check_hresult(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(imagingFactory.put())));
com_ptr<IStream> istream;
check_hresult(CreateStreamOverRandomAccessStream(results.as<::IUnknown>().get(), __uuidof(IStream), istream.put_void()));
com_ptr<IWICBitmapDecoder> bitmapDecoder;
if (imagingFactory->CreateDecoderFromStream(
istream.get(), nullptr, WICDecodeMetadataCacheOnDemand, bitmapDecoder.put()) < 0) {
return nullptr;
}
com_ptr<IWICBitmapFrameDecode> decodedFrame;
check_hresult(bitmapDecoder->GetFrame(0, decodedFrame.put()));
return decodedFrame;
}
void ImageView::generateBitmap(InMemoryRandomAccessStream const &results) {
com_ptr<IWICBitmapSource> decodedFrame = wicBitmapSourceFromStream(results);
if (!decodedFrame) {
return;
}
com_ptr<IWICImagingFactory> imagingFactory;
check_hresult(
CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(imagingFactory.put())));
com_ptr<IWICFormatConverter> 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

View File

@@ -22,12 +22,8 @@ struct ImageView : ImageViewT<ImageView, RNSVG::implementation::RenderableView>
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<ImageView, RNSVG::implementation::RenderableView>
RNSVG::MeetOrSlice m_meetOrSlice{RNSVG::MeetOrSlice::Meet};
ImageSource m_source{};
Microsoft::Graphics::Canvas::CanvasBitmap m_bitmap{nullptr};
com_ptr<IWICBitmap> m_wicbitmap;
Windows::Foundation::IAsyncAction LoadImageSourceAsync(Microsoft::Graphics::Canvas::ICanvasResourceCreator resourceCreator, bool invalidate);
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::InMemoryRandomAccessStream>
Windows::Foundation::IAsyncAction LoadImageSourceAsync(bool invalidate);
Windows::Foundation::IAsyncOperation<Windows::Storage::Streams::InMemoryRandomAccessStream>
GetImageMemoryStreamAsync(ImageSource source);
Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::InMemoryRandomAccessStream>
Windows::Foundation::IAsyncOperation<Windows::Storage::Streams::InMemoryRandomAccessStream>
GetImageStreamAsync(ImageSource source);
Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::InMemoryRandomAccessStream>
Windows::Foundation::IAsyncOperation<Windows::Storage::Streams::InMemoryRandomAccessStream>
GetImageInlineDataAsync(ImageSource source);
com_ptr<IWICBitmapSource> ImageView::wicBitmapSourceFromStream(
Windows::Storage::Streams::InMemoryRandomAccessStream const &stream);
void generateBitmap(
Windows::Storage::Streams::InMemoryRandomAccessStream const &results);
};
} // namespace winrt::RNSVG::implementation

View File

@@ -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<ICanvasResourceCreator>()};
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
com_ptr<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
com_ptr<ID2D1PathGeometry> geometry;
check_hresult(factory->CreatePathGeometry(geometry.put()));
com_ptr<ID2D1GeometrySink> 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<RNSVG::implementation::D2DGeometry>(geometry.as<ID2D1Geometry>()));
}
} // namespace winrt::RNSVG::implementation

View File

@@ -7,7 +7,7 @@ struct LineView : LineViewT<LineView, RNSVG::implementation::RenderableView> {
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{};

View File

@@ -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<ICanvasResourceCreator>()};
Brushes::CanvasLinearGradientBrush brush{resourceCreator, m_stops};
auto const root{SvgRoot()};
SetPoints(brush, {0, 0, canvas.Size().Width, canvas.Size().Height});
com_ptr<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
if (m_transformSet) {
brush.Transform(m_transform);
}
winrt::com_ptr<ID2D1GradientStopCollection> stopCollection;
winrt::check_hresult(deviceContext->CreateGradientStopCollection(&m_stops[0], static_cast<uint32_t>(m_stops.size()), stopCollection.put()));
m_brush = brush;
Size size{static_cast<float>(root.ActualWidth()), static_cast<float>(root.ActualHeight())};
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES brushProperties;
brushProperties.startPoint = {0, 0};
brushProperties.endPoint = {size.Width, size.Height};
winrt::com_ptr<ID2D1LinearGradientBrush> 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<RNSVG::implementation::D2DBrush>(linearBrush.as<ID2D1Brush>());
}
void LinearGradientView::UpdateBounds() {
if (m_gradientUnits == "objectBoundingBox") {
SetPoints(m_brush.as<Brushes::CanvasLinearGradientBrush>(), m_bounds);
com_ptr<ID2D1LinearGradientBrush> brush{get_self<D2DBrush>(m_brush)->Get().as<ID2D1LinearGradientBrush>()};
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

View File

@@ -16,14 +16,12 @@ struct LinearGradientView : LinearGradientViewT<LinearGradientView, RNSVG::imple
RNSVG::SVGLength m_y1{};
RNSVG::SVGLength m_x2{};
RNSVG::SVGLength m_y2{};
std::vector<Microsoft::Graphics::Canvas::Brushes::CanvasGradientStop> m_stops{};
std::vector<D2D1_GRADIENT_STOP> 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

View File

@@ -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

View File

@@ -0,0 +1,17 @@
#pragma once
#include "MarkerView.g.h"
#include "GroupView.h"
namespace winrt::RNSVG::implementation {
struct MarkerView : MarkerViewT<MarkerView, RNSVG::implementation::GroupView> {
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<MarkerView, implementation::MarkerView> {};
} // namespace winrt::RNSVG::factory_implementation

View File

@@ -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<hstring, ViewManagerPropertyType> MarkerViewManager::NativeProps() {
auto const &parentProps{__super::NativeProps()};
auto const &nativeProps{winrt::single_threaded_map<hstring, ViewManagerPropertyType>()};
for (auto const &prop : parentProps) {
nativeProps.Insert(prop.Key(), prop.Value());
}
return nativeProps.GetView();
}
} // namespace winrt::RNSVG::implementation

View File

@@ -0,0 +1,16 @@
#pragma once
#include "MarkerViewManager.g.h"
#include "GroupViewManager.h"
namespace winrt::RNSVG::implementation {
struct MarkerViewManager : MarkerViewManagerT<MarkerViewManager, RNSVG::implementation::GroupViewManager> {
MarkerViewManager();
// IViewManagerWithNativeProperties
Windows::Foundation::Collections::IMapView<hstring, Microsoft::ReactNative::ViewManagerPropertyType> NativeProps();
};
} // namespace winrt::RNSVG::implementation
namespace winrt::RNSVG::factory_implementation {
struct MarkerViewManager : MarkerViewManagerT<MarkerViewManager, implementation::MarkerViewManager> {};
} // namespace winrt::RNSVG::factory_implementation

View File

@@ -4,14 +4,13 @@
#include "PathView.g.cpp"
#endif
#include <winrt/Microsoft.Graphics.Canvas.Svg.h>
#include "d2d1svg.h"
#include <cctype>
#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<ICanvasResourceCreator>()};
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<ID2D1SvgDocument> doc;
com_ptr<ID2D1DeviceContext5> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get().as<ID2D1DeviceContext5>()};
check_hresult(deviceContext->CreateSvgDocument(
nullptr,
D2D1::SizeF(static_cast<float>(root.ActualWidth()), static_cast<float>(root.ActualHeight())),
doc.put()));
m_segmentData.resize(m_segmentData.size());
m_commands.resize(m_commands.size());
com_ptr<ID2D1SvgPathData> path;
check_hresult(doc->CreatePathData(
&m_segmentData[0],
static_cast<uint32_t>(m_segmentData.size()),
&m_commands[0],
static_cast<uint32_t>(m_commands.size()),
path.put()));
com_ptr<ID2D1PathGeometry1> geometry;
check_hresult(path->CreatePathGeometry(D2DHelpers::GetFillRule(FillRule()), geometry.put()));
Geometry(make<RNSVG::implementation::D2DGeometry>(geometry.as<ID2D1Geometry>()));
}
void PathView::ParsePath() {

View File

@@ -9,34 +9,34 @@ struct PathView : PathViewT<PathView, RNSVG::implementation::RenderableView> {
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<float> m_segmentData;
std::vector<Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand> m_commands;
std::vector<D2D1_SVG_PATH_COMMAND> m_commands;
std::map<char, Microsoft::Graphics::Canvas::Svg::CanvasSvgPathCommand> 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<char, D2D1_SVG_PATH_COMMAND> 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();

View File

@@ -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<ID2D1ImageBrush> brush{get_self<D2DBrush>(m_brush)->Get().as<ID2D1ImageBrush>()};
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<float>(root.ActualWidth()), static_cast<float>(root.ActualHeight())})};
CreateBrush(elRect);
}
void PatternView::CreateBrush(Windows::Foundation::Rect const &rect) {
auto const &canvas{SvgRoot().Canvas()};
auto const &resourceCreator{canvas.try_as<ICanvasResourceCreator>()};
void PatternView::CreateBrush(D2D1_RECT_F rect) {
auto const &root{SvgRoot()};
if (auto const &cmdList{GetCommandList(rect)}) {
Brushes::CanvasImageBrush brush{resourceCreator, cmdList};
com_ptr<ID2D1Device> device{get_self<D2DDevice>(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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
m_brush = brush;
com_ptr<ID2D1ImageBrush> 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<RNSVG::implementation::D2DBrush>(imageBrush.as<ID2D1Brush>());
}
}
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<ID2D1CommandList> PatternView::GetCommandList(ID2D1Device* device, D2D1_RECT_F rect) {
com_ptr<ID2D1DeviceContext> deviceContext;
check_hresult(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, deviceContext.put()));
com_ptr<ID2D1CommandList> 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<D2DDeviceContext>(deviceContext);
for (auto const &child : Children()) {
child.Render(canvas, session);
child.Draw(context, D2DHelpers::SizeFromD2DRect(rect));
}
session.Close();
cmdList->Close();
deviceContext->EndDraw();
return cmdList;
}

View File

@@ -9,6 +9,7 @@ struct PatternView : PatternViewT<PatternView, RNSVG::implementation::BrushView>
// 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<PatternView, RNSVG::implementation::BrushView>
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<PatternView, RNSVG::implementation::BrushView>
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<ID2D1CommandList> GetCommandList(ID2D1Device* device, D2D1_RECT_F elRect);
};
} // namespace winrt::RNSVG::implementation

View File

@@ -112,6 +112,9 @@
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">dxguid.lib;WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
@@ -123,6 +126,11 @@
<ClInclude Include="CircleView.h" />
<ClInclude Include="CircleViewManager.h" />
<ClInclude Include="ClipPathView.h" />
<ClInclude Include="D2DBrush.h" />
<ClInclude Include="D2DDevice.h" />
<ClInclude Include="D2DDeviceContext.h" />
<ClInclude Include="D2DGeometry.h" />
<ClInclude Include="D2DHelpers.h" />
<ClInclude Include="DefsView.h" />
<ClInclude Include="DefsViewManager.h" />
<ClInclude Include="EllipseView.h" />
@@ -136,6 +144,8 @@
<ClInclude Include="LinearGradientViewManager.h" />
<ClInclude Include="LineView.h" />
<ClInclude Include="LineViewManager.h" />
<ClInclude Include="MarkerView.h" />
<ClInclude Include="MarkerViewManager.h" />
<ClInclude Include="PathView.h" />
<ClInclude Include="PathViewManager.h" />
<ClInclude Include="PatternView.h" />
@@ -170,6 +180,10 @@
<ClCompile Include="CircleViewManager.cpp" />
<ClCompile Include="ClipPathView.cpp" />
<ClCompile Include="ClipPathViewManager.cpp" />
<ClCompile Include="D2DBrush.cpp" />
<ClCompile Include="D2DDevice.cpp" />
<ClCompile Include="D2DDeviceContext.cpp" />
<ClCompile Include="D2DGeometry.cpp" />
<ClCompile Include="DefsView.cpp" />
<ClCompile Include="DefsViewManager.cpp" />
<ClCompile Include="EllipseView.cpp" />
@@ -182,6 +196,8 @@
<ClCompile Include="LinearGradientViewManager.cpp" />
<ClCompile Include="LineView.cpp" />
<ClCompile Include="LineViewManager.cpp" />
<ClCompile Include="MarkerView.cpp" />
<ClCompile Include="MarkerViewManager.cpp" />
<ClCompile Include="PathView.cpp" />
<ClCompile Include="PatternView.cpp" />
<ClCompile Include="PatternViewManager.cpp" />
@@ -231,7 +247,6 @@
</ImportGroup>
<ItemGroup Condition="$(RnwUsesPackageReference)=='true'">
<PackageReference Include="Microsoft.Windows.CppWinRT" Version="$(CppWinRTVersion)" PrivateAssets="all" />
<PackageReference Include="Win2D.uwp" Version="1.26.0" />
</ItemGroup>
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -243,7 +258,6 @@
<ImportGroup Label="ExtensionTargets" Condition="$(RnwUsesPackageReference)!='true'">
<Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(SolutionDir)\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets" Condition="'$(OverrideWinUIPackage)'!='true' And Exists('$(SolutionDir)\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets')" />
<Import Project="$(SolutionDir)\packages\Win2D.uwp.1.26.0\build\native\Win2D.uwp.targets" Condition="Exists('$(SolutionDir)\packages\Win2D.uwp.1.26.0\build\native\Win2D.uwp.targets')" />
<Import Project="$(SolutionDir)\packages\Microsoft.ReactNative.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.targets" Condition="'$(UseExperimentalNuget)'=='true' And Exists('$(SolutionDir)\packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets')" />
<Import Project="$(SolutionDir)\packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets" Condition="'$(UseExperimentalNuget)'=='true' And Exists('$(SolutionDir)\packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets')" />
</ImportGroup>
@@ -254,7 +268,6 @@
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.$(CppWinRTVersion)\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="'$(OverrideWinUIPackage)'!='true' And !Exists('$(SolutionDir)\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\Win2D.uwp.1.26.0\build\native\Win2D.uwp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Win2D.uwp.1.26.0\build\native\Win2D.uwp.targets'))" />
<Error Condition="'$(UseExperimentalNuget)'=='true' And !Exists('$(SolutionDir)\packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets'))" />
<Error Condition="'$(UseExperimentalNuget)'=='true' And !Exists('$(SolutionDir)\packages\Microsoft.ReactNative.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.ReactNative.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.targets'))" />
</Target>

View File

@@ -130,6 +130,24 @@
<ClCompile Include="ClipPathView.cpp">
<Filter>Views\GroupViews</Filter>
</ClCompile>
<ClCompile Include="D2DGeometry.cpp">
<Filter>D2DWrappers</Filter>
</ClCompile>
<ClCompile Include="MarkerViewManager.cpp">
<Filter>ViewManagers\GroupViewManagers</Filter>
</ClCompile>
<ClCompile Include="MarkerView.cpp">
<Filter>Views\GroupViews</Filter>
</ClCompile>
<ClCompile Include="D2DDeviceContext.cpp">
<Filter>D2DWrappers</Filter>
</ClCompile>
<ClCompile Include="D2DDevice.cpp">
<Filter>D2DWrappers</Filter>
</ClCompile>
<ClCompile Include="D2DBrush.cpp">
<Filter>D2DWrappers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -252,6 +270,27 @@
<ClInclude Include="ClipPathView.h">
<Filter>Views\GroupViews</Filter>
</ClInclude>
<ClInclude Include="D2DHelpers.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="D2DGeometry.h">
<Filter>D2DWrappers</Filter>
</ClInclude>
<ClInclude Include="MarkerViewManager.h">
<Filter>ViewManagers\GroupViewManagers</Filter>
</ClInclude>
<ClInclude Include="MarkerView.h">
<Filter>Views\GroupViews</Filter>
</ClInclude>
<ClInclude Include="D2DDeviceContext.h">
<Filter>D2DWrappers</Filter>
</ClInclude>
<ClInclude Include="D2DDevice.h">
<Filter>D2DWrappers</Filter>
</ClInclude>
<ClInclude Include="D2DBrush.h">
<Filter>D2DWrappers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
@@ -276,5 +315,8 @@
<Filter Include="IDLs">
<UniqueIdentifier>{50f81ae3-d543-477a-a60c-a0f310289f29}</UniqueIdentifier>
</Filter>
<Filter Include="D2DWrappers">
<UniqueIdentifier>{d4e16389-e870-4046-b1fe-61bc171edad9}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@@ -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<ICanvasResourceCreator>()};
Brushes::CanvasRadialGradientBrush brush{resourceCreator, m_stops};
auto const root{SvgRoot()};
SetPoints(brush, {0, 0, canvas.Size().Width, canvas.Size().Height});
com_ptr<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
if (m_transformSet) {
brush.Transform(m_transform);
winrt::com_ptr<ID2D1GradientStopCollection> stopCollection;
winrt::check_hresult(deviceContext->CreateGradientStopCollection(&m_stops[0], static_cast<uint32_t>(m_stops.size()), stopCollection.put()));
D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES brushProperties{};
winrt::com_ptr<ID2D1RadialGradientBrush> radialBrush;
winrt::check_hresult(
deviceContext->CreateRadialGradientBrush(brushProperties, stopCollection.get(), radialBrush.put()));
SetPoints(radialBrush.get(), {0, 0, static_cast<float>(root.ActualWidth()), static_cast<float>(root.ActualHeight())});
if (!m_transform.IsIdentity()) {
radialBrush->SetTransform(m_transform);
}
m_brush = brush;
m_brush = make<RNSVG::implementation::D2DBrush>(radialBrush.as<ID2D1Brush>());
}
void RadialGradientView::UpdateBounds() {
if (m_gradientUnits == "objectBoundingBox") {
SetPoints(m_brush.as<Brushes::CanvasRadialGradientBrush>(), m_bounds);
com_ptr<ID2D1RadialGradientBrush> brush{get_self<D2DBrush>(m_brush)->Get().as<ID2D1RadialGradientBrush>()};
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

View File

@@ -18,14 +18,12 @@ struct RadialGradientView : RadialGradientViewT<RadialGradientView, RNSVG::imple
RNSVG::SVGLength m_ry{};
RNSVG::SVGLength m_cx{};
RNSVG::SVGLength m_cy{};
std::vector<Microsoft::Graphics::Canvas::Brushes::CanvasGradientStop> m_stops{};
std::vector<D2D1_GRADIENT_STOP> 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

View File

@@ -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<RadialGradientViewManager>(); });
packageBuilder.AddViewManager(L"PatternViewManager", []() { return winrt::make<PatternViewManager>(); });
packageBuilder.AddViewManager(L"ClipPathViewManager", []() { return winrt::make<ClipPathViewManager>(); });
packageBuilder.AddViewManager(L"MarkerViewManager", []() { return winrt::make<MarkerViewManager>(); });
}
} // namespace winrt::RNSVG::implementation

View File

@@ -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<ICanvasResourceCreator>()};
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
com_ptr<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
com_ptr<ID2D1RoundedRectangleGeometry> geometry;
check_hresult(factory->CreateRoundedRectangleGeometry(
D2D1::RoundedRect(D2D1::RectF(x, y, width + x, height + y), rx, ry), geometry.put()));
Geometry(make<RNSVG::implementation::D2DGeometry>(geometry.as<ID2D1Geometry>()));
}
} // namespace winrt::RNSVG::implementation

View File

@@ -9,7 +9,7 @@ struct RectView : RectViewT<RectView, RNSVG::implementation::RenderableView> {
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{};

View File

@@ -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<RNSVG::LineCap>(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<RNSVG::LineJoin>(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<RNSVG::FillRule>(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<ICanvasResourceCreator>()};
void RenderableView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) {
if (m_recreateResources) {
CreateGeometry(canvas);
CreateGeometry();
}
auto geometry{Geometry()};
if (!Geometry()) {
return;
}
com_ptr<ID2D1Geometry> geometry{get_self<D2DGeometry>(m_geometry)->Get()};
com_ptr<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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<ID2D1Factory> factory;
deviceContext->GetFactory(factory.put());
geometry = Geometry::CanvasGeometry::CreateGroup(resourceCreator, {geometry}, FillRule());
com_ptr<ID2D1GeometryGroup> 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<ID2D1Geometry> clipPathGeometry;
if (ClipPathGeometry()) {
clipPathGeometry = get_self<D2DGeometry>(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<ID2D1StrokeStyle> 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<ID2D1Geometry> geometry{get_self<D2DGeometry>(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

View File

@@ -2,6 +2,8 @@
#include "RenderableView.g.h"
#include "SVGLength.h"
#include "D2DDeviceContext.h"
#include "D2DGeometry.h"
namespace winrt::RNSVG::implementation {
struct RenderableView : RenderableViewT<RenderableView> {
@@ -14,8 +16,8 @@ struct RenderableView : RenderableViewT<RenderableView> {
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<RenderableView> {
float StrokeDashOffset() { return m_strokeDashOffset; }
RNSVG::SVGLength StrokeWidth() { return m_strokeWidth; }
Windows::Foundation::Collections::IVector<RNSVG::SVGLength> 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<RenderableView> {
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<RenderableView> {
float m_strokeOpacity{1.0f};
float m_strokeMiterLimit{0.0f};
float m_strokeDashOffset{0.0f};
std::vector<float> m_adjustedStrokeDashArray;
RNSVG::SVGLength m_strokeWidth{1.0f, RNSVG::LengthType::Pixel};
Windows::Foundation::Collections::IVector<RNSVG::SVGLength> m_strokeDashArray{
winrt::single_threaded_vector<RNSVG::SVGLength>()};
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

View File

@@ -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();

View File

@@ -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()};

View File

@@ -9,7 +9,7 @@ struct SVGLength : SVGLengthT<SVGLength> {
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; }

View File

@@ -5,36 +5,66 @@
#include "SvgView.g.cpp"
#endif
#include <windows.ui.xaml.media.dxinterop.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
#include <winrt/Windows.Graphics.Display.h>
#include "D2DDevice.h"
#include "D2DDeviceContext.h"
#include "GroupView.h"
#include "Utils.h"
#include <d3d11_4.h>
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<float>(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<ID3D11Device> 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<IDXGIDevice> dxgiDevice{d3dDevice.as<IDXGIDevice>()};
// Create the Direct2D device and a corresponding context.
com_ptr<ID2D1Device> device;
check_hresult(D2D1CreateDevice(dxgiDevice.get(), nullptr, device.put()));
m_device = make<RNSVG::implementation::D2DDevice>(device);
com_ptr<ID2D1DeviceContext> deviceContext;
check_hresult(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, deviceContext.put()));
m_deviceContext = make<RNSVG::implementation::D2DDeviceContext>(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<float>(canvas.ActualWidth())};
float height{static_cast<float>(canvas.ActualHeight())};
bool nested{m_parent};
void SvgView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) {
com_ptr<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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<RNSVG::SvgView>()}) {
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<RNSVG::SvgView>()}) {
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(DeviceContext())->Get()};
Size size{static_cast<float>(ActualWidth()), static_cast<float>(ActualHeight())};
xaml::Media::Imaging::SurfaceImageSource surfaceImageSource(
static_cast<int32_t>(size.Width), static_cast<int32_t>(size.Height));
com_ptr<ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D{surfaceImageSource.as<ISurfaceImageSourceNativeWithD2D>()};
// Associate the Direct2D device with the SurfaceImageSource.
com_ptr<ID2D1Device> device{get_self<D2DDevice>(Device())->Get()};
sisNativeWithD2D->SetDevice(device.get());
com_ptr<IDXGISurface> 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<long>(size.Width), static_cast<long>(size.Height)};
POINT offset{0, 0};
check_hresult(sisNativeWithD2D->BeginDraw(updateRect, __uuidof(IDXGISurface), dxgiSurface.put_void(), &offset));
// Create render target.
com_ptr<ID2D1Bitmap1> 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<float>(offset.x), static_cast<float>(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

View File

@@ -9,18 +9,18 @@ struct SvgView : SvgViewT<SvgView> {
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<SvgView> {
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<SvgView> {
winrt::single_threaded_map<hstring, RNSVG::IRenderable>()};
Windows::Foundation::Collections::IMap<hstring, RNSVG::BrushView> m_brushes{
winrt::single_threaded_map<hstring, RNSVG::BrushView>()};
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

View File

@@ -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;

View File

@@ -5,7 +5,6 @@
#include "Utils.h"
using namespace winrt;
using namespace Microsoft::Graphics::Canvas;
using namespace Microsoft::ReactNative;
namespace winrt::RNSVG::implementation {

View File

@@ -16,9 +16,7 @@ struct SymbolView : SymbolViewT<SymbolView, RNSVG::implementation::GroupView> {
// 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};

View File

@@ -2,11 +2,11 @@
#include "TSpanView.h"
#include "TSpanView.g.cpp"
#include <codecvt>
#include "Utils.h"
#include <winrt/Microsoft.Graphics.Canvas.Text.h>
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<ICanvasResourceCreator>()};
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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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<ID2D1Factory> d2dFactory;
deviceContext->GetFactory(d2dFactory.put());
com_ptr<IDWriteFactory> dwriteFactory;
check_hresult(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<::IUnknown **>(dwriteFactory.put_void())));
com_ptr<IDWriteTextFormat> 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<uint32_t>(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

View File

@@ -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;

View File

@@ -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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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

View File

@@ -14,9 +14,7 @@ struct TextView : TextViewT<TextView, RNSVG::implementation::GroupView> {
Windows::Foundation::Collections::IVector<RNSVG::SVGLength> 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<RNSVG::SVGLength> m_x{winrt::single_threaded_vector<RNSVG::SVGLength>()};

View File

@@ -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();
};
}

View File

@@ -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<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(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<RNSVG::SymbolView>()}) {
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<RNSVG::SymbolView>()}) {
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<RNSVG::SymbolView>()}) {
symbol.DrawGroup(context, size);
} else {
view.Draw(context, size);
}
deviceContext->PopLayer();
// Restore original template props
if (auto const &parent{view.SvgParent().try_as<RNSVG::RenderableView>()}) {
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");
}
}

View File

@@ -8,9 +8,7 @@ struct UseView : UseViewT<UseView, RNSVG::implementation::RenderableView> {
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""};

View File

@@ -2,19 +2,16 @@
#include "pch.h"
#include <winrt/Microsoft.Graphics.Canvas.Brushes.h>
#include <winrt/Windows.Foundation.Numerics.h>
#include <winrt/Windows.UI.Text.h>
#include "JSValueReader.h"
#include "D2DHelpers.h"
#include "D2DBrush.h"
#define _USE_MATH_DEFINES
#include <math.h>
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<float> GetAdjustedStrokeArray(IVector<SVGLength> const &value, float strokeWidth, float canvasDiagonal) {
std::vector<float> 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<float>(M_SQRT1_2);
}
static float GetAbsoluteLength(SVGLength const& length, double relativeTo) {
return GetAbsoluteLength(length, static_cast<float>(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<RNSVG::GroupView>()};
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<uint16_t>(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<Xaml::Media::Brush>()}) {
if (auto const &scb{brush.try_as<Xaml::Media::SolidColorBrush>()}) {
} else if (auto const &brush{value.To<xaml::Media::Brush>()}) {
if (auto const &scb{brush.try_as<xaml::Media::SolidColorBrush>()}) {
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<Brushes::CanvasGradientStop> 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<D2D1_GRADIENT_STOP> JSValueAsStops(JSValue const &value) {
if (value.IsNull()) {
return {};
}
auto const &stops{value.AsArray()};
std::vector<Brushes::CanvasGradientStop> canvasStops{};
std::vector<D2D1_GRADIENT_STOP> 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<ID2D1Brush> 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<ID2D1Geometry> const &geometry) {
com_ptr<ID2D1Brush> brush;
com_ptr<ID2D1DeviceContext> deviceContext{get_self<RNSVG::implementation::D2DDeviceContext>(root.DeviceContext())->Get()};
if (root && brushId != L"") {
if (brushId == L"currentColor") {
brush = Brushes::CanvasSolidColorBrush(resourceCreator, root.CurrentColor());
com_ptr<ID2D1SolidColorBrush> scb;
deviceContext->CreateSolidColorBrush(D2DHelpers::AsD2DColor(root.CurrentColor()), scb.put());
brush = scb.as<ID2D1Brush>();
} 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<RNSVG::implementation::D2DBrush>(brushView.Brush())->Get();
}
}
if (!brush) {
brush = Brushes::CanvasSolidColorBrush(resourceCreator, color);
com_ptr<ID2D1SolidColorBrush> scb;
deviceContext->CreateSolidColorBrush(D2DHelpers::AsD2DColor(color), scb.put());
brush = scb.as<ID2D1Brush>();
}
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

View File

@@ -108,6 +108,12 @@ namespace RNSVG
ClipPathViewManager();
};
[default_interface]
runtimeclass MarkerViewManager : GroupViewManager
{
MarkerViewManager();
};
[default_interface]
unsealed runtimeclass TextViewManager : GroupViewManager
{

View File

@@ -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<String, IRenderable> Templates{ get; };
Windows.Foundation.Collections.IMap<String, BrushView> Brushes{ get; };
void InvalidateCanvas();
void Invalidate();
};
[default_interface]
@@ -55,10 +51,10 @@ namespace RNSVG
Single StrokeMiterLimit{ get; };
Single StrokeDashOffset{ get; };
Windows.Foundation.Collections.IVector<SVGLength> 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);
};

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.3.191129002" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Win2D.uwp" version="1.26.0" targetFramework="native" />
</packages>
</packages>

View File

@@ -2,30 +2,14 @@
#define NOMINMAX
#include <d2d1_3.h>
#include <hstring.h>
#include <restrictederrorinfo.h>
#include <unknwn.h>
#include <windows.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#include <winrt/Windows.UI.Xaml.Navigation.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Microsoft.Graphics.Canvas.h>
#include <winrt/Microsoft.Graphics.Canvas.Geometry.h>
#include <winrt/Microsoft.Graphics.Canvas.UI.Xaml.h>
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/Microsoft.UI.Xaml.Automation.Peers.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Media.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
using namespace winrt::Windows::Foundation;