Files
react-native-svg/windows/RNSVG/GroupView.cpp
Marlene Cota f88532d195 [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.
2023-11-14 11:33:19 +01:00

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