mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-05 22:56:11 +00:00
[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:
@@ -32,6 +32,8 @@
|
||||
See https://microsoft.github.io/react-native-windows/docs/nuget
|
||||
-->
|
||||
<UseExperimentalNuget>false</UseExperimentalNuget>
|
||||
|
||||
<ReactExperimentalFeaturesSet>true</ReactExperimentalFeaturesSet>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "ClipPathView.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Microsoft::Graphics::Canvas;
|
||||
using namespace Microsoft::ReactNative;
|
||||
|
||||
namespace winrt::RNSVG::implementation {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
5
windows/RNSVG/D2DBrush.cpp
Normal file
5
windows/RNSVG/D2DBrush.cpp
Normal 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
19
windows/RNSVG/D2DBrush.h
Normal 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
|
||||
5
windows/RNSVG/D2DDevice.cpp
Normal file
5
windows/RNSVG/D2DDevice.cpp
Normal 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
19
windows/RNSVG/D2DDevice.h
Normal 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
|
||||
5
windows/RNSVG/D2DDeviceContext.cpp
Normal file
5
windows/RNSVG/D2DDeviceContext.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "pch.h"
|
||||
#include "D2DDeviceContext.h"
|
||||
#include "D2DDeviceContext.g.cpp"
|
||||
|
||||
namespace winrt::RNSVG::implementation {}
|
||||
19
windows/RNSVG/D2DDeviceContext.h
Normal file
19
windows/RNSVG/D2DDeviceContext.h
Normal 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
|
||||
7
windows/RNSVG/D2DGeometry.cpp
Normal file
7
windows/RNSVG/D2DGeometry.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "D2DGeometry.h"
|
||||
#include "D2DGeometry.g.cpp"
|
||||
|
||||
namespace winrt::RNSVG::implementation
|
||||
{
|
||||
}
|
||||
19
windows/RNSVG/D2DGeometry.h
Normal file
19
windows/RNSVG/D2DGeometry.h
Normal 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
225
windows/RNSVG/D2DHelpers.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
8
windows/RNSVG/MarkerView.cpp
Normal file
8
windows/RNSVG/MarkerView.cpp
Normal 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
|
||||
17
windows/RNSVG/MarkerView.h
Normal file
17
windows/RNSVG/MarkerView.h
Normal 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
|
||||
24
windows/RNSVG/MarkerViewManager.cpp
Normal file
24
windows/RNSVG/MarkerViewManager.cpp
Normal 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
|
||||
16
windows/RNSVG/MarkerViewManager.h
Normal file
16
windows/RNSVG/MarkerViewManager.h
Normal 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
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()};
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Microsoft::Graphics::Canvas;
|
||||
using namespace Microsoft::ReactNative;
|
||||
|
||||
namespace winrt::RNSVG::implementation {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>()};
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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""};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -108,6 +108,12 @@ namespace RNSVG
|
||||
ClipPathViewManager();
|
||||
};
|
||||
|
||||
[default_interface]
|
||||
runtimeclass MarkerViewManager : GroupViewManager
|
||||
{
|
||||
MarkerViewManager();
|
||||
};
|
||||
|
||||
[default_interface]
|
||||
unsealed runtimeclass TextViewManager : GroupViewManager
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user