Files
react-native-svg/windows/RNSVG/PatternView.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

158 lines
5.0 KiB
C++

#include "pch.h"
#include "PatternView.h"
#include "PatternView.g.cpp"
#include "Utils.h"
#include "D2DDevice.h"
using namespace winrt;
using namespace Microsoft::ReactNative;
namespace winrt::RNSVG::implementation {
void PatternView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, bool invalidate) {
const JSValueObject &propertyMap{JSValue::ReadObjectFrom(reader)};
for (auto const &pair : propertyMap) {
auto const &propertyName{pair.first};
auto const &propertyValue{pair.second};
if (propertyName == "x") {
m_x = SVGLength::From(propertyValue);
} else if (propertyName == "y") {
m_y = SVGLength::From(propertyValue);
} else if (propertyName == "width") {
m_width = SVGLength::From(propertyValue);
} else if (propertyName == "height") {
m_height = SVGLength::From(propertyValue);
} else if (propertyName == "patternUnits") {
m_patternUnits = Utils::JSValueAsBrushUnits(propertyValue);
} else if (propertyName == "patternContentUnits") {
m_patternContentUnits = Utils::JSValueAsBrushUnits(propertyValue, "userSpaceOnUse");
} else if (propertyName == "patternTransform") {
m_transform = Utils::JSValueAsD2DTransform(propertyValue);
if (propertyValue.IsNull()) {
m_transform = D2D1::Matrix3x2F::Identity();
}
} else if (propertyName == "vbWidth") {
m_vbWidth = Utils::JSValueAsFloat(propertyValue);
} else if (propertyName == "vbHeight") {
m_vbHeight = Utils::JSValueAsFloat(propertyValue);
} else if (propertyName == "minX") {
m_minX = Utils::JSValueAsFloat(propertyValue);
} else if (propertyName == "minY") {
m_minY = Utils::JSValueAsFloat(propertyValue);
} else if (propertyName == "align") {
m_align = Utils::JSValueAsString(propertyValue);
} else if (propertyName == "meetOrSlice") {
m_meetOrSlice = Utils::GetMeetOrSlice(propertyValue);
}
}
__super::UpdateProperties(reader, forceUpdate, invalidate);
SaveDefinition();
if (auto const &root{SvgRoot()}) {
root.Invalidate();
}
}
void PatternView::UpdateBounds() {
if (m_patternUnits == "objectBoundingBox") {
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 root{SvgRoot()};
D2D1_RECT_F elRect{GetAdjustedRect({0, 0, static_cast<float>(root.ActualWidth()), static_cast<float>(root.ActualHeight())})};
CreateBrush(elRect);
}
void PatternView::CreateBrush(D2D1_RECT_F rect) {
auto const &root{SvgRoot()};
com_ptr<ID2D1Device> device{get_self<D2DDevice>(root.Device())->Get()};
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;
com_ptr<ID2D1DeviceContext> deviceContext{get_self<D2DDeviceContext>(root.DeviceContext())->Get()};
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>());
}
}
D2D1_RECT_F PatternView::GetAdjustedRect(D2D1_RECT_F bounds) {
float width{D2DHelpers::WidthFromD2DRect(bounds)};
float height{D2DHelpers::HeightFromD2DRect(bounds)};
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};
}
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,
m_minY,
(m_vbWidth + m_minX),
(m_vbHeight + m_minY)};
auto viewboxTransform{Utils::GetViewBoxTransform(
vbRect,
D2DHelpers::FromD2DRect(rect),
m_align,
m_meetOrSlice)};
transform = D2DHelpers::AsD2DTransform(viewboxTransform) * transform;
}
deviceContext->SetTransform(transform);
auto context = make<D2DDeviceContext>(deviceContext);
for (auto const &child : Children()) {
child.Draw(context, D2DHelpers::SizeFromD2DRect(rect));
}
cmdList->Close();
deviceContext->EndDraw();
return cmdList;
}
} // namespace winrt::RNSVG::implementation