mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
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.
232 lines
6.6 KiB
C++
232 lines
6.6 KiB
C++
#include "pch.h"
|
|
|
|
#include "JSValueXaml.h"
|
|
|
|
#include "GroupView.h"
|
|
#if __has_include("GroupView.g.cpp")
|
|
#include "GroupView.g.cpp"
|
|
#endif
|
|
|
|
#include "SVGLength.h"
|
|
#include "Utils.h"
|
|
|
|
using namespace winrt;
|
|
using namespace Microsoft::ReactNative;
|
|
|
|
namespace winrt::RNSVG::implementation {
|
|
void GroupView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, bool invalidate) {
|
|
const JSValueObject &propertyMap{JSValue::ReadObjectFrom(reader)};
|
|
|
|
auto const &parent{SvgParent().try_as<RNSVG::GroupView>()};
|
|
auto fontProp{RNSVG::FontProp::Unknown};
|
|
|
|
for (auto const &pair : propertyMap) {
|
|
auto const &propertyName{pair.first};
|
|
auto const &propertyValue{pair.second};
|
|
|
|
if (propertyName == "font") {
|
|
auto const &font{propertyValue.AsObject()};
|
|
|
|
// When any of the font props update, you don't get individual updates.
|
|
// Instead, you get a new JSValueObject with all font props set on the element.
|
|
// If a prop was removed, you will not get a null type - it just won't
|
|
// be part of the new prop object, so we will reset all font values.
|
|
if (forceUpdate) {
|
|
m_fontPropMap[RNSVG::FontProp::FontSize] = false;
|
|
m_fontPropMap[RNSVG::FontProp::FontFamily] = false;
|
|
m_fontPropMap[RNSVG::FontProp::FontWeight] = false;
|
|
}
|
|
|
|
for (auto const &item : m_fontPropMap) {
|
|
if (!item.second) {
|
|
switch (item.first) {
|
|
case RNSVG::FontProp::FontSize:
|
|
m_fontSize = parent ? parent.FontSize() : 12.0f;
|
|
break;
|
|
case RNSVG::FontProp::FontFamily:
|
|
m_fontFamily = parent ? parent.FontFamily() : L"Segoe UI";
|
|
break;
|
|
case RNSVG::FontProp::FontWeight:
|
|
m_fontWeight = L"auto";
|
|
break;
|
|
default:
|
|
throw hresult_error();
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto const &prop : font) {
|
|
auto const &key{prop.first};
|
|
auto const &value{prop.second};
|
|
|
|
if (key == "fontSize") {
|
|
fontProp = RNSVG::FontProp::FontSize;
|
|
if (forceUpdate || !m_fontPropMap[fontProp]) {
|
|
m_fontSize = value.AsSingle();
|
|
}
|
|
} else if (key == "fontFamily") {
|
|
fontProp = RNSVG::FontProp::FontFamily;
|
|
if (forceUpdate || !m_fontPropMap[fontProp]) {
|
|
m_fontFamily = to_hstring(value.AsString());
|
|
}
|
|
} else if (key == "fontWeight") {
|
|
fontProp = RNSVG::FontProp::FontWeight;
|
|
auto fontWeight{to_hstring(value.AsString())};
|
|
if (forceUpdate) {
|
|
m_fontWeight = fontWeight;
|
|
} else if (!m_fontPropMap[fontProp]) {
|
|
m_fontWeight = L"auto";
|
|
}
|
|
}
|
|
|
|
// forceUpdate = true means the property is being set on an element
|
|
// instead of being inherited from the parent.
|
|
if (forceUpdate && (fontProp != RNSVG::FontProp::Unknown)) {
|
|
// If the propertyValue is null, that means we reset the property
|
|
m_fontPropMap[fontProp] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
__super::UpdateProperties(reader, forceUpdate, false);
|
|
|
|
for (auto const &child : Children()) {
|
|
child.UpdateProperties(reader, false, false);
|
|
}
|
|
|
|
if (invalidate && SvgParent()) {
|
|
SvgRoot().Invalidate();
|
|
}
|
|
}
|
|
|
|
void GroupView::CreateGeometry() {
|
|
std::vector<ID2D1Geometry*> geometries;
|
|
for (auto const child : Children()) {
|
|
if (!child.Geometry()) {
|
|
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());
|
|
}
|
|
}
|
|
|
|
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() {
|
|
__super::SaveDefinition();
|
|
|
|
for (auto const &child : Children()) {
|
|
child.SaveDefinition();
|
|
}
|
|
}
|
|
|
|
void GroupView::MergeProperties(RNSVG::RenderableView const &other) {
|
|
__super::MergeProperties(other);
|
|
|
|
for (auto const &child : Children()) {
|
|
child.MergeProperties(*this);
|
|
}
|
|
}
|
|
|
|
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]) {
|
|
deviceContext->SetTransform(D2DHelpers::AsD2DTransform(SvgTransform()) * transform);
|
|
}
|
|
|
|
com_ptr<ID2D1Geometry> clipPathGeometry;
|
|
|
|
if (ClipPathGeometry()) {
|
|
clipPathGeometry = get_self<D2DGeometry>(ClipPathGeometry())->Get();
|
|
}
|
|
|
|
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::DrawGroup(RNSVG::D2DDeviceContext const &context, Size const &size) {
|
|
for (auto const &child : Children()) {
|
|
child.Draw(context, size);
|
|
}
|
|
}
|
|
|
|
void GroupView::CreateResources() {
|
|
for (auto const &child : Children()) {
|
|
child.CreateResources();
|
|
}
|
|
}
|
|
|
|
void GroupView::Unload() {
|
|
for (auto const &child : Children()) {
|
|
child.Unload();
|
|
}
|
|
|
|
m_reactContext = nullptr;
|
|
m_fontPropMap.clear();
|
|
m_children.Clear();
|
|
|
|
__super::Unload();
|
|
}
|
|
|
|
winrt::RNSVG::IRenderable GroupView::HitTest(Point const &point) {
|
|
RNSVG::IRenderable renderable{nullptr};
|
|
if (IsResponsible()) {
|
|
for (auto const &child : Children()) {
|
|
if (auto const &hit{child.HitTest(point)}) {
|
|
renderable = hit;
|
|
}
|
|
}
|
|
if (renderable && !renderable.IsResponsible()) {
|
|
return *this;
|
|
} else if (!renderable) {
|
|
if (!Geometry()) {
|
|
CreateGeometry();
|
|
}
|
|
|
|
if (Geometry()) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return renderable;
|
|
}
|
|
} // namespace winrt::RNSVG::implementation
|